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