libxt_conntrack.c revision bf97128c7262f17a02fec41cdae75b472ba77f88
1/* 2 * libxt_conntrack 3 * Shared library add-on to iptables for conntrack matching support. 4 * 5 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). 6 * Copyright © CC Computer Consultants GmbH, 2007 - 2008 7 * Jan Engelhardt <jengelh@computergmbh.de> 8 */ 9#include <sys/socket.h> 10#include <sys/types.h> 11#include <ctype.h> 12#include <getopt.h> 13#include <netdb.h> 14#include <stdbool.h> 15#include <stddef.h> 16#include <stdio.h> 17#include <stdlib.h> 18#include <string.h> 19#include <xtables.h> 20#include <linux/netfilter.h> 21#include <linux/netfilter/xt_conntrack.h> 22#include <linux/netfilter/nf_conntrack_common.h> 23#include <arpa/inet.h> 24 25static void conntrack_mt_help(void) 26{ 27 printf( 28"conntrack match options:\n" 29"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n" 30" State(s) to match\n" 31"[!] --ctproto proto Protocol to match; by number or name, e.g. \"tcp\"\n" 32"[!] --ctorigsrc address[/mask]\n" 33"[!] --ctorigdst address[/mask]\n" 34"[!] --ctreplsrc address[/mask]\n" 35"[!] --ctrepldst address[/mask]\n" 36" Original/Reply source/destination address\n" 37"[!] --ctorigsrcport port\n" 38"[!] --ctorigdstport port\n" 39"[!] --ctreplsrcport port\n" 40"[!] --ctrepldstport port\n" 41" TCP/UDP/SCTP orig./reply source/destination port\n" 42"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n" 43" Status(es) to match\n" 44"[!] --ctexpire time[:time] Match remaining lifetime in seconds against\n" 45" value or range of values (inclusive)\n" 46" --ctdir {ORIGINAL|REPLY} Flow direction of packet\n"); 47} 48 49static const struct option conntrack_mt_opts_v0[] = { 50 {.name = "ctstate", .has_arg = true, .val = '1'}, 51 {.name = "ctproto", .has_arg = true, .val = '2'}, 52 {.name = "ctorigsrc", .has_arg = true, .val = '3'}, 53 {.name = "ctorigdst", .has_arg = true, .val = '4'}, 54 {.name = "ctreplsrc", .has_arg = true, .val = '5'}, 55 {.name = "ctrepldst", .has_arg = true, .val = '6'}, 56 {.name = "ctstatus", .has_arg = true, .val = '7'}, 57 {.name = "ctexpire", .has_arg = true, .val = '8'}, 58 { .name = NULL } 59}; 60 61static const struct option conntrack_mt_opts[] = { 62 {.name = "ctstate", .has_arg = true, .val = '1'}, 63 {.name = "ctproto", .has_arg = true, .val = '2'}, 64 {.name = "ctorigsrc", .has_arg = true, .val = '3'}, 65 {.name = "ctorigdst", .has_arg = true, .val = '4'}, 66 {.name = "ctreplsrc", .has_arg = true, .val = '5'}, 67 {.name = "ctrepldst", .has_arg = true, .val = '6'}, 68 {.name = "ctstatus", .has_arg = true, .val = '7'}, 69 {.name = "ctexpire", .has_arg = true, .val = '8'}, 70 {.name = "ctorigsrcport", .has_arg = true, .val = 'a'}, 71 {.name = "ctorigdstport", .has_arg = true, .val = 'b'}, 72 {.name = "ctreplsrcport", .has_arg = true, .val = 'c'}, 73 {.name = "ctrepldstport", .has_arg = true, .val = 'd'}, 74 {.name = "ctdir", .has_arg = true, .val = 'e'}, 75 {.name = NULL}, 76}; 77 78static int 79parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo) 80{ 81 if (strncasecmp(state, "INVALID", len) == 0) 82 sinfo->statemask |= XT_CONNTRACK_STATE_INVALID; 83 else if (strncasecmp(state, "NEW", len) == 0) 84 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); 85 else if (strncasecmp(state, "ESTABLISHED", len) == 0) 86 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); 87 else if (strncasecmp(state, "RELATED", len) == 0) 88 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); 89 else if (strncasecmp(state, "UNTRACKED", len) == 0) 90 sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED; 91 else if (strncasecmp(state, "SNAT", len) == 0) 92 sinfo->statemask |= XT_CONNTRACK_STATE_SNAT; 93 else if (strncasecmp(state, "DNAT", len) == 0) 94 sinfo->statemask |= XT_CONNTRACK_STATE_DNAT; 95 else 96 return 0; 97 return 1; 98} 99 100static void 101parse_states(const char *arg, struct xt_conntrack_info *sinfo) 102{ 103 const char *comma; 104 105 while ((comma = strchr(arg, ',')) != NULL) { 106 if (comma == arg || !parse_state(arg, comma-arg, sinfo)) 107 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg); 108 arg = comma+1; 109 } 110 if (!*arg) 111 xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of " 112 "states with no spaces, e.g. " 113 "ESTABLISHED,RELATED"); 114 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo)) 115 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg); 116} 117 118static bool 119conntrack_ps_state(struct xt_conntrack_mtinfo2 *info, const char *state, 120 size_t z) 121{ 122 if (strncasecmp(state, "INVALID", z) == 0) 123 info->state_mask |= XT_CONNTRACK_STATE_INVALID; 124 else if (strncasecmp(state, "NEW", z) == 0) 125 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); 126 else if (strncasecmp(state, "ESTABLISHED", z) == 0) 127 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); 128 else if (strncasecmp(state, "RELATED", z) == 0) 129 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); 130 else if (strncasecmp(state, "UNTRACKED", z) == 0) 131 info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED; 132 else if (strncasecmp(state, "SNAT", z) == 0) 133 info->state_mask |= XT_CONNTRACK_STATE_SNAT; 134 else if (strncasecmp(state, "DNAT", z) == 0) 135 info->state_mask |= XT_CONNTRACK_STATE_DNAT; 136 else 137 return false; 138 return true; 139} 140 141static void 142conntrack_ps_states(struct xt_conntrack_mtinfo2 *info, const char *arg) 143{ 144 const char *comma; 145 146 while ((comma = strchr(arg, ',')) != NULL) { 147 if (comma == arg || !conntrack_ps_state(info, arg, comma - arg)) 148 xtables_error(PARAMETER_PROBLEM, 149 "Bad ctstate \"%s\"", arg); 150 arg = comma + 1; 151 } 152 153 if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg))) 154 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg); 155} 156 157static int 158parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo) 159{ 160 if (strncasecmp(status, "NONE", len) == 0) 161 sinfo->statusmask |= 0; 162 else if (strncasecmp(status, "EXPECTED", len) == 0) 163 sinfo->statusmask |= IPS_EXPECTED; 164 else if (strncasecmp(status, "SEEN_REPLY", len) == 0) 165 sinfo->statusmask |= IPS_SEEN_REPLY; 166 else if (strncasecmp(status, "ASSURED", len) == 0) 167 sinfo->statusmask |= IPS_ASSURED; 168#ifdef IPS_CONFIRMED 169 else if (strncasecmp(status, "CONFIRMED", len) == 0) 170 sinfo->statusmask |= IPS_CONFIRMED; 171#endif 172 else 173 return 0; 174 return 1; 175} 176 177static void 178parse_statuses(const char *arg, struct xt_conntrack_info *sinfo) 179{ 180 const char *comma; 181 182 while ((comma = strchr(arg, ',')) != NULL) { 183 if (comma == arg || !parse_status(arg, comma-arg, sinfo)) 184 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg); 185 arg = comma+1; 186 } 187 188 if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo)) 189 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg); 190} 191 192static bool 193conntrack_ps_status(struct xt_conntrack_mtinfo2 *info, const char *status, 194 size_t z) 195{ 196 if (strncasecmp(status, "NONE", z) == 0) 197 info->status_mask |= 0; 198 else if (strncasecmp(status, "EXPECTED", z) == 0) 199 info->status_mask |= IPS_EXPECTED; 200 else if (strncasecmp(status, "SEEN_REPLY", z) == 0) 201 info->status_mask |= IPS_SEEN_REPLY; 202 else if (strncasecmp(status, "ASSURED", z) == 0) 203 info->status_mask |= IPS_ASSURED; 204 else if (strncasecmp(status, "CONFIRMED", z) == 0) 205 info->status_mask |= IPS_CONFIRMED; 206 else 207 return false; 208 return true; 209} 210 211static void 212conntrack_ps_statuses(struct xt_conntrack_mtinfo2 *info, const char *arg) 213{ 214 const char *comma; 215 216 while ((comma = strchr(arg, ',')) != NULL) { 217 if (comma == arg || !conntrack_ps_status(info, arg, comma - arg)) 218 xtables_error(PARAMETER_PROBLEM, 219 "Bad ctstatus \"%s\"", arg); 220 arg = comma + 1; 221 } 222 223 if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg))) 224 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg); 225} 226 227static unsigned long 228parse_expire(const char *s) 229{ 230 unsigned int len; 231 232 if (!xtables_strtoui(s, NULL, &len, 0, UINT32_MAX)) 233 xtables_error(PARAMETER_PROBLEM, "expire value invalid: \"%s\"\n", s); 234 else 235 return len; 236} 237 238/* If a single value is provided, min and max are both set to the value */ 239static void 240parse_expires(const char *s, struct xt_conntrack_info *sinfo) 241{ 242 char *buffer; 243 char *cp; 244 245 buffer = strdup(s); 246 if ((cp = strchr(buffer, ':')) == NULL) 247 sinfo->expires_min = sinfo->expires_max = 248 parse_expire(buffer); 249 else { 250 *cp = '\0'; 251 cp++; 252 253 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0; 254 sinfo->expires_max = cp[0] 255 ? parse_expire(cp) 256 : (unsigned long)-1; 257 } 258 free(buffer); 259 260 if (sinfo->expires_min > sinfo->expires_max) 261 xtables_error(PARAMETER_PROBLEM, 262 "expire min. range value `%lu' greater than max. " 263 "range value `%lu'", sinfo->expires_min, sinfo->expires_max); 264} 265 266static void 267conntrack_ps_expires(struct xt_conntrack_mtinfo2 *info, const char *s) 268{ 269 unsigned int min, max; 270 char *end; 271 272 if (!xtables_strtoui(s, &end, &min, 0, UINT32_MAX)) 273 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s); 274 max = min; 275 if (*end == ':') 276 if (!xtables_strtoui(s, &end, &max, 0, UINT32_MAX)) 277 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s); 278 if (*end != '\0') 279 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s); 280 281 if (min > max) 282 xtables_error(PARAMETER_PROBLEM, 283 "expire min. range value \"%u\" greater than max. " 284 "range value \"%u\"", min, max); 285 286 info->expires_min = min; 287 info->expires_max = max; 288} 289 290static int conntrack_parse(int c, char **argv, int invert, unsigned int *flags, 291 const void *entry, struct xt_entry_match **match) 292{ 293 struct xt_conntrack_info *sinfo = (void *)(*match)->data; 294 char *protocol = NULL; 295 unsigned int naddrs = 0; 296 struct in_addr *addrs = NULL; 297 298 299 switch (c) { 300 case '1': 301 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 302 303 parse_states(argv[optind-1], sinfo); 304 if (invert) { 305 sinfo->invflags |= XT_CONNTRACK_STATE; 306 } 307 sinfo->flags |= XT_CONNTRACK_STATE; 308 break; 309 310 case '2': 311 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 312 313 if(invert) 314 sinfo->invflags |= XT_CONNTRACK_PROTO; 315 316 /* Canonicalize into lower case */ 317 for (protocol = argv[optind-1]; *protocol; protocol++) 318 *protocol = tolower(*protocol); 319 320 protocol = argv[optind-1]; 321 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = 322 xtables_parse_protocol(protocol); 323 324 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0 325 && (sinfo->invflags & XT_INV_PROTO)) 326 xtables_error(PARAMETER_PROBLEM, 327 "rule would never match protocol"); 328 329 sinfo->flags |= XT_CONNTRACK_PROTO; 330 break; 331 332 case '3': 333 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 334 335 if (invert) 336 sinfo->invflags |= XT_CONNTRACK_ORIGSRC; 337 338 xtables_ipparse_any(argv[optind-1], &addrs, 339 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], 340 &naddrs); 341 if(naddrs > 1) 342 xtables_error(PARAMETER_PROBLEM, 343 "multiple IP addresses not allowed"); 344 345 if(naddrs == 1) { 346 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr; 347 } 348 349 sinfo->flags |= XT_CONNTRACK_ORIGSRC; 350 break; 351 352 case '4': 353 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 354 355 if (invert) 356 sinfo->invflags |= XT_CONNTRACK_ORIGDST; 357 358 xtables_ipparse_any(argv[optind-1], &addrs, 359 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], 360 &naddrs); 361 if(naddrs > 1) 362 xtables_error(PARAMETER_PROBLEM, 363 "multiple IP addresses not allowed"); 364 365 if(naddrs == 1) { 366 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr; 367 } 368 369 sinfo->flags |= XT_CONNTRACK_ORIGDST; 370 break; 371 372 case '5': 373 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 374 375 if (invert) 376 sinfo->invflags |= XT_CONNTRACK_REPLSRC; 377 378 xtables_ipparse_any(argv[optind-1], &addrs, 379 &sinfo->sipmsk[IP_CT_DIR_REPLY], 380 &naddrs); 381 if(naddrs > 1) 382 xtables_error(PARAMETER_PROBLEM, 383 "multiple IP addresses not allowed"); 384 385 if(naddrs == 1) { 386 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr; 387 } 388 389 sinfo->flags |= XT_CONNTRACK_REPLSRC; 390 break; 391 392 case '6': 393 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 394 395 if (invert) 396 sinfo->invflags |= XT_CONNTRACK_REPLDST; 397 398 xtables_ipparse_any(argv[optind-1], &addrs, 399 &sinfo->dipmsk[IP_CT_DIR_REPLY], 400 &naddrs); 401 if(naddrs > 1) 402 xtables_error(PARAMETER_PROBLEM, 403 "multiple IP addresses not allowed"); 404 405 if(naddrs == 1) { 406 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr; 407 } 408 409 sinfo->flags |= XT_CONNTRACK_REPLDST; 410 break; 411 412 case '7': 413 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 414 415 parse_statuses(argv[optind-1], sinfo); 416 if (invert) { 417 sinfo->invflags |= XT_CONNTRACK_STATUS; 418 } 419 sinfo->flags |= XT_CONNTRACK_STATUS; 420 break; 421 422 case '8': 423 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 424 425 parse_expires(argv[optind-1], sinfo); 426 if (invert) { 427 sinfo->invflags |= XT_CONNTRACK_EXPIRES; 428 } 429 sinfo->flags |= XT_CONNTRACK_EXPIRES; 430 break; 431 432 default: 433 return 0; 434 } 435 436 *flags = sinfo->flags; 437 return 1; 438} 439 440static int 441conntrack_mt_parse(int c, bool invert, unsigned int *flags, 442 struct xt_conntrack_mtinfo2 *info) 443{ 444 unsigned int port; 445 char *p; 446 447 switch (c) { 448 case '1': /* --ctstate */ 449 conntrack_ps_states(info, optarg); 450 info->match_flags |= XT_CONNTRACK_STATE; 451 if (invert) 452 info->invert_flags |= XT_CONNTRACK_STATE; 453 break; 454 455 case '2': /* --ctproto */ 456 /* Canonicalize into lower case */ 457 for (p = optarg; *p != '\0'; ++p) 458 *p = tolower(*p); 459 info->l4proto = xtables_parse_protocol(optarg); 460 461 if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO)) 462 xtables_error(PARAMETER_PROBLEM, "conntrack: rule would " 463 "never match protocol"); 464 465 info->match_flags |= XT_CONNTRACK_PROTO; 466 if (invert) 467 info->invert_flags |= XT_CONNTRACK_PROTO; 468 break; 469 470 case '7': /* --ctstatus */ 471 conntrack_ps_statuses(info, optarg); 472 info->match_flags |= XT_CONNTRACK_STATUS; 473 if (invert) 474 info->invert_flags |= XT_CONNTRACK_STATUS; 475 break; 476 477 case '8': /* --ctexpire */ 478 conntrack_ps_expires(info, optarg); 479 info->match_flags |= XT_CONNTRACK_EXPIRES; 480 if (invert) 481 info->invert_flags |= XT_CONNTRACK_EXPIRES; 482 break; 483 484 case 'a': /* --ctorigsrcport */ 485 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) 486 xtables_param_act(XTF_BAD_VALUE, "conntrack", 487 "--ctorigsrcport", optarg); 488 info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT; 489 info->origsrc_port = htons(port); 490 if (invert) 491 info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT; 492 break; 493 494 case 'b': /* --ctorigdstport */ 495 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) 496 xtables_param_act(XTF_BAD_VALUE, "conntrack", 497 "--ctorigdstport", optarg); 498 info->match_flags |= XT_CONNTRACK_ORIGDST_PORT; 499 info->origdst_port = htons(port); 500 if (invert) 501 info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT; 502 break; 503 504 case 'c': /* --ctreplsrcport */ 505 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) 506 xtables_param_act(XTF_BAD_VALUE, "conntrack", 507 "--ctreplsrcport", optarg); 508 info->match_flags |= XT_CONNTRACK_REPLSRC_PORT; 509 info->replsrc_port = htons(port); 510 if (invert) 511 info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT; 512 break; 513 514 case 'd': /* --ctrepldstport */ 515 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) 516 xtables_param_act(XTF_BAD_VALUE, "conntrack", 517 "--ctrepldstport", optarg); 518 info->match_flags |= XT_CONNTRACK_REPLDST_PORT; 519 info->repldst_port = htons(port); 520 if (invert) 521 info->invert_flags |= XT_CONNTRACK_REPLDST_PORT; 522 break; 523 524 case 'e': /* --ctdir */ 525 xtables_param_act(XTF_NO_INVERT, "conntrack", "--ctdir", invert); 526 if (strcasecmp(optarg, "ORIGINAL") == 0) { 527 info->match_flags |= XT_CONNTRACK_DIRECTION; 528 info->invert_flags &= ~XT_CONNTRACK_DIRECTION; 529 } else if (strcasecmp(optarg, "REPLY") == 0) { 530 info->match_flags |= XT_CONNTRACK_DIRECTION; 531 info->invert_flags |= XT_CONNTRACK_DIRECTION; 532 } else { 533 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", optarg); 534 } 535 break; 536 537 default: 538 return false; 539 } 540 541 *flags = info->match_flags; 542 return true; 543} 544 545static int 546conntrack_mt4_parse(int c, bool invert, unsigned int *flags, 547 struct xt_conntrack_mtinfo2 *info) 548{ 549 struct in_addr *addr = NULL; 550 unsigned int naddrs = 0; 551 552 switch (c) { 553 case '3': /* --ctorigsrc */ 554 xtables_ipparse_any(optarg, &addr, &info->origsrc_mask.in, 555 &naddrs); 556 if (naddrs > 1) 557 xtables_error(PARAMETER_PROBLEM, 558 "multiple IP addresses not allowed"); 559 if (naddrs == 1) 560 memcpy(&info->origsrc_addr.in, addr, sizeof(*addr)); 561 info->match_flags |= XT_CONNTRACK_ORIGSRC; 562 if (invert) 563 info->invert_flags |= XT_CONNTRACK_ORIGSRC; 564 break; 565 566 case '4': /* --ctorigdst */ 567 xtables_ipparse_any(optarg, &addr, &info->origdst_mask.in, 568 &naddrs); 569 if (naddrs > 1) 570 xtables_error(PARAMETER_PROBLEM, 571 "multiple IP addresses not allowed"); 572 if (naddrs == 1) 573 memcpy(&info->origdst_addr.in, addr, sizeof(*addr)); 574 info->match_flags |= XT_CONNTRACK_ORIGDST; 575 if (invert) 576 info->invert_flags |= XT_CONNTRACK_ORIGDST; 577 break; 578 579 case '5': /* --ctreplsrc */ 580 xtables_ipparse_any(optarg, &addr, &info->replsrc_mask.in, 581 &naddrs); 582 if (naddrs > 1) 583 xtables_error(PARAMETER_PROBLEM, 584 "multiple IP addresses not allowed"); 585 if (naddrs == 1) 586 memcpy(&info->replsrc_addr.in, addr, sizeof(*addr)); 587 info->match_flags |= XT_CONNTRACK_REPLSRC; 588 if (invert) 589 info->invert_flags |= XT_CONNTRACK_REPLSRC; 590 break; 591 592 case '6': /* --ctrepldst */ 593 xtables_ipparse_any(optarg, &addr, &info->repldst_mask.in, 594 &naddrs); 595 if (naddrs > 1) 596 xtables_error(PARAMETER_PROBLEM, 597 "multiple IP addresses not allowed"); 598 if (naddrs == 1) 599 memcpy(&info->repldst_addr.in, addr, sizeof(*addr)); 600 info->match_flags |= XT_CONNTRACK_REPLDST; 601 if (invert) 602 info->invert_flags |= XT_CONNTRACK_REPLDST; 603 break; 604 605 606 default: 607 return conntrack_mt_parse(c, invert, flags, info); 608 } 609 610 *flags = info->match_flags; 611 return true; 612} 613 614static int 615conntrack_mt6_parse(int c, bool invert, unsigned int *flags, 616 struct xt_conntrack_mtinfo2 *info) 617{ 618 struct in6_addr *addr = NULL; 619 unsigned int naddrs = 0; 620 621 switch (c) { 622 case '3': /* --ctorigsrc */ 623 xtables_ip6parse_any(optarg, &addr, 624 &info->origsrc_mask.in6, &naddrs); 625 if (naddrs > 1) 626 xtables_error(PARAMETER_PROBLEM, 627 "multiple IP addresses not allowed"); 628 if (naddrs == 1) 629 memcpy(&info->origsrc_addr.in6, addr, sizeof(*addr)); 630 info->match_flags |= XT_CONNTRACK_ORIGSRC; 631 if (invert) 632 info->invert_flags |= XT_CONNTRACK_ORIGSRC; 633 break; 634 635 case '4': /* --ctorigdst */ 636 xtables_ip6parse_any(optarg, &addr, 637 &info->origdst_mask.in6, &naddrs); 638 if (naddrs > 1) 639 xtables_error(PARAMETER_PROBLEM, 640 "multiple IP addresses not allowed"); 641 if (naddrs == 1) 642 memcpy(&info->origdst_addr.in, addr, sizeof(*addr)); 643 info->match_flags |= XT_CONNTRACK_ORIGDST; 644 if (invert) 645 info->invert_flags |= XT_CONNTRACK_ORIGDST; 646 break; 647 648 case '5': /* --ctreplsrc */ 649 xtables_ip6parse_any(optarg, &addr, 650 &info->replsrc_mask.in6, &naddrs); 651 if (naddrs > 1) 652 xtables_error(PARAMETER_PROBLEM, 653 "multiple IP addresses not allowed"); 654 if (naddrs == 1) 655 memcpy(&info->replsrc_addr.in, addr, sizeof(*addr)); 656 info->match_flags |= XT_CONNTRACK_REPLSRC; 657 if (invert) 658 info->invert_flags |= XT_CONNTRACK_REPLSRC; 659 break; 660 661 case '6': /* --ctrepldst */ 662 xtables_ip6parse_any(optarg, &addr, 663 &info->repldst_mask.in6, &naddrs); 664 if (naddrs > 1) 665 xtables_error(PARAMETER_PROBLEM, 666 "multiple IP addresses not allowed"); 667 if (naddrs == 1) 668 memcpy(&info->repldst_addr.in, addr, sizeof(*addr)); 669 info->match_flags |= XT_CONNTRACK_REPLDST; 670 if (invert) 671 info->invert_flags |= XT_CONNTRACK_REPLDST; 672 break; 673 674 675 default: 676 return conntrack_mt_parse(c, invert, flags, info); 677 } 678 679 *flags = info->match_flags; 680 return true; 681} 682 683#define cinfo_transform(r, l) \ 684 do { \ 685 memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \ 686 (r)->state_mask = (l)->state_mask; \ 687 (r)->status_mask = (l)->status_mask; \ 688 } while (false); 689 690static int 691conntrack1_mt4_parse(int c, char **argv, int invert, unsigned int *flags, 692 const void *entry, struct xt_entry_match **match) 693{ 694 struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data; 695 struct xt_conntrack_mtinfo2 up; 696 697 cinfo_transform(&up, info); 698 if (!conntrack_mt4_parse(c, invert, flags, &up)) 699 return false; 700 cinfo_transform(info, &up); 701 return true; 702} 703 704static int 705conntrack1_mt6_parse(int c, char **argv, int invert, unsigned int *flags, 706 const void *entry, struct xt_entry_match **match) 707{ 708 struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data; 709 struct xt_conntrack_mtinfo2 up; 710 711 cinfo_transform(&up, info); 712 if (!conntrack_mt6_parse(c, invert, flags, &up)) 713 return false; 714 cinfo_transform(info, &up); 715 return true; 716} 717 718static int 719conntrack2_mt4_parse(int c, char **argv, int invert, unsigned int *flags, 720 const void *entry, struct xt_entry_match **match) 721{ 722 return conntrack_mt4_parse(c, invert, flags, (void *)(*match)->data); 723} 724 725static int 726conntrack2_mt6_parse(int c, char **argv, int invert, unsigned int *flags, 727 const void *entry, struct xt_entry_match **match) 728{ 729 return conntrack_mt6_parse(c, invert, flags, (void *)(*match)->data); 730} 731 732static void conntrack_mt_check(unsigned int flags) 733{ 734 if (flags == 0) 735 xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option " 736 "is required"); 737} 738 739static void 740print_state(unsigned int statemask) 741{ 742 const char *sep = ""; 743 744 if (statemask & XT_CONNTRACK_STATE_INVALID) { 745 printf("%sINVALID", sep); 746 sep = ","; 747 } 748 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) { 749 printf("%sNEW", sep); 750 sep = ","; 751 } 752 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) { 753 printf("%sRELATED", sep); 754 sep = ","; 755 } 756 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) { 757 printf("%sESTABLISHED", sep); 758 sep = ","; 759 } 760 if (statemask & XT_CONNTRACK_STATE_UNTRACKED) { 761 printf("%sUNTRACKED", sep); 762 sep = ","; 763 } 764 if (statemask & XT_CONNTRACK_STATE_SNAT) { 765 printf("%sSNAT", sep); 766 sep = ","; 767 } 768 if (statemask & XT_CONNTRACK_STATE_DNAT) { 769 printf("%sDNAT", sep); 770 sep = ","; 771 } 772 printf(" "); 773} 774 775static void 776print_status(unsigned int statusmask) 777{ 778 const char *sep = ""; 779 780 if (statusmask & IPS_EXPECTED) { 781 printf("%sEXPECTED", sep); 782 sep = ","; 783 } 784 if (statusmask & IPS_SEEN_REPLY) { 785 printf("%sSEEN_REPLY", sep); 786 sep = ","; 787 } 788 if (statusmask & IPS_ASSURED) { 789 printf("%sASSURED", sep); 790 sep = ","; 791 } 792 if (statusmask & IPS_CONFIRMED) { 793 printf("%sCONFIRMED", sep); 794 sep = ","; 795 } 796 if (statusmask == 0) 797 printf("%sNONE", sep); 798 printf(" "); 799} 800 801static void 802conntrack_dump_addr(const union nf_inet_addr *addr, 803 const union nf_inet_addr *mask, 804 unsigned int family, bool numeric) 805{ 806 if (family == NFPROTO_IPV4) { 807 if (!numeric && addr->ip == 0) { 808 printf("anywhere "); 809 return; 810 } 811 if (numeric) 812 printf("%s ", xtables_ipaddr_to_numeric(&addr->in)); 813 else 814 printf("%s ", xtables_ipaddr_to_anyname(&addr->in)); 815 } else if (family == NFPROTO_IPV6) { 816 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 && 817 addr->ip6[2] == 0 && addr->ip6[3] == 0) { 818 printf("anywhere "); 819 return; 820 } 821 if (numeric) 822 printf("%s ", xtables_ip6addr_to_numeric(&addr->in6)); 823 else 824 printf("%s ", xtables_ip6addr_to_anyname(&addr->in6)); 825 } 826} 827 828static void 829print_addr(const struct in_addr *addr, const struct in_addr *mask, 830 int inv, int numeric) 831{ 832 char buf[BUFSIZ]; 833 834 if (inv) 835 printf("! "); 836 837 if (mask->s_addr == 0L && !numeric) 838 printf("%s ", "anywhere"); 839 else { 840 if (numeric) 841 strcpy(buf, xtables_ipaddr_to_numeric(addr)); 842 else 843 strcpy(buf, xtables_ipaddr_to_anyname(addr)); 844 strcat(buf, xtables_ipmask_to_numeric(mask)); 845 printf("%s ", buf); 846 } 847} 848 849static void 850matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx) 851{ 852 const struct xt_conntrack_info *sinfo = (const void *)match->data; 853 854 if(sinfo->flags & XT_CONNTRACK_STATE) { 855 if (sinfo->invflags & XT_CONNTRACK_STATE) 856 printf("! "); 857 printf("%sctstate ", optpfx); 858 print_state(sinfo->statemask); 859 } 860 861 if(sinfo->flags & XT_CONNTRACK_PROTO) { 862 if (sinfo->invflags & XT_CONNTRACK_PROTO) 863 printf("! "); 864 printf("%sctproto ", optpfx); 865 printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum); 866 } 867 868 if(sinfo->flags & XT_CONNTRACK_ORIGSRC) { 869 if (sinfo->invflags & XT_CONNTRACK_ORIGSRC) 870 printf("! "); 871 printf("%sctorigsrc ", optpfx); 872 873 print_addr( 874 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 875 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], 876 false, 877 numeric); 878 } 879 880 if(sinfo->flags & XT_CONNTRACK_ORIGDST) { 881 if (sinfo->invflags & XT_CONNTRACK_ORIGDST) 882 printf("! "); 883 printf("%sctorigdst ", optpfx); 884 885 print_addr( 886 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 887 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], 888 false, 889 numeric); 890 } 891 892 if(sinfo->flags & XT_CONNTRACK_REPLSRC) { 893 if (sinfo->invflags & XT_CONNTRACK_REPLSRC) 894 printf("! "); 895 printf("%sctreplsrc ", optpfx); 896 897 print_addr( 898 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 899 &sinfo->sipmsk[IP_CT_DIR_REPLY], 900 false, 901 numeric); 902 } 903 904 if(sinfo->flags & XT_CONNTRACK_REPLDST) { 905 if (sinfo->invflags & XT_CONNTRACK_REPLDST) 906 printf("! "); 907 printf("%sctrepldst ", optpfx); 908 909 print_addr( 910 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 911 &sinfo->dipmsk[IP_CT_DIR_REPLY], 912 false, 913 numeric); 914 } 915 916 if(sinfo->flags & XT_CONNTRACK_STATUS) { 917 if (sinfo->invflags & XT_CONNTRACK_STATUS) 918 printf("! "); 919 printf("%sctstatus ", optpfx); 920 print_status(sinfo->statusmask); 921 } 922 923 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 924 if (sinfo->invflags & XT_CONNTRACK_EXPIRES) 925 printf("! "); 926 printf("%sctexpire ", optpfx); 927 928 if (sinfo->expires_max == sinfo->expires_min) 929 printf("%lu ", sinfo->expires_min); 930 else 931 printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max); 932 } 933 934 if (sinfo->flags & XT_CONNTRACK_DIRECTION) { 935 if (sinfo->invflags & XT_CONNTRACK_DIRECTION) 936 printf("%sctdir REPLY", optpfx); 937 else 938 printf("%sctdir ORIGINAL", optpfx); 939 } 940 941} 942 943static void 944conntrack_dump(const struct xt_conntrack_mtinfo2 *info, const char *prefix, 945 unsigned int family, bool numeric) 946{ 947 if (info->match_flags & XT_CONNTRACK_STATE) { 948 if (info->invert_flags & XT_CONNTRACK_STATE) 949 printf("! "); 950 printf("%sctstate ", prefix); 951 print_state(info->state_mask); 952 } 953 954 if (info->match_flags & XT_CONNTRACK_PROTO) { 955 if (info->invert_flags & XT_CONNTRACK_PROTO) 956 printf("! "); 957 printf("%sctproto %u ", prefix, info->l4proto); 958 } 959 960 if (info->match_flags & XT_CONNTRACK_ORIGSRC) { 961 if (info->invert_flags & XT_CONNTRACK_ORIGSRC) 962 printf("! "); 963 printf("%sctorigsrc ", prefix); 964 conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask, 965 family, numeric); 966 } 967 968 if (info->match_flags & XT_CONNTRACK_ORIGDST) { 969 if (info->invert_flags & XT_CONNTRACK_ORIGDST) 970 printf("! "); 971 printf("%sctorigdst ", prefix); 972 conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask, 973 family, numeric); 974 } 975 976 if (info->match_flags & XT_CONNTRACK_REPLSRC) { 977 if (info->invert_flags & XT_CONNTRACK_REPLSRC) 978 printf("! "); 979 printf("%sctreplsrc ", prefix); 980 conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask, 981 family, numeric); 982 } 983 984 if (info->match_flags & XT_CONNTRACK_REPLDST) { 985 if (info->invert_flags & XT_CONNTRACK_REPLDST) 986 printf("! "); 987 printf("%sctrepldst ", prefix); 988 conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask, 989 family, numeric); 990 } 991 992 if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) { 993 if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT) 994 printf("! "); 995 printf("%sctorigsrcport %u ", prefix, 996 ntohs(info->origsrc_port)); 997 } 998 999 if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) { 1000 if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT) 1001 printf("! "); 1002 printf("%sctorigdstport %u ", prefix, 1003 ntohs(info->origdst_port)); 1004 } 1005 1006 if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) { 1007 if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT) 1008 printf("! "); 1009 printf("%sctreplsrcport %u ", prefix, 1010 ntohs(info->replsrc_port)); 1011 } 1012 1013 if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) { 1014 if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT) 1015 printf("! "); 1016 printf("%sctrepldstport %u ", prefix, 1017 ntohs(info->repldst_port)); 1018 } 1019 1020 if (info->match_flags & XT_CONNTRACK_STATUS) { 1021 if (info->invert_flags & XT_CONNTRACK_STATUS) 1022 printf("! "); 1023 printf("%sctstatus ", prefix); 1024 print_status(info->status_mask); 1025 } 1026 1027 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 1028 if (info->invert_flags & XT_CONNTRACK_EXPIRES) 1029 printf("! "); 1030 printf("%sctexpire ", prefix); 1031 1032 if (info->expires_max == info->expires_min) 1033 printf("%u ", (unsigned int)info->expires_min); 1034 else 1035 printf("%u:%u ", (unsigned int)info->expires_min, 1036 (unsigned int)info->expires_max); 1037 } 1038 1039 if (info->match_flags & XT_CONNTRACK_DIRECTION) { 1040 if (info->invert_flags & XT_CONNTRACK_DIRECTION) 1041 printf("%sctdir REPLY", prefix); 1042 else 1043 printf("%sctdir ORIGINAL", prefix); 1044 } 1045} 1046 1047static void conntrack_print(const void *ip, const struct xt_entry_match *match, 1048 int numeric) 1049{ 1050 matchinfo_print(ip, match, numeric, ""); 1051} 1052 1053static void 1054conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match, 1055 int numeric) 1056{ 1057 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 1058 struct xt_conntrack_mtinfo2 up; 1059 1060 cinfo_transform(&up, info); 1061 conntrack_dump(&up, "", NFPROTO_IPV4, numeric); 1062} 1063 1064static void 1065conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match, 1066 int numeric) 1067{ 1068 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 1069 struct xt_conntrack_mtinfo2 up; 1070 1071 cinfo_transform(&up, info); 1072 conntrack_dump(&up, "", NFPROTO_IPV6, numeric); 1073} 1074 1075static void 1076conntrack_mt_print(const void *ip, const struct xt_entry_match *match, 1077 int numeric) 1078{ 1079 conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric); 1080} 1081 1082static void 1083conntrack_mt6_print(const void *ip, const struct xt_entry_match *match, 1084 int numeric) 1085{ 1086 conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric); 1087} 1088 1089static void conntrack_save(const void *ip, const struct xt_entry_match *match) 1090{ 1091 matchinfo_print(ip, match, 1, "--"); 1092} 1093 1094static void conntrack_mt_save(const void *ip, 1095 const struct xt_entry_match *match) 1096{ 1097 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true); 1098} 1099 1100static void conntrack_mt6_save(const void *ip, 1101 const struct xt_entry_match *match) 1102{ 1103 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true); 1104} 1105 1106static void 1107conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match) 1108{ 1109 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 1110 struct xt_conntrack_mtinfo2 up; 1111 1112 cinfo_transform(&up, info); 1113 conntrack_dump(&up, "--", NFPROTO_IPV4, true); 1114} 1115 1116static void 1117conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match) 1118{ 1119 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 1120 struct xt_conntrack_mtinfo2 up; 1121 1122 cinfo_transform(&up, info); 1123 conntrack_dump(&up, "--", NFPROTO_IPV6, true); 1124} 1125 1126static struct xtables_match conntrack_mt_reg[] = { 1127 { 1128 .version = XTABLES_VERSION, 1129 .name = "conntrack", 1130 .revision = 0, 1131 .family = NFPROTO_IPV4, 1132 .size = XT_ALIGN(sizeof(struct xt_conntrack_info)), 1133 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)), 1134 .help = conntrack_mt_help, 1135 .parse = conntrack_parse, 1136 .final_check = conntrack_mt_check, 1137 .print = conntrack_print, 1138 .save = conntrack_save, 1139 .extra_opts = conntrack_mt_opts_v0, 1140 }, 1141 { 1142 .version = XTABLES_VERSION, 1143 .name = "conntrack", 1144 .revision = 1, 1145 .family = NFPROTO_IPV4, 1146 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 1147 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 1148 .help = conntrack_mt_help, 1149 .parse = conntrack1_mt4_parse, 1150 .final_check = conntrack_mt_check, 1151 .print = conntrack1_mt4_print, 1152 .save = conntrack1_mt4_save, 1153 .extra_opts = conntrack_mt_opts, 1154 }, 1155 { 1156 .version = XTABLES_VERSION, 1157 .name = "conntrack", 1158 .revision = 1, 1159 .family = NFPROTO_IPV6, 1160 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 1161 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 1162 .help = conntrack_mt_help, 1163 .parse = conntrack1_mt6_parse, 1164 .final_check = conntrack_mt_check, 1165 .print = conntrack1_mt6_print, 1166 .save = conntrack1_mt6_save, 1167 .extra_opts = conntrack_mt_opts, 1168 }, 1169 { 1170 .version = XTABLES_VERSION, 1171 .name = "conntrack", 1172 .revision = 2, 1173 .family = NFPROTO_IPV4, 1174 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1175 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1176 .help = conntrack_mt_help, 1177 .parse = conntrack2_mt4_parse, 1178 .final_check = conntrack_mt_check, 1179 .print = conntrack_mt_print, 1180 .save = conntrack_mt_save, 1181 .extra_opts = conntrack_mt_opts, 1182 }, 1183 { 1184 .version = XTABLES_VERSION, 1185 .name = "conntrack", 1186 .revision = 2, 1187 .family = NFPROTO_IPV6, 1188 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1189 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1190 .help = conntrack_mt_help, 1191 .parse = conntrack2_mt6_parse, 1192 .final_check = conntrack_mt_check, 1193 .print = conntrack_mt6_print, 1194 .save = conntrack_mt6_save, 1195 .extra_opts = conntrack_mt_opts, 1196 }, 1197}; 1198 1199void _init(void) 1200{ 1201 xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 1202} 1203