1 /* 2 * libxt_conntrack 3 * Shared library add-on to iptables for conntrack matching support. 4 * 5 * GPL (C) 2001 Marc Boucher (marc (at) mbsi.ca). 6 * Copyright CC Computer Consultants GmbH, 2007 - 2008 7 * Jan Engelhardt <jengelh (at) 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 18 struct 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 37 struct 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 51 enum { 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 67 static 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 */ 92 static 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 */ 115 static 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 145 static int 146 parse_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 167 static void 168 parse_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 185 static bool 186 conntrack_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 208 static void 209 conntrack_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 224 static int 225 parse_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 244 static void 245 parse_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 259 static bool 260 conntrack_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 278 static void 279 conntrack_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 294 static void conntrack_parse(struct xt_option_call *cb) 295 { 296 struct xt_conntrack_info *sinfo = cb->data; 297 298 xtables_option_parse(cb); 299 switch (cb->entry->id) { 300 case O_CTSTATE: 301 parse_states(cb->arg, sinfo); 302 if (cb->invert) 303 sinfo->invflags |= XT_CONNTRACK_STATE; 304 break; 305 case O_CTPROTO: 306 if (cb->invert) 307 sinfo->invflags |= XT_CONNTRACK_PROTO; 308 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol; 309 310 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0 311 && (sinfo->invflags & XT_INV_PROTO)) 312 xtables_error(PARAMETER_PROBLEM, 313 "rule would never match protocol"); 314 315 sinfo->flags |= XT_CONNTRACK_PROTO; 316 break; 317 case O_CTORIGSRC: 318 if (cb->invert) 319 sinfo->invflags |= XT_CONNTRACK_ORIGSRC; 320 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip; 321 sinfo->flags |= XT_CONNTRACK_ORIGSRC; 322 break; 323 case O_CTORIGDST: 324 if (cb->invert) 325 sinfo->invflags |= XT_CONNTRACK_ORIGDST; 326 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip; 327 sinfo->flags |= XT_CONNTRACK_ORIGDST; 328 break; 329 case O_CTREPLSRC: 330 if (cb->invert) 331 sinfo->invflags |= XT_CONNTRACK_REPLSRC; 332 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip; 333 sinfo->flags |= XT_CONNTRACK_REPLSRC; 334 break; 335 case O_CTREPLDST: 336 if (cb->invert) 337 sinfo->invflags |= XT_CONNTRACK_REPLDST; 338 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip; 339 sinfo->flags |= XT_CONNTRACK_REPLDST; 340 break; 341 case O_CTSTATUS: 342 parse_statuses(cb->arg, sinfo); 343 if (cb->invert) 344 sinfo->invflags |= XT_CONNTRACK_STATUS; 345 sinfo->flags |= XT_CONNTRACK_STATUS; 346 break; 347 case O_CTEXPIRE: 348 sinfo->expires_min = cb->val.u32_range[0]; 349 sinfo->expires_max = cb->val.u32_range[0]; 350 if (cb->nvals >= 2) 351 sinfo->expires_max = cb->val.u32_range[1]; 352 if (cb->invert) 353 sinfo->invflags |= XT_CONNTRACK_EXPIRES; 354 sinfo->flags |= XT_CONNTRACK_EXPIRES; 355 break; 356 } 357 } 358 359 static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev) 360 { 361 struct xt_conntrack_mtinfo3 *info = cb->data; 362 363 xtables_option_parse(cb); 364 switch (cb->entry->id) { 365 case O_CTSTATE: 366 conntrack_ps_states(info, cb->arg); 367 info->match_flags |= XT_CONNTRACK_STATE; 368 if (cb->invert) 369 info->invert_flags |= XT_CONNTRACK_STATE; 370 break; 371 case O_CTPROTO: 372 info->l4proto = cb->val.protocol; 373 if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO)) 374 xtables_error(PARAMETER_PROBLEM, "conntrack: rule would " 375 "never match protocol"); 376 377 info->match_flags |= XT_CONNTRACK_PROTO; 378 if (cb->invert) 379 info->invert_flags |= XT_CONNTRACK_PROTO; 380 break; 381 case O_CTORIGSRC: 382 info->origsrc_addr = cb->val.haddr; 383 info->origsrc_mask = cb->val.hmask; 384 info->match_flags |= XT_CONNTRACK_ORIGSRC; 385 if (cb->invert) 386 info->invert_flags |= XT_CONNTRACK_ORIGSRC; 387 break; 388 case O_CTORIGDST: 389 info->origdst_addr = cb->val.haddr; 390 info->origdst_mask = cb->val.hmask; 391 info->match_flags |= XT_CONNTRACK_ORIGDST; 392 if (cb->invert) 393 info->invert_flags |= XT_CONNTRACK_ORIGDST; 394 break; 395 case O_CTREPLSRC: 396 info->replsrc_addr = cb->val.haddr; 397 info->replsrc_mask = cb->val.hmask; 398 info->match_flags |= XT_CONNTRACK_REPLSRC; 399 if (cb->invert) 400 info->invert_flags |= XT_CONNTRACK_REPLSRC; 401 break; 402 case O_CTREPLDST: 403 info->repldst_addr = cb->val.haddr; 404 info->repldst_mask = cb->val.hmask; 405 info->match_flags |= XT_CONNTRACK_REPLDST; 406 if (cb->invert) 407 info->invert_flags |= XT_CONNTRACK_REPLDST; 408 break; 409 case O_CTSTATUS: 410 conntrack_ps_statuses(info, cb->arg); 411 info->match_flags |= XT_CONNTRACK_STATUS; 412 if (cb->invert) 413 info->invert_flags |= XT_CONNTRACK_STATUS; 414 break; 415 case O_CTEXPIRE: 416 info->expires_min = cb->val.u32_range[0]; 417 info->expires_max = cb->val.u32_range[0]; 418 if (cb->nvals >= 2) 419 info->expires_max = cb->val.u32_range[1]; 420 info->match_flags |= XT_CONNTRACK_EXPIRES; 421 if (cb->invert) 422 info->invert_flags |= XT_CONNTRACK_EXPIRES; 423 break; 424 case O_CTORIGSRCPORT: 425 info->origsrc_port = cb->val.port_range[0]; 426 info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2]; 427 info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT; 428 if (cb->invert) 429 info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT; 430 break; 431 case O_CTORIGDSTPORT: 432 info->origdst_port = cb->val.port_range[0]; 433 info->origdst_port_high = cb->val.port_range[cb->nvals >= 2]; 434 info->match_flags |= XT_CONNTRACK_ORIGDST_PORT; 435 if (cb->invert) 436 info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT; 437 break; 438 case O_CTREPLSRCPORT: 439 info->replsrc_port = cb->val.port_range[0]; 440 info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2]; 441 info->match_flags |= XT_CONNTRACK_REPLSRC_PORT; 442 if (cb->invert) 443 info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT; 444 break; 445 case O_CTREPLDSTPORT: 446 info->repldst_port = cb->val.port_range[0]; 447 info->repldst_port_high = cb->val.port_range[cb->nvals >= 2]; 448 info->match_flags |= XT_CONNTRACK_REPLDST_PORT; 449 if (cb->invert) 450 info->invert_flags |= XT_CONNTRACK_REPLDST_PORT; 451 break; 452 case O_CTDIR: 453 if (strcasecmp(cb->arg, "ORIGINAL") == 0) { 454 info->match_flags |= XT_CONNTRACK_DIRECTION; 455 info->invert_flags &= ~XT_CONNTRACK_DIRECTION; 456 } else if (strcasecmp(cb->arg, "REPLY") == 0) { 457 info->match_flags |= XT_CONNTRACK_DIRECTION; 458 info->invert_flags |= XT_CONNTRACK_DIRECTION; 459 } else { 460 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg); 461 } 462 break; 463 } 464 } 465 466 #define cinfo_transform(r, l) \ 467 do { \ 468 memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \ 469 (r)->state_mask = (l)->state_mask; \ 470 (r)->status_mask = (l)->status_mask; \ 471 } while (false); 472 473 static void conntrack1_mt_parse(struct xt_option_call *cb) 474 { 475 struct xt_conntrack_mtinfo1 *info = cb->data; 476 struct xt_conntrack_mtinfo3 up; 477 478 memset(&up, 0, sizeof(up)); 479 cinfo_transform(&up, info); 480 up.origsrc_port_high = up.origsrc_port; 481 up.origdst_port_high = up.origdst_port; 482 up.replsrc_port_high = up.replsrc_port; 483 up.repldst_port_high = up.repldst_port; 484 cb->data = &up; 485 conntrack_mt_parse(cb, 3); 486 if (up.origsrc_port != up.origsrc_port_high || 487 up.origdst_port != up.origdst_port_high || 488 up.replsrc_port != up.replsrc_port_high || 489 up.repldst_port != up.repldst_port_high) 490 xtables_error(PARAMETER_PROBLEM, 491 "conntrack rev 1 does not support port ranges"); 492 cinfo_transform(info, &up); 493 cb->data = info; 494 } 495 496 static void conntrack2_mt_parse(struct xt_option_call *cb) 497 { 498 #define cinfo2_transform(r, l) \ 499 memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info)); 500 501 struct xt_conntrack_mtinfo2 *info = cb->data; 502 struct xt_conntrack_mtinfo3 up; 503 504 memset(&up, 0, sizeof(up)); 505 memcpy(&up, info, sizeof(*info)); 506 up.origsrc_port_high = up.origsrc_port; 507 up.origdst_port_high = up.origdst_port; 508 up.replsrc_port_high = up.replsrc_port; 509 up.repldst_port_high = up.repldst_port; 510 cb->data = &up; 511 conntrack_mt_parse(cb, 3); 512 if (up.origsrc_port != up.origsrc_port_high || 513 up.origdst_port != up.origdst_port_high || 514 up.replsrc_port != up.replsrc_port_high || 515 up.repldst_port != up.repldst_port_high) 516 xtables_error(PARAMETER_PROBLEM, 517 "conntrack rev 2 does not support port ranges"); 518 memcpy(info, &up, sizeof(*info)); 519 cb->data = info; 520 #undef cinfo2_transform 521 } 522 523 static void conntrack3_mt_parse(struct xt_option_call *cb) 524 { 525 conntrack_mt_parse(cb, 3); 526 } 527 528 static void conntrack_mt_check(struct xt_fcheck_call *cb) 529 { 530 if (cb->xflags == 0) 531 xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option " 532 "is required"); 533 } 534 535 static void 536 print_state(unsigned int statemask) 537 { 538 const char *sep = " "; 539 540 if (statemask & XT_CONNTRACK_STATE_INVALID) { 541 printf("%sINVALID", sep); 542 sep = ","; 543 } 544 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) { 545 printf("%sNEW", sep); 546 sep = ","; 547 } 548 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) { 549 printf("%sRELATED", sep); 550 sep = ","; 551 } 552 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) { 553 printf("%sESTABLISHED", sep); 554 sep = ","; 555 } 556 if (statemask & XT_CONNTRACK_STATE_UNTRACKED) { 557 printf("%sUNTRACKED", sep); 558 sep = ","; 559 } 560 if (statemask & XT_CONNTRACK_STATE_SNAT) { 561 printf("%sSNAT", sep); 562 sep = ","; 563 } 564 if (statemask & XT_CONNTRACK_STATE_DNAT) { 565 printf("%sDNAT", sep); 566 sep = ","; 567 } 568 } 569 570 static void 571 print_status(unsigned int statusmask) 572 { 573 const char *sep = " "; 574 575 if (statusmask & IPS_EXPECTED) { 576 printf("%sEXPECTED", sep); 577 sep = ","; 578 } 579 if (statusmask & IPS_SEEN_REPLY) { 580 printf("%sSEEN_REPLY", sep); 581 sep = ","; 582 } 583 if (statusmask & IPS_ASSURED) { 584 printf("%sASSURED", sep); 585 sep = ","; 586 } 587 if (statusmask & IPS_CONFIRMED) { 588 printf("%sCONFIRMED", sep); 589 sep = ","; 590 } 591 if (statusmask == 0) 592 printf("%sNONE", sep); 593 } 594 595 static void 596 conntrack_dump_addr(const union nf_inet_addr *addr, 597 const union nf_inet_addr *mask, 598 unsigned int family, bool numeric) 599 { 600 if (family == NFPROTO_IPV4) { 601 if (!numeric && addr->ip == 0) { 602 printf(" anywhere"); 603 return; 604 } 605 if (numeric) 606 printf(" %s%s", 607 xtables_ipaddr_to_numeric(&addr->in), 608 xtables_ipmask_to_numeric(&mask->in)); 609 else 610 printf(" %s%s", 611 xtables_ipaddr_to_anyname(&addr->in), 612 xtables_ipmask_to_numeric(&mask->in)); 613 } else if (family == NFPROTO_IPV6) { 614 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 && 615 addr->ip6[2] == 0 && addr->ip6[3] == 0) { 616 printf(" anywhere"); 617 return; 618 } 619 if (numeric) 620 printf(" %s%s", 621 xtables_ip6addr_to_numeric(&addr->in6), 622 xtables_ip6mask_to_numeric(&mask->in6)); 623 else 624 printf(" %s%s", 625 xtables_ip6addr_to_anyname(&addr->in6), 626 xtables_ip6mask_to_numeric(&mask->in6)); 627 } 628 } 629 630 static void 631 print_addr(const struct in_addr *addr, const struct in_addr *mask, 632 int inv, int numeric) 633 { 634 char buf[BUFSIZ]; 635 636 if (inv) 637 printf(" !"); 638 639 if (mask->s_addr == 0L && !numeric) 640 printf(" %s", "anywhere"); 641 else { 642 if (numeric) 643 strcpy(buf, xtables_ipaddr_to_numeric(addr)); 644 else 645 strcpy(buf, xtables_ipaddr_to_anyname(addr)); 646 strcat(buf, xtables_ipmask_to_numeric(mask)); 647 printf(" %s", buf); 648 } 649 } 650 651 static void 652 matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx) 653 { 654 const struct xt_conntrack_info *sinfo = (const void *)match->data; 655 656 if(sinfo->flags & XT_CONNTRACK_STATE) { 657 if (sinfo->invflags & XT_CONNTRACK_STATE) 658 printf(" !"); 659 printf(" %sctstate", optpfx); 660 print_state(sinfo->statemask); 661 } 662 663 if(sinfo->flags & XT_CONNTRACK_PROTO) { 664 if (sinfo->invflags & XT_CONNTRACK_PROTO) 665 printf(" !"); 666 printf(" %sctproto", optpfx); 667 printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum); 668 } 669 670 if(sinfo->flags & XT_CONNTRACK_ORIGSRC) { 671 if (sinfo->invflags & XT_CONNTRACK_ORIGSRC) 672 printf(" !"); 673 printf(" %sctorigsrc", optpfx); 674 675 print_addr( 676 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 677 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], 678 false, 679 numeric); 680 } 681 682 if(sinfo->flags & XT_CONNTRACK_ORIGDST) { 683 if (sinfo->invflags & XT_CONNTRACK_ORIGDST) 684 printf(" !"); 685 printf(" %sctorigdst", optpfx); 686 687 print_addr( 688 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 689 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], 690 false, 691 numeric); 692 } 693 694 if(sinfo->flags & XT_CONNTRACK_REPLSRC) { 695 if (sinfo->invflags & XT_CONNTRACK_REPLSRC) 696 printf(" !"); 697 printf(" %sctreplsrc", optpfx); 698 699 print_addr( 700 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 701 &sinfo->sipmsk[IP_CT_DIR_REPLY], 702 false, 703 numeric); 704 } 705 706 if(sinfo->flags & XT_CONNTRACK_REPLDST) { 707 if (sinfo->invflags & XT_CONNTRACK_REPLDST) 708 printf(" !"); 709 printf(" %sctrepldst", optpfx); 710 711 print_addr( 712 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 713 &sinfo->dipmsk[IP_CT_DIR_REPLY], 714 false, 715 numeric); 716 } 717 718 if(sinfo->flags & XT_CONNTRACK_STATUS) { 719 if (sinfo->invflags & XT_CONNTRACK_STATUS) 720 printf(" !"); 721 printf(" %sctstatus", optpfx); 722 print_status(sinfo->statusmask); 723 } 724 725 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 726 if (sinfo->invflags & XT_CONNTRACK_EXPIRES) 727 printf(" !"); 728 printf(" %sctexpire ", optpfx); 729 730 if (sinfo->expires_max == sinfo->expires_min) 731 printf("%lu", sinfo->expires_min); 732 else 733 printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max); 734 } 735 736 if (sinfo->flags & XT_CONNTRACK_DIRECTION) { 737 if (sinfo->invflags & XT_CONNTRACK_DIRECTION) 738 printf(" %sctdir REPLY", optpfx); 739 else 740 printf(" %sctdir ORIGINAL", optpfx); 741 } 742 743 } 744 745 static void 746 conntrack_dump_ports(const char *prefix, const char *opt, 747 u_int16_t port_low, u_int16_t port_high) 748 { 749 if (port_high == 0 || port_low == port_high) 750 printf(" %s%s %u", prefix, opt, port_low); 751 else 752 printf(" %s%s %u:%u", prefix, opt, port_low, port_high); 753 } 754 755 static void 756 conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix, 757 unsigned int family, bool numeric, bool v3) 758 { 759 if (info->match_flags & XT_CONNTRACK_STATE) { 760 if (info->invert_flags & XT_CONNTRACK_STATE) 761 printf(" !"); 762 printf(" %sctstate", prefix); 763 print_state(info->state_mask); 764 } 765 766 if (info->match_flags & XT_CONNTRACK_PROTO) { 767 if (info->invert_flags & XT_CONNTRACK_PROTO) 768 printf(" !"); 769 printf(" %sctproto %u", prefix, info->l4proto); 770 } 771 772 if (info->match_flags & XT_CONNTRACK_ORIGSRC) { 773 if (info->invert_flags & XT_CONNTRACK_ORIGSRC) 774 printf(" !"); 775 printf(" %sctorigsrc", prefix); 776 conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask, 777 family, numeric); 778 } 779 780 if (info->match_flags & XT_CONNTRACK_ORIGDST) { 781 if (info->invert_flags & XT_CONNTRACK_ORIGDST) 782 printf(" !"); 783 printf(" %sctorigdst", prefix); 784 conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask, 785 family, numeric); 786 } 787 788 if (info->match_flags & XT_CONNTRACK_REPLSRC) { 789 if (info->invert_flags & XT_CONNTRACK_REPLSRC) 790 printf(" !"); 791 printf(" %sctreplsrc", prefix); 792 conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask, 793 family, numeric); 794 } 795 796 if (info->match_flags & XT_CONNTRACK_REPLDST) { 797 if (info->invert_flags & XT_CONNTRACK_REPLDST) 798 printf(" !"); 799 printf(" %sctrepldst", prefix); 800 conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask, 801 family, numeric); 802 } 803 804 if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) { 805 if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT) 806 printf(" !"); 807 conntrack_dump_ports(prefix, "ctorigsrcport", 808 v3 ? info->origsrc_port : ntohs(info->origsrc_port), 809 v3 ? info->origsrc_port_high : 0); 810 } 811 812 if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) { 813 if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT) 814 printf(" !"); 815 conntrack_dump_ports(prefix, "ctorigdstport", 816 v3 ? info->origdst_port : ntohs(info->origdst_port), 817 v3 ? info->origdst_port_high : 0); 818 } 819 820 if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) { 821 if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT) 822 printf(" !"); 823 conntrack_dump_ports(prefix, "ctreplsrcport", 824 v3 ? info->replsrc_port : ntohs(info->replsrc_port), 825 v3 ? info->replsrc_port_high : 0); 826 } 827 828 if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) { 829 if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT) 830 printf(" !"); 831 conntrack_dump_ports(prefix, "ctrepldstport", 832 v3 ? info->repldst_port : ntohs(info->repldst_port), 833 v3 ? info->repldst_port_high : 0); 834 } 835 836 if (info->match_flags & XT_CONNTRACK_STATUS) { 837 if (info->invert_flags & XT_CONNTRACK_STATUS) 838 printf(" !"); 839 printf(" %sctstatus", prefix); 840 print_status(info->status_mask); 841 } 842 843 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 844 if (info->invert_flags & XT_CONNTRACK_EXPIRES) 845 printf(" !"); 846 printf(" %sctexpire ", prefix); 847 848 if (info->expires_max == info->expires_min) 849 printf("%u", (unsigned int)info->expires_min); 850 else 851 printf("%u:%u", (unsigned int)info->expires_min, 852 (unsigned int)info->expires_max); 853 } 854 855 if (info->match_flags & XT_CONNTRACK_DIRECTION) { 856 if (info->invert_flags & XT_CONNTRACK_DIRECTION) 857 printf(" %sctdir REPLY", prefix); 858 else 859 printf(" %sctdir ORIGINAL", prefix); 860 } 861 } 862 863 static void conntrack_print(const void *ip, const struct xt_entry_match *match, 864 int numeric) 865 { 866 matchinfo_print(ip, match, numeric, ""); 867 } 868 869 static void 870 conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match, 871 int numeric) 872 { 873 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 874 struct xt_conntrack_mtinfo3 up; 875 876 cinfo_transform(&up, info); 877 conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false); 878 } 879 880 static void 881 conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match, 882 int numeric) 883 { 884 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 885 struct xt_conntrack_mtinfo3 up; 886 887 cinfo_transform(&up, info); 888 conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false); 889 } 890 891 static void 892 conntrack2_mt_print(const void *ip, const struct xt_entry_match *match, 893 int numeric) 894 { 895 conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false); 896 } 897 898 static void 899 conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match, 900 int numeric) 901 { 902 conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false); 903 } 904 905 static void 906 conntrack3_mt_print(const void *ip, const struct xt_entry_match *match, 907 int numeric) 908 { 909 conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true); 910 } 911 912 static void 913 conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match, 914 int numeric) 915 { 916 conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true); 917 } 918 919 static void conntrack_save(const void *ip, const struct xt_entry_match *match) 920 { 921 matchinfo_print(ip, match, 1, "--"); 922 } 923 924 static void conntrack3_mt_save(const void *ip, 925 const struct xt_entry_match *match) 926 { 927 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true); 928 } 929 930 static void conntrack3_mt6_save(const void *ip, 931 const struct xt_entry_match *match) 932 { 933 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true); 934 } 935 936 static void conntrack2_mt_save(const void *ip, 937 const struct xt_entry_match *match) 938 { 939 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false); 940 } 941 942 static void conntrack2_mt6_save(const void *ip, 943 const struct xt_entry_match *match) 944 { 945 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false); 946 } 947 948 static void 949 conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match) 950 { 951 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 952 struct xt_conntrack_mtinfo3 up; 953 954 cinfo_transform(&up, info); 955 conntrack_dump(&up, "--", NFPROTO_IPV4, true, false); 956 } 957 958 static void 959 conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match) 960 { 961 const struct xt_conntrack_mtinfo1 *info = (void *)match->data; 962 struct xt_conntrack_mtinfo3 up; 963 964 cinfo_transform(&up, info); 965 conntrack_dump(&up, "--", NFPROTO_IPV6, true, false); 966 } 967 968 static struct xtables_match conntrack_mt_reg[] = { 969 { 970 .version = XTABLES_VERSION, 971 .name = "conntrack", 972 .revision = 0, 973 .family = NFPROTO_IPV4, 974 .size = XT_ALIGN(sizeof(struct xt_conntrack_info)), 975 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)), 976 .help = conntrack_mt_help, 977 .x6_parse = conntrack_parse, 978 .x6_fcheck = conntrack_mt_check, 979 .print = conntrack_print, 980 .save = conntrack_save, 981 .x6_options = conntrack_mt_opts_v0, 982 }, 983 { 984 .version = XTABLES_VERSION, 985 .name = "conntrack", 986 .revision = 1, 987 .family = NFPROTO_IPV4, 988 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 989 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 990 .help = conntrack_mt_help, 991 .x6_parse = conntrack1_mt_parse, 992 .x6_fcheck = conntrack_mt_check, 993 .print = conntrack1_mt4_print, 994 .save = conntrack1_mt4_save, 995 .x6_options = conntrack_mt_opts, 996 }, 997 { 998 .version = XTABLES_VERSION, 999 .name = "conntrack", 1000 .revision = 1, 1001 .family = NFPROTO_IPV6, 1002 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 1003 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), 1004 .help = conntrack_mt_help, 1005 .x6_parse = conntrack1_mt_parse, 1006 .x6_fcheck = conntrack_mt_check, 1007 .print = conntrack1_mt6_print, 1008 .save = conntrack1_mt6_save, 1009 .x6_options = conntrack_mt_opts, 1010 }, 1011 { 1012 .version = XTABLES_VERSION, 1013 .name = "conntrack", 1014 .revision = 2, 1015 .family = NFPROTO_IPV4, 1016 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1017 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1018 .help = conntrack_mt_help, 1019 .x6_parse = conntrack2_mt_parse, 1020 .x6_fcheck = conntrack_mt_check, 1021 .print = conntrack2_mt_print, 1022 .save = conntrack2_mt_save, 1023 .x6_options = conntrack_mt_opts, 1024 }, 1025 { 1026 .version = XTABLES_VERSION, 1027 .name = "conntrack", 1028 .revision = 2, 1029 .family = NFPROTO_IPV6, 1030 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1031 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), 1032 .help = conntrack_mt_help, 1033 .x6_parse = conntrack2_mt_parse, 1034 .x6_fcheck = conntrack_mt_check, 1035 .print = conntrack2_mt6_print, 1036 .save = conntrack2_mt6_save, 1037 .x6_options = conntrack_mt_opts, 1038 }, 1039 { 1040 .version = XTABLES_VERSION, 1041 .name = "conntrack", 1042 .revision = 3, 1043 .family = NFPROTO_IPV4, 1044 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)), 1045 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)), 1046 .help = conntrack_mt_help, 1047 .x6_parse = conntrack3_mt_parse, 1048 .x6_fcheck = conntrack_mt_check, 1049 .print = conntrack3_mt_print, 1050 .save = conntrack3_mt_save, 1051 .x6_options = conntrack_mt_opts, 1052 }, 1053 { 1054 .version = XTABLES_VERSION, 1055 .name = "conntrack", 1056 .revision = 3, 1057 .family = NFPROTO_IPV6, 1058 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)), 1059 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)), 1060 .help = conntrack_mt_help, 1061 .x6_parse = conntrack3_mt_parse, 1062 .x6_fcheck = conntrack_mt_check, 1063 .print = conntrack3_mt6_print, 1064 .save = conntrack3_mt6_save, 1065 .x6_options = conntrack_mt_opts, 1066 }, 1067 }; 1068 1069 void _init(void) 1070 { 1071 xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 1072 } 1073