xref: /freebsd/sys/dev/netmap/netmap_legacy.c (revision 4d3fc8b0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2018 Vincenzo Maffione
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /* $FreeBSD$ */
30 
31 #if defined(__FreeBSD__)
32 #include <sys/cdefs.h> /* prerequisite */
33 #include <sys/types.h>
34 #include <sys/param.h>	/* defines used in kernel.h */
35 #include <sys/filio.h>	/* FIONBIO */
36 #include <sys/malloc.h>
37 #include <sys/socketvar.h>	/* struct socket */
38 #include <sys/socket.h> /* sockaddrs */
39 #include <sys/sysctl.h>
40 #include <net/if.h>
41 #include <net/if_var.h>
42 #include <net/bpf.h>		/* BIOCIMMEDIATE */
43 #include <machine/bus.h>	/* bus_dmamap_* */
44 #include <sys/endian.h>
45 #elif defined(linux)
46 #include "bsd_glue.h"
47 #elif defined(__APPLE__)
48 #warning OSX support is only partial
49 #include "osx_glue.h"
50 #elif defined (_WIN32)
51 #include "win_glue.h"
52 #endif
53 
54 /*
55  * common headers
56  */
57 #include <net/netmap.h>
58 #include <dev/netmap/netmap_kern.h>
59 #include <dev/netmap/netmap_bdg.h>
60 
61 static int
62 nmreq_register_from_legacy(struct nmreq *nmr, struct nmreq_header *hdr,
63 				struct nmreq_register *req)
64 {
65 	req->nr_offset = nmr->nr_offset;
66 	req->nr_memsize = nmr->nr_memsize;
67 	req->nr_tx_slots = nmr->nr_tx_slots;
68 	req->nr_rx_slots = nmr->nr_rx_slots;
69 	req->nr_tx_rings = nmr->nr_tx_rings;
70 	req->nr_rx_rings = nmr->nr_rx_rings;
71 	req->nr_host_tx_rings = 0;
72 	req->nr_host_rx_rings = 0;
73 	req->nr_mem_id = nmr->nr_arg2;
74 	req->nr_ringid = nmr->nr_ringid & NETMAP_RING_MASK;
75 	if ((nmr->nr_flags & NR_REG_MASK) == NR_REG_DEFAULT) {
76 		/* Convert the older nmr->nr_ringid (original
77 		 * netmap control API) to nmr->nr_flags. */
78 		u_int regmode = NR_REG_DEFAULT;
79 		if (nmr->nr_ringid & NETMAP_SW_RING) {
80 			regmode = NR_REG_SW;
81 		} else if (nmr->nr_ringid & NETMAP_HW_RING) {
82 			regmode = NR_REG_ONE_NIC;
83 		} else {
84 			regmode = NR_REG_ALL_NIC;
85 		}
86 		req->nr_mode = regmode;
87 	} else {
88 		req->nr_mode = nmr->nr_flags & NR_REG_MASK;
89 	}
90 
91 	/* Fix nr_name, nr_mode and nr_ringid to handle pipe requests. */
92 	if (req->nr_mode == NR_REG_PIPE_MASTER ||
93 			req->nr_mode == NR_REG_PIPE_SLAVE) {
94 		char suffix[10];
95 		snprintf(suffix, sizeof(suffix), "%c%d",
96 			(req->nr_mode == NR_REG_PIPE_MASTER ? '{' : '}'),
97 			req->nr_ringid);
98 		if (strlen(hdr->nr_name) + strlen(suffix)
99 					>= sizeof(hdr->nr_name)) {
100 			/* No space for the pipe suffix. */
101 			return ENOBUFS;
102 		}
103 		strlcat(hdr->nr_name, suffix, sizeof(hdr->nr_name));
104 		req->nr_mode = NR_REG_ALL_NIC;
105 		req->nr_ringid = 0;
106 	}
107 	req->nr_flags = nmr->nr_flags & (~NR_REG_MASK);
108 	if (nmr->nr_ringid & NETMAP_NO_TX_POLL) {
109 		req->nr_flags |= NR_NO_TX_POLL;
110 	}
111 	if (nmr->nr_ringid & NETMAP_DO_RX_POLL) {
112 		req->nr_flags |= NR_DO_RX_POLL;
113 	}
114 	/* nmr->nr_arg1 (nr_pipes) ignored */
115 	req->nr_extra_bufs = nmr->nr_arg3;
116 
117 	return 0;
118 }
119 
120 /* Convert the legacy 'nmr' struct into one of the nmreq_xyz structs
121  * (new API). The new struct is dynamically allocated. */
122 static struct nmreq_header *
123 nmreq_from_legacy(struct nmreq *nmr, u_long ioctl_cmd)
124 {
125 	struct nmreq_header *hdr = nm_os_malloc(sizeof(*hdr));
126 
127 	if (hdr == NULL) {
128 		goto oom;
129 	}
130 
131 	/* Sanitize nmr->nr_name by adding the string terminator. */
132 	if (ioctl_cmd == NIOCGINFO || ioctl_cmd == NIOCREGIF) {
133 		nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';
134 	}
135 
136 	/* First prepare the request header. */
137 	hdr->nr_version = NETMAP_API; /* new API */
138 	strlcpy(hdr->nr_name, nmr->nr_name, sizeof(nmr->nr_name));
139 	hdr->nr_options = (uintptr_t)NULL;
140 	hdr->nr_body = (uintptr_t)NULL;
141 
142 	switch (ioctl_cmd) {
143 	case NIOCREGIF: {
144 		switch (nmr->nr_cmd) {
145 		case 0: {
146 			/* Regular NIOCREGIF operation. */
147 			struct nmreq_register *req = nm_os_malloc(sizeof(*req));
148 			if (!req) { goto oom; }
149 			hdr->nr_body = (uintptr_t)req;
150 			hdr->nr_reqtype = NETMAP_REQ_REGISTER;
151 			if (nmreq_register_from_legacy(nmr, hdr, req)) {
152 				goto oom;
153 			}
154 			break;
155 		}
156 		case NETMAP_BDG_ATTACH: {
157 			struct nmreq_vale_attach *req = nm_os_malloc(sizeof(*req));
158 			if (!req) { goto oom; }
159 			hdr->nr_body = (uintptr_t)req;
160 			hdr->nr_reqtype = NETMAP_REQ_VALE_ATTACH;
161 			if (nmreq_register_from_legacy(nmr, hdr, &req->reg)) {
162 				goto oom;
163 			}
164 			/* Fix nr_mode, starting from nr_arg1. */
165 			if (nmr->nr_arg1 & NETMAP_BDG_HOST) {
166 				req->reg.nr_mode = NR_REG_NIC_SW;
167 			} else {
168 				req->reg.nr_mode = NR_REG_ALL_NIC;
169 			}
170 			break;
171 		}
172 		case NETMAP_BDG_DETACH: {
173 			hdr->nr_reqtype = NETMAP_REQ_VALE_DETACH;
174 			hdr->nr_body = (uintptr_t)nm_os_malloc(sizeof(struct nmreq_vale_detach));
175 			break;
176 		}
177 		case NETMAP_BDG_VNET_HDR:
178 		case NETMAP_VNET_HDR_GET: {
179 			struct nmreq_port_hdr *req = nm_os_malloc(sizeof(*req));
180 			if (!req) { goto oom; }
181 			hdr->nr_body = (uintptr_t)req;
182 			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_VNET_HDR) ?
183 				NETMAP_REQ_PORT_HDR_SET : NETMAP_REQ_PORT_HDR_GET;
184 			req->nr_hdr_len = nmr->nr_arg1;
185 			break;
186 		}
187 		case NETMAP_BDG_NEWIF : {
188 			struct nmreq_vale_newif *req = nm_os_malloc(sizeof(*req));
189 			if (!req) { goto oom; }
190 			hdr->nr_body = (uintptr_t)req;
191 			hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF;
192 			req->nr_tx_slots = nmr->nr_tx_slots;
193 			req->nr_rx_slots = nmr->nr_rx_slots;
194 			req->nr_tx_rings = nmr->nr_tx_rings;
195 			req->nr_rx_rings = nmr->nr_rx_rings;
196 			req->nr_mem_id = nmr->nr_arg2;
197 			break;
198 		}
199 		case NETMAP_BDG_DELIF: {
200 			hdr->nr_reqtype = NETMAP_REQ_VALE_DELIF;
201 			break;
202 		}
203 		case NETMAP_BDG_POLLING_ON:
204 		case NETMAP_BDG_POLLING_OFF: {
205 			struct nmreq_vale_polling *req = nm_os_malloc(sizeof(*req));
206 			if (!req) { goto oom; }
207 			hdr->nr_body = (uintptr_t)req;
208 			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_POLLING_ON) ?
209 				NETMAP_REQ_VALE_POLLING_ENABLE :
210 				NETMAP_REQ_VALE_POLLING_DISABLE;
211 			switch (nmr->nr_flags & NR_REG_MASK) {
212 			default:
213 				req->nr_mode = 0; /* invalid */
214 				break;
215 			case NR_REG_ONE_NIC:
216 				req->nr_mode = NETMAP_POLLING_MODE_MULTI_CPU;
217 				break;
218 			case NR_REG_ALL_NIC:
219 				req->nr_mode = NETMAP_POLLING_MODE_SINGLE_CPU;
220 				break;
221 			}
222 			req->nr_first_cpu_id = nmr->nr_ringid & NETMAP_RING_MASK;
223 			req->nr_num_polling_cpus = nmr->nr_arg1;
224 			break;
225 		}
226 		case NETMAP_PT_HOST_CREATE:
227 		case NETMAP_PT_HOST_DELETE: {
228 			nm_prerr("Netmap passthrough not supported yet");
229 			return NULL;
230 			break;
231 		}
232 		}
233 		break;
234 	}
235 	case NIOCGINFO: {
236 		if (nmr->nr_cmd == NETMAP_BDG_LIST) {
237 			struct nmreq_vale_list *req = nm_os_malloc(sizeof(*req));
238 			if (!req) { goto oom; }
239 			hdr->nr_body = (uintptr_t)req;
240 			hdr->nr_reqtype = NETMAP_REQ_VALE_LIST;
241 			req->nr_bridge_idx = nmr->nr_arg1;
242 			req->nr_port_idx = nmr->nr_arg2;
243 		} else {
244 			/* Regular NIOCGINFO. */
245 			struct nmreq_port_info_get *req = nm_os_malloc(sizeof(*req));
246 			if (!req) { goto oom; }
247 			hdr->nr_body = (uintptr_t)req;
248 			hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
249 			req->nr_memsize = nmr->nr_memsize;
250 			req->nr_tx_slots = nmr->nr_tx_slots;
251 			req->nr_rx_slots = nmr->nr_rx_slots;
252 			req->nr_tx_rings = nmr->nr_tx_rings;
253 			req->nr_rx_rings = nmr->nr_rx_rings;
254 			req->nr_host_tx_rings = 0;
255 			req->nr_host_rx_rings = 0;
256 			req->nr_mem_id = nmr->nr_arg2;
257 		}
258 		break;
259 	}
260 	}
261 
262 	return hdr;
263 oom:
264 	if (hdr) {
265 		if (hdr->nr_body) {
266 			nm_os_free((void *)(uintptr_t)hdr->nr_body);
267 		}
268 		nm_os_free(hdr);
269 	}
270 	nm_prerr("Failed to allocate memory for nmreq_xyz struct");
271 
272 	return NULL;
273 }
274 
275 static void
276 nmreq_register_to_legacy(const struct nmreq_register *req, struct nmreq *nmr)
277 {
278 	nmr->nr_offset = req->nr_offset;
279 	nmr->nr_memsize = req->nr_memsize;
280 	nmr->nr_tx_slots = req->nr_tx_slots;
281 	nmr->nr_rx_slots = req->nr_rx_slots;
282 	nmr->nr_tx_rings = req->nr_tx_rings;
283 	nmr->nr_rx_rings = req->nr_rx_rings;
284 	nmr->nr_arg2 = req->nr_mem_id;
285 	nmr->nr_arg3 = req->nr_extra_bufs;
286 }
287 
288 /* Convert a nmreq_xyz struct (new API) to the legacy 'nmr' struct.
289  * It also frees the nmreq_xyz struct, as it was allocated by
290  * nmreq_from_legacy(). */
291 static int
292 nmreq_to_legacy(struct nmreq_header *hdr, struct nmreq *nmr)
293 {
294 	int ret = 0;
295 
296 	/* We only write-back the fields that the user expects to be
297 	 * written back. */
298 	switch (hdr->nr_reqtype) {
299 	case NETMAP_REQ_REGISTER: {
300 		struct nmreq_register *req =
301 			(struct nmreq_register *)(uintptr_t)hdr->nr_body;
302 		nmreq_register_to_legacy(req, nmr);
303 		break;
304 	}
305 	case NETMAP_REQ_PORT_INFO_GET: {
306 		struct nmreq_port_info_get *req =
307 			(struct nmreq_port_info_get *)(uintptr_t)hdr->nr_body;
308 		nmr->nr_memsize = req->nr_memsize;
309 		nmr->nr_tx_slots = req->nr_tx_slots;
310 		nmr->nr_rx_slots = req->nr_rx_slots;
311 		nmr->nr_tx_rings = req->nr_tx_rings;
312 		nmr->nr_rx_rings = req->nr_rx_rings;
313 		nmr->nr_arg2 = req->nr_mem_id;
314 		break;
315 	}
316 	case NETMAP_REQ_VALE_ATTACH: {
317 		struct nmreq_vale_attach *req =
318 			(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
319 		nmreq_register_to_legacy(&req->reg, nmr);
320 		break;
321 	}
322 	case NETMAP_REQ_VALE_DETACH: {
323 		break;
324 	}
325 	case NETMAP_REQ_VALE_LIST: {
326 		struct nmreq_vale_list *req =
327 			(struct nmreq_vale_list *)(uintptr_t)hdr->nr_body;
328 		strlcpy(nmr->nr_name, hdr->nr_name, sizeof(nmr->nr_name));
329 		nmr->nr_arg1 = req->nr_bridge_idx;
330 		nmr->nr_arg2 = req->nr_port_idx;
331 		break;
332 	}
333 	case NETMAP_REQ_PORT_HDR_SET:
334 	case NETMAP_REQ_PORT_HDR_GET: {
335 		struct nmreq_port_hdr *req =
336 			(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
337 		nmr->nr_arg1 = req->nr_hdr_len;
338 		break;
339 	}
340 	case NETMAP_REQ_VALE_NEWIF: {
341 		struct nmreq_vale_newif *req =
342 			(struct nmreq_vale_newif *)(uintptr_t)hdr->nr_body;
343 		nmr->nr_tx_slots = req->nr_tx_slots;
344 		nmr->nr_rx_slots = req->nr_rx_slots;
345 		nmr->nr_tx_rings = req->nr_tx_rings;
346 		nmr->nr_rx_rings = req->nr_rx_rings;
347 		nmr->nr_arg2 = req->nr_mem_id;
348 		break;
349 	}
350 	case NETMAP_REQ_VALE_DELIF:
351 	case NETMAP_REQ_VALE_POLLING_ENABLE:
352 	case NETMAP_REQ_VALE_POLLING_DISABLE: {
353 		break;
354 	}
355 	}
356 
357 	return ret;
358 }
359 
360 int
361 netmap_ioctl_legacy(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
362 			struct thread *td)
363 {
364 	int error = 0;
365 
366 	switch (cmd) {
367 	case NIOCGINFO:
368 	case NIOCREGIF: {
369 		/* Request for the legacy control API. Convert it to a
370 		 * NIOCCTRL request. */
371 		struct nmreq *nmr = (struct nmreq *) data;
372 		struct nmreq_header *hdr;
373 
374 		if (nmr->nr_version < 14) {
375 			nm_prerr("Minimum supported API is 14 (requested %u)",
376 			    nmr->nr_version);
377 			return EINVAL;
378 		}
379 		hdr = nmreq_from_legacy(nmr, cmd);
380 		if (hdr == NULL) { /* out of memory */
381 			return ENOMEM;
382 		}
383 		error = netmap_ioctl(priv, NIOCCTRL, (caddr_t)hdr, td,
384 					/*nr_body_is_user=*/0);
385 		if (error == 0) {
386 			nmreq_to_legacy(hdr, nmr);
387 		}
388 		if (hdr->nr_body) {
389 			nm_os_free((void *)(uintptr_t)hdr->nr_body);
390 		}
391 		nm_os_free(hdr);
392 		break;
393 	}
394 #ifdef WITH_VALE
395 	case NIOCCONFIG: {
396 		struct nm_ifreq *nr = (struct nm_ifreq *)data;
397 		error = netmap_bdg_config(nr);
398 		break;
399 	}
400 #endif
401 #ifdef __FreeBSD__
402 	case FIONBIO:
403 	case FIOASYNC:
404 		/* FIONBIO/FIOASYNC are no-ops. */
405 		break;
406 
407 	case BIOCIMMEDIATE:
408 	case BIOCGHDRCMPLT:
409 	case BIOCSHDRCMPLT:
410 	case BIOCSSEESENT:
411 		/* Ignore these commands. */
412 		break;
413 
414 	default:	/* allow device-specific ioctls */
415 	    {
416 		struct nmreq *nmr = (struct nmreq *)data;
417 		if_t ifp = ifunit_ref(nmr->nr_name);
418 		if (ifp == NULL) {
419 			error = ENXIO;
420 		} else {
421 			struct socket so;
422 
423 			bzero(&so, sizeof(so));
424 			so.so_vnet = if_getvnet(ifp);
425 			// so->so_proto not null.
426 			error = ifioctl(&so, cmd, data, td);
427 			if_rele(ifp);
428 		}
429 		break;
430 	    }
431 
432 #else /* linux */
433 	default:
434 		error = EOPNOTSUPP;
435 #endif /* linux */
436 	}
437 
438 	return error;
439 }
440