1/*
2 * Copyright (C) 2013-2014 Michio Honda. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *   1. Redistributions of source code must retain the above copyright
8 *      notice, this list of conditions and the following disclaimer.
9 *   2. Redistributions in binary form must reproduce the above copyright
10 *      notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26/* $FreeBSD: stable/11/tools/tools/netmap/vale-ctl.c 341432 2018-12-03 17:46:53Z vmaffione $ */
27
28#define NETMAP_WITH_LIBS
29#include <net/netmap_user.h>
30#include <net/netmap.h>
31
32#include <errno.h>
33#include <stdio.h>
34#include <inttypes.h>	/* PRI* macros */
35#include <string.h>	/* strcmp */
36#include <fcntl.h>	/* open */
37#include <unistd.h>	/* close */
38#include <sys/ioctl.h>	/* ioctl */
39#include <sys/param.h>
40#include <sys/socket.h>	/* apple needs sockaddr */
41#include <net/if.h>	/* ifreq */
42#include <libgen.h>	/* basename */
43#include <stdlib.h>	/* atoi, free */
44
45/* XXX cut and paste from pkt-gen.c because I'm not sure whether this
46 * program may include nm_util.h
47 */
48void parse_nmr_config(const char* conf, struct nmreq *nmr)
49{
50	char *w, *tok;
51	int i, v;
52
53	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
54	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
55	if (conf == NULL || ! *conf)
56		return;
57	w = strdup(conf);
58	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
59		v = atoi(tok);
60		switch (i) {
61		case 0:
62			nmr->nr_tx_slots = nmr->nr_rx_slots = v;
63			break;
64		case 1:
65			nmr->nr_rx_slots = v;
66			break;
67		case 2:
68			nmr->nr_tx_rings = nmr->nr_rx_rings = v;
69			break;
70		case 3:
71			nmr->nr_rx_rings = v;
72			break;
73		default:
74			D("ignored config: %s", tok);
75			break;
76		}
77	}
78	D("txr %d txd %d rxr %d rxd %d",
79			nmr->nr_tx_rings, nmr->nr_tx_slots,
80			nmr->nr_rx_rings, nmr->nr_rx_slots);
81	free(w);
82}
83
84static int
85bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config, int nr_arg2)
86{
87	struct nmreq nmr;
88	int error = 0;
89	int fd = open("/dev/netmap", O_RDWR);
90
91	if (fd == -1) {
92		D("Unable to open /dev/netmap");
93		return -1;
94	}
95
96	bzero(&nmr, sizeof(nmr));
97	nmr.nr_version = NETMAP_API;
98	if (name != NULL) /* might be NULL */
99		strncpy(nmr.nr_name, name, sizeof(nmr.nr_name)-1);
100	nmr.nr_cmd = nr_cmd;
101	parse_nmr_config(nmr_config, &nmr);
102	nmr.nr_arg2 = nr_arg2;
103
104	switch (nr_cmd) {
105	case NETMAP_BDG_DELIF:
106	case NETMAP_BDG_NEWIF:
107		error = ioctl(fd, NIOCREGIF, &nmr);
108		if (error == -1) {
109			ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
110			perror(name);
111		} else {
112			ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
113		}
114		break;
115	case NETMAP_BDG_ATTACH:
116	case NETMAP_BDG_DETACH:
117		nmr.nr_flags = NR_REG_ALL_NIC;
118		if (nr_arg && nr_arg != NETMAP_BDG_HOST) {
119			nmr.nr_flags = NR_REG_NIC_SW;
120			nr_arg = 0;
121		}
122		nmr.nr_arg1 = nr_arg;
123		error = ioctl(fd, NIOCREGIF, &nmr);
124		if (error == -1) {
125			ND("Unable to %s %s to the bridge", nr_cmd ==
126			    NETMAP_BDG_DETACH?"detach":"attach", name);
127			perror(name);
128		} else
129			ND("Success to %s %s to the bridge", nr_cmd ==
130			    NETMAP_BDG_DETACH?"detach":"attach", name);
131		break;
132
133	case NETMAP_BDG_LIST:
134		if (strlen(nmr.nr_name)) { /* name to bridge/port info */
135			error = ioctl(fd, NIOCGINFO, &nmr);
136			if (error) {
137				ND("Unable to obtain info for %s", name);
138				perror(name);
139			} else
140				D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
141				    nmr.nr_arg2);
142			break;
143		}
144
145		/* scan all the bridges and ports */
146		nmr.nr_arg1 = nmr.nr_arg2 = 0;
147		for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
148			D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
149			    nmr.nr_name);
150			nmr.nr_name[0] = '\0';
151		}
152
153		break;
154
155	case NETMAP_BDG_POLLING_ON:
156	case NETMAP_BDG_POLLING_OFF:
157		/* We reuse nmreq fields as follows:
158		 *   nr_tx_slots: 0 and non-zero indicate REG_ALL_NIC
159		 *                REG_ONE_NIC, respectively.
160		 *   nr_rx_slots: CPU core index. This also indicates the
161		 *                first queue in the case of REG_ONE_NIC
162		 *   nr_tx_rings: (REG_ONE_NIC only) indicates the
163		 *                number of CPU cores or the last queue
164		 */
165		nmr.nr_flags |= nmr.nr_tx_slots ?
166			NR_REG_ONE_NIC : NR_REG_ALL_NIC;
167		nmr.nr_ringid = nmr.nr_rx_slots;
168		/* number of cores/rings */
169		if (nmr.nr_flags == NR_REG_ALL_NIC)
170			nmr.nr_arg1 = 1;
171		else
172			nmr.nr_arg1 = nmr.nr_tx_rings;
173
174		error = ioctl(fd, NIOCREGIF, &nmr);
175		if (!error)
176			D("polling on %s %s", nmr.nr_name,
177				nr_cmd == NETMAP_BDG_POLLING_ON ?
178				"started" : "stopped");
179		else
180			D("polling on %s %s (err %d)", nmr.nr_name,
181				nr_cmd == NETMAP_BDG_POLLING_ON ?
182				"couldn't start" : "couldn't stop", error);
183		break;
184
185	default: /* GINFO */
186		nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
187		error = ioctl(fd, NIOCGINFO, &nmr);
188		if (error) {
189			ND("Unable to get if info for %s", name);
190			perror(name);
191		} else
192			D("%s: %d queues.", name, nmr.nr_rx_rings);
193		break;
194	}
195	close(fd);
196	return error;
197}
198
199static void
200usage(int errcode)
201{
202	fprintf(stderr,
203	    "Usage:\n"
204	    "vale-ctl arguments\n"
205	    "\t-g interface	interface name to get info\n"
206	    "\t-d interface	interface name to be detached\n"
207	    "\t-a interface	interface name to be attached\n"
208	    "\t-h interface	interface name to be attached with the host stack\n"
209	    "\t-n interface	interface name to be created\n"
210	    "\t-r interface	interface name to be deleted\n"
211	    "\t-l list all or specified bridge's interfaces (default)\n"
212	    "\t-C string ring/slot setting of an interface creating by -n\n"
213	    "\t-p interface start polling. Additional -C x,y,z configures\n"
214	    "\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
215	    "\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
216	    "\t\t z: (ONE_NIC only) num of total cores/rings\n"
217	    "\t-P interface stop polling\n"
218	    "\t-m memid to use when creating a new interface\n");
219	exit(errcode);
220}
221
222int
223main(int argc, char *argv[])
224{
225	int ch, nr_cmd = 0, nr_arg = 0;
226	char *name = NULL, *nmr_config = NULL;
227	int nr_arg2 = 0;
228
229	while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:")) != -1) {
230		if (ch != 'C' && ch != 'm')
231			name = optarg; /* default */
232		switch (ch) {
233		default:
234			fprintf(stderr, "bad option %c %s", ch, optarg);
235			usage(-1);
236			break;
237		case 'd':
238			nr_cmd = NETMAP_BDG_DETACH;
239			break;
240		case 'a':
241			nr_cmd = NETMAP_BDG_ATTACH;
242			break;
243		case 'h':
244			nr_cmd = NETMAP_BDG_ATTACH;
245			nr_arg = NETMAP_BDG_HOST;
246			break;
247		case 'n':
248			nr_cmd = NETMAP_BDG_NEWIF;
249			break;
250		case 'r':
251			nr_cmd = NETMAP_BDG_DELIF;
252			break;
253		case 'g':
254			nr_cmd = 0;
255			break;
256		case 'l':
257			nr_cmd = NETMAP_BDG_LIST;
258			break;
259		case 'C':
260			nmr_config = strdup(optarg);
261			break;
262		case 'p':
263			nr_cmd = NETMAP_BDG_POLLING_ON;
264			break;
265		case 'P':
266			nr_cmd = NETMAP_BDG_POLLING_OFF;
267			break;
268		case 'm':
269			nr_arg2 = atoi(optarg);
270			break;
271		}
272	}
273	if (optind != argc) {
274		// fprintf(stderr, "optind %d argc %d\n", optind, argc);
275		usage(-1);
276	}
277	if (argc == 1) {
278		nr_cmd = NETMAP_BDG_LIST;
279		name = NULL;
280	}
281	return bdg_ctl(name, nr_cmd, nr_arg, nmr_config, nr_arg2) ? 1 : 0;
282}
283