void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { sd_ipv4ll *ll = userdata; IPV4LL_DONT_DESTROY(ll); int r; assert(acd); assert(ll); switch (event) { case SD_IPV4ACD_EVENT_STOP: ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); ll->claimed_address = 0; break; case SD_IPV4ACD_EVENT_BIND: ll->claimed_address = ll->address; ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND); break; case SD_IPV4ACD_EVENT_CONFLICT: /* if an address was already bound we must call up to the user to handle this, otherwise we just try again */ if (ll->claimed_address != 0) { ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT); ll->claimed_address = 0; } else { r = ipv4ll_pick_address(ll); if (r < 0) goto error; r = sd_ipv4acd_start(ll->acd); if (r < 0) goto error; } break; default: assert_not_reached("Invalid IPv4ACD event."); } return; error: ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); }
static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) { assert(ll); ll->receive_message = sd_event_source_unref(ll->receive_message); ll->fd = safe_close(ll->fd); ll->timer = sd_event_source_unref(ll->timer); log_ipv4ll(ll, "STOPPED"); ll = ipv4ll_client_notify(ll, event); if (ll) { ll->claimed_address = 0; ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); } return ll; }
static int ipv4ll_stop(sd_ipv4ll *ll, int event) { assert(ll); ll->receive_message = sd_event_source_unref(ll->receive_message); if (ll->fd >= 0) close_nointr_nofail(ll->fd); ll->fd = -1; ll->timer = sd_event_source_unref(ll->timer); ipv4ll_client_notify(ll, event); ll->claimed_address = 0; ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); log_ipv4ll(ll, "STOPPED"); return 0; }
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); }