diff --git a/apps/lwip/CMakeLists.txt b/apps/lwip/CMakeLists.txt new file mode 100644 index 00000000..c293c007 --- /dev/null +++ b/apps/lwip/CMakeLists.txt @@ -0,0 +1,62 @@ +# +# Copyright 2019, Data61 +# Commonwealth Scientific and Industrial Research Organisation (CSIRO) +# ABN 41 687 119 230. +# +# This software may be distributed and modified according to the terms of +# the BSD 2-Clause license. Note that NO WARRANTY is provided. +# See "LICENSE_BSD2.txt" for details. +# +# @TAG(DATA61_BSD) +# + +cmake_minimum_required(VERSION 3.8.2) + +project(lwip C) + +includeGlobalComponents() + +set(LibLwip ON CACHE BOOL "" FORCE) +set(LibPicotcpBsd OFF CACHE BOOL "" FORCE) +set(LibEthdriverNumPreallocatedBuffers 32 CACHE STRING "" FORCE) + +# For x86, we map DMA frames into the IOMMU to use as buffers for the +# Ethernet device. The VKA and VSpace libraries do not like pages that +# are not 4K in size. +set(CAmkESDMALargeFramePromotion OFF CACHE BOOL "" FORCE) + +# The app has only been tested on hardware, and not on QEMU +set(SIMULATION OFF CACHE BOOL "" FORCE) +if("${KernelArch}" STREQUAL "x86") + # The IOMMU is required for the Ethdriver component on x86 + set(KernelIOMMU ON CACHE BOOL "" FORCE) +endif() + +set(CAmkESCPP ON CACHE BOOL "" FORCE) +if("${KernelArch}" STREQUAL "x86") + set(cpp_define -DKernelArchX86) +elseif("${KernelArch}" STREQUAL "arm") + set(cpp_define -DKernelArchArm) +endif() + +set(LWIPSERVER_IP_ADDR "" CACHE STRING "IP address for the LWIP server component") + +DeclareCAmkESComponent( + Server + SOURCES + components/Server/src/main.c + components/Server/src/eth_interface.c + INCLUDES + components/include/ + LIBS + sel4utils + lwip + ethdrivers +) + +DeclareCAmkESRootserver( + lwip.camkes + CPP_FLAGS + ${cpp_define} + -DLWIPSERVER_IP_ADDR=\"${LWIPSERVER_IP_ADDR}\" +) diff --git a/apps/lwip/README.md b/apps/lwip/README.md new file mode 100644 index 00000000..3a09eb34 --- /dev/null +++ b/apps/lwip/README.md @@ -0,0 +1,47 @@ +# LWIP app + +LWIP echo server with any luck, based off the picoserver + +## Troubleshooting + +### ping doesn't work + +Try ```arpping``` + +```bash +arpping -i eth0 192.168.168.2 +``` + +You should see many packets going back and forth + +Check that you have set the interface up [correctly](https://www.nongnu.org/lwip/2_1_x/sys_init.html) + +and you can add logging to LWIP to help for example + +```C +/* debugging */ +#define LWIP_DEBUG 1 +#define NETIF_DEBUG LWIP_DBG_ON +#define ICMP_DEBUG LWIP_DBG_ON +#define IP_DEBUG LWIP_DBG_ON | LWIP_DBG_TRACE +#define RAW_DEBUG LWIP_DBG_ON +#define UDP_DEBUG LWIP_DBG_ON +#define TCP_DEBUG LWIP_DBG_ON +#define INET_DEBUG LWIP_DBG_ON +#define ETHARP_DEBUG LWIP_DBG_ON +#define TIMERS_DEBUG LWIP_DBG_ON +``` + +### arp ping only returns one packet + +```bash +arpping -i eth0 192.168.168.2 +``` + +try pinging broadcast + +```bash +arpping -b -i eth0 192.168.168.2 +``` + +if this works it is likely you have different mac addresses for your hardware and lwip stack. diff --git a/apps/lwip/components/Server/src/eth_interface.c b/apps/lwip/components/Server/src/eth_interface.c new file mode 100644 index 00000000..8031c71f --- /dev/null +++ b/apps/lwip/components/Server/src/eth_interface.c @@ -0,0 +1,219 @@ +/* + * Copyright 2019, Data61 + * Commonwealth Scientific and Industrial Research Organisation (CSIRO) + * ABN 41 687 119 230. + * + * This software may be distributed and modified according to the terms of + * the GNU General Public License version 2. Note that NO WARRANTY is provided. + * See "LICENSE_GPLv2.txt" for details. + * + * @TAG(DATA61_GPL) + */ + +#include + +#include +#include + +/* remove the camkes ERR_IF definition to not overlap with lwip */ +#undef ERR_IF + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static lwip_iface_t lwip_driver = {0}; + +static void low_level_init(struct eth_driver *driver, uint8_t *mac, int *mtu) +{ + // 1500 is the standard ethernet MTU at the network layer. + *mtu = 1500; + ethdriver_mac(&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); +} + +extern void *ethdriver_buf; + +static void raw_poll(struct eth_driver *driver) +{ + int len; + int status; + status = ethdriver_rx(&len); + while (status != -1) { + void *buf; + void *cookie; + buf = (void *)driver->i_cb.allocate_rx_buf(driver->cb_cookie, len, &cookie); + if (buf) { + // Only proceed if we successfully got a buffer. If not, the packet will simply be dropped. + // UDP doesn't mind, and the packet will be resent for TCP due to us not sending an ACK. + // This prevents crashing in a DDOS attack or malicious packet that is too large. + memcpy(buf, (void *)ethdriver_buf, len); + driver->i_cb.rx_complete(driver->cb_cookie, 1, &cookie, (unsigned int *)&len); + } + + if (status == 1) { + status = ethdriver_rx(&len); + } else { + /* if status is 0 we already saw the last packet */ + assert(status == 0); + status = -1; + } + } +} + +static int raw_tx(struct eth_driver *driver, unsigned int num, uintptr_t *phys, unsigned int *len, void *cookie) +{ + unsigned int total_len = 0; + int i; + void *p = (void *)ethdriver_buf; + for (i = 0; i < num; i++) { + memcpy(p + total_len, (void *)phys[i], len[i]); + total_len += len[i]; + } + ethdriver_tx(total_len); + return ETHIF_TX_COMPLETE; +} + +static void handle_irq(struct eth_driver *driver, int irq) +{ + raw_poll(driver); +} + +void interface_tick() { + handle_irq(&lwip_driver.driver, 0); +} + +static struct raw_iface_funcs iface_fns = { + .raw_handleIRQ = handle_irq, + .print_state = NULL, + .low_level_init = low_level_init, + .raw_tx = raw_tx, + .raw_poll = raw_poll +}; + +static int ethdriver_init(struct eth_driver *eth_driver, ps_io_ops_t io_ops, void *config) +{ + eth_driver->eth_data = NULL; + eth_driver->dma_alignment = 1; + eth_driver->i_fn = iface_fns; + return 0; +} + +static void *malloc_dma_alloc(void *cookie, size_t size, int align, int cached, ps_mem_flags_t flags) +{ + assert(cached); + int error; + void *ret = malloc(size); + if (ret == NULL) { + ZF_LOGE("ERR: Failed to allocate %d\n", size); + return NULL; + } + return ret; +} + +static void malloc_dma_free(void *cookie, void *addr, size_t size) +{ + free(addr); +} + +static uintptr_t malloc_dma_pin(void *cookie, void *addr, size_t size) +{ + return (uintptr_t)addr; +} + +static void malloc_dma_unpin(void *cookie, void *addr, size_t size) +{ +} + +static void malloc_dma_cache_op(void *cookie, void *addr, size_t size, dma_cache_op_t op) +{ +} + +void udp_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *ip, u16_t port) { + printf("Received udp packet of %d bytes from %s:%d\n", p->len, ipaddr_ntoa(ip), port); + char *data = malloc(p->len + 1); + strncpy(data, p->payload, p->len); + data[p->len] = '\0'; + + printf("Data: %s\n", data); + free(data); + /* send the data back */ + udp_sendto(pcb, p, ip, port); + pbuf_free(p); +} + +void netif_link_callback(struct netif *netif) { + if (netif_is_link_up(netif)) { + int err; + printf("Link is up\n"); + printf("IPADDR is %s\n", ipaddr_ntoa(&netif->ip_addr)); + struct udp_pcb *udp_conn = udp_new(); + ZF_LOGF_IF(!udp_conn, "Failed to create udp connection"); + + err = udp_bind(udp_conn, IP_ADDR_ANY, 7); + ZF_LOGF_IF(err != ERR_OK, "Failed to bind port 7"); + udp_recv(udp_conn, udp_recv_callback, NULL); + } +} + +static struct netif *init_interface(lwip_iface_t *lwip) { + assert(lwip->netif == NULL); + struct netif *netif = malloc(sizeof(*netif)); + err_t err; + ip_addr_t addr, mask, gw; + IP4_ADDR(&mask, 255,255,255,0); + IP4_ADDR(&gw, 192,168,168,1); + IP4_ADDR(&addr, 192,168,168,2); + + /* Initialise after configuration */ + lwip->netif = netif_add(netif, &addr, &mask, &gw, lwip, ethif_get_ethif_init(lwip), ethernet_input); + + assert(lwip->netif != NULL); + netif_set_default(lwip->netif); + netif_set_status_callback(lwip->netif, netif_link_callback); + netif_set_link_up(lwip->netif); + netif_set_up(netif); + // err = dhcp_start(lwip->netif); + // ZF_LOGF_IF(err != ERR_OK, "Failed to start dhcp"); + + return netif; +} + +static ps_io_ops_t io_ops; + +void eth_init() +{ + memset(&io_ops, 0, sizeof(io_ops)); + io_ops.dma_manager = (ps_dma_man_t) { + .cookie = NULL, + .dma_alloc_fn = malloc_dma_alloc, + .dma_free_fn = malloc_dma_free, + .dma_pin_fn = malloc_dma_pin, + .dma_unpin_fn = malloc_dma_unpin, + .dma_cache_op_fn = malloc_dma_cache_op + }; + + /* Create a driver. This utilises preallocated buffers, backed up by malloc above */ + if (NULL == ethif_new_lwip_driver_no_malloc(io_ops, NULL, ethdriver_init, NULL, &lwip_driver)) { + ZF_LOGF("Failed to create the lwip Driver"); + } + + /* Initialise LWIP stack */ + lwip_init(); + + // lwip iface + struct netif *iface = init_interface(&lwip_driver); + if (iface == NULL) { + ZF_LOGF("Failed to create the lwip interface"); + } +} diff --git a/apps/lwip/components/Server/src/main.c b/apps/lwip/components/Server/src/main.c new file mode 100644 index 00000000..abde0b06 --- /dev/null +++ b/apps/lwip/components/Server/src/main.c @@ -0,0 +1,43 @@ +#include +#include +#include +/* remove the camkes ERR_IF definition to not overlap with lwip */ +#undef ERR_IF + +#include +#include + +#define LWIP_TICK_MS 100 + +static uint32_t counter_s = 0; + +void eth_init(); +void interface_tick(); + +u32_t sys_now(void) { + return counter_s/10; +} + + +void pre_init(void) +{ + eth_init(); + /* Start the timer for the TCP stack */ + timer_periodic(0, NS_IN_MS * LWIP_TICK_MS); +} +/* Callback that gets called when the timer fires. */ +void timer_complete_callback(void) +{ + int err = mut_lock(); + counter_s +=1; + sys_check_timeouts(); + interface_tick(); + err = mut_unlock(); +} + + +int run(void) +{ + int retval = 0; + return retval; +} \ No newline at end of file diff --git a/apps/lwip/lwip.camkes b/apps/lwip/lwip.camkes new file mode 100644 index 00000000..e6beb578 --- /dev/null +++ b/apps/lwip/lwip.camkes @@ -0,0 +1,76 @@ +/* + * Copyright 2019, Data61 + * Commonwealth Scientific and Industrial Research Organisation (CSIRO) + * ABN 41 687 119 230. + * + * This software may be distributed and modified according to the terms of + * the BSD 2-Clause license. Note that NO WARRANTY is provided. + * See "LICENSE_BSD2.txt" for details. + * + * @TAG(DATA61_BSD) + */ + +import ; +import ; +import ; +import ; + +component Server { + control; + + has mutex mut; + + uses Timer timer; + consumes Notification timer_complete; + + uses Ethdriver ethdriver; + + /* This field is exclusive to this application and not necessary elsewhere */ + attribute string ip_addr; + attribute string multicast_addr; + attribute int num_clients; +} + + +assembly { + composition { + + /* PicoServer component */ + component Server server; + component EthdriverARMPlat ethdriver; + /* Timer component */ + component TimeServer time_server; + + /* Timer to Server */ + connection seL4TimeServer picoserver_timer(from server.timer, to time_server.the_timer); + connection seL4GlobalAsynchCallback picoserver_timer_int(from time_server.timer_notification, to server.timer_complete); + + /* Driver to Server */ + connection seL4Ethdriver eth_driver_conn(from server.ethdriver, to ethdriver.client); + + } + + configuration { + /* Ethernet driver configuration */ + ethdriver.simple = true; + ethdriver.cnode_size_bits = 12; + ethdriver.simple_untyped20_pool = 2; + ethdriver.heap_size = 0x10000; + ethdriver.dma_pool = 0x200000; + + time_server.timers_per_client = 8; + + /* IP address to assign to the networking device */ + server.ip_addr = LWIPSERVER_IP_ADDR; + server.multicast_addr = "0.0.0.0"; + /* This should match the number of clients connected to this component */ + server.num_clients = 2; + + server.ethdriver_shmem_size = 0x1000; + server.ethdriver_mac = [0x00, 0x19, 0xb8, 0x00, 0xf0, 0xA3]; + server.heap_size = 0x40000; + /* Ethernet driver configuration */ + ethdriver.promiscuous_mode = 0; + ethdriver.mac <- server.ethdriver_mac; + } +}