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