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