Blame view
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
11.6 KB
9fb9cbb10
|
1 2 3 4 5 6 7 8 9 |
/* * Copyright (C)2004 USAGI/WIDE Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Author: * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> |
9fb9cbb10
|
10 |
*/ |
9fb9cbb10
|
11 12 13 14 15 16 17 18 19 |
#include <linux/types.h> #include <linux/ipv6.h> #include <linux/in6.h> #include <linux/netfilter.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/icmp.h> #include <linux/sysctl.h> #include <net/ipv6.h> |
04128f233
|
20 |
#include <net/inet_frag.h> |
9fb9cbb10
|
21 22 23 24 |
#include <linux/netfilter_ipv6.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_helper.h> |
605dcad6c
|
25 |
#include <net/netfilter/nf_conntrack_l4proto.h> |
9fb9cbb10
|
26 27 |
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_core.h> |
9d2493f88
|
28 |
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
9fb9cbb10
|
29 |
|
8ce8439a3
|
30 31 |
static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple) |
9fb9cbb10
|
32 |
{ |
32948588a
|
33 34 |
const u_int32_t *ap; u_int32_t _addrs[8]; |
9fb9cbb10
|
35 36 37 38 |
ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr), sizeof(_addrs), _addrs); if (ap == NULL) |
8ce8439a3
|
39 |
return false; |
9fb9cbb10
|
40 41 42 |
memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6)); memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6)); |
8ce8439a3
|
43 |
return true; |
9fb9cbb10
|
44 |
} |
8ce8439a3
|
45 46 |
static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *orig) |
9fb9cbb10
|
47 48 49 |
{ memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6)); memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6)); |
8ce8439a3
|
50 |
return true; |
9fb9cbb10
|
51 52 53 54 55 |
} static int ipv6_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { |
5b095d989
|
56 |
return seq_printf(s, "src=%pI6 dst=%pI6 ", |
0c6ce78ab
|
57 |
tuple->src.u3.ip6, tuple->dst.u3.ip6); |
9fb9cbb10
|
58 |
} |
9fb9cbb10
|
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
/* * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c * * This function parses (probably truncated) exthdr set "hdr" * of length "len". "nexthdrp" initially points to some place, * where type of the first header can be found. * * It skips all well-known exthdrs, and returns pointer to the start * of unparsable area i.e. the first header with unknown type. * if success, *nexthdr is updated by type/protocol of this header. * * NOTES: - it may return pointer pointing beyond end of packet, * if the last recognized header is truncated in the middle. * - if packet is truncated, so that all parsed headers are skipped, * it returns -1. * - if packet is fragmented, return pointer of the fragment header. * - ESP is unparsable for now and considered like * normal payload protocol. * - Note also special handling of AUTH header. Thanks to IPsec wizards. */ |
1a3a206f7
|
79 80 |
static int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, int len) |
9fb9cbb10
|
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
{ u8 nexthdr = *nexthdrp; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr hdr; int hdrlen; if (len < (int)sizeof(struct ipv6_opt_hdr)) return -1; if (nexthdr == NEXTHDR_NONE) break; if (nexthdr == NEXTHDR_FRAGMENT) break; if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) BUG(); if (nexthdr == NEXTHDR_AUTH) hdrlen = (hdr.hdrlen+2)<<2; else hdrlen = ipv6_optlen(&hdr); nexthdr = hdr.nexthdr; len -= hdrlen; start += hdrlen; } *nexthdrp = nexthdr; return start; } |
ffc306904
|
109 110 |
static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, unsigned int *dataoff, u_int8_t *protonum) |
9fb9cbb10
|
111 |
{ |
ffc306904
|
112 113 114 115 116 117 118 119 120 121 122 |
unsigned int extoff = nhoff + sizeof(struct ipv6hdr); unsigned char pnum; int protoff; if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr), &pnum, sizeof(pnum)) != 0) { pr_debug("ip6_conntrack_core: can't get nexthdr "); return -NF_ACCEPT; } protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff); |
9fb9cbb10
|
123 |
/* |
ffc306904
|
124 |
* (protoff == skb->len) mean that the packet doesn't have no data |
9fb9cbb10
|
125 126 |
* except of IPv6 & ext headers. but it's tracked anyway. - YK */ |
ffc306904
|
127 |
if ((protoff < 0) || (protoff > skb->len)) { |
0d53778e8
|
128 129 |
pr_debug("ip6_conntrack_core: can't find proto in pkt "); |
9fb9cbb10
|
130 131 132 133 134 135 136 |
return -NF_ACCEPT; } *dataoff = protoff; *protonum = pnum; return NF_ACCEPT; } |
9fb9cbb10
|
137 |
static unsigned int ipv6_confirm(unsigned int hooknum, |
3db05fea5
|
138 |
struct sk_buff *skb, |
9fb9cbb10
|
139 140 141 142 143 |
const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; |
32948588a
|
144 145 |
const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; |
9fb9cbb10
|
146 |
enum ip_conntrack_info ctinfo; |
dc808fe28
|
147 |
unsigned int ret, protoff; |
3db05fea5
|
148 149 |
unsigned int extoff = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; unsigned char pnum = ipv6_hdr(skb)->nexthdr; |
dc808fe28
|
150 |
|
9fb9cbb10
|
151 152 |
/* This is where we call the helper: as the packet goes out. */ |
3db05fea5
|
153 |
ct = nf_ct_get(skb, &ctinfo); |
6442f1cf8
|
154 |
if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) |
dc808fe28
|
155 156 157 |
goto out; help = nfct_help(ct); |
3c158f7f5
|
158 159 160 161 162 |
if (!help) goto out; /* rcu_read_lock()ed by nf_hook_slow */ helper = rcu_dereference(help->helper); if (!helper) |
dc808fe28
|
163 |
goto out; |
3db05fea5
|
164 165 166 |
protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff); if (protoff > skb->len || pnum == NEXTHDR_FRAGMENT) { |
0d53778e8
|
167 168 |
pr_debug("proto header not found "); |
dc808fe28
|
169 |
return NF_ACCEPT; |
9fb9cbb10
|
170 |
} |
3db05fea5
|
171 |
ret = helper->help(skb, protoff, ct, ctinfo); |
dc808fe28
|
172 173 174 |
if (ret != NF_ACCEPT) return ret; out: |
9fb9cbb10
|
175 |
/* We've seen it coming out the other side: confirm it */ |
3db05fea5
|
176 |
return nf_conntrack_confirm(skb); |
9fb9cbb10
|
177 |
} |
9fb9cbb10
|
178 |
static unsigned int ipv6_defrag(unsigned int hooknum, |
3db05fea5
|
179 |
struct sk_buff *skb, |
9fb9cbb10
|
180 181 182 183 184 185 186 |
const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *reasm; /* Previously seen (loopback)? */ |
3db05fea5
|
187 |
if (skb->nfct) |
9fb9cbb10
|
188 |
return NF_ACCEPT; |
3db05fea5
|
189 |
reasm = nf_ct_frag6_gather(skb); |
9fb9cbb10
|
190 191 192 193 194 195 |
/* queued */ if (reasm == NULL) return NF_STOLEN; /* error occured or not fragmented */ |
3db05fea5
|
196 |
if (reasm == skb) |
9fb9cbb10
|
197 198 199 200 201 202 203 |
return NF_ACCEPT; nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in, (struct net_device *)out, okfn); return NF_STOLEN; } |
a702a65fc
|
204 205 206 207 |
static unsigned int __ipv6_conntrack_in(struct net *net, unsigned int hooknum, struct sk_buff *skb, int (*okfn)(struct sk_buff *)) |
9fb9cbb10
|
208 |
{ |
3db05fea5
|
209 |
struct sk_buff *reasm = skb->nfct_reasm; |
9fb9cbb10
|
210 211 212 213 214 215 |
/* This packet is fragmented and has reassembled packet. */ if (reasm) { /* Reassembled packet isn't parsed yet ? */ if (!reasm->nfct) { unsigned int ret; |
a702a65fc
|
216 |
ret = nf_conntrack_in(net, PF_INET6, hooknum, reasm); |
9fb9cbb10
|
217 218 219 220 |
if (ret != NF_ACCEPT) return ret; } nf_conntrack_get(reasm->nfct); |
3db05fea5
|
221 222 |
skb->nfct = reasm->nfct; skb->nfctinfo = reasm->nfctinfo; |
9fb9cbb10
|
223 224 |
return NF_ACCEPT; } |
a702a65fc
|
225 226 227 228 229 230 231 232 233 234 |
return nf_conntrack_in(net, PF_INET6, hooknum, skb); } static unsigned int ipv6_conntrack_in(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { return __ipv6_conntrack_in(dev_net(in), hooknum, skb, okfn); |
9fb9cbb10
|
235 236 237 |
} static unsigned int ipv6_conntrack_local(unsigned int hooknum, |
3db05fea5
|
238 |
struct sk_buff *skb, |
9fb9cbb10
|
239 240 241 242 243 |
const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { /* root is playing with raw sockets. */ |
3db05fea5
|
244 |
if (skb->len < sizeof(struct ipv6hdr)) { |
9fb9cbb10
|
245 246 247 248 249 |
if (net_ratelimit()) printk("ipv6_conntrack_local: packet too short "); return NF_ACCEPT; } |
a702a65fc
|
250 |
return __ipv6_conntrack_in(dev_net(out), hooknum, skb, okfn); |
9fb9cbb10
|
251 |
} |
1999414a4
|
252 |
static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { |
964ddaa10
|
253 254 255 256 |
{ .hook = ipv6_defrag, .owner = THIS_MODULE, .pf = PF_INET6, |
6e23ae2a4
|
257 |
.hooknum = NF_INET_PRE_ROUTING, |
964ddaa10
|
258 259 260 261 262 263 |
.priority = NF_IP6_PRI_CONNTRACK_DEFRAG, }, { .hook = ipv6_conntrack_in, .owner = THIS_MODULE, .pf = PF_INET6, |
6e23ae2a4
|
264 |
.hooknum = NF_INET_PRE_ROUTING, |
964ddaa10
|
265 266 267 268 269 270 |
.priority = NF_IP6_PRI_CONNTRACK, }, { .hook = ipv6_conntrack_local, .owner = THIS_MODULE, .pf = PF_INET6, |
6e23ae2a4
|
271 |
.hooknum = NF_INET_LOCAL_OUT, |
964ddaa10
|
272 273 274 275 276 277 |
.priority = NF_IP6_PRI_CONNTRACK, }, { .hook = ipv6_defrag, .owner = THIS_MODULE, .pf = PF_INET6, |
6e23ae2a4
|
278 |
.hooknum = NF_INET_LOCAL_OUT, |
964ddaa10
|
279 280 281 282 283 284 |
.priority = NF_IP6_PRI_CONNTRACK_DEFRAG, }, { .hook = ipv6_confirm, .owner = THIS_MODULE, .pf = PF_INET6, |
6e23ae2a4
|
285 |
.hooknum = NF_INET_POST_ROUTING, |
964ddaa10
|
286 287 288 289 290 291 |
.priority = NF_IP6_PRI_LAST, }, { .hook = ipv6_confirm, .owner = THIS_MODULE, .pf = PF_INET6, |
6e23ae2a4
|
292 |
.hooknum = NF_INET_LOCAL_IN, |
964ddaa10
|
293 294 |
.priority = NF_IP6_PRI_LAST-1, }, |
9fb9cbb10
|
295 |
}; |
e281db5cd
|
296 |
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) |
c1d10adb4
|
297 298 299 |
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_conntrack.h> |
fdf708322
|
300 |
static int ipv6_tuple_to_nlattr(struct sk_buff *skb, |
c1d10adb4
|
301 302 |
const struct nf_conntrack_tuple *tuple) { |
df6fb868d
|
303 |
NLA_PUT(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4, |
c1d10adb4
|
304 |
&tuple->src.u3.ip6); |
df6fb868d
|
305 |
NLA_PUT(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4, |
c1d10adb4
|
306 307 |
&tuple->dst.u3.ip6); return 0; |
df6fb868d
|
308 |
nla_put_failure: |
c1d10adb4
|
309 310 |
return -1; } |
f73e924cd
|
311 312 313 |
static const struct nla_policy ipv6_nla_policy[CTA_IP_MAX+1] = { [CTA_IP_V6_SRC] = { .len = sizeof(u_int32_t)*4 }, [CTA_IP_V6_DST] = { .len = sizeof(u_int32_t)*4 }, |
c1d10adb4
|
314 |
}; |
fdf708322
|
315 |
static int ipv6_nlattr_to_tuple(struct nlattr *tb[], |
c1d10adb4
|
316 317 |
struct nf_conntrack_tuple *t) { |
df6fb868d
|
318 |
if (!tb[CTA_IP_V6_SRC] || !tb[CTA_IP_V6_DST]) |
c1d10adb4
|
319 |
return -EINVAL; |
df6fb868d
|
320 |
memcpy(&t->src.u3.ip6, nla_data(tb[CTA_IP_V6_SRC]), |
c1d10adb4
|
321 |
sizeof(u_int32_t) * 4); |
df6fb868d
|
322 |
memcpy(&t->dst.u3.ip6, nla_data(tb[CTA_IP_V6_DST]), |
c1d10adb4
|
323 324 325 326 327 |
sizeof(u_int32_t) * 4); return 0; } #endif |
61075af51
|
328 |
struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { |
9fb9cbb10
|
329 330 331 332 333 |
.l3proto = PF_INET6, .name = "ipv6", .pkt_to_tuple = ipv6_pkt_to_tuple, .invert_tuple = ipv6_invert_tuple, .print_tuple = ipv6_print_tuple, |
ffc306904
|
334 |
.get_l4proto = ipv6_get_l4proto, |
e281db5cd
|
335 |
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) |
fdf708322
|
336 337 |
.tuple_to_nlattr = ipv6_tuple_to_nlattr, .nlattr_to_tuple = ipv6_nlattr_to_tuple, |
f73e924cd
|
338 |
.nla_policy = ipv6_nla_policy, |
c1d10adb4
|
339 |
#endif |
933a41e7e
|
340 341 342 343 |
#ifdef CONFIG_SYSCTL .ctl_table_path = nf_net_netfilter_sysctl_path, .ctl_table = nf_ct_ipv6_sysctl_table, #endif |
9fb9cbb10
|
344 345 |
.me = THIS_MODULE, }; |
32292a7ff
|
346 347 348 349 350 |
MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6)); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); static int __init nf_conntrack_l3proto_ipv6_init(void) |
9fb9cbb10
|
351 352 |
{ int ret = 0; |
32292a7ff
|
353 |
need_conntrack(); |
9fb9cbb10
|
354 355 356 357 358 |
ret = nf_ct_frag6_init(); if (ret < 0) { printk("nf_conntrack_ipv6: can't initialize frag6. "); |
32292a7ff
|
359 |
return ret; |
9fb9cbb10
|
360 |
} |
605dcad6c
|
361 |
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6); |
9fb9cbb10
|
362 363 364 365 366 |
if (ret < 0) { printk("nf_conntrack_ipv6: can't register tcp. "); goto cleanup_frag6; } |
605dcad6c
|
367 |
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6); |
9fb9cbb10
|
368 369 370 371 372 |
if (ret < 0) { printk("nf_conntrack_ipv6: can't register udp. "); goto cleanup_tcp; } |
605dcad6c
|
373 |
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmpv6); |
9fb9cbb10
|
374 375 376 377 378 379 380 381 382 383 384 385 |
if (ret < 0) { printk("nf_conntrack_ipv6: can't register icmpv6. "); goto cleanup_udp; } ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6); if (ret < 0) { printk("nf_conntrack_ipv6: can't register ipv6 "); goto cleanup_icmpv6; } |
964ddaa10
|
386 387 |
ret = nf_register_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); |
9fb9cbb10
|
388 389 390 391 392 393 |
if (ret < 0) { printk("nf_conntrack_ipv6: can't register pre-routing defrag " "hook. "); goto cleanup_ipv6; } |
9fb9cbb10
|
394 |
return ret; |
9fb9cbb10
|
395 396 397 |
cleanup_ipv6: nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); cleanup_icmpv6: |
605dcad6c
|
398 |
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); |
9fb9cbb10
|
399 |
cleanup_udp: |
605dcad6c
|
400 |
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); |
9fb9cbb10
|
401 |
cleanup_tcp: |
605dcad6c
|
402 |
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); |
9fb9cbb10
|
403 404 |
cleanup_frag6: nf_ct_frag6_cleanup(); |
9fb9cbb10
|
405 406 |
return ret; } |
65b4b4e81
|
407 |
static void __exit nf_conntrack_l3proto_ipv6_fini(void) |
9fb9cbb10
|
408 |
{ |
32292a7ff
|
409 |
synchronize_net(); |
32292a7ff
|
410 411 |
nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); |
605dcad6c
|
412 413 414 |
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); |
32292a7ff
|
415 |
nf_ct_frag6_cleanup(); |
9fb9cbb10
|
416 |
} |
65b4b4e81
|
417 418 |
module_init(nf_conntrack_l3proto_ipv6_init); module_exit(nf_conntrack_l3proto_ipv6_fini); |