Example #1
0
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);
}
Example #2
0
int sd_ipv4ll_start (sd_ipv4ll *ll) {
        int r;

        assert_return(ll, -EINVAL);
        assert_return(ll->event, -EINVAL);
        assert_return(ll->index > 0, -EINVAL);
        assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);

        r = arp_network_bind_raw_socket(ll->index, &ll->link);

        if (r < 0)
                goto out;

        ll->fd = r;
        ll->conflict = 0;
        ll->defend_window = 0;
        ll->claimed_address = 0;

        if (ll->address == 0)
                ll->address = ipv4ll_pick_address(ll);

        ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);

        r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
                            EPOLLIN, ipv4ll_receive_message, ll);
        if (r < 0)
                goto out;

        r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
        if (r < 0)
                goto out;

        r = sd_event_add_monotonic(ll->event, &ll->timer, now(CLOCK_MONOTONIC), 0,
                                   ipv4ll_timer, ll);

        if (r < 0)
                goto out;

        r = sd_event_source_set_priority(ll->timer, ll->event_priority);

out:
        if (r < 0)
                ipv4ll_stop(ll, IPV4LL_EVENT_STOP);

        return 0;
}
Example #3
0
int sd_ipv4ll_start(sd_ipv4ll *ll) {
        int r;

        assert_return(ll, -EINVAL);
        assert_return(ll->random_data, -EINVAL);

        if (ll->address == 0) {
                r = ipv4ll_pick_address(ll);
                if (r < 0)
                        return r;
        }

        r = sd_ipv4acd_start(ll->acd);
        if (r < 0)
                return r;

        return 0;
}
Example #4
0
int sd_ipv4ll_start (sd_ipv4ll *ll) {
    int r;

    assert_return(ll, -EINVAL);
    assert_return(ll->event, -EINVAL);
    assert_return(ll->index > 0, -EINVAL);
    assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
                         IPV4LL_STATE_STOPPED), -EBUSY);

    ll->state = IPV4LL_STATE_INIT;

    r = arp_network_bind_raw_socket(ll->index, &ll->link);

    if (r < 0)
        goto out;

    ll->fd = r;
    ll->conflict = 0;
    ll->defend_window = 0;
    ll->claimed_address = 0;

    if (!ll->random_data) {
        uint8_t seed[8];

        /* Fallback to mac */
        siphash24(seed, &ll->mac_addr.ether_addr_octet,
                  ETH_ALEN, HASH_KEY.bytes);

        r = sd_ipv4ll_set_address_seed(ll, seed);
        if (r < 0)
            goto out;
    }

    if (ll->address == 0) {
        r = ipv4ll_pick_address(ll, &ll->address);
        if (r < 0)
            goto out;
    }

    ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);

    r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
                        EPOLLIN, ipv4ll_receive_message, ll);
    if (r < 0)
        goto out;

    r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
    if (r < 0)
        goto out;

    r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
    if (r < 0)
        goto out;

    r = sd_event_add_time(ll->event,
                          &ll->timer,
                          clock_boottime_or_monotonic(),
                          now(clock_boottime_or_monotonic()), 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");
out:
    if (r < 0)
        ipv4ll_stop(ll, IPV4LL_EVENT_STOP);

    return 0;
}
Example #5
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);
}