diff options
Diffstat (limited to 'net/ipv6/route.c')
| -rw-r--r-- | net/ipv6/route.c | 166 | 
1 files changed, 119 insertions, 47 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 843406f14d7..de2b1decd78 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -89,12 +89,12 @@ static void		ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);  #ifdef CONFIG_IPV6_ROUTE_INFO  static struct rt6_info *rt6_add_route_info(struct net *net, -					   struct in6_addr *prefix, int prefixlen, -					   struct in6_addr *gwaddr, int ifindex, +					   const struct in6_addr *prefix, int prefixlen, +					   const struct in6_addr *gwaddr, int ifindex,  					   unsigned pref);  static struct rt6_info *rt6_get_route_info(struct net *net, -					   struct in6_addr *prefix, int prefixlen, -					   struct in6_addr *gwaddr, int ifindex); +					   const struct in6_addr *prefix, int prefixlen, +					   const struct in6_addr *gwaddr, int ifindex);  #endif  static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) @@ -153,6 +153,12 @@ static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)  {  } +static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, +					 unsigned long old) +{ +	return NULL; +} +  static struct dst_ops ip6_dst_blackhole_ops = {  	.family			=	AF_INET6,  	.protocol		=	cpu_to_be16(ETH_P_IPV6), @@ -161,6 +167,7 @@ static struct dst_ops ip6_dst_blackhole_ops = {  	.default_mtu		=	ip6_blackhole_default_mtu,  	.default_advmss		=	ip6_default_advmss,  	.update_pmtu		=	ip6_rt_blackhole_update_pmtu, +	.cow_metrics		=	ip6_rt_blackhole_cow_metrics,  };  static const u32 ip6_template_metrics[RTAX_MAX] = { @@ -220,9 +227,14 @@ static struct rt6_info ip6_blk_hole_entry_template = {  #endif  /* allocate dst with ip6_dst_ops */ -static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops) +static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops, +					     struct net_device *dev)  { -	return (struct rt6_info *)dst_alloc(ops, 0); +	struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, 0); + +	memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); + +	return rt;  }  static void ip6_dst_destroy(struct dst_entry *dst) @@ -283,7 +295,7 @@ static __inline__ int rt6_check_expired(const struct rt6_info *rt)  		time_after(jiffies, rt->rt6i_expires);  } -static inline int rt6_need_strict(struct in6_addr *daddr) +static inline int rt6_need_strict(const struct in6_addr *daddr)  {  	return ipv6_addr_type(daddr) &  		(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); @@ -295,7 +307,7 @@ static inline int rt6_need_strict(struct in6_addr *daddr)  static inline struct rt6_info *rt6_device_match(struct net *net,  						    struct rt6_info *rt, -						    struct in6_addr *saddr, +						    const struct in6_addr *saddr,  						    int oif,  						    int flags)  { @@ -507,7 +519,7 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)  #ifdef CONFIG_IPV6_ROUTE_INFO  int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, -		  struct in6_addr *gwaddr) +		  const struct in6_addr *gwaddr)  {  	struct net *net = dev_net(dev);  	struct route_info *rinfo = (struct route_info *) opt; @@ -670,8 +682,8 @@ int ip6_ins_rt(struct rt6_info *rt)  	return __ip6_ins_rt(rt, &info);  } -static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr, -				      struct in6_addr *saddr) +static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, const struct in6_addr *daddr, +				      const struct in6_addr *saddr)  {  	struct rt6_info *rt; @@ -739,7 +751,7 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad  	return rt;  } -static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr) +static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, const struct in6_addr *daddr)  {  	struct rt6_info *rt = ip6_rt_copy(ort);  	if (rt) { @@ -830,7 +842,7 @@ static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *  void ip6_route_input(struct sk_buff *skb)  { -	struct ipv6hdr *iph = ipv6_hdr(skb); +	const struct ipv6hdr *iph = ipv6_hdr(skb);  	struct net *net = dev_net(skb->dev);  	int flags = RT6_LOOKUP_F_HAS_SADDR;  	struct flowi6 fl6 = { @@ -874,11 +886,13 @@ EXPORT_SYMBOL(ip6_route_output);  struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)  { -	struct rt6_info *rt = dst_alloc(&ip6_dst_blackhole_ops, 1); -	struct rt6_info *ort = (struct rt6_info *) dst_orig; +	struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;  	struct dst_entry *new = NULL; +	rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0);  	if (rt) { +		memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); +  		new = &rt->dst;  		new->__use = 1; @@ -886,9 +900,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori  		new->output = dst_discard;  		dst_copy_metrics(new, &ort->dst); -		new->dev = ort->dst.dev; -		if (new->dev) -			dev_hold(new->dev);  		rt->rt6i_idev = ort->rt6i_idev;  		if (rt->rt6i_idev)  			in6_dev_hold(rt->rt6i_idev); @@ -1031,13 +1042,12 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,  	if (unlikely(idev == NULL))  		return NULL; -	rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); +	rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev);  	if (unlikely(rt == NULL)) {  		in6_dev_put(idev);  		goto out;  	} -	dev_hold(dev);  	if (neigh)  		neigh_hold(neigh);  	else { @@ -1046,7 +1056,6 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,  			neigh = NULL;  	} -	rt->rt6i_dev	  = dev;  	rt->rt6i_idev     = idev;  	rt->rt6i_nexthop  = neigh;  	atomic_set(&rt->dst.__refcnt, 1); @@ -1205,7 +1214,7 @@ int ip6_route_add(struct fib6_config *cfg)  		goto out;  	} -	rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); +	rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL);  	if (rt == NULL) {  		err = -ENOMEM; @@ -1272,7 +1281,7 @@ int ip6_route_add(struct fib6_config *cfg)  	}  	if (cfg->fc_flags & RTF_GATEWAY) { -		struct in6_addr *gw_addr; +		const struct in6_addr *gw_addr;  		int gwa_type;  		gw_addr = &cfg->fc_gateway; @@ -1325,6 +1334,16 @@ int ip6_route_add(struct fib6_config *cfg)  	if (dev == NULL)  		goto out; +	if (!ipv6_addr_any(&cfg->fc_prefsrc)) { +		if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { +			err = -EINVAL; +			goto out; +		} +		ipv6_addr_copy(&rt->rt6i_prefsrc.addr, &cfg->fc_prefsrc); +		rt->rt6i_prefsrc.plen = 128; +	} else +		rt->rt6i_prefsrc.plen = 0; +  	if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {  		rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);  		if (IS_ERR(rt->rt6i_nexthop)) { @@ -1502,9 +1521,9 @@ out:  	return rt;  }; -static struct rt6_info *ip6_route_redirect(struct in6_addr *dest, -					   struct in6_addr *src, -					   struct in6_addr *gateway, +static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest, +					   const struct in6_addr *src, +					   const struct in6_addr *gateway,  					   struct net_device *dev)  {  	int flags = RT6_LOOKUP_F_HAS_SADDR; @@ -1526,8 +1545,8 @@ static struct rt6_info *ip6_route_redirect(struct in6_addr *dest,  						   flags, __ip6_route_redirect);  } -void rt6_redirect(struct in6_addr *dest, struct in6_addr *src, -		  struct in6_addr *saddr, +void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, +		  const struct in6_addr *saddr,  		  struct neighbour *neigh, u8 *lladdr, int on_link)  {  	struct rt6_info *rt, *nrt = NULL; @@ -1601,7 +1620,7 @@ out:   *	i.e. Path MTU discovery   */ -static void rt6_do_pmtu_disc(struct in6_addr *daddr, struct in6_addr *saddr, +static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr,  			     struct net *net, u32 pmtu, int ifindex)  {  	struct rt6_info *rt, *nrt; @@ -1686,7 +1705,7 @@ out:  	dst_release(&rt->dst);  } -void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, +void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr,  			struct net_device *dev, u32 pmtu)  {  	struct net *net = dev_net(dev); @@ -1714,7 +1733,8 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,  static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)  {  	struct net *net = dev_net(ort->rt6i_dev); -	struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); +	struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, +					    ort->dst.dev);  	if (rt) {  		rt->dst.input = ort->dst.input; @@ -1722,9 +1742,6 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)  		dst_copy_metrics(&rt->dst, &ort->dst);  		rt->dst.error = ort->dst.error; -		rt->dst.dev = ort->dst.dev; -		if (rt->dst.dev) -			dev_hold(rt->dst.dev);  		rt->rt6i_idev = ort->rt6i_idev;  		if (rt->rt6i_idev)  			in6_dev_hold(rt->rt6i_idev); @@ -1739,6 +1756,7 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)  #ifdef CONFIG_IPV6_SUBTREES  		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));  #endif +		memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key));  		rt->rt6i_table = ort->rt6i_table;  	}  	return rt; @@ -1746,8 +1764,8 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)  #ifdef CONFIG_IPV6_ROUTE_INFO  static struct rt6_info *rt6_get_route_info(struct net *net, -					   struct in6_addr *prefix, int prefixlen, -					   struct in6_addr *gwaddr, int ifindex) +					   const struct in6_addr *prefix, int prefixlen, +					   const struct in6_addr *gwaddr, int ifindex)  {  	struct fib6_node *fn;  	struct rt6_info *rt = NULL; @@ -1778,8 +1796,8 @@ out:  }  static struct rt6_info *rt6_add_route_info(struct net *net, -					   struct in6_addr *prefix, int prefixlen, -					   struct in6_addr *gwaddr, int ifindex, +					   const struct in6_addr *prefix, int prefixlen, +					   const struct in6_addr *gwaddr, int ifindex,  					   unsigned pref)  {  	struct fib6_config cfg = { @@ -1807,7 +1825,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net,  }  #endif -struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) +struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev)  {  	struct rt6_info *rt;  	struct fib6_table *table; @@ -1829,7 +1847,7 @@ struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *d  	return rt;  } -struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr, +struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,  				     struct net_device *dev,  				     unsigned int pref)  { @@ -1994,7 +2012,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,  				    int anycast)  {  	struct net *net = dev_net(idev->dev); -	struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); +	struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, +					    net->loopback_dev);  	struct neighbour *neigh;  	if (rt == NULL) { @@ -2004,15 +2023,12 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,  		return ERR_PTR(-ENOMEM);  	} -	dev_hold(net->loopback_dev);  	in6_dev_hold(idev);  	rt->dst.flags = DST_HOST;  	rt->dst.input = ip6_input;  	rt->dst.output = ip6_output; -	rt->rt6i_dev = net->loopback_dev;  	rt->rt6i_idev = idev; -	dst_metric_set(&rt->dst, RTAX_HOPLIMIT, -1);  	rt->dst.obsolete = -1;  	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; @@ -2037,6 +2053,55 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,  	return rt;  } +int ip6_route_get_saddr(struct net *net, +			struct rt6_info *rt, +			const struct in6_addr *daddr, +			unsigned int prefs, +			struct in6_addr *saddr) +{ +	struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); +	int err = 0; +	if (rt->rt6i_prefsrc.plen) +		ipv6_addr_copy(saddr, &rt->rt6i_prefsrc.addr); +	else +		err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, +					 daddr, prefs, saddr); +	return err; +} + +/* remove deleted ip from prefsrc entries */ +struct arg_dev_net_ip { +	struct net_device *dev; +	struct net *net; +	struct in6_addr *addr; +}; + +static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) +{ +	struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; +	struct net *net = ((struct arg_dev_net_ip *)arg)->net; +	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; + +	if (((void *)rt->rt6i_dev == dev || dev == NULL) && +	    rt != net->ipv6.ip6_null_entry && +	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { +		/* remove prefsrc entry */ +		rt->rt6i_prefsrc.plen = 0; +	} +	return 0; +} + +void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) +{ +	struct net *net = dev_net(ifp->idev->dev); +	struct arg_dev_net_ip adni = { +		.dev = ifp->idev->dev, +		.net = net, +		.addr = &ifp->addr, +	}; +	fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); +} +  struct arg_dev_net {  	struct net_device *dev;  	struct net *net; @@ -2183,6 +2248,9 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,  		nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);  	} +	if (tb[RTA_PREFSRC]) +		nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); +  	if (tb[RTA_OIF])  		cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); @@ -2325,13 +2393,17 @@ static int rt6_fill_node(struct net *net,  #endif  			NLA_PUT_U32(skb, RTA_IIF, iif);  	} else if (dst) { -		struct inet6_dev *idev = ip6_dst_idev(&rt->dst);  		struct in6_addr saddr_buf; -		if (ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, -				       dst, 0, &saddr_buf) == 0) +		if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0)  			NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);  	} +	if (rt->rt6i_prefsrc.plen) { +		struct in6_addr saddr_buf; +		ipv6_addr_copy(&saddr_buf, &rt->rt6i_prefsrc.addr); +		NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); +	} +  	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)  		goto nla_put_failure;  |