static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac) { int retval = -1; if (!dstmac) return -1; #ifdef PICO_SUPPORT_IPV6 if (destination_is_mcast(f)) { uint8_t pico_mcast6_mac[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }; pico_ethernet_mcast6_translate(f, pico_mcast6_mac); memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH); retval = 0; } else { struct pico_eth *neighbor = pico_ipv6_get_neighbor(f); if (neighbor) { memcpy(dstmac, neighbor, PICO_SIZE_ETH); retval = 0; } } #else (void)f; pico_err = PICO_ERR_EPROTONOSUPPORT; #endif return retval; }
/* 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; }
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); }