xref: /illumos-gate/usr/src/cmd/bhyve/common/net_backend_dlpi.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2024 OmniOS Community Edition (OmniOSce) Association.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  * The illumos dlpi backend
30  */
31 
32 #include <sys/types.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <sys/socket.h>
37 #include <net/ethernet.h>
38 #include <libdlpi.h>
39 
40 #include "config.h"
41 #include "debug.h"
42 #include "iov.h"
43 #include "mevent.h"
44 #include "net_backends.h"
45 #include "net_backends_priv.h"
46 
47 /*
48  * The size of the bounce buffer used to implement the peek callback.
49  * This value should be big enough to accommodate the largest of all possible
50  * frontend packet lengths. The value here matches the definition of
51  * VTNET_MAX_PKT_LEN in pci_virtio_net.c
52  */
53 #define	DLPI_BBUF_SIZE (65536 + 64)
54 
55 typedef struct be_dlpi_priv {
56 	dlpi_handle_t bdp_dhp;
57 	struct mevent *bdp_mevp;
58 	/*
59 	 * A bounce buffer that allows us to implement the peek_recvlen
60 	 * callback. Each structure is only used by a single thread so
61 	 * one is enough.
62 	 */
63 	uint8_t bdp_bbuf[DLPI_BBUF_SIZE];
64 	ssize_t bdp_bbuflen;
65 } be_dlpi_priv_t;
66 
67 static void
be_dlpi_cleanup(net_backend_t * be)68 be_dlpi_cleanup(net_backend_t *be)
69 {
70 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
71 
72 	if (priv->bdp_dhp != NULL)
73 		dlpi_close(priv->bdp_dhp);
74 	priv->bdp_dhp = NULL;
75 
76 	if (priv->bdp_mevp != NULL)
77 		mevent_delete(priv->bdp_mevp);
78 	priv->bdp_mevp = NULL;
79 
80 	priv->bdp_bbuflen = 0;
81 	be->fd = -1;
82 }
83 
84 static void
be_dlpi_err(int ret,const char * dev,char * msg)85 be_dlpi_err(int ret, const char *dev, char *msg)
86 {
87 	EPRINTLN("%s: %s (%s)", dev, msg, dlpi_strerror(ret));
88 }
89 
90 static int
be_dlpi_init(net_backend_t * be,const char * devname __unused,nvlist_t * nvl,net_be_rxeof_t cb,void * param)91 be_dlpi_init(net_backend_t *be, const char *devname __unused,
92      nvlist_t *nvl, net_be_rxeof_t cb, void *param)
93 {
94 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
95 	const char *vnic;
96 	int ret;
97 
98 	if (cb == NULL) {
99 		EPRINTLN("dlpi backend requires non-NULL callback");
100 		return (-1);
101 	}
102 
103 	vnic = get_config_value_node(nvl, "vnic");
104 	if (vnic == NULL) {
105 		EPRINTLN("dlpi backend requires a VNIC");
106 		return (-1);
107 	}
108 
109 	priv->bdp_bbuflen = 0;
110 
111 	ret = dlpi_open(vnic, &priv->bdp_dhp, DLPI_RAW);
112 
113 	if (ret != DLPI_SUCCESS) {
114 		be_dlpi_err(ret, vnic, "open failed");
115 		goto error;
116 	}
117 
118 	if ((ret = dlpi_bind(priv->bdp_dhp, DLPI_ANY_SAP, NULL)) !=
119 	    DLPI_SUCCESS) {
120 		be_dlpi_err(ret, vnic, "bind failed");
121 		goto error;
122 	}
123 
124 	if (get_config_bool_node_default(nvl, "promiscrxonly", true)) {
125 		if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_RX_ONLY)) !=
126 		    DLPI_SUCCESS) {
127 			be_dlpi_err(ret, vnic,
128 			    "enable promiscuous mode(rxonly) failed");
129 			goto error;
130 		}
131 	}
132 	if (get_config_bool_node_default(nvl, "promiscphys", false)) {
133 		if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_PHYS)) !=
134 		    DLPI_SUCCESS) {
135 			be_dlpi_err(ret, vnic,
136 			    "enable promiscuous mode(physical) failed");
137 			goto error;
138 		}
139 	}
140 	if (get_config_bool_node_default(nvl, "promiscsap", true)) {
141 		if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_SAP)) !=
142 		    DLPI_SUCCESS) {
143 			be_dlpi_err(ret, vnic,
144 			    "enable promiscuous mode(SAP) failed");
145 			goto error;
146 		}
147 	}
148 	if (get_config_bool_node_default(nvl, "promiscmulti", true)) {
149 		if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_MULTI)) !=
150 		    DLPI_SUCCESS) {
151 			be_dlpi_err(ret, vnic,
152 			    "enable promiscuous mode(muticast) failed");
153 			goto error;
154 		}
155 	}
156 
157         be->fd = dlpi_fd(priv->bdp_dhp);
158 
159         if (fcntl(be->fd, F_SETFL, O_NONBLOCK) < 0) {
160                 EPRINTLN("%s: enable O_NONBLOCK failed", vnic);
161 		goto error;
162         }
163 
164 	priv->bdp_mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
165 	if (priv->bdp_mevp == NULL) {
166 		EPRINTLN("Could not register event");
167 		goto error;
168 	}
169 
170 	return (0);
171 
172 error:
173 	be_dlpi_cleanup(be);
174 	return (-1);
175 }
176 
177 /*
178  * Called to send a buffer chain out to the dlpi device
179  */
180 static ssize_t
be_dlpi_send(net_backend_t * be,const struct iovec * iov,int iovcnt)181 be_dlpi_send(net_backend_t *be, const struct iovec *iov, int iovcnt)
182 {
183 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
184 	ssize_t len = 0;
185 	int ret;
186 
187 	if (iovcnt == 1) {
188 		len = iov[0].iov_len;
189 		ret = dlpi_send(priv->bdp_dhp, NULL, 0, iov[0].iov_base, len,
190 		    NULL);
191 	} else {
192 		void *buf = NULL;
193 
194 		len = iov_to_buf(iov, iovcnt, &buf);
195 
196 		if (len <= 0 || buf == NULL)
197 			return (-1);
198 
199 		ret = dlpi_send(priv->bdp_dhp, NULL, 0, buf, len, NULL);
200 		free(buf);
201 	}
202 
203 	if (ret != DLPI_SUCCESS)
204 		return (-1);
205 
206 	return (len);
207 }
208 
209 static ssize_t
be_dlpi_peek_recvlen(net_backend_t * be)210 be_dlpi_peek_recvlen(net_backend_t *be)
211 {
212 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
213 	dlpi_recvinfo_t recv;
214 	size_t len;
215 	int ret;
216 
217 	/*
218 	 * We already have a packet in the bounce buffer.
219 	 * Just return its length.
220 	 */
221 	if (priv->bdp_bbuflen > 0)
222 		return (priv->bdp_bbuflen);
223 
224 	/*
225 	 * Read the next packet (if any) into the bounce buffer, so
226 	 * that we get to know its length and we can return that
227 	 * to the caller.
228 	 */
229 	len = sizeof (priv->bdp_bbuf);
230 	ret = dlpi_recv(priv->bdp_dhp, NULL, NULL, priv->bdp_bbuf, &len,
231 	    0, &recv);
232 	if (ret == DL_SYSERR) {
233 		if (errno == EWOULDBLOCK)
234 			return (0);
235 		return (-1);
236 	} else if (ret == DLPI_ETIMEDOUT) {
237 		return (0);
238 	} else if (ret != DLPI_SUCCESS) {
239 		return (-1);
240 	}
241 
242 	if (recv.dri_totmsglen > sizeof (priv->bdp_bbuf)) {
243 		EPRINTLN("DLPI bounce buffer was too small! - needed %x bytes",
244 		    recv.dri_totmsglen);
245 	}
246 
247 	priv->bdp_bbuflen = len;
248 
249 	return (len);
250 }
251 
252 static ssize_t
be_dlpi_recv(net_backend_t * be,const struct iovec * iov,int iovcnt)253 be_dlpi_recv(net_backend_t *be, const struct iovec *iov, int iovcnt)
254 {
255 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
256 	size_t len;
257 	int ret;
258 
259 	if (priv->bdp_bbuflen > 0) {
260 		/*
261 		 * A packet is available in the bounce buffer, so
262 		 * we read it from there.
263 		 */
264 		len = buf_to_iov(priv->bdp_bbuf, priv->bdp_bbuflen,
265 		    iov, iovcnt, 0);
266 
267 		/* Mark the bounce buffer as empty. */
268 		priv->bdp_bbuflen = 0;
269 
270 		return (len);
271 	}
272 
273 	len = iov[0].iov_len;
274 	ret = dlpi_recv(priv->bdp_dhp, NULL, NULL,
275 	    (uint8_t *)iov[0].iov_base, &len, 0, NULL);
276 	if (ret == DL_SYSERR) {
277 		if (errno == EWOULDBLOCK)
278 			return (0);
279 		return (-1);
280 	} else if (ret == DLPI_ETIMEDOUT) {
281 		return (0);
282 	} else if (ret != DLPI_SUCCESS) {
283 		return (-1);
284 	}
285 
286 	return (len);
287 }
288 
289 static void
be_dlpi_recv_enable(net_backend_t * be)290 be_dlpi_recv_enable(net_backend_t *be)
291 {
292 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
293 
294 	mevent_enable(priv->bdp_mevp);
295 }
296 
297 static void
be_dlpi_recv_disable(net_backend_t * be)298 be_dlpi_recv_disable(net_backend_t *be)
299 {
300 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
301 
302 	mevent_disable(priv->bdp_mevp);
303 }
304 
305 static uint64_t
be_dlpi_get_cap(net_backend_t * be)306 be_dlpi_get_cap(net_backend_t *be)
307 {
308 	return (0); /* no capabilities for now */
309 }
310 
311 static int
be_dlpi_set_cap(net_backend_t * be,uint64_t features,unsigned vnet_hdr_len)312 be_dlpi_set_cap(net_backend_t *be, uint64_t features,
313     unsigned vnet_hdr_len)
314 {
315 	return ((features || vnet_hdr_len) ? -1 : 0);
316 }
317 
318 static int
be_dlpi_get_mac(net_backend_t * be,void * buf,size_t * buflen)319 be_dlpi_get_mac(net_backend_t *be, void *buf, size_t *buflen)
320 {
321 	be_dlpi_priv_t *priv = NET_BE_PRIV(be);
322 	uchar_t physaddr[DLPI_PHYSADDR_MAX];
323 	size_t physaddrlen = DLPI_PHYSADDR_MAX;
324 	int ret;
325 
326 	if ((ret = dlpi_get_physaddr(priv->bdp_dhp, DL_CURR_PHYS_ADDR,
327 	    physaddr, &physaddrlen)) != DLPI_SUCCESS) {
328 		be_dlpi_err(ret, dlpi_linkname(priv->bdp_dhp),
329 		    "read MAC address failed");
330 		return (EINVAL);
331 	}
332 
333 	if (physaddrlen != ETHERADDRL) {
334 		EPRINTLN("%s: bad MAC address len %d",
335 		    dlpi_linkname(priv->bdp_dhp), physaddrlen);
336 		return (EINVAL);
337 	}
338 
339 	if (physaddrlen > *buflen) {
340 		EPRINTLN("%s: MAC address too long (%d bytes required)",
341 		    dlpi_linkname(priv->bdp_dhp), physaddrlen);
342 		return (ENOMEM);
343 	}
344 
345 	*buflen = physaddrlen;
346 	memcpy(buf, physaddr, *buflen);
347 
348 	return (0);
349 }
350 
351 static struct net_backend dlpi_backend = {
352 	.prefix = "dlpi",
353 	.priv_size = sizeof(struct be_dlpi_priv),
354 	.init = be_dlpi_init,
355 	.cleanup = be_dlpi_cleanup,
356 	.send = be_dlpi_send,
357 	.peek_recvlen = be_dlpi_peek_recvlen,
358 	.recv = be_dlpi_recv,
359 	.recv_enable = be_dlpi_recv_enable,
360 	.recv_disable = be_dlpi_recv_disable,
361 	.get_cap = be_dlpi_get_cap,
362 	.set_cap = be_dlpi_set_cap,
363 	.get_mac = be_dlpi_get_mac,
364 };
365 
366 DATA_SET(net_backend_set, dlpi_backend);
367