Loading include/uapi/linux/in6.h +1 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,7 @@ struct in6_flowlabel_req { * * IP6T_SO_GET_REVISION_MATCH 68 * IP6T_SO_GET_REVISION_TARGET 69 * IP6T_SO_ORIGINAL_DST 80 */ /* RFC5014: Source address selection */ Loading include/uapi/linux/netfilter_ipv6/ip6_tables.h +3 −0 Original line number Diff line number Diff line Loading @@ -178,6 +178,9 @@ struct ip6t_error { #define IP6T_SO_GET_REVISION_TARGET (IP6T_BASE_CTL + 5) #define IP6T_SO_GET_MAX IP6T_SO_GET_REVISION_TARGET /* obtain original address if REDIRECT'd connection */ #define IP6T_SO_ORIGINAL_DST 80 /* ICMP matching stuff */ struct ip6t_icmp { __u8 type; /* type to match */ Loading net/ipv4/netfilter/iptable_nat.c +1 −3 Original line number Diff line number Diff line Loading @@ -276,9 +276,7 @@ static int __net_init iptable_nat_net_init(struct net *net) return -ENOMEM; net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl); kfree(repl); if (IS_ERR(net->ipv4.nat_table)) return PTR_ERR(net->ipv4.nat_table); return 0; return PTR_RET(net->ipv4.nat_table); } static void __net_exit iptable_nat_net_exit(struct net *net) Loading net/ipv6/netfilter/ip6table_nat.c +1 −3 Original line number Diff line number Diff line Loading @@ -277,9 +277,7 @@ static int __net_init ip6table_nat_net_init(struct net *net) return -ENOMEM; net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl); kfree(repl); if (IS_ERR(net->ipv6.ip6table_nat)) return PTR_ERR(net->ipv6.ip6table_nat); return 0; return PTR_RET(net->ipv6.ip6table_nat); } static void __net_exit ip6table_nat_net_exit(struct net *net) Loading net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +66 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/netfilter_bridge.h> #include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6/ip6_tables.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_l4proto.h> Loading Loading @@ -295,6 +296,55 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { }, }; static int ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) { const struct inet_sock *inet = inet_sk(sk); const struct ipv6_pinfo *inet6 = inet6_sk(sk); const struct nf_conntrack_tuple_hash *h; struct sockaddr_in6 sin6; struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; struct nf_conn *ct; tuple.src.u3.in6 = inet6->rcv_saddr; tuple.src.u.tcp.port = inet->inet_sport; tuple.dst.u3.in6 = inet6->daddr; tuple.dst.u.tcp.port = inet->inet_dport; tuple.dst.protonum = sk->sk_protocol; if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) return -ENOPROTOOPT; if (*len < 0 || (unsigned int) *len < sizeof(sin6)) return -EINVAL; h = nf_conntrack_find_get(sock_net(sk), NF_CT_DEFAULT_ZONE, &tuple); if (!h) { pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n", &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port), &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port)); return -ENOENT; } ct = nf_ct_tuplehash_to_ctrack(h); sin6.sin6_family = AF_INET6; sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port; sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK; memcpy(&sin6.sin6_addr, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6, sizeof(sin6.sin6_addr)); nf_ct_put(ct); if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) sin6.sin6_scope_id = sk->sk_bound_dev_if; else sin6.sin6_scope_id = 0; return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0; } #if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include <linux/netfilter/nfnetlink.h> Loading Loading @@ -359,6 +409,14 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6)); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); static struct nf_sockopt_ops so_getorigdst6 = { .pf = NFPROTO_IPV6, .get_optmin = IP6T_SO_ORIGINAL_DST, .get_optmax = IP6T_SO_ORIGINAL_DST + 1, .get = ipv6_getorigdst, .owner = THIS_MODULE, }; static int ipv6_net_init(struct net *net) { int ret = 0; Loading Loading @@ -425,6 +483,12 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) need_conntrack(); nf_defrag_ipv6_enable(); ret = nf_register_sockopt(&so_getorigdst6); if (ret < 0) { pr_err("Unable to register netfilter socket option\n"); return ret; } ret = register_pernet_subsys(&ipv6_net_ops); if (ret < 0) goto cleanup_pernet; Loading @@ -440,6 +504,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) cleanup_ipv6: unregister_pernet_subsys(&ipv6_net_ops); cleanup_pernet: nf_unregister_sockopt(&so_getorigdst6); return ret; } Loading @@ -448,6 +513,7 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) synchronize_net(); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); unregister_pernet_subsys(&ipv6_net_ops); nf_unregister_sockopt(&so_getorigdst6); } module_init(nf_conntrack_l3proto_ipv6_init); Loading Loading
include/uapi/linux/in6.h +1 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,7 @@ struct in6_flowlabel_req { * * IP6T_SO_GET_REVISION_MATCH 68 * IP6T_SO_GET_REVISION_TARGET 69 * IP6T_SO_ORIGINAL_DST 80 */ /* RFC5014: Source address selection */ Loading
include/uapi/linux/netfilter_ipv6/ip6_tables.h +3 −0 Original line number Diff line number Diff line Loading @@ -178,6 +178,9 @@ struct ip6t_error { #define IP6T_SO_GET_REVISION_TARGET (IP6T_BASE_CTL + 5) #define IP6T_SO_GET_MAX IP6T_SO_GET_REVISION_TARGET /* obtain original address if REDIRECT'd connection */ #define IP6T_SO_ORIGINAL_DST 80 /* ICMP matching stuff */ struct ip6t_icmp { __u8 type; /* type to match */ Loading
net/ipv4/netfilter/iptable_nat.c +1 −3 Original line number Diff line number Diff line Loading @@ -276,9 +276,7 @@ static int __net_init iptable_nat_net_init(struct net *net) return -ENOMEM; net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl); kfree(repl); if (IS_ERR(net->ipv4.nat_table)) return PTR_ERR(net->ipv4.nat_table); return 0; return PTR_RET(net->ipv4.nat_table); } static void __net_exit iptable_nat_net_exit(struct net *net) Loading
net/ipv6/netfilter/ip6table_nat.c +1 −3 Original line number Diff line number Diff line Loading @@ -277,9 +277,7 @@ static int __net_init ip6table_nat_net_init(struct net *net) return -ENOMEM; net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl); kfree(repl); if (IS_ERR(net->ipv6.ip6table_nat)) return PTR_ERR(net->ipv6.ip6table_nat); return 0; return PTR_RET(net->ipv6.ip6table_nat); } static void __net_exit ip6table_nat_net_exit(struct net *net) Loading
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +66 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/netfilter_bridge.h> #include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6/ip6_tables.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_l4proto.h> Loading Loading @@ -295,6 +296,55 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { }, }; static int ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) { const struct inet_sock *inet = inet_sk(sk); const struct ipv6_pinfo *inet6 = inet6_sk(sk); const struct nf_conntrack_tuple_hash *h; struct sockaddr_in6 sin6; struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; struct nf_conn *ct; tuple.src.u3.in6 = inet6->rcv_saddr; tuple.src.u.tcp.port = inet->inet_sport; tuple.dst.u3.in6 = inet6->daddr; tuple.dst.u.tcp.port = inet->inet_dport; tuple.dst.protonum = sk->sk_protocol; if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) return -ENOPROTOOPT; if (*len < 0 || (unsigned int) *len < sizeof(sin6)) return -EINVAL; h = nf_conntrack_find_get(sock_net(sk), NF_CT_DEFAULT_ZONE, &tuple); if (!h) { pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n", &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port), &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port)); return -ENOENT; } ct = nf_ct_tuplehash_to_ctrack(h); sin6.sin6_family = AF_INET6; sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port; sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK; memcpy(&sin6.sin6_addr, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6, sizeof(sin6.sin6_addr)); nf_ct_put(ct); if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) sin6.sin6_scope_id = sk->sk_bound_dev_if; else sin6.sin6_scope_id = 0; return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0; } #if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include <linux/netfilter/nfnetlink.h> Loading Loading @@ -359,6 +409,14 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6)); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>"); static struct nf_sockopt_ops so_getorigdst6 = { .pf = NFPROTO_IPV6, .get_optmin = IP6T_SO_ORIGINAL_DST, .get_optmax = IP6T_SO_ORIGINAL_DST + 1, .get = ipv6_getorigdst, .owner = THIS_MODULE, }; static int ipv6_net_init(struct net *net) { int ret = 0; Loading Loading @@ -425,6 +483,12 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) need_conntrack(); nf_defrag_ipv6_enable(); ret = nf_register_sockopt(&so_getorigdst6); if (ret < 0) { pr_err("Unable to register netfilter socket option\n"); return ret; } ret = register_pernet_subsys(&ipv6_net_ops); if (ret < 0) goto cleanup_pernet; Loading @@ -440,6 +504,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) cleanup_ipv6: unregister_pernet_subsys(&ipv6_net_ops); cleanup_pernet: nf_unregister_sockopt(&so_getorigdst6); return ret; } Loading @@ -448,6 +513,7 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) synchronize_net(); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); unregister_pernet_subsys(&ipv6_net_ops); nf_unregister_sockopt(&so_getorigdst6); } module_init(nf_conntrack_l3proto_ipv6_init); Loading