xref: /freebsd/sbin/ifconfig/ifpfsync.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
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 		memcpy(&addr, sin, sizeof(*sin));
231 		break;
232 	}
233 #endif
234 #ifdef INET6
235 	case AF_INET6: {
236 		struct sockaddr_in6 *sin6 = satosin6(peerres->ai_addr);
237 
238 		memcpy(&addr, sin6, sizeof(*sin6));
239 		break;
240 	}
241 #endif
242 	default:
243 		errx(1, "syncpeer address %s not supported", val);
244 	}
245 
246 	if (nvlist_exists_nvlist(nvl, "syncpeer"))
247 		nvlist_free_nvlist(nvl, "syncpeer");
248 
249 	nvlist_add_nvlist(nvl, "syncpeer",
250 	    pfsync_sockaddr_to_syncpeer_nvlist(&addr));
251 
252 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
253 		err(1, "SIOCSETPFSYNCNV");
254 
255 	nvlist_destroy(nvl);
256 	freeaddrinfo(peerres);
257 }
258 
259 static void
260 unsetpfsync_syncpeer(if_ctx *ctx, const char *val __unused, int dummy __unused)
261 {
262 	struct sockaddr_storage addr;
263 	memset(&addr, 0, sizeof(addr));
264 
265 	nvlist_t *nvl = nvlist_create(0);
266 
267 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
268 		err(1, "SIOCGETPFSYNCNV");
269 
270 	if (nvlist_exists_nvlist(nvl, "syncpeer"))
271 		nvlist_free_nvlist(nvl, "syncpeer");
272 
273 	nvlist_add_nvlist(nvl, "syncpeer",
274 	    pfsync_sockaddr_to_syncpeer_nvlist(&addr));
275 
276 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
277 		err(1, "SIOCSETPFSYNCNV");
278 
279 	nvlist_destroy(nvl);
280 }
281 
282 static void
283 setpfsync_maxupd(if_ctx *ctx, const char *val, int dummy __unused)
284 {
285 	int maxupdates;
286 	nvlist_t *nvl = nvlist_create(0);
287 
288 	maxupdates = atoi(val);
289 	if ((maxupdates < 0) || (maxupdates > 255))
290 		errx(1, "maxupd %s: out of range", val);
291 
292 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
293 		err(1, "SIOCGETPFSYNCNV");
294 
295 	nvlist_free_number(nvl, "maxupdates");
296 	nvlist_add_number(nvl, "maxupdates", maxupdates);
297 
298 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
299 		err(1, "SIOCSETPFSYNCNV");
300 
301 	nvlist_destroy(nvl);
302 }
303 
304 static void
305 setpfsync_defer(if_ctx *ctx, const char *val __unused, int d)
306 {
307 	nvlist_t *nvl = nvlist_create(0);
308 
309 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
310 		err(1, "SIOCGETPFSYNCNV");
311 
312 	nvlist_free_number(nvl, "flags");
313 	nvlist_add_number(nvl, "flags", d ? PFSYNCF_DEFER : 0);
314 
315 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
316 		err(1, "SIOCSETPFSYNCNV");
317 
318 	nvlist_destroy(nvl);
319 }
320 
321 static void
322 setpfsync_version(if_ctx *ctx, const char *val, int dummy __unused)
323 {
324 	int version;
325 	nvlist_t *nvl = nvlist_create(0);
326 
327 	/* Don't verify, kernel knows which versions are supported.*/
328 	version = atoi(val);
329 
330 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1)
331 		err(1, "SIOCGETPFSYNCNV");
332 
333 	nvlist_free_number(nvl, "version");
334 	nvlist_add_number(nvl, "version", version);
335 
336 	if (pfsync_do_ioctl(ctx, SIOCSETPFSYNCNV, &nvl) == -1)
337 		err(1, "SIOCSETPFSYNCNV");
338 
339 	nvlist_destroy(nvl);
340 }
341 
342 static void
343 pfsync_status(if_ctx *ctx)
344 {
345 	nvlist_t *nvl;
346 	char syncdev[IFNAMSIZ];
347 	char syncpeer_str[NI_MAXHOST];
348 	struct sockaddr_storage syncpeer;
349 	int maxupdates = 0;
350 	int flags = 0;
351 	int version;
352 	int error;
353 
354 	nvl = nvlist_create(0);
355 
356 	if (pfsync_do_ioctl(ctx, SIOCGETPFSYNCNV, &nvl) == -1) {
357 		nvlist_destroy(nvl);
358 		return;
359 	}
360 
361 	memset((char *)&syncdev, 0, IFNAMSIZ);
362 	if (nvlist_exists_string(nvl, "syncdev"))
363 		strlcpy(syncdev, nvlist_get_string(nvl, "syncdev"),
364 		    IFNAMSIZ);
365 	if (nvlist_exists_number(nvl, "maxupdates"))
366 		maxupdates = nvlist_get_number(nvl, "maxupdates");
367 	if (nvlist_exists_number(nvl, "version"))
368 		version = nvlist_get_number(nvl, "version");
369 	if (nvlist_exists_number(nvl, "flags"))
370 		flags = nvlist_get_number(nvl, "flags");
371 	if (nvlist_exists_nvlist(nvl, "syncpeer")) {
372 		pfsync_syncpeer_nvlist_to_sockaddr(nvlist_get_nvlist(nvl,
373 							     "syncpeer"),
374 		    &syncpeer);
375 	}
376 
377 	nvlist_destroy(nvl);
378 
379 	if (syncdev[0] != '\0' || syncpeer.ss_family != AF_UNSPEC)
380 		printf("\t");
381 
382 	if (syncdev[0] != '\0')
383 		printf("syncdev: %s ", syncdev);
384 
385 	if ((syncpeer.ss_family == AF_INET &&
386 	    ((struct sockaddr_in *)&syncpeer)->sin_addr.s_addr !=
387 	    htonl(INADDR_PFSYNC_GROUP)) || syncpeer.ss_family == AF_INET6) {
388 
389 		struct sockaddr *syncpeer_sa =
390 		    (struct sockaddr *)&syncpeer;
391 		if ((error = getnameinfo(syncpeer_sa, syncpeer_sa->sa_len,
392 			 syncpeer_str, sizeof(syncpeer_str), NULL, 0,
393 			 NI_NUMERICHOST)) != 0)
394 			errx(1, "getnameinfo: %s", gai_strerror(error));
395 		printf("syncpeer: %s ", syncpeer_str);
396 	}
397 
398 	printf("maxupd: %d ", maxupdates);
399 	printf("defer: %s ", (flags & PFSYNCF_DEFER) ? "on" : "off");
400 	printf("version: %d\n", version);
401 	printf("\tsyncok: %d\n", (flags & PFSYNCF_OK) ? 1 : 0);
402 }
403 
404 static struct cmd pfsync_cmds[] = {
405 	DEF_CMD_ARG("syncdev",		setpfsync_syncdev),
406 	DEF_CMD("-syncdev",	1,	unsetpfsync_syncdev),
407 	DEF_CMD_ARG("syncif",		setpfsync_syncdev),
408 	DEF_CMD("-syncif",	1,	unsetpfsync_syncdev),
409 	DEF_CMD_ARG("syncpeer",		setpfsync_syncpeer),
410 	DEF_CMD("-syncpeer",	1,	unsetpfsync_syncpeer),
411 	DEF_CMD_ARG("maxupd",		setpfsync_maxupd),
412 	DEF_CMD("defer",	1,	setpfsync_defer),
413 	DEF_CMD("-defer",	0,	setpfsync_defer),
414 	DEF_CMD_ARG("version",		setpfsync_version),
415 };
416 static struct afswtch af_pfsync = {
417 	.af_name	= "af_pfsync",
418 	.af_af		= AF_UNSPEC,
419 	.af_other_status = pfsync_status,
420 };
421 
422 static __constructor void
423 pfsync_ctor(void)
424 {
425 	for (size_t i = 0; i < nitems(pfsync_cmds);  i++)
426 		cmd_register(&pfsync_cmds[i]);
427 	af_register(&af_pfsync);
428 }
429