X-Git-Url: http://git.onelab.eu/?p=iproute2.git;a=blobdiff_plain;f=misc%2Farpd.c;fp=misc%2Farpd.c;h=0000000000000000000000000000000000000000;hp=4fd226ec85c748a2b82be2ee2c1787a03df63f26;hb=3331a68859fd71047bb1f309048960b48eab2d83;hpb=2bd4a72f2100be7ad7d9518cb1d49bb2a5b71994 diff --git a/misc/arpd.c b/misc/arpd.c deleted file mode 100644 index 4fd226e..0000000 --- a/misc/arpd.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - * arpd.c ARP helper daemon. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Authors: Alexey Kuznetsov, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libnetlink.h" -#include "utils.h" - -int resolve_hosts; - -DB *dbase; -char *dbname = "/var/lib/arpd/arpd.db"; - -int ifnum; -int *ifvec; -char **ifnames; - -struct dbkey -{ - __u32 iface; - __u32 addr; -}; - -#define IS_NEG(x) (((__u8*)(x))[0] == 0xFF) -#define NEG_TIME(x) (((x)[2]<<24)|((x)[3]<<16)|((x)[4]<<8)|(x)[5]) -#define NEG_AGE(x) ((__u32)time(NULL) - NEG_TIME((__u8*)x)) -#define NEG_VALID(x) (NEG_AGE(x) < negative_timeout) -#define NEG_CNT(x) (((__u8*)(x))[1]) - -struct rtnl_handle rth; - -struct pollfd pset[2]; -int udp_sock = -1; - -volatile int do_exit; -volatile int do_sync; -volatile int do_stats; - -struct { - unsigned long arp_new; - unsigned long arp_change; - - unsigned long app_recv; - unsigned long app_success; - unsigned long app_bad; - unsigned long app_neg; - unsigned long app_suppressed; - - unsigned long kern_neg; - unsigned long kern_new; - unsigned long kern_change; - - unsigned long probes_sent; - unsigned long probes_suppressed; -} stats; - -int active_probing; -int negative_timeout = 60; -int no_kernel_broadcasts; -int broadcast_rate = 1000; -int broadcast_burst = 3000; - -void usage(void) -{ - fprintf(stderr, -"Usage: arpd [ -lk ] [ -a N ] [ -b dbase ] [ -f file ] [ interfaces ]\n"); - exit(1); -} - -int handle_if(int ifindex) -{ - int i; - - if (ifnum == 0) - return 1; - - for (i=0; i=2 ? 1 : 3-active_probing); - fputs(buf, fp); - fclose(fp); - } - } - - sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]); - if ((fp = fopen(buf, "w")) != NULL) { - sprintf(buf, "%d\n", active_probing<=1 ? 1 : active_probing); - fputs(buf, fp); - fclose(fp); - } - } - sysctl_adjusted = 1; -} - -void undo_sysctl_adjustments(void) -{ - int i; - - if (!sysctl_adjusted) - return; - - for (i=0; iar_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; - - sll.sll_family = AF_PACKET; - memset(sll.sll_addr, 0xFF, sizeof(sll.sll_addr)); - sll.sll_ifindex = ifindex; - sll.sll_protocol = htons(ETH_P_ARP); - 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. */ - -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; -} - -int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen) -{ - struct { - struct nlmsghdr n; - struct ndmsg ndm; - char buf[256]; - } req; - - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_NEWNEIGH; - req.ndm.ndm_family = AF_INET; - req.ndm.ndm_state = NUD_STALE; - req.ndm.ndm_ifindex = ifindex; - req.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, (char*)&req, req.n.nlmsg_len) <= 0; -} - -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; -} - - -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; -} - -void load_initial_table(void) -{ - rtnl_wilddump_request(&rth, AF_INET, RTM_GETNEIGH); -} - -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 - }; - - memset(&nladdr, 0, sizeof(nladdr)); - - 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)); - } -} - -/* Receive gratuitous ARP messages and store them, that's all. */ -void get_arp_pkt(void) -{ - unsigned char buf[1024]; - struct sockaddr_ll sll; - socklen_t sll_len = sizeof(sll); - struct arphdr *a = (struct arphdr*)buf; - struct dbkey key; - DBT dbkey, dbdat; - int n; - - n = recvfrom(pset[0].fd, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr*)&sll, &sll_len); - if (n < 0) { - if (errno != EINTR && errno != EAGAIN) - syslog(LOG_ERR, "recvfrom: %m"); - return; - } - - if (ifnum && !handle_if(sll.sll_ifindex)) - return; - - /* Sanity checks */ - - if (n < sizeof(*a) || - (a->ar_op != htons(ARPOP_REQUEST) && - a->ar_op != htons(ARPOP_REPLY)) || - a->ar_pln != 4 || - a->ar_pro != htons(ETH_P_IP) || - a->ar_hln != sll.sll_halen || - sizeof(*a) + 2*4 + 2*a->ar_hln > n) - return; - - key.iface = sll.sll_ifindex; - memcpy(&key.addr, (char*)(a+1) + a->ar_hln, 4); - - /* DAD message, ignore. */ - if (key.addr == 0) - return; - - dbkey.data = &key; - dbkey.size = sizeof(key); - - if (dbase->get(dbase, &dbkey, &dbdat, 0) == 0 && !IS_NEG(dbdat.data)) { - if (memcmp(dbdat.data, a+1, dbdat.size) == 0) - return; - stats.arp_change++; - } else { - stats.arp_new++; - } - - dbdat.data = a+1; - dbdat.size = a->ar_hln; - dbase->put(dbase, &dbkey, &dbdat, 0); -} - -void catch_signal(int sig, void (*handler)(int)) -{ - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; -#ifdef SA_INTERRUPT - sa.sa_flags = SA_INTERRUPT; -#endif - sigaction(sig, &sa, NULL); -} - -#include -sigjmp_buf env; -volatile int in_poll; - -void sig_exit(int signo) -{ - do_exit = 1; - if (in_poll) - siglongjmp(env, 1); -} - -void sig_sync(int signo) -{ - do_sync = 1; - if (in_poll) - siglongjmp(env, 1); -} - -void sig_stats(int signo) -{ - do_sync = 1; - do_stats = 1; - if (in_poll) - siglongjmp(env, 1); -} - -void send_stats(void) -{ - syslog(LOG_INFO, "arp_rcv: n%lu c%lu app_rcv: tot %lu hits %lu bad %lu neg %lu sup %lu", - stats.arp_new, stats.arp_change, - - stats.app_recv, stats.app_success, - stats.app_bad, stats.app_neg, stats.app_suppressed - ); - syslog(LOG_INFO, "kern: n%lu c%lu neg %lu arp_send: %lu rlim %lu", - stats.kern_new, stats.kern_change, stats.kern_neg, - - stats.probes_sent, stats.probes_suppressed - ); - do_stats = 0; -} - - -int main(int argc, char **argv) -{ - int opt; - int do_list = 0; - char *do_load = NULL; - - while ((opt = getopt(argc, argv, "h?b:lf:a:n:kR:B:")) != EOF) { - switch (opt) { - case 'b': - dbname = optarg; - break; - case 'f': - if (do_load) { - fprintf(stderr, "Duplicate option -f\n"); - usage(); - } - do_load = optarg; - break; - case 'l': - do_list = 1; - break; - case 'a': - active_probing = atoi(optarg); - break; - case 'n': - negative_timeout = atoi(optarg); - break; - case 'k': - no_kernel_broadcasts = 1; - break; - case 'R': - if ((broadcast_rate = atoi(optarg)) <= 0 || - (broadcast_rate = 1000/broadcast_rate) <= 0) { - fprintf(stderr, "Invalid ARP rate\n"); - exit(-1); - } - break; - case 'B': - if ((broadcast_burst = atoi(optarg)) <= 0 || - (broadcast_burst = 1000*broadcast_burst) <= 0) { - fprintf(stderr, "Invalid ARP burst\n"); - exit(-1); - } - break; - case 'h': - case '?': - default: - usage(); - } - } - argc -= optind; - argv += optind; - - if (argc > 0) { - ifnum = argc; - ifnames = argv; - ifvec = malloc(argc*sizeof(int)); - if (!ifvec) { - perror("malloc"); - exit(-1); - } - } - - if ((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - exit(-1); - } - - if (ifnum) { - int i; - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - for (i=0; iput(dbase, &dbkey, &dbdat, 0)) { - perror("hash->put"); - goto do_abort; - } - } - dbase->sync(dbase, 0); - if (fp != stdin) - fclose(fp); - } - - if (do_list) { - DBT dbkey, dbdat; - printf("%-8s %-15s %s\n", "#Ifindex", "IP", "MAC"); - while (dbase->seq(dbase, &dbkey, &dbdat, R_NEXT) == 0) { - struct dbkey *key = dbkey.data; - if (handle_if(key->iface)) { - if (!IS_NEG(dbdat.data)) { - char b1[18]; - printf("%-8d %-15s %s\n", - key->iface, - inet_ntoa(*(struct in_addr*)&key->addr), - hexstring_n2a(dbdat.data, 6, b1, 18)); - } else { - printf("%-8d %-15s FAILED: %dsec ago\n", - key->iface, - inet_ntoa(*(struct in_addr*)&key->addr), - NEG_AGE(dbdat.data)); - } - } - } - } - - if (do_load || do_list) - goto out; - - pset[0].fd = socket(PF_PACKET, SOCK_DGRAM, 0); - if (pset[0].fd < 0) { - perror("socket"); - exit(-1); - } - - if (1) { - struct sockaddr_ll sll; - memset(&sll, 0, sizeof(sll)); - sll.sll_family = AF_PACKET; - sll.sll_protocol = htons(ETH_P_ARP); - sll.sll_ifindex = (ifnum == 1 ? ifvec[0] : 0); - if (bind(pset[0].fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) { - perror("bind"); - goto do_abort; - } - } - - if (rtnl_open(&rth, RTMGRP_NEIGH) < 0) { - perror("rtnl_open"); - goto do_abort; - } - pset[1].fd = rth.fd; - - load_initial_table(); - - if (1) { - int fd; - pid_t pid = fork(); - - if (pid > 0) - _exit(0); - if (pid < 0) { - perror("arpd: fork"); - goto do_abort; - } - - chdir("/"); - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - setsid(); - } - - openlog("arpd", LOG_PID | LOG_CONS, LOG_DAEMON); - catch_signal(SIGINT, sig_exit); - catch_signal(SIGTERM, sig_exit); - catch_signal(SIGHUP, sig_sync); - catch_signal(SIGUSR1, sig_stats); - -#define EVENTS (POLLIN|POLLPRI|POLLERR|POLLHUP) - pset[0].events = EVENTS; - pset[0].revents = 0; - pset[1].events = EVENTS; - pset[1].revents = 0; - - sigsetjmp(env, 1); - - for (;;) { - in_poll = 1; - - if (do_exit) - break; - if (do_sync) { - in_poll = 0; - dbase->sync(dbase, 0); - do_sync = 0; - in_poll = 1; - } - if (do_stats) - send_stats(); - if (poll(pset, 2, 30000) > 0) { - in_poll = 0; - if (pset[0].revents&EVENTS) - get_arp_pkt(); - if (pset[1].revents&EVENTS) - get_kern_msg(); - } else { - do_sync = 1; - } - } - - undo_sysctl_adjustments(); -out: - dbase->close(dbase); - exit(0); - -do_abort: - dbase->close(dbase); - exit(-1); -}