devif: prototype of UDP queue
authorRoni Häcki <roni.haecki@inf.ethz.ch>
Fri, 11 Aug 2017 12:29:16 +0000 (14:29 +0200)
committerRoni Häcki <roni.haecki@inf.ethz.ch>
Fri, 11 Aug 2017 12:29:16 +0000 (14:29 +0200)
Signed-off-by: Roni Häcki <roni.haecki@inf.ethz.ch>

errors/errno.fugu
include/devif/backends/net/udp.h [new file with mode: 0644]
lib/devif/backends/net/udp/Hakefile [new file with mode: 0644]
lib/devif/backends/net/udp/devif_backend_udp.c [new file with mode: 0644]
lib/devif/backends/net/udp/headers.h [new file with mode: 0644]

index e41cfc7..6ce326b 100755 (executable)
@@ -1240,6 +1240,13 @@ errors cpuid DEVQ_ERR_ {
     failure DESCQ_INIT              "Failure in descriptor queue init",
 };
 
+errors cpuid UDPQ_ERR_ {
+    failure WRONG_PORT              "Received packet with wrong src/dst port",
+    failure WRONG_IP                "Received packet with wrong src/dst IP",
+    failure NOT_UDP                 "Received packet is not UDP",
+    failure UNKNOWN_BUF_TYPE        "Neither RX or TX buffer",
+};
+
 // errors generated by network cards
 errors sfn NIC_ERR_{
     failure IO                   "Error during card IO",
diff --git a/include/devif/backends/net/udp.h b/include/devif/backends/net/udp.h
new file mode 100644 (file)
index 0000000..0e177d6
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#ifndef DEVIF_UDP_H_
+#define DEVIF_UDP_H_ 1
+
+
+#include <barrelfish/barrelfish.h>
+
+struct udp_q;
+    
+struct __attribute__((packed)) eth_addr {
+  uint8_t addr[6];
+};
+
+/**
+ *  @param q        udp queue to destroy
+ */
+errval_t udp_destroy(struct udp_q* q);
+
+/**
+ * @brief initalized a queue that can send UPD packets with a certain requirements.
+ *        all other packets received on this queue will be dropped.
+ *
+ * @param q            udp queue return value
+ * @param card_name    the card name from which a hardware queue will be used
+ *                      to send UPD packets. Internally a queue to the device with
+ *                      the card_name will be initalized
+ * @param src_port     UDP source port
+ * @param dst_port     UPD destination port
+ * @param src_ip       Source IP
+ * @param dst_ip       Destination IP
+ * @param src_mac      Source MAC addressy              
+ * @param dst_mac      Destination MAC address
+ * @param interrupt    Interrupt handler
+ * @param poll         If the queue is polled or should use interrupts             
+ *
+ */
+errval_t udp_create(struct udp_q** q, const char* card_name, 
+                    uint16_t src_port, uint16_t dst_port,
+                    uint32_t src_ip, uint32_t dst_ip,
+                    struct eth_addr src_mac, struct eth_addr dst_mac,
+                    void(*interrupt)(void*), bool poll);
+/*
+ * @brief  Writes into a buffer so that we still have space to add the headers
+ *
+ * @param q           udp queue to which the region was registered to
+ * @param rid         The region ID in which the buffer to write to is contained
+ * @param offset      The offset into the region at which the buffer start
+ * @param data        The data to write into the buffer
+ * @param len         The length of the data
+ * 
+ */
+errval_t udp_write_buffer(struct udp_q* q, regionid_t rid, genoffset_t offset,
+                          void* data, uint16_t len);
+#endif /* DEVIF_UDP_H_ */
diff --git a/lib/devif/backends/net/udp/Hakefile b/lib/devif/backends/net/udp/Hakefile
new file mode 100644 (file)
index 0000000..9a26cfa
--- /dev/null
@@ -0,0 +1,23 @@
+--------------------------------------------------------------------------
+-- Copyright (c) 2017, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for lib/devif/backends/udp/
+-- 
+-- Ethernet backend implementation
+--
+--------------------------------------------------------------------------
+
+  [ 
+    build library { target = "devif_backend_udp",
+                      cFiles = [ "devif_backend_udp.c"],
+                      addLibraries = libDeps ["devif_internal", "net", "lwip2"],
+                      addIncludes = ["/lib/devif/"]
+                    }
+  ]
+
+
diff --git a/lib/devif/backends/net/udp/devif_backend_udp.c b/lib/devif/backends/net/udp/devif_backend_udp.c
new file mode 100644 (file)
index 0000000..5bdb8fb
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitätstrasse 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/waitset.h>
+#include <devif/queue_interface.h>
+#include <devif/backends/net/udp.h>
+#include <lwip/inet_chksum.h>
+#include <lwip/lwip/inet.h>
+#include <net_interfaces/flags.h>
+#include <net/net.h>
+#include <net/net_queue.h>
+#include <net/net_filter.h>
+#include "../../../queue_interface_internal.h"
+#include "headers.h"
+
+#define MAX_NUM_REGIONS 64
+
+//#define DEBUG_ENABLED
+
+#if defined(DEBUG_ENABLED) 
+#define DEBUG(x...) do { printf("UDP_QUEUE: %s.%d:%s:%d: ", \
+            disp_name(), disp_get_core_id(), __func__, __LINE__); \
+                printf(x);\
+        } while (0)
+
+#else
+#define DEBUG(x...) ((void)0)
+#endif 
+
+struct region_vaddr {
+    void* va;
+    regionid_t rid;
+};
+
+struct pkt_udp_headers {
+    struct eth_hdr eth;
+    struct ip_hdr ip;
+    struct udp_hdr udp;
+} __attribute__ ((packed));
+
+struct udp_q {
+    struct devq my_q;
+    struct devq* q;
+    struct pkt_udp_headers header; // can fill in this header and reuse it by copying
+    struct region_vaddr regions[MAX_NUM_REGIONS];
+    struct net_filter_state* filter;
+};
+
+
+#ifdef DEBUG_ENABLED
+static void print_buffer(struct udp_q* q, void* start, uint64_t len)
+{
+    uint8_t* buf = (uint8_t*) start;
+    printf("Packet in region at address %p len %zu \n",
+           buf, len);
+    for (int i = 0; i < len; i+=2) {
+        if (((i % 16) == 0) && i > 0) {
+            printf("\n");
+        }
+        printf("%2X", buf[i]);
+        printf("%2X ", buf[i+1]);
+    }
+    printf("\n");
+}
+#endif
+
+static errval_t udp_register(struct devq* q, struct capref cap,
+                            regionid_t rid) 
+{
+       
+    errval_t err;
+    struct frame_identity frameid = { .base = 0, .bytes = 0 };
+
+    struct udp_q* que = (struct udp_q*) q;
+
+    // Map device registers
+    invoke_frame_identify(cap, &frameid);
+
+    err = vspace_map_one_frame_attr(&que->regions[rid % MAX_NUM_REGIONS].va, 
+                                    frameid.bytes, cap, VREGION_FLAGS_READ_WRITE, 
+                                    NULL, NULL);
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "vspace_map_one_frame failed");
+        return err;
+    }
+    que->regions[rid % MAX_NUM_REGIONS].rid = rid;
+    DEBUG("id-%d va-%p \n", que->regions[rid % MAX_NUM_REGIONS].rid, 
+          que->regions[rid % MAX_NUM_REGIONS].va);
+
+    return que->q->f.reg(que->q, cap, rid);
+}
+
+static errval_t udp_deregister(struct devq* q, regionid_t rid) 
+{
+    
+    struct udp_q* que = (struct udp_q*) q;
+    que->regions[rid % MAX_NUM_REGIONS].va = NULL;
+    que->regions[rid % MAX_NUM_REGIONS].rid = 0;
+    return que->q->f.dereg(que->q, rid);
+}
+
+
+static errval_t udp_control(struct devq* q, uint64_t cmd, uint64_t value,
+                           uint64_t* result)
+{
+    struct udp_q* que = (struct udp_q*) q;
+    return que->q->f.ctrl(que->q, cmd, value, result);
+}
+
+
+static errval_t udp_notify(struct devq* q)
+{
+    struct udp_q* que = (struct udp_q*) q;
+    return que->q->f.notify(que->q);
+}
+
+static errval_t udp_enqueue(struct devq* q, regionid_t rid, 
+                           genoffset_t offset, genoffset_t length,
+                           genoffset_t valid_data, genoffset_t valid_length,
+                           uint64_t flags)
+{
+
+    // for now limit length
+    //  TODO fragmentation
+
+    struct udp_q* que = (struct udp_q*) q;
+    if (flags & NETIF_TXFLAG) {
+        
+        DEBUG("TX rid: %d offset %ld length %ld valid_length %ld \n", rid, offset, 
+              length, valid_length);
+        assert(valid_length <= 1500);    
+        que->header.udp.len = htons(valid_length + sizeof(struct udp_hdr));
+        que->header.ip._len = htons(valid_length + sizeof(struct udp_hdr) + IP_HLEN);   
+        que->header.ip._chksum = inet_chksum(&que->header.ip, IP_HLEN);
+
+        assert(que->regions[rid % MAX_NUM_REGIONS].va != NULL);
+
+        uint8_t* start = (uint8_t*) que->regions[rid % MAX_NUM_REGIONS].va + 
+                         offset + valid_data;   
+
+        memcpy(start, &que->header, sizeof(que->header));   
+
+        return que->q->f.enq(que->q, rid, offset, length, valid_data, 
+                             valid_length+sizeof(struct pkt_udp_headers), flags);
+    } 
+
+    if (flags & NETIF_RXFLAG) {
+        assert(valid_length <= 2048);    
+        DEBUG("RX rid: %d offset %ld length %ld valid_length %ld \n", rid, offset, 
+              length, valid_length);
+        return que->q->f.enq(que->q, rid, offset, length, valid_data, 
+                             valid_length, flags);
+    } 
+
+    return UDPQ_ERR_UNKNOWN_BUF_TYPE;
+}
+
+static errval_t udp_dequeue(struct devq* q, regionid_t* rid, genoffset_t* offset,
+                           genoffset_t* length, genoffset_t* valid_data,
+                           genoffset_t* valid_length, uint64_t* flags)
+{
+    errval_t err;
+    struct udp_q* que = (struct udp_q*) q;
+
+    err = que->q->f.deq(que->q, rid, offset, length, valid_data, valid_length, flags);
+    if (err_is_fail(err)) {    
+        return err;
+    }
+
+    if (*flags & NETIF_RXFLAG) {
+        DEBUG("RX rid: %d offset %ld valid_data %ld length %ld va %p \n", *rid, 
+              *offset, *valid_data, 
+              *valid_length, que->regions[*rid % MAX_NUM_REGIONS].va + *offset + *valid_data);
+
+        struct pkt_udp_headers* header = (struct pkt_udp_headers*) 
+                                         (que->regions[*rid % MAX_NUM_REGIONS].va +
+                                         *offset + *valid_data);
+        // is this a UPD packet       
+        if (header->ip._proto != 0x11) {
+            printf("UDP queue: dropping packet, not udp %d \n",
+                   header->ip._proto);
+            err = que->q->f.enq(que->q, *rid, *offset, *length, 0, 0, NETIF_RXFLAG);
+            return err_push(err, UDPQ_ERR_NOT_UDP);
+        }
+
+        // IP checksum
+        if (header->ip._chksum == inet_chksum(&header->ip, IP_HLEN)) {
+            printf("UDP queue: dropping packet wrong checksum \n");
+            err = que->q->f.enq(que->q, *rid, *offset, *length, 0, 0, NETIF_RXFLAG);
+            return err_push(err, UDPQ_ERR_NOT_UDP);
+        }
+
+        // Correct port for this queue?
+        if (header->udp.dest != que->header.udp.dest) {
+            printf("UDP queue: dropping packet, wrong port %d %d \n",
+                   header->udp.dest, que->header.udp.dest);
+            err = que->q->f.enq(que->q, *rid, *offset, *length, 0, 0, NETIF_RXFLAG);
+            return err_push(err, UDPQ_ERR_WRONG_PORT);
+        }
+
+        // Correct ip for this queue?
+        if (header->ip.src != que->header.ip.dest) {
+            printf("UDP queue: dropping packet, wrong IP is %lu should be %lu\n",
+                   header->ip.src, que->header.ip.dest);
+            //print_buffer(que, header, *valid_length);
+            //print_buffer(que, &que->header, sizeof(que->header));
+            err = que->q->f.enq(que->q, *rid, *offset, *length, 0, 0, NETIF_RXFLAG);
+            return err_push(err, UDPQ_ERR_WRONG_IP);
+        }
+        
+#ifdef DEBUG_ENABLED
+        print_buffer(que, que->regions[*rid % MAX_NUM_REGIONS].va + *offset, *valid_length);
+#endif
+
+        *valid_data += sizeof(que->header);
+        *valid_length = ntohs(header->udp.len) - sizeof(que->header.udp);
+        //print_buffer(que, que->regions[*rid % MAX_NUM_REGIONS].va + *offset+ *valid_data, *valid_length);
+        return SYS_ERR_OK;
+    }
+
+#ifdef DEBUG_ENABLED
+    DEBUG("TX rid: %d offset %ld length %ld \n", *rid, *offset, 
+          *valid_length);
+#endif
+
+    return SYS_ERR_OK;
+}
+
+/*
+ * Public functions
+ *
+ */
+errval_t udp_create(struct udp_q** q, const char* card_name, 
+                    uint16_t src_port, uint16_t dst_port,
+                    uint32_t src_ip, uint32_t dst_ip,
+                    struct eth_addr src_mac, struct eth_addr dst_mac,
+                    inthandler_t interrupt, bool poll)
+{
+    errval_t err;
+    struct udp_q* que;
+    que = calloc(1, sizeof(struct udp_q));
+    assert(que);
+
+    // init other queue
+    uint64_t qid;
+    err = net_queue_create(interrupt, card_name, &qid, poll, &que->q);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    err = net_filter_init(&que->filter, card_name);
+    if (err_is_fail(err)) {
+        return err;
+    }  
+
+    struct net_filter_ip ip = {
+        .qid = qid,
+        .ip_src = dst_ip,
+        .ip_dst = src_ip,
+        .port_dst = dst_port,
+        .type = NET_FILTER_UDP,    
+    };
+
+    err = net_filter_ip_install(que->filter, &ip);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    err = devq_init(&que->my_q, false);
+    if (err_is_fail(err)) {
+        errval_t err2;
+        err2 = net_filter_ip_remove(que->filter, &ip);
+        if (err_is_fail(err)) {
+            return err_push(err2, err);
+        }
+        return err;
+    }   
+
+    // fill in header that is reused for each packet
+    // Ethernet
+    memcpy(&(que->header.eth.dest.addr), &dst_mac, ETH_HWADDR_LEN);
+    memcpy(&(que->header.eth.src.addr), &src_mac, ETH_HWADDR_LEN);
+    que->header.eth.type = htons(ETHTYPE_IP);
+
+    // IP
+    que->header.ip._v_hl = 69;
+    IPH_TOS_SET(&que->header.ip, 0x0);
+    IPH_ID_SET(&que->header.ip, htons(0x3));
+    que->header.ip._offset = htons(IP_DF);
+    que->header.ip._proto = 0x11; // UDP
+    que->header.ip._ttl = 0x40; // 64
+    que->header.ip.src = htonl(src_ip);
+    que->header.ip.dest = htonl(dst_ip);
+
+    // UDP fields
+    que->header.udp.src = htons(src_port);
+    que->header.udp.dest = htons(dst_port);
+    que->header.udp.chksum = 0x0;
+
+
+    que->my_q.f.reg = udp_register;
+    que->my_q.f.dereg = udp_deregister;
+    que->my_q.f.ctrl = udp_control;
+    que->my_q.f.notify = udp_notify;
+    que->my_q.f.enq = udp_enqueue;
+    que->my_q.f.deq = udp_dequeue;
+    *q = que;
+    return SYS_ERR_OK;
+}
+
+errval_t udp_destroy(struct udp_q* q)
+{
+    // TODO destroy q->q;
+    free(q);    
+
+    return SYS_ERR_OK;
+}
+
+errval_t udp_write_buffer(struct udp_q* q, regionid_t rid, genoffset_t offset,
+                          void* data, uint16_t len) 
+{
+    assert(len <= 1500);
+    if (q->regions[rid % MAX_NUM_REGIONS].va != NULL) {
+        uint8_t* start = q->regions[rid % MAX_NUM_REGIONS].va + offset;
+        memcpy(start + sizeof(struct pkt_udp_headers), data, len);
+        return SYS_ERR_OK;
+    } else {
+        return DEVQ_ERR_INVALID_REGION_ARGS;
+    }
+}
+
+
+
diff --git a/lib/devif/backends/net/udp/headers.h b/lib/devif/backends/net/udp/headers.h
new file mode 100644 (file)
index 0000000..2517214
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich. 
+ * Attn: Systems Group.
+ */
+
+#ifndef HEADERS_IMPL_H
+#define HEADERS_IMPL_H
+
+#define ETH_HWADDR_LEN     6
+
+#define PACK_STRUCT_FIELD(x) x
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_END
+
+PACK_STRUCT_BEGIN
+/** Ethernet header */
+struct eth_hdr {
+  PACK_STRUCT_FIELD(struct eth_addr dest);
+  PACK_STRUCT_FIELD(struct eth_addr src);
+  PACK_STRUCT_FIELD(uint16_t type);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+
+#define ETH_HEADER_LEN 14
+
+#define ETHTYPE_IP        0x0800U
+
+PACK_STRUCT_BEGIN
+struct ip_hdr {
+  /* version / header length */
+  PACK_STRUCT_FIELD(uint8_t _v_hl);
+  /* type of service */
+  PACK_STRUCT_FIELD(uint8_t _tos);
+  /* total length */
+  PACK_STRUCT_FIELD(uint16_t _len);
+  /* identification */
+  PACK_STRUCT_FIELD(uint16_t _id);
+  /* fragment offset field */
+  PACK_STRUCT_FIELD(uint16_t _offset);
+#define IP_RF 0x8000U        /* reserved fragment flag */
+#define IP_DF 0x4000U        /* dont fragment flag */
+#define IP_MF 0x2000U        /* more fragments flag */
+#define IP_OFFMASK 0x1fffU   /* mask for fragmenting bits */
+  /* time to live */
+  PACK_STRUCT_FIELD(uint8_t _ttl);
+  /* protocol*/
+  PACK_STRUCT_FIELD(uint8_t _proto);
+  /* checksum */
+  PACK_STRUCT_FIELD(uint16_t _chksum);
+  /* source and destination IP addresses */
+  PACK_STRUCT_FIELD(uint32_t src);
+  PACK_STRUCT_FIELD(uint32_t dest); 
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+
+#define IPH_V(hdr)  ((hdr)->_v_hl >> 4)
+#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f)
+#define IPH_TOS(hdr) ((hdr)->_tos)
+#define IPH_LEN(hdr) ((hdr)->_len)
+#define IPH_ID(hdr) ((hdr)->_id)
+#define IPH_OFFSET(hdr) ((hdr)->_offset)
+#define IPH_TTL(hdr) ((hdr)->_ttl)
+#define IPH_PROTO(hdr) ((hdr)->_proto)
+#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
+
+#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl))
+#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos)
+#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
+#define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
+#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
+#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (uint8_t)(ttl)
+#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (uint8_t)(proto)
+#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
+
+#define IP_HLEN 20
+
+#define IP_PROTO_IP      0
+#define IP_PROTO_UDP     17
+
+PACK_STRUCT_BEGIN
+struct udp_hdr {
+  PACK_STRUCT_FIELD(uint16_t src);
+  PACK_STRUCT_FIELD(uint16_t dest);  /* src/dest UDP ports */
+  PACK_STRUCT_FIELD(uint16_t len);
+  PACK_STRUCT_FIELD(uint16_t chksum);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+
+#endif