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 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 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 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 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 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 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 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 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 306 be_dlpi_get_cap(net_backend_t *be) 307 { 308 return (0); /* no capabilities for now */ 309 } 310 311 static int 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 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