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