I'm currently trying to retrieve reachable neighbors from the arp table in a user space program written in c. I've looked through the source code to the "ip neigh" command (ipneigh.c) and it appears that I should use the flag NUD_REACHABLE to get the reachable nodes.
However, when I look at the data returned from the kernel, I only have stale entries. In fact, no matter what I put for req.r.ndm_state it seems to return only entries marked as stale by ip neigh!
I've included a sample app below (modeled after: http://linux-hacks.blogspot.com/2009/01/sample-code-to-learn-netlink.html) so that you can compile it and try it yourself.
How do I get reachable nodes to display as well?
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <linux/rtnetlink.h>
int main(int argc, char** argv) {
struct {
struct nlmsghdr n;
struct ndmsg r;
} req;
struct rtattr *rta;
int status;
char buf[16384];
struct nlmsghdr *nlmp;
struct ndmsg *rtmp;
struct rtattr *rtatp;
int rtattrlen;
struct in_addr *inp;
char lladdr[6];
char ipv4string[INET_ADDRSTRLEN];
int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_REQUEST;
req.r.ndm_state = NUD_REACHABLE | NUD_DELAY; //TODO - get this working
req.n.nlmsg_type = RTM_GETNEIGH;
req.r.ndm_family = AF_INET;
rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
rta->rta_len = RTA_LENGTH(4);
status = send(fd, &req, req.n.nlmsg_len, 0);
if (status < 0) {
//error sending
}
status = recv(fd, buf, sizeof(buf), 0);
if (status < 0) {
//error receiving
}
for(nlmp = (struct nlmsghdr *)buf; status > sizeof(*nlmp);){
int len = nlmp->nlmsg_len;
int req_len = len - sizeof(*nlmp);
if (req_len<0 || len>status || !NLMSG_OK(nlmp, status)) {
printf("error");
}
rtmp = (struct ndmsg *)NLMSG_DATA(nlmp);
rtatp = (struct rtattr *)IFA_RTA(rtmp);
rtattrlen = IFA_PAYLOAD(nlmp);
for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
if(rtatp->rta_type == NDA_DST){
inp = (struct in_addr *)RTA_DATA(rtatp);
inet_ntop(AF_INET, inp, ipv4string, INET_ADDRSTRLEN);
printf("addr: %s",ipv4string);
}
if(rtatp->rta_type == NDA_LLADDR){
memcpy(lladdr, RTA_DATA(rtatp), 6);
printf(" MAC: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
lladdr[0],
lladdr[1],
lladdr[2],
lladdr[3],
lladdr[4],
lladdr[5]);
}
}
status -= NLMSG_ALIGN(len);
nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
}
return 0;
}