Home | History | Annotate | Download | only in extensions
      1 /* Shared library add-on to iptables to add TCP support. */
      2 #include <stdio.h>
      3 #include <netdb.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include <getopt.h>
      7 #include <ip6tables.h>
      8 #include <linux/netfilter_ipv6/ip6_tables.h>
      9 
     10 /* Function which prints out usage message. */
     11 static void
     12 help(void)
     13 {
     14 	printf(
     15 "TCP v%s options:\n"
     16 " --tcp-flags [!] mask comp	match when TCP flags & mask == comp\n"
     17 "				(Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
     18 "[!] --syn			match when only SYN flag set\n"
     19 "				(equivalent to --tcp-flags SYN,RST,ACK SYN)\n"
     20 " --source-port [!] port[:port]\n"
     21 " --sport ...\n"
     22 "				match source port(s)\n"
     23 " --destination-port [!] port[:port]\n"
     24 " --dport ...\n"
     25 "				match destination port(s)\n"
     26 " --tcp-option [!] number       match if TCP option set\n\n",
     27 IPTABLES_VERSION);
     28 }
     29 
     30 static struct option opts[] = {
     31 	{ "source-port", 1, 0, '1' },
     32 	{ "sport", 1, 0, '1' }, /* synonym */
     33 	{ "destination-port", 1, 0, '2' },
     34 	{ "dport", 1, 0, '2' }, /* synonym */
     35 	{ "syn", 0, 0, '3' },
     36 	{ "tcp-flags", 1, 0, '4' },
     37 	{ "tcp-option", 1, 0, '5' },
     38 	{0}
     39 };
     40 
     41 static void
     42 parse_tcp_ports(const char *portstring, u_int16_t *ports)
     43 {
     44 	char *buffer;
     45 	char *cp;
     46 
     47 	buffer = strdup(portstring);
     48 	if ((cp = strchr(buffer, ':')) == NULL)
     49 		ports[0] = ports[1] = parse_port(buffer, "tcp");
     50 	else {
     51 		*cp = '\0';
     52 		cp++;
     53 
     54 		ports[0] = buffer[0] ? parse_port(buffer, "tcp") : 0;
     55 		ports[1] = cp[0] ? parse_port(cp, "tcp") : 0xFFFF;
     56 
     57 		if (ports[0] > ports[1])
     58 			exit_error(PARAMETER_PROBLEM,
     59 				   "invalid portrange (min > max)");
     60 	}
     61 	free(buffer);
     62 }
     63 
     64 struct tcp_flag_names {
     65 	const char *name;
     66 	unsigned int flag;
     67 };
     68 
     69 static struct tcp_flag_names tcp_flag_names[]
     70 = { { "FIN", 0x01 },
     71     { "SYN", 0x02 },
     72     { "RST", 0x04 },
     73     { "PSH", 0x08 },
     74     { "ACK", 0x10 },
     75     { "URG", 0x20 },
     76     { "ALL", 0x3F },
     77     { "NONE", 0 },
     78 };
     79 
     80 static unsigned int
     81 parse_tcp_flag(const char *flags)
     82 {
     83 	unsigned int ret = 0;
     84 	char *ptr;
     85 	char *buffer;
     86 
     87 	buffer = strdup(flags);
     88 
     89 	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
     90 		unsigned int i;
     91 		for (i = 0;
     92 		     i < sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names);
     93 		     i++) {
     94 			if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
     95 				ret |= tcp_flag_names[i].flag;
     96 				break;
     97 			}
     98 		}
     99 		if (i == sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names))
    100 			exit_error(PARAMETER_PROBLEM,
    101 				   "Unknown TCP flag `%s'", ptr);
    102 		}
    103 
    104 	free(buffer);
    105 	return ret;
    106 }
    107 
    108 static void
    109 parse_tcp_flags(struct ip6t_tcp *tcpinfo,
    110 		const char *mask,
    111 		const char *cmp,
    112 		int invert)
    113 {
    114 	tcpinfo->flg_mask = parse_tcp_flag(mask);
    115 	tcpinfo->flg_cmp = parse_tcp_flag(cmp);
    116 
    117 	if (invert)
    118 		tcpinfo->invflags |= IP6T_TCP_INV_FLAGS;
    119 }
    120 
    121 static void
    122 parse_tcp_option(const char *option, u_int8_t *result)
    123 {
    124 	unsigned int ret;
    125 
    126 	if (string_to_number(option, 1, 255, &ret) == -1)
    127 		exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
    128 
    129 	*result = (u_int8_t)ret;
    130 }
    131 
    132 /* Initialize the match. */
    133 static void
    134 init(struct ip6t_entry_match *m, unsigned int *nfcache)
    135 {
    136 	struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)m->data;
    137 
    138 	tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
    139 }
    140 
    141 #define TCP_SRC_PORTS 0x01
    142 #define TCP_DST_PORTS 0x02
    143 #define TCP_FLAGS 0x04
    144 #define TCP_OPTION	0x08
    145 
    146 /* Function which parses command options; returns true if it
    147    ate an option. */
    148 static int
    149 parse(int c, char **argv, int invert, unsigned int *flags,
    150       const struct ip6t_entry *entry,
    151       unsigned int *nfcache,
    152       struct ip6t_entry_match **match)
    153 {
    154 	struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)(*match)->data;
    155 
    156 	switch (c) {
    157 	case '1':
    158 		if (*flags & TCP_SRC_PORTS)
    159 			exit_error(PARAMETER_PROBLEM,
    160 				   "Only one `--source-port' allowed");
    161 		check_inverse(optarg, &invert, &optind, 0);
    162 		parse_tcp_ports(argv[optind-1], tcpinfo->spts);
    163 		if (invert)
    164 			tcpinfo->invflags |= IP6T_TCP_INV_SRCPT;
    165 		*flags |= TCP_SRC_PORTS;
    166 		break;
    167 
    168 	case '2':
    169 		if (*flags & TCP_DST_PORTS)
    170 			exit_error(PARAMETER_PROBLEM,
    171 				   "Only one `--destination-port' allowed");
    172 		check_inverse(optarg, &invert, &optind, 0);
    173 		parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
    174 		if (invert)
    175 			tcpinfo->invflags |= IP6T_TCP_INV_DSTPT;
    176 		*flags |= TCP_DST_PORTS;
    177 		break;
    178 
    179 	case '3':
    180 		if (*flags & TCP_FLAGS)
    181 			exit_error(PARAMETER_PROBLEM,
    182 				   "Only one of `--syn' or `--tcp-flags' "
    183 				   " allowed");
    184 		parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert);
    185 		*flags |= TCP_FLAGS;
    186 		break;
    187 
    188 	case '4':
    189 		if (*flags & TCP_FLAGS)
    190 			exit_error(PARAMETER_PROBLEM,
    191 				   "Only one of `--syn' or `--tcp-flags' "
    192 				   " allowed");
    193 		check_inverse(optarg, &invert, &optind, 0);
    194 
    195 		if (!argv[optind]
    196 		    || argv[optind][0] == '-' || argv[optind][0] == '!')
    197 			exit_error(PARAMETER_PROBLEM,
    198 				   "--tcp-flags requires two args.");
    199 
    200 		parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind],
    201 				invert);
    202 		optind++;
    203 		*flags |= TCP_FLAGS;
    204 		break;
    205 
    206 	case '5':
    207 		if (*flags & TCP_OPTION)
    208 			exit_error(PARAMETER_PROBLEM,
    209 				   "Only one `--tcp-option' allowed");
    210 		check_inverse(optarg, &invert, &optind, 0);
    211 		parse_tcp_option(argv[optind-1], &tcpinfo->option);
    212 		if (invert)
    213 			tcpinfo->invflags |= IP6T_TCP_INV_OPTION;
    214 		*flags |= TCP_OPTION;
    215 		break;
    216 
    217 	default:
    218 		return 0;
    219 	}
    220 
    221 	return 1;
    222 }
    223 
    224 /* Final check; we don't care. */
    225 static void
    226 final_check(unsigned int flags)
    227 {
    228 }
    229 
    230 static char *
    231 port_to_service(int port)
    232 {
    233 	struct servent *service;
    234 
    235 	if ((service = getservbyport(htons(port), "tcp")))
    236 		return service->s_name;
    237 
    238 	return NULL;
    239 }
    240 
    241 static void
    242 print_port(u_int16_t port, int numeric)
    243 {
    244 	char *service;
    245 
    246 	if (numeric || (service = port_to_service(port)) == NULL)
    247 		printf("%u", port);
    248 	else
    249 		printf("%s", service);
    250 }
    251 
    252 static void
    253 print_ports(const char *name, u_int16_t min, u_int16_t max,
    254 	    int invert, int numeric)
    255 {
    256 	const char *inv = invert ? "!" : "";
    257 
    258 	if (min != 0 || max != 0xFFFF || invert) {
    259 		printf("%s", name);
    260 		if (min == max) {
    261 			printf(":%s", inv);
    262 			print_port(min, numeric);
    263 		} else {
    264 			printf("s:%s", inv);
    265 			print_port(min, numeric);
    266 			printf(":");
    267 			print_port(max, numeric);
    268 		}
    269 		printf(" ");
    270 	}
    271 }
    272 
    273 static void
    274 print_option(u_int8_t option, int invert, int numeric)
    275 {
    276 	if (option || invert)
    277 		printf("option=%s%u ", invert ? "!" : "", option);
    278 }
    279 
    280 static void
    281 print_tcpf(u_int8_t flags)
    282 {
    283 	int have_flag = 0;
    284 
    285 	while (flags) {
    286 		unsigned int i;
    287 
    288 		for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
    289 
    290 		if (have_flag)
    291 			printf(",");
    292 		printf("%s", tcp_flag_names[i].name);
    293 		have_flag = 1;
    294 
    295 		flags &= ~tcp_flag_names[i].flag;
    296 	}
    297 
    298 	if (!have_flag)
    299 		printf("NONE");
    300 }
    301 
    302 static void
    303 print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
    304 {
    305 	if (mask || invert) {
    306 		printf("flags:%s", invert ? "!" : "");
    307 		if (numeric)
    308 			printf("0x%02X/0x%02X ", mask, cmp);
    309 		else {
    310 			print_tcpf(mask);
    311 			printf("/");
    312 			print_tcpf(cmp);
    313 			printf(" ");
    314 		}
    315 	}
    316 }
    317 
    318 /* Prints out the union ipt_matchinfo. */
    319 static void
    320 print(const struct ip6t_ip6 *ip,
    321       const struct ip6t_entry_match *match, int numeric)
    322 {
    323 	const struct ip6t_tcp *tcp = (struct ip6t_tcp *)match->data;
    324 
    325 	printf("tcp ");
    326 	print_ports("spt", tcp->spts[0], tcp->spts[1],
    327 		    tcp->invflags & IP6T_TCP_INV_SRCPT,
    328 		    numeric);
    329 	print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
    330 		    tcp->invflags & IP6T_TCP_INV_DSTPT,
    331 		    numeric);
    332 	print_option(tcp->option,
    333 		     tcp->invflags & IP6T_TCP_INV_OPTION,
    334 		     numeric);
    335 	print_flags(tcp->flg_mask, tcp->flg_cmp,
    336 		    tcp->invflags & IP6T_TCP_INV_FLAGS,
    337 		    numeric);
    338 	if (tcp->invflags & ~IP6T_TCP_INV_MASK)
    339 		printf("Unknown invflags: 0x%X ",
    340 		       tcp->invflags & ~IP6T_TCP_INV_MASK);
    341 }
    342 
    343 /* Saves the union ipt_matchinfo in parsable form to stdout. */
    344 static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
    345 {
    346 	const struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)match->data;
    347 
    348 	if (tcpinfo->spts[0] != 0
    349 	    || tcpinfo->spts[1] != 0xFFFF) {
    350 		if (tcpinfo->invflags & IP6T_TCP_INV_SRCPT)
    351 			printf("! ");
    352 		if (tcpinfo->spts[0]
    353 		    != tcpinfo->spts[1])
    354 			printf("--sport %u:%u ",
    355 			       tcpinfo->spts[0],
    356 			       tcpinfo->spts[1]);
    357 		else
    358 			printf("--sport %u ",
    359 			       tcpinfo->spts[0]);
    360 	}
    361 
    362 	if (tcpinfo->dpts[0] != 0
    363 	    || tcpinfo->dpts[1] != 0xFFFF) {
    364 		if (tcpinfo->invflags & IP6T_TCP_INV_DSTPT)
    365 			printf("! ");
    366 		if (tcpinfo->dpts[0]
    367 		    != tcpinfo->dpts[1])
    368 			printf("--dport %u:%u ",
    369 			       tcpinfo->dpts[0],
    370 			       tcpinfo->dpts[1]);
    371 		else
    372 			printf("--dport %u ",
    373 			       tcpinfo->dpts[0]);
    374 	}
    375 
    376 	if (tcpinfo->option
    377 	    || (tcpinfo->invflags & IP6T_TCP_INV_OPTION)) {
    378 		if (tcpinfo->invflags & IP6T_TCP_INV_OPTION)
    379 			printf("! ");
    380 		printf("--tcp-option %u ", tcpinfo->option);
    381 	}
    382 
    383 	if (tcpinfo->flg_mask
    384 	    || (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)) {
    385 		if (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)
    386 			printf("! ");
    387 
    388 		printf("--tcp-flags ");
    389 		if (tcpinfo->flg_mask != 0xFF) {
    390 			print_tcpf(tcpinfo->flg_mask);
    391 		}
    392 		printf(" ");
    393 		print_tcpf(tcpinfo->flg_cmp);
    394 		printf(" ");
    395 	}
    396 }
    397 
    398 static struct ip6tables_match tcp = {
    399 	.name 		= "tcp",
    400 	.version	= IPTABLES_VERSION,
    401 	.size		= IP6T_ALIGN(sizeof(struct ip6t_tcp)),
    402 	.userspacesize	= IP6T_ALIGN(sizeof(struct ip6t_tcp)),
    403 	.help		= &help,
    404 	.init		= &init,
    405 	.parse		= &parse,
    406 	.final_check	= &final_check,
    407 	.print		= &print,
    408 	.save		= &save,
    409 	.extra_opts	= opts,
    410 };
    411 
    412 void
    413 _init(void)
    414 {
    415 	register_match6(&tcp);
    416 }
    417