1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 Vincenzo Maffione <vmaffione@FreeBSD.org> 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 #ifndef WITHOUT_CAPSICUM 29 #include <sys/capsicum.h> 30 #endif 31 #include <sys/socket.h> 32 #include <sys/sysctl.h> 33 34 #ifndef WITHOUT_CAPSICUM 35 #include <capsicum_helpers.h> 36 #endif 37 #include <err.h> 38 #include <netgraph.h> 39 #include <string.h> 40 #include <sysexits.h> 41 #include <unistd.h> 42 43 #include "config.h" 44 #include "debug.h" 45 #include "net_backends.h" 46 #include "net_backends_priv.h" 47 48 #define NG_SBUF_MAX_SIZE (4 * 1024 * 1024) 49 50 static int 51 ng_init(struct net_backend *be, const char *devname __unused, 52 nvlist_t *nvl, net_be_rxeof_t cb, void *param) 53 { 54 struct tap_priv *p = NET_BE_PRIV(be); 55 struct ngm_connect ngc; 56 const char *value, *nodename; 57 int sbsz; 58 int ctrl_sock; 59 int flags; 60 unsigned long maxsbsz; 61 size_t msbsz; 62 #ifndef WITHOUT_CAPSICUM 63 cap_rights_t rights; 64 #endif 65 66 if (cb == NULL) { 67 EPRINTLN("Netgraph backend requires non-NULL callback"); 68 return (-1); 69 } 70 71 be->fd = -1; 72 73 memset(&ngc, 0, sizeof(ngc)); 74 75 value = get_config_value_node(nvl, "path"); 76 if (value == NULL) { 77 EPRINTLN("path must be provided"); 78 return (-1); 79 } 80 strncpy(ngc.path, value, NG_PATHSIZ - 1); 81 82 value = get_config_value_node(nvl, "hook"); 83 if (value == NULL) 84 value = "vmlink"; 85 strncpy(ngc.ourhook, value, NG_HOOKSIZ - 1); 86 87 value = get_config_value_node(nvl, "peerhook"); 88 if (value == NULL) { 89 EPRINTLN("peer hook must be provided"); 90 return (-1); 91 } 92 strncpy(ngc.peerhook, value, NG_HOOKSIZ - 1); 93 94 nodename = get_config_value_node(nvl, "socket"); 95 if (NgMkSockNode(nodename, 96 &ctrl_sock, &be->fd) < 0) { 97 EPRINTLN("can't get Netgraph sockets"); 98 return (-1); 99 } 100 101 if (NgSendMsg(ctrl_sock, ".", 102 NGM_GENERIC_COOKIE, 103 NGM_CONNECT, &ngc, sizeof(ngc)) < 0) { 104 EPRINTLN("can't connect to node"); 105 close(ctrl_sock); 106 goto error; 107 } 108 109 close(ctrl_sock); 110 111 flags = fcntl(be->fd, F_GETFL); 112 113 if (flags < 0) { 114 EPRINTLN("can't get socket flags"); 115 goto error; 116 } 117 118 if (fcntl(be->fd, F_SETFL, flags | O_NONBLOCK) < 0) { 119 EPRINTLN("can't set O_NONBLOCK flag"); 120 goto error; 121 } 122 123 /* 124 * The default ng_socket(4) buffer's size is too low. 125 * Calculate the minimum value between NG_SBUF_MAX_SIZE 126 * and kern.ipc.maxsockbuf. 127 */ 128 msbsz = sizeof(maxsbsz); 129 if (sysctlbyname("kern.ipc.maxsockbuf", &maxsbsz, &msbsz, 130 NULL, 0) < 0) { 131 EPRINTLN("can't get 'kern.ipc.maxsockbuf' value"); 132 goto error; 133 } 134 135 /* 136 * We can't set the socket buffer size to kern.ipc.maxsockbuf value, 137 * as it takes into account the mbuf(9) overhead. 138 */ 139 maxsbsz = maxsbsz * MCLBYTES / (MSIZE + MCLBYTES); 140 141 sbsz = MIN(NG_SBUF_MAX_SIZE, maxsbsz); 142 143 if (setsockopt(be->fd, SOL_SOCKET, SO_SNDBUF, &sbsz, 144 sizeof(sbsz)) < 0) { 145 EPRINTLN("can't set TX buffer size"); 146 goto error; 147 } 148 149 if (setsockopt(be->fd, SOL_SOCKET, SO_RCVBUF, &sbsz, 150 sizeof(sbsz)) < 0) { 151 EPRINTLN("can't set RX buffer size"); 152 goto error; 153 } 154 155 #ifndef WITHOUT_CAPSICUM 156 cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE); 157 if (caph_rights_limit(be->fd, &rights) == -1) 158 errx(EX_OSERR, "Unable to apply rights for sandbox"); 159 #endif 160 161 memset(p->bbuf, 0, sizeof(p->bbuf)); 162 p->bbuflen = 0; 163 164 p->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); 165 if (p->mevp == NULL) { 166 EPRINTLN("Could not register event"); 167 goto error; 168 } 169 170 return (0); 171 172 error: 173 tap_cleanup(be); 174 return (-1); 175 } 176 177 static struct net_backend ng_backend = { 178 .prefix = "netgraph", 179 .priv_size = sizeof(struct tap_priv), 180 .init = ng_init, 181 .cleanup = tap_cleanup, 182 .send = tap_send, 183 .peek_recvlen = tap_peek_recvlen, 184 .recv = tap_recv, 185 .recv_enable = tap_recv_enable, 186 .recv_disable = tap_recv_disable, 187 .get_cap = tap_get_cap, 188 .set_cap = tap_set_cap, 189 }; 190 191 DATA_SET(net_backend_set, ng_backend); 192