/* * Copyright (C) 2016 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @{ * * @file * @author Martine Lenders */ #include "msg.h" #include "net/ethernet.h" #include "net/ipv6.h" #include "net/netdev/eth.h" #include "net/netdev_test.h" #include "net/sock.h" #include "net/udp.h" #include "sched.h" #include "xtimer.h" #include "lwip.h" #include "lwip/ip4.h" #include "lwip/inet_chksum.h" #include "lwip/nd6.h" #include "lwip/priv/nd6_priv.h" #include "lwip/netif.h" #include "lwip/netif/netdev.h" #include "lwip/opt.h" #include "lwip/tcpip.h" #include "netif/etharp.h" #include "constants.h" #include "stack.h" #define _MSG_QUEUE_SIZE (1) #define _SEND_DONE (0x92d7) #define _NETDEV_BUFFER_SIZE (128) static msg_t _msg_queue[_MSG_QUEUE_SIZE]; static uint8_t _netdev_buffer[_NETDEV_BUFFER_SIZE]; netdev_test_t netdev; static struct netif netif; static kernel_pid_t _check_pid = KERNEL_PID_UNDEF; static mutex_t _netdev_buffer_mutex = MUTEX_INIT; static uint8_t _netdev_buffer_size; static inline void _get_iid(uint8_t *iid) { static const uint8_t _local_ip[] = _TEST_ADDR6_LOCAL; memcpy(iid, &_local_ip[8], sizeof(uint64_t)); iid[0] ^= 0x2; } static int _get_max_pkt_size(netdev_t *dev, void *value, size_t max_len) { return netdev_eth_get(dev, NETOPT_MAX_PACKET_SIZE, value, max_len); } static int _get_src_len(netdev_t *dev, void *value, size_t max_len) { uint16_t *v = value; (void)dev; if (max_len != sizeof(uint16_t)) { return -EOVERFLOW; } *v = sizeof(uint64_t); return sizeof(uint16_t); } static int _get_addr(netdev_t *dev, void *value, size_t max_len) { uint8_t iid[ETHERNET_ADDR_LEN + 2]; uint8_t *addr = value; (void)dev; if (max_len < ETHERNET_ADDR_LEN) { return -EOVERFLOW; } _get_iid(iid); addr[0] = iid[0]; addr[1] = iid[1]; addr[2] = iid[2]; addr[3] = iid[5]; addr[4] = iid[6]; addr[5] = iid[7]; return ETHERNET_ADDR_LEN; } static int _get_addr_len(netdev_t *dev, void *value, size_t max_len) { return netdev_eth_get(dev, NETOPT_ADDR_LEN, value, max_len); } static int _get_device_type(netdev_t *dev, void *value, size_t max_len) { return netdev_eth_get(dev, NETOPT_DEVICE_TYPE, value, max_len); } static int _get_ipv6_iid(netdev_t *dev, void *value, size_t max_len) { (void)dev; if (max_len != sizeof(uint64_t)) { return -EOVERFLOW; } _get_iid(value); return sizeof(uint64_t); } static void _netdev_isr(netdev_t *dev) { dev->event_callback(dev, NETDEV_EVENT_RX_COMPLETE); } static int _netdev_recv(netdev_t *dev, char *buf, int len, void *info) { int res; (void)dev; (void)info; mutex_lock(&_netdev_buffer_mutex); if (buf != NULL) { if ((unsigned)len < _netdev_buffer_size) { mutex_unlock(&_netdev_buffer_mutex); return -ENOBUFS; } memcpy(buf, _netdev_buffer, _netdev_buffer_size); } res = _netdev_buffer_size; mutex_unlock(&_netdev_buffer_mutex); return res; } static int _netdev_send(netdev_t *dev, const struct iovec *vector, int count) { msg_t done = { .type = _SEND_DONE }; unsigned offset = 0; (void)dev; mutex_lock(&_netdev_buffer_mutex); for (int i = 0; i < count; i++) { memcpy(&_netdev_buffer[offset], vector[i].iov_base, vector[i].iov_len); offset += vector[i].iov_len; if (offset > sizeof(_netdev_buffer)) { mutex_unlock(&_netdev_buffer_mutex); return -ENOBUFS; } } mutex_unlock(&_netdev_buffer_mutex); done.content.value = (uint32_t)offset - sizeof(ethernet_hdr_t) - sizeof(udp_hdr_t); msg_send(&done, _check_pid); return offset; } void _net_init(void) { xtimer_init(); msg_init_queue(_msg_queue, _MSG_QUEUE_SIZE); _check_pid = sched_active_pid; netdev_test_setup(&netdev, NULL); netdev_test_set_get_cb(&netdev, NETOPT_SRC_LEN, _get_src_len); netdev_test_set_get_cb(&netdev, NETOPT_MAX_PACKET_SIZE, _get_max_pkt_size); netdev_test_set_get_cb(&netdev, NETOPT_ADDRESS, _get_addr); netdev_test_set_get_cb(&netdev, NETOPT_ADDR_LEN, _get_addr_len); netdev_test_set_get_cb(&netdev, NETOPT_SRC_LEN, _get_addr_len); netdev_test_set_get_cb(&netdev, NETOPT_DEVICE_TYPE, _get_device_type); netdev_test_set_get_cb(&netdev, NETOPT_IPV6_IID, _get_ipv6_iid); netdev_test_set_recv_cb(&netdev, _netdev_recv); netdev_test_set_isr_cb(&netdev, _netdev_isr); /* netdev needs to be set-up */ assert(netdev.netdev.driver); #if LWIP_IPV4 ip4_addr_t local4, mask4, gw4; local4.addr = htonl(_TEST_ADDR4_LOCAL); mask4.addr = htonl(_TEST_ADDR4_MASK); gw4.addr = htonl(_TEST_ADDR4_GW); netif_add(&netif, &local4, &mask4, &gw4, &netdev, lwip_netdev_init, tcpip_input); #else netif_add(&netif, &netdev, lwip_netdev_init, tcpip_input); #endif #if LWIP_IPV6 static const uint8_t local6[] = _TEST_ADDR6_LOCAL; s8_t idx; netif_add_ip6_address(&netif, (ip6_addr_t *)&local6, &idx); for (int i = 0; i <= idx; i++) { netif.ip6_addr_state[i] |= IP6_ADDR_VALID; } #endif netif_set_default(&netif); lwip_bootstrap(); xtimer_sleep(3); /* Let the auto-configuration run warm */ } void _prepare_send_checks(void) { uint8_t remote6[] = _TEST_ADDR6_REMOTE; uint8_t mac[sizeof(uint64_t)]; memcpy(mac, &remote6[8], sizeof(uint64_t)); mac[0] ^= 0x2; mac[3] = mac[5]; mac[4] = mac[6]; mac[5] = mac[7]; netdev_test_set_send_cb(&netdev, _netdev_send); #if LWIP_ARP const ip4_addr_t remote4 = { .addr = htonl(_TEST_ADDR4_REMOTE) }; assert(ERR_OK == etharp_add_static_entry(&remote4, (struct eth_addr *)mac)); #endif #if LWIP_IPV6 memset(destination_cache, 0, LWIP_ND6_NUM_DESTINATIONS * sizeof(struct nd6_destination_cache_entry)); memset(neighbor_cache, 0, LWIP_ND6_NUM_NEIGHBORS * sizeof(struct nd6_neighbor_cache_entry)); for (int i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { struct nd6_neighbor_cache_entry *nc = &neighbor_cache[i]; if (nc->state == ND6_NO_ENTRY) { nc->state = ND6_REACHABLE; memcpy(&nc->next_hop_address, remote6, sizeof(ip6_addr_t)); memcpy(&nc->lladdr, mac, 6); nc->netif = &netif; nc->counter.reachable_time = UINT32_MAX; break; } } #endif } bool _inject_4packet(uint32_t src, uint32_t dst, uint16_t src_port, uint16_t dst_port, void *data, size_t data_len, uint16_t netif) { #if LWIP_IPV4 mutex_lock(&_netdev_buffer_mutex); ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer; struct ip_hdr *ip_hdr = (struct ip_hdr *)(eth_hdr + 1); udp_hdr_t *udp_hdr = (udp_hdr_t *)(ip_hdr + 1); uint8_t *payload = (uint8_t *)(udp_hdr + 1); const uint16_t udp_len = (uint16_t)(sizeof(udp_hdr_t) + data_len); (void)netif; _get_addr((netdev_t *)&netdev, ð_hdr->dst, sizeof(eth_hdr->dst)); eth_hdr->type = byteorder_htons(ETHERTYPE_IPV4); IPH_VHL_SET(ip_hdr, 4, 5); IPH_TOS_SET(ip_hdr, 0); IPH_LEN_SET(ip_hdr, htons(sizeof(struct ip_hdr) + udp_len)); IPH_TTL_SET(ip_hdr, 64); IPH_PROTO_SET(ip_hdr, PROTNUM_UDP); ip_hdr->src.addr = htonl(src); ip_hdr->dest.addr = htonl(dst); IPH_CHKSUM_SET(ip_hdr, 0); IPH_CHKSUM_SET(ip_hdr, inet_chksum(ip_hdr, sizeof(struct ip_hdr))); udp_hdr->src_port = byteorder_htons(src_port); udp_hdr->dst_port = byteorder_htons(dst_port); udp_hdr->length.u16 = IPH_LEN(ip_hdr); udp_hdr->checksum.u16 = 0; memcpy(payload, data, data_len); _netdev_buffer_size = sizeof(ethernet_hdr_t) + sizeof(struct ip_hdr) + sizeof(udp_hdr_t) + data_len; mutex_unlock(&_netdev_buffer_mutex); ((netdev_t *)&netdev)->event_callback((netdev_t *)&netdev, NETDEV_EVENT_ISR); return true; #else (void)src; (void)dst; (void)src_port; (void)dst_port; (void)netif; (void)data; (void)data_len; return false; #endif } bool _inject_6packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, uint16_t src_port, uint16_t dst_port, void *data, size_t data_len, uint16_t netif) { #if LWIP_IPV6 mutex_lock(&_netdev_buffer_mutex); ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer; ipv6_hdr_t *ipv6_hdr = (ipv6_hdr_t *)(eth_hdr + 1); udp_hdr_t *udp_hdr = (udp_hdr_t *)(ipv6_hdr + 1); uint8_t *payload = (uint8_t *)(udp_hdr + 1); const uint16_t udp_len = (uint16_t)(sizeof(udp_hdr_t) + data_len); uint16_t csum = 0; (void)netif; _get_addr((netdev_t *)&netdev, ð_hdr->dst, sizeof(eth_hdr->dst)); eth_hdr->type = byteorder_htons(ETHERTYPE_IPV6); ipv6_hdr_set_version(ipv6_hdr); ipv6_hdr->len = byteorder_htons(udp_len); ipv6_hdr->nh = PROTNUM_UDP; ipv6_hdr->hl = 64; memcpy(&ipv6_hdr->src, src, sizeof(ipv6_hdr->src)); memcpy(&ipv6_hdr->dst, dst, sizeof(ipv6_hdr->dst)); csum = ipv6_hdr_inet_csum(csum, ipv6_hdr, ipv6_hdr->nh, udp_len); udp_hdr->src_port = byteorder_htons(src_port); udp_hdr->dst_port = byteorder_htons(dst_port); udp_hdr->length = ipv6_hdr->len; udp_hdr->checksum.u16 = 0; memcpy(payload, data, data_len); csum = inet_csum(csum, (uint8_t *)udp_hdr, udp_len); if (csum == UINT16_MAX) { udp_hdr->checksum = byteorder_htons(csum); } else { udp_hdr->checksum = byteorder_htons(~csum); } _netdev_buffer_size = sizeof(ethernet_hdr_t) + sizeof(ipv6_hdr_t) + sizeof(udp_hdr_t) + data_len; mutex_unlock(&_netdev_buffer_mutex); ((netdev_t *)&netdev)->event_callback((netdev_t *)&netdev, NETDEV_EVENT_ISR); return true; #else (void)src; (void)dst; (void)src_port; (void)dst_port; (void)netif; (void)data; (void)data_len; return false; #endif } bool _check_net(void) { /* TODO maybe check packet buffer here too? */ return true; } bool _check_4packet(uint32_t src, uint32_t dst, uint16_t src_port, uint16_t dst_port, void *data, size_t data_len, uint16_t netif, bool random_src_port) { #if LWIP_IPV4 msg_t msg; (void)netif; while (data_len != (msg.content.value - sizeof(struct ip_hdr))) { msg_receive(&msg); } mutex_lock(&_netdev_buffer_mutex); ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer; struct ip_hdr *ip_hdr = (struct ip_hdr *)(eth_hdr + 1); udp_hdr_t *udp_hdr = (udp_hdr_t *)(ip_hdr + 1); uint8_t *payload = (uint8_t *)(udp_hdr + 1); uint16_t payload_len = byteorder_ntohs(udp_hdr->length) - sizeof(udp_hdr_t); const bool ip_correct = ((src == 0) || (src = ip_hdr->src.addr)) && (dst = ip_hdr->dest.addr); const bool udp_correct = (random_src_port || (src_port == byteorder_ntohs(udp_hdr->src_port))) && (dst_port == byteorder_ntohs(udp_hdr->dst_port)); const bool payload_correct = (data_len == payload_len) && (memcmp(data, payload, data_len) == 0); mutex_unlock(&_netdev_buffer_mutex); return ip_correct && udp_correct && payload_correct; #else (void)src; (void)dst; (void)src_port; (void)dst_port; (void)netif; (void)data; (void)data_len; (void)random_src_port; return false; #endif } bool _check_6packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, uint16_t src_port, uint16_t dst_port, void *data, size_t data_len, uint16_t netif, bool random_src_port) { #if LWIP_IPV6 msg_t msg; (void)netif; while (data_len != (msg.content.value - sizeof(ipv6_hdr_t))) { msg_receive(&msg); } mutex_lock(&_netdev_buffer_mutex); ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer; ipv6_hdr_t *ipv6_hdr = (ipv6_hdr_t *)(eth_hdr + 1); udp_hdr_t *udp_hdr = (udp_hdr_t *)(ipv6_hdr + 1); uint8_t *payload = (uint8_t *)(udp_hdr + 1); uint16_t payload_len = byteorder_ntohs(udp_hdr->length) - sizeof(udp_hdr_t); const bool ip_correct = (ipv6_addr_is_unspecified(src) || ipv6_addr_equal(src, &ipv6_hdr->src)) && ipv6_addr_equal(dst, &ipv6_hdr->dst); const bool udp_correct = (random_src_port || (src_port == byteorder_ntohs(udp_hdr->src_port))) && (dst_port == byteorder_ntohs(udp_hdr->dst_port)); const bool payload_correct = (data_len == payload_len) && (memcmp(data, payload, data_len) == 0); mutex_unlock(&_netdev_buffer_mutex); return ip_correct && udp_correct && payload_correct; #else (void)src; (void)dst; (void)src_port; (void)dst_port; (void)netif; (void)data; (void)data_len; (void)random_src_port; return false; #endif } /** @} */