diff options
| -rw-r--r-- | net/ipv6/netfilter/Kconfig | 10 | ||||
| -rw-r--r-- | net/ipv6/netfilter/Makefile | 1 | ||||
| -rw-r--r-- | net/ipv6/netfilter/ip6t_rpfilter.c | 133 |
3 files changed, 144 insertions, 0 deletions
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index f792b34cbe9c..9a68fb5b9e77 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
| @@ -125,6 +125,16 @@ config IP6_NF_MATCH_MH | |||
| 125 | 125 | ||
| 126 | To compile it as a module, choose M here. If unsure, say N. | 126 | To compile it as a module, choose M here. If unsure, say N. |
| 127 | 127 | ||
| 128 | config IP6_NF_MATCH_RPFILTER | ||
| 129 | tristate '"rpfilter" reverse path filter match support' | ||
| 130 | depends on NETFILTER_ADVANCED | ||
| 131 | ---help--- | ||
| 132 | This option allows you to match packets whose replies would | ||
| 133 | go out via the interface the packet came in. | ||
| 134 | |||
| 135 | To compile it as a module, choose M here. If unsure, say N. | ||
| 136 | The module will be called ip6t_rpfilter. | ||
| 137 | |||
| 128 | config IP6_NF_MATCH_RT | 138 | config IP6_NF_MATCH_RT |
| 129 | tristate '"rt" Routing header match support' | 139 | tristate '"rt" Routing header match support' |
| 130 | depends on NETFILTER_ADVANCED | 140 | depends on NETFILTER_ADVANCED |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index abfee91ce816..2eaed96db02c 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
| @@ -27,6 +27,7 @@ obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o | |||
| 27 | obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o | 27 | obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o |
| 28 | obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o | 28 | obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o |
| 29 | obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o | 29 | obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o |
| 30 | obj-$(CONFIG_IP6_NF_MATCH_RPFILTER) += ip6t_rpfilter.o | ||
| 30 | obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o | 31 | obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o |
| 31 | 32 | ||
| 32 | # targets | 33 | # targets |
diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c new file mode 100644 index 000000000000..5d1d8b04d694 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_rpfilter.c | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2011 Florian Westphal <fw@strlen.de> | ||
| 3 | * | ||
| 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. | ||
| 7 | */ | ||
| 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/skbuff.h> | ||
| 11 | #include <linux/netdevice.h> | ||
| 12 | #include <linux/route.h> | ||
| 13 | #include <net/ip6_fib.h> | ||
| 14 | #include <net/ip6_route.h> | ||
| 15 | |||
| 16 | #include <linux/netfilter/xt_rpfilter.h> | ||
| 17 | #include <linux/netfilter/x_tables.h> | ||
| 18 | |||
| 19 | MODULE_LICENSE("GPL"); | ||
| 20 | MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); | ||
| 21 | MODULE_DESCRIPTION("Xtables: IPv6 reverse path filter match"); | ||
| 22 | |||
| 23 | static bool rpfilter_addr_unicast(const struct in6_addr *addr) | ||
| 24 | { | ||
| 25 | int addr_type = ipv6_addr_type(addr); | ||
| 26 | return addr_type & IPV6_ADDR_UNICAST; | ||
| 27 | } | ||
| 28 | |||
| 29 | static bool rpfilter_lookup_reverse6(const struct sk_buff *skb, | ||
| 30 | const struct net_device *dev, u8 flags) | ||
| 31 | { | ||
| 32 | struct rt6_info *rt; | ||
| 33 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
| 34 | bool ret = false; | ||
| 35 | struct flowi6 fl6 = { | ||
| 36 | .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, | ||
| 37 | .flowi6_proto = iph->nexthdr, | ||
| 38 | .daddr = iph->saddr, | ||
| 39 | }; | ||
| 40 | int lookup_flags; | ||
| 41 | |||
| 42 | if (rpfilter_addr_unicast(&iph->daddr)) { | ||
| 43 | memcpy(&fl6.saddr, &iph->daddr, sizeof(struct in6_addr)); | ||
| 44 | lookup_flags = RT6_LOOKUP_F_HAS_SADDR; | ||
| 45 | } else { | ||
| 46 | lookup_flags = 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | fl6.flowi6_mark = flags & XT_RPFILTER_VALID_MARK ? skb->mark : 0; | ||
| 50 | if ((flags & XT_RPFILTER_LOOSE) == 0) { | ||
| 51 | fl6.flowi6_oif = dev->ifindex; | ||
| 52 | lookup_flags |= RT6_LOOKUP_F_IFACE; | ||
| 53 | } | ||
| 54 | |||
| 55 | rt = (void *) ip6_route_lookup(dev_net(dev), &fl6, lookup_flags); | ||
| 56 | if (rt->dst.error) | ||
| 57 | goto out; | ||
| 58 | |||
| 59 | if (rt->rt6i_flags & (RTF_REJECT|RTF_ANYCAST)) | ||
| 60 | goto out; | ||
| 61 | |||
| 62 | if (rt->rt6i_flags & RTF_LOCAL) { | ||
| 63 | ret = flags & XT_RPFILTER_ACCEPT_LOCAL; | ||
| 64 | goto out; | ||
| 65 | } | ||
| 66 | |||
| 67 | if (rt->rt6i_idev->dev == dev || (flags & XT_RPFILTER_LOOSE)) | ||
| 68 | ret = true; | ||
| 69 | out: | ||
| 70 | dst_release(&rt->dst); | ||
| 71 | return ret; | ||
| 72 | } | ||
| 73 | |||
| 74 | static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) | ||
| 75 | { | ||
| 76 | const struct xt_rpfilter_info *info = par->matchinfo; | ||
| 77 | int saddrtype; | ||
| 78 | struct ipv6hdr *iph; | ||
| 79 | bool invert = info->flags & XT_RPFILTER_INVERT; | ||
| 80 | |||
| 81 | if (par->in->flags & IFF_LOOPBACK) | ||
| 82 | return true ^ invert; | ||
| 83 | |||
| 84 | iph = ipv6_hdr(skb); | ||
| 85 | saddrtype = ipv6_addr_type(&iph->saddr); | ||
| 86 | if (unlikely(saddrtype == IPV6_ADDR_ANY)) | ||
| 87 | return true ^ invert; /* not routable: forward path will drop it */ | ||
| 88 | |||
| 89 | return rpfilter_lookup_reverse6(skb, par->in, info->flags) ^ invert; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int rpfilter_check(const struct xt_mtchk_param *par) | ||
| 93 | { | ||
| 94 | const struct xt_rpfilter_info *info = par->matchinfo; | ||
| 95 | unsigned int options = ~XT_RPFILTER_OPTION_MASK; | ||
| 96 | |||
| 97 | if (info->flags & options) { | ||
| 98 | pr_info("unknown options encountered"); | ||
| 99 | return -EINVAL; | ||
| 100 | } | ||
| 101 | |||
| 102 | if (strcmp(par->table, "mangle") != 0 && | ||
| 103 | strcmp(par->table, "raw") != 0) { | ||
| 104 | pr_info("match only valid in the \'raw\' " | ||
| 105 | "or \'mangle\' tables, not \'%s\'.\n", par->table); | ||
| 106 | return -EINVAL; | ||
| 107 | } | ||
| 108 | |||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | static struct xt_match rpfilter_mt_reg __read_mostly = { | ||
| 113 | .name = "rpfilter", | ||
| 114 | .family = NFPROTO_IPV6, | ||
| 115 | .checkentry = rpfilter_check, | ||
| 116 | .match = rpfilter_mt, | ||
| 117 | .matchsize = sizeof(struct xt_rpfilter_info), | ||
| 118 | .hooks = (1 << NF_INET_PRE_ROUTING), | ||
| 119 | .me = THIS_MODULE | ||
| 120 | }; | ||
| 121 | |||
| 122 | static int __init rpfilter_mt_init(void) | ||
| 123 | { | ||
| 124 | return xt_register_match(&rpfilter_mt_reg); | ||
| 125 | } | ||
| 126 | |||
| 127 | static void __exit rpfilter_mt_exit(void) | ||
| 128 | { | ||
| 129 | xt_unregister_match(&rpfilter_mt_reg); | ||
| 130 | } | ||
| 131 | |||
| 132 | module_init(rpfilter_mt_init); | ||
| 133 | module_exit(rpfilter_mt_exit); | ||
