xref: /freebsd/sbin/pflowctl/pflowctl.c (revision 5f047ae0c73b4424b29e64d50b71346b9abd058e)
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 static bool verbose = false;
51 
52 extern char *__progname;
53 
54 static void
usage(void)55 usage(void)
56 {
57 	fprintf(stderr,
58 "usage: %s [-lc] [-d id] [-s id ...] [-v]\n",
59 	    __progname);
60 
61 	exit(1);
62 }
63 
64 static int
pflow_to_id(const char * name)65 pflow_to_id(const char *name)
66 {
67 	int ret, id;
68 
69 	ret = sscanf(name, "pflow%d", &id);
70 	if (ret == 1)
71 		return (id);
72 
73 	ret = sscanf(name, "%d", &id);
74 	if (ret == 1)
75 		return (id);
76 
77 	return (-1);
78 }
79 
80 struct pflowctl_list {
81 	int id;
82 };
83 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
84 #define	_OUT(_field)	offsetof(struct pflowctl_list, _field)
85 static struct snl_attr_parser ap_list[] = {
86 	{ .type = PFLOWNL_L_ID, .off = _OUT(id), .cb = snl_attr_get_int32 },
87 };
88 static struct snl_field_parser fp_list[] = {};
89 #undef _IN
90 #undef _OUT
91 SNL_DECLARE_PARSER(list_parser, struct genlmsghdr, fp_list, ap_list);
92 
93 static int
list(void)94 list(void)
95 {
96 	struct snl_state ss = {};
97 	struct snl_errmsg_data e = {};
98 	struct pflowctl_list l = {};
99 	struct snl_writer nw;
100 	struct nlmsghdr *hdr;
101 	uint32_t seq_id;
102 	int family_id;
103 
104 	snl_init(&ss, NETLINK_GENERIC);
105 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
106 	if (family_id == 0)
107 		errx(1, "pflow.ko is not loaded.");
108 
109 	snl_init_writer(&ss, &nw);
110 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_LIST);
111 
112 	hdr = snl_finalize_msg(&nw);
113 	if (hdr == NULL)
114 		return (ENOMEM);
115 	seq_id = hdr->nlmsg_seq;
116 
117 	snl_send_message(&ss, hdr);
118 
119 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
120 		if (! snl_parse_nlmsg(&ss, hdr, &list_parser, &l))
121 			continue;
122 
123 		get(l.id);
124 	}
125 
126 	if (e.error)
127 		errc(1, e.error, "failed to list");
128 
129 	return (0);
130 }
131 
132 struct pflowctl_create {
133 	int id;
134 };
135 #define	_IN(_field)	offsetof(struct genlmsghsdr, _field)
136 #define	_OUT(_field)	offsetof(struct pflowctl_create, _field)
137 static struct snl_attr_parser ap_create[] = {
138 	{ .type = PFLOWNL_CREATE_ID, .off = _OUT(id), .cb = snl_attr_get_int32 },
139 };
140 static struct snl_field_parser pf_create[] = {};
141 #undef _IN
142 #undef _OUT
143 SNL_DECLARE_PARSER(create_parser, struct genlmsghdr, pf_create, ap_create);
144 
145 static int
create(void)146 create(void)
147 {
148 	struct snl_state ss = {};
149 	struct snl_errmsg_data e = {};
150 	struct pflowctl_create c = {};
151 	struct snl_writer nw;
152 	struct nlmsghdr *hdr;
153 	uint32_t seq_id;
154 	int family_id;
155 
156 	snl_init(&ss, NETLINK_GENERIC);
157 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
158 	if (family_id == 0)
159 		errx(1, "pflow.ko is not loaded.");
160 
161 	snl_init_writer(&ss, &nw);
162 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_CREATE);
163 
164 	hdr = snl_finalize_msg(&nw);
165 	if (hdr == NULL)
166 		return (ENOMEM);
167 	seq_id = hdr->nlmsg_seq;
168 
169 	snl_send_message(&ss, hdr);
170 
171 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
172 		if (! snl_parse_nlmsg(&ss, hdr, &create_parser, &c))
173 			continue;
174 
175 		printf("pflow%d\n", c.id);
176 	}
177 
178 	if (e.error)
179 		errc(1, e.error, "failed to create");
180 
181 	return (0);
182 }
183 
184 static int
del(char * idstr)185 del(char *idstr)
186 {
187 	struct snl_state ss = {};
188 	struct snl_errmsg_data e = {};
189 	struct snl_writer nw;
190 	struct nlmsghdr *hdr;
191 	int family_id;
192 	int id;
193 
194 	id = pflow_to_id(idstr);
195 	if (id < 0)
196 		return (EINVAL);
197 
198 	snl_init(&ss, NETLINK_GENERIC);
199 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
200 	if (family_id == 0)
201 		errx(1, "pflow.ko is not loaded.");
202 
203 	snl_init_writer(&ss, &nw);
204 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_DEL);
205 
206 	snl_add_msg_attr_s32(&nw, PFLOWNL_DEL_ID, id);
207 
208 	hdr = snl_finalize_msg(&nw);
209 	if (hdr == NULL)
210 		return (ENOMEM);
211 
212 	snl_send_message(&ss, hdr);
213 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
214 
215 	if (e.error)
216 		errc(1, e.error, "failed to delete");
217 
218 	return (0);
219 }
220 
221 struct pflowctl_sockaddr {
222 	union {
223 		struct sockaddr_in in;
224 		struct sockaddr_in6 in6;
225 		struct sockaddr_storage storage;
226 	};
227 };
228 static bool
pflowctl_post_sockaddr(struct snl_state * ss __unused,void * target)229 pflowctl_post_sockaddr(struct snl_state* ss __unused, void *target)
230 {
231 	struct pflowctl_sockaddr *s = (struct pflowctl_sockaddr *)target;
232 
233 	if (s->storage.ss_family == AF_INET)
234 		s->storage.ss_len = sizeof(struct sockaddr_in);
235 	else if (s->storage.ss_family == AF_INET6)
236 		s->storage.ss_len = sizeof(struct sockaddr_in6);
237 	else
238 		return (false);
239 
240 	return (true);
241 }
242 #define _OUT(_field)	offsetof(struct pflowctl_sockaddr, _field)
243 static struct snl_attr_parser nla_p_sockaddr[] = {
244 	{ .type = PFLOWNL_ADDR_FAMILY, .off = _OUT(in.sin_family), .cb = snl_attr_get_uint8 },
245 	{ .type = PFLOWNL_ADDR_PORT, .off = _OUT(in.sin_port), .cb = snl_attr_get_uint16 },
246 	{ .type = PFLOWNL_ADDR_IP, .off = _OUT(in.sin_addr), .cb = snl_attr_get_in_addr },
247 	{ .type = PFLOWNL_ADDR_IP6, .off = _OUT(in6.sin6_addr), .cb = snl_attr_get_in6_addr },
248 };
249 SNL_DECLARE_ATTR_PARSER_EXT(sockaddr_parser, 0, nla_p_sockaddr, pflowctl_post_sockaddr);
250 #undef _OUT
251 
252 struct pflowctl_get {
253 	int id;
254 	int version;
255 	struct pflowctl_sockaddr src;
256 	struct pflowctl_sockaddr dst;
257 	uint32_t obs_dom;
258 	uint8_t so_status;
259 };
260 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
261 #define	_OUT(_field)	offsetof(struct pflowctl_get, _field)
262 static struct snl_attr_parser ap_get[] = {
263 	{ .type = PFLOWNL_GET_ID, .off = _OUT(id), .cb = snl_attr_get_int32 },
264 	{ .type = PFLOWNL_GET_VERSION, .off = _OUT(version), .cb = snl_attr_get_int16 },
265 	{ .type = PFLOWNL_GET_SRC, .off = _OUT(src), .arg = &sockaddr_parser, .cb = snl_attr_get_nested },
266 	{ .type = PFLOWNL_GET_DST, .off = _OUT(dst), .arg = &sockaddr_parser, .cb = snl_attr_get_nested },
267 	{ .type = PFLOWNL_GET_OBSERVATION_DOMAIN, .off = _OUT(obs_dom), .cb = snl_attr_get_uint32 },
268 	{ .type = PFLOWNL_GET_SOCKET_STATUS, .off = _OUT(so_status), .cb = snl_attr_get_uint8 },
269 };
270 static struct snl_field_parser fp_get[] = {};
271 #undef _IN
272 #undef _OUT
273 SNL_DECLARE_PARSER(get_parser, struct genlmsghdr, fp_get, ap_get);
274 
275 static void
print_sockaddr(const char * prefix,const struct sockaddr_storage * s)276 print_sockaddr(const char *prefix, const struct sockaddr_storage *s)
277 {
278 	char buf[INET6_ADDRSTRLEN];
279 	int error;
280 
281 	if (s->ss_family != AF_INET && s->ss_family != AF_INET6)
282 		return;
283 
284 	if (s->ss_family == AF_INET ||
285 	    s->ss_family == AF_INET6) {
286 		error = getnameinfo((const struct sockaddr *)s,
287 		    s->ss_len, buf, sizeof(buf), NULL, 0,
288 		    NI_NUMERICHOST);
289 		if (error)
290 			err(1, "sender: %s", gai_strerror(error));
291 	}
292 
293 	printf("%s", prefix);
294 	switch (s->ss_family) {
295 	case AF_INET: {
296 		const struct sockaddr_in *sin = (const struct sockaddr_in *)s;
297 		if (sin->sin_addr.s_addr != INADDR_ANY) {
298 			printf("%s", buf);
299 			if (sin->sin_port != 0)
300 				printf(":%u", ntohs(sin->sin_port));
301 		}
302 		break;
303 	}
304 	case AF_INET6: {
305 		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)s;
306 		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
307 			printf("[%s]", buf);
308 			if (sin6->sin6_port != 0)
309 				printf(":%u", ntohs(sin6->sin6_port));
310 		}
311 		break;
312 	}
313 	}
314 }
315 
316 static int
get(int id)317 get(int id)
318 {
319 	struct snl_state ss = {};
320 	struct snl_errmsg_data e = {};
321 	struct pflowctl_get g = {};
322 	struct snl_writer nw;
323 	struct nlmsghdr *hdr;
324 	uint32_t seq_id;
325 	int family_id;
326 
327 	snl_init(&ss, NETLINK_GENERIC);
328 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
329 	if (family_id == 0)
330 		errx(1, "pflow.ko is not loaded.");
331 
332 	snl_init_writer(&ss, &nw);
333 	hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_GET);
334 	snl_add_msg_attr_s32(&nw, PFLOWNL_GET_ID, id);
335 
336 	hdr = snl_finalize_msg(&nw);
337 	if (hdr == NULL)
338 		return (ENOMEM);
339 	seq_id = hdr->nlmsg_seq;
340 
341 	snl_send_message(&ss, hdr);
342 
343 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
344 		if (! snl_parse_nlmsg(&ss, hdr, &get_parser, &g))
345 			continue;
346 
347 		printf("pflow%d: version %d domain %u", g.id, g.version, g.obs_dom);
348 		print_sockaddr(" src ", &g.src.storage);
349 		print_sockaddr(" dst ", &g.dst.storage);
350 		printf("\n");
351 		if (verbose) {
352 			printf("\tsocket: %s\n",
353 			    g.so_status ? "connected" : "disconnected");
354 		}
355 	}
356 
357 	if (e.error)
358 		errc(1, e.error, "failed to get");
359 
360 	return (0);
361 }
362 
363 struct pflowctl_set {
364 	int id;
365 	uint16_t version;
366 	struct sockaddr_storage src;
367 	struct sockaddr_storage dst;
368 	uint32_t obs_dom;
369 };
370 static inline bool
snl_add_msg_attr_sockaddr(struct snl_writer * nw,int attrtype,struct sockaddr_storage * s)371 snl_add_msg_attr_sockaddr(struct snl_writer *nw, int attrtype, struct sockaddr_storage *s)
372 {
373 	int off = snl_add_msg_attr_nested(nw, attrtype);
374 
375 	snl_add_msg_attr_u8(nw, PFLOWNL_ADDR_FAMILY, s->ss_family);
376 
377 	switch (s->ss_family) {
378 	case AF_INET: {
379 		const struct sockaddr_in *in = (const struct sockaddr_in *)s;
380 		snl_add_msg_attr_u16(nw, PFLOWNL_ADDR_PORT, in->sin_port);
381 		snl_add_msg_attr_ip4(nw, PFLOWNL_ADDR_IP, &in->sin_addr);
382 		break;
383 	}
384 	case AF_INET6: {
385 		const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)s;
386 		snl_add_msg_attr_u16(nw, PFLOWNL_ADDR_PORT, in6->sin6_port);
387 		snl_add_msg_attr_ip6(nw, PFLOWNL_ADDR_IP6, &in6->sin6_addr);
388 		break;
389 	}
390 	default:
391 		return (false);
392 	}
393 	snl_end_attr_nested(nw, off);
394 
395 	return (true);
396 }
397 
398 static int
do_set(struct pflowctl_set * s)399 do_set(struct pflowctl_set *s)
400 {
401 	struct snl_state ss = {};
402 	struct snl_errmsg_data e = {};
403 	struct snl_writer nw;
404 	struct nlmsghdr *hdr;
405 	int family_id;
406 
407 	snl_init(&ss, NETLINK_GENERIC);
408 	family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME);
409 	if (family_id == 0)
410 		errx(1, "pflow.ko is not loaded.");
411 
412 	snl_init_writer(&ss, &nw);
413 	snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_SET);
414 
415 	snl_add_msg_attr_s32(&nw, PFLOWNL_SET_ID, s->id);
416 	if (s->version != 0)
417 		snl_add_msg_attr_u16(&nw, PFLOWNL_SET_VERSION, s->version);
418 	if (s->src.ss_len != 0)
419 		snl_add_msg_attr_sockaddr(&nw, PFLOWNL_SET_SRC, &s->src);
420 	if (s->dst.ss_len != 0)
421 		snl_add_msg_attr_sockaddr(&nw, PFLOWNL_SET_DST, &s->dst);
422 	if (s->obs_dom != 0)
423 		snl_add_msg_attr_u32(&nw, PFLOWNL_SET_OBSERVATION_DOMAIN, s->obs_dom);
424 
425 	hdr = snl_finalize_msg(&nw);
426 	if (hdr == NULL)
427 		return (1);
428 
429 	snl_send_message(&ss, hdr);
430 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
431 
432 	if (e.error)
433 		errc(1, e.error, "failed to set");
434 
435 	return (0);
436 }
437 
438 static void
pflowctl_addr(const char * val,struct sockaddr_storage * ss)439 pflowctl_addr(const char *val, struct sockaddr_storage *ss)
440 {
441 	struct addrinfo *res0;
442 	int error;
443 	bool flag;
444 	char *ip, *port;
445 	char buf[sysconf(_SC_HOST_NAME_MAX) + 1 + sizeof(":65535")];
446 	struct addrinfo hints = {
447 		.ai_family = AF_UNSPEC,
448 		.ai_socktype = SOCK_DGRAM, /*dummy*/
449 		.ai_flags = AI_NUMERICHOST,
450 	};
451 
452 	if (strlcpy(buf, val, sizeof(buf)) >= sizeof(buf))
453 		errx(1, "%s bad value", val);
454 
455 	port = NULL;
456 	flag = *buf == '[';
457 
458 	for (char *cp = buf; *cp; ++cp) {
459 		if (*cp == ']' && *(cp + 1) == ':' && flag) {
460 			*cp = '\0';
461 			*(cp + 1) = '\0';
462 			port = cp + 2;
463 			break;
464 		}
465 		if (*cp == ']' && *(cp + 1) == '\0' && flag) {
466 			*cp = '\0';
467 			port = NULL;
468 			break;
469 		}
470 		if (*cp == ':' && !flag) {
471 			*cp = '\0';
472 			port = cp + 1;
473 			break;
474 		}
475 	}
476 
477 	ip = buf;
478 	if (flag)
479 		ip++;
480 
481 	if ((error = getaddrinfo(ip, port, &hints, &res0)) != 0)
482 		errx(1, "error in parsing address string: %s",
483 		    gai_strerror(error));
484 
485 	memcpy(ss, res0->ai_addr, res0->ai_addr->sa_len);
486 	freeaddrinfo(res0);
487 }
488 
489 static int
set(char * idstr,int argc,char * argv[])490 set(char *idstr, int argc, char *argv[])
491 {
492 	struct pflowctl_set s = {};
493 
494 	s.id = pflow_to_id(idstr);
495 	if (s.id < 0)
496 		return (EINVAL);
497 
498 	while (argc > 0) {
499 		if (strcmp(argv[0], "src") == 0) {
500 			if (argc < 2)
501 				usage();
502 
503 			pflowctl_addr(argv[1], &s.src);
504 
505 			argc -= 2;
506 			argv += 2;
507 		} else if (strcmp(argv[0], "dst") == 0) {
508 			if (argc < 2)
509 				usage();
510 
511 			pflowctl_addr(argv[1], &s.dst);
512 
513 			argc -= 2;
514 			argv += 2;
515 		} else if (strcmp(argv[0], "proto") == 0) {
516 			if (argc < 2)
517 				usage();
518 
519 			s.version = strtol(argv[1], NULL, 10);
520 
521 			argc -= 2;
522 			argv += 2;
523 		} else if (strcmp(argv[0], "domain") == 0) {
524 			if (argc < 2)
525 				usage();
526 
527 			s.obs_dom = strtol(argv[1], NULL, 10);
528 
529 			argc -= 2;
530 			argv += 2;
531 		} else {
532 			usage();
533 		}
534 	}
535 
536 	return (do_set(&s));
537 }
538 
539 static const struct snl_hdr_parser *all_parsers[] = {
540 	&list_parser,
541 	&get_parser,
542 };
543 
544 enum pflowctl_op_t {
545 	OP_HELP,
546 	OP_LIST,
547 	OP_CREATE,
548 	OP_DELETE,
549 	OP_SET,
550 };
551 int
main(int argc,char * argv[])552 main(int argc, char *argv[])
553 {
554 	int ch;
555 	enum pflowctl_op_t op = OP_HELP;
556 	char **set_args = NULL;
557 	size_t set_arg_count = 0;
558 
559 	SNL_VERIFY_PARSERS(all_parsers);
560 
561 	if (argc < 2)
562 		usage();
563 
564 	while ((ch = getopt(argc, argv,
565 	    "lcd:s:v")) != -1) {
566 		switch (ch) {
567 		case 'l':
568 			op = OP_LIST;
569 			break;
570 		case 'c':
571 			op = OP_CREATE;
572 			break;
573 		case 'd':
574 			op = OP_DELETE;
575 			break;
576 		case 's':
577 			op = OP_SET;
578 			set_arg_count = argc - optind;
579 			set_args = argv + optind;
580 			break;
581 		case 'v':
582 			verbose = true;
583 			break;
584 		}
585 	}
586 
587 	switch (op) {
588 	case OP_LIST:
589 		return (list());
590 	case OP_CREATE:
591 		return (create());
592 	case OP_DELETE:
593 		return (del(optarg));
594 	case OP_SET:
595 		return (set(optarg, set_arg_count, set_args));
596 	case OP_HELP:
597 		usage();
598 		break;
599 	}
600 
601 	return (0);
602 }
603