]> bbs.cooldavid.org Git - net-next-2.6.git/blob - net/ipv6/netfilter/ip6t_rt.c
netfilter: xtables: substitute temporary defines by final name
[net-next-2.6.git] / net / ipv6 / netfilter / ip6t_rt.c
1 /* Kernel module to match ROUTING parameters. */
2
3 /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/module.h>
11 #include <linux/skbuff.h>
12 #include <linux/ipv6.h>
13 #include <linux/types.h>
14 #include <net/checksum.h>
15 #include <net/ipv6.h>
16
17 #include <asm/byteorder.h>
18
19 #include <linux/netfilter/x_tables.h>
20 #include <linux/netfilter_ipv6/ip6_tables.h>
21 #include <linux/netfilter_ipv6/ip6t_rt.h>
22
23 MODULE_LICENSE("GPL");
24 MODULE_DESCRIPTION("Xtables: IPv6 Routing Header match");
25 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
26
27 /* Returns 1 if the id is matched by the range, 0 otherwise */
28 static inline bool
29 segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert)
30 {
31         bool r;
32         pr_debug("segsleft_match:%c 0x%x <= 0x%x <= 0x%x\n",
33                  invert ? '!' : ' ', min, id, max);
34         r = (id >= min && id <= max) ^ invert;
35         pr_debug(" result %s\n", r ? "PASS" : "FAILED");
36         return r;
37 }
38
39 static bool rt_mt6(const struct sk_buff *skb,
40                    const struct xt_action_param *par)
41 {
42         struct ipv6_rt_hdr _route;
43         const struct ipv6_rt_hdr *rh;
44         const struct ip6t_rt *rtinfo = par->matchinfo;
45         unsigned int temp;
46         unsigned int ptr;
47         unsigned int hdrlen = 0;
48         bool ret = false;
49         struct in6_addr _addr;
50         const struct in6_addr *ap;
51         int err;
52
53         err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL);
54         if (err < 0) {
55                 if (err != -ENOENT)
56                         *par->hotdrop = true;
57                 return false;
58         }
59
60         rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route);
61         if (rh == NULL) {
62                 *par->hotdrop = true;
63                 return false;
64         }
65
66         hdrlen = ipv6_optlen(rh);
67         if (skb->len - ptr < hdrlen) {
68                 /* Pcket smaller than its length field */
69                 return false;
70         }
71
72         pr_debug("IPv6 RT LEN %u %u ", hdrlen, rh->hdrlen);
73         pr_debug("TYPE %04X ", rh->type);
74         pr_debug("SGS_LEFT %u %02X\n", rh->segments_left, rh->segments_left);
75
76         pr_debug("IPv6 RT segsleft %02X ",
77                  segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
78                                 rh->segments_left,
79                                 !!(rtinfo->invflags & IP6T_RT_INV_SGS)));
80         pr_debug("type %02X %02X %02X ",
81                  rtinfo->rt_type, rh->type,
82                  (!(rtinfo->flags & IP6T_RT_TYP) ||
83                   ((rtinfo->rt_type == rh->type) ^
84                    !!(rtinfo->invflags & IP6T_RT_INV_TYP))));
85         pr_debug("len %02X %04X %02X ",
86                  rtinfo->hdrlen, hdrlen,
87                  !(rtinfo->flags & IP6T_RT_LEN) ||
88                   ((rtinfo->hdrlen == hdrlen) ^
89                    !!(rtinfo->invflags & IP6T_RT_INV_LEN)));
90         pr_debug("res %02X %02X %02X ",
91                  rtinfo->flags & IP6T_RT_RES,
92                  ((const struct rt0_hdr *)rh)->reserved,
93                  !((rtinfo->flags & IP6T_RT_RES) &&
94                    (((const struct rt0_hdr *)rh)->reserved)));
95
96         ret = (rh != NULL) &&
97               (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
98                               rh->segments_left,
99                               !!(rtinfo->invflags & IP6T_RT_INV_SGS))) &&
100               (!(rtinfo->flags & IP6T_RT_LEN) ||
101                ((rtinfo->hdrlen == hdrlen) ^
102                 !!(rtinfo->invflags & IP6T_RT_INV_LEN))) &&
103               (!(rtinfo->flags & IP6T_RT_TYP) ||
104                ((rtinfo->rt_type == rh->type) ^
105                 !!(rtinfo->invflags & IP6T_RT_INV_TYP)));
106
107         if (ret && (rtinfo->flags & IP6T_RT_RES)) {
108                 const u_int32_t *rp;
109                 u_int32_t _reserved;
110                 rp = skb_header_pointer(skb,
111                                         ptr + offsetof(struct rt0_hdr,
112                                                        reserved),
113                                         sizeof(_reserved),
114                                         &_reserved);
115
116                 ret = (*rp == 0);
117         }
118
119         pr_debug("#%d ", rtinfo->addrnr);
120         if (!(rtinfo->flags & IP6T_RT_FST)) {
121                 return ret;
122         } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) {
123                 pr_debug("Not strict ");
124                 if (rtinfo->addrnr > (unsigned int)((hdrlen - 8) / 16)) {
125                         pr_debug("There isn't enough space\n");
126                         return false;
127                 } else {
128                         unsigned int i = 0;
129
130                         pr_debug("#%d ", rtinfo->addrnr);
131                         for (temp = 0;
132                              temp < (unsigned int)((hdrlen - 8) / 16);
133                              temp++) {
134                                 ap = skb_header_pointer(skb,
135                                                         ptr
136                                                         + sizeof(struct rt0_hdr)
137                                                         + temp * sizeof(_addr),
138                                                         sizeof(_addr),
139                                                         &_addr);
140
141                                 BUG_ON(ap == NULL);
142
143                                 if (ipv6_addr_equal(ap, &rtinfo->addrs[i])) {
144                                         pr_debug("i=%d temp=%d;\n", i, temp);
145                                         i++;
146                                 }
147                                 if (i == rtinfo->addrnr)
148                                         break;
149                         }
150                         pr_debug("i=%d #%d\n", i, rtinfo->addrnr);
151                         if (i == rtinfo->addrnr)
152                                 return ret;
153                         else
154                                 return false;
155                 }
156         } else {
157                 pr_debug("Strict ");
158                 if (rtinfo->addrnr > (unsigned int)((hdrlen - 8) / 16)) {
159                         pr_debug("There isn't enough space\n");
160                         return false;
161                 } else {
162                         pr_debug("#%d ", rtinfo->addrnr);
163                         for (temp = 0; temp < rtinfo->addrnr; temp++) {
164                                 ap = skb_header_pointer(skb,
165                                                         ptr
166                                                         + sizeof(struct rt0_hdr)
167                                                         + temp * sizeof(_addr),
168                                                         sizeof(_addr),
169                                                         &_addr);
170                                 BUG_ON(ap == NULL);
171
172                                 if (!ipv6_addr_equal(ap, &rtinfo->addrs[temp]))
173                                         break;
174                         }
175                         pr_debug("temp=%d #%d\n", temp, rtinfo->addrnr);
176                         if (temp == rtinfo->addrnr &&
177                             temp == (unsigned int)((hdrlen - 8) / 16))
178                                 return ret;
179                         else
180                                 return false;
181                 }
182         }
183
184         return false;
185 }
186
187 static int rt_mt6_check(const struct xt_mtchk_param *par)
188 {
189         const struct ip6t_rt *rtinfo = par->matchinfo;
190
191         if (rtinfo->invflags & ~IP6T_RT_INV_MASK) {
192                 pr_debug("unknown flags %X\n", rtinfo->invflags);
193                 return -EINVAL;
194         }
195         if ((rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST_MASK)) &&
196             (!(rtinfo->flags & IP6T_RT_TYP) ||
197              (rtinfo->rt_type != 0) ||
198              (rtinfo->invflags & IP6T_RT_INV_TYP))) {
199                 pr_debug("`--rt-type 0' required before `--rt-0-*'");
200                 return -EINVAL;
201         }
202
203         return 0;
204 }
205
206 static struct xt_match rt_mt6_reg __read_mostly = {
207         .name           = "rt",
208         .family         = NFPROTO_IPV6,
209         .match          = rt_mt6,
210         .matchsize      = sizeof(struct ip6t_rt),
211         .checkentry     = rt_mt6_check,
212         .me             = THIS_MODULE,
213 };
214
215 static int __init rt_mt6_init(void)
216 {
217         return xt_register_match(&rt_mt6_reg);
218 }
219
220 static void __exit rt_mt6_exit(void)
221 {
222         xt_unregister_match(&rt_mt6_reg);
223 }
224
225 module_init(rt_mt6_init);
226 module_exit(rt_mt6_exit);