xref: /freebsd/sys/dev/netmap/netmap_legacy.c (revision 4f52dfbb)
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 
60 static int
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_mem_id = nmr->nr_arg2;
71 	req->nr_ringid = nmr->nr_ringid & NETMAP_RING_MASK;
72 	if ((nmr->nr_flags & NR_REG_MASK) == NR_REG_DEFAULT) {
73 		/* Convert the older nmr->nr_ringid (original
74 		 * netmap control API) to nmr->nr_flags. */
75 		u_int regmode = NR_REG_DEFAULT;
76 		if (req->nr_ringid & NETMAP_SW_RING) {
77 			regmode = NR_REG_SW;
78 		} else if (req->nr_ringid & NETMAP_HW_RING) {
79 			regmode = NR_REG_ONE_NIC;
80 		} else {
81 			regmode = NR_REG_ALL_NIC;
82 		}
83 		nmr->nr_flags = regmode |
84 			(nmr->nr_flags & (~NR_REG_MASK));
85 	}
86 	req->nr_mode = nmr->nr_flags & NR_REG_MASK;
87 	/* Fix nr_name, nr_mode and nr_ringid to handle pipe requests. */
88 	if (req->nr_mode == NR_REG_PIPE_MASTER ||
89 			req->nr_mode == NR_REG_PIPE_SLAVE) {
90 		char suffix[10];
91 		snprintf(suffix, sizeof(suffix), "%c%d",
92 			(req->nr_mode == NR_REG_PIPE_MASTER ? '{' : '}'),
93 			req->nr_ringid);
94 		if (strlen(hdr->nr_name) + strlen(suffix)
95 					>= sizeof(hdr->nr_name)) {
96 			/* No space for the pipe suffix. */
97 			return ENOBUFS;
98 		}
99 		strncat(hdr->nr_name, suffix, strlen(suffix));
100 		req->nr_mode = NR_REG_ALL_NIC;
101 		req->nr_ringid = 0;
102 	}
103 	req->nr_flags = nmr->nr_flags & (~NR_REG_MASK);
104 	if (nmr->nr_ringid & NETMAP_NO_TX_POLL) {
105 		req->nr_flags |= NR_NO_TX_POLL;
106 	}
107 	if (nmr->nr_ringid & NETMAP_DO_RX_POLL) {
108 		req->nr_flags |= NR_DO_RX_POLL;
109 	}
110 	/* nmr->nr_arg1 (nr_pipes) ignored */
111 	req->nr_extra_bufs = nmr->nr_arg3;
112 
113 	return 0;
114 }
115 
116 /* Convert the legacy 'nmr' struct into one of the nmreq_xyz structs
117  * (new API). The new struct is dynamically allocated. */
118 static struct nmreq_header *
119 nmreq_from_legacy(struct nmreq *nmr, u_long ioctl_cmd)
120 {
121 	struct nmreq_header *hdr = nm_os_malloc(sizeof(*hdr));
122 
123 	if (hdr == NULL) {
124 		goto oom;
125 	}
126 
127 	/* Sanitize nmr->nr_name by adding the string terminator. */
128 	if (ioctl_cmd == NIOCGINFO || ioctl_cmd == NIOCREGIF) {
129 		nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';
130 	}
131 
132 	/* First prepare the request header. */
133 	hdr->nr_version = NETMAP_API; /* new API */
134 	strncpy(hdr->nr_name, nmr->nr_name, sizeof(nmr->nr_name));
135 	hdr->nr_options = (uint64_t)NULL;
136 	hdr->nr_body = (uint64_t)NULL;
137 
138 	switch (ioctl_cmd) {
139 	case NIOCREGIF: {
140 		switch (nmr->nr_cmd) {
141 		case 0: {
142 			/* Regular NIOCREGIF operation. */
143 			struct nmreq_register *req = nm_os_malloc(sizeof(*req));
144 			if (!req) { goto oom; }
145 			hdr->nr_body = (uint64_t)req;
146 			hdr->nr_reqtype = NETMAP_REQ_REGISTER;
147 			if (nmreq_register_from_legacy(nmr, hdr, req)) {
148 				goto oom;
149 			}
150 			break;
151 		}
152 		case NETMAP_BDG_ATTACH: {
153 			struct nmreq_vale_attach *req = nm_os_malloc(sizeof(*req));
154 			if (!req) { goto oom; }
155 			hdr->nr_body = (uint64_t)req;
156 			hdr->nr_reqtype = NETMAP_REQ_VALE_ATTACH;
157 			if (nmreq_register_from_legacy(nmr, hdr, &req->reg)) {
158 				goto oom;
159 			}
160 			/* Fix nr_mode, starting from nr_arg1. */
161 			if (nmr->nr_arg1 & NETMAP_BDG_HOST) {
162 				req->reg.nr_mode = NR_REG_NIC_SW;
163 			} else {
164 				req->reg.nr_mode = NR_REG_ALL_NIC;
165 			}
166 			break;
167 		}
168 		case NETMAP_BDG_DETACH: {
169 			hdr->nr_reqtype = NETMAP_REQ_VALE_DETACH;
170 			hdr->nr_body = (uint64_t)nm_os_malloc(sizeof(struct nmreq_vale_detach));
171 			break;
172 		}
173 		case NETMAP_BDG_VNET_HDR:
174 		case NETMAP_VNET_HDR_GET: {
175 			struct nmreq_port_hdr *req = nm_os_malloc(sizeof(*req));
176 			if (!req) { goto oom; }
177 			hdr->nr_body = (uint64_t)req;
178 			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_VNET_HDR) ?
179 				NETMAP_REQ_PORT_HDR_SET : NETMAP_REQ_PORT_HDR_GET;
180 			req->nr_hdr_len = nmr->nr_arg1;
181 			break;
182 		}
183 		case NETMAP_BDG_NEWIF : {
184 			struct nmreq_vale_newif *req = nm_os_malloc(sizeof(*req));
185 			if (!req) { goto oom; }
186 			hdr->nr_body = (uint64_t)req;
187 			hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF;
188 			req->nr_tx_slots = nmr->nr_tx_slots;
189 			req->nr_rx_slots = nmr->nr_rx_slots;
190 			req->nr_tx_rings = nmr->nr_tx_rings;
191 			req->nr_rx_rings = nmr->nr_rx_rings;
192 			req->nr_mem_id = nmr->nr_arg2;
193 			break;
194 		}
195 		case NETMAP_BDG_DELIF: {
196 			hdr->nr_reqtype = NETMAP_REQ_VALE_DELIF;
197 			break;
198 		}
199 		case NETMAP_BDG_POLLING_ON:
200 		case NETMAP_BDG_POLLING_OFF: {
201 			struct nmreq_vale_polling *req = nm_os_malloc(sizeof(*req));
202 			if (!req) { goto oom; }
203 			hdr->nr_body = (uint64_t)req;
204 			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_POLLING_ON) ?
205 				NETMAP_REQ_VALE_POLLING_ENABLE :
206 				NETMAP_REQ_VALE_POLLING_DISABLE;
207 			switch (nmr->nr_flags & NR_REG_MASK) {
208 			default:
209 				req->nr_mode = 0; /* invalid */
210 				break;
211 			case NR_REG_ONE_NIC:
212 				req->nr_mode = NETMAP_POLLING_MODE_MULTI_CPU;
213 				break;
214 			case NR_REG_ALL_NIC:
215 				req->nr_mode = NETMAP_POLLING_MODE_SINGLE_CPU;
216 				break;
217 			}
218 			req->nr_first_cpu_id = nmr->nr_ringid & NETMAP_RING_MASK;
219 			req->nr_num_polling_cpus = nmr->nr_arg1;
220 			break;
221 		}
222 		case NETMAP_PT_HOST_CREATE:
223 		case NETMAP_PT_HOST_DELETE: {
224 			D("Netmap passthrough not supported yet");
225 			return NULL;
226 			break;
227 		}
228 		}
229 		break;
230 	}
231 	case NIOCGINFO: {
232 		if (nmr->nr_cmd == NETMAP_BDG_LIST) {
233 			struct nmreq_vale_list *req = nm_os_malloc(sizeof(*req));
234 			if (!req) { goto oom; }
235 			hdr->nr_body = (uint64_t)req;
236 			hdr->nr_reqtype = NETMAP_REQ_VALE_LIST;
237 			req->nr_bridge_idx = nmr->nr_arg1;
238 			req->nr_port_idx = nmr->nr_arg2;
239 		} else {
240 			/* Regular NIOCGINFO. */
241 			struct nmreq_port_info_get *req = nm_os_malloc(sizeof(*req));
242 			if (!req) { goto oom; }
243 			hdr->nr_body = (uint64_t)req;
244 			hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
245 			req->nr_offset = nmr->nr_offset;
246 			req->nr_memsize = nmr->nr_memsize;
247 			req->nr_tx_slots = nmr->nr_tx_slots;
248 			req->nr_rx_slots = nmr->nr_rx_slots;
249 			req->nr_tx_rings = nmr->nr_tx_rings;
250 			req->nr_rx_rings = nmr->nr_rx_rings;
251 			req->nr_mem_id = nmr->nr_arg2;
252 		}
253 		break;
254 	}
255 	}
256 
257 	return hdr;
258 oom:
259 	if (hdr) {
260 		if (hdr->nr_body) {
261 			nm_os_free((void *)hdr->nr_body);
262 		}
263 		nm_os_free(hdr);
264 	}
265 	D("Failed to allocate memory for nmreq_xyz struct");
266 
267 	return NULL;
268 }
269 
270 static void
271 nmreq_register_to_legacy(const struct nmreq_register *req, struct nmreq *nmr)
272 {
273 	nmr->nr_offset = req->nr_offset;
274 	nmr->nr_memsize = req->nr_memsize;
275 	nmr->nr_tx_slots = req->nr_tx_slots;
276 	nmr->nr_rx_slots = req->nr_rx_slots;
277 	nmr->nr_tx_rings = req->nr_tx_rings;
278 	nmr->nr_rx_rings = req->nr_rx_rings;
279 	nmr->nr_arg2 = req->nr_mem_id;
280 	nmr->nr_arg3 = req->nr_extra_bufs;
281 }
282 
283 /* Convert a nmreq_xyz struct (new API) to the legacy 'nmr' struct.
284  * It also frees the nmreq_xyz struct, as it was allocated by
285  * nmreq_from_legacy(). */
286 static int
287 nmreq_to_legacy(struct nmreq_header *hdr, struct nmreq *nmr)
288 {
289 	int ret = 0;
290 
291 	/* We only write-back the fields that the user expects to be
292 	 * written back. */
293 	switch (hdr->nr_reqtype) {
294 	case NETMAP_REQ_REGISTER: {
295 		struct nmreq_register *req =
296 			(struct nmreq_register *)hdr->nr_body;
297 		nmreq_register_to_legacy(req, nmr);
298 		break;
299 	}
300 	case NETMAP_REQ_PORT_INFO_GET: {
301 		struct nmreq_port_info_get *req =
302 			(struct nmreq_port_info_get *)hdr->nr_body;
303 		nmr->nr_offset = req->nr_offset;
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 *)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 *)hdr->nr_body;
324 		strncpy(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 *)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 *)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 *)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