Fork for IVI and add .changes file
[profile/ivi/iptables.git] / extensions / libip6t_ipv6header.c
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
7 #include <getopt.h>
8 #include <xtables.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <netdb.h>
14 #include <sys/types.h>
15
16 #include <linux/netfilter_ipv6/ip6t_ipv6header.h>
17
18 /* This maybe required 
19 #include <linux/in.h>
20 #include <linux/in6.h>
21 */
22
23
24 /* A few hardcoded protocols for 'all' and in case the user has no
25  *    /etc/protocols */
26 struct pprot {
27         char *name;
28         u_int8_t num;
29 };
30
31 struct numflag {
32         u_int8_t proto;
33         u_int8_t flag;
34 };
35
36 static const struct pprot chain_protos[] = {
37         { "hop-by-hop", IPPROTO_HOPOPTS },
38         { "protocol", IPPROTO_RAW },
39         { "hop", IPPROTO_HOPOPTS },
40         { "dst", IPPROTO_DSTOPTS },
41         { "route", IPPROTO_ROUTING },
42         { "frag", IPPROTO_FRAGMENT },
43         { "auth", IPPROTO_AH },
44         { "esp", IPPROTO_ESP },
45         { "none", IPPROTO_NONE },
46         { "prot", IPPROTO_RAW },
47         { "0", IPPROTO_HOPOPTS },
48         { "60", IPPROTO_DSTOPTS },
49         { "43", IPPROTO_ROUTING },
50         { "44", IPPROTO_FRAGMENT },
51         { "51", IPPROTO_AH },
52         { "50", IPPROTO_ESP },
53         { "59", IPPROTO_NONE },
54         { "255", IPPROTO_RAW },
55         /* { "all", 0 }, */
56 };
57
58 static const struct numflag chain_flags[] = {
59         { IPPROTO_HOPOPTS, MASK_HOPOPTS },
60         { IPPROTO_DSTOPTS, MASK_DSTOPTS },
61         { IPPROTO_ROUTING, MASK_ROUTING },
62         { IPPROTO_FRAGMENT, MASK_FRAGMENT },
63         { IPPROTO_AH, MASK_AH },
64         { IPPROTO_ESP, MASK_ESP },
65         { IPPROTO_NONE, MASK_NONE },
66         { IPPROTO_RAW, MASK_PROTO },
67 };
68
69 static char *
70 proto_to_name(u_int8_t proto, int nolookup)
71 {
72         unsigned int i;
73
74         if (proto && !nolookup) {
75                 struct protoent *pent = getprotobynumber(proto);
76                 if (pent)
77                         return pent->p_name;
78         }
79
80         for (i = 0; i < ARRAY_SIZE(chain_protos); ++i)
81                 if (chain_protos[i].num == proto)
82                         return chain_protos[i].name;
83
84         return NULL;
85 }
86
87 static u_int16_t
88 name_to_proto(const char *s)
89 {
90         unsigned int proto=0;
91         struct protoent *pent;
92
93         if ((pent = getprotobyname(s)))
94                 proto = pent->p_proto;
95         else {
96                 unsigned int i;
97                 for (i = 0; i < ARRAY_SIZE(chain_protos); ++i)
98                         if (strcmp(s, chain_protos[i].name) == 0) {
99                                 proto = chain_protos[i].num;
100                                 break;
101                         }
102
103                 if (i == ARRAY_SIZE(chain_protos))
104                         xtables_error(PARAMETER_PROBLEM,
105                                 "unknown header `%s' specified",
106                                 s);
107         }
108
109         return proto;
110 }
111
112 static unsigned int 
113 add_proto_to_mask(int proto){
114         unsigned int i=0, flag=0;
115
116         for (i = 0; i < ARRAY_SIZE(chain_flags); ++i)
117                         if (proto == chain_flags[i].proto){
118                                 flag = chain_flags[i].flag;
119                                 break;
120                         }
121
122         if (i == ARRAY_SIZE(chain_flags))
123                 xtables_error(PARAMETER_PROBLEM,
124                 "unknown header `%d' specified",
125                 proto);
126         
127         return flag;
128 }       
129
130 static void ipv6header_help(void)
131 {
132         printf(
133 "ipv6header match options:\n"
134 "[!] --header headers     Type of header to match, by name\n"
135 "                         names: hop,dst,route,frag,auth,esp,none,proto\n"
136 "                    long names: hop-by-hop,ipv6-opts,ipv6-route,\n"
137 "                                ipv6-frag,ah,esp,ipv6-nonxt,protocol\n"
138 "                       numbers: 0,60,43,44,51,50,59\n"
139 "--soft                    The header CONTAINS the specified extensions\n");
140 }
141
142 static const struct option ipv6header_opts[] = {
143         { "header", 1, NULL, '1' },
144         { "soft", 0, NULL, '2' },
145         { .name = NULL }
146 };
147
148 static void ipv6header_init(struct xt_entry_match *m)
149 {
150         struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)m->data;
151
152         info->matchflags = 0x00;
153         info->invflags = 0x00;
154         info->modeflag = 0x00;
155 }
156
157 static unsigned int
158 parse_header(const char *flags) {
159         unsigned int ret = 0;
160         char *ptr;
161         char *buffer;
162
163         buffer = strdup(flags);
164
165         for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) 
166                 ret |= add_proto_to_mask(name_to_proto(ptr));
167                 
168         free(buffer);
169         return ret;
170 }
171
172 #define IPV6_HDR_HEADER 0x01
173 #define IPV6_HDR_SOFT   0x02
174
175 static int
176 ipv6header_parse(int c, char **argv, int invert, unsigned int *flags,
177                  const void *entry, struct xt_entry_match **match)
178 {
179         struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)(*match)->data;
180
181         switch (c) {
182                 case '1' : 
183                         /* Parse the provided header names */
184                         if (*flags & IPV6_HDR_HEADER)
185                                 xtables_error(PARAMETER_PROBLEM,
186                                         "Only one `--header' allowed");
187
188                         xtables_check_inverse(optarg, &invert, &optind, 0, argv);
189
190                         if (! (info->matchflags = parse_header(optarg)) )
191                                 xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names");
192
193                         if (invert) 
194                                 info->invflags |= 0xFF;
195                         *flags |= IPV6_HDR_HEADER;
196                         break;
197                 case '2' : 
198                         /* Soft-mode requested? */
199                         if (*flags & IPV6_HDR_SOFT)
200                                 xtables_error(PARAMETER_PROBLEM,
201                                         "Only one `--soft' allowed");
202
203                         info->modeflag |= 0xFF;
204                         *flags |= IPV6_HDR_SOFT;
205                         break;
206                 default:
207                         return 0;
208         }
209
210         return 1;
211 }
212
213 static void ipv6header_check(unsigned int flags)
214 {
215         if (!flags) xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: no options specified");
216 }
217
218 static void
219 print_header(u_int8_t flags){
220         int have_flag = 0;
221
222         while (flags) {
223                 unsigned int i;
224
225                 for (i = 0; (flags & chain_flags[i].flag) == 0; i++);
226
227                 if (have_flag)
228                         printf(",");
229
230                 printf("%s", proto_to_name(chain_flags[i].proto,0));
231                 have_flag = 1;
232
233                 flags &= ~chain_flags[i].flag;
234         }
235
236         if (!have_flag)
237                 printf("NONE");
238 }
239
240 static void ipv6header_print(const void *ip,
241                              const struct xt_entry_match *match, int numeric)
242 {
243         const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
244         printf("ipv6header ");
245
246         if (info->matchflags || info->invflags) {
247                 printf("flags:%s", info->invflags ? "!" : "");
248                 if (numeric)
249                         printf("0x%02X ", info->matchflags);
250                 else {
251                         print_header(info->matchflags);
252                         printf(" ");
253                 }
254         }
255
256         if (info->modeflag)
257                 printf("soft ");
258 }
259
260 static void ipv6header_save(const void *ip, const struct xt_entry_match *match)
261 {
262
263         const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
264
265         printf("%s--header ", info->invflags ? "! " : "");
266         print_header(info->matchflags);
267         printf(" ");
268         if (info->modeflag)
269                 printf("--soft ");
270 }
271
272 static struct xtables_match ipv6header_mt6_reg = {
273         .name           = "ipv6header",
274         .version        = XTABLES_VERSION,
275         .family         = NFPROTO_IPV6,
276         .size           = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)),
277         .userspacesize  = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)),
278         .help           = ipv6header_help,
279         .init           = ipv6header_init,
280         .parse          = ipv6header_parse,
281         .final_check    = ipv6header_check,
282         .print          = ipv6header_print,
283         .save           = ipv6header_save,
284         .extra_opts     = ipv6header_opts,
285 };
286
287 void _init(void)
288 {
289         xtables_register_match(&ipv6header_mt6_reg);
290 }