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