blob: 2e59b11b0c3c4ac2b35d27a5de0f796402b3ad2a [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
Harald Welte0b7614b2021-01-30 17:59:43 +010024#include <errno.h>
25#include <string.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <stdint.h>
29#include <inttypes.h>
30
31
Vadim Yanitskiybe5e8382022-04-23 02:16:07 +030032#include <osmocom/core/talloc.h>
Harald Welte0b7614b2021-01-30 17:59:43 +010033#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
Harald Welte9bbb7032021-01-31 16:44:46 +010049extern struct gprs_ns2_inst *g_nsi;
Harald Welte0b7614b2021-01-30 17:59:43 +010050static struct llist_head g_ns_traf_gens = LLIST_HEAD_INIT(g_ns_traf_gens);
Harald Welte9bbb7032021-01-31 16:44:46 +010051int g_mirror_mode;
Harald Welte0b7614b2021-01-30 17:59:43 +010052
53/* one NS traffic generator instance. You can have as many of these as you want,
54 * just as long as they have unique names */
55struct ns_traf_gen {
56 struct llist_head list;
57 const char *name;
58 struct {
59 uint16_t nsei;
60 uint16_t bvci;
61 /* size of each packet */
62 uint16_t pkt_size;
63 /* interval between packets in us */
64 uint32_t interval_us;
65 /* fixed (false) or random (true) LSP */
66 bool lsp_randomize;
67 /* (fixeD) Link Selector Parameter */
68 uint32_t lsp;
69 } cfg;
70 struct osmo_fd timerfd;
71 bool running;
72};
73
74#define LOGNTG(ntg, lvl, fmt, args ...) \
75 LOGP(DLGLOBAL, lvl, "traf-gen(%s): " fmt, (ntg)->name, ## args)
76
77/* allocate and transmit one NS message */
78static int ntg_tx_one(struct ns_traf_gen *ntg)
79{
80 struct osmo_gprs_ns2_prim nsp = {};
81 struct msgb *msg = msgb_alloc_headroom(3072, 20, "NS traffic gen");
82
83 if (!msg)
84 return -ENOMEM;
85 msgb_put(msg, ntg->cfg.pkt_size);
86 nsp.bvci = ntg->cfg.bvci;
87 nsp.nsei = ntg->cfg.nsei;
88 if (ntg->cfg.lsp_randomize)
89 nsp.u.unitdata.link_selector = rand();
90 else
91 nsp.u.unitdata.link_selector = ntg->cfg.lsp;
92 osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
93 return gprs_ns2_recv_prim(g_nsi, &nsp.oph);
94}
95
96/* call-back from transmit timer-fd */
97static int ntg_timerfd_cb(struct osmo_fd *ofd, unsigned int what)
98{
99 uint64_t expire_count;
100 struct ns_traf_gen *ntg = ofd->data;
101 unsigned int i;
102 int rc;
103
104 OSMO_ASSERT(what & OSMO_FD_READ);
105
106 rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
107 if (rc < 0 && errno == EAGAIN)
108 return 0;
109 OSMO_ASSERT(rc == sizeof(expire_count));
110
111 for (i = 0; i < expire_count; i++)
112 ntg_tx_one(ntg);
113
114 return 0;
115}
116
117static struct ns_traf_gen *ns_traf_gen_find(const char *name)
118{
119 struct ns_traf_gen *ntg;
120
121 llist_for_each_entry(ntg, &g_ns_traf_gens, list) {
122 if (!strcmp(ntg->name, name))
123 return ntg;
124 }
125 return NULL;
126}
127
128static struct ns_traf_gen *ns_traf_gen_find_or_alloc(const char *name)
129{
130 struct ns_traf_gen *ntg;
131 int rc;
132
133 ntg = ns_traf_gen_find(name);
134 if (ntg)
135 return ntg;
136
137 ntg = talloc_zero(g_nsi, struct ns_traf_gen);
138 OSMO_ASSERT(ntg);
139 ntg->name = talloc_strdup(ntg, name);
140 ntg->timerfd.fd = -1;
141 rc = osmo_timerfd_setup(&ntg->timerfd, ntg_timerfd_cb, ntg);
142 OSMO_ASSERT(rc >= 0);
143 llist_add_tail(&ntg->list, &g_ns_traf_gens);
144
145 return ntg;
146}
147
148enum nodes {
149 NTG_NODE = _LAST_OSMOVTY_NODE + 1,
150};
151
152static struct cmd_node ntg_node = {
153 NTG_NODE,
154 "%s(config-ns-traf-gen)# ",
155 1,
156};
157
158static int config_write_ntg(struct vty *vty)
159{
160 struct ns_traf_gen *ntg;
161
162 llist_for_each_entry(ntg, &g_ns_traf_gens, list) {
163 vty_out(vty, "ns-traffic-generator %s%s", ntg->name, VTY_NEWLINE);
164 vty_out(vty, " nsei %u%s", ntg->cfg.nsei, VTY_NEWLINE);
165 vty_out(vty, " bvci %u%s", ntg->cfg.bvci, VTY_NEWLINE);
166 vty_out(vty, " packet-size %u%s", ntg->cfg.pkt_size, VTY_NEWLINE);
167 vty_out(vty, " interval-us %u%s", ntg->cfg.interval_us, VTY_NEWLINE);
168 vty_out(vty, " lsp %u%s", ntg->cfg.lsp, VTY_NEWLINE);
169 vty_out(vty, " lsp-mode %s%s", ntg->cfg.lsp_randomize ? "randomized" : "fixed", VTY_NEWLINE);
170 }
Harald Welte9bbb7032021-01-31 16:44:46 +0100171 vty_out(vty, "mirror-mode %s%s", g_mirror_mode ? "enable" : "disable", VTY_NEWLINE);
Harald Welte0b7614b2021-01-30 17:59:43 +0100172
173 return 0;
174}
175
176DEFUN(ntg_start, ntg_start_stop_cmd,
177 "ns-traffic-generator (start|stop) NAME",
178 "Control named NS traffic generator\n"
179 "Start generating traffic in this traffic generator\n"
180 "Stop generating traffic in this traffic generator\n"
181 "Name of NS traffic generator to start\n")
182{
183 struct ns_traf_gen *ntg = ns_traf_gen_find(argv[1]);
184 if (!ntg) {
185 vty_out(vty, "NS Traffic generator '%s' doesn't exist%s", argv[1], VTY_NEWLINE);
186 return CMD_WARNING;
187 }
188
189 if (!strcmp(argv[0], "start")) {
190 struct timespec interval;
191 if (ntg->running) {
192 vty_out(vty, "NS Traffic generator was already started%s", VTY_NEWLINE);
193 return CMD_WARNING;
194 }
195 interval.tv_sec = ntg->cfg.interval_us / 1000000;
196 interval.tv_nsec = (ntg->cfg.interval_us % 1000000) * 1000;
197 osmo_timerfd_schedule(&ntg->timerfd, NULL, &interval);
198 ntg->running = true;
199 } else {
200 if (!ntg->running) {
201 vty_out(vty, "NS Traffic generator was already stopped%s", VTY_NEWLINE);
202 return CMD_WARNING;
203 }
204 osmo_timerfd_disable(&ntg->timerfd);
205 ntg->running = false;
206 }
207
208 return CMD_SUCCESS;
209}
210
211DEFUN(ntg_nsei, ntg_nsei_cmd,
212 "nsei <0-65535>",
213 "NSEI to use when generating traffic\n"
214 "NSEI to use when generating traffic\n")
215{
216 struct ns_traf_gen *ntg = vty->index;
217 ntg->cfg.nsei = atoi(argv[0]);
218 return CMD_SUCCESS;
219}
220
221DEFUN(ntg_bvci, ntg_bvci_cmd,
222 "bvci <0-65535>",
223 "BVCI to use when generating traffic\n"
224 "BVCI to use when generating traffic\n")
225{
226 struct ns_traf_gen *ntg = vty->index;
227 ntg->cfg.bvci = atoi(argv[0]);
228 return CMD_SUCCESS;
229}
230
231DEFUN(ntg_pkt_size, ntg_pkt_size_cmd,
232 "packet-size <0-2048>",
233 "Packet size for generated NS-UNITDATA payload\n"
234 "Packet size for generated NS-UNITDATA payload\n")
235{
236 struct ns_traf_gen *ntg = vty->index;
237 ntg->cfg.pkt_size = atoi(argv[0]);
238 return CMD_SUCCESS;
239}
240
241DEFUN(ntg_pkt_intv_us, ntg_pkt_intv_us_cmd,
242 "interval-us <0-1000000>",
243 "Interval between packets in microseconds\n"
244 "Interval between packets in microseconds\n")
245{
246 struct ns_traf_gen *ntg = vty->index;
247 ntg->cfg.interval_us = atoi(argv[0]);
248 if (ntg->running) {
249 /* TODO: update timer */
250 }
251 return CMD_SUCCESS;
252}
253
254DEFUN(ntg_lsp, ntg_lsp_cmd,
255 "lsp <0-4294967295>",
256 "Link Selector Parameter (only used in fixed mode)\n"
257 "Link Selector Parameter (only used in fixed mode)\n")
258{
259 struct ns_traf_gen *ntg = vty->index;
260 ntg->cfg.lsp = strtoul(argv[0], NULL, 10);
261 return CMD_SUCCESS;
262}
263
264DEFUN(ntg_lsp_mode, ntg_lsp_mode_cmd,
265 "lsp-mode (fixed|randomized)",
266 "Link Selector Parameter Mode\n"
267 "Fixed / Staic LSP\n"
268 "Randomized LSP\n")
269{
270 struct ns_traf_gen *ntg = vty->index;
271 if (!strcmp(argv[0], "randomized"))
272 ntg->cfg.lsp_randomize = true;
273 else
274 ntg->cfg.lsp_randomize = false;
275 return CMD_SUCCESS;
276}
277
278DEFUN(gen_traffic, gen_traffic_cmd,
279 "ns-traffic-generator NAME",
280 "Configure a given NS traffic generator\n" "Name of NS traffic generator\n")
281{
282 struct ns_traf_gen *ntg = ns_traf_gen_find_or_alloc(argv[0]);
283
284 if (!ntg)
285 return CMD_WARNING;
286
287 vty->index = ntg;
288 vty->node = NTG_NODE;
289
290 return CMD_SUCCESS;
291}
292
Harald Welte9bbb7032021-01-31 16:44:46 +0100293DEFUN(mirror_mode, mirror_mode_cmd,
294 "mirror-mode (enable|disable)",
295 "Configure mirroring of incoming NS-UNITDATA\n"
296 "Enable mirroring of incoming NS-UNITDATA\n"
297 "Disable mirroring of incoming NS-UNITDATA\n")
Harald Welte0b7614b2021-01-30 17:59:43 +0100298{
Harald Welte9bbb7032021-01-31 16:44:46 +0100299 if (!strcmp(argv[0], "enable"))
300 g_mirror_mode = true;
301 else
302 g_mirror_mode = false;
Harald Welte0b7614b2021-01-30 17:59:43 +0100303
Harald Welte9bbb7032021-01-31 16:44:46 +0100304 return CMD_SUCCESS;
305}
306
307
308int nsdummy_vty_init(void)
309{
Harald Welte0b7614b2021-01-30 17:59:43 +0100310 /* configuration of traffic generators via CONFIG / NTG node */
311 install_element(CONFIG_NODE, &gen_traffic_cmd);
Harald Welte9bbb7032021-01-31 16:44:46 +0100312 install_element(CONFIG_NODE, &mirror_mode_cmd);
Harald Welte0b7614b2021-01-30 17:59:43 +0100313 install_node(&ntg_node, config_write_ntg);
314 install_element(NTG_NODE, &ntg_nsei_cmd);
315 install_element(NTG_NODE, &ntg_bvci_cmd);
316 install_element(NTG_NODE, &ntg_pkt_size_cmd);
317 install_element(NTG_NODE, &ntg_pkt_intv_us_cmd);
318 install_element(NTG_NODE, &ntg_lsp_cmd);
319 install_element(NTG_NODE, &ntg_lsp_mode_cmd);
320
321 /* starting/stopping the traffic generators is in 'enable' mode, not 'config' */
322 install_element(ENABLE_NODE, &ntg_start_stop_cmd);
323
324 return 0;
325}