blob: 1b7ce1c766dca0b701c823bfff47f39737a3aa50 [file] [log] [blame]
Harald Welte0b7614b2021-01-30 17:59:43 +01001/* VTY for osmo-ns-dummy */
2
3/* (C) 2021 Harald Welte <laforge@gnumonks.org>
4 *
5 * All Rights Reserved
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <talloc.h>
25#include <errno.h>
26#include <string.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <stdint.h>
30#include <inttypes.h>
31
32
33#include <osmocom/core/select.h>
34#include <osmocom/core/application.h>
35#include <osmocom/core/stats.h>
36#include <osmocom/core/utils.h>
37#include <osmocom/core/linuxlist.h>
38#include <osmocom/core/logging.h>
39
40#include <osmocom/gsm/prim.h>
41#include <osmocom/gprs/gprs_ns2.h>
42
43#include <osmocom/vty/vty.h>
44#include <osmocom/vty/command.h>
45#include <osmocom/vty/logging.h>
46#include <osmocom/vty/stats.h>
47#include <osmocom/vty/misc.h>
48
49static struct gprs_ns2_inst *g_nsi;
50static struct llist_head g_ns_traf_gens = LLIST_HEAD_INIT(g_ns_traf_gens);
51
52/* one NS traffic generator instance. You can have as many of these as you want,
53 * just as long as they have unique names */
54struct ns_traf_gen {
55 struct llist_head list;
56 const char *name;
57 struct {
58 uint16_t nsei;
59 uint16_t bvci;
60 /* size of each packet */
61 uint16_t pkt_size;
62 /* interval between packets in us */
63 uint32_t interval_us;
64 /* fixed (false) or random (true) LSP */
65 bool lsp_randomize;
66 /* (fixeD) Link Selector Parameter */
67 uint32_t lsp;
68 } cfg;
69 struct osmo_fd timerfd;
70 bool running;
71};
72
73#define LOGNTG(ntg, lvl, fmt, args ...) \
74 LOGP(DLGLOBAL, lvl, "traf-gen(%s): " fmt, (ntg)->name, ## args)
75
76/* allocate and transmit one NS message */
77static int ntg_tx_one(struct ns_traf_gen *ntg)
78{
79 struct osmo_gprs_ns2_prim nsp = {};
80 struct msgb *msg = msgb_alloc_headroom(3072, 20, "NS traffic gen");
81
82 if (!msg)
83 return -ENOMEM;
84 msgb_put(msg, ntg->cfg.pkt_size);
85 nsp.bvci = ntg->cfg.bvci;
86 nsp.nsei = ntg->cfg.nsei;
87 if (ntg->cfg.lsp_randomize)
88 nsp.u.unitdata.link_selector = rand();
89 else
90 nsp.u.unitdata.link_selector = ntg->cfg.lsp;
91 osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
92 return gprs_ns2_recv_prim(g_nsi, &nsp.oph);
93}
94
95/* call-back from transmit timer-fd */
96static int ntg_timerfd_cb(struct osmo_fd *ofd, unsigned int what)
97{
98 uint64_t expire_count;
99 struct ns_traf_gen *ntg = ofd->data;
100 unsigned int i;
101 int rc;
102
103 OSMO_ASSERT(what & OSMO_FD_READ);
104
105 rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
106 if (rc < 0 && errno == EAGAIN)
107 return 0;
108 OSMO_ASSERT(rc == sizeof(expire_count));
109
110 for (i = 0; i < expire_count; i++)
111 ntg_tx_one(ntg);
112
113 return 0;
114}
115
116static struct ns_traf_gen *ns_traf_gen_find(const char *name)
117{
118 struct ns_traf_gen *ntg;
119
120 llist_for_each_entry(ntg, &g_ns_traf_gens, list) {
121 if (!strcmp(ntg->name, name))
122 return ntg;
123 }
124 return NULL;
125}
126
127static struct ns_traf_gen *ns_traf_gen_find_or_alloc(const char *name)
128{
129 struct ns_traf_gen *ntg;
130 int rc;
131
132 ntg = ns_traf_gen_find(name);
133 if (ntg)
134 return ntg;
135
136 ntg = talloc_zero(g_nsi, struct ns_traf_gen);
137 OSMO_ASSERT(ntg);
138 ntg->name = talloc_strdup(ntg, name);
139 ntg->timerfd.fd = -1;
140 rc = osmo_timerfd_setup(&ntg->timerfd, ntg_timerfd_cb, ntg);
141 OSMO_ASSERT(rc >= 0);
142 llist_add_tail(&ntg->list, &g_ns_traf_gens);
143
144 return ntg;
145}
146
147enum nodes {
148 NTG_NODE = _LAST_OSMOVTY_NODE + 1,
149};
150
151static struct cmd_node ntg_node = {
152 NTG_NODE,
153 "%s(config-ns-traf-gen)# ",
154 1,
155};
156
157static int config_write_ntg(struct vty *vty)
158{
159 struct ns_traf_gen *ntg;
160
161 llist_for_each_entry(ntg, &g_ns_traf_gens, list) {
162 vty_out(vty, "ns-traffic-generator %s%s", ntg->name, VTY_NEWLINE);
163 vty_out(vty, " nsei %u%s", ntg->cfg.nsei, VTY_NEWLINE);
164 vty_out(vty, " bvci %u%s", ntg->cfg.bvci, VTY_NEWLINE);
165 vty_out(vty, " packet-size %u%s", ntg->cfg.pkt_size, VTY_NEWLINE);
166 vty_out(vty, " interval-us %u%s", ntg->cfg.interval_us, VTY_NEWLINE);
167 vty_out(vty, " lsp %u%s", ntg->cfg.lsp, VTY_NEWLINE);
168 vty_out(vty, " lsp-mode %s%s", ntg->cfg.lsp_randomize ? "randomized" : "fixed", VTY_NEWLINE);
169 }
170
171 return 0;
172}
173
174DEFUN(ntg_start, ntg_start_stop_cmd,
175 "ns-traffic-generator (start|stop) NAME",
176 "Control named NS traffic generator\n"
177 "Start generating traffic in this traffic generator\n"
178 "Stop generating traffic in this traffic generator\n"
179 "Name of NS traffic generator to start\n")
180{
181 struct ns_traf_gen *ntg = ns_traf_gen_find(argv[1]);
182 if (!ntg) {
183 vty_out(vty, "NS Traffic generator '%s' doesn't exist%s", argv[1], VTY_NEWLINE);
184 return CMD_WARNING;
185 }
186
187 if (!strcmp(argv[0], "start")) {
188 struct timespec interval;
189 if (ntg->running) {
190 vty_out(vty, "NS Traffic generator was already started%s", VTY_NEWLINE);
191 return CMD_WARNING;
192 }
193 interval.tv_sec = ntg->cfg.interval_us / 1000000;
194 interval.tv_nsec = (ntg->cfg.interval_us % 1000000) * 1000;
195 osmo_timerfd_schedule(&ntg->timerfd, NULL, &interval);
196 ntg->running = true;
197 } else {
198 if (!ntg->running) {
199 vty_out(vty, "NS Traffic generator was already stopped%s", VTY_NEWLINE);
200 return CMD_WARNING;
201 }
202 osmo_timerfd_disable(&ntg->timerfd);
203 ntg->running = false;
204 }
205
206 return CMD_SUCCESS;
207}
208
209DEFUN(ntg_nsei, ntg_nsei_cmd,
210 "nsei <0-65535>",
211 "NSEI to use when generating traffic\n"
212 "NSEI to use when generating traffic\n")
213{
214 struct ns_traf_gen *ntg = vty->index;
215 ntg->cfg.nsei = atoi(argv[0]);
216 return CMD_SUCCESS;
217}
218
219DEFUN(ntg_bvci, ntg_bvci_cmd,
220 "bvci <0-65535>",
221 "BVCI to use when generating traffic\n"
222 "BVCI to use when generating traffic\n")
223{
224 struct ns_traf_gen *ntg = vty->index;
225 ntg->cfg.bvci = atoi(argv[0]);
226 return CMD_SUCCESS;
227}
228
229DEFUN(ntg_pkt_size, ntg_pkt_size_cmd,
230 "packet-size <0-2048>",
231 "Packet size for generated NS-UNITDATA payload\n"
232 "Packet size for generated NS-UNITDATA payload\n")
233{
234 struct ns_traf_gen *ntg = vty->index;
235 ntg->cfg.pkt_size = atoi(argv[0]);
236 return CMD_SUCCESS;
237}
238
239DEFUN(ntg_pkt_intv_us, ntg_pkt_intv_us_cmd,
240 "interval-us <0-1000000>",
241 "Interval between packets in microseconds\n"
242 "Interval between packets in microseconds\n")
243{
244 struct ns_traf_gen *ntg = vty->index;
245 ntg->cfg.interval_us = atoi(argv[0]);
246 if (ntg->running) {
247 /* TODO: update timer */
248 }
249 return CMD_SUCCESS;
250}
251
252DEFUN(ntg_lsp, ntg_lsp_cmd,
253 "lsp <0-4294967295>",
254 "Link Selector Parameter (only used in fixed mode)\n"
255 "Link Selector Parameter (only used in fixed mode)\n")
256{
257 struct ns_traf_gen *ntg = vty->index;
258 ntg->cfg.lsp = strtoul(argv[0], NULL, 10);
259 return CMD_SUCCESS;
260}
261
262DEFUN(ntg_lsp_mode, ntg_lsp_mode_cmd,
263 "lsp-mode (fixed|randomized)",
264 "Link Selector Parameter Mode\n"
265 "Fixed / Staic LSP\n"
266 "Randomized LSP\n")
267{
268 struct ns_traf_gen *ntg = vty->index;
269 if (!strcmp(argv[0], "randomized"))
270 ntg->cfg.lsp_randomize = true;
271 else
272 ntg->cfg.lsp_randomize = false;
273 return CMD_SUCCESS;
274}
275
276DEFUN(gen_traffic, gen_traffic_cmd,
277 "ns-traffic-generator NAME",
278 "Configure a given NS traffic generator\n" "Name of NS traffic generator\n")
279{
280 struct ns_traf_gen *ntg = ns_traf_gen_find_or_alloc(argv[0]);
281
282 if (!ntg)
283 return CMD_WARNING;
284
285 vty->index = ntg;
286 vty->node = NTG_NODE;
287
288 return CMD_SUCCESS;
289}
290
291int nsdummy_vty_init(struct gprs_ns2_inst *nsi)
292{
293 g_nsi = nsi;
294
295 /* configuration of traffic generators via CONFIG / NTG node */
296 install_element(CONFIG_NODE, &gen_traffic_cmd);
297 install_node(&ntg_node, config_write_ntg);
298 install_element(NTG_NODE, &ntg_nsei_cmd);
299 install_element(NTG_NODE, &ntg_bvci_cmd);
300 install_element(NTG_NODE, &ntg_pkt_size_cmd);
301 install_element(NTG_NODE, &ntg_pkt_intv_us_cmd);
302 install_element(NTG_NODE, &ntg_lsp_cmd);
303 install_element(NTG_NODE, &ntg_lsp_mode_cmd);
304
305 /* starting/stopping the traffic generators is in 'enable' mode, not 'config' */
306 install_element(ENABLE_NODE, &ntg_start_stop_cmd);
307
308 return 0;
309}