Home | History | Annotate | Download | only in extensions
      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