xref: /freebsd/sbin/pflowctl/pflowctl.c (revision 78ae60b4)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
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  *
10  *    - Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *    - Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials provided
15  *      with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 #include <sys/cdefs.h>
32 
33 #include <err.h>
34 #include <errno.h>
35 #include <netdb.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 
40 #include <net/pflow.h>
41 
42 #include <netlink/netlink.h>
43 #include <netlink/netlink_generic.h>
44 #include <netlink/netlink_snl.h>
45 #include <netlink/netlink_snl_generic.h>
46 #include <netlink/netlink_snl_route.h>
47 
48 static int get(int id);
49 
50 extern char *__progname;
51 
52 static void
53 usage(void)
54 {
55 	fprintf(stderr,
56 "usage: %s [-la] [-d id]\n",
57 	    __progname);
58 
59 	exit(1);
60 }
61 
62 static int
63 pflow_to_id(const char *name)
64 {
65 	int ret, id;
66 
67 	ret = sscanf(name, "pflow%d", &id);
68 	if (ret == 1)
69 		return (id);
70 
71 	ret = sscanf(name, "%d", &id);
72 	if (ret == 1)
73 		return (id);
74 
75 	return (-1);
76 }
77 
78 struct pflowctl_list {
79 	int id;
80 };
81 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
82 #define	_OUT(_field)	offsetof(struct pflowctl_list, _field)
83 static struct snl_attr_parser ap_list[] = {
84 	{ .type = PFLOWNL_L_ID, .off = _OUT(id), .cb = snl_attr_get_int32 },
85 };
86 static struct snl_field_parser fp_list[] = {};
87 #undef _IN
88 #undef _OUT
89 SNL_DECLARE_PARSER(list_parser, struct genlmsghdr, fp_list, ap_list);
90 
91 static int
92 list(void)
93 {
94 	struct snl_state ss = {};
95 	struct snl_errmsg_data e = {};
96 	struct pflowctl_list l = {};
97 	struct snl_writer nw;
98 	struct nlmsghdr *hdr;
99 	uint32_t seq_id;
100 	int family_id;
101 
102 	snl_init(&ss, NETLINK_GENERIC);
103 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
104 	if (family_id == 0)
105 		errx(1, "pflow.ko is not loaded.");
106 
107 	snl_init_writer(&ss, &nw);
108 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_LIST);
109 
110 	hdr = snl_finalize_msg(&nw);
111 	if (hdr == NULL)
112 		return (ENOMEM);
113 	seq_id = hdr->nlmsg_seq;
114 
115 	snl_send_message(&ss, hdr);
116 
117 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
118 		if (! snl_parse_nlmsg(&ss, hdr, &list_parser, &l))
119 			continue;
120 
121 		get(l.id);
122 	}
123 
124 	if (e.error)
125 		errc(1, e.error, "failed to list");
126 
127 	return (0);
128 }
129 
130 struct pflowctl_create {
131 	int id;
132 };
133 #define	_IN(_field)	offsetof(struct genlmsghsdr, _field)
134 #define	_OUT(_field)	offsetof(struct pflowctl_create, _field)
135 static struct snl_attr_parser ap_create[] = {
136 	{ .type = PFLOWNL_CREATE_ID, .off = _OUT(id), .cb = snl_attr_get_int32 },
137 };
138 static struct snl_field_parser pf_create[] = {};
139 #undef _IN
140 #undef _OUT
141 SNL_DECLARE_PARSER(create_parser, struct genlmsghdr, pf_create, ap_create);
142 
143 static int
144 create(void)
145 {
146 	struct snl_state ss = {};
147 	struct snl_errmsg_data e = {};
148 	struct pflowctl_create c = {};
149 	struct snl_writer nw;
150 	struct nlmsghdr *hdr;
151 	uint32_t seq_id;
152 	int family_id;
153 
154 	snl_init(&ss, NETLINK_GENERIC);
155 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
156 	if (family_id == 0)
157 		errx(1, "pflow.ko is not loaded.");
158 
159 	snl_init_writer(&ss, &nw);
160 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_CREATE);
161 
162 	hdr = snl_finalize_msg(&nw);
163 	if (hdr == NULL)
164 		return (ENOMEM);
165 	seq_id = hdr->nlmsg_seq;
166 
167 	snl_send_message(&ss, hdr);
168 
169 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
170 		if (! snl_parse_nlmsg(&ss, hdr, &create_parser, &c))
171 			continue;
172 
173 		printf("pflow%d\n", c.id);
174 	}
175 
176 	if (e.error)
177 		errc(1, e.error, "failed to create");
178 
179 	return (0);
180 }
181 
182 static int
183 del(char *idstr)
184 {
185 	struct snl_state ss = {};
186 	struct snl_errmsg_data e = {};
187 	struct snl_writer nw;
188 	struct nlmsghdr *hdr;
189 	int family_id;
190 	int id;
191 
192 	id = pflow_to_id(idstr);
193 	if (id < 0)
194 		return (EINVAL);
195 
196 	snl_init(&ss, NETLINK_GENERIC);
197 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
198 	if (family_id == 0)
199 		errx(1, "pflow.ko is not loaded.");
200 
201 	snl_init_writer(&ss, &nw);
202 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_DEL);
203 
204 	snl_add_msg_attr_s32(&nw, PFLOWNL_DEL_ID, id);
205 
206 	hdr = snl_finalize_msg(&nw);
207 	if (hdr == NULL)
208 		return (ENOMEM);
209 
210 	snl_send_message(&ss, hdr);
211 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
212 
213 	if (e.error)
214 		errc(1, e.error, "failed to delete");
215 
216 	return (0);
217 }
218 
219 struct pflowctl_sockaddr {
220 	union {
221 		struct sockaddr_in in;
222 		struct sockaddr_in6 in6;
223 		struct sockaddr_storage storage;
224 	};
225 };
226 static bool
227 pflowctl_post_sockaddr(struct snl_state* ss __unused, void *target)
228 {
229 	struct pflowctl_sockaddr *s = (struct pflowctl_sockaddr *)target;
230 
231 	if (s->storage.ss_family == AF_INET)
232 		s->storage.ss_len = sizeof(struct sockaddr_in);
233 	else if (s->storage.ss_family == AF_INET6)
234 		s->storage.ss_len = sizeof(struct sockaddr_in6);
235 	else
236 		return (false);
237 
238 	return (true);
239 }
240 #define _OUT(_field)	offsetof(struct pflowctl_sockaddr, _field)
241 static struct snl_attr_parser nla_p_sockaddr[] = {
242 	{ .type = PFLOWNL_ADDR_FAMILY, .off = _OUT(in.sin_family), .cb = snl_attr_get_uint8 },
243 	{ .type = PFLOWNL_ADDR_PORT, .off = _OUT(in.sin_port), .cb = snl_attr_get_uint16 },
244 	{ .type = PFLOWNL_ADDR_IP, .off = _OUT(in.sin_addr), .cb = snl_attr_get_in_addr },
245 	{ .type = PFLOWNL_ADDR_IP6, .off = _OUT(in6.sin6_addr), .cb = snl_attr_get_in6_addr },
246 };
247 SNL_DECLARE_ATTR_PARSER_EXT(sockaddr_parser, 0, nla_p_sockaddr, pflowctl_post_sockaddr);
248 #undef _OUT
249 
250 struct pflowctl_get {
251 	int id;
252 	int version;
253 	struct pflowctl_sockaddr src;
254 	struct pflowctl_sockaddr dst;
255 	uint32_t obs_dom;
256 };
257 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
258 #define	_OUT(_field)	offsetof(struct pflowctl_get, _field)
259 static struct snl_attr_parser ap_get[] = {
260 	{ .type = PFLOWNL_GET_ID, .off = _OUT(id), .cb = snl_attr_get_int32 },
261 	{ .type = PFLOWNL_GET_VERSION, .off = _OUT(version), .cb = snl_attr_get_int16 },
262 	{ .type = PFLOWNL_GET_SRC, .off = _OUT(src), .arg = &sockaddr_parser, .cb = snl_attr_get_nested },
263 	{ .type = PFLOWNL_GET_DST, .off = _OUT(dst), .arg = &sockaddr_parser, .cb = snl_attr_get_nested },
264 	{ .type = PFLOWNL_GET_OBSERVATION_DOMAIN, .off = _OUT(obs_dom), .cb = snl_attr_get_uint32 },
265 };
266 static struct snl_field_parser fp_get[] = {};
267 #undef _IN
268 #undef _OUT
269 SNL_DECLARE_PARSER(get_parser, struct genlmsghdr, fp_get, ap_get);
270 
271 static void
272 print_sockaddr(const char *prefix, const struct sockaddr_storage *s)
273 {
274 	char buf[INET6_ADDRSTRLEN];
275 	int error;
276 
277 	if (s->ss_family != AF_INET && s->ss_family != AF_INET6)
278 		return;
279 
280 	if (s->ss_family == AF_INET ||
281 	    s->ss_family == AF_INET6) {
282 		error = getnameinfo((const struct sockaddr *)s,
283 		    s->ss_len, buf, sizeof(buf), NULL, 0,
284 		    NI_NUMERICHOST);
285 		if (error)
286 			err(1, "sender: %s", gai_strerror(error));
287 	}
288 
289 	printf("%s", prefix);
290 	switch (s->ss_family) {
291 	case AF_INET: {
292 		const struct sockaddr_in *sin = (const struct sockaddr_in *)s;
293 		if (sin->sin_addr.s_addr != INADDR_ANY) {
294 			printf("%s", buf);
295 			if (sin->sin_port != 0)
296 				printf(":%u", ntohs(sin->sin_port));
297 		}
298 		break;
299 	}
300 	case AF_INET6: {
301 		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)s;
302 		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
303 			printf("[%s]", buf);
304 			if (sin6->sin6_port != 0)
305 				printf(":%u", ntohs(sin6->sin6_port));
306 		}
307 		break;
308 	}
309 	}
310 }
311 
312 static int
313 get(int id)
314 {
315 	struct snl_state ss = {};
316 	struct snl_errmsg_data e = {};
317 	struct pflowctl_get g = {};
318 	struct snl_writer nw;
319 	struct nlmsghdr *hdr;
320 	uint32_t seq_id;
321 	int family_id;
322 
323 	snl_init(&ss, NETLINK_GENERIC);
324 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
325 	if (family_id == 0)
326 		errx(1, "pflow.ko is not loaded.");
327 
328 	snl_init_writer(&ss, &nw);
329 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_GET);
330 	snl_add_msg_attr_s32(&nw, PFLOWNL_GET_ID, id);
331 
332 	hdr = snl_finalize_msg(&nw);
333 	if (hdr == NULL)
334 		return (ENOMEM);
335 	seq_id = hdr->nlmsg_seq;
336 
337 	snl_send_message(&ss, hdr);
338 
339 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
340 		if (! snl_parse_nlmsg(&ss, hdr, &get_parser, &g))
341 			continue;
342 
343 		printf("pflow%d: version %d domain %d", g.id, g.version, g.obs_dom);
344 		print_sockaddr(" src ", &g.src.storage);
345 		print_sockaddr(" dst ", &g.dst.storage);
346 		printf("\n");
347 	}
348 
349 	if (e.error)
350 		errc(1, e.error, "failed to get");
351 
352 	return (0);
353 }
354 
355 struct pflowctl_set {
356 	int id;
357 	uint16_t version;
358 	struct sockaddr_storage src;
359 	struct sockaddr_storage dst;
360 	uint32_t obs_dom;
361 };
362 static inline bool
363 snl_add_msg_attr_sockaddr(struct snl_writer *nw, int attrtype, struct sockaddr_storage *s)
364 {
365 	int off = snl_add_msg_attr_nested(nw, attrtype);
366 
367 	snl_add_msg_attr_u8(nw, PFLOWNL_ADDR_FAMILY, s->ss_family);
368 
369 	switch (s->ss_family) {
370 	case AF_INET: {
371 		const struct sockaddr_in *in = (const struct sockaddr_in *)s;
372 		snl_add_msg_attr_u16(nw, PFLOWNL_ADDR_PORT, in->sin_port);
373 		snl_add_msg_attr_ip4(nw, PFLOWNL_ADDR_IP, &in->sin_addr);
374 		break;
375 	}
376 	case AF_INET6: {
377 		const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)s;
378 		snl_add_msg_attr_u16(nw, PFLOWNL_ADDR_PORT, in6->sin6_port);
379 		snl_add_msg_attr_ip6(nw, PFLOWNL_ADDR_IP6, &in6->sin6_addr);
380 		break;
381 	}
382 	default:
383 		return (false);
384 	}
385 	snl_end_attr_nested(nw, off);
386 
387 	return (true);
388 }
389 
390 static int
391 do_set(struct pflowctl_set *s)
392 {
393 	struct snl_state ss = {};
394 	struct snl_errmsg_data e = {};
395 	struct snl_writer nw;
396 	struct nlmsghdr *hdr;
397 	int family_id;
398 
399 	snl_init(&ss, NETLINK_GENERIC);
400 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
401 	if (family_id == 0)
402 		errx(1, "pflow.ko is not loaded.");
403 
404 	snl_init_writer(&ss, &nw);
405 	snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_SET);
406 
407 	snl_add_msg_attr_s32(&nw, PFLOWNL_SET_ID, s->id);
408 	if (s->version != 0)
409 		snl_add_msg_attr_u16(&nw, PFLOWNL_SET_VERSION, s->version);
410 	if (s->src.ss_len != 0)
411 		snl_add_msg_attr_sockaddr(&nw, PFLOWNL_SET_SRC, &s->src);
412 	if (s->dst.ss_len != 0)
413 		snl_add_msg_attr_sockaddr(&nw, PFLOWNL_SET_DST, &s->dst);
414 	if (s->obs_dom != 0)
415 		snl_add_msg_attr_u32(&nw, PFLOWNL_SET_OBSERVATION_DOMAIN, s->obs_dom);
416 
417 	hdr = snl_finalize_msg(&nw);
418 	if (hdr == NULL)
419 		return (1);
420 
421 	snl_send_message(&ss, hdr);
422 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
423 
424 	if (e.error)
425 		errc(1, e.error, "failed to set");
426 
427 	return (0);
428 }
429 
430 static void
431 pflowctl_addr(const char *val, struct sockaddr_storage *ss)
432 {
433 	struct addrinfo *res0;
434 	int error;
435 	bool flag;
436 	char *ip, *port;
437 	char buf[sysconf(_SC_HOST_NAME_MAX) + 1 + sizeof(":65535")];
438 	struct addrinfo hints = {
439 		.ai_family = AF_UNSPEC,
440 		.ai_socktype = SOCK_DGRAM, /*dummy*/
441 		.ai_flags = AI_NUMERICHOST,
442 	};
443 
444 	if (strlcpy(buf, val, sizeof(buf)) >= sizeof(buf))
445 		errx(1, "%s bad value", val);
446 
447 	port = NULL;
448 	flag = *buf == '[';
449 
450 	for (char *cp = buf; *cp; ++cp) {
451 		if (*cp == ']' && *(cp + 1) == ':' && flag) {
452 			*cp = '\0';
453 			*(cp + 1) = '\0';
454 			port = cp + 2;
455 			break;
456 		}
457 		if (*cp == ']' && *(cp + 1) == '\0' && flag) {
458 			*cp = '\0';
459 			port = NULL;
460 			break;
461 		}
462 		if (*cp == ':' && !flag) {
463 			*cp = '\0';
464 			port = cp + 1;
465 			break;
466 		}
467 	}
468 
469 	ip = buf;
470 	if (flag)
471 		ip++;
472 
473 	if ((error = getaddrinfo(ip, port, &hints, &res0)) != 0)
474 		errx(1, "error in parsing address string: %s",
475 		    gai_strerror(error));
476 
477 	memcpy(ss, res0->ai_addr, res0->ai_addr->sa_len);
478 	freeaddrinfo(res0);
479 }
480 
481 static int
482 set(char *idstr, int argc, char *argv[])
483 {
484 	struct pflowctl_set s = {};
485 
486 	s.id = pflow_to_id(idstr);
487 	if (s.id < 0)
488 		return (EINVAL);
489 
490 	while (argc > 0) {
491 		if (strcmp(argv[0], "src") == 0) {
492 			if (argc < 2)
493 				usage();
494 
495 			pflowctl_addr(argv[1], &s.src);
496 
497 			argc -= 2;
498 			argv += 2;
499 		} else if (strcmp(argv[0], "dst") == 0) {
500 			if (argc < 2)
501 				usage();
502 
503 			pflowctl_addr(argv[1], &s.dst);
504 
505 			argc -= 2;
506 			argv += 2;
507 		} else if (strcmp(argv[0], "proto") == 0) {
508 			if (argc < 2)
509 				usage();
510 
511 			s.version = strtol(argv[1], NULL, 10);
512 
513 			argc -= 2;
514 			argv += 2;
515 		} else if (strcmp(argv[0], "domain") == 0) {
516 			if (argc < 2)
517 				usage();
518 
519 			s.obs_dom = strtol(argv[1], NULL, 10);
520 
521 			argc -= 2;
522 			argv += 2;
523 		} else {
524 			usage();
525 		}
526 	}
527 
528 	return (do_set(&s));
529 }
530 
531 static const struct snl_hdr_parser *all_parsers[] = {
532 	&list_parser,
533 	&get_parser,
534 };
535 
536 int
537 main(int argc, char *argv[])
538 {
539 	int ch;
540 
541 	SNL_VERIFY_PARSERS(all_parsers);
542 
543 	if (argc < 2)
544 		usage();
545 
546 	while ((ch = getopt(argc, argv,
547 	    "lcd:s:")) != -1) {
548 		switch (ch) {
549 		case 'l':
550 			return (list());
551 		case 'c':
552 			return (create());
553 		case 'd':
554 			return (del(optarg));
555 		case 's':
556 			return (set(optarg, argc - optind, argv + optind));
557 		}
558 	}
559 
560 	return (0);
561 }
562