Example #1
0
/* This is called by dev loop in order to ensure correct ethernet addressing.
 * Returns 0 if the destination is unknown, and -1 if the packet is not deliverable
 * due to ethernet addressing (i.e., no arp association was possible.
 *
 * Only IP packets must pass by this. ARP will always use direct dev->send() function, so
 * we assume IP is used.
 */
int32_t pico_ethernet_send(struct pico_frame *f)
{
    const struct pico_eth *dstmac = NULL;
    int32_t ret = -1;

    if (IS_IPV6(f)) {
        /*TODO: Neighbor solicitation */
        dstmac = NULL;
    }

    else if (IS_IPV4(f)) {
        if (IS_BCAST(f) || destination_is_bcast(f)) {
            dstmac = (const struct pico_eth *const) PICO_ETHADDR_ALL;
        }

#ifdef PICO_SUPPORT_MCAST
        else if (destination_is_mcast(f)) {
            uint8_t pico_mcast_mac[6] = {
                0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
            };
            dstmac = pico_ethernet_mcast_translate(f, pico_mcast_mac);
        }
#endif
        else {
            dstmac = pico_arp_get(f);
            if (!dstmac)
                return 0;
        }
        /* This sets destination and source address, then pushes the packet to the device. */
        if (dstmac && (f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR)) {
            struct pico_eth_hdr *hdr;
            f->start -= PICO_SIZE_ETHHDR;
            f->len += PICO_SIZE_ETHHDR;
            f->datalink_hdr = f->start;
            hdr = (struct pico_eth_hdr *) f->datalink_hdr;
            memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
            memcpy(hdr->daddr, dstmac, PICO_SIZE_ETH);
            hdr->proto = PICO_IDETH_IPV4;
            if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
                dbg("sending out packet destined for our own mac\n");
                return pico_ethernet_receive(f);
            } else if(IS_LIMITED_BCAST(f)) {
                ret = pico_device_broadcast(f);
            } else {
                ret = (int32_t)f->dev->send(f->dev, f->start, (int) f->len);
                /* Frame is discarded after this return by the caller */
            }

            if(!ret) pico_frame_discard(f);

            return ret;
        } else {
            return -1;
        }
    } /* End IPV4 ethernet addressing */

    return -1;

}
Example #2
0
int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f)
{
    struct pico_eth dstmac;
    uint8_t dstmac_valid = 0;
    uint16_t proto = PICO_IDETH_IPV4;

#ifdef PICO_SUPPORT_IPV6
    /* Step 1: If the frame has an IPv6 packet,
     * destination address is taken from the ND tables
     */
    if (IS_IPV6(f)) {
        if (pico_ethernet_ipv6_dst(f, &dstmac) < 0)
        {
            pico_ipv6_nd_postpone(f);
            return 0; /* I don't care if frame was actually postponed. If there is no room in the ND table, discard safely. */
        }

        dstmac_valid = 1;
        proto = PICO_IDETH_IPV6;
    }
    else
#endif

    /* In case of broadcast (IPV4 only), dst mac is FF:FF:... */
    if (IS_BCAST(f) || destination_is_bcast(f))
    {
        memcpy(&dstmac, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
        dstmac_valid = 1;
    }

    /* In case of multicast, dst mac is translated from the group address */
    else if (destination_is_mcast(f)) {
        uint8_t pico_mcast_mac[6] = {
            0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
        };
        pico_ethernet_mcast_translate(f, pico_mcast_mac);
        memcpy(&dstmac, pico_mcast_mac, PICO_SIZE_ETH);
        dstmac_valid = 1;
    }

#if (defined PICO_SUPPORT_IPV4)
    else {
        struct pico_eth *arp_get;
        arp_get = pico_arp_get(f);
        if (arp_get) {
            memcpy(&dstmac, arp_get, PICO_SIZE_ETH);
            dstmac_valid = 1;
        } else {
            /* At this point, ARP will discard the frame in any case.
             * It is safe to return without discarding.
             */
            pico_arp_postpone(f);
            return 0;
            /* Same case as for IPv6 ... */
        }

    }
#endif

    /* This sets destination and source address, then pushes the packet to the device. */
    if (dstmac_valid) {
        struct pico_eth_hdr *hdr;
        hdr = (struct pico_eth_hdr *) f->datalink_hdr;
        if ((f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR))
        {
            f->start -= PICO_SIZE_ETHHDR;
            f->len += PICO_SIZE_ETHHDR;
            f->datalink_hdr = f->start;
            hdr = (struct pico_eth_hdr *) f->datalink_hdr;
            memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
            memcpy(hdr->daddr, &dstmac, PICO_SIZE_ETH);
            hdr->proto = proto;
        }

        if (pico_ethsend_local(f, hdr) || pico_ethsend_bcast(f) || pico_ethsend_dispatch(f)) {
            /* one of the above functions has delivered the frame accordingly. (returned != 0)
             * It is safe to directly return success.
             * */
            return 0;
        }
    }

    /* Failure: do not dequeue the frame, keep it for later. */
    return -1;
}
END_TEST
START_TEST(tc_destination_is_mcast)
{
    struct pico_ip6 addr = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 }};
    struct pico_ip6 mcast = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 }};
    struct pico_ip4 addr4 = {0};
    struct pico_ip4 mcast4 = {0};
    struct pico_frame *f = pico_frame_alloc(sizeof(struct pico_ipv6_hdr));
    struct pico_ipv6_hdr *h = (struct pico_ipv6_hdr *)f->buffer;
    struct pico_ipv4_hdr *h4 = (struct pico_ipv4_hdr *)f->buffer;
    /* Test parameters */
    int ret = 0, count = 0;

    f->net_hdr = (uint8_t*) h;
    f->buffer[0] = 0x60; /* Ipv6 */

    STARTING();

    pico_string_to_ipv4("232.1.1.0", &(mcast4.addr)); /* 0 */
    pico_string_to_ipv4("10.20.0.1", &(addr4.addr));

    pico_string_to_ipv6("ff00:0:0:0:0:0:e801:100", (mcast.addr)); /* 0 */
    pico_string_to_ipv6("fe80:0:0:0:0:0:a28:100", (addr.addr)); /* 0 */

    TRYING("With IPv6 unicast addr\n");
    memcpy(h->dst.addr, addr.addr, PICO_SIZE_IP6);
    ret = destination_is_mcast(f);
    CHECKING(count);
    fail_unless(0 == ret, "Should've returned 0 since not an IPv6 multicast\n");
    SUCCESS();

    TRYING("With IPv6 multicast addr\n");
    memcpy(h->dst.addr, mcast.addr, PICO_SIZE_IP6);
    ret = destination_is_mcast(f);
    CHECKING(count);
    fail_unless(1 == ret, "Should've returned 1 since an IPv6 multicast\n");
    SUCCESS();

    pico_frame_discard(f);
    f = pico_frame_alloc(sizeof(struct pico_ipv4_hdr));
    h4 = (struct pico_ipv4_hdr *)f->buffer;
    f->net_hdr = (uint8_t *)h4;
    f->buffer[0] = 0x40; /* IPv4 */

    TRYING("With IPv4 unicast addr\n");
    h4->dst = addr4;
    ret = destination_is_bcast(f);
    CHECKING(count);
    fail_unless(0 == ret, "Should've returned 0 since not an IPv4 mcast address\n");
    SUCCESS();

    TRYING("With IPv4 multicast addr\n");
    h4->dst = mcast4;
    ret = destination_is_mcast(f);
    CHECKING(count);
    fail_unless(1 == ret, "Should've returned 1 since an IPv4 multicast\n");
    SUCCESS();

    BREAKING();
    ret = destination_is_bcast(NULL);
    CHECKING(count);
    fail_unless(0 == ret, "Should've returned 0 since NULL-pointer\n");
    SUCCESS();

    ENDING(count);
}