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
50static int
51ng_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
172error:
173	tap_cleanup(be);
174	return (-1);
175}
176
177static 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
191DATA_SET(net_backend_set, ng_backend);
192