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