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; | ||||
| 
 | ||||
| 	struct rcu_head		rcu; | ||||
| 	struct in6_addr		peer_addr; | ||||
| }; | ||||
| 
 | ||||
| struct ip6_sf_socklist { | ||||
|  |  | |||
|  | @ -2402,6 +2402,7 @@ err_exit: | |||
|  *	Manual configuration of address on an interface | ||||
|  */ | ||||
| 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, | ||||
| 			  __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->prefered_lft = prefered_lft; | ||||
| 		ifp->tstamp = jiffies; | ||||
| 		if (peer_pfx) | ||||
| 			ifp->peer_addr = *peer_pfx; | ||||
| 		spin_unlock_bh(&ifp->lock); | ||||
| 
 | ||||
| 		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; | ||||
| 
 | ||||
| 	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, | ||||
| 			     INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); | ||||
| 	rtnl_unlock(); | ||||
|  | @ -3610,17 +3613,19 @@ restart: | |||
| 	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; | ||||
| 
 | ||||
| 	*peer_pfx = NULL; | ||||
| 
 | ||||
| 	if (addr) | ||||
| 		pfx = nla_data(addr); | ||||
| 
 | ||||
| 	if (local) { | ||||
| 		if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) | ||||
| 			pfx = NULL; | ||||
| 		else | ||||
| 			*peer_pfx = pfx; | ||||
| 		pfx = nla_data(local); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -3639,7 +3644,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
| 	struct net *net = sock_net(skb->sk); | ||||
| 	struct ifaddrmsg *ifm; | ||||
| 	struct nlattr *tb[IFA_MAX+1]; | ||||
| 	struct in6_addr *pfx; | ||||
| 	struct in6_addr *pfx, *peer_pfx; | ||||
| 	int err; | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	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) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
|  | @ -3705,7 +3710,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
| 	struct net *net = sock_net(skb->sk); | ||||
| 	struct ifaddrmsg *ifm; | ||||
| 	struct nlattr *tb[IFA_MAX+1]; | ||||
| 	struct in6_addr *pfx; | ||||
| 	struct in6_addr *pfx, *peer_pfx; | ||||
| 	struct inet6_ifaddr *ifa; | ||||
| 	struct net_device *dev; | ||||
| 	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; | ||||
| 
 | ||||
| 	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) | ||||
| 		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 | ||||
| 		 * 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, | ||||
| 				      preferred_lft, valid_lft); | ||||
| 	} | ||||
|  | @ -3802,6 +3807,7 @@ static inline int rt_scope(int ifa_scope) | |||
| static inline int inet6_ifaddr_msgsize(void) | ||||
| { | ||||
| 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) | ||||
| 	       + nla_total_size(16) /* IFA_LOCAL */ | ||||
| 	       + nla_total_size(16) /* IFA_ADDRESS */ | ||||
| 	       + 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; | ||||
| 	} | ||||
| 
 | ||||
| 	if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 || | ||||
| 	    put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) { | ||||
| 		nlmsg_cancel(skb, nlh); | ||||
| 		return -EMSGSIZE; | ||||
| 	} | ||||
| 	if (ipv6_addr_type(&ifa->peer_addr) != IPV6_ADDR_ANY) { | ||||
| 		if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || | ||||
| 		    nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) | ||||
| 			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); | ||||
| 
 | ||||
| error: | ||||
| 	nlmsg_cancel(skb, nlh); | ||||
| 	return -EMSGSIZE; | ||||
| } | ||||
| 
 | ||||
| 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 ifaddrmsg *ifm; | ||||
| 	struct nlattr *tb[IFA_MAX+1]; | ||||
| 	struct in6_addr *addr = NULL; | ||||
| 	struct in6_addr *addr = NULL, *peer; | ||||
| 	struct net_device *dev = NULL; | ||||
| 	struct inet6_ifaddr *ifa; | ||||
| 	struct sk_buff *skb; | ||||
|  | @ -4056,7 +4071,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) | |||
| 	if (err < 0) | ||||
| 		goto errout; | ||||
| 
 | ||||
| 	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | ||||
| 	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer); | ||||
| 	if (addr == NULL) { | ||||
| 		err = -EINVAL; | ||||
| 		goto errout; | ||||
|  | @ -4564,11 +4579,26 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) | |||
| 			ip6_ins_rt(ifp->rt); | ||||
| 		if (ifp->idev->cnf.forwarding) | ||||
| 			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; | ||||
| 	case RTM_DELADDR: | ||||
| 		if (ifp->idev->cnf.forwarding) | ||||
| 			addrconf_leave_anycast(ifp); | ||||
| 		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); | ||||
| 
 | ||||
| 		if (ip6_del_rt(ifp->rt)) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nicolas Dichtel
				Nicolas Dichtel