From beb2cd45109f28b9067d7fa902e49cbbc7667ff5 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu 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 Signed-off-by: Cosmin Ratiu --- net/core/neighbour.c | 75 ++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 61 insertions(+), 14 deletions(-) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index f35377b..243fa5b 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -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