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
ng_init(struct net_backend * be,const char * devname __unused,nvlist_t * nvl,net_be_rxeof_t cb,void * param)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