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@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 *
proto_to_name(uint8_t proto,int nolookup)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
name_to_proto(const char * s)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
add_proto_to_mask(int proto)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 
ipv6header_help(void)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
parse_header(const char * flags)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 
ipv6header_parse(struct xt_option_call * cb)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
print_header(uint8_t flags)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 
ipv6header_print(const void * ip,const struct xt_entry_match * match,int numeric)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 
ipv6header_save(const void * ip,const struct xt_entry_match * match)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 
_init(void)242 void _init(void)
243 {
244 	xtables_register_match(&ipv6header_mt6_reg);
245 }
246