libxt_conntrack.c revision a80b6046fa216c26dbc18d587f6255afa8444885
1/* Shared library add-on to iptables for conntrack matching support. 2 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). 3 */ 4 5#include <ctype.h> 6#include <getopt.h> 7#include <netdb.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <iptables.h> 12#include <linux/netfilter.h> 13#include <linux/netfilter/xt_conntrack.h> 14#include <linux/netfilter/nf_conntrack_common.h> 15 16/* Function which prints out usage message. */ 17static void conntrack_mt_help(void) 18{ 19 printf( 20"conntrack match options:\n" 21"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n" 22" State(s) to match\n" 23"[!] --ctproto proto Protocol to match; by number or name, e.g. \"tcp\"\n" 24"[!] --ctorigsrc address[/mask]\n" 25"[!] --ctorigdst address[/mask]\n" 26"[!] --ctreplsrc address[/mask]\n" 27"[!] --ctrepldst address[/mask]\n" 28" Original/Reply source/destination address\n" 29"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n" 30" Status(es) to match\n" 31"[!] --ctexpire time[:time] Match remaining lifetime in seconds against\n" 32" value or range of values (inclusive)\n" 33"\n"); 34} 35 36static const struct option conntrack_mt_opts[] = { 37 {.name = "ctstate", .has_arg = true, .val = '1'}, 38 {.name = "ctproto", .has_arg = true, .val = '2'}, 39 {.name = "ctorigsrc", .has_arg = true, .val = '3'}, 40 {.name = "ctorigdst", .has_arg = true, .val = '4'}, 41 {.name = "ctreplsrc", .has_arg = true, .val = '5'}, 42 {.name = "ctrepldst", .has_arg = true, .val = '6'}, 43 {.name = "ctstatus", .has_arg = true, .val = '7'}, 44 {.name = "ctexpire", .has_arg = true, .val = '8'}, 45 {}, 46}; 47 48static int 49parse_state(const char *state, size_t strlen, struct xt_conntrack_info *sinfo) 50{ 51 if (strncasecmp(state, "INVALID", strlen) == 0) 52 sinfo->statemask |= XT_CONNTRACK_STATE_INVALID; 53 else if (strncasecmp(state, "NEW", strlen) == 0) 54 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); 55 else if (strncasecmp(state, "ESTABLISHED", strlen) == 0) 56 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); 57 else if (strncasecmp(state, "RELATED", strlen) == 0) 58 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); 59 else if (strncasecmp(state, "UNTRACKED", strlen) == 0) 60 sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED; 61 else if (strncasecmp(state, "SNAT", strlen) == 0) 62 sinfo->statemask |= XT_CONNTRACK_STATE_SNAT; 63 else if (strncasecmp(state, "DNAT", strlen) == 0) 64 sinfo->statemask |= XT_CONNTRACK_STATE_DNAT; 65 else 66 return 0; 67 return 1; 68} 69 70static void 71parse_states(const char *arg, struct xt_conntrack_info *sinfo) 72{ 73 const char *comma; 74 75 while ((comma = strchr(arg, ',')) != NULL) { 76 if (comma == arg || !parse_state(arg, comma-arg, sinfo)) 77 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg); 78 arg = comma+1; 79 } 80 81 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo)) 82 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg); 83} 84 85static int 86parse_status(const char *status, size_t strlen, struct xt_conntrack_info *sinfo) 87{ 88 if (strncasecmp(status, "NONE", strlen) == 0) 89 sinfo->statusmask |= 0; 90 else if (strncasecmp(status, "EXPECTED", strlen) == 0) 91 sinfo->statusmask |= IPS_EXPECTED; 92 else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0) 93 sinfo->statusmask |= IPS_SEEN_REPLY; 94 else if (strncasecmp(status, "ASSURED", strlen) == 0) 95 sinfo->statusmask |= IPS_ASSURED; 96#ifdef IPS_CONFIRMED 97 else if (strncasecmp(status, "CONFIRMED", strlen) == 0) 98 sinfo->stausmask |= IPS_CONFIRMED; 99#endif 100 else 101 return 0; 102 return 1; 103} 104 105static void 106parse_statuses(const char *arg, struct xt_conntrack_info *sinfo) 107{ 108 const char *comma; 109 110 while ((comma = strchr(arg, ',')) != NULL) { 111 if (comma == arg || !parse_status(arg, comma-arg, sinfo)) 112 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg); 113 arg = comma+1; 114 } 115 116 if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo)) 117 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg); 118} 119 120static unsigned long 121parse_expire(const char *s) 122{ 123 unsigned int len; 124 125 if (string_to_number(s, 0, 0, &len) == -1) 126 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s); 127 else 128 return len; 129} 130 131/* If a single value is provided, min and max are both set to the value */ 132static void 133parse_expires(const char *s, struct xt_conntrack_info *sinfo) 134{ 135 char *buffer; 136 char *cp; 137 138 buffer = strdup(s); 139 if ((cp = strchr(buffer, ':')) == NULL) 140 sinfo->expires_min = sinfo->expires_max = parse_expire(buffer); 141 else { 142 *cp = '\0'; 143 cp++; 144 145 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0; 146 sinfo->expires_max = cp[0] ? parse_expire(cp) : -1; 147 } 148 free(buffer); 149 150 if (sinfo->expires_min > sinfo->expires_max) 151 exit_error(PARAMETER_PROBLEM, 152 "expire min. range value `%lu' greater than max. " 153 "range value `%lu'", sinfo->expires_min, sinfo->expires_max); 154} 155 156/* Function which parses command options; returns true if it 157 ate an option */ 158static int conntrack_parse(int c, char **argv, int invert, unsigned int *flags, 159 const void *entry, struct xt_entry_match **match) 160{ 161 struct xt_conntrack_info *sinfo = (void *)(*match)->data; 162 char *protocol = NULL; 163 unsigned int naddrs = 0; 164 struct in_addr *addrs = NULL; 165 166 167 switch (c) { 168 case '1': 169 check_inverse(optarg, &invert, &optind, 0); 170 171 parse_states(argv[optind-1], sinfo); 172 if (invert) { 173 sinfo->invflags |= XT_CONNTRACK_STATE; 174 } 175 sinfo->flags |= XT_CONNTRACK_STATE; 176 break; 177 178 case '2': 179 check_inverse(optarg, &invert, &optind, 0); 180 181 if(invert) 182 sinfo->invflags |= XT_CONNTRACK_PROTO; 183 184 /* Canonicalize into lower case */ 185 for (protocol = argv[optind-1]; *protocol; protocol++) 186 *protocol = tolower(*protocol); 187 188 protocol = argv[optind-1]; 189 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol); 190 191 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0 192 && (sinfo->invflags & XT_INV_PROTO)) 193 exit_error(PARAMETER_PROBLEM, 194 "rule would never match protocol"); 195 196 sinfo->flags |= XT_CONNTRACK_PROTO; 197 break; 198 199 case '3': 200 check_inverse(optarg, &invert, &optind, 0); 201 202 if (invert) 203 sinfo->invflags |= XT_CONNTRACK_ORIGSRC; 204 205 parse_hostnetworkmask(argv[optind-1], &addrs, 206 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], 207 &naddrs); 208 if(naddrs > 1) 209 exit_error(PARAMETER_PROBLEM, 210 "multiple IP addresses not allowed"); 211 212 if(naddrs == 1) { 213 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr; 214 } 215 216 sinfo->flags |= XT_CONNTRACK_ORIGSRC; 217 break; 218 219 case '4': 220 check_inverse(optarg, &invert, &optind, 0); 221 222 if (invert) 223 sinfo->invflags |= XT_CONNTRACK_ORIGDST; 224 225 parse_hostnetworkmask(argv[optind-1], &addrs, 226 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], 227 &naddrs); 228 if(naddrs > 1) 229 exit_error(PARAMETER_PROBLEM, 230 "multiple IP addresses not allowed"); 231 232 if(naddrs == 1) { 233 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr; 234 } 235 236 sinfo->flags |= XT_CONNTRACK_ORIGDST; 237 break; 238 239 case '5': 240 check_inverse(optarg, &invert, &optind, 0); 241 242 if (invert) 243 sinfo->invflags |= XT_CONNTRACK_REPLSRC; 244 245 parse_hostnetworkmask(argv[optind-1], &addrs, 246 &sinfo->sipmsk[IP_CT_DIR_REPLY], 247 &naddrs); 248 if(naddrs > 1) 249 exit_error(PARAMETER_PROBLEM, 250 "multiple IP addresses not allowed"); 251 252 if(naddrs == 1) { 253 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr; 254 } 255 256 sinfo->flags |= XT_CONNTRACK_REPLSRC; 257 break; 258 259 case '6': 260 check_inverse(optarg, &invert, &optind, 0); 261 262 if (invert) 263 sinfo->invflags |= XT_CONNTRACK_REPLDST; 264 265 parse_hostnetworkmask(argv[optind-1], &addrs, 266 &sinfo->dipmsk[IP_CT_DIR_REPLY], 267 &naddrs); 268 if(naddrs > 1) 269 exit_error(PARAMETER_PROBLEM, 270 "multiple IP addresses not allowed"); 271 272 if(naddrs == 1) { 273 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr; 274 } 275 276 sinfo->flags |= XT_CONNTRACK_REPLDST; 277 break; 278 279 case '7': 280 check_inverse(optarg, &invert, &optind, 0); 281 282 parse_statuses(argv[optind-1], sinfo); 283 if (invert) { 284 sinfo->invflags |= XT_CONNTRACK_STATUS; 285 } 286 sinfo->flags |= XT_CONNTRACK_STATUS; 287 break; 288 289 case '8': 290 check_inverse(optarg, &invert, &optind, 0); 291 292 parse_expires(argv[optind-1], sinfo); 293 if (invert) { 294 sinfo->invflags |= XT_CONNTRACK_EXPIRES; 295 } 296 sinfo->flags |= XT_CONNTRACK_EXPIRES; 297 break; 298 299 default: 300 return 0; 301 } 302 303 *flags = sinfo->flags; 304 return 1; 305} 306 307static void conntrack_mt_check(unsigned int flags) 308{ 309 if (flags == 0) 310 exit_error(PARAMETER_PROBLEM, "You must specify one or more options"); 311} 312 313static void 314print_state(unsigned int statemask) 315{ 316 const char *sep = ""; 317 318 if (statemask & XT_CONNTRACK_STATE_INVALID) { 319 printf("%sINVALID", sep); 320 sep = ","; 321 } 322 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) { 323 printf("%sNEW", sep); 324 sep = ","; 325 } 326 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) { 327 printf("%sRELATED", sep); 328 sep = ","; 329 } 330 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) { 331 printf("%sESTABLISHED", sep); 332 sep = ","; 333 } 334 if (statemask & XT_CONNTRACK_STATE_UNTRACKED) { 335 printf("%sUNTRACKED", sep); 336 sep = ","; 337 } 338 if (statemask & XT_CONNTRACK_STATE_SNAT) { 339 printf("%sSNAT", sep); 340 sep = ","; 341 } 342 if (statemask & XT_CONNTRACK_STATE_DNAT) { 343 printf("%sDNAT", sep); 344 sep = ","; 345 } 346 printf(" "); 347} 348 349static void 350print_status(unsigned int statusmask) 351{ 352 const char *sep = ""; 353 354 if (statusmask & IPS_EXPECTED) { 355 printf("%sEXPECTED", sep); 356 sep = ","; 357 } 358 if (statusmask & IPS_SEEN_REPLY) { 359 printf("%sSEEN_REPLY", sep); 360 sep = ","; 361 } 362 if (statusmask & IPS_ASSURED) { 363 printf("%sASSURED", sep); 364 sep = ","; 365 } 366#ifdef IPS_CONFIRMED 367 if (statusmask & IPS_CONFIRMED) { 368 printf("%sCONFIRMED", sep); 369 sep =","; 370 } 371#endif 372 if (statusmask == 0) { 373 printf("%sNONE", sep); 374 sep = ","; 375 } 376 printf(" "); 377} 378 379static void 380print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric) 381{ 382 char buf[BUFSIZ]; 383 384 if (inv) 385 printf("! "); 386 387 if (mask->s_addr == 0L && !numeric) 388 printf("%s ", "anywhere"); 389 else { 390 if (numeric) 391 sprintf(buf, "%s", addr_to_dotted(addr)); 392 else 393 sprintf(buf, "%s", addr_to_anyname(addr)); 394 strcat(buf, mask_to_dotted(mask)); 395 printf("%s ", buf); 396 } 397} 398 399/* Saves the matchinfo in parsable form to stdout. */ 400static void 401matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx) 402{ 403 struct xt_conntrack_info *sinfo = (void *)match->data; 404 405 if(sinfo->flags & XT_CONNTRACK_STATE) { 406 if (sinfo->invflags & XT_CONNTRACK_STATE) 407 printf("! "); 408 printf("%sctstate ", optpfx); 409 print_state(sinfo->statemask); 410 } 411 412 if(sinfo->flags & XT_CONNTRACK_PROTO) { 413 if (sinfo->invflags & XT_CONNTRACK_PROTO) 414 printf("! "); 415 printf("%sctproto ", optpfx); 416 printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum); 417 } 418 419 if(sinfo->flags & XT_CONNTRACK_ORIGSRC) { 420 if (sinfo->invflags & XT_CONNTRACK_ORIGSRC) 421 printf("! "); 422 printf("%sctorigsrc ", optpfx); 423 424 print_addr( 425 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 426 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], 427 false, 428 numeric); 429 } 430 431 if(sinfo->flags & XT_CONNTRACK_ORIGDST) { 432 if (sinfo->invflags & XT_CONNTRACK_ORIGDST) 433 printf("! "); 434 printf("%sctorigdst ", optpfx); 435 436 print_addr( 437 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 438 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], 439 false, 440 numeric); 441 } 442 443 if(sinfo->flags & XT_CONNTRACK_REPLSRC) { 444 if (sinfo->invflags & XT_CONNTRACK_REPLSRC) 445 printf("! "); 446 printf("%sctreplsrc ", optpfx); 447 448 print_addr( 449 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 450 &sinfo->sipmsk[IP_CT_DIR_REPLY], 451 false, 452 numeric); 453 } 454 455 if(sinfo->flags & XT_CONNTRACK_REPLDST) { 456 if (sinfo->invflags & XT_CONNTRACK_REPLDST) 457 printf("! "); 458 printf("%sctrepldst ", optpfx); 459 460 print_addr( 461 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 462 &sinfo->dipmsk[IP_CT_DIR_REPLY], 463 false, 464 numeric); 465 } 466 467 if(sinfo->flags & XT_CONNTRACK_STATUS) { 468 if (sinfo->invflags & XT_CONNTRACK_STATUS) 469 printf("! "); 470 printf("%sctstatus ", optpfx); 471 print_status(sinfo->statusmask); 472 } 473 474 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 475 if (sinfo->invflags & XT_CONNTRACK_EXPIRES) 476 printf("! "); 477 printf("%sctexpire ", optpfx); 478 479 if (sinfo->expires_max == sinfo->expires_min) 480 printf("%lu ", sinfo->expires_min); 481 else 482 printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max); 483 } 484} 485 486/* Prints out the matchinfo. */ 487static void conntrack_print(const void *ip, const struct xt_entry_match *match, 488 int numeric) 489{ 490 matchinfo_print(ip, match, numeric, ""); 491} 492 493/* Saves the matchinfo in parsable form to stdout. */ 494static void conntrack_save(const void *ip, const struct xt_entry_match *match) 495{ 496 matchinfo_print(ip, match, 1, "--"); 497} 498 499static struct xtables_match conntrack_match = { 500 .version = IPTABLES_VERSION, 501 .name = "conntrack", 502 .revision = 0, 503 .family = AF_INET, 504 .size = XT_ALIGN(sizeof(struct xt_conntrack_info)), 505 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)), 506 .help = conntrack_mt_help, 507 .parse = conntrack_parse, 508 .final_check = conntrack_mt_check, 509 .print = conntrack_print, 510 .save = conntrack_save, 511 .extra_opts = conntrack_mt_opts, 512}; 513 514void _init(void) 515{ 516 xtables_register_match(&conntrack_match); 517} 518