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