|
@@ -30,222 +30,57 @@ static const struct xt_table nf_nat_ipv6_table = {
|
|
|
.af = NFPROTO_IPV6,
|
|
|
};
|
|
|
|
|
|
-static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
|
|
|
-{
|
|
|
- /* Force range to this IP; let proto decide mapping for
|
|
|
- * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
|
|
|
- */
|
|
|
- struct nf_nat_range range;
|
|
|
-
|
|
|
- range.flags = 0;
|
|
|
- pr_debug("Allocating NULL binding for %p (%pI6)\n", ct,
|
|
|
- HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
|
|
|
- &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6 :
|
|
|
- &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6);
|
|
|
-
|
|
|
- return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
|
|
|
-}
|
|
|
-
|
|
|
-static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
|
|
|
- const struct net_device *in,
|
|
|
- const struct net_device *out,
|
|
|
- struct nf_conn *ct)
|
|
|
+static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ const struct net_device *in,
|
|
|
+ const struct net_device *out,
|
|
|
+ struct nf_conn *ct)
|
|
|
{
|
|
|
struct net *net = nf_ct_net(ct);
|
|
|
- unsigned int ret;
|
|
|
|
|
|
- ret = ip6t_do_table(skb, hooknum, in, out, net->ipv6.ip6table_nat);
|
|
|
- if (ret == NF_ACCEPT) {
|
|
|
- if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
|
|
|
- ret = alloc_null_binding(ct, hooknum);
|
|
|
- }
|
|
|
- return ret;
|
|
|
+ return ip6t_do_table(skb, ops->hooknum, in, out, net->ipv6.ip6table_nat);
|
|
|
}
|
|
|
|
|
|
-static unsigned int
|
|
|
-nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
|
|
|
- struct sk_buff *skb,
|
|
|
- const struct net_device *in,
|
|
|
- const struct net_device *out,
|
|
|
- int (*okfn)(struct sk_buff *))
|
|
|
+static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ const struct net_device *in,
|
|
|
+ const struct net_device *out,
|
|
|
+ int (*okfn)(struct sk_buff *))
|
|
|
{
|
|
|
- struct nf_conn *ct;
|
|
|
- enum ip_conntrack_info ctinfo;
|
|
|
- struct nf_conn_nat *nat;
|
|
|
- enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
|
|
|
- __be16 frag_off;
|
|
|
- int hdrlen;
|
|
|
- u8 nexthdr;
|
|
|
-
|
|
|
- ct = nf_ct_get(skb, &ctinfo);
|
|
|
- /* Can't track? It's not due to stress, or conntrack would
|
|
|
- * have dropped it. Hence it's the user's responsibilty to
|
|
|
- * packet filter it out, or implement conntrack/NAT for that
|
|
|
- * protocol. 8) --RR
|
|
|
- */
|
|
|
- if (!ct)
|
|
|
- return NF_ACCEPT;
|
|
|
-
|
|
|
- /* Don't try to NAT if this packet is not conntracked */
|
|
|
- if (nf_ct_is_untracked(ct))
|
|
|
- return NF_ACCEPT;
|
|
|
-
|
|
|
- nat = nf_ct_nat_ext_add(ct);
|
|
|
- if (nat == NULL)
|
|
|
- return NF_ACCEPT;
|
|
|
-
|
|
|
- switch (ctinfo) {
|
|
|
- case IP_CT_RELATED:
|
|
|
- case IP_CT_RELATED_REPLY:
|
|
|
- nexthdr = ipv6_hdr(skb)->nexthdr;
|
|
|
- hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
|
|
|
- &nexthdr, &frag_off);
|
|
|
-
|
|
|
- if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
|
|
|
- if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
|
|
|
- ops->hooknum,
|
|
|
- hdrlen))
|
|
|
- return NF_DROP;
|
|
|
- else
|
|
|
- return NF_ACCEPT;
|
|
|
- }
|
|
|
- /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
|
|
|
- case IP_CT_NEW:
|
|
|
- /* Seen it before? This can happen for loopback, retrans,
|
|
|
- * or local packets.
|
|
|
- */
|
|
|
- if (!nf_nat_initialized(ct, maniptype)) {
|
|
|
- unsigned int ret;
|
|
|
-
|
|
|
- ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct);
|
|
|
- if (ret != NF_ACCEPT)
|
|
|
- return ret;
|
|
|
- } else {
|
|
|
- pr_debug("Already setup manip %s for ct %p\n",
|
|
|
- maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
|
|
|
- ct);
|
|
|
- if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
|
|
|
- goto oif_changed;
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- /* ESTABLISHED */
|
|
|
- NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
|
|
|
- ctinfo == IP_CT_ESTABLISHED_REPLY);
|
|
|
- if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
|
|
|
- goto oif_changed;
|
|
|
- }
|
|
|
-
|
|
|
- return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
|
|
|
-
|
|
|
-oif_changed:
|
|
|
- nf_ct_kill_acct(ct, ctinfo, skb);
|
|
|
- return NF_DROP;
|
|
|
+ return nf_nat_ipv6_fn(ops, skb, in, out, ip6table_nat_do_chain);
|
|
|
}
|
|
|
|
|
|
-static unsigned int
|
|
|
-nf_nat_ipv6_in(const struct nf_hook_ops *ops,
|
|
|
- struct sk_buff *skb,
|
|
|
- const struct net_device *in,
|
|
|
- const struct net_device *out,
|
|
|
- int (*okfn)(struct sk_buff *))
|
|
|
+static unsigned int ip6table_nat_in(const struct nf_hook_ops *ops,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ const struct net_device *in,
|
|
|
+ const struct net_device *out,
|
|
|
+ int (*okfn)(struct sk_buff *))
|
|
|
{
|
|
|
- unsigned int ret;
|
|
|
- struct in6_addr daddr = ipv6_hdr(skb)->daddr;
|
|
|
-
|
|
|
- ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
|
|
|
- if (ret != NF_DROP && ret != NF_STOLEN &&
|
|
|
- ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
|
|
|
- skb_dst_drop(skb);
|
|
|
-
|
|
|
- return ret;
|
|
|
+ return nf_nat_ipv6_in(ops, skb, in, out, ip6table_nat_do_chain);
|
|
|
}
|
|
|
|
|
|
-static unsigned int
|
|
|
-nf_nat_ipv6_out(const struct nf_hook_ops *ops,
|
|
|
- struct sk_buff *skb,
|
|
|
- const struct net_device *in,
|
|
|
- const struct net_device *out,
|
|
|
- int (*okfn)(struct sk_buff *))
|
|
|
+static unsigned int ip6table_nat_out(const struct nf_hook_ops *ops,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ const struct net_device *in,
|
|
|
+ const struct net_device *out,
|
|
|
+ int (*okfn)(struct sk_buff *))
|
|
|
{
|
|
|
-#ifdef CONFIG_XFRM
|
|
|
- const struct nf_conn *ct;
|
|
|
- enum ip_conntrack_info ctinfo;
|
|
|
- int err;
|
|
|
-#endif
|
|
|
- unsigned int ret;
|
|
|
-
|
|
|
- /* root is playing with raw sockets. */
|
|
|
- if (skb->len < sizeof(struct ipv6hdr))
|
|
|
- return NF_ACCEPT;
|
|
|
-
|
|
|
- ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
|
|
|
-#ifdef CONFIG_XFRM
|
|
|
- if (ret != NF_DROP && ret != NF_STOLEN &&
|
|
|
- !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
|
|
|
- (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
|
|
|
- enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
|
|
-
|
|
|
- if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
|
|
|
- &ct->tuplehash[!dir].tuple.dst.u3) ||
|
|
|
- (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
|
|
|
- ct->tuplehash[dir].tuple.src.u.all !=
|
|
|
- ct->tuplehash[!dir].tuple.dst.u.all)) {
|
|
|
- err = nf_xfrm_me_harder(skb, AF_INET6);
|
|
|
- if (err < 0)
|
|
|
- ret = NF_DROP_ERR(err);
|
|
|
- }
|
|
|
- }
|
|
|
-#endif
|
|
|
- return ret;
|
|
|
+ return nf_nat_ipv6_out(ops, skb, in, out, ip6table_nat_do_chain);
|
|
|
}
|
|
|
|
|
|
-static unsigned int
|
|
|
-nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
|
|
|
- struct sk_buff *skb,
|
|
|
- const struct net_device *in,
|
|
|
- const struct net_device *out,
|
|
|
- int (*okfn)(struct sk_buff *))
|
|
|
+static unsigned int ip6table_nat_local_fn(const struct nf_hook_ops *ops,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ const struct net_device *in,
|
|
|
+ const struct net_device *out,
|
|
|
+ int (*okfn)(struct sk_buff *))
|
|
|
{
|
|
|
- const struct nf_conn *ct;
|
|
|
- enum ip_conntrack_info ctinfo;
|
|
|
- unsigned int ret;
|
|
|
- int err;
|
|
|
-
|
|
|
- /* root is playing with raw sockets. */
|
|
|
- if (skb->len < sizeof(struct ipv6hdr))
|
|
|
- return NF_ACCEPT;
|
|
|
-
|
|
|
- ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
|
|
|
- if (ret != NF_DROP && ret != NF_STOLEN &&
|
|
|
- (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
|
|
|
- enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
|
|
-
|
|
|
- if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
|
|
|
- &ct->tuplehash[!dir].tuple.src.u3)) {
|
|
|
- err = ip6_route_me_harder(skb);
|
|
|
- if (err < 0)
|
|
|
- ret = NF_DROP_ERR(err);
|
|
|
- }
|
|
|
-#ifdef CONFIG_XFRM
|
|
|
- else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
|
|
|
- ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
|
|
|
- ct->tuplehash[dir].tuple.dst.u.all !=
|
|
|
- ct->tuplehash[!dir].tuple.src.u.all) {
|
|
|
- err = nf_xfrm_me_harder(skb, AF_INET6);
|
|
|
- if (err < 0)
|
|
|
- ret = NF_DROP_ERR(err);
|
|
|
- }
|
|
|
-#endif
|
|
|
- }
|
|
|
- return ret;
|
|
|
+ return nf_nat_ipv6_local_fn(ops, skb, in, out, ip6table_nat_do_chain);
|
|
|
}
|
|
|
|
|
|
static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
|
|
|
/* Before packet filtering, change destination */
|
|
|
{
|
|
|
- .hook = nf_nat_ipv6_in,
|
|
|
+ .hook = ip6table_nat_in,
|
|
|
.owner = THIS_MODULE,
|
|
|
.pf = NFPROTO_IPV6,
|
|
|
.hooknum = NF_INET_PRE_ROUTING,
|
|
@@ -253,7 +88,7 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
|
|
|
},
|
|
|
/* After packet filtering, change source */
|
|
|
{
|
|
|
- .hook = nf_nat_ipv6_out,
|
|
|
+ .hook = ip6table_nat_out,
|
|
|
.owner = THIS_MODULE,
|
|
|
.pf = NFPROTO_IPV6,
|
|
|
.hooknum = NF_INET_POST_ROUTING,
|
|
@@ -261,7 +96,7 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
|
|
|
},
|
|
|
/* Before packet filtering, change destination */
|
|
|
{
|
|
|
- .hook = nf_nat_ipv6_local_fn,
|
|
|
+ .hook = ip6table_nat_local_fn,
|
|
|
.owner = THIS_MODULE,
|
|
|
.pf = NFPROTO_IPV6,
|
|
|
.hooknum = NF_INET_LOCAL_OUT,
|
|
@@ -269,7 +104,7 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
|
|
|
},
|
|
|
/* After packet filtering, change source */
|
|
|
{
|
|
|
- .hook = nf_nat_ipv6_fn,
|
|
|
+ .hook = ip6table_nat_fn,
|
|
|
.owner = THIS_MODULE,
|
|
|
.pf = NFPROTO_IPV6,
|
|
|
.hooknum = NF_INET_LOCAL_IN,
|