diff options
author | Nicolas Dichtel <nicolas.dichtel@6wind.com> | 2013-05-16 18:32:00 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-05-19 18:09:26 -0400 |
commit | caeaba79009c2ee858c3b2bf8caf922cd719fead (patch) | |
tree | ee7e04fe068584f7e88a382c9ba615a71fffcfe9 /net/ipv6/addrconf.c | |
parent | 5199dfe531db19f3ac76542753849877e9854441 (diff) |
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>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 64 |
1 files changed, 47 insertions, 17 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d1ab6ab29a55..d684d23bc027 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -2402,6 +2402,7 @@ err_exit: | |||
2402 | * Manual configuration of address on an interface | 2402 | * Manual configuration of address on an interface |
2403 | */ | 2403 | */ |
2404 | static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, | 2404 | static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, |
2405 | const struct in6_addr *peer_pfx, | ||
2405 | unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, | 2406 | unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, |
2406 | __u32 valid_lft) | 2407 | __u32 valid_lft) |
2407 | { | 2408 | { |
@@ -2457,6 +2458,8 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p | |||
2457 | ifp->valid_lft = valid_lft; | 2458 | ifp->valid_lft = valid_lft; |
2458 | ifp->prefered_lft = prefered_lft; | 2459 | ifp->prefered_lft = prefered_lft; |
2459 | ifp->tstamp = jiffies; | 2460 | ifp->tstamp = jiffies; |
2461 | if (peer_pfx) | ||
2462 | ifp->peer_addr = *peer_pfx; | ||
2460 | spin_unlock_bh(&ifp->lock); | 2463 | spin_unlock_bh(&ifp->lock); |
2461 | 2464 | ||
2462 | addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, | 2465 | addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, |
@@ -2526,7 +2529,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg) | |||
2526 | return -EFAULT; | 2529 | return -EFAULT; |
2527 | 2530 | ||
2528 | rtnl_lock(); | 2531 | rtnl_lock(); |
2529 | err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, | 2532 | err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL, |
2530 | ireq.ifr6_prefixlen, IFA_F_PERMANENT, | 2533 | ireq.ifr6_prefixlen, IFA_F_PERMANENT, |
2531 | INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); | 2534 | INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); |
2532 | rtnl_unlock(); | 2535 | rtnl_unlock(); |
@@ -3610,18 +3613,20 @@ restart: | |||
3610 | rcu_read_unlock_bh(); | 3613 | rcu_read_unlock_bh(); |
3611 | } | 3614 | } |
3612 | 3615 | ||
3613 | static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) | 3616 | static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, |
3617 | struct in6_addr **peer_pfx) | ||
3614 | { | 3618 | { |
3615 | struct in6_addr *pfx = NULL; | 3619 | struct in6_addr *pfx = NULL; |
3616 | 3620 | ||
3621 | *peer_pfx = NULL; | ||
3622 | |||
3617 | if (addr) | 3623 | if (addr) |
3618 | pfx = nla_data(addr); | 3624 | pfx = nla_data(addr); |
3619 | 3625 | ||
3620 | if (local) { | 3626 | if (local) { |
3621 | if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) | 3627 | if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) |
3622 | pfx = NULL; | 3628 | *peer_pfx = pfx; |
3623 | else | 3629 | pfx = nla_data(local); |
3624 | pfx = nla_data(local); | ||
3625 | } | 3630 | } |
3626 | 3631 | ||
3627 | return pfx; | 3632 | return pfx; |
@@ -3639,7 +3644,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
3639 | struct net *net = sock_net(skb->sk); | 3644 | struct net *net = sock_net(skb->sk); |
3640 | struct ifaddrmsg *ifm; | 3645 | struct ifaddrmsg *ifm; |
3641 | struct nlattr *tb[IFA_MAX+1]; | 3646 | struct nlattr *tb[IFA_MAX+1]; |
3642 | struct in6_addr *pfx; | 3647 | struct in6_addr *pfx, *peer_pfx; |
3643 | int err; | 3648 | int err; |
3644 | 3649 | ||
3645 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); | 3650 | 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) | |||
3647 | return err; | 3652 | return err; |
3648 | 3653 | ||
3649 | ifm = nlmsg_data(nlh); | 3654 | ifm = nlmsg_data(nlh); |
3650 | pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | 3655 | pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); |
3651 | if (pfx == NULL) | 3656 | if (pfx == NULL) |
3652 | return -EINVAL; | 3657 | return -EINVAL; |
3653 | 3658 | ||
@@ -3705,7 +3710,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
3705 | struct net *net = sock_net(skb->sk); | 3710 | struct net *net = sock_net(skb->sk); |
3706 | struct ifaddrmsg *ifm; | 3711 | struct ifaddrmsg *ifm; |
3707 | struct nlattr *tb[IFA_MAX+1]; | 3712 | struct nlattr *tb[IFA_MAX+1]; |
3708 | struct in6_addr *pfx; | 3713 | struct in6_addr *pfx, *peer_pfx; |
3709 | struct inet6_ifaddr *ifa; | 3714 | struct inet6_ifaddr *ifa; |
3710 | struct net_device *dev; | 3715 | struct net_device *dev; |
3711 | u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; | 3716 | 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) | |||
3717 | return err; | 3722 | return err; |
3718 | 3723 | ||
3719 | ifm = nlmsg_data(nlh); | 3724 | ifm = nlmsg_data(nlh); |
3720 | pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | 3725 | pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); |
3721 | if (pfx == NULL) | 3726 | if (pfx == NULL) |
3722 | return -EINVAL; | 3727 | return -EINVAL; |
3723 | 3728 | ||
@@ -3745,7 +3750,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
3745 | * It would be best to check for !NLM_F_CREATE here but | 3750 | * It would be best to check for !NLM_F_CREATE here but |
3746 | * userspace alreay relies on not having to provide this. | 3751 | * userspace alreay relies on not having to provide this. |
3747 | */ | 3752 | */ |
3748 | return inet6_addr_add(net, ifm->ifa_index, pfx, | 3753 | return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx, |
3749 | ifm->ifa_prefixlen, ifa_flags, | 3754 | ifm->ifa_prefixlen, ifa_flags, |
3750 | preferred_lft, valid_lft); | 3755 | preferred_lft, valid_lft); |
3751 | } | 3756 | } |
@@ -3802,6 +3807,7 @@ static inline int rt_scope(int ifa_scope) | |||
3802 | static inline int inet6_ifaddr_msgsize(void) | 3807 | static inline int inet6_ifaddr_msgsize(void) |
3803 | { | 3808 | { |
3804 | return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) | 3809 | return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) |
3810 | + nla_total_size(16) /* IFA_LOCAL */ | ||
3805 | + nla_total_size(16) /* IFA_ADDRESS */ | 3811 | + nla_total_size(16) /* IFA_ADDRESS */ |
3806 | + nla_total_size(sizeof(struct ifa_cacheinfo)); | 3812 | + nla_total_size(sizeof(struct ifa_cacheinfo)); |
3807 | } | 3813 | } |
@@ -3840,13 +3846,22 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, | |||
3840 | valid = INFINITY_LIFE_TIME; | 3846 | valid = INFINITY_LIFE_TIME; |
3841 | } | 3847 | } |
3842 | 3848 | ||
3843 | if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 || | 3849 | if (ipv6_addr_type(&ifa->peer_addr) != IPV6_ADDR_ANY) { |
3844 | put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) { | 3850 | if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || |
3845 | nlmsg_cancel(skb, nlh); | 3851 | nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) |
3846 | return -EMSGSIZE; | 3852 | goto error; |
3847 | } | 3853 | } else |
3854 | if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0) | ||
3855 | goto error; | ||
3856 | |||
3857 | if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) | ||
3858 | goto error; | ||
3848 | 3859 | ||
3849 | return nlmsg_end(skb, nlh); | 3860 | return nlmsg_end(skb, nlh); |
3861 | |||
3862 | error: | ||
3863 | nlmsg_cancel(skb, nlh); | ||
3864 | return -EMSGSIZE; | ||
3850 | } | 3865 | } |
3851 | 3866 | ||
3852 | static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, | 3867 | 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) | |||
4046 | struct net *net = sock_net(in_skb->sk); | 4061 | struct net *net = sock_net(in_skb->sk); |
4047 | struct ifaddrmsg *ifm; | 4062 | struct ifaddrmsg *ifm; |
4048 | struct nlattr *tb[IFA_MAX+1]; | 4063 | struct nlattr *tb[IFA_MAX+1]; |
4049 | struct in6_addr *addr = NULL; | 4064 | struct in6_addr *addr = NULL, *peer; |
4050 | struct net_device *dev = NULL; | 4065 | struct net_device *dev = NULL; |
4051 | struct inet6_ifaddr *ifa; | 4066 | struct inet6_ifaddr *ifa; |
4052 | struct sk_buff *skb; | 4067 | struct sk_buff *skb; |
@@ -4056,7 +4071,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) | |||
4056 | if (err < 0) | 4071 | if (err < 0) |
4057 | goto errout; | 4072 | goto errout; |
4058 | 4073 | ||
4059 | addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); | 4074 | addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer); |
4060 | if (addr == NULL) { | 4075 | if (addr == NULL) { |
4061 | err = -EINVAL; | 4076 | err = -EINVAL; |
4062 | goto errout; | 4077 | goto errout; |
@@ -4564,11 +4579,26 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) | |||
4564 | ip6_ins_rt(ifp->rt); | 4579 | ip6_ins_rt(ifp->rt); |
4565 | if (ifp->idev->cnf.forwarding) | 4580 | if (ifp->idev->cnf.forwarding) |
4566 | addrconf_join_anycast(ifp); | 4581 | addrconf_join_anycast(ifp); |
4582 | if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) | ||
4583 | addrconf_prefix_route(&ifp->peer_addr, 128, | ||
4584 | ifp->idev->dev, 0, 0); | ||
4567 | break; | 4585 | break; |
4568 | case RTM_DELADDR: | 4586 | case RTM_DELADDR: |
4569 | if (ifp->idev->cnf.forwarding) | 4587 | if (ifp->idev->cnf.forwarding) |
4570 | addrconf_leave_anycast(ifp); | 4588 | addrconf_leave_anycast(ifp); |
4571 | addrconf_leave_solict(ifp->idev, &ifp->addr); | 4589 | addrconf_leave_solict(ifp->idev, &ifp->addr); |
4590 | if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) { | ||
4591 | struct rt6_info *rt; | ||
4592 | struct net_device *dev = ifp->idev->dev; | ||
4593 | |||
4594 | rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL, | ||
4595 | dev->ifindex, 1); | ||
4596 | if (rt) { | ||
4597 | dst_hold(&rt->dst); | ||
4598 | if (ip6_del_rt(rt)) | ||
4599 | dst_free(&rt->dst); | ||
4600 | } | ||
4601 | } | ||
4572 | dst_hold(&ifp->rt->dst); | 4602 | dst_hold(&ifp->rt->dst); |
4573 | 4603 | ||
4574 | if (ip6_del_rt(ifp->rt)) | 4604 | if (ip6_del_rt(ifp->rt)) |