/* 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; }
END_TEST START_TEST(tc_pico_arp_queue) { struct pico_ip4 addr = { .addr = 0xaabbccdd }; int i; struct pico_frame *f = pico_frame_alloc(sizeof(struct pico_ipv4_hdr)); struct pico_ipv4_hdr *h = (struct pico_ipv4_hdr *) f->buffer; fail_if(!f); f->net_hdr = h; h->dst.addr = addr.addr; for (i = 0; i < PICO_ND_MAX_FRAMES_QUEUED; i++) { fail_if(frames_queued[i] != NULL); } pico_arp_unreachable(&addr); for (i = 0; i < PICO_ND_MAX_FRAMES_QUEUED; i++) { fail_if(frames_queued[i] != NULL); } pico_arp_postpone(f); fail_if(frames_queued[0]->buffer != f->buffer); pico_arp_unreachable(&addr); for (i = 0; i < PICO_ND_MAX_FRAMES_QUEUED; i++) { fail_if(frames_queued[i] != NULL); } PICO_FREE(f); } END_TEST START_TEST (arp_receive_test) { struct mock_device *mock; struct pico_frame *f = NULL; struct pico_arp_hdr *ah = NULL; struct pico_eth_hdr *eh = NULL; uint8_t macaddr1[6] = { 0, 0, 0, 0xa, 0xb, 0xf }; uint8_t macaddr2[6] = { 0, 0, 0, 0xc, 0xd, 0xf }; struct pico_ip4 netmask = { .addr = long_be(0xffffff00) }; struct pico_ip4 ip1 = { .addr = long_be(0x0A28000A) }; struct pico_ip4 ip2 = { .addr = long_be(0x0A28000B) }; pico_stack_init(); /* Create mock device */ mock = pico_mock_create(macaddr1); fail_if(!mock, "MOCK DEVICE creation failed"); fail_if(pico_ipv4_link_add(mock->dev, ip1, netmask), "add link to mock device failed"); /* Normal ARP request */ f = init_frame(mock->dev); fail_if(!f, "FRAME INIT failed"); eh = (struct pico_eth_hdr *) f->datalink_hdr; ah = (struct pico_arp_hdr *) f->net_hdr; memcpy(eh->saddr, macaddr2, PICO_SIZE_ETH); memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH); eh->proto = PICO_IDETH_ARP; ah->htype = PICO_ARP_HTYPE_ETH; ah->ptype = PICO_IDETH_IPV4; ah->hsize = PICO_SIZE_ETH; ah->psize = PICO_SIZE_IP4; ah->opcode = PICO_ARP_REQUEST; memcpy(ah->s_mac, macaddr2, PICO_SIZE_ETH); ah->src.addr = ip2.addr; ah->dst.addr = ip1.addr; fail_unless(pico_arp_receive(f) == 0); /* net_hdr is a nullpointer */ f = init_frame(mock->dev); fail_if(!f, "FRAME INIT failed"); f->net_hdr = NULL; fail_unless(pico_arp_receive(f) == -1); /* wrong hardware type */ f = init_frame(mock->dev); fail_if(!f, "FRAME INIT failed"); ah = (struct pico_arp_hdr *) f->net_hdr; ah->htype = 0; fail_unless(pico_arp_receive(f) == -1); /* wrong protocol type */ f = init_frame(mock->dev); fail_if(!f, "FRAME INIT failed"); ah = (struct pico_arp_hdr *) f->net_hdr; ah->ptype = 0; fail_unless(pico_arp_receive(f) == -1); /* source mac address is multicast */ f = init_frame(mock->dev); fail_if(!f, "FRAME INIT failed"); ah = (struct pico_arp_hdr *) f->net_hdr; ah->s_mac[0] = 0x01; fail_unless(pico_arp_receive(f) == -1); } END_TEST START_TEST (arp_get_test) { struct pico_frame *f = NULL; struct mock_device *mock; struct pico_ipv4_hdr *hdr = NULL; struct pico_eth *eth = NULL; uint8_t macaddr[6] = { 0, 0, 0, 0xa, 0xb, 0xf }; struct pico_ip4 netmask = { .addr = long_be(0xffffff00) }; struct pico_ip4 ip = { .addr = long_be(0x0A28000A) }; mock = pico_mock_create(macaddr); fail_if(!mock, "MOCK DEVICE creation failed"); fail_if(pico_ipv4_link_add(mock->dev, ip, netmask), "add link to mock device failed"); f = pico_frame_alloc(PICO_SIZE_ETHHDR + sizeof(struct pico_ipv4_hdr)); f->net_hdr = f->start + PICO_SIZE_ETHHDR; f->datalink_hdr = f->start; f->dev = mock->dev; hdr = (struct pico_ipv4_hdr *) f->net_hdr; hdr->dst.addr = ip.addr; eth = pico_arp_get(f); fail_unless(eth == &mock->dev->eth->mac); } END_TEST
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; }