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