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