diff --git a/src/arp.c b/src/arp.c index f6a223f58..364336ead 100644 --- a/src/arp.c +++ b/src/arp.c @@ -18,6 +18,7 @@ #include #include "netinfo_priv.h" #include "socket_priv.h" +#include "array_priv.h" #include "buffer.h" static void ni_arp_socket_recv(ni_socket_t *); @@ -220,6 +221,57 @@ ni_arp_timeout_left(struct timeval *started, const struct timeval *current, unsi return left; } +static void +ni_arp_address_free(ni_arp_address_t *aa) +{ + ni_address_free(aa->address); + free(aa); +} + +static ni_define_ptr_array_init(ni_arp_address); +static ni_define_ptr_array_destroy(ni_arp_address); +static ni_define_ptr_array_realloc(ni_arp_address, NI_ARP_ADDRESS_ARRAY_CHUNK); +static ni_define_ptr_array_append(ni_arp_address); + +static ni_bool_t +ni_arp_address_array_append_addr(ni_arp_address_array_t *arr, ni_address_t *addr) +{ + ni_arp_address_t *e; + + if (!(e = calloc(1, sizeof(*e)))) + return FALSE; + + e->address = ni_address_ref(addr); + if (e->address && ni_arp_address_array_append(arr, e)) + return TRUE; + + ni_arp_address_free(e); + return FALSE; +} + +static ni_arp_address_t * +ni_arp_address_array_find_match_addr(ni_arp_address_array_t *array, ni_address_t *ap, + unsigned int *index, ni_bool_t (*match)(const ni_address_t *, const ni_address_t *)) +{ + ni_arp_address_t *a; + unsigned int i; + + if (array) { + match = match ?: ni_address_equal_ref; + + for (i = index ? *index : 0; i < array->count; ++i) { + a = array->data[i]; + if (match(a->address, ap)) { + if (index) + *index = i; + return a; + } + } + } + if (index) + *index = -1U; + return NULL; +} void ni_arp_verify_init(ni_arp_verify_t *vfy, const ni_config_arp_verify_t *cfg) @@ -240,79 +292,89 @@ ni_arp_verify_reset(ni_arp_verify_t *vfy, const ni_config_arp_verify_t *cfg) vfy->probe_max_ms = cfg->interval.max; timerclear(&vfy->started); vfy->last_timeout = 0; - ni_arp_verify_address_array_destroy(&vfy->ipaddrs); + ni_arp_address_array_destroy(&vfy->ipaddrs); } void ni_arp_verify_destroy(ni_arp_verify_t *vfy) { - ni_arp_verify_address_array_destroy(&vfy->ipaddrs); + ni_arp_address_array_destroy(&vfy->ipaddrs); memset(vfy, 0, sizeof(*vfy)); } unsigned int ni_arp_verify_add_address(ni_arp_verify_t *vfy, ni_address_t *ap) { - ni_address_t *ref; - if (!vfy || !ap || !vfy->nprobes) return 0; if (ap->family != AF_INET || !ni_sockaddr_is_ipv4_specified(&ap->local_addr)) return 0; - if (ni_arp_verify_address_array_find_match(&vfy->ipaddrs, ap, NULL, ni_address_equal_local_addr)) + if (ni_arp_address_array_find_match_addr(&vfy->ipaddrs, ap, NULL, ni_address_equal_local_addr)) return 0; /* already have it */ - ref = ni_address_ref(ap); - if (!ref || !ni_arp_verify_address_array_append(&vfy->ipaddrs, ref)) { - ni_address_free(ref); + if (!ni_arp_address_array_append_addr(&vfy->ipaddrs, ap)) return 0; - } return vfy->ipaddrs.count; } -void -ni_arp_verify_process(ni_arp_socket_t *sock, const ni_arp_packet_t *pkt, void *user_data) +unsigned int +ni_arp_verify_add_in_addr(ni_arp_verify_t * vfy, struct in_addr addr) +{ + unsigned int ret; + ni_sockaddr_t sockaddr; + ni_address_t *address; + + memset(&sockaddr, 0, sizeof(sockaddr)); + ni_sockaddr_set_ipv4(&sockaddr, addr, 0); + + if (!(address = ni_address_create(AF_INET, 32, &sockaddr, NULL))) + return 0; + + ni_address_set_tentative(address, TRUE); + + ret = ni_arp_verify_add_address(vfy, address); + ni_address_free(address); + + return ret; +} + +ni_arp_address_t * +ni_arp_reply_match_address(ni_arp_socket_t *sock, ni_arp_address_array_t *ipaddrs, + const ni_arp_packet_t *pkt) { - ni_arp_verify_t *vfy = (ni_arp_verify_t *)user_data; ni_netconfig_t *nc = ni_global_state_handle(0); const ni_netdev_t *dev; ni_bool_t false_alarm = FALSE; ni_bool_t found_addr = FALSE; - ni_address_t sip, *dup; - ni_arp_verify_address_t *vap; - const char *hwaddr; + ni_arp_address_t *vap; + ni_address_t sip; - if (!sock || !pkt || pkt->op != ARPOP_REPLY || !vfy) - return; + if (!sock || !pkt || pkt->op != ARPOP_REPLY || !ipaddrs) + return NULL; - /* Is it about the address we're validating? */ memset(&sip, 0, sizeof(sip)); ni_sockaddr_set_ipv4(&sip.local_addr, pkt->sip, 0); - vap = ni_arp_verify_address_array_find_match(&vfy->ipaddrs, &sip, NULL, ni_address_equal_local_addr); + vap = ni_arp_address_array_find_match_addr(ipaddrs, &sip, NULL, ni_address_equal_local_addr); if (!vap) { ni_debug_application("%s: ignore report about unrelated address %s from %s", sock->dev_info.ifname, ni_sockaddr_print(&sip.local_addr), ni_link_address_print(&pkt->sha)); - return; - } else if (ni_address_is_duplicate(vap->address)) { - ni_debug_application("%s: ignore further reply about duplicate address %s from %s", - sock->dev_info.ifname, ni_sockaddr_print(&sip.local_addr), - ni_link_address_print(&pkt->sha)); - return; + return NULL; } - dup = vap->address; + if (ni_address_is_duplicate(vap->address)) + return vap; /* Ignore any ARP replies that seem to come from our own MAC * address. Some helpful switches seem to generate them. */ if (ni_link_address_equal(&sock->dev_info.hwaddr, &pkt->sha)) { - ni_debug_application("%s: ifgnore address %s in use by our own mac address %s", + ni_debug_application("%s: ignore address %s in use by our own mac address %s", sock->dev_info.ifname, ni_sockaddr_print(&sip.local_addr), ni_link_address_print(&pkt->sha)); - return; + return NULL; } /* As well as ARP replies that seem to come from our own host: @@ -335,23 +397,18 @@ ni_arp_verify_process(ni_arp_socket_t *sock, const ni_arp_packet_t *pkt, void *u * alarm, except it really has the IP assigned. */ false_alarm = TRUE; - found_addr = !!ni_address_list_find(dev->addrs, &dup->local_addr); + found_addr = !!ni_address_list_find(dev->addrs, &vap->address->local_addr); } if (false_alarm && !found_addr) { ni_debug_application("%s: reply from one of our interfaces", sock->dev_info.ifname); - return; + return NULL; } - ni_address_set_duplicate(dup, TRUE); - - hwaddr = ni_link_address_print(&pkt->sha); - ni_error("%s: IPv4 duplicate address %s detected%s%s%s!", - sock->dev_info.ifname, ni_sockaddr_print(&dup->local_addr), - hwaddr ? " (in use by " : "", hwaddr ? hwaddr : "", hwaddr ? ")" : ""); + return vap; } -ni_bool_t +ni_arp_send_status_t ni_arp_verify_send(ni_arp_socket_t *sock, ni_arp_verify_t *vfy, ni_timeout_t *timeout) { static struct in_addr null = { 0 }; @@ -359,18 +416,18 @@ ni_arp_verify_send(ni_arp_socket_t *sock, ni_arp_verify_t *vfy, ni_timeout_t *ti unsigned int i; struct timeval now; ni_address_t *ap; - ni_arp_verify_address_t *vap; + ni_arp_address_t *vap; ni_bool_t need_wait; if (!sock || !vfy || !timeout) - return FALSE; + return NI_ARP_SEND_FAILURE; ni_timer_get_time(&now); if ((*timeout = ni_arp_timeout_left(&vfy->started, &now, vfy->last_timeout))) - return TRUE; + return NI_ARP_SEND_PROGRESS; for (i = 0; i < vfy->ipaddrs.count; ++i) { - vap = &vfy->ipaddrs.data[i]; + vap = vfy->ipaddrs.data[i]; ap = vap->address; if (ni_address_is_duplicate(ap)) @@ -378,14 +435,14 @@ ni_arp_verify_send(ni_arp_socket_t *sock, ni_arp_verify_t *vfy, ni_timeout_t *ti if (!ni_address_is_tentative(ap)) continue; - if (vap->nprobes >= vfy->nprobes) + if (vap->nattempts >= vfy->nprobes) ni_address_set_tentative(ap, FALSE); } need_wait = FALSE; vfy->started = now; for (i = 0; i < vfy->ipaddrs.count; ++i) { - vap = &vfy->ipaddrs.data[i]; + vap = vfy->ipaddrs.data[i]; ap = vap->address; if (ni_address_is_duplicate(ap)) @@ -393,14 +450,13 @@ ni_arp_verify_send(ni_arp_socket_t *sock, ni_arp_verify_t *vfy, ni_timeout_t *ti if (!ni_address_is_tentative(ap)) continue; - ni_debug_application("%s: sending arp verify for IP %s, probe: %u", - sock->dev_info.ifname, - ni_sockaddr_print(&ap->local_addr), - vap->nprobes + 1); + ni_debug_application("%s: sending arp verify for IP %s (%u/%u)", + sock->dev_info.ifname, ni_sockaddr_print(&ap->local_addr), + vap->nattempts + 1, vfy->nprobes); ip = &ap->local_addr.sin.sin_addr; if (ni_arp_send_request(sock, null, *ip) > 0) { - vap->nprobes++; + vap->nattempts++; need_wait = TRUE; } else { if (errno == ENOBUFS) { @@ -417,7 +473,7 @@ ni_arp_verify_send(ni_arp_socket_t *sock, ni_arp_verify_t *vfy, ni_timeout_t *ti " ENOBUFS, probes:%u/%u errors:%u/%u", sock->dev_info.ifname, ni_sockaddr_print(&ap->local_addr), - vap->nprobes, vfy->nprobes, + vap->nattempts, vfy->nprobes, vap->nerrors, vfy->nretry); } } else { @@ -431,10 +487,15 @@ ni_arp_verify_send(ni_arp_socket_t *sock, ni_arp_verify_t *vfy, ni_timeout_t *ti if (need_wait) { vfy->last_timeout = ni_timeout_random_range(vfy->probe_min_ms, vfy->probe_max_ms); *timeout = vfy->last_timeout; - return TRUE; + return NI_ARP_SEND_PROGRESS; + } + for (i = 0; i < vfy->ipaddrs.count; ++i) { + vap = vfy->ipaddrs.data[i]; + if (vap->nattempts > 0) + return NI_ARP_SEND_COMPLETE; } - return FALSE; + return NI_ARP_SEND_FAILURE; } void @@ -453,35 +514,30 @@ ni_arp_notify_reset(ni_arp_notify_t *nfy, const ni_config_arp_notify_t *cfg) nfy->nretry = cfg->retries; nfy->wait_ms = cfg->interval; timerclear(&nfy->started); - ni_address_array_destroy(&nfy->ipaddrs); + ni_arp_address_array_destroy(&nfy->ipaddrs); } void ni_arp_notify_destroy(ni_arp_notify_t *nfy) { - ni_address_array_destroy(&nfy->ipaddrs); + ni_arp_address_array_destroy(&nfy->ipaddrs); memset(nfy, 0, sizeof(*nfy)); } unsigned int ni_arp_notify_add_address(ni_arp_notify_t *nfy, ni_address_t *ap) { - ni_address_t *ref; - if (!nfy || !ap || !nfy->nclaims) return 0; if (ap->family != AF_INET || !ni_sockaddr_is_ipv4_specified(&ap->local_addr)) return 0; - if (ni_address_array_find_match(&nfy->ipaddrs, ap, NULL, ni_address_equal_local_addr)) + if (ni_arp_address_array_find_match_addr(&nfy->ipaddrs, ap, NULL, ni_address_equal_local_addr)) return 0; /* already have it */ - ref = ni_address_ref(ap); - if (!ref || !ni_address_array_append(&nfy->ipaddrs, ref)) { - ni_address_free(ref); + if (!ni_arp_address_array_append_addr(&nfy->ipaddrs, ap)) return 0; - } return nfy->ipaddrs.count; } @@ -490,9 +546,11 @@ ni_bool_t ni_arp_notify_send(ni_arp_socket_t *sock, ni_arp_notify_t *nfy, ni_timeout_t *timeout) { const struct in_addr *ip; - unsigned int i, count; + unsigned int i; struct timeval now; + ni_arp_address_t *aa; ni_address_t *ap; + ni_bool_t need_wait; if (!sock || !nfy || !timeout) return FALSE; @@ -503,10 +561,11 @@ ni_arp_notify_send(ni_arp_socket_t *sock, ni_arp_notify_t *nfy, ni_timeout_t *ti if (nfy->nclaims && nfy->ipaddrs.count) { nfy->started = now; - nfy->nclaims--; + need_wait = FALSE; - for (count = 0, i = 0; i < nfy->ipaddrs.count; ++i) { - ap = nfy->ipaddrs.data[i]; + for (i = 0; i < nfy->ipaddrs.count; ++i) { + aa = nfy->ipaddrs.data[i]; + ap = aa->address; if (ni_address_is_duplicate(ap)) continue; @@ -514,15 +573,38 @@ ni_arp_notify_send(ni_arp_socket_t *sock, ni_arp_notify_t *nfy, ni_timeout_t *ti if (ni_address_is_tentative(ap)) continue; - ni_debug_application("%s: sending arp notify for IP %s", - sock->dev_info.ifname, - ni_sockaddr_print(&ap->local_addr)); + if ((nfy->nretry && aa->nerrors >= nfy->nretry) || + aa->nattempts >= nfy->nclaims) + continue; + + ni_debug_application("%s: sending arp notify for IP %s (%u/%u)", + sock->dev_info.ifname, ni_sockaddr_print(&ap->local_addr), + aa->nattempts + 1, nfy->nclaims); ip = &ap->local_addr.sin.sin_addr; - if (ni_arp_send_grat_request(sock, *ip) > 0) - count++; + if (ni_arp_send_grat_request(sock, *ip) > 0) { + aa->nattempts++; + need_wait = TRUE; + } else { + if (errno == ENOBUFS) { + aa->nerrors++; + if (aa->nerrors >= nfy->nretry) { + ni_error("%s: ARP notify failed for %s - max (%u) retry!", + sock->dev_info.ifname, + ni_sockaddr_print(&ap->local_addr), + nfy->nretry); + } else { + need_wait = TRUE; + } + } else { + ni_error("%s: ARP notify send failed for %s - unexpected error!", + sock->dev_info.ifname, + ni_sockaddr_print(&ap->local_addr)); + ni_address_set_duplicate(ap, TRUE); + } + } } - if (count) { + if (need_wait) { *timeout = nfy->wait_ms; return TRUE; } @@ -531,85 +613,3 @@ ni_arp_notify_send(ni_arp_socket_t *sock, ni_arp_notify_t *nfy, ni_timeout_t *ti return FALSE; } -void -ni_arp_verify_address_array_init(ni_arp_verify_address_array_t *array) -{ - memset(array, 0, sizeof(*array)); -} - -void -ni_arp_verify_address_array_destroy(ni_arp_verify_address_array_t *array) -{ - if (array) { - while (array->count) { - array->count--; - ni_address_free(array->data[array->count].address); - } - free(array->data); - array->data = NULL; - } -} - -static ni_bool_t -ni_arp_verify_address_array_realloc(ni_arp_verify_address_array_t *array, unsigned int newlen) -{ - static const ni_arp_verify_address_t def_value = NI_ARP_VERIFY_ADDRESS_INIT; - ni_arp_verify_address_t *newdata; - size_t newsize; - unsigned int i; - - if (!array || (UINT_MAX - NI_ARP_VERIFY_ADDRESS_ARRAY_CHUNK) <= newlen) - return FALSE; - - newlen += NI_ARP_VERIFY_ADDRESS_ARRAY_CHUNK; - newsize = newlen * sizeof(ni_arp_verify_address_t); - newdata = xrealloc(array->data, newsize); - if (!newdata) - return FALSE; - - array->data = newdata; - for (i = array->count; i < newlen; ++i) - array->data[i] = def_value; - return TRUE; -} - - -ni_bool_t -ni_arp_verify_address_array_append(ni_arp_verify_address_array_t *array, ni_address_t *ap) -{ - if (!array) - return FALSE; - - if ((array->count % NI_ARP_VERIFY_ADDRESS_ARRAY_CHUNK) == 0 && - !ni_arp_verify_address_array_realloc(array, array->count)) - return FALSE; - - array->data[array->count++].address = ap; - return TRUE; -} - - -extern ni_arp_verify_address_t * -ni_arp_verify_address_array_find_match(ni_arp_verify_address_array_t *array, ni_address_t *ap, - unsigned int *index, ni_bool_t (*match)(const ni_address_t *, const ni_address_t *)) -{ - ni_arp_verify_address_t *a; - unsigned int i; - - if (array) { - match = match ?: ni_address_equal_ref; - - for (i = index ? *index : 0; i < array->count; ++i) { - a = &array->data[i]; - if (match(a->address, ap)) { - if (index) - *index = i; - return a; - } - } - } - if (index) - *index = -1U; - return NULL; -} - diff --git a/src/ifconfig.c b/src/ifconfig.c index ecda7a308..661a12e94 100644 --- a/src/ifconfig.c +++ b/src/ifconfig.c @@ -4975,6 +4975,34 @@ ni_address_updater_arp_notify_enabled(ni_netdev_t *dev) return !ni_tristate_is_disabled(ipv4->conf.arp_verify); } + +void +ni_address_updater_process_arp(ni_arp_socket_t *sock, const ni_arp_packet_t *pkt, void *user_data) +{ + ni_address_updater_t *au = (ni_address_updater_t *) user_data; + ni_arp_address_t *vap; + ni_stringbuf_t sb = NI_STRINGBUF_INIT_DYNAMIC; + const char *hwaddr; + + if (!(vap = ni_arp_reply_match_address(sock, &au->verify.ipaddrs, pkt))) + return; + + if (ni_address_is_duplicate(vap->address)) { + ni_debug_application("%s: ignore further reply about duplicate address %s from %s", + sock->dev_info.ifname, ni_address_print(&sb, vap->address), + ni_link_address_print(&pkt->sha)); + ni_stringbuf_destroy(&sb); + return; + } + + ni_address_set_duplicate(vap->address, TRUE); + + hwaddr = ni_link_address_print(&pkt->sha); + ni_error("%s: IPv4 duplicate address %s detected%s%s%s!", + sock->dev_info.ifname, ni_address_print(&sb, vap->address), + hwaddr ? " (in use by " : "", hwaddr ? hwaddr : "", hwaddr ? ")" : ""); +} + static ni_bool_t ni_address_updater_arp_open(ni_address_updater_t *au, ni_netdev_t *dev) { @@ -4983,7 +5011,7 @@ ni_address_updater_arp_open(ni_address_updater_t *au, ni_netdev_t *dev) if (ni_capture_devinfo_init(&dev_info, dev->name, &dev->link) < 0) return FALSE; - au->sock = ni_arp_socket_open(&dev_info, ni_arp_verify_process, au); + au->sock = ni_arp_socket_open(&dev_info, ni_address_updater_process_arp, au); ni_capture_devinfo_destroy(&dev_info); return au->sock != NULL; } @@ -5052,7 +5080,7 @@ ni_address_updater_arp_send(ni_addrconf_updater_t *updater, ni_netdev_t *dev, ni } if (au->verify.nprobes) { - if (ni_arp_verify_send(au->sock, &au->verify, &wait_verify)) { + if (ni_arp_verify_send(au->sock, &au->verify, &wait_verify) == NI_ARP_SEND_PROGRESS) { updater->timeout = wait_verify; return TRUE; } diff --git a/src/netinfo_priv.h b/src/netinfo_priv.h index d752643ea..15418a50e 100644 --- a/src/netinfo_priv.h +++ b/src/netinfo_priv.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "appconfig.h" typedef struct ni_capture ni_capture_t; @@ -199,23 +200,19 @@ extern int ni_arp_send_grat_reply(ni_arp_socket_t *, struct in_addr); extern int ni_arp_send_grat_request(ni_arp_socket_t *, struct in_addr); extern int ni_arp_send(ni_arp_socket_t *, const ni_arp_packet_t *); -#define NI_ARP_VERIFY_ADDRESS_INIT { .nprobes = 0, .nerrors = 0, .address = NULL } - -typedef struct ni_arp_verify_address { - unsigned int nprobes; +typedef struct ni_arp_address ni_arp_address_t; +struct ni_arp_address { + unsigned int nattempts; unsigned int nerrors; ni_address_t * address; -} ni_arp_verify_address_t; - -#define NI_ARP_VERIFY_ADDRESS_ARRAY_CHUNK 32 -#define NI_ARP_VERIFY_ADDRESS_ARRAY_INIT { .count = 0, .data = NULL } +}; -typedef struct ni_arp_verify_address_array { - unsigned int count; - ni_arp_verify_address_t * data; -} ni_arp_verify_address_array_t; +#define NI_ARP_ADDRESS_ARRAY_CHUNK 32 +ni_declare_ptr_array_type(ni_arp_address); -typedef struct ni_arp_verify { +typedef struct ni_arp_verify ni_arp_verify_t; +typedef void (*ni_arp_duplicated_addr_fn)(ni_arp_verify_t *, const ni_arp_packet_t *, void *); +struct ni_arp_verify { unsigned int nprobes; unsigned int nretry; @@ -224,23 +221,23 @@ typedef struct ni_arp_verify { ni_timeout_t last_timeout; struct timeval started; - ni_arp_verify_address_array_t ipaddrs; -} ni_arp_verify_t; + ni_arp_address_array_t ipaddrs; +}; -extern void ni_arp_verify_address_array_init(ni_arp_verify_address_array_t *); -extern void ni_arp_verify_address_array_destroy(ni_arp_verify_address_array_t *); -extern ni_bool_t ni_arp_verify_address_array_append(ni_arp_verify_address_array_t *, - ni_address_t *); -extern ni_arp_verify_address_t * ni_arp_verify_address_array_find_match(ni_arp_verify_address_array_t *, - ni_address_t *, unsigned int *, - ni_bool_t (*)(const ni_address_t *, const ni_address_t *)); +typedef enum { + NI_ARP_SEND_FAILURE = -1, + NI_ARP_SEND_COMPLETE = 0, + NI_ARP_SEND_PROGRESS = 1 +} ni_arp_send_status_t; extern void ni_arp_verify_init(ni_arp_verify_t *, const ni_config_arp_verify_t *); extern void ni_arp_verify_reset(ni_arp_verify_t *, const ni_config_arp_verify_t *); extern void ni_arp_verify_destroy(ni_arp_verify_t *); extern unsigned int ni_arp_verify_add_address(ni_arp_verify_t *, ni_address_t *); -extern void ni_arp_verify_process(ni_arp_socket_t *, const ni_arp_packet_t *, void *); -extern ni_bool_t ni_arp_verify_send(ni_arp_socket_t *, ni_arp_verify_t *, ni_timeout_t *); +extern unsigned int ni_arp_verify_add_in_addr(ni_arp_verify_t *, struct in_addr); +extern ni_arp_send_status_t ni_arp_verify_send(ni_arp_socket_t *, ni_arp_verify_t *, ni_timeout_t *); +extern ni_arp_address_t * ni_arp_reply_match_address(ni_arp_socket_t *, ni_arp_address_array_t *, + const ni_arp_packet_t *); typedef struct ni_arp_notify { unsigned int nclaims; @@ -249,7 +246,7 @@ typedef struct ni_arp_notify { unsigned int wait_ms; struct timeval started; - ni_address_array_t ipaddrs; + ni_arp_address_array_t ipaddrs; } ni_arp_notify_t; extern void ni_arp_notify_init(ni_arp_notify_t *, const ni_config_arp_notify_t *);