int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = arg; struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr * tb[NDA_MAX+1]; if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (r->ndm_family != AF_BRIDGE) return 0; if (filter_index && filter_index != r->ndm_ifindex) return 0; parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (n->nlmsg_type == RTM_DELNEIGH) fprintf(fp, "Deleted "); if (tb[NDA_LLADDR]) { SPRINT_BUF(b1); fprintf(fp, "%s ", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), RTA_PAYLOAD(tb[NDA_LLADDR]), ll_index_to_type(r->ndm_ifindex), b1, sizeof(b1))); } if (!filter_index && r->ndm_ifindex) fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); if (tb[NDA_DST]) { SPRINT_BUF(abuf); fprintf(fp, "dst %s ", format_host(AF_INET, RTA_PAYLOAD(tb[NDA_DST]), RTA_DATA(tb[NDA_DST]), abuf, sizeof(abuf))); } if (show_stats && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); int hz = get_user_hz(); fprintf(fp, " used %d/%d", ci->ndm_used/hz, ci->ndm_updated/hz); } if (r->ndm_flags & NTF_SELF) fprintf(fp, "self "); if (r->ndm_flags & NTF_MASTER) fprintf(fp, "master "); fprintf(fp, "%s\n", state_n2a(r->ndm_state)); return 0; }
static void rtnl_print_neigh(struct nlmsghdr *hdr) { struct ndmsg *ndm = NLMSG_DATA(hdr); uint32_t attrs_len = NDA_PAYLOAD(hdr); struct rtattr *attr = NDA_RTA(ndm); struct nda_cacheinfo *ci; int hz = get_user_hz(); char addr_str[256]; char hw_addr[30]; char states[256]; char flags[256]; if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(*ndm))) return; tprintf(" [ Neigh Family %d (%s%s%s)", ndm->ndm_family, colorize_start(bold), addr_family2str(ndm->ndm_family), colorize_end()); tprintf(", Link Index %d", ndm->ndm_ifindex); tprintf(", State %d (%s%s%s)", ndm->ndm_state, colorize_start(bold), flags2str(neigh_states, ndm->ndm_state, states, sizeof(states)), colorize_end()); tprintf(", Flags %d (%s%s%s)", ndm->ndm_flags, colorize_start(bold), flags2str(neigh_flags, ndm->ndm_flags, flags, sizeof(flags)), colorize_end()); tprintf(", Type %d (%s%s%s)", ndm->ndm_type, colorize_start(bold), route_type2str(ndm->ndm_type), colorize_end()); tprintf(" ]\n"); for (; RTA_OK(attr, attrs_len); attr = RTA_NEXT(attr, attrs_len)) { switch (attr->rta_type) { case NDA_DST: rta_fmt(attr, "Address %s", addr2str(ndm->ndm_family, RTA_DATA(attr), addr_str, sizeof(addr_str))); break; case NDA_LLADDR: rta_fmt(attr, "HW Address %s", device_addr2str(RTA_DATA(attr), RTA_LEN(attr), 0, hw_addr, sizeof(hw_addr))); break; case NDA_PROBES: rta_fmt(attr, "Probes %d", RTA_UINT32(attr)); break; case NDA_CACHEINFO: ci = RTA_DATA(attr); tprintf("\tA: Cache ("); tprintf("confirmed(%ds)", ci->ndm_confirmed / hz); tprintf(", used(%ds)", ci->ndm_used / hz); tprintf(", updated(%ds)", ci->ndm_updated / hz); tprintf(", refcnt(%d))", ci->ndm_refcnt); tprintf(", Len %d\n", RTA_LEN(attr)); break; default: rta_fmt(attr, "0x%x", attr->rta_type); break; } } }
static int parse_v4_neighbor( orc_options_t * options, struct nlmsghdr * nl_msg, orc_v4_neighbor_t * neigh) { struct ndmsg * nd_msg; struct rtattr * attr; int rtattr_len; int found_l3_addr = 0; nd_msg = (struct ndmsg *) NLMSG_DATA(nl_msg); /* extract first attribute */ attr = (struct rtattr *) NDA_RTA(nd_msg); rtattr_len = NDA_PAYLOAD(nl_msg); if (nd_msg->ndm_family != AF_INET) { /* TODO: add IPv6 support */ orc_debug("Ignoring IPv6 neighbor update: IPv6 unsupported\n"); return 0; } neigh->if_index = nd_msg->ndm_ifindex; /** TODO: ignore neighbor updates on non-orc interfaces */ if (nd_msg->ndm_state != NUD_STALE && nd_msg->ndm_state != NUD_PERMANENT && nd_msg->ndm_state != NUD_FAILED && nd_msg->ndm_state != NUD_REACHABLE) { orc_trace("Ignoring irrelevant neighbor update:" " not STALE/PERM/REACHABLE/FAILED\n"); return 0; } /** note: the RTA_NEXT() macro decrements rtattr_len each time */ for(; RTA_OK(attr, rtattr_len); attr = RTA_NEXT(attr, rtattr_len)) { switch (attr->rta_type) { case NDA_DST: found_l3_addr = 1; memcpy(&neigh->ip, RTA_DATA(attr), sizeof(neigh->ip)); orc_debug("Parsed IPv4 neighbor L3 addr: " IPV4_FORMAT "\n", IPV4_ADDR_PRINT(neigh->ip)); break; case NDA_LLADDR: neigh->mac_valid = 1; memcpy(neigh->mac, RTA_DATA(attr), 6); orc_debug("Parsed IPv4 neighbor L2 addr: " ETH_FORMAT "\n", ETH_ADDR_PRINT(neigh->mac)); break; case NDA_CACHEINFO: orc_trace("Ignoring irrelevant neighbor attr NDA_CACHEINFO\n"); break; case NDA_PROBES: orc_trace("Ignoring irrelevant neighbor attr NDA_PROBES\n"); break; default: /** TODO: decide if we actually need anything from here */ orc_warn("Skipping unhandled ipv4 neighbor attr: %d\n", attr->rta_type); }; } return found_l3_addr; }
int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = (FILE*)arg; struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr * tb[NDA_MAX+1]; char abuf[256]; if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH && n->nlmsg_type != RTM_GETNEIGH) { fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) return 0; if (filter.family && filter.family != r->ndm_family) return 0; if (filter.index && filter.index != r->ndm_ifindex) return 0; if (!(filter.state&r->ndm_state) && !(r->ndm_flags & NTF_PROXY) && (r->ndm_state || !(filter.state&0x100)) && (r->ndm_family != AF_DECnet)) return 0; parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[NDA_DST]) { if (filter.pfx.family) { inet_prefix dst; memset(&dst, 0, sizeof(dst)); dst.family = r->ndm_family; memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) return 0; } } if (filter.unused_only && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); if (ci->ndm_refcnt) return 0; } if (filter.flushb) { struct nlmsghdr *fn; if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { if (flush_update()) return -1; } fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELNEIGH; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++rth.seq; filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; } if (n->nlmsg_type == RTM_DELNEIGH) fprintf(fp, "delete "); else if (n->nlmsg_type == RTM_GETNEIGH) fprintf(fp, "miss "); if (tb[NDA_DST]) { fprintf(fp, "%s ", format_host(r->ndm_family, RTA_PAYLOAD(tb[NDA_DST]), RTA_DATA(tb[NDA_DST]), abuf, sizeof(abuf))); } if (!filter.index && r->ndm_ifindex) fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); if (tb[NDA_LLADDR]) { SPRINT_BUF(b1); fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), RTA_PAYLOAD(tb[NDA_LLADDR]), ll_index_to_type(r->ndm_ifindex), b1, sizeof(b1))); } if (r->ndm_flags & NTF_ROUTER) { fprintf(fp, " router"); } if (r->ndm_flags & NTF_PROXY) { fprintf(fp, " proxy"); } if (tb[NDA_CACHEINFO] && show_stats) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); int hz = get_user_hz(); if (ci->ndm_refcnt) printf(" ref %d", ci->ndm_refcnt); fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz, ci->ndm_confirmed/hz, ci->ndm_updated/hz); } if (tb[NDA_PROBES] && show_stats) { __u32 p = rta_getattr_u32(tb[NDA_PROBES]); fprintf(fp, " probes %u", p); } if (r->ndm_state) { int nud = r->ndm_state; fprintf(fp, " "); #define PRINT_FLAG(f) if (nud & NUD_##f) { \ nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); } PRINT_FLAG(INCOMPLETE); PRINT_FLAG(REACHABLE); PRINT_FLAG(STALE); PRINT_FLAG(DELAY); PRINT_FLAG(PROBE); PRINT_FLAG(FAILED); PRINT_FLAG(NOARP); PRINT_FLAG(PERMANENT); #undef PRINT_FLAG } fprintf(fp, "\n"); fflush(fp); return 0; }
int print_neigh(struct nlmsghdr *n, void *arg) { FILE *fp = (FILE *)arg; struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[NDA_MAX+1]; static int logit = 1; __u8 protocol = 0; if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH && n->nlmsg_type != RTM_GETNEIGH) { fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) return 0; if (filter.family && filter.family != r->ndm_family) return 0; if (filter.index && filter.index != r->ndm_ifindex) return 0; if (!(filter.state&r->ndm_state) && !(r->ndm_flags & NTF_PROXY) && !(r->ndm_flags & NTF_EXT_LEARNED) && (r->ndm_state || !(filter.state&0x100)) && (r->ndm_family != AF_DECnet)) return 0; if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) { if (logit) { logit = 0; fprintf(fp, "\nWARNING: Kernel does not support filtering by master device\n\n"); } } parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (inet_addr_match_rta(&filter.pfx, tb[NDA_DST])) return 0; if (tb[NDA_PROTOCOL]) protocol = rta_getattr_u8(tb[NDA_PROTOCOL]); if (filter.protocol && filter.protocol != protocol) return 0; if (filter.unused_only && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); if (ci->ndm_refcnt) return 0; } if (filter.flushb) { struct nlmsghdr *fn; if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { if (flush_update()) return -1; } fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELNEIGH; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++rth.seq; filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; } open_json_object(NULL); if (n->nlmsg_type == RTM_DELNEIGH) print_bool(PRINT_ANY, "deleted", "Deleted ", true); else if (n->nlmsg_type == RTM_GETNEIGH) print_null(PRINT_ANY, "miss", "%s ", "miss"); if (tb[NDA_DST]) { const char *dst; int family = r->ndm_family; if (family == AF_BRIDGE) { if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr)) family = AF_INET6; else family = AF_INET; } dst = format_host_rta(family, tb[NDA_DST]); print_color_string(PRINT_ANY, ifa_family_color(family), "dst", "%s ", dst); } if (!filter.index && r->ndm_ifindex) { if (!is_json_context()) fprintf(fp, "dev "); print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "%s ", ll_index_to_name(r->ndm_ifindex)); } if (tb[NDA_LLADDR]) { const char *lladdr; SPRINT_BUF(b1); lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), RTA_PAYLOAD(tb[NDA_LLADDR]), ll_index_to_type(r->ndm_ifindex), b1, sizeof(b1)); if (!is_json_context()) fprintf(fp, "lladdr "); print_color_string(PRINT_ANY, COLOR_MAC, "lladdr", "%s", lladdr); } if (r->ndm_flags & NTF_ROUTER) print_null(PRINT_ANY, "router", " %s", "router"); if (r->ndm_flags & NTF_PROXY) print_null(PRINT_ANY, "proxy", " %s", "proxy"); if (r->ndm_flags & NTF_EXT_LEARNED) print_null(PRINT_ANY, "extern_learn", " %s ", "extern_learn"); if (show_stats) { if (tb[NDA_CACHEINFO]) print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO])); if (tb[NDA_PROBES]) print_uint(PRINT_ANY, "probes", " probes %u", rta_getattr_u32(tb[NDA_PROBES])); } if (r->ndm_state) print_neigh_state(r->ndm_state); if (protocol) { SPRINT_BUF(b1); print_string(PRINT_ANY, "protocol", " proto %s ", rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); } print_string(PRINT_FP, NULL, "\n", ""); close_json_object(); fflush(stdout); return 0; }
static int neigh_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { struct rtnl_neigh neigh = RTNL_INIT_NEIGH(); struct rtattr *tb[NDA_MAX + 1]; struct nl_parser_param *pp = arg; size_t len; struct ndmsg *nm; int err; if (n->nlmsg_type != RTM_NEWNEIGH) return P_IGNORE; len = n->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg)); if (len < 0) return nl_error(EINVAL, "Message too short to be a " \ "neighbour message"); nm = NLMSG_DATA(n); err = nl_parse_rtattr(tb, NDA_MAX, NDA_RTA(nm), len); if (err < 0) return err; neigh.n_family = nm->ndm_family; neigh.n_ifindex = nm->ndm_ifindex; neigh.n_state = nm->ndm_state; neigh.n_flags = nm->ndm_flags; neigh.n_type = nm->ndm_type; if (tb[NDA_LLADDR]) { nl_copy_addr(&neigh.n_lladdr, tb[NDA_LLADDR]); neigh.n_mask |= NEIGH_HAS_LLADDR; } if (tb[NDA_DST]) { nl_copy_addr(&neigh.n_dst, tb[NDA_DST]); neigh.n_dst.a_family = neigh.n_family; neigh.n_mask |= NEIGH_HAS_DST; } if (tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci; if (RTA_PAYLOAD(tb[NDA_CACHEINFO]) < sizeof(*ci)) return nl_error(EINVAL, "cacheinfo TLV is too short"); ci = (struct nda_cacheinfo *) RTA_DATA(tb[NDA_CACHEINFO]); neigh.n_cacheinfo.nci_confirmed = ci->ndm_confirmed; neigh.n_cacheinfo.nci_used = ci->ndm_used; neigh.n_cacheinfo.nci_updated = ci->ndm_updated; neigh.n_cacheinfo.nci_refcnt = ci->ndm_refcnt; neigh.n_mask |= NEIGH_HAS_CACHEINFO; } err = pp->pp_cb((struct nl_common *) &neigh, pp); if (err < 0) return err; return P_ACCEPT; }
int do_one_request(struct nlmsghdr *n) { struct ndmsg *ndm = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr * tb[NDA_MAX+1]; struct dbkey key; DBT dbkey, dbdat; int do_acct = 0; if (n->nlmsg_type == NLMSG_DONE) { dbase->sync(dbase, 0); /* Now we have at least mirror of kernel db, so that * may start real resolution. */ do_sysctl_adjustments(); return 0; } if (n->nlmsg_type != RTM_GETNEIGH && n->nlmsg_type != RTM_NEWNEIGH) return 0; len -= NLMSG_LENGTH(sizeof(*ndm)); if (len < 0) return -1; if (ndm->ndm_family != AF_INET || (ifnum && !handle_if(ndm->ndm_ifindex)) || ndm->ndm_flags || ndm->ndm_type != RTN_UNICAST || !(ndm->ndm_state&~NUD_NOARP)) return 0; parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); if (!tb[NDA_DST]) return 0; key.iface = ndm->ndm_ifindex; memcpy(&key.addr, RTA_DATA(tb[NDA_DST]), 4); dbkey.data = &key; dbkey.size = sizeof(key); if (dbase->get(dbase, &dbkey, &dbdat, 0) != 0) { dbdat.data = 0; dbdat.size = 0; } if (n->nlmsg_type == RTM_GETNEIGH) { if (!(n->nlmsg_flags&NLM_F_REQUEST)) return 0; if (!(ndm->ndm_state&(NUD_PROBE|NUD_INCOMPLETE))) { stats.app_bad++; return 0; } if (ndm->ndm_state&NUD_PROBE) { /* If we get this, kernel still has some valid * address, but unicast probing failed and host * is either dead or changed its mac address. * Kernel is going to initiate broadcast resolution. * OK, we invalidate our information as well. */ if (dbdat.data && !IS_NEG(dbdat.data)) stats.app_neg++; dbase->del(dbase, &dbkey, 0); } else { /* If we get this kernel does not have any information. * If we have something tell this to kernel. */ stats.app_recv++; if (dbdat.data && !IS_NEG(dbdat.data)) { stats.app_success++; respond_to_kernel(key.iface, key.addr, dbdat.data, dbdat.size); return 0; } /* Sheeit! We have nothing to tell. */ /* If we have recent negative entry, be silent. */ if (dbdat.data && NEG_VALID(dbdat.data)) { if (NEG_CNT(dbdat.data) >= active_probing) { stats.app_suppressed++; return 0; } do_acct = 1; } } if (active_probing && queue_active_probe(ndm->ndm_ifindex, key.addr) == 0 && do_acct) { NEG_CNT(dbdat.data)++; dbase->put(dbase, &dbkey, &dbdat, 0); } } else if (n->nlmsg_type == RTM_NEWNEIGH) { if (n->nlmsg_flags&NLM_F_REQUEST) return 0; if (ndm->ndm_state&NUD_FAILED) { /* Kernel was not able to resolve. Host is dead. * Create negative entry if it is not present * or renew it if it is too old. */ if (!dbdat.data || !IS_NEG(dbdat.data) || !NEG_VALID(dbdat.data)) { __u8 ndata[6]; stats.kern_neg++; prepare_neg_entry(ndata, time(NULL)); dbdat.data = ndata; dbdat.size = sizeof(ndata); dbase->put(dbase, &dbkey, &dbdat, 0); } } else if (tb[NDA_LLADDR]) { if (dbdat.data && !IS_NEG(dbdat.data)) { if (memcmp(RTA_DATA(tb[NDA_LLADDR]), dbdat.data, dbdat.size) == 0) return 0; stats.kern_change++; } else { stats.kern_new++; } dbdat.data = RTA_DATA(tb[NDA_LLADDR]); dbdat.size = RTA_PAYLOAD(tb[NDA_LLADDR]); dbase->put(dbase, &dbkey, &dbdat, 0); } } return 0; }
/* family = AF_UNSPEC finds ARP table entries. family = AF_LOCAL finds MAC addresses. */ int iface_enumerate(int family, void *parm, int (*callback)()) { struct sockaddr_nl addr; struct nlmsghdr *h; ssize_t len; static unsigned int seq = 0; int callback_ok = 1; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; addr.nl_family = AF_NETLINK; addr.nl_pad = 0; addr.nl_groups = 0; addr.nl_pid = 0; /* address to kernel */ again: if (family == AF_UNSPEC) req.nlh.nlmsg_type = RTM_GETNEIGH; else if (family == AF_LOCAL) req.nlh.nlmsg_type = RTM_GETLINK; else req.nlh.nlmsg_type = RTM_GETADDR; req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = ++seq; req.g.rtgen_family = family; /* Don't block in recvfrom if send fails */ while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr)))); if (errno != 0) return 0; while (1) { if ((len = netlink_recv()) == -1) { if (errno == ENOBUFS) { sleep(1); goto again; } return 0; } for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) { /* May be multicast arriving async */ nl_async(h); } else if (h->nlmsg_seq != seq) { /* May be part of incomplete response to previous request after ENOBUFS. Drop it. */ continue; } else if (h->nlmsg_type == NLMSG_DONE) return callback_ok; else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) { struct ifaddrmsg *ifa = NLMSG_DATA(h); struct rtattr *rta = IFA_RTA(ifa); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)); if (ifa->ifa_family == family) { if (ifa->ifa_family == AF_INET) { struct in_addr netmask, addr, broadcast; char *label = NULL; netmask.s_addr = htonl(~(in_addr_t)0 << (32 - ifa->ifa_prefixlen)); addr.s_addr = 0; broadcast.s_addr = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFA_LOCAL) addr = *((struct in_addr *)(rta+1)); else if (rta->rta_type == IFA_BROADCAST) broadcast = *((struct in_addr *)(rta+1)); else if (rta->rta_type == IFA_LABEL) label = RTA_DATA(rta); rta = RTA_NEXT(rta, len1); } if (addr.s_addr && callback_ok) if (!((*callback)(addr, ifa->ifa_index, label, netmask, broadcast, parm))) callback_ok = 0; } #ifdef HAVE_IPV6 else if (ifa->ifa_family == AF_INET6) { struct in6_addr *addrp = NULL; u32 valid = 0, preferred = 0; int flags = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFA_ADDRESS) addrp = ((struct in6_addr *)(rta+1)); else if (rta->rta_type == IFA_CACHEINFO) { struct ifa_cacheinfo *ifc = (struct ifa_cacheinfo *)(rta+1); preferred = ifc->ifa_prefered; valid = ifc->ifa_valid; } rta = RTA_NEXT(rta, len1); } if (ifa->ifa_flags & IFA_F_TENTATIVE) flags |= IFACE_TENTATIVE; if (ifa->ifa_flags & IFA_F_DEPRECATED) flags |= IFACE_DEPRECATED; if (!(ifa->ifa_flags & IFA_F_TEMPORARY)) flags |= IFACE_PERMANENT; if (addrp && callback_ok) if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), (int)(ifa->ifa_index), flags, (int) preferred, (int)valid, parm))) callback_ok = 0; } #endif } } else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC) { struct ndmsg *neigh = NLMSG_DATA(h); struct rtattr *rta = NDA_RTA(neigh); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh)); size_t maclen = 0; char *inaddr = NULL, *mac = NULL; while (RTA_OK(rta, len1)) { if (rta->rta_type == NDA_DST) inaddr = (char *)(rta+1); else if (rta->rta_type == NDA_LLADDR) { maclen = rta->rta_len - sizeof(struct rtattr); mac = (char *)(rta+1); } rta = RTA_NEXT(rta, len1); } if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) && inaddr && mac && callback_ok) if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) callback_ok = 0; } #ifdef HAVE_DHCP6 else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL) { struct ifinfomsg *link = NLMSG_DATA(h); struct rtattr *rta = IFLA_RTA(link); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link)); char *mac = NULL; size_t maclen = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFLA_ADDRESS) { maclen = rta->rta_len - sizeof(struct rtattr); mac = (char *)(rta+1); } rta = RTA_NEXT(rta, len1); } if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && !((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm))) callback_ok = 0; } #endif } }
int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = (FILE*)arg; struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr * tb[NDA_MAX+1]; char abuf[256]; if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) return 0; if (filter.family && filter.family != r->ndm_family) return 0; if (filter.index && filter.index != r->ndm_ifindex) return 0; if (!(filter.state&r->ndm_state) && (r->ndm_state || !(filter.state&0x100)) && (r->ndm_family != AF_DECnet)) return 0; memset(tb, 0, sizeof(tb)); parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (tb[NDA_DST]) { if (filter.pfx.family) { inet_prefix dst; memset(&dst, 0, sizeof(dst)); dst.family = r->ndm_family; memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) return 0; } } if (filter.unused_only && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); if (ci->ndm_refcnt) return 0; } if (filter.flushb) { struct nlmsghdr *fn; if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { if (flush_update()) return -1; } fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELNEIGH; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++filter.rth->seq; filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; } if (tb[NDA_DST]) { fprintf(fp, "%s ", format_host(r->ndm_family, RTA_PAYLOAD(tb[NDA_DST]), RTA_DATA(tb[NDA_DST]), abuf, sizeof(abuf))); } if (!filter.index && r->ndm_ifindex) fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); if (tb[NDA_LLADDR]) { SPRINT_BUF(b1); fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), RTA_PAYLOAD(tb[NDA_LLADDR]), ll_index_to_type(r->ndm_ifindex), b1, sizeof(b1))); } if (r->ndm_flags & NTF_ROUTER) { fprintf(fp, " router"); } if (tb[NDA_CACHEINFO] && show_stats) { static int hz; struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); if (!hz) hz = get_hz(); if (ci->ndm_refcnt) printf(" ref %d", ci->ndm_refcnt); fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz, ci->ndm_confirmed/hz, ci->ndm_updated/hz); } if (r->ndm_state) { SPRINT_BUF(b1); fprintf(fp, " nud %s", nud_state_n2a(r->ndm_state, b1, sizeof(b1))); } fprintf(fp, "\n"); fflush(fp); return 0; }
/* family = AF_UNSPEC finds ARP table entries. family = AF_LOCAL finds MAC addresses. */ int iface_enumerate(int family, void *parm, int (*callback)()) { struct sockaddr_nl addr; struct nlmsghdr *h; ssize_t len; static unsigned int seq = 0; int callback_ok = 1, newaddr = 0; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; addr.nl_family = AF_NETLINK; addr.nl_pad = 0; addr.nl_groups = 0; addr.nl_pid = 0; /* address to kernel */ again: if (family == AF_UNSPEC) req.nlh.nlmsg_type = RTM_GETNEIGH; else if (family == AF_LOCAL) req.nlh.nlmsg_type = RTM_GETLINK; else req.nlh.nlmsg_type = RTM_GETADDR; req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = ++seq; req.g.rtgen_family = family; /* Don't block in recvfrom if send fails */ while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send()); if (len == -1) return 0; while (1) { if ((len = netlink_recv()) == -1) { if (errno == ENOBUFS) { sleep(1); goto again; } return 0; } for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) { /* May be multicast arriving async */ if (nl_async(h) && option_bool(OPT_CLEVERBIND)) newaddr = 1; } else if (h->nlmsg_type == NLMSG_DONE) { /* handle async new interface address arrivals, these have to be done after we complete as we're not re-entrant */ if (newaddr) { enumerate_interfaces(); create_bound_listeners(0); } return callback_ok; } else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) { struct ifaddrmsg *ifa = NLMSG_DATA(h); struct rtattr *rta = IFA_RTA(ifa); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)); if (ifa->ifa_family == family) { if (ifa->ifa_family == AF_INET) { struct in_addr netmask, addr, broadcast; netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen)); addr.s_addr = 0; broadcast.s_addr = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFA_LOCAL) addr = *((struct in_addr *)(rta+1)); else if (rta->rta_type == IFA_BROADCAST) broadcast = *((struct in_addr *)(rta+1)); rta = RTA_NEXT(rta, len1); } if (addr.s_addr && callback_ok) if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm))) callback_ok = 0; } #ifdef HAVE_IPV6 else if (ifa->ifa_family == AF_INET6) { struct in6_addr *addrp = NULL; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFA_ADDRESS) addrp = ((struct in6_addr *)(rta+1)); rta = RTA_NEXT(rta, len1); } if (addrp && callback_ok) if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), (int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm))) callback_ok = 0; } #endif } } else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC) { struct ndmsg *neigh = NLMSG_DATA(h); struct rtattr *rta = NDA_RTA(neigh); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh)); size_t maclen = 0; char *inaddr = NULL, *mac = NULL; while (RTA_OK(rta, len1)) { if (rta->rta_type == NDA_DST) inaddr = (char *)(rta+1); else if (rta->rta_type == NDA_LLADDR) { maclen = rta->rta_len - sizeof(struct rtattr); mac = (char *)(rta+1); } rta = RTA_NEXT(rta, len1); } if (inaddr && mac && callback_ok) if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) callback_ok = 0; } #ifdef HAVE_DHCP6 else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL) { struct ifinfomsg *link = NLMSG_DATA(h); struct rtattr *rta = IFLA_RTA(link); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link)); char *mac = NULL; size_t maclen = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFLA_ADDRESS) { maclen = rta->rta_len - sizeof(struct rtattr); mac = (char *)(rta+1); } rta = RTA_NEXT(rta, len1); } if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && !((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm))) callback_ok = 0; } #endif } }
static int addr2mac_init_linux() { struct nlmsghdr *nlmsg; struct ndmsg *ndmsg; struct rtattr *rta, *tb[NDA_MAX]; struct sockaddr_nl snl; struct msghdr msg; struct iovec iov; struct timeval tv; pid_t pid; uint8_t buf[16384]; ssize_t ssize; ssize_t len; int rlen; int fd = -1; void *ip, *mac; int iptype; pid = getpid(); memset(buf, 0, sizeof(buf)); nlmsg = (struct nlmsghdr *)buf; nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); nlmsg->nlmsg_type = RTM_GETNEIGH; nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; nlmsg->nlmsg_seq = 0; nlmsg->nlmsg_pid = pid; ndmsg = NLMSG_DATA(nlmsg); ndmsg->ndm_family = AF_UNSPEC; if((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) == -1) { printerror(errno, strerror, __func__, "could not open netlink"); goto err; } len = nlmsg->nlmsg_len; if((ssize = send(fd, buf, len, 0)) < len) { if(ssize == -1) { printerror(errno, strerror, __func__, "could not send netlink"); } goto err; } for(;;) { iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_name = &snl; msg.msg_namelen = sizeof(snl); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; if((len = recvmsg(fd, &msg, 0)) == -1) { if(errno == EINTR) continue; printerror(errno, strerror, __func__, "could not recvmsg"); goto err; } gettimeofday_wrap(&tv); nlmsg = (struct nlmsghdr *)buf; while(NLMSG_OK(nlmsg, len)) { if(nlmsg->nlmsg_pid != pid || nlmsg->nlmsg_seq != 0) { goto skip; } if(nlmsg->nlmsg_type == NLMSG_DONE) { goto done; } if(nlmsg->nlmsg_type == NLMSG_ERROR) { scamper_debug(__func__, "nlmsg error"); goto err; } /* get current neighbour entries only */ if(nlmsg->nlmsg_type != RTM_NEWNEIGH) { goto skip; } /* make sure the address is reachable */ ndmsg = NLMSG_DATA(nlmsg); if((ndmsg->ndm_state & NUD_REACHABLE) == 0) { goto skip; } /* make sure we can process this address type */ switch(ndmsg->ndm_family) { case AF_INET: iptype = SCAMPER_ADDR_TYPE_IPV4; break; case AF_INET6: iptype = SCAMPER_ADDR_TYPE_IPV6; break; default: goto skip; } /* fill a table with parameters from the payload */ memset(tb, 0, sizeof(tb)); rlen = nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg)); for(rta = NDA_RTA(ndmsg); RTA_OK(rta,rlen); rta = RTA_NEXT(rta,rlen)) { if(rta->rta_type >= NDA_MAX) continue; tb[rta->rta_type] = rta; } /* * skip if we don't have a destination IP address, or if * we don't have an ethernet mac address */ if(tb[NDA_DST] == NULL || tb[NDA_LLADDR] == NULL || RTA_PAYLOAD(tb[NDA_LLADDR]) != 6) { goto skip; } ip = RTA_DATA(tb[NDA_DST]); mac = RTA_DATA(tb[NDA_LLADDR]); addr2mac_add(ndmsg->ndm_ifindex, iptype, ip, mac, tv.tv_sec+600); skip: nlmsg = NLMSG_NEXT(nlmsg, len); } } done: close(fd); return 0; err: close(fd); return -1; }
static int send_probe(int ifindex, __u32 addr) { struct ifreq ifr = { .ifr_ifindex = ifindex }; struct sockaddr_in dst = { .sin_family = AF_INET, .sin_port = htons(1025), .sin_addr.s_addr = addr, }; socklen_t len; unsigned char buf[256]; struct arphdr *ah = (struct arphdr *)buf; unsigned char *p = (unsigned char *)(ah+1); struct sockaddr_ll sll = { .sll_family = AF_PACKET, .sll_ifindex = ifindex, .sll_protocol = htons(ETH_P_ARP), }; if (ioctl(udp_sock, SIOCGIFNAME, &ifr)) return -1; if (ioctl(udp_sock, SIOCGIFHWADDR, &ifr)) return -1; if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) return -1; if (setsockopt(udp_sock, SOL_SOCKET, SO_BINDTODEVICE, ifr.ifr_name, strlen(ifr.ifr_name)+1) < 0) return -1; if (connect(udp_sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) return -1; len = sizeof(dst); if (getsockname(udp_sock, (struct sockaddr *)&dst, &len) < 0) return -1; ah->ar_hrd = htons(ifr.ifr_hwaddr.sa_family); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = 6; ah->ar_pln = 4; ah->ar_op = htons(ARPOP_REQUEST); memcpy(p, ifr.ifr_hwaddr.sa_data, ah->ar_hln); p += ah->ar_hln; memcpy(p, &dst.sin_addr, 4); p += 4; memset(sll.sll_addr, 0xFF, sizeof(sll.sll_addr)); memcpy(p, &sll.sll_addr, ah->ar_hln); p += ah->ar_hln; memcpy(p, &addr, 4); p += 4; if (sendto(pset[0].fd, buf, p-buf, 0, (struct sockaddr *)&sll, sizeof(sll)) < 0) return -1; stats.probes_sent++; return 0; } /* Be very tough on sending probes: 1 per second with burst of 3. */ static int queue_active_probe(int ifindex, __u32 addr) { static struct timeval prev; static int buckets; struct timeval now; gettimeofday(&now, NULL); if (prev.tv_sec) { int diff = (now.tv_sec-prev.tv_sec)*1000+(now.tv_usec-prev.tv_usec)/1000; buckets += diff; } else { buckets = broadcast_burst; } if (buckets > broadcast_burst) buckets = broadcast_burst; if (buckets >= broadcast_rate && !send_probe(ifindex, addr)) { buckets -= broadcast_rate; prev = now; return 0; } stats.probes_suppressed++; return -1; } static int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen) { struct { struct nlmsghdr n; struct ndmsg ndm; char buf[256]; } req = { .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), .n.nlmsg_flags = NLM_F_REQUEST, .n.nlmsg_type = RTM_NEWNEIGH, .ndm.ndm_family = AF_INET, .ndm.ndm_state = NUD_STALE, .ndm.ndm_ifindex = ifindex, .ndm.ndm_type = RTN_UNICAST, }; addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4); addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); return rtnl_send(&rth, &req, req.n.nlmsg_len) <= 0; } static void prepare_neg_entry(__u8 *ndata, __u32 stamp) { ndata[0] = 0xFF; ndata[1] = 0; ndata[2] = stamp>>24; ndata[3] = stamp>>16; ndata[4] = stamp>>8; ndata[5] = stamp; } static int do_one_request(struct nlmsghdr *n) { struct ndmsg *ndm = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[NDA_MAX+1]; struct dbkey key; DBT dbkey, dbdat; int do_acct = 0; if (n->nlmsg_type == NLMSG_DONE) { dbase->sync(dbase, 0); /* Now we have at least mirror of kernel db, so that * may start real resolution. */ do_sysctl_adjustments(); return 0; } if (n->nlmsg_type != RTM_GETNEIGH && n->nlmsg_type != RTM_NEWNEIGH) return 0; len -= NLMSG_LENGTH(sizeof(*ndm)); if (len < 0) return -1; if (ndm->ndm_family != AF_INET || (ifnum && !handle_if(ndm->ndm_ifindex)) || ndm->ndm_flags || ndm->ndm_type != RTN_UNICAST || !(ndm->ndm_state&~NUD_NOARP)) return 0; parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); if (!tb[NDA_DST]) return 0; key.iface = ndm->ndm_ifindex; memcpy(&key.addr, RTA_DATA(tb[NDA_DST]), 4); dbkey.data = &key; dbkey.size = sizeof(key); if (dbase->get(dbase, &dbkey, &dbdat, 0) != 0) { dbdat.data = 0; dbdat.size = 0; } if (n->nlmsg_type == RTM_GETNEIGH) { if (!(n->nlmsg_flags&NLM_F_REQUEST)) return 0; if (!(ndm->ndm_state&(NUD_PROBE|NUD_INCOMPLETE))) { stats.app_bad++; return 0; } if (ndm->ndm_state&NUD_PROBE) { /* If we get this, kernel still has some valid * address, but unicast probing failed and host * is either dead or changed its mac address. * Kernel is going to initiate broadcast resolution. * OK, we invalidate our information as well. */ if (dbdat.data && !IS_NEG(dbdat.data)) stats.app_neg++; dbase->del(dbase, &dbkey, 0); } else { /* If we get this kernel does not have any information. * If we have something tell this to kernel. */ stats.app_recv++; if (dbdat.data && !IS_NEG(dbdat.data)) { stats.app_success++; respond_to_kernel(key.iface, key.addr, dbdat.data, dbdat.size); return 0; } /* Sheeit! We have nothing to tell. */ /* If we have recent negative entry, be silent. */ if (dbdat.data && NEG_VALID(dbdat.data)) { if (NEG_CNT(dbdat.data) >= active_probing) { stats.app_suppressed++; return 0; } do_acct = 1; } } if (active_probing && queue_active_probe(ndm->ndm_ifindex, key.addr) == 0 && do_acct) { NEG_CNT(dbdat.data)++; dbase->put(dbase, &dbkey, &dbdat, 0); } } else if (n->nlmsg_type == RTM_NEWNEIGH) { if (n->nlmsg_flags&NLM_F_REQUEST) return 0; if (ndm->ndm_state&NUD_FAILED) { /* Kernel was not able to resolve. Host is dead. * Create negative entry if it is not present * or renew it if it is too old. */ if (!dbdat.data || !IS_NEG(dbdat.data) || !NEG_VALID(dbdat.data)) { __u8 ndata[6]; stats.kern_neg++; prepare_neg_entry(ndata, time(NULL)); dbdat.data = ndata; dbdat.size = sizeof(ndata); dbase->put(dbase, &dbkey, &dbdat, 0); } } else if (tb[NDA_LLADDR]) { if (dbdat.data && !IS_NEG(dbdat.data)) { if (memcmp(RTA_DATA(tb[NDA_LLADDR]), dbdat.data, dbdat.size) == 0) return 0; stats.kern_change++; } else { stats.kern_new++; } dbdat.data = RTA_DATA(tb[NDA_LLADDR]); dbdat.size = RTA_PAYLOAD(tb[NDA_LLADDR]); dbase->put(dbase, &dbkey, &dbdat, 0); } } return 0; } static void load_initial_table(void) { if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETNEIGH) < 0) { perror("dump request failed"); exit(1); } } static void get_kern_msg(void) { int status; struct nlmsghdr *h; struct sockaddr_nl nladdr = {}; struct iovec iov; char buf[8192]; struct msghdr msg = { (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; iov.iov_base = buf; iov.iov_len = sizeof(buf); status = recvmsg(rth.fd, &msg, MSG_DONTWAIT); if (status <= 0) return; if (msg.msg_namelen != sizeof(nladdr)) return; if (nladdr.nl_pid) return; for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); if (l < 0 || len > status) return; if (do_one_request(h) < 0) return; status -= NLMSG_ALIGN(len); h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } }