1 /* 2 * Copyright (c) 2010-2013 Patrick McHardy <kaber (at) trash.net> 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <xtables.h> 8 #include <linux/netfilter/nf_conntrack_common.h> 9 #include <linux/netfilter/xt_CT.h> 10 11 static void ct_help(void) 12 { 13 printf( 14 "CT target options:\n" 15 " --notrack Don't track connection\n" 16 " --helper name Use conntrack helper 'name' for connection\n" 17 " --ctevents event[,event...] Generate specified conntrack events for connection\n" 18 " --expevents event[,event...] Generate specified expectation events for connection\n" 19 " --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n" 20 " --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n" 21 " --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n" 22 ); 23 } 24 25 static void ct_help_v1(void) 26 { 27 printf( 28 "CT target options:\n" 29 " --notrack Don't track connection\n" 30 " --helper name Use conntrack helper 'name' for connection\n" 31 " --timeout name Use timeout policy 'name' for connection\n" 32 " --ctevents event[,event...] Generate specified conntrack events for connection\n" 33 " --expevents event[,event...] Generate specified expectation events for connection\n" 34 " --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n" 35 " --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n" 36 " --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n" 37 ); 38 } 39 40 enum { 41 O_NOTRACK = 0, 42 O_HELPER, 43 O_TIMEOUT, 44 O_CTEVENTS, 45 O_EXPEVENTS, 46 O_ZONE, 47 O_ZONE_ORIG, 48 O_ZONE_REPLY, 49 }; 50 51 #define s struct xt_ct_target_info 52 static const struct xt_option_entry ct_opts[] = { 53 {.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE}, 54 {.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING, 55 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)}, 56 {.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING}, 57 {.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING}, 58 {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING}, 59 {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING}, 60 {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING}, 61 XTOPT_TABLEEND, 62 }; 63 #undef s 64 65 #define s struct xt_ct_target_info_v1 66 static const struct xt_option_entry ct_opts_v1[] = { 67 {.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE}, 68 {.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING, 69 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)}, 70 {.name = "timeout", .id = O_TIMEOUT, .type = XTTYPE_STRING, 71 .flags = XTOPT_PUT, XTOPT_POINTER(s, timeout)}, 72 {.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING}, 73 {.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING}, 74 {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING}, 75 {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING}, 76 {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING}, 77 XTOPT_TABLEEND, 78 }; 79 #undef s 80 81 struct event_tbl { 82 const char *name; 83 unsigned int event; 84 }; 85 86 static const struct event_tbl ct_event_tbl[] = { 87 { "new", IPCT_NEW }, 88 { "related", IPCT_RELATED }, 89 { "destroy", IPCT_DESTROY }, 90 { "reply", IPCT_REPLY }, 91 { "assured", IPCT_ASSURED }, 92 { "protoinfo", IPCT_PROTOINFO }, 93 { "helper", IPCT_HELPER }, 94 { "mark", IPCT_MARK }, 95 { "natseqinfo", IPCT_NATSEQADJ }, 96 { "secmark", IPCT_SECMARK }, 97 }; 98 99 static const struct event_tbl exp_event_tbl[] = { 100 { "new", IPEXP_NEW }, 101 }; 102 103 static void ct_parse_zone_id(const char *opt, unsigned int opt_id, 104 uint16_t *zone_id, uint16_t *flags) 105 { 106 if (opt_id == O_ZONE_ORIG) 107 *flags |= XT_CT_ZONE_DIR_ORIG; 108 if (opt_id == O_ZONE_REPLY) 109 *flags |= XT_CT_ZONE_DIR_REPL; 110 111 *zone_id = 0; 112 113 if (strcasecmp(opt, "mark") == 0) { 114 *flags |= XT_CT_ZONE_MARK; 115 } else { 116 uintmax_t val; 117 118 if (!xtables_strtoul(opt, NULL, &val, 0, UINT16_MAX)) 119 xtables_error(PARAMETER_PROBLEM, 120 "Cannot parse %s as a zone ID\n", opt); 121 122 *zone_id = (uint16_t)val; 123 } 124 } 125 126 static void ct_print_zone_id(const char *pfx, uint16_t zone_id, uint16_t flags) 127 { 128 printf(" %s", pfx); 129 130 if ((flags & (XT_CT_ZONE_DIR_ORIG | 131 XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_ORIG) 132 printf("-orig"); 133 if ((flags & (XT_CT_ZONE_DIR_ORIG | 134 XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_REPL) 135 printf("-reply"); 136 if (flags & XT_CT_ZONE_MARK) 137 printf(" mark"); 138 else 139 printf(" %u", zone_id); 140 } 141 142 static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size, 143 const char *events) 144 { 145 char str[strlen(events) + 1], *e = str, *t; 146 unsigned int mask = 0, i; 147 148 strcpy(str, events); 149 while ((t = strsep(&e, ","))) { 150 for (i = 0; i < size; i++) { 151 if (strcmp(t, tbl[i].name)) 152 continue; 153 mask |= 1 << tbl[i].event; 154 break; 155 } 156 157 if (i == size) 158 xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t); 159 } 160 161 return mask; 162 } 163 164 static void ct_print_events(const char *pfx, const struct event_tbl *tbl, 165 unsigned int size, uint32_t mask) 166 { 167 const char *sep = ""; 168 unsigned int i; 169 170 printf(" %s ", pfx); 171 for (i = 0; i < size; i++) { 172 if (mask & (1 << tbl[i].event)) { 173 printf("%s%s", sep, tbl[i].name); 174 sep = ","; 175 } 176 } 177 } 178 179 static void ct_parse(struct xt_option_call *cb) 180 { 181 struct xt_ct_target_info *info = cb->data; 182 183 xtables_option_parse(cb); 184 switch (cb->entry->id) { 185 case O_NOTRACK: 186 info->flags |= XT_CT_NOTRACK; 187 break; 188 case O_ZONE_ORIG: 189 case O_ZONE_REPLY: 190 case O_ZONE: 191 ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone, 192 &info->flags); 193 break; 194 case O_CTEVENTS: 195 info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), cb->arg); 196 break; 197 case O_EXPEVENTS: 198 info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), cb->arg); 199 break; 200 } 201 } 202 203 static void ct_parse_v1(struct xt_option_call *cb) 204 { 205 struct xt_ct_target_info_v1 *info = cb->data; 206 207 xtables_option_parse(cb); 208 switch (cb->entry->id) { 209 case O_NOTRACK: 210 info->flags |= XT_CT_NOTRACK; 211 break; 212 case O_ZONE_ORIG: 213 case O_ZONE_REPLY: 214 case O_ZONE: 215 ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone, 216 &info->flags); 217 break; 218 case O_CTEVENTS: 219 info->ct_events = ct_parse_events(ct_event_tbl, 220 ARRAY_SIZE(ct_event_tbl), 221 cb->arg); 222 break; 223 case O_EXPEVENTS: 224 info->exp_events = ct_parse_events(exp_event_tbl, 225 ARRAY_SIZE(exp_event_tbl), 226 cb->arg); 227 break; 228 } 229 } 230 231 static void ct_print(const void *ip, const struct xt_entry_target *target, int numeric) 232 { 233 const struct xt_ct_target_info *info = 234 (const struct xt_ct_target_info *)target->data; 235 236 printf(" CT"); 237 if (info->flags & XT_CT_NOTRACK) 238 printf(" notrack"); 239 if (info->helper[0]) 240 printf(" helper %s", info->helper); 241 if (info->ct_events) 242 ct_print_events("ctevents", ct_event_tbl, 243 ARRAY_SIZE(ct_event_tbl), info->ct_events); 244 if (info->exp_events) 245 ct_print_events("expevents", exp_event_tbl, 246 ARRAY_SIZE(exp_event_tbl), info->exp_events); 247 if (info->flags & XT_CT_ZONE_MARK || info->zone) 248 ct_print_zone_id("zone", info->zone, info->flags); 249 } 250 251 static void 252 ct_print_v1(const void *ip, const struct xt_entry_target *target, int numeric) 253 { 254 const struct xt_ct_target_info_v1 *info = 255 (const struct xt_ct_target_info_v1 *)target->data; 256 257 if (info->flags & XT_CT_NOTRACK_ALIAS) { 258 printf (" NOTRACK"); 259 return; 260 } 261 printf(" CT"); 262 if (info->flags & XT_CT_NOTRACK) 263 printf(" notrack"); 264 if (info->helper[0]) 265 printf(" helper %s", info->helper); 266 if (info->timeout[0]) 267 printf(" timeout %s", info->timeout); 268 if (info->ct_events) 269 ct_print_events("ctevents", ct_event_tbl, 270 ARRAY_SIZE(ct_event_tbl), info->ct_events); 271 if (info->exp_events) 272 ct_print_events("expevents", exp_event_tbl, 273 ARRAY_SIZE(exp_event_tbl), info->exp_events); 274 if (info->flags & XT_CT_ZONE_MARK || info->zone) 275 ct_print_zone_id("zone", info->zone, info->flags); 276 } 277 278 static void ct_save(const void *ip, const struct xt_entry_target *target) 279 { 280 const struct xt_ct_target_info *info = 281 (const struct xt_ct_target_info *)target->data; 282 283 if (info->flags & XT_CT_NOTRACK_ALIAS) 284 return; 285 if (info->flags & XT_CT_NOTRACK) 286 printf(" --notrack"); 287 if (info->helper[0]) 288 printf(" --helper %s", info->helper); 289 if (info->ct_events) 290 ct_print_events("--ctevents", ct_event_tbl, 291 ARRAY_SIZE(ct_event_tbl), info->ct_events); 292 if (info->exp_events) 293 ct_print_events("--expevents", exp_event_tbl, 294 ARRAY_SIZE(exp_event_tbl), info->exp_events); 295 if (info->flags & XT_CT_ZONE_MARK || info->zone) 296 ct_print_zone_id("--zone", info->zone, info->flags); 297 } 298 299 static void ct_save_v1(const void *ip, const struct xt_entry_target *target) 300 { 301 const struct xt_ct_target_info_v1 *info = 302 (const struct xt_ct_target_info_v1 *)target->data; 303 304 if (info->flags & XT_CT_NOTRACK_ALIAS) 305 return; 306 if (info->flags & XT_CT_NOTRACK) 307 printf(" --notrack"); 308 if (info->helper[0]) 309 printf(" --helper %s", info->helper); 310 if (info->timeout[0]) 311 printf(" --timeout %s", info->timeout); 312 if (info->ct_events) 313 ct_print_events("--ctevents", ct_event_tbl, 314 ARRAY_SIZE(ct_event_tbl), info->ct_events); 315 if (info->exp_events) 316 ct_print_events("--expevents", exp_event_tbl, 317 ARRAY_SIZE(exp_event_tbl), info->exp_events); 318 if (info->flags & XT_CT_ZONE_MARK || info->zone) 319 ct_print_zone_id("--zone", info->zone, info->flags); 320 } 321 322 static const char * 323 ct_print_name_alias(const struct xt_entry_target *target) 324 { 325 struct xt_ct_target_info *info = (void *)target->data; 326 327 return info->flags & XT_CT_NOTRACK_ALIAS ? "NOTRACK" : "CT"; 328 } 329 330 static void notrack_ct0_tg_init(struct xt_entry_target *target) 331 { 332 struct xt_ct_target_info *info = (void *)target->data; 333 334 info->flags = XT_CT_NOTRACK; 335 } 336 337 static void notrack_ct1_tg_init(struct xt_entry_target *target) 338 { 339 struct xt_ct_target_info_v1 *info = (void *)target->data; 340 341 info->flags = XT_CT_NOTRACK; 342 } 343 344 static void notrack_ct2_tg_init(struct xt_entry_target *target) 345 { 346 struct xt_ct_target_info_v1 *info = (void *)target->data; 347 348 info->flags = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS; 349 } 350 351 static struct xtables_target ct_target_reg[] = { 352 { 353 .family = NFPROTO_UNSPEC, 354 .name = "CT", 355 .version = XTABLES_VERSION, 356 .size = XT_ALIGN(sizeof(struct xt_ct_target_info)), 357 .userspacesize = offsetof(struct xt_ct_target_info, ct), 358 .help = ct_help, 359 .print = ct_print, 360 .save = ct_save, 361 .x6_parse = ct_parse, 362 .x6_options = ct_opts, 363 }, 364 { 365 .family = NFPROTO_UNSPEC, 366 .name = "CT", 367 .revision = 1, 368 .version = XTABLES_VERSION, 369 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), 370 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), 371 .help = ct_help_v1, 372 .print = ct_print_v1, 373 .save = ct_save_v1, 374 .x6_parse = ct_parse_v1, 375 .x6_options = ct_opts_v1, 376 }, 377 { 378 .family = NFPROTO_UNSPEC, 379 .name = "CT", 380 .revision = 2, 381 .version = XTABLES_VERSION, 382 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), 383 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), 384 .help = ct_help_v1, 385 .print = ct_print_v1, 386 .save = ct_save_v1, 387 .alias = ct_print_name_alias, 388 .x6_parse = ct_parse_v1, 389 .x6_options = ct_opts_v1, 390 }, 391 { 392 .family = NFPROTO_UNSPEC, 393 .name = "NOTRACK", 394 .real_name = "CT", 395 .revision = 0, 396 .version = XTABLES_VERSION, 397 .size = XT_ALIGN(sizeof(struct xt_ct_target_info)), 398 .userspacesize = offsetof(struct xt_ct_target_info, ct), 399 .init = notrack_ct0_tg_init, 400 }, 401 { 402 .family = NFPROTO_UNSPEC, 403 .name = "NOTRACK", 404 .real_name = "CT", 405 .revision = 1, 406 .version = XTABLES_VERSION, 407 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), 408 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), 409 .init = notrack_ct1_tg_init, 410 }, 411 { 412 .family = NFPROTO_UNSPEC, 413 .name = "NOTRACK", 414 .real_name = "CT", 415 .revision = 2, 416 .ext_flags = XTABLES_EXT_ALIAS, 417 .version = XTABLES_VERSION, 418 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), 419 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), 420 .init = notrack_ct2_tg_init, 421 }, 422 { 423 .family = NFPROTO_UNSPEC, 424 .name = "NOTRACK", 425 .revision = 0, 426 .version = XTABLES_VERSION, 427 }, 428 }; 429 430 void _init(void) 431 { 432 xtables_register_targets(ct_target_reg, ARRAY_SIZE(ct_target_reg)); 433 } 434