libip6t_ipv6header.c revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
1/* ipv6header match - matches IPv6 packets based
2on 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
14enum {
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 */
21struct pprot {
22	char *name;
23	uint8_t num;
24};
25
26struct numflag {
27	uint8_t proto;
28	uint8_t flag;
29};
30
31static 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
53static 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
64static const char *
65proto_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
82static uint16_t
83name_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
107static unsigned int
108add_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
125static 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
137static 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
144static unsigned int
145parse_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
159static 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
177static void
178print_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
199static 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
218static 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
229static 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
242void _init(void)
243{
244	xtables_register_match(&ipv6header_mt6_reg);
245}
246