/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2024 OmniOS Community Edition (OmniOSce) Association. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * The illumos dlpi backend */ #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "iov.h" #include "mevent.h" #include "net_backends.h" #include "net_backends_priv.h" /* * The size of the bounce buffer used to implement the peek callback. * This value should be big enough to accommodate the largest of all possible * frontend packet lengths. The value here matches the definition of * VTNET_MAX_PKT_LEN in pci_virtio_net.c */ #define DLPI_BBUF_SIZE (65536 + 64) typedef struct be_dlpi_priv { dlpi_handle_t bdp_dhp; struct mevent *bdp_mevp; /* * A bounce buffer that allows us to implement the peek_recvlen * callback. Each structure is only used by a single thread so * one is enough. */ uint8_t bdp_bbuf[DLPI_BBUF_SIZE]; ssize_t bdp_bbuflen; } be_dlpi_priv_t; static void be_dlpi_cleanup(net_backend_t *be) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); if (priv->bdp_dhp != NULL) dlpi_close(priv->bdp_dhp); priv->bdp_dhp = NULL; if (priv->bdp_mevp != NULL) mevent_delete(priv->bdp_mevp); priv->bdp_mevp = NULL; priv->bdp_bbuflen = 0; be->fd = -1; } static void be_dlpi_err(int ret, const char *dev, char *msg) { EPRINTLN("%s: %s (%s)", dev, msg, dlpi_strerror(ret)); } static int be_dlpi_init(net_backend_t *be, const char *devname __unused, nvlist_t *nvl, net_be_rxeof_t cb, void *param) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); const char *vnic; int ret; if (cb == NULL) { EPRINTLN("dlpi backend requires non-NULL callback"); return (-1); } vnic = get_config_value_node(nvl, "vnic"); if (vnic == NULL) { EPRINTLN("dlpi backend requires a VNIC"); return (-1); } priv->bdp_bbuflen = 0; ret = dlpi_open(vnic, &priv->bdp_dhp, DLPI_RAW); if (ret != DLPI_SUCCESS) { be_dlpi_err(ret, vnic, "open failed"); goto error; } if ((ret = dlpi_bind(priv->bdp_dhp, DLPI_ANY_SAP, NULL)) != DLPI_SUCCESS) { be_dlpi_err(ret, vnic, "bind failed"); goto error; } if (get_config_bool_node_default(nvl, "promiscrxonly", true)) { if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_RX_ONLY)) != DLPI_SUCCESS) { be_dlpi_err(ret, vnic, "enable promiscuous mode(rxonly) failed"); goto error; } } if (get_config_bool_node_default(nvl, "promiscphys", false)) { if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_PHYS)) != DLPI_SUCCESS) { be_dlpi_err(ret, vnic, "enable promiscuous mode(physical) failed"); goto error; } } if (get_config_bool_node_default(nvl, "promiscsap", true)) { if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_SAP)) != DLPI_SUCCESS) { be_dlpi_err(ret, vnic, "enable promiscuous mode(SAP) failed"); goto error; } } if (get_config_bool_node_default(nvl, "promiscmulti", true)) { if ((ret = dlpi_promiscon(priv->bdp_dhp, DL_PROMISC_MULTI)) != DLPI_SUCCESS) { be_dlpi_err(ret, vnic, "enable promiscuous mode(muticast) failed"); goto error; } } be->fd = dlpi_fd(priv->bdp_dhp); if (fcntl(be->fd, F_SETFL, O_NONBLOCK) < 0) { EPRINTLN("%s: enable O_NONBLOCK failed", vnic); goto error; } priv->bdp_mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); if (priv->bdp_mevp == NULL) { EPRINTLN("Could not register event"); goto error; } return (0); error: be_dlpi_cleanup(be); return (-1); } /* * Called to send a buffer chain out to the dlpi device */ static ssize_t be_dlpi_send(net_backend_t *be, const struct iovec *iov, int iovcnt) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); ssize_t len = 0; int ret; if (iovcnt == 1) { len = iov[0].iov_len; ret = dlpi_send(priv->bdp_dhp, NULL, 0, iov[0].iov_base, len, NULL); } else { void *buf = NULL; len = iov_to_buf(iov, iovcnt, &buf); if (len <= 0 || buf == NULL) return (-1); ret = dlpi_send(priv->bdp_dhp, NULL, 0, buf, len, NULL); free(buf); } if (ret != DLPI_SUCCESS) return (-1); return (len); } static ssize_t be_dlpi_peek_recvlen(net_backend_t *be) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); dlpi_recvinfo_t recv; size_t len; int ret; /* * We already have a packet in the bounce buffer. * Just return its length. */ if (priv->bdp_bbuflen > 0) return (priv->bdp_bbuflen); /* * Read the next packet (if any) into the bounce buffer, so * that we get to know its length and we can return that * to the caller. */ len = sizeof (priv->bdp_bbuf); ret = dlpi_recv(priv->bdp_dhp, NULL, NULL, priv->bdp_bbuf, &len, 0, &recv); if (ret == DL_SYSERR) { if (errno == EWOULDBLOCK) return (0); return (-1); } else if (ret == DLPI_ETIMEDOUT) { return (0); } else if (ret != DLPI_SUCCESS) { return (-1); } if (recv.dri_totmsglen > sizeof (priv->bdp_bbuf)) { EPRINTLN("DLPI bounce buffer was too small! - needed %x bytes", recv.dri_totmsglen); } priv->bdp_bbuflen = len; return (len); } static ssize_t be_dlpi_recv(net_backend_t *be, const struct iovec *iov, int iovcnt) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); size_t len; int ret; if (priv->bdp_bbuflen > 0) { /* * A packet is available in the bounce buffer, so * we read it from there. */ len = buf_to_iov(priv->bdp_bbuf, priv->bdp_bbuflen, iov, iovcnt, 0); /* Mark the bounce buffer as empty. */ priv->bdp_bbuflen = 0; return (len); } len = iov[0].iov_len; ret = dlpi_recv(priv->bdp_dhp, NULL, NULL, (uint8_t *)iov[0].iov_base, &len, 0, NULL); if (ret == DL_SYSERR) { if (errno == EWOULDBLOCK) return (0); return (-1); } else if (ret == DLPI_ETIMEDOUT) { return (0); } else if (ret != DLPI_SUCCESS) { return (-1); } return (len); } static void be_dlpi_recv_enable(net_backend_t *be) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); mevent_enable(priv->bdp_mevp); } static void be_dlpi_recv_disable(net_backend_t *be) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); mevent_disable(priv->bdp_mevp); } static uint64_t be_dlpi_get_cap(net_backend_t *be) { return (0); /* no capabilities for now */ } static int be_dlpi_set_cap(net_backend_t *be, uint64_t features, unsigned vnet_hdr_len) { return ((features || vnet_hdr_len) ? -1 : 0); } static int be_dlpi_get_mac(net_backend_t *be, void *buf, size_t *buflen) { be_dlpi_priv_t *priv = NET_BE_PRIV(be); uchar_t physaddr[DLPI_PHYSADDR_MAX]; size_t physaddrlen = DLPI_PHYSADDR_MAX; int ret; if ((ret = dlpi_get_physaddr(priv->bdp_dhp, DL_CURR_PHYS_ADDR, physaddr, &physaddrlen)) != DLPI_SUCCESS) { be_dlpi_err(ret, dlpi_linkname(priv->bdp_dhp), "read MAC address failed"); return (EINVAL); } if (physaddrlen != ETHERADDRL) { EPRINTLN("%s: bad MAC address len %d", dlpi_linkname(priv->bdp_dhp), physaddrlen); return (EINVAL); } if (physaddrlen > *buflen) { EPRINTLN("%s: MAC address too long (%d bytes required)", dlpi_linkname(priv->bdp_dhp), physaddrlen); return (ENOMEM); } *buflen = physaddrlen; memcpy(buf, physaddr, *buflen); return (0); } static struct net_backend dlpi_backend = { .prefix = "dlpi", .priv_size = sizeof(struct be_dlpi_priv), .init = be_dlpi_init, .cleanup = be_dlpi_cleanup, .send = be_dlpi_send, .peek_recvlen = be_dlpi_peek_recvlen, .recv = be_dlpi_recv, .recv_enable = be_dlpi_recv_enable, .recv_disable = be_dlpi_recv_disable, .get_cap = be_dlpi_get_cap, .set_cap = be_dlpi_set_cap, .get_mac = be_dlpi_get_mac, }; DATA_SET(net_backend_set, dlpi_backend);