1341477Svmaffione/*
2341477Svmaffione * Copyright (C) 2018 Vincenzo Maffione
3341477Svmaffione * All rights reserved.
4341477Svmaffione *
5341477Svmaffione * Redistribution and use in source and binary forms, with or without
6341477Svmaffione * modification, are permitted provided that the following conditions
7341477Svmaffione * are met:
8341477Svmaffione *   1. Redistributions of source code must retain the above copyright
9341477Svmaffione *      notice, this list of conditions and the following disclaimer.
10341477Svmaffione *   2. Redistributions in binary form must reproduce the above copyright
11341477Svmaffione *      notice, this list of conditions and the following disclaimer in the
12341477Svmaffione *      documentation and/or other materials provided with the distribution.
13341477Svmaffione *
14341477Svmaffione * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15341477Svmaffione * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16341477Svmaffione * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17341477Svmaffione * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18341477Svmaffione * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19341477Svmaffione * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20341477Svmaffione * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21341477Svmaffione * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22341477Svmaffione * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23341477Svmaffione * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24341477Svmaffione * SUCH DAMAGE.
25341477Svmaffione */
26341477Svmaffione/* $FreeBSD: stable/11/sys/dev/netmap/netmap_legacy.c 364756 2020-08-25 11:12:30Z vmaffione $ */
27341477Svmaffione
28341477Svmaffione#if defined(__FreeBSD__)
29341477Svmaffione#include <sys/cdefs.h> /* prerequisite */
30341477Svmaffione#include <sys/types.h>
31341477Svmaffione#include <sys/param.h>	/* defines used in kernel.h */
32341477Svmaffione#include <sys/filio.h>	/* FIONBIO */
33341477Svmaffione#include <sys/malloc.h>
34341477Svmaffione#include <sys/socketvar.h>	/* struct socket */
35341477Svmaffione#include <sys/socket.h> /* sockaddrs */
36341477Svmaffione#include <sys/sysctl.h>
37341477Svmaffione#include <net/if.h>
38341477Svmaffione#include <net/if_var.h>
39341477Svmaffione#include <net/bpf.h>		/* BIOCIMMEDIATE */
40341477Svmaffione#include <machine/bus.h>	/* bus_dmamap_* */
41341477Svmaffione#include <sys/endian.h>
42341477Svmaffione#elif defined(linux)
43341477Svmaffione#include "bsd_glue.h"
44341477Svmaffione#elif defined(__APPLE__)
45341477Svmaffione#warning OSX support is only partial
46341477Svmaffione#include "osx_glue.h"
47341477Svmaffione#elif defined (_WIN32)
48341477Svmaffione#include "win_glue.h"
49341477Svmaffione#endif
50341477Svmaffione
51341477Svmaffione/*
52341477Svmaffione * common headers
53341477Svmaffione */
54341477Svmaffione#include <net/netmap.h>
55341477Svmaffione#include <dev/netmap/netmap_kern.h>
56342033Svmaffione#include <dev/netmap/netmap_bdg.h>
57341477Svmaffione
58341477Svmaffionestatic int
59341477Svmaffionenmreq_register_from_legacy(struct nmreq *nmr, struct nmreq_header *hdr,
60341477Svmaffione				struct nmreq_register *req)
61341477Svmaffione{
62341477Svmaffione	req->nr_offset = nmr->nr_offset;
63341477Svmaffione	req->nr_memsize = nmr->nr_memsize;
64341477Svmaffione	req->nr_tx_slots = nmr->nr_tx_slots;
65341477Svmaffione	req->nr_rx_slots = nmr->nr_rx_slots;
66341477Svmaffione	req->nr_tx_rings = nmr->nr_tx_rings;
67341477Svmaffione	req->nr_rx_rings = nmr->nr_rx_rings;
68341477Svmaffione	req->nr_mem_id = nmr->nr_arg2;
69341477Svmaffione	req->nr_ringid = nmr->nr_ringid & NETMAP_RING_MASK;
70341477Svmaffione	if ((nmr->nr_flags & NR_REG_MASK) == NR_REG_DEFAULT) {
71341477Svmaffione		/* Convert the older nmr->nr_ringid (original
72341477Svmaffione		 * netmap control API) to nmr->nr_flags. */
73341477Svmaffione		u_int regmode = NR_REG_DEFAULT;
74364756Svmaffione		if (nmr->nr_ringid & NETMAP_SW_RING) {
75341477Svmaffione			regmode = NR_REG_SW;
76364756Svmaffione		} else if (nmr->nr_ringid & NETMAP_HW_RING) {
77341477Svmaffione			regmode = NR_REG_ONE_NIC;
78341477Svmaffione		} else {
79341477Svmaffione			regmode = NR_REG_ALL_NIC;
80341477Svmaffione		}
81342033Svmaffione		req->nr_mode = regmode;
82342033Svmaffione	} else {
83342033Svmaffione		req->nr_mode = nmr->nr_flags & NR_REG_MASK;
84341477Svmaffione	}
85342033Svmaffione
86341477Svmaffione	/* Fix nr_name, nr_mode and nr_ringid to handle pipe requests. */
87341477Svmaffione	if (req->nr_mode == NR_REG_PIPE_MASTER ||
88341477Svmaffione			req->nr_mode == NR_REG_PIPE_SLAVE) {
89341477Svmaffione		char suffix[10];
90341477Svmaffione		snprintf(suffix, sizeof(suffix), "%c%d",
91341477Svmaffione			(req->nr_mode == NR_REG_PIPE_MASTER ? '{' : '}'),
92341477Svmaffione			req->nr_ringid);
93341477Svmaffione		if (strlen(hdr->nr_name) + strlen(suffix)
94341477Svmaffione					>= sizeof(hdr->nr_name)) {
95341477Svmaffione			/* No space for the pipe suffix. */
96341477Svmaffione			return ENOBUFS;
97341477Svmaffione		}
98341477Svmaffione		strncat(hdr->nr_name, suffix, strlen(suffix));
99341477Svmaffione		req->nr_mode = NR_REG_ALL_NIC;
100341477Svmaffione		req->nr_ringid = 0;
101341477Svmaffione	}
102341477Svmaffione	req->nr_flags = nmr->nr_flags & (~NR_REG_MASK);
103341477Svmaffione	if (nmr->nr_ringid & NETMAP_NO_TX_POLL) {
104341477Svmaffione		req->nr_flags |= NR_NO_TX_POLL;
105341477Svmaffione	}
106341477Svmaffione	if (nmr->nr_ringid & NETMAP_DO_RX_POLL) {
107341477Svmaffione		req->nr_flags |= NR_DO_RX_POLL;
108341477Svmaffione	}
109341477Svmaffione	/* nmr->nr_arg1 (nr_pipes) ignored */
110341477Svmaffione	req->nr_extra_bufs = nmr->nr_arg3;
111341477Svmaffione
112341477Svmaffione	return 0;
113341477Svmaffione}
114341477Svmaffione
115341477Svmaffione/* Convert the legacy 'nmr' struct into one of the nmreq_xyz structs
116341477Svmaffione * (new API). The new struct is dynamically allocated. */
117341477Svmaffionestatic struct nmreq_header *
118341477Svmaffionenmreq_from_legacy(struct nmreq *nmr, u_long ioctl_cmd)
119341477Svmaffione{
120341477Svmaffione	struct nmreq_header *hdr = nm_os_malloc(sizeof(*hdr));
121341477Svmaffione
122341477Svmaffione	if (hdr == NULL) {
123341477Svmaffione		goto oom;
124341477Svmaffione	}
125341477Svmaffione
126341477Svmaffione	/* Sanitize nmr->nr_name by adding the string terminator. */
127341477Svmaffione	if (ioctl_cmd == NIOCGINFO || ioctl_cmd == NIOCREGIF) {
128341477Svmaffione		nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';
129341477Svmaffione	}
130341477Svmaffione
131341477Svmaffione	/* First prepare the request header. */
132341477Svmaffione	hdr->nr_version = NETMAP_API; /* new API */
133342033Svmaffione	strlcpy(hdr->nr_name, nmr->nr_name, sizeof(nmr->nr_name));
134341477Svmaffione	hdr->nr_options = (uintptr_t)NULL;
135341477Svmaffione	hdr->nr_body = (uintptr_t)NULL;
136341477Svmaffione
137341477Svmaffione	switch (ioctl_cmd) {
138341477Svmaffione	case NIOCREGIF: {
139341477Svmaffione		switch (nmr->nr_cmd) {
140341477Svmaffione		case 0: {
141341477Svmaffione			/* Regular NIOCREGIF operation. */
142341477Svmaffione			struct nmreq_register *req = nm_os_malloc(sizeof(*req));
143341477Svmaffione			if (!req) { goto oom; }
144341477Svmaffione			hdr->nr_body = (uintptr_t)req;
145341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_REGISTER;
146341477Svmaffione			if (nmreq_register_from_legacy(nmr, hdr, req)) {
147341477Svmaffione				goto oom;
148341477Svmaffione			}
149341477Svmaffione			break;
150341477Svmaffione		}
151341477Svmaffione		case NETMAP_BDG_ATTACH: {
152341477Svmaffione			struct nmreq_vale_attach *req = nm_os_malloc(sizeof(*req));
153341477Svmaffione			if (!req) { goto oom; }
154341477Svmaffione			hdr->nr_body = (uintptr_t)req;
155341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_VALE_ATTACH;
156341477Svmaffione			if (nmreq_register_from_legacy(nmr, hdr, &req->reg)) {
157341477Svmaffione				goto oom;
158341477Svmaffione			}
159341477Svmaffione			/* Fix nr_mode, starting from nr_arg1. */
160341477Svmaffione			if (nmr->nr_arg1 & NETMAP_BDG_HOST) {
161341477Svmaffione				req->reg.nr_mode = NR_REG_NIC_SW;
162341477Svmaffione			} else {
163341477Svmaffione				req->reg.nr_mode = NR_REG_ALL_NIC;
164341477Svmaffione			}
165341477Svmaffione			break;
166341477Svmaffione		}
167341477Svmaffione		case NETMAP_BDG_DETACH: {
168341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_VALE_DETACH;
169341477Svmaffione			hdr->nr_body = (uintptr_t)nm_os_malloc(sizeof(struct nmreq_vale_detach));
170341477Svmaffione			break;
171341477Svmaffione		}
172341477Svmaffione		case NETMAP_BDG_VNET_HDR:
173341477Svmaffione		case NETMAP_VNET_HDR_GET: {
174341477Svmaffione			struct nmreq_port_hdr *req = nm_os_malloc(sizeof(*req));
175341477Svmaffione			if (!req) { goto oom; }
176341477Svmaffione			hdr->nr_body = (uintptr_t)req;
177341477Svmaffione			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_VNET_HDR) ?
178341477Svmaffione				NETMAP_REQ_PORT_HDR_SET : NETMAP_REQ_PORT_HDR_GET;
179341477Svmaffione			req->nr_hdr_len = nmr->nr_arg1;
180341477Svmaffione			break;
181341477Svmaffione		}
182341477Svmaffione		case NETMAP_BDG_NEWIF : {
183341477Svmaffione			struct nmreq_vale_newif *req = nm_os_malloc(sizeof(*req));
184341477Svmaffione			if (!req) { goto oom; }
185341477Svmaffione			hdr->nr_body = (uintptr_t)req;
186341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF;
187341477Svmaffione			req->nr_tx_slots = nmr->nr_tx_slots;
188341477Svmaffione			req->nr_rx_slots = nmr->nr_rx_slots;
189341477Svmaffione			req->nr_tx_rings = nmr->nr_tx_rings;
190341477Svmaffione			req->nr_rx_rings = nmr->nr_rx_rings;
191341477Svmaffione			req->nr_mem_id = nmr->nr_arg2;
192341477Svmaffione			break;
193341477Svmaffione		}
194341477Svmaffione		case NETMAP_BDG_DELIF: {
195341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_VALE_DELIF;
196341477Svmaffione			break;
197341477Svmaffione		}
198341477Svmaffione		case NETMAP_BDG_POLLING_ON:
199341477Svmaffione		case NETMAP_BDG_POLLING_OFF: {
200341477Svmaffione			struct nmreq_vale_polling *req = nm_os_malloc(sizeof(*req));
201341477Svmaffione			if (!req) { goto oom; }
202341477Svmaffione			hdr->nr_body = (uintptr_t)req;
203341477Svmaffione			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_POLLING_ON) ?
204341477Svmaffione				NETMAP_REQ_VALE_POLLING_ENABLE :
205341477Svmaffione				NETMAP_REQ_VALE_POLLING_DISABLE;
206341477Svmaffione			switch (nmr->nr_flags & NR_REG_MASK) {
207341477Svmaffione			default:
208341477Svmaffione				req->nr_mode = 0; /* invalid */
209341477Svmaffione				break;
210341477Svmaffione			case NR_REG_ONE_NIC:
211341477Svmaffione				req->nr_mode = NETMAP_POLLING_MODE_MULTI_CPU;
212341477Svmaffione				break;
213341477Svmaffione			case NR_REG_ALL_NIC:
214341477Svmaffione				req->nr_mode = NETMAP_POLLING_MODE_SINGLE_CPU;
215341477Svmaffione				break;
216341477Svmaffione			}
217341477Svmaffione			req->nr_first_cpu_id = nmr->nr_ringid & NETMAP_RING_MASK;
218341477Svmaffione			req->nr_num_polling_cpus = nmr->nr_arg1;
219341477Svmaffione			break;
220341477Svmaffione		}
221341477Svmaffione		case NETMAP_PT_HOST_CREATE:
222341477Svmaffione		case NETMAP_PT_HOST_DELETE: {
223342033Svmaffione			nm_prerr("Netmap passthrough not supported yet");
224341477Svmaffione			return NULL;
225341477Svmaffione			break;
226341477Svmaffione		}
227341477Svmaffione		}
228341477Svmaffione		break;
229341477Svmaffione	}
230341477Svmaffione	case NIOCGINFO: {
231341477Svmaffione		if (nmr->nr_cmd == NETMAP_BDG_LIST) {
232341477Svmaffione			struct nmreq_vale_list *req = nm_os_malloc(sizeof(*req));
233341477Svmaffione			if (!req) { goto oom; }
234341477Svmaffione			hdr->nr_body = (uintptr_t)req;
235341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_VALE_LIST;
236341477Svmaffione			req->nr_bridge_idx = nmr->nr_arg1;
237341477Svmaffione			req->nr_port_idx = nmr->nr_arg2;
238341477Svmaffione		} else {
239341477Svmaffione			/* Regular NIOCGINFO. */
240341477Svmaffione			struct nmreq_port_info_get *req = nm_os_malloc(sizeof(*req));
241341477Svmaffione			if (!req) { goto oom; }
242341477Svmaffione			hdr->nr_body = (uintptr_t)req;
243341477Svmaffione			hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
244341477Svmaffione			req->nr_memsize = nmr->nr_memsize;
245341477Svmaffione			req->nr_tx_slots = nmr->nr_tx_slots;
246341477Svmaffione			req->nr_rx_slots = nmr->nr_rx_slots;
247341477Svmaffione			req->nr_tx_rings = nmr->nr_tx_rings;
248341477Svmaffione			req->nr_rx_rings = nmr->nr_rx_rings;
249341477Svmaffione			req->nr_mem_id = nmr->nr_arg2;
250341477Svmaffione		}
251341477Svmaffione		break;
252341477Svmaffione	}
253341477Svmaffione	}
254341477Svmaffione
255341477Svmaffione	return hdr;
256341477Svmaffioneoom:
257341477Svmaffione	if (hdr) {
258341477Svmaffione		if (hdr->nr_body) {
259341477Svmaffione			nm_os_free((void *)(uintptr_t)hdr->nr_body);
260341477Svmaffione		}
261341477Svmaffione		nm_os_free(hdr);
262341477Svmaffione	}
263342033Svmaffione	nm_prerr("Failed to allocate memory for nmreq_xyz struct");
264341477Svmaffione
265341477Svmaffione	return NULL;
266341477Svmaffione}
267341477Svmaffione
268341477Svmaffionestatic void
269341477Svmaffionenmreq_register_to_legacy(const struct nmreq_register *req, struct nmreq *nmr)
270341477Svmaffione{
271341477Svmaffione	nmr->nr_offset = req->nr_offset;
272341477Svmaffione	nmr->nr_memsize = req->nr_memsize;
273341477Svmaffione	nmr->nr_tx_slots = req->nr_tx_slots;
274341477Svmaffione	nmr->nr_rx_slots = req->nr_rx_slots;
275341477Svmaffione	nmr->nr_tx_rings = req->nr_tx_rings;
276341477Svmaffione	nmr->nr_rx_rings = req->nr_rx_rings;
277341477Svmaffione	nmr->nr_arg2 = req->nr_mem_id;
278341477Svmaffione	nmr->nr_arg3 = req->nr_extra_bufs;
279341477Svmaffione}
280341477Svmaffione
281341477Svmaffione/* Convert a nmreq_xyz struct (new API) to the legacy 'nmr' struct.
282341477Svmaffione * It also frees the nmreq_xyz struct, as it was allocated by
283341477Svmaffione * nmreq_from_legacy(). */
284341477Svmaffionestatic int
285341477Svmaffionenmreq_to_legacy(struct nmreq_header *hdr, struct nmreq *nmr)
286341477Svmaffione{
287341477Svmaffione	int ret = 0;
288341477Svmaffione
289341477Svmaffione	/* We only write-back the fields that the user expects to be
290341477Svmaffione	 * written back. */
291341477Svmaffione	switch (hdr->nr_reqtype) {
292341477Svmaffione	case NETMAP_REQ_REGISTER: {
293341477Svmaffione		struct nmreq_register *req =
294341477Svmaffione			(struct nmreq_register *)(uintptr_t)hdr->nr_body;
295341477Svmaffione		nmreq_register_to_legacy(req, nmr);
296341477Svmaffione		break;
297341477Svmaffione	}
298341477Svmaffione	case NETMAP_REQ_PORT_INFO_GET: {
299341477Svmaffione		struct nmreq_port_info_get *req =
300341477Svmaffione			(struct nmreq_port_info_get *)(uintptr_t)hdr->nr_body;
301341477Svmaffione		nmr->nr_memsize = req->nr_memsize;
302341477Svmaffione		nmr->nr_tx_slots = req->nr_tx_slots;
303341477Svmaffione		nmr->nr_rx_slots = req->nr_rx_slots;
304341477Svmaffione		nmr->nr_tx_rings = req->nr_tx_rings;
305341477Svmaffione		nmr->nr_rx_rings = req->nr_rx_rings;
306341477Svmaffione		nmr->nr_arg2 = req->nr_mem_id;
307341477Svmaffione		break;
308341477Svmaffione	}
309341477Svmaffione	case NETMAP_REQ_VALE_ATTACH: {
310341477Svmaffione		struct nmreq_vale_attach *req =
311341477Svmaffione			(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
312341477Svmaffione		nmreq_register_to_legacy(&req->reg, nmr);
313341477Svmaffione		break;
314341477Svmaffione	}
315341477Svmaffione	case NETMAP_REQ_VALE_DETACH: {
316341477Svmaffione		break;
317341477Svmaffione	}
318341477Svmaffione	case NETMAP_REQ_VALE_LIST: {
319341477Svmaffione		struct nmreq_vale_list *req =
320341477Svmaffione			(struct nmreq_vale_list *)(uintptr_t)hdr->nr_body;
321342033Svmaffione		strlcpy(nmr->nr_name, hdr->nr_name, sizeof(nmr->nr_name));
322341477Svmaffione		nmr->nr_arg1 = req->nr_bridge_idx;
323341477Svmaffione		nmr->nr_arg2 = req->nr_port_idx;
324341477Svmaffione		break;
325341477Svmaffione	}
326341477Svmaffione	case NETMAP_REQ_PORT_HDR_SET:
327341477Svmaffione	case NETMAP_REQ_PORT_HDR_GET: {
328341477Svmaffione		struct nmreq_port_hdr *req =
329341477Svmaffione			(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
330341477Svmaffione		nmr->nr_arg1 = req->nr_hdr_len;
331341477Svmaffione		break;
332341477Svmaffione	}
333341477Svmaffione	case NETMAP_REQ_VALE_NEWIF: {
334341477Svmaffione		struct nmreq_vale_newif *req =
335341477Svmaffione			(struct nmreq_vale_newif *)(uintptr_t)hdr->nr_body;
336341477Svmaffione		nmr->nr_tx_slots = req->nr_tx_slots;
337341477Svmaffione		nmr->nr_rx_slots = req->nr_rx_slots;
338341477Svmaffione		nmr->nr_tx_rings = req->nr_tx_rings;
339341477Svmaffione		nmr->nr_rx_rings = req->nr_rx_rings;
340341477Svmaffione		nmr->nr_arg2 = req->nr_mem_id;
341341477Svmaffione		break;
342341477Svmaffione	}
343341477Svmaffione	case NETMAP_REQ_VALE_DELIF:
344341477Svmaffione	case NETMAP_REQ_VALE_POLLING_ENABLE:
345341477Svmaffione	case NETMAP_REQ_VALE_POLLING_DISABLE: {
346341477Svmaffione		break;
347341477Svmaffione	}
348341477Svmaffione	}
349341477Svmaffione
350341477Svmaffione	return ret;
351341477Svmaffione}
352341477Svmaffione
353341477Svmaffioneint
354341477Svmaffionenetmap_ioctl_legacy(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
355341477Svmaffione			struct thread *td)
356341477Svmaffione{
357341477Svmaffione	int error = 0;
358341477Svmaffione
359341477Svmaffione	switch (cmd) {
360341477Svmaffione	case NIOCGINFO:
361341477Svmaffione	case NIOCREGIF: {
362341477Svmaffione		/* Request for the legacy control API. Convert it to a
363341477Svmaffione		 * NIOCCTRL request. */
364341477Svmaffione		struct nmreq *nmr = (struct nmreq *) data;
365344047Svmaffione		struct nmreq_header *hdr;
366344047Svmaffione
367344047Svmaffione		if (nmr->nr_version < 11) {
368344047Svmaffione			nm_prerr("Minimum supported API is 11 (requested %u)",
369344047Svmaffione			    nmr->nr_version);
370344047Svmaffione			return EINVAL;
371344047Svmaffione		}
372344047Svmaffione		hdr = nmreq_from_legacy(nmr, cmd);
373341477Svmaffione		if (hdr == NULL) { /* out of memory */
374341477Svmaffione			return ENOMEM;
375341477Svmaffione		}
376341477Svmaffione		error = netmap_ioctl(priv, NIOCCTRL, (caddr_t)hdr, td,
377341477Svmaffione					/*nr_body_is_user=*/0);
378341477Svmaffione		if (error == 0) {
379341477Svmaffione			nmreq_to_legacy(hdr, nmr);
380341477Svmaffione		}
381341477Svmaffione		if (hdr->nr_body) {
382341477Svmaffione			nm_os_free((void *)(uintptr_t)hdr->nr_body);
383341477Svmaffione		}
384341477Svmaffione		nm_os_free(hdr);
385341477Svmaffione		break;
386341477Svmaffione	}
387341477Svmaffione#ifdef WITH_VALE
388341477Svmaffione	case NIOCCONFIG: {
389341477Svmaffione		struct nm_ifreq *nr = (struct nm_ifreq *)data;
390341477Svmaffione		error = netmap_bdg_config(nr);
391341477Svmaffione		break;
392341477Svmaffione	}
393341477Svmaffione#endif
394341477Svmaffione#ifdef __FreeBSD__
395341477Svmaffione	case FIONBIO:
396341477Svmaffione	case FIOASYNC:
397344047Svmaffione		/* FIONBIO/FIOASYNC are no-ops. */
398341477Svmaffione		break;
399341477Svmaffione
400341477Svmaffione	case BIOCIMMEDIATE:
401341477Svmaffione	case BIOCGHDRCMPLT:
402341477Svmaffione	case BIOCSHDRCMPLT:
403341477Svmaffione	case BIOCSSEESENT:
404344047Svmaffione		/* Ignore these commands. */
405341477Svmaffione		break;
406341477Svmaffione
407341477Svmaffione	default:	/* allow device-specific ioctls */
408341477Svmaffione	    {
409341477Svmaffione		struct nmreq *nmr = (struct nmreq *)data;
410341477Svmaffione		struct ifnet *ifp = ifunit_ref(nmr->nr_name);
411341477Svmaffione		if (ifp == NULL) {
412341477Svmaffione			error = ENXIO;
413341477Svmaffione		} else {
414341477Svmaffione			struct socket so;
415341477Svmaffione
416341477Svmaffione			bzero(&so, sizeof(so));
417341477Svmaffione			so.so_vnet = ifp->if_vnet;
418341477Svmaffione			// so->so_proto not null.
419341477Svmaffione			error = ifioctl(&so, cmd, data, td);
420341477Svmaffione			if_rele(ifp);
421341477Svmaffione		}
422341477Svmaffione		break;
423341477Svmaffione	    }
424341477Svmaffione
425341477Svmaffione#else /* linux */
426341477Svmaffione	default:
427341477Svmaffione		error = EOPNOTSUPP;
428341477Svmaffione#endif /* linux */
429341477Svmaffione	}
430341477Svmaffione
431341477Svmaffione	return error;
432341477Svmaffione}
433