From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Leblond Subject: [RFC PATCH] ipv6: basic implementation of reverse path filtering Date: Mon, 6 Jun 2011 13:34:34 +0200 Message-ID: <1307360074-25473-1-git-send-email-eric@regit.org> Cc: Eric Leblond To: netdev@vger.kernel.org Return-path: Received: from ks28632.kimsufi.com ([91.121.96.152]:39441 "EHLO ks28632.kimsufi.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756061Ab1FFL6A (ORCPT ); Mon, 6 Jun 2011 07:58:00 -0400 Sender: netdev-owner@vger.kernel.org List-ID: This patch provides a basic implementation of reverse path filtering for IPv6. Functionnality can be activatedor desactivated through an rp_filter entry similar to the IPv4 one. The functionnality is disabled by default for backward compatibility but should be enable on all IPv6 routers/firewalls for security reason. This implementation is heavily based on the patch Denis Semmau proposed in 2006. Signed-off-by: Eric Leblond --- include/linux/ipv6.h | 2 ++ include/linux/sysctl.h | 1 + net/ipv6/addrconf.c | 10 ++++++++++ net/ipv6/ip6_output.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 0 deletions(-) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 0c99776..e61b88d 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -134,6 +134,7 @@ struct ipv6hdr { */ struct ipv6_devconf { __s32 forwarding; + __s32 rp_filter; __s32 hop_limit; __s32 mtu6; __s32 accept_ra; @@ -213,6 +214,7 @@ enum { DEVCONF_DISABLE_IPV6, DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, + DEVCONF_RP_FILTER, DEVCONF_MAX }; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 11684d9..bdcb7f8 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -568,6 +568,7 @@ enum { NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22, NET_IPV6_PROXY_NDP=23, NET_IPV6_ACCEPT_SOURCE_ROUTE=25, + NET_IPV6_RP_FILTER=26, __NET_IPV6_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 498b927..ba1c574 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -196,6 +196,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, .accept_dad = 1, + .rp_filter = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -230,6 +231,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, .accept_dad = 1, + .rp_filter = 0, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -3805,6 +3807,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; + array[DEVCONF_RP_FILTER] = cnf->rp_filter; } static inline size_t inet6_ifla6_size(void) @@ -4459,6 +4462,13 @@ static struct addrconf_sysctl_table .proc_handler = proc_dointvec }, { + .procname = "rp_filter", + .data = &ipv6_devconf.rp_filter, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { /* sentinel */ } }, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 9d4b165..c035494 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -374,6 +374,18 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) return 0; } +static int rt6_validate_source(struct sk_buff *skb) +{ + struct rt6_info *rt; + struct ipv6hdr *hdr = ipv6_hdr(skb); + rt = rt6_lookup(dev_net(skb->dev), &hdr->saddr, NULL, 0, 0); + if (rt != NULL) { + if (rt->rt6i_idev->dev == skb->dev) + return 0; + } + return -1; +} + static inline int ip6_forward_finish(struct sk_buff *skb) { return dst_output(skb); @@ -384,6 +396,7 @@ int ip6_forward(struct sk_buff *skb) struct dst_entry *dst = skb_dst(skb); struct ipv6hdr *hdr = ipv6_hdr(skb); struct inet6_skb_parm *opt = IP6CB(skb); + struct inet6_dev *idev = NULL; struct net *net = dev_net(dst->dev); u32 mtu; @@ -401,6 +414,25 @@ int ip6_forward(struct sk_buff *skb) if (skb->pkt_type != PACKET_HOST) goto drop; + idev = in6_dev_get(skb->dev); + if (!idev) { + printk(KERN_WARNING "idev error for rp_filter\n"); + goto error; + } + + if (net->ipv6.devconf_all->rp_filter & idev->cnf.rp_filter) { + if (rt6_validate_source(skb) < 0) { + printk(KERN_WARNING + "rp_filter: packet refused on %s, invalid src %pI6 (dst: %pI6)", + skb->dev->name, + &hdr->saddr, + &hdr->daddr + ); + goto drop; + } + + } + skb_forward_csum(skb); /* -- 1.7.5.3