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