Home | History | Annotate | Download | only in extensions
      1 /* ipv6header match - matches IPv6 packets based
      2 on whether they contain certain headers */
      3 
      4 /* Original idea: Brad Chapman
      5  * Rewritten by: Andras Kis-Szabo <kisza (at) sch.bme.hu> */
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <netdb.h>
     11 #include <xtables.h>
     12 #include <linux/netfilter_ipv6/ip6t_ipv6header.h>
     13 
     14 enum {
     15 	O_HEADER = 0,
     16 	O_SOFT,
     17 };
     18 
     19 /* A few hardcoded protocols for 'all' and in case the user has no
     20  *    /etc/protocols */
     21 struct pprot {
     22 	char *name;
     23 	uint8_t num;
     24 };
     25 
     26 struct numflag {
     27 	uint8_t proto;
     28 	uint8_t flag;
     29 };
     30 
     31 static const struct pprot chain_protos[] = {
     32 	{ "hop-by-hop", IPPROTO_HOPOPTS },
     33 	{ "protocol", IPPROTO_RAW },
     34 	{ "hop", IPPROTO_HOPOPTS },
     35 	{ "dst", IPPROTO_DSTOPTS },
     36 	{ "route", IPPROTO_ROUTING },
     37 	{ "frag", IPPROTO_FRAGMENT },
     38 	{ "auth", IPPROTO_AH },
     39 	{ "esp", IPPROTO_ESP },
     40 	{ "none", IPPROTO_NONE },
     41 	{ "prot", IPPROTO_RAW },
     42 	{ "0", IPPROTO_HOPOPTS },
     43 	{ "60", IPPROTO_DSTOPTS },
     44 	{ "43", IPPROTO_ROUTING },
     45 	{ "44", IPPROTO_FRAGMENT },
     46 	{ "51", IPPROTO_AH },
     47 	{ "50", IPPROTO_ESP },
     48 	{ "59", IPPROTO_NONE },
     49 	{ "255", IPPROTO_RAW },
     50 	/* { "all", 0 }, */
     51 };
     52 
     53 static const struct numflag chain_flags[] = {
     54 	{ IPPROTO_HOPOPTS, MASK_HOPOPTS },
     55 	{ IPPROTO_DSTOPTS, MASK_DSTOPTS },
     56 	{ IPPROTO_ROUTING, MASK_ROUTING },
     57 	{ IPPROTO_FRAGMENT, MASK_FRAGMENT },
     58 	{ IPPROTO_AH, MASK_AH },
     59 	{ IPPROTO_ESP, MASK_ESP },
     60 	{ IPPROTO_NONE, MASK_NONE },
     61 	{ IPPROTO_RAW, MASK_PROTO },
     62 };
     63 
     64 static const char *
     65 proto_to_name(uint8_t proto, int nolookup)
     66 {
     67         unsigned int i;
     68 
     69         if (proto && !nolookup) {
     70 		const struct protoent *pent = getprotobynumber(proto);
     71                 if (pent)
     72                         return pent->p_name;
     73         }
     74 
     75         for (i = 0; i < ARRAY_SIZE(chain_protos); ++i)
     76                 if (chain_protos[i].num == proto)
     77                         return chain_protos[i].name;
     78 
     79         return NULL;
     80 }
     81 
     82 static uint16_t
     83 name_to_proto(const char *s)
     84 {
     85         unsigned int proto=0;
     86 	const struct protoent *pent;
     87 
     88         if ((pent = getprotobyname(s)))
     89         	proto = pent->p_proto;
     90         else {
     91         	unsigned int i;
     92         	for (i = 0; i < ARRAY_SIZE(chain_protos); ++i)
     93         		if (strcmp(s, chain_protos[i].name) == 0) {
     94         			proto = chain_protos[i].num;
     95         			break;
     96         		}
     97 
     98 		if (i == ARRAY_SIZE(chain_protos))
     99 			xtables_error(PARAMETER_PROBLEM,
    100         			"unknown header `%s' specified",
    101         			s);
    102         }
    103 
    104         return proto;
    105 }
    106 
    107 static unsigned int
    108 add_proto_to_mask(int proto){
    109 	unsigned int i=0, flag=0;
    110 
    111 	for (i = 0; i < ARRAY_SIZE(chain_flags); ++i)
    112 			if (proto == chain_flags[i].proto){
    113 				flag = chain_flags[i].flag;
    114 				break;
    115 			}
    116 
    117 	if (i == ARRAY_SIZE(chain_flags))
    118 		xtables_error(PARAMETER_PROBLEM,
    119 		"unknown header `%d' specified",
    120 		proto);
    121 
    122 	return flag;
    123 }
    124 
    125 static void ipv6header_help(void)
    126 {
    127 	printf(
    128 "ipv6header match options:\n"
    129 "[!] --header headers     Type of header to match, by name\n"
    130 "                         names: hop,dst,route,frag,auth,esp,none,proto\n"
    131 "                    long names: hop-by-hop,ipv6-opts,ipv6-route,\n"
    132 "                                ipv6-frag,ah,esp,ipv6-nonxt,protocol\n"
    133 "                       numbers: 0,60,43,44,51,50,59\n"
    134 "--soft                    The header CONTAINS the specified extensions\n");
    135 }
    136 
    137 static const struct xt_option_entry ipv6header_opts[] = {
    138 	{.name = "header", .id = O_HEADER, .type = XTTYPE_STRING,
    139 	 .flags = XTOPT_MAND | XTOPT_INVERT},
    140 	{.name = "soft", .id = O_SOFT, .type = XTTYPE_NONE},
    141 	XTOPT_TABLEEND,
    142 };
    143 
    144 static unsigned int
    145 parse_header(const char *flags) {
    146         unsigned int ret = 0;
    147         char *ptr;
    148         char *buffer;
    149 
    150         buffer = strdup(flags);
    151 
    152         for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ","))
    153 		ret |= add_proto_to_mask(name_to_proto(ptr));
    154 
    155         free(buffer);
    156         return ret;
    157 }
    158 
    159 static void ipv6header_parse(struct xt_option_call *cb)
    160 {
    161 	struct ip6t_ipv6header_info *info = cb->data;
    162 
    163 	xtables_option_parse(cb);
    164 	switch (cb->entry->id) {
    165 	case O_HEADER:
    166 		if (!(info->matchflags = parse_header(cb->arg)))
    167 			xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names");
    168 		if (cb->invert)
    169 			info->invflags |= 0xFF;
    170 		break;
    171 	case O_SOFT:
    172 		info->modeflag |= 0xFF;
    173 		break;
    174 	}
    175 }
    176 
    177 static void
    178 print_header(uint8_t flags){
    179         int have_flag = 0;
    180 
    181         while (flags) {
    182                 unsigned int i;
    183 
    184                 for (i = 0; (flags & chain_flags[i].flag) == 0; i++);
    185 
    186                 if (have_flag)
    187                         printf(",");
    188 
    189                 printf("%s", proto_to_name(chain_flags[i].proto,0));
    190                 have_flag = 1;
    191 
    192                 flags &= ~chain_flags[i].flag;
    193         }
    194 
    195         if (!have_flag)
    196                 printf("NONE");
    197 }
    198 
    199 static void ipv6header_print(const void *ip,
    200                              const struct xt_entry_match *match, int numeric)
    201 {
    202 	const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
    203 	printf(" ipv6header");
    204 
    205         if (info->matchflags || info->invflags) {
    206 		printf(" flags:%s", info->invflags ? "!" : "");
    207                 if (numeric)
    208 			printf("0x%02X", info->matchflags);
    209                 else {
    210                         print_header(info->matchflags);
    211                 }
    212         }
    213 
    214 	if (info->modeflag)
    215 		printf(" soft");
    216 }
    217 
    218 static void ipv6header_save(const void *ip, const struct xt_entry_match *match)
    219 {
    220 
    221 	const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
    222 
    223 	printf("%s --header ", info->invflags ? " !" : "");
    224 	print_header(info->matchflags);
    225 	if (info->modeflag)
    226 		printf(" --soft");
    227 }
    228 
    229 static struct xtables_match ipv6header_mt6_reg = {
    230 	.name		= "ipv6header",
    231 	.version	= XTABLES_VERSION,
    232 	.family		= NFPROTO_IPV6,
    233 	.size		= XT_ALIGN(sizeof(struct ip6t_ipv6header_info)),
    234 	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_ipv6header_info)),
    235 	.help		= ipv6header_help,
    236 	.print		= ipv6header_print,
    237 	.save		= ipv6header_save,
    238 	.x6_parse	= ipv6header_parse,
    239 	.x6_options	= ipv6header_opts,
    240 };
    241 
    242 void _init(void)
    243 {
    244 	xtables_register_match(&ipv6header_mt6_reg);
    245 }
    246