ipv6: add support of peer address
This patch adds the support of peer address for IPv6. For example, it is possible to specify the remote end of a 6inY tunnel. This was already possible in IPv4: ip addr add ip1 peer ip2 dev dev1 The peer address is specified with IFA_ADDRESS and the local address with IFA_LOCAL (like explained in include/uapi/linux/if_addr.h). Note that the API is not changed, because before this patch, it was not possible to specify two different addresses in IFA_LOCAL and IFA_REMOTE. There is a small change for the dump: if the peer is different from ::, IFA_ADDRESS will contain the peer address instead of the local address. Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
					parent
					
						
							
								5199dfe531
							
						
					
				
			
			
				commit
				
					
						caeaba7900
					
				
			
		
					 2 changed files with 48 additions and 17 deletions
				
			
		|  | @ -74,6 +74,7 @@ struct inet6_ifaddr { | ||||||
| 	bool			tokenized; | 	bool			tokenized; | ||||||
| 
 | 
 | ||||||
| 	struct rcu_head		rcu; | 	struct rcu_head		rcu; | ||||||
|  | 	struct in6_addr		peer_addr; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct ip6_sf_socklist { | struct ip6_sf_socklist { | ||||||
|  |  | ||||||
|  | @ -2402,6 +2402,7 @@ err_exit: | ||||||
|  *	Manual configuration of address on an interface |  *	Manual configuration of address on an interface | ||||||
|  */ |  */ | ||||||
| static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, | static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, | ||||||
|  | 			  const struct in6_addr *peer_pfx, | ||||||
| 			  unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, | 			  unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, | ||||||
| 			  __u32 valid_lft) | 			  __u32 valid_lft) | ||||||
| { | { | ||||||
|  | @ -2457,6 +2458,8 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p | ||||||
| 		ifp->valid_lft = valid_lft; | 		ifp->valid_lft = valid_lft; | ||||||
| 		ifp->prefered_lft = prefered_lft; | 		ifp->prefered_lft = prefered_lft; | ||||||
| 		ifp->tstamp = jiffies; | 		ifp->tstamp = jiffies; | ||||||
|  | 		if (peer_pfx) | ||||||
|  | 			ifp->peer_addr = *peer_pfx; | ||||||
| 		spin_unlock_bh(&ifp->lock); | 		spin_unlock_bh(&ifp->lock); | ||||||
| 
 | 
 | ||||||
| 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, | 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, | ||||||
|  | @ -2526,7 +2529,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg) | ||||||
| 		return -EFAULT; | 		return -EFAULT; | ||||||
| 
 | 
 | ||||||
| 	rtnl_lock(); | 	rtnl_lock(); | ||||||
| 	err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, | 	err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL, | ||||||
| 			     ireq.ifr6_prefixlen, IFA_F_PERMANENT, | 			     ireq.ifr6_prefixlen, IFA_F_PERMANENT, | ||||||
| 			     INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); | 			     INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); | ||||||
| 	rtnl_unlock(); | 	rtnl_unlock(); | ||||||
|  | @ -3610,18 +3613,20 @@ restart: | ||||||
| 	rcu_read_unlock_bh(); | 	rcu_read_unlock_bh(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) | static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, | ||||||
|  | 				     struct in6_addr **peer_pfx) | ||||||
| { | { | ||||||
| 	struct in6_addr *pfx = NULL; | 	struct in6_addr *pfx = NULL; | ||||||
| 
 | 
 | ||||||
|  | 	*peer_pfx = NULL; | ||||||
|  | 
 | ||||||
| 	if (addr) | 	if (addr) | ||||||
| 		pfx = nla_data(addr); | 		pfx = nla_data(addr); | ||||||
| 
 | 
 | ||||||
| 	if (local) { | 	if (local) { | ||||||
| 		if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) | 		if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) | ||||||
| 			pfx = NULL; | 			*peer_pfx = pfx; | ||||||
| 		else | 		pfx = nla_data(local); | ||||||
| 			pfx = nla_data(local); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return pfx; | 	return pfx; | ||||||
|  | @ -3639,7 +3644,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||||
| 	struct net *net = sock_net(skb->sk); | 	struct net *net = sock_net(skb->sk); | ||||||
| 	struct ifaddrmsg *ifm; | 	struct ifaddrmsg *ifm; | ||||||
| 	struct nlattr *tb[IFA_MAX+1]; | 	struct nlattr *tb[IFA_MAX+1]; | ||||||
| 	struct in6_addr *pfx; | 	struct in6_addr *pfx, *peer_pfx; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); | 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); | ||||||
|  | @ -3647,7 +3652,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
| 	ifm = nlmsg_data(nlh); | 	ifm = nlmsg_data(nlh); | ||||||
| 	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | 	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); | ||||||
| 	if (pfx == NULL) | 	if (pfx == NULL) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | @ -3705,7 +3710,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||||
| 	struct net *net = sock_net(skb->sk); | 	struct net *net = sock_net(skb->sk); | ||||||
| 	struct ifaddrmsg *ifm; | 	struct ifaddrmsg *ifm; | ||||||
| 	struct nlattr *tb[IFA_MAX+1]; | 	struct nlattr *tb[IFA_MAX+1]; | ||||||
| 	struct in6_addr *pfx; | 	struct in6_addr *pfx, *peer_pfx; | ||||||
| 	struct inet6_ifaddr *ifa; | 	struct inet6_ifaddr *ifa; | ||||||
| 	struct net_device *dev; | 	struct net_device *dev; | ||||||
| 	u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; | 	u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; | ||||||
|  | @ -3717,7 +3722,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
| 	ifm = nlmsg_data(nlh); | 	ifm = nlmsg_data(nlh); | ||||||
| 	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | 	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); | ||||||
| 	if (pfx == NULL) | 	if (pfx == NULL) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | @ -3745,7 +3750,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) | ||||||
| 		 * It would be best to check for !NLM_F_CREATE here but | 		 * It would be best to check for !NLM_F_CREATE here but | ||||||
| 		 * userspace alreay relies on not having to provide this. | 		 * userspace alreay relies on not having to provide this. | ||||||
| 		 */ | 		 */ | ||||||
| 		return inet6_addr_add(net, ifm->ifa_index, pfx, | 		return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx, | ||||||
| 				      ifm->ifa_prefixlen, ifa_flags, | 				      ifm->ifa_prefixlen, ifa_flags, | ||||||
| 				      preferred_lft, valid_lft); | 				      preferred_lft, valid_lft); | ||||||
| 	} | 	} | ||||||
|  | @ -3802,6 +3807,7 @@ static inline int rt_scope(int ifa_scope) | ||||||
| static inline int inet6_ifaddr_msgsize(void) | static inline int inet6_ifaddr_msgsize(void) | ||||||
| { | { | ||||||
| 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) | 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) | ||||||
|  | 	       + nla_total_size(16) /* IFA_LOCAL */ | ||||||
| 	       + nla_total_size(16) /* IFA_ADDRESS */ | 	       + nla_total_size(16) /* IFA_ADDRESS */ | ||||||
| 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); | 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); | ||||||
| } | } | ||||||
|  | @ -3840,13 +3846,22 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, | ||||||
| 		valid = INFINITY_LIFE_TIME; | 		valid = INFINITY_LIFE_TIME; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 || | 	if (ipv6_addr_type(&ifa->peer_addr) != IPV6_ADDR_ANY) { | ||||||
| 	    put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) { | 		if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || | ||||||
| 		nlmsg_cancel(skb, nlh); | 		    nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) | ||||||
| 		return -EMSGSIZE; | 			goto error; | ||||||
| 	} | 	} else | ||||||
|  | 		if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0) | ||||||
|  | 			goto error; | ||||||
|  | 
 | ||||||
