Example #1
0
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
    bool need_restart = false;

    assert_return(ll, -EINVAL);
    assert_return(addr, -EINVAL);

    if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
        return 0;

    if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
        log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
                   "client, restarting");
        ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
        need_restart = true;
    }

    if (!ll)
        return 0;

    memcpy(&ll->mac_addr, addr, ETH_ALEN);

    if (need_restart)
        sd_ipv4ll_start(ll);

    return 0;
}
Example #2
0
int arp_packet_verify_headers(struct ether_arp *arp) {
        assert(arp);

        if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) {
                log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER");
                return -EINVAL;
        }
        if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) {
                log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP");
                return -EINVAL;
        }
        if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) &&
            arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) {
                log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY");
                return -EINVAL;
        }

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