From beb2cd45109f28b9067d7fa902e49cbbc7667ff5 Mon Sep 17 00:00:00 2001
From: Cosmin Ratiu <cratiu@ixiacom.com>
Date: Fri, 15 Jan 2010 18:34:39 +0200
Subject: [PATCH] Extend RTM_GETNEIGH to allow getting more precise information.
RTM_GETNEIGH makes a complete dump of the neighbour table for a given
net and address family. The patch allows requests for a single entry
by specifying the device and IP address. This should increase
performance when there are many neighbour entries and a specific one is
desired.
Signed-off-by: Pinaki Chakrabarti <pchakrabarti@ixiacom.com>
Signed-off-by: Cosmin Ratiu <cratiu@ixiacom.com>
---
net/core/neighbour.c | 75 ++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 61 insertions(+), 14 deletions(-)
@@ -2119,24 +2119,71 @@ out:
static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
{
struct neigh_table *tbl;
- int t, family, s_t;
+ struct net *net = sock_net(skb->sk);
+ const struct nlmsghdr *nlh = cb->nlh;
+ int min_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ int family = ((struct rtgenmsg *) nlmsg_data(nlh))->rtgen_family;
- read_lock(&neigh_tbl_lock);
- family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
- s_t = cb->args[0];
+ if (nlh->nlmsg_len <= min_len) {
+ struct neigh_table *tbl;
+ int t, s_t;
- for (tbl = neigh_tables, t = 0; tbl; 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)
+ read_lock(&neigh_tbl_lock);
+ s_t = cb->args[0];
+
+ for (tbl = neigh_tables, t = 0; tbl; 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;
+ }
+ read_unlock(&neigh_tbl_lock);
+ cb->args[0] = t;
+ } else {
+ char key[16] = {0};
+ struct ndmsg *ndm = NULL;
+ struct net_device *dev = NULL;
+ struct rtattr *attr = NULL;
+ int attrlen;
+ struct neighbour *n;
+
+ if (cb->args[0])
+ goto out;
+ cb->args[0] = 1;
+
+ ndm = NLMSG_DATA(nlh);
+ if (!(dev = dev_get_by_index(net, ndm->ndm_ifindex)))
+ goto out;
+
+ attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+ attr = (void*)nlh + NLMSG_ALIGN(min_len);
+ if (!RTA_OK(attr, attrlen) || attr->rta_type != NDA_DST)
+ goto out;
+ memcpy(key, RTA_DATA(attr), attr->rta_len);
+
+ /* look for the neighbor */
+ read_lock(&neigh_tbl_lock);
+ for (tbl = neigh_tables; tbl; tbl = tbl->next) {
+ if (tbl->family != family)
+ continue;
+ n = neigh_lookup(tbl, key, dev);
+ if (!n)
+ continue;
+ if (n->nud_state & NUD_CONNECTED) {
+ neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
+ nlh->nlmsg_seq, RTM_NEWNEIGH, 0);
+ neigh_release(n);
+ }
break;
+ }
+ read_unlock(&neigh_tbl_lock);
+out:
+ if (dev)
+ dev_put(dev);
}
- read_unlock(&neigh_tbl_lock);
-
- cb->args[0] = t;
return skb->len;
}
--
1.6.5