net: Allow ipv6 proxies and arp proxies be shown with iproute2
authorTony Zelenoff <antonz@parallels.com>
Thu, 26 Jan 2012 22:28:58 +0000 (22:28 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Jan 2012 17:57:34 +0000 (12:57 -0500)
Add ability to return neighbour proxies list to caller if
it sent full ndmsg structure and has NTF_PROXY flag set.

Before this patch (and before iproute2 patches):
$ ip neigh add proxy 2001::1 dev eth0
$ ip -6 neigh show
$

After it and with applied iproute2 patches:
$ ip neigh add proxy 2001::1 dev eth0
$ ip -6 neigh show
2001::1 dev eth0  proxy
$

Compatibility with old versions of iproute2 is not broken,
kernel checks for incoming structure size and properly
works if old structure is came.

[v2]
* changed comments style.
* removed useless line with continue and curly bracket.
* changed incoming message size check from equal to more or
  equal.

CC: davem@davemloft.net
CC: kuznet@ms2.inr.ac.ru
CC: netdev@vger.kernel.org
CC: xemul@parallels.com
Signed-off-by: Tony Zelenoff <antonz@parallels.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/neighbour.c

index e287346e09343f1f315866fc7e77fb3770d927b0..f98ec444133aaf7a466d32ef2a176dbdf82e6438 100644 (file)
@@ -2165,6 +2165,35 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
+static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
+                           u32 pid, u32 seq, int type, unsigned int flags,
+                           struct neigh_table *tbl)
+{
+       struct nlmsghdr *nlh;
+       struct ndmsg *ndm;
+
+       nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       ndm = nlmsg_data(nlh);
+       ndm->ndm_family  = tbl->family;
+       ndm->ndm_pad1    = 0;
+       ndm->ndm_pad2    = 0;
+       ndm->ndm_flags   = pn->flags | NTF_PROXY;
+       ndm->ndm_type    = NDA_DST;
+       ndm->ndm_ifindex = pn->dev->ifindex;
+       ndm->ndm_state   = NUD_NONE;
+
+       NLA_PUT(skb, NDA_DST, tbl->key_len, pn->key);
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
 static void neigh_update_notify(struct neighbour *neigh)
 {
        call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
@@ -2214,23 +2243,78 @@ out:
        return rc;
 }
 
+static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
+                            struct netlink_callback *cb)
+{
+       struct pneigh_entry *n;
+       struct net *net = sock_net(skb->sk);
+       int rc, h, s_h = cb->args[3];
+       int idx, s_idx = idx = cb->args[4];
+
+       read_lock_bh(&tbl->lock);
+
+       for (h = 0; h <= PNEIGH_HASHMASK; h++) {
+               if (h < s_h)
+                       continue;
+               if (h > s_h)
+                       s_idx = 0;
+               for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) {
+                       if (dev_net(n->dev) != net)
+                               continue;
+                       if (idx < s_idx)
+                               goto next;
+                       if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
+                                           cb->nlh->nlmsg_seq,
+                                           RTM_NEWNEIGH,
+                                           NLM_F_MULTI, tbl) <= 0) {
+                               read_unlock_bh(&tbl->lock);
+                               rc = -1;
+                               goto out;
+                       }
+               next:
+                       idx++;
+               }
+       }
+
+       read_unlock_bh(&tbl->lock);
+       rc = skb->len;
+out:
+       cb->args[3] = h;
+       cb->args[4] = idx;
+       return rc;
+
+}
+
 static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct neigh_table *tbl;
        int t, family, s_t;
+       int proxy = 0;
+       int err = 0;
 
        read_lock(&neigh_tbl_lock);
        family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
+
+       /* check for full ndmsg structure presence, family member is
+        * the same for both structures
+        */
+       if (nlmsg_len(cb->nlh) >= sizeof(struct ndmsg) &&
+           ((struct ndmsg *) nlmsg_data(cb->nlh))->ndm_flags == NTF_PROXY)
+               proxy = 1;
+
        s_t = cb->args[0];
 
-       for (tbl = neigh_tables, t = 0; tbl; tbl = tbl->next, t++) {
+       for (tbl = neigh_tables, t = 0; tbl && (err >= 0);
+            tbl = tbl->next, t++) {
                if (t < s_t || (family && tbl->family != family))
                        continue;
                if (t > s_t)
                        memset(&cb->args[1], 0, sizeof(cb->args) -
                                                sizeof(cb->args[0]));
-               if (neigh_dump_table(tbl, skb, cb) < 0)
-                       break;
+               if (proxy)
+                       err = pneigh_dump_table(tbl, skb, cb);
+               else
+                       err = neigh_dump_table(tbl, skb, cb);
        }
        read_unlock(&neigh_tbl_lock);