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 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 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 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 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 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 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 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 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 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 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 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 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 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