|  | 	if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) | ||||||
|  | 		goto error; | ||||||
| 
 | 
 | ||||||
| 	return nlmsg_end(skb, nlh); | 	return nlmsg_end(skb, nlh); | ||||||
|  | 
 | ||||||
|  | error: | ||||||
|  | 	nlmsg_cancel(skb, nlh); | ||||||
|  | 	return -EMSGSIZE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, | static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, | ||||||
|  | @ -4046,7 +4061,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) | ||||||
| 	struct net *net = sock_net(in_skb->sk); | 	struct net *net = sock_net(in_skb->sk); | ||||||
| 	struct ifaddrmsg *ifm; | 	struct ifaddrmsg *ifm; | ||||||
| 	struct nlattr *tb[IFA_MAX+1]; | 	struct nlattr *tb[IFA_MAX+1]; | ||||||
| 	struct in6_addr *addr = NULL; | 	struct in6_addr *addr = NULL, *peer; | ||||||
| 	struct net_device *dev = NULL; | 	struct net_device *dev = NULL; | ||||||
| 	struct inet6_ifaddr *ifa; | 	struct inet6_ifaddr *ifa; | ||||||
| 	struct sk_buff *skb; | 	struct sk_buff *skb; | ||||||
|  | @ -4056,7 +4071,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| 		goto errout; | 		goto errout; | ||||||
| 
 | 
 | ||||||
| 	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | 	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer); | ||||||
| 	if (addr == NULL) { | 	if (addr == NULL) { | ||||||
| 		err = -EINVAL; | 		err = -EINVAL; | ||||||
| 		goto errout; | 		goto errout; | ||||||
|  | @ -4564,11 +4579,26 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) | ||||||
| 			ip6_ins_rt(ifp->rt); | 			ip6_ins_rt(ifp->rt); | ||||||
| 		if (ifp->idev->cnf.forwarding) | 		if (ifp->idev->cnf.forwarding) | ||||||
| 			addrconf_join_anycast(ifp); | 			addrconf_join_anycast(ifp); | ||||||
|  | 		if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) | ||||||
|  | 			addrconf_prefix_route(&ifp->peer_addr, 128, | ||||||
|  | 					      ifp->idev->dev, 0, 0); | ||||||
| 		break; | 		break; | ||||||
| 	case RTM_DELADDR: | 	case RTM_DELADDR: | ||||||
| 		if (ifp->idev->cnf.forwarding) | 		if (ifp->idev->cnf.forwarding) | ||||||
| 			addrconf_leave_anycast(ifp); | 			addrconf_leave_anycast(ifp); | ||||||
| 		addrconf_leave_solict(ifp->idev, &ifp->addr); | 		addrconf_leave_solict(ifp->idev, &ifp->addr); | ||||||
|  | 		if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) { | ||||||
|  | 			struct rt6_info *rt; | ||||||
|  | 			struct net_device *dev = ifp->idev->dev; | ||||||
|  | 
 | ||||||
|  | 			rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL, | ||||||
|  | 					dev->ifindex, 1); | ||||||
|  | 			if (rt) { | ||||||
|  | 				dst_hold(&rt->dst); | ||||||
|  | 				if (ip6_del_rt(rt)) | ||||||
|  | 					dst_free(&rt->dst); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		dst_hold(&ifp->rt->dst); | 		dst_hold(&ifp->rt->dst); | ||||||
| 
 | 
 | ||||||
| 		if (ip6_del_rt(ifp->rt)) | 		if (ip6_del_rt(ifp->rt)) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nicolas Dichtel
				Nicolas Dichtel