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 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
|
||||||
* 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,17 +3613,19 @@ static void addrconf_verify(unsigned long foo)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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…
Reference in a new issue