|
@@ -4491,6 +4491,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
|
|
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
|
|
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
|
|
[IFA_FLAGS] = { .len = sizeof(u32) },
|
|
[IFA_FLAGS] = { .len = sizeof(u32) },
|
|
[IFA_RT_PRIORITY] = { .len = sizeof(u32) },
|
|
[IFA_RT_PRIORITY] = { .len = sizeof(u32) },
|
|
|
|
+ [IFA_TARGET_NETNSID] = { .type = NLA_S32 },
|
|
};
|
|
};
|
|
|
|
|
|
static int
|
|
static int
|
|
@@ -4794,7 +4795,8 @@ static inline int inet6_ifaddr_msgsize(void)
|
|
}
|
|
}
|
|
|
|
|
|
static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
|
|
static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
|
|
- u32 portid, u32 seq, int event, unsigned int flags)
|
|
|
|
|
|
+ u32 portid, u32 seq, int event, unsigned int flags,
|
|
|
|
+ int netnsid)
|
|
{
|
|
{
|
|
struct nlmsghdr *nlh;
|
|
struct nlmsghdr *nlh;
|
|
u32 preferred, valid;
|
|
u32 preferred, valid;
|
|
@@ -4806,6 +4808,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
|
|
put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope),
|
|
put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope),
|
|
ifa->idev->dev->ifindex);
|
|
ifa->idev->dev->ifindex);
|
|
|
|
|
|
|
|
+ if (netnsid >= 0 && nla_put_s32(skb, IFA_TARGET_NETNSID, netnsid))
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
if (!((ifa->flags&IFA_F_PERMANENT) &&
|
|
if (!((ifa->flags&IFA_F_PERMANENT) &&
|
|
(ifa->prefered_lft == INFINITY_LIFE_TIME))) {
|
|
(ifa->prefered_lft == INFINITY_LIFE_TIME))) {
|
|
preferred = ifa->prefered_lft;
|
|
preferred = ifa->prefered_lft;
|
|
@@ -4855,7 +4860,8 @@ error:
|
|
}
|
|
}
|
|
|
|
|
|
static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
|
|
static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
|
|
- u32 portid, u32 seq, int event, u16 flags)
|
|
|
|
|
|
+ u32 portid, u32 seq, int event, u16 flags,
|
|
|
|
+ int netnsid)
|
|
{
|
|
{
|
|
struct nlmsghdr *nlh;
|
|
struct nlmsghdr *nlh;
|
|
u8 scope = RT_SCOPE_UNIVERSE;
|
|
u8 scope = RT_SCOPE_UNIVERSE;
|
|
@@ -4868,6 +4874,9 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
|
|
if (!nlh)
|
|
if (!nlh)
|
|
return -EMSGSIZE;
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
+ if (netnsid >= 0 && nla_put_s32(skb, IFA_TARGET_NETNSID, netnsid))
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+
|
|
put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
|
|
put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
|
|
if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 ||
|
|
if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 ||
|
|
put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp,
|
|
put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp,
|
|
@@ -4881,7 +4890,8 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
|
|
}
|
|
}
|
|
|
|
|
|
static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca,
|
|
static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca,
|
|
- u32 portid, u32 seq, int event, unsigned int flags)
|
|
|
|
|
|
+ u32 portid, u32 seq, int event,
|
|
|
|
+ unsigned int flags, int netnsid)
|
|
{
|
|
{
|
|
struct net_device *dev = fib6_info_nh_dev(ifaca->aca_rt);
|
|
struct net_device *dev = fib6_info_nh_dev(ifaca->aca_rt);
|
|
int ifindex = dev ? dev->ifindex : 1;
|
|
int ifindex = dev ? dev->ifindex : 1;
|
|
@@ -4895,6 +4905,9 @@ static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca,
|
|
if (!nlh)
|
|
if (!nlh)
|
|
return -EMSGSIZE;
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
+ if (netnsid >= 0 && nla_put_s32(skb, IFA_TARGET_NETNSID, netnsid))
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+
|
|
put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
|
|
put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
|
|
if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 ||
|
|
if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 ||
|
|
put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp,
|
|
put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp,
|
|
@@ -4916,7 +4929,7 @@ enum addr_type_t {
|
|
/* called with rcu_read_lock() */
|
|
/* called with rcu_read_lock() */
|
|
static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
|
|
static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
|
|
struct netlink_callback *cb, enum addr_type_t type,
|
|
struct netlink_callback *cb, enum addr_type_t type,
|
|
- int s_ip_idx, int *p_ip_idx)
|
|
|
|
|
|
+ int s_ip_idx, int *p_ip_idx, int netnsid)
|
|
{
|
|
{
|
|
struct ifmcaddr6 *ifmca;
|
|
struct ifmcaddr6 *ifmca;
|
|
struct ifacaddr6 *ifaca;
|
|
struct ifacaddr6 *ifaca;
|
|
@@ -4936,7 +4949,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq,
|
|
cb->nlh->nlmsg_seq,
|
|
RTM_NEWADDR,
|
|
RTM_NEWADDR,
|
|
- NLM_F_MULTI);
|
|
|
|
|
|
+ NLM_F_MULTI, netnsid);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
break;
|
|
break;
|
|
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
|
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
|
@@ -4953,7 +4966,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq,
|
|
cb->nlh->nlmsg_seq,
|
|
RTM_GETMULTICAST,
|
|
RTM_GETMULTICAST,
|
|
- NLM_F_MULTI);
|
|
|
|
|
|
+ NLM_F_MULTI, netnsid);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -4968,7 +4981,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq,
|
|
cb->nlh->nlmsg_seq,
|
|
RTM_GETANYCAST,
|
|
RTM_GETANYCAST,
|
|
- NLM_F_MULTI);
|
|
|
|
|
|
+ NLM_F_MULTI, netnsid);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -4985,6 +4998,9 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
|
|
enum addr_type_t type)
|
|
enum addr_type_t type)
|
|
{
|
|
{
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net *net = sock_net(skb->sk);
|
|
|
|
+ struct nlattr *tb[IFA_MAX+1];
|
|
|
|
+ struct net *tgt_net = net;
|
|
|
|
+ int netnsid = -1;
|
|
int h, s_h;
|
|
int h, s_h;
|
|
int idx, ip_idx;
|
|
int idx, ip_idx;
|
|
int s_idx, s_ip_idx;
|
|
int s_idx, s_ip_idx;
|
|
@@ -4996,11 +5012,22 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
|
|
s_idx = idx = cb->args[1];
|
|
s_idx = idx = cb->args[1];
|
|
s_ip_idx = ip_idx = cb->args[2];
|
|
s_ip_idx = ip_idx = cb->args[2];
|
|
|
|
|
|
|
|
+ if (nlmsg_parse(cb->nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX,
|
|
|
|
+ ifa_ipv6_policy, NULL) >= 0) {
|
|
|
|
+ if (tb[IFA_TARGET_NETNSID]) {
|
|
|
|
+ netnsid = nla_get_s32(tb[IFA_TARGET_NETNSID]);
|
|
|
|
+
|
|
|
|
+ tgt_net = rtnl_get_net_ns_capable(skb->sk, netnsid);
|
|
|
|
+ if (IS_ERR(tgt_net))
|
|
|
|
+ return PTR_ERR(tgt_net);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
- cb->seq = atomic_read(&net->ipv6.dev_addr_genid) ^ net->dev_base_seq;
|
|
|
|
|
|
+ cb->seq = atomic_read(&tgt_net->ipv6.dev_addr_genid) ^ tgt_net->dev_base_seq;
|
|
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
|
|
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
|
|
idx = 0;
|
|
idx = 0;
|
|
- head = &net->dev_index_head[h];
|
|
|
|
|
|
+ head = &tgt_net->dev_index_head[h];
|
|
hlist_for_each_entry_rcu(dev, head, index_hlist) {
|
|
hlist_for_each_entry_rcu(dev, head, index_hlist) {
|
|
if (idx < s_idx)
|
|
if (idx < s_idx)
|
|
goto cont;
|
|
goto cont;
|
|
@@ -5012,7 +5039,7 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
|
|
goto cont;
|
|
goto cont;
|
|
|
|
|
|
if (in6_dump_addrs(idev, skb, cb, type,
|
|
if (in6_dump_addrs(idev, skb, cb, type,
|
|
- s_ip_idx, &ip_idx) < 0)
|
|
|
|
|
|
+ s_ip_idx, &ip_idx, netnsid) < 0)
|
|
goto done;
|
|
goto done;
|
|
cont:
|
|
cont:
|
|
idx++;
|
|
idx++;
|
|
@@ -5023,6 +5050,8 @@ done:
|
|
cb->args[0] = h;
|
|
cb->args[0] = h;
|
|
cb->args[1] = idx;
|
|
cb->args[1] = idx;
|
|
cb->args[2] = ip_idx;
|
|
cb->args[2] = ip_idx;
|
|
|
|
+ if (netnsid >= 0)
|
|
|
|
+ put_net(tgt_net);
|
|
|
|
|
|
return skb->len;
|
|
return skb->len;
|
|
}
|
|
}
|
|
@@ -5053,12 +5082,14 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|
struct netlink_ext_ack *extack)
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
{
|
|
struct net *net = sock_net(in_skb->sk);
|
|
struct net *net = sock_net(in_skb->sk);
|
|
|
|
+ struct net *tgt_net = net;
|
|
struct ifaddrmsg *ifm;
|
|
struct ifaddrmsg *ifm;
|
|
struct nlattr *tb[IFA_MAX+1];
|
|
struct nlattr *tb[IFA_MAX+1];
|
|
struct in6_addr *addr = NULL, *peer;
|
|
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;
|
|
|
|
+ int netnsid = -1;
|
|
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,
|
|
@@ -5066,15 +5097,24 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|
if (err < 0)
|
|
if (err < 0)
|
|
return err;
|
|
return err;
|
|
|
|
|
|
|
|
+ if (tb[IFA_TARGET_NETNSID]) {
|
|
|
|
+ netnsid = nla_get_s32(tb[IFA_TARGET_NETNSID]);
|
|
|
|
+
|
|
|
|
+ tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(in_skb).sk,
|
|
|
|
+ netnsid);
|
|
|
|
+ if (IS_ERR(tgt_net))
|
|
|
|
+ return PTR_ERR(tgt_net);
|
|
|
|
+ }
|
|
|
|
+
|
|
addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);
|
|
addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);
|
|
if (!addr)
|
|
if (!addr)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
ifm = nlmsg_data(nlh);
|
|
ifm = nlmsg_data(nlh);
|
|
if (ifm->ifa_index)
|
|
if (ifm->ifa_index)
|
|
- dev = dev_get_by_index(net, ifm->ifa_index);
|
|
|
|
|
|
+ dev = dev_get_by_index(tgt_net, ifm->ifa_index);
|
|
|
|
|
|
- ifa = ipv6_get_ifaddr(net, addr, dev, 1);
|
|
|
|
|
|
+ ifa = ipv6_get_ifaddr(tgt_net, addr, dev, 1);
|
|
if (!ifa) {
|
|
if (!ifa) {
|
|
err = -EADDRNOTAVAIL;
|
|
err = -EADDRNOTAVAIL;
|
|
goto errout;
|
|
goto errout;
|
|
@@ -5087,19 +5127,22 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|
}
|
|
}
|
|
|
|
|
|
err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).portid,
|
|
err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).portid,
|
|
- nlh->nlmsg_seq, RTM_NEWADDR, 0);
|
|
|
|
|
|
+ nlh->nlmsg_seq, RTM_NEWADDR, 0, netnsid);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
/* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
|
|
/* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
|
|
WARN_ON(err == -EMSGSIZE);
|
|
WARN_ON(err == -EMSGSIZE);
|
|
kfree_skb(skb);
|
|
kfree_skb(skb);
|
|
goto errout_ifa;
|
|
goto errout_ifa;
|
|
}
|
|
}
|
|
- err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
|
|
|
|
|
|
+ err = rtnl_unicast(skb, tgt_net, NETLINK_CB(in_skb).portid);
|
|
errout_ifa:
|
|
errout_ifa:
|
|
in6_ifa_put(ifa);
|
|
in6_ifa_put(ifa);
|
|
errout:
|
|
errout:
|
|
if (dev)
|
|
if (dev)
|
|
dev_put(dev);
|
|
dev_put(dev);
|
|
|
|
+ if (netnsid >= 0)
|
|
|
|
+ put_net(tgt_net);
|
|
|
|
+
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -5113,7 +5156,7 @@ static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
|
|
if (!skb)
|
|
if (!skb)
|
|
goto errout;
|
|
goto errout;
|
|
|
|
|
|
- err = inet6_fill_ifaddr(skb, ifa, 0, 0, event, 0);
|
|
|
|
|
|
+ err = inet6_fill_ifaddr(skb, ifa, 0, 0, event, 0, -1);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
/* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
|
|
/* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
|
|
WARN_ON(err == -EMSGSIZE);
|
|
WARN_ON(err == -EMSGSIZE);
|