2 * Copyright (C)2004 USAGI/WIDE Project
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
9 * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
12 #include <linux/types.h>
13 #include <linux/ipv6.h>
14 #include <linux/in6.h>
15 #include <linux/netfilter.h>
16 #include <linux/module.h>
17 #include <linux/skbuff.h>
18 #include <linux/icmp.h>
20 #include <net/inet_frag.h>
22 #include <linux/netfilter_bridge.h>
23 #include <linux/netfilter_ipv6.h>
24 #include <linux/netfilter_ipv6/ip6_tables.h>
25 #include <net/netfilter/nf_conntrack.h>
26 #include <net/netfilter/nf_conntrack_helper.h>
27 #include <net/netfilter/nf_conntrack_l4proto.h>
28 #include <net/netfilter/nf_conntrack_l3proto.h>
29 #include <net/netfilter/nf_conntrack_core.h>
30 #include <net/netfilter/nf_conntrack_zones.h>
31 #include <net/netfilter/nf_conntrack_seqadj.h>
32 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
33 #include <net/netfilter/nf_nat_helper.h>
34 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
35 #include <net/netfilter/nf_log.h>
37 static int conntrack6_net_id
;
38 static DEFINE_MUTEX(register_ipv6_hooks
);
40 struct conntrack6_net
{
44 static bool ipv6_pkt_to_tuple(const struct sk_buff
*skb
, unsigned int nhoff
,
45 struct nf_conntrack_tuple
*tuple
)
50 ap
= skb_header_pointer(skb
, nhoff
+ offsetof(struct ipv6hdr
, saddr
),
51 sizeof(_addrs
), _addrs
);
55 memcpy(tuple
->src
.u3
.ip6
, ap
, sizeof(tuple
->src
.u3
.ip6
));
56 memcpy(tuple
->dst
.u3
.ip6
, ap
+ 4, sizeof(tuple
->dst
.u3
.ip6
));
61 static bool ipv6_invert_tuple(struct nf_conntrack_tuple
*tuple
,
62 const struct nf_conntrack_tuple
*orig
)
64 memcpy(tuple
->src
.u3
.ip6
, orig
->dst
.u3
.ip6
, sizeof(tuple
->src
.u3
.ip6
));
65 memcpy(tuple
->dst
.u3
.ip6
, orig
->src
.u3
.ip6
, sizeof(tuple
->dst
.u3
.ip6
));
70 static void ipv6_print_tuple(struct seq_file
*s
,
71 const struct nf_conntrack_tuple
*tuple
)
73 seq_printf(s
, "src=%pI6 dst=%pI6 ",
74 tuple
->src
.u3
.ip6
, tuple
->dst
.u3
.ip6
);
77 static int ipv6_get_l4proto(const struct sk_buff
*skb
, unsigned int nhoff
,
78 unsigned int *dataoff
, u_int8_t
*protonum
)
80 unsigned int extoff
= nhoff
+ sizeof(struct ipv6hdr
);
85 if (skb_copy_bits(skb
, nhoff
+ offsetof(struct ipv6hdr
, nexthdr
),
86 &nexthdr
, sizeof(nexthdr
)) != 0) {
87 pr_debug("ip6_conntrack_core: can't get nexthdr\n");
90 protoff
= ipv6_skip_exthdr(skb
, extoff
, &nexthdr
, &frag_off
);
92 * (protoff == skb->len) means the packet has not data, just
93 * IPv6 and possibly extensions headers, but it is tracked anyway
95 if (protoff
< 0 || (frag_off
& htons(~0x7)) != 0) {
96 pr_debug("ip6_conntrack_core: can't find proto in pkt\n");
105 static unsigned int ipv6_helper(void *priv
,
107 const struct nf_hook_state
*state
)
110 const struct nf_conn_help
*help
;
111 const struct nf_conntrack_helper
*helper
;
112 enum ip_conntrack_info ctinfo
;
117 /* This is where we call the helper: as the packet goes out. */
118 ct
= nf_ct_get(skb
, &ctinfo
);
119 if (!ct
|| ctinfo
== IP_CT_RELATED_REPLY
)
122 help
= nfct_help(ct
);
125 /* rcu_read_lock()ed by nf_hook_thresh */
126 helper
= rcu_dereference(help
->helper
);
130 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
131 protoff
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
), &nexthdr
,
133 if (protoff
< 0 || (frag_off
& htons(~0x7)) != 0) {
134 pr_debug("proto header not found\n");
138 return helper
->help(skb
, protoff
, ct
, ctinfo
);
141 static unsigned int ipv6_confirm(void *priv
,
143 const struct nf_hook_state
*state
)
146 enum ip_conntrack_info ctinfo
;
147 unsigned char pnum
= ipv6_hdr(skb
)->nexthdr
;
151 ct
= nf_ct_get(skb
, &ctinfo
);
152 if (!ct
|| ctinfo
== IP_CT_RELATED_REPLY
)
155 protoff
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
), &pnum
,
157 if (protoff
< 0 || (frag_off
& htons(~0x7)) != 0) {
158 pr_debug("proto header not found\n");
162 /* adjust seqs for loopback traffic only in outgoing direction */
163 if (test_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
) &&
164 !nf_is_loopback_packet(skb
)) {
165 if (!nf_ct_seq_adjust(skb
, ct
, ctinfo
, protoff
)) {
166 NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct
), drop
);
171 /* We've seen it coming out the other side: confirm it */
172 return nf_conntrack_confirm(skb
);
175 static unsigned int ipv6_conntrack_in(void *priv
,
177 const struct nf_hook_state
*state
)
179 return nf_conntrack_in(state
->net
, PF_INET6
, state
->hook
, skb
);
182 static unsigned int ipv6_conntrack_local(void *priv
,
184 const struct nf_hook_state
*state
)
186 /* root is playing with raw sockets. */
187 if (skb
->len
< sizeof(struct ipv6hdr
)) {
188 net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
191 return nf_conntrack_in(state
->net
, PF_INET6
, state
->hook
, skb
);
194 static const struct nf_hook_ops ipv6_conntrack_ops
[] = {
196 .hook
= ipv6_conntrack_in
,
198 .hooknum
= NF_INET_PRE_ROUTING
,
199 .priority
= NF_IP6_PRI_CONNTRACK
,
202 .hook
= ipv6_conntrack_local
,
204 .hooknum
= NF_INET_LOCAL_OUT
,
205 .priority
= NF_IP6_PRI_CONNTRACK
,
210 .hooknum
= NF_INET_POST_ROUTING
,
211 .priority
= NF_IP6_PRI_CONNTRACK_HELPER
,
214 .hook
= ipv6_confirm
,
216 .hooknum
= NF_INET_POST_ROUTING
,
217 .priority
= NF_IP6_PRI_LAST
,
222 .hooknum
= NF_INET_LOCAL_IN
,
223 .priority
= NF_IP6_PRI_CONNTRACK_HELPER
,
226 .hook
= ipv6_confirm
,
228 .hooknum
= NF_INET_LOCAL_IN
,
229 .priority
= NF_IP6_PRI_LAST
-1,
234 ipv6_getorigdst(struct sock
*sk
, int optval
, void __user
*user
, int *len
)
236 const struct inet_sock
*inet
= inet_sk(sk
);
237 const struct ipv6_pinfo
*inet6
= inet6_sk(sk
);
238 const struct nf_conntrack_tuple_hash
*h
;
239 struct sockaddr_in6 sin6
;
240 struct nf_conntrack_tuple tuple
= { .src
.l3num
= NFPROTO_IPV6
};
243 tuple
.src
.u3
.in6
= sk
->sk_v6_rcv_saddr
;
244 tuple
.src
.u
.tcp
.port
= inet
->inet_sport
;
245 tuple
.dst
.u3
.in6
= sk
->sk_v6_daddr
;
246 tuple
.dst
.u
.tcp
.port
= inet
->inet_dport
;
247 tuple
.dst
.protonum
= sk
->sk_protocol
;
249 if (sk
->sk_protocol
!= IPPROTO_TCP
&& sk
->sk_protocol
!= IPPROTO_SCTP
)
252 if (*len
< 0 || (unsigned int) *len
< sizeof(sin6
))
255 h
= nf_conntrack_find_get(sock_net(sk
), &nf_ct_zone_dflt
, &tuple
);
257 pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n",
258 &tuple
.src
.u3
.ip6
, ntohs(tuple
.src
.u
.tcp
.port
),
259 &tuple
.dst
.u3
.ip6
, ntohs(tuple
.dst
.u
.tcp
.port
));
263 ct
= nf_ct_tuplehash_to_ctrack(h
);
265 sin6
.sin6_family
= AF_INET6
;
266 sin6
.sin6_port
= ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.dst
.u
.tcp
.port
;
267 sin6
.sin6_flowinfo
= inet6
->flow_label
& IPV6_FLOWINFO_MASK
;
268 memcpy(&sin6
.sin6_addr
,
269 &ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.dst
.u3
.in6
,
270 sizeof(sin6
.sin6_addr
));
273 sin6
.sin6_scope_id
= ipv6_iface_scope_id(&sin6
.sin6_addr
,
274 sk
->sk_bound_dev_if
);
275 return copy_to_user(user
, &sin6
, sizeof(sin6
)) ? -EFAULT
: 0;
278 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
280 #include <linux/netfilter/nfnetlink.h>
281 #include <linux/netfilter/nfnetlink_conntrack.h>
283 static int ipv6_tuple_to_nlattr(struct sk_buff
*skb
,
284 const struct nf_conntrack_tuple
*tuple
)
286 if (nla_put_in6_addr(skb
, CTA_IP_V6_SRC
, &tuple
->src
.u3
.in6
) ||
287 nla_put_in6_addr(skb
, CTA_IP_V6_DST
, &tuple
->dst
.u3
.in6
))
288 goto nla_put_failure
;
295 static const struct nla_policy ipv6_nla_policy
[CTA_IP_MAX
+1] = {
296 [CTA_IP_V6_SRC
] = { .len
= sizeof(u_int32_t
)*4 },
297 [CTA_IP_V6_DST
] = { .len
= sizeof(u_int32_t
)*4 },
300 static int ipv6_nlattr_to_tuple(struct nlattr
*tb
[],
301 struct nf_conntrack_tuple
*t
)
303 if (!tb
[CTA_IP_V6_SRC
] || !tb
[CTA_IP_V6_DST
])
306 t
->src
.u3
.in6
= nla_get_in6_addr(tb
[CTA_IP_V6_SRC
]);
307 t
->dst
.u3
.in6
= nla_get_in6_addr(tb
[CTA_IP_V6_DST
]);
313 static int ipv6_hooks_register(struct net
*net
)
315 struct conntrack6_net
*cnet
= net_generic(net
, conntrack6_net_id
);
318 mutex_lock(®ister_ipv6_hooks
);
323 err
= nf_defrag_ipv6_enable(net
);
329 err
= nf_register_net_hooks(net
, ipv6_conntrack_ops
,
330 ARRAY_SIZE(ipv6_conntrack_ops
));
334 mutex_unlock(®ister_ipv6_hooks
);
338 static void ipv6_hooks_unregister(struct net
*net
)
340 struct conntrack6_net
*cnet
= net_generic(net
, conntrack6_net_id
);
342 mutex_lock(®ister_ipv6_hooks
);
343 if (cnet
->users
&& (--cnet
->users
== 0))
344 nf_unregister_net_hooks(net
, ipv6_conntrack_ops
,
345 ARRAY_SIZE(ipv6_conntrack_ops
));
346 mutex_unlock(®ister_ipv6_hooks
);
349 struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly
= {
352 .pkt_to_tuple
= ipv6_pkt_to_tuple
,
353 .invert_tuple
= ipv6_invert_tuple
,
354 .print_tuple
= ipv6_print_tuple
,
355 .get_l4proto
= ipv6_get_l4proto
,
356 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
357 .tuple_to_nlattr
= ipv6_tuple_to_nlattr
,
358 .nlattr_to_tuple
= ipv6_nlattr_to_tuple
,
359 .nla_policy
= ipv6_nla_policy
,
360 .nla_size
= NLA_ALIGN(NLA_HDRLEN
+ sizeof(u32
[4])) +
361 NLA_ALIGN(NLA_HDRLEN
+ sizeof(u32
[4])),
363 .net_ns_get
= ipv6_hooks_register
,
364 .net_ns_put
= ipv6_hooks_unregister
,
368 MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6
));
369 MODULE_LICENSE("GPL");
370 MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
372 static struct nf_sockopt_ops so_getorigdst6
= {
374 .get_optmin
= IP6T_SO_ORIGINAL_DST
,
375 .get_optmax
= IP6T_SO_ORIGINAL_DST
+ 1,
376 .get
= ipv6_getorigdst
,
377 .owner
= THIS_MODULE
,
380 static struct nf_conntrack_l4proto
*builtin_l4proto6
[] = {
381 &nf_conntrack_l4proto_tcp6
,
382 &nf_conntrack_l4proto_udp6
,
383 &nf_conntrack_l4proto_icmpv6
,
384 #ifdef CONFIG_NF_CT_PROTO_DCCP
385 &nf_conntrack_l4proto_dccp6
,
387 #ifdef CONFIG_NF_CT_PROTO_SCTP
388 &nf_conntrack_l4proto_sctp6
,
390 #ifdef CONFIG_NF_CT_PROTO_UDPLITE
391 &nf_conntrack_l4proto_udplite6
,
395 static int ipv6_net_init(struct net
*net
)
397 return nf_ct_l4proto_pernet_register(net
, builtin_l4proto6
,
398 ARRAY_SIZE(builtin_l4proto6
));
401 static void ipv6_net_exit(struct net
*net
)
403 nf_ct_l4proto_pernet_unregister(net
, builtin_l4proto6
,
404 ARRAY_SIZE(builtin_l4proto6
));
407 static struct pernet_operations ipv6_net_ops
= {
408 .init
= ipv6_net_init
,
409 .exit
= ipv6_net_exit
,
410 .id
= &conntrack6_net_id
,
411 .size
= sizeof(struct conntrack6_net
),
414 static int __init
nf_conntrack_l3proto_ipv6_init(void)
420 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
421 if (WARN_ON(nla_policy_len(ipv6_nla_policy
, CTA_IP_MAX
+ 1) !=
422 nf_conntrack_l3proto_ipv6
.nla_size
))
426 ret
= nf_register_sockopt(&so_getorigdst6
);
428 pr_err("Unable to register netfilter socket option\n");
432 ret
= register_pernet_subsys(&ipv6_net_ops
);
434 goto cleanup_sockopt
;
436 ret
= nf_ct_l4proto_register(builtin_l4proto6
,
437 ARRAY_SIZE(builtin_l4proto6
));
441 ret
= nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6
);
443 pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n");
444 goto cleanup_l4proto
;
448 nf_ct_l4proto_unregister(builtin_l4proto6
,
449 ARRAY_SIZE(builtin_l4proto6
));
451 unregister_pernet_subsys(&ipv6_net_ops
);
453 nf_unregister_sockopt(&so_getorigdst6
);
457 static void __exit
nf_conntrack_l3proto_ipv6_fini(void)
460 nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6
);
461 nf_ct_l4proto_unregister(builtin_l4proto6
,
462 ARRAY_SIZE(builtin_l4proto6
));
463 unregister_pernet_subsys(&ipv6_net_ops
);
464 nf_unregister_sockopt(&so_getorigdst6
);
467 module_init(nf_conntrack_l3proto_ipv6_init
);
468 module_exit(nf_conntrack_l3proto_ipv6_fini
);