1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Kernel module to match ROUTING parameters. */ |
3 | |
4 | /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu> |
5 | */ |
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
7 | #include <linux/module.h> |
8 | #include <linux/skbuff.h> |
9 | #include <linux/ipv6.h> |
10 | #include <linux/types.h> |
11 | #include <net/checksum.h> |
12 | #include <net/ipv6.h> |
13 | |
14 | #include <asm/byteorder.h> |
15 | |
16 | #include <linux/netfilter/x_tables.h> |
17 | #include <linux/netfilter_ipv6/ip6_tables.h> |
18 | #include <linux/netfilter_ipv6/ip6t_rt.h> |
19 | |
20 | MODULE_LICENSE("GPL" ); |
21 | MODULE_DESCRIPTION("Xtables: IPv6 Routing Header match" ); |
22 | MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>" ); |
23 | |
24 | /* Returns 1 if the id is matched by the range, 0 otherwise */ |
25 | static inline bool |
26 | segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) |
27 | { |
28 | return (id >= min && id <= max) ^ invert; |
29 | } |
30 | |
31 | static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) |
32 | { |
33 | struct ipv6_rt_hdr _route; |
34 | const struct ipv6_rt_hdr *rh; |
35 | const struct ip6t_rt *rtinfo = par->matchinfo; |
36 | unsigned int temp; |
37 | unsigned int ptr = 0; |
38 | unsigned int hdrlen = 0; |
39 | bool ret = false; |
40 | struct in6_addr _addr; |
41 | const struct in6_addr *ap; |
42 | int err; |
43 | |
44 | err = ipv6_find_hdr(skb, offset: &ptr, NEXTHDR_ROUTING, NULL, NULL); |
45 | if (err < 0) { |
46 | if (err != -ENOENT) |
47 | par->hotdrop = true; |
48 | return false; |
49 | } |
50 | |
51 | rh = skb_header_pointer(skb, offset: ptr, len: sizeof(_route), buffer: &_route); |
52 | if (rh == NULL) { |
53 | par->hotdrop = true; |
54 | return false; |
55 | } |
56 | |
57 | hdrlen = ipv6_optlen(rh); |
58 | if (skb->len - ptr < hdrlen) { |
59 | /* Pcket smaller than its length field */ |
60 | return false; |
61 | } |
62 | |
63 | ret = (segsleft_match(min: rtinfo->segsleft[0], max: rtinfo->segsleft[1], |
64 | id: rh->segments_left, |
65 | invert: !!(rtinfo->invflags & IP6T_RT_INV_SGS))) && |
66 | (!(rtinfo->flags & IP6T_RT_LEN) || |
67 | ((rtinfo->hdrlen == hdrlen) ^ |
68 | !!(rtinfo->invflags & IP6T_RT_INV_LEN))) && |
69 | (!(rtinfo->flags & IP6T_RT_TYP) || |
70 | ((rtinfo->rt_type == rh->type) ^ |
71 | !!(rtinfo->invflags & IP6T_RT_INV_TYP))); |
72 | |
73 | if (ret && (rtinfo->flags & IP6T_RT_RES)) { |
74 | const u_int32_t *rp; |
75 | u_int32_t _reserved; |
76 | rp = skb_header_pointer(skb, |
77 | offset: ptr + offsetof(struct rt0_hdr, |
78 | reserved), |
79 | len: sizeof(_reserved), |
80 | buffer: &_reserved); |
81 | if (!rp) { |
82 | par->hotdrop = true; |
83 | return false; |
84 | } |
85 | |
86 | ret = (*rp == 0); |
87 | } |
88 | |
89 | if (!(rtinfo->flags & IP6T_RT_FST)) { |
90 | return ret; |
91 | } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) { |
92 | if (rtinfo->addrnr > (unsigned int)((hdrlen - 8) / 16)) { |
93 | return false; |
94 | } else { |
95 | unsigned int i = 0; |
96 | |
97 | for (temp = 0; |
98 | temp < (unsigned int)((hdrlen - 8) / 16); |
99 | temp++) { |
100 | ap = skb_header_pointer(skb, |
101 | offset: ptr |
102 | + sizeof(struct rt0_hdr) |
103 | + temp * sizeof(_addr), |
104 | len: sizeof(_addr), |
105 | buffer: &_addr); |
106 | |
107 | if (ap == NULL) { |
108 | par->hotdrop = true; |
109 | return false; |
110 | } |
111 | |
112 | if (ipv6_addr_equal(a1: ap, a2: &rtinfo->addrs[i])) |
113 | i++; |
114 | if (i == rtinfo->addrnr) |
115 | break; |
116 | } |
117 | if (i == rtinfo->addrnr) |
118 | return ret; |
119 | else |
120 | return false; |
121 | } |
122 | } else { |
123 | if (rtinfo->addrnr > (unsigned int)((hdrlen - 8) / 16)) { |
124 | return false; |
125 | } else { |
126 | for (temp = 0; temp < rtinfo->addrnr; temp++) { |
127 | ap = skb_header_pointer(skb, |
128 | offset: ptr |
129 | + sizeof(struct rt0_hdr) |
130 | + temp * sizeof(_addr), |
131 | len: sizeof(_addr), |
132 | buffer: &_addr); |
133 | if (ap == NULL) { |
134 | par->hotdrop = true; |
135 | return false; |
136 | } |
137 | |
138 | if (!ipv6_addr_equal(a1: ap, a2: &rtinfo->addrs[temp])) |
139 | break; |
140 | } |
141 | if (temp == rtinfo->addrnr && |
142 | temp == (unsigned int)((hdrlen - 8) / 16)) |
143 | return ret; |
144 | else |
145 | return false; |
146 | } |
147 | } |
148 | |
149 | return false; |
150 | } |
151 | |
152 | static int rt_mt6_check(const struct xt_mtchk_param *par) |
153 | { |
154 | const struct ip6t_rt *rtinfo = par->matchinfo; |
155 | |
156 | if (rtinfo->invflags & ~IP6T_RT_INV_MASK) { |
157 | pr_debug("unknown flags %X\n" , rtinfo->invflags); |
158 | return -EINVAL; |
159 | } |
160 | if ((rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST_MASK)) && |
161 | (!(rtinfo->flags & IP6T_RT_TYP) || |
162 | (rtinfo->rt_type != 0) || |
163 | (rtinfo->invflags & IP6T_RT_INV_TYP))) { |
164 | pr_debug("`--rt-type 0' required before `--rt-0-*'" ); |
165 | return -EINVAL; |
166 | } |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static struct xt_match rt_mt6_reg __read_mostly = { |
172 | .name = "rt" , |
173 | .family = NFPROTO_IPV6, |
174 | .match = rt_mt6, |
175 | .matchsize = sizeof(struct ip6t_rt), |
176 | .checkentry = rt_mt6_check, |
177 | .me = THIS_MODULE, |
178 | }; |
179 | |
180 | static int __init rt_mt6_init(void) |
181 | { |
182 | return xt_register_match(target: &rt_mt6_reg); |
183 | } |
184 | |
185 | static void __exit rt_mt6_exit(void) |
186 | { |
187 | xt_unregister_match(target: &rt_mt6_reg); |
188 | } |
189 | |
190 | module_init(rt_mt6_init); |
191 | module_exit(rt_mt6_exit); |
192 | |