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