xref: /freebsd/sbin/ifconfig/ifpfsync.c (revision 43e29d03f416d7dda52112a29600a7c82ee1a91e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003 Ryan McBride. All rights reserved.
5  * Copyright (c) 2004 Max Laier. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 #include <sys/ioctl.h>
34 #include <sys/nv.h>
35 #include <sys/socket.h>
36 
37 #include <net/if.h>
38 #include <netinet/in.h>
39 #include <net/pfvar.h>
40 #include <net/if_pfsync.h>
41 #include <net/route.h>
42 #include <arpa/inet.h>
43 
44 #include <err.h>
45 #include <netdb.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include "ifconfig.h"
52 
53 static int
54 pfsync_do_ioctl(if_ctx *ctx, uint cmd, nvlist_t **nvl)
55 {
56 	void *data;
57 	size_t nvlen;
58 	struct ifreq ifr = {};
59 
60 	data = nvlist_pack(*nvl, &nvlen);
61 
62 	ifr.ifr_cap_nv.buffer = malloc(IFR_CAP_NV_MAXBUFSIZE);
63 	memcpy(ifr.ifr_cap_nv.buffer, data, nvlen);
64 	ifr.ifr_cap_nv.buf_length = IFR_CAP_NV_MAXBUFSIZE;
65 	ifr.ifr_cap_nv.length = nvlen;
66 	free(data);
67 
68 	if (ioctl_ctx_ifr(ctx, cmd, &ifr) == -1) {
69 		free(ifr.ifr_cap_nv.buffer);
70 		return -1;
71 	}
72 
73 	nvlist_destroy(*nvl);
74 	*nvl = NULL;
75 
76 	*nvl = nvlist_unpack(ifr.ifr_cap_nv.buffer, ifr.ifr_cap_nv.length, 0);
77 	if (*nvl == NULL) {
78 		free(ifr.ifr_cap_nv.buffer);
79 		return (EIO);
80 	}
81 
82 	free(ifr.ifr_cap_nv.buffer);
83 	return (errno);
84 }
85 
86 static nvlist_t *
87 pfsync_sockaddr_to_syncpeer_nvlist(struct sockaddr_storage *sa)
88 {
89 	nvlist_t *nvl;
90 
91 	nvl = nvlist_create(0);
92 	if (nvl == NULL) {
93 		return (nvl);
94 	}
95 
96 	switch (sa->ss_family) {
97 #ifdef INET
98 	case AF_INET: {
99 		struct sockaddr_in *in = (struct sockaddr_in *)sa;
100 		nvlist_add_number(nvl, "af", in->sin_family);
101 		nvlist_add_binary(nvl, "address", in, sizeof(*in));
102 		break;
103 	}
104 #endif
105 #ifdef INET6
106 	case AF_INET6: {
107 		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
108 		nvlist_add_number(nvl, "af", in6->sin6_family);
109 		nvlist_add_binary(nvl, "address", in6, sizeof(*in6));
110 		break;
111 	}
112 #endif
113 	default:
114 		nvlist_add_number(nvl, "af", AF_UNSPEC);
115 		nvlist_add_binary(nvl, "address", sa, sizeof(*sa));
116 		break;
117 	}
118 
119 	return (nvl);
120 }
121 
122 static int
123 pfsync_syncpeer_nvlist_to_sockaddr(const nvlist_t *nvl,
124     struct sockaddr_storage *sa)
125 {
126 	int af;
127 
128 #if (!defined INET && !defined INET6)
129 	(void)sa;
130 #endif
131 
132 	if (!nvlist_exists_number(nvl, "af"))
133 		return (EINVAL);
134 	if (!nvlist_exists_binary(nvl, "address"))
135 		return (EINVAL);
136 
137 	af = nvlist_get_number(nvl, "af");
138 
139 	switch (af) {
140 #ifdef INET
141 	case AF_INET: {
142 		struct sockaddr_in *in = (struct sockaddr_in *)sa;
143 		size_t len;
144 		const void *addr = nvlist_get_binary(nvl, "address", &len);
145 		in->sin_family = af;
146 		if (len != sizeof(*in))
147 			return (EINVAL);
148 
149 		memcpy(in, addr, sizeof(*in));
150 		break;
151 	}
152 #endif
153 #ifdef INET6
154 	case AF_INET6: {
155 		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
156 		size_t len;
157 		const void *addr = nvlist_get_binary(nvl, "address", &len);
158 		if (len != sizeof(*in6))
159 			return (EINVAL);
160 
161 		memcpy(in6, addr, sizeof(*in6));
162 		break;
163 	}
164 #endif
165 	default:
166 		return (EINVAL);
167 	}
168 
169 	return (0);
170 }
171 
172 static void
173 setpfsync_syncdev(if_ctx *ctx, const char *val, int dummy __unused)
174 {
175 	nvlist_t *nvl = nvlist_create(0);
176 
177 	if (strlen(val) > IFNAMSIZ)
178 		errx(1, "interface name %s is too long", val);
179 
180 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
181 		err(1, "SIOCGETPFSYNCNV");
182 
183 	if (nvlist_exists_string(nvl, "syncdev"))
184 		nvlist_free_string(nvl, "syncdev");
185 
186 	nvlist_add_string(nvl, "syncdev", val);
187 
188 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
189 		err(1, "SIOCSETPFSYNCNV");
190 }
191 
192 static void
193 unsetpfsync_syncdev(if_ctx *ctx, const char *val __unused, int dummy __unused)
194 {
195 	nvlist_t *nvl = nvlist_create(0);
196 
197 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
198 		err(1, "SIOCGETPFSYNCNV");
199 
200 	if (nvlist_exists_string(nvl, "syncdev"))
201 		nvlist_free_string(nvl, "syncdev");
202 
203 	nvlist_add_string(nvl, "syncdev", "");
204 
205 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
206 		err(1, "SIOCSETPFSYNCNV");
207 }
208 
209 static void
210 setpfsync_syncpeer(if_ctx *ctx, const char *val, int dummy __unused)
211 {
212 	struct addrinfo *peerres;
213 	struct sockaddr_storage addr;
214 	int ecode;
215 
216 	nvlist_t *nvl = nvlist_create(0);
217 
218 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
219 		err(1, "SIOCGETPFSYNCNV");
220 
221 	if ((ecode = getaddrinfo(val, NULL, NULL, &peerres)) != 0)
222 		errx(1, "error in parsing address string: %s",
223 		    gai_strerror(ecode));
224 
225 	switch (peerres->ai_family) {
226 #ifdef INET
227 	case AF_INET: {
228 		struct sockaddr_in *sin = satosin(peerres->ai_addr);
229 
230 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
231 			errx(1, "syncpeer address cannot be multicast");
232 
233 		memcpy(&addr, sin, sizeof(*sin));
234 		break;
235 	}
236 #endif
237 	default:
238 		errx(1, "syncpeer address %s not supported", val);
239 	}
240 
241 	if (nvlist_exists_nvlist(nvl, "syncpeer"))
242 		nvlist_free_nvlist(nvl, "syncpeer");
243 
244 	nvlist_add_nvlist(nvl, "syncpeer",
245 	    pfsync_sockaddr_to_syncpeer_nvlist(&addr));
246 
247 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
248 		err(1, "SIOCSETPFSYNCNV");
249 
250 	nvlist_destroy(nvl);
251 	freeaddrinfo(peerres);
252 }
253 
254 static void
255 unsetpfsync_syncpeer(if_ctx *ctx, const char *val __unused, int dummy __unused)
256 {
257 	struct sockaddr_storage addr;
258 	memset(&addr, 0, sizeof(addr));
259 
260 	nvlist_t *nvl = nvlist_create(0);
261 
262 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
263 		err(1, "SIOCGETPFSYNCNV");
264 
265 	if (nvlist_exists_nvlist(nvl, "syncpeer"))
266 		nvlist_free_nvlist(nvl, "syncpeer");
267 
268 	nvlist_add_nvlist(nvl, "syncpeer",
269 	    pfsync_sockaddr_to_syncpeer_nvlist(&addr));
270 
271 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
272 		err(1, "SIOCSETPFSYNCNV");
273 
274 	nvlist_destroy(nvl);
275 }
276 
277 static void
278 setpfsync_maxupd(if_ctx *ctx, const char *val, int dummy __unused)
279 {
280 	int maxupdates;
281 	nvlist_t *nvl = nvlist_create(0);
282 
283 	maxupdates = atoi(val);
284 	if ((maxupdates < 0) || (maxupdates > 255))
285 		errx(1, "maxupd %s: out of range", val);
286 
287 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
288 		err(1, "SIOCGETPFSYNCNV");
289 
290 	nvlist_free_number(nvl, "maxupdates");
291 	nvlist_add_number(nvl, "maxupdates", maxupdates);
292 
293 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
294 		err(1, "SIOCSETPFSYNCNV");
295 
296 	nvlist_destroy(nvl);
297 }
298 
299 static void
300 setpfsync_defer(if_ctx *ctx, const char *val __unused, int d)
301 {
302 	nvlist_t *nvl = nvlist_create(0);
303 
304 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
305 		err(1, "SIOCGETPFSYNCNV");
306 
307 	nvlist_free_number(nvl, "flags");
308 	nvlist_add_number(nvl, "flags", d ? PFSYNCF_DEFER : 0);
309 
310 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
311 		err(1, "SIOCSETPFSYNCNV");
312 
313 	nvlist_destroy(nvl);
314 }
315 
316 static void
317 setpfsync_version(if_ctx *ctx, const char *val, int dummy __unused)
318 {
319 	int version;
320 	nvlist_t *nvl = nvlist_create(0);
321 
322 	/* Don't verify, kernel knows which versions are supported.*/
323 	version = atoi(val);
324 
325 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
326 		err(1, "SIOCGETPFSYNCNV");
327 
328 	nvlist_free_number(nvl, "version");
329 	nvlist_add_number(nvl, "version", version);
330 
331 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
332 		err(1, "SIOCSETPFSYNCNV");
333 
334 	nvlist_destroy(nvl);
335 }
336 
337 static void
338 pfsync_status(if_ctx *ctx)
339 {
340 	nvlist_t *nvl;
341 	char syncdev[IFNAMSIZ];
342 	char syncpeer_str[NI_MAXHOST];
343 	struct sockaddr_storage syncpeer;
344 	int maxupdates = 0;
345 	int flags = 0;
346 	int version;
347 	int error;
348 
349 	nvl = nvlist_create(0);
350 
351 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1) {
352 		nvlist_destroy(nvl);
353 		return;
354 	}
355 
356 	memset((char *)&syncdev, 0, IFNAMSIZ);
357 	if (nvlist_exists_string(nvl, "syncdev"))
358 		strlcpy(syncdev, nvlist_get_string(nvl, "syncdev"),
359 		    IFNAMSIZ);
360 	if (nvlist_exists_number(nvl, "maxupdates"))
361 		maxupdates = nvlist_get_number(nvl, "maxupdates");
362 	if (nvlist_exists_number(nvl, "version"))
363 		version = nvlist_get_number(nvl, "version");
364 	if (nvlist_exists_number(nvl, "flags"))
365 		flags = nvlist_get_number(nvl, "flags");
366 	if (nvlist_exists_nvlist(nvl, "syncpeer")) {
367 		pfsync_syncpeer_nvlist_to_sockaddr(nvlist_get_nvlist(nvl,
368 							     "syncpeer"),
369 		    &syncpeer);
370 	}
371 
372 	nvlist_destroy(nvl);
373 
374 	if (syncdev[0] != '\0' || syncpeer.ss_family != AF_UNSPEC)
375 		printf("\t");
376 
377 	if (syncdev[0] != '\0')
378 		printf("syncdev: %s ", syncdev);
379 
380 	if (syncpeer.ss_family == AF_INET &&
381 	    ((struct sockaddr_in *)&syncpeer)->sin_addr.s_addr !=
382 		htonl(INADDR_PFSYNC_GROUP)) {
383 
384 		struct sockaddr *syncpeer_sa =
385 		    (struct sockaddr *)&syncpeer;
386 		if ((error = getnameinfo(syncpeer_sa, syncpeer_sa->sa_len,
387 			 syncpeer_str, sizeof(syncpeer_str), NULL, 0,
388 			 NI_NUMERICHOST)) != 0)
389 			errx(1, "getnameinfo: %s", gai_strerror(error));
390 		printf("syncpeer: %s ", syncpeer_str);
391 	}
392 
393 	printf("maxupd: %d ", maxupdates);
394 	printf("defer: %s ", (flags & PFSYNCF_DEFER) ? "on" : "off");
395 	printf("version: %d\n", version);
396 	printf("\tsyncok: %d\n", (flags & PFSYNCF_OK) ? 1 : 0);
397 }
398 
399 static struct cmd pfsync_cmds[] = {
400 	DEF_CMD_ARG("syncdev",		setpfsync_syncdev),
401 	DEF_CMD("-syncdev",	1,	unsetpfsync_syncdev),
402 	DEF_CMD_ARG("syncif",		setpfsync_syncdev),
403 	DEF_CMD("-syncif",	1,	unsetpfsync_syncdev),
404 	DEF_CMD_ARG("syncpeer",		setpfsync_syncpeer),
405 	DEF_CMD("-syncpeer",	1,	unsetpfsync_syncpeer),
406 	DEF_CMD_ARG("maxupd",		setpfsync_maxupd),
407 	DEF_CMD("defer",	1,	setpfsync_defer),
408 	DEF_CMD("-defer",	0,	setpfsync_defer),
409 	DEF_CMD_ARG("version",		setpfsync_version),
410 };
411 static struct afswtch af_pfsync = {
412 	.af_name	= "af_pfsync",
413 	.af_af		= AF_UNSPEC,
414 	.af_other_status = pfsync_status,
415 };
416 
417 static __constructor void
418 pfsync_ctor(void)
419 {
420 	for (size_t i = 0; i < nitems(pfsync_cmds);  i++)
421 		cmd_register(&pfsync_cmds[i]);
422 	af_register(&af_pfsync);
423 }
424