int arp_send_announcement(int fd, int ifindex, be32_t pa, const struct ether_addr *ha) { struct ether_arp ea = {}; assert(fd >= 0); assert(ifindex > 0); assert(pa != 0); assert(ha); return arp_network_send_raw_socket(fd, ifindex, &ea); }
static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) { struct ether_arp out_packet; int out_packet_ready = 0; int r = 0; assert(ll); assert(trigger < _IPV4LL_TRIGGER_MAX); if (ll->state == IPV4LL_STATE_INIT) { log_ipv4ll(ll, "PROBE"); ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) || (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) { /* Send a probe */ arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); out_packet_ready = 1; ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0); ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) { /* Send the last probe */ arp_packet_probe(&out_packet, ll->address, &ll->mac_addr); out_packet_ready = 1; ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1); ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) || (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) { /* Send announcement packet */ arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); out_packet_ready = 1; ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0); ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); if (ll->iteration == 0) { log_ipv4ll(ll, "ANNOUNCE"); ll->claimed_address = ll->address; ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND); if (!ll || ll->state == IPV4LL_STATE_STOPPED) goto out; ll->conflict = 0; } } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= ANNOUNCE_NUM-1)) { ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0); ll->next_wakeup_valid = 0; } else if (trigger == IPV4LL_TRIGGER_PACKET) { int conflicted = 0; usec_t time_now; struct ether_arp* in_packet = (struct ether_arp*)trigger_data; assert(in_packet); if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) { if (ipv4ll_arp_conflict(ll, in_packet)) { r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) goto out; /* Defend address */ if (time_now > ll->defend_window) { ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC; arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr); out_packet_ready = 1; } else conflicted = 1; } } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE, IPV4LL_STATE_PROBING, IPV4LL_STATE_WAITING_ANNOUNCE)) { conflicted = ipv4ll_arp_probe_conflict(ll, in_packet); } if (conflicted) { log_ipv4ll(ll, "CONFLICT"); ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT); if (!ll || ll->state == IPV4LL_STATE_STOPPED) goto out; ll->claimed_address = 0; /* Pick a new address */ r = ipv4ll_pick_address(ll, &ll->address); if (r < 0) goto out; ll->conflict++; ll->defend_window = 0; ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); if (ll->conflict >= MAX_CONFLICTS) { log_ipv4ll(ll, "MAX_CONFLICTS"); ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); } else ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); } } if (out_packet_ready) { r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet); if (r < 0) { log_ipv4ll(ll, "failed to send arp packet out"); goto out; } } if (ll->next_wakeup_valid) { ll->timer = sd_event_source_unref(ll->timer); r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(), ll->next_wakeup, 0, ipv4ll_timer, ll); if (r < 0) goto out; r = sd_event_source_set_priority(ll->timer, ll->event_priority); if (r < 0) goto out; r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); if (r < 0) goto out; } out: if (r < 0 && ll) ipv4ll_stop(ll, r); }