Socket infrastructure change and basic udp send.
authorDavid Zhu <yuzhu@cs.berkeley.edu>
Sat, 12 Mar 2011 05:49:22 +0000 (21:49 -0800)
committerDavid Zhu <yuzhu@cs.berkeley.edu>
Mon, 2 Apr 2012 22:03:28 +0000 (15:03 -0700)
Added a test program to test udp send.
Added support for mbuf like pbuf.
Added udp send functionality to e1000 and rl drivers.
Work in progress commit towards a basic working network system.

29 files changed:
kern/arch/i686/e1000.c
kern/arch/i686/e1000.h
kern/arch/i686/nic_common.c
kern/arch/i686/nic_common.h
kern/arch/i686/rl8168.c
kern/arch/i686/rl8168.h
kern/arch/i686/types.h
kern/include/bits/netinet.h
kern/include/net.h
kern/include/net/dev.h [new file with mode: 0644]
kern/include/net/ip.h [new file with mode: 0644]
kern/include/net/pbuf.h [new file with mode: 0644]
kern/include/net/udp.h [new file with mode: 0644]
kern/include/ros/bits/syscall.h
kern/include/ros/common.h
kern/include/socket.h [new file with mode: 0644]
kern/include/sys/uio.h [new file with mode: 0644]
kern/include/vfs.h
kern/src/Makefrag
kern/src/init.c
kern/src/net/ip.c [new file with mode: 0644]
kern/src/net/pbuf.c [new file with mode: 0644]
kern/src/net/udp.c [new file with mode: 0644]
kern/src/socket.c [new file with mode: 0644]
kern/src/syscall.c
tests/udp_test.c
tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/recvfrom.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/sendto.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/socket.c

index 62802b4..17b23ee 100644 (file)
@@ -93,7 +93,7 @@ void e1000_dump_stats() {
        while (offset <= 0x040FC) {
                if ((offset % 16) == 0)
                        printk("\n");
-                printk("%x:%d ", offset,e1000_rr32(offset));
+               printk("%x:%d ", offset,e1000_rr32(offset));
 
                offset = offset + 4;
        }
@@ -118,6 +118,7 @@ void e1000_init() {
 
        // "Register" our send_frame with the global system
        send_frame = &e1000_send_frame;
+       send_pbuf = &e1000_send_pbuf;
 
        // sudo /sbin/ifconfig eth0 up
        eth_up = 1;
@@ -848,6 +849,52 @@ void e1000_handle_rx_packet() {
        return;
 }
 
+int e1000_send_pbuf(struct pbuf *p) {
+       int len = p->tot_len;
+       // print_pbuf(p);
+       if (p == NULL) 
+               return -1;
+       if (len == 0)
+               return 0;
+       
+       // Find where we want to write
+       uint32_t head = e1000_rr32(E1000_TDH);
+
+       
+       // Fail if we are out of space
+       if (((e1000_tx_index + 1) % NUM_TX_DESCRIPTORS) == head) {
+               e1000_frame_debug("-->TX Ring Buffer Full!\n");
+               return -1;
+       }
+       
+       // Fail if we are too large
+       if (len > MAX_FRAME_SIZE) {
+               e1000_frame_debug("-->Frame Too Large!\n");
+               return -1;
+       }
+       
+       // Move the data
+       int cplen = pbuf_copy_out(p, KADDR(tx_des_kva[e1000_tx_index].buffer_addr), len, 0);
+
+       for(int i = 0; i< cplen; i++){
+               printd("%x", ((uint8_t*)KADDR(tx_des_kva[e1000_tx_index].buffer_addr))[i]);
+       }
+       // Set the length
+       tx_des_kva[e1000_tx_index].lower.flags.length = len;
+       
+       // Magic that means send 1 fragment and report.
+       tx_des_kva[e1000_tx_index].lower.flags.cmd = 0x0B;
+
+       // Track our location
+       e1000_tx_index = (e1000_tx_index + 1) % NUM_TX_DESCRIPTORS;
+       
+       // Bump the tail.
+       e1000_wr32(E1000_TDT, e1000_tx_index);
+
+       e1000_frame_debug("-->Sent packet.\n");
+       
+       return len;
+}
 // Main routine to send a frame. Just sends it and goes.
 // Card supports sending across multiple fragments, we don't.
 // Would we want to write a function that takes a larger packet and generates fragments?
index d3cb25d..084aeb9 100644 (file)
@@ -5,6 +5,7 @@
 #include <trap.h>
 #include <pmap.h>
 #include <arch/nic_common.h>
+#include <net/pbuf.h>
 
 #define e1000_debug(...)               //printk(__VA_ARGS__)  
 #define e1000_interrupt_debug(...)     //printk(__VA_ARGS__)  
@@ -47,5 +48,6 @@ void e1000_handle_rx_packet(void);
 void e1000_set_rx_descriptor(uint32_t des_num, uint8_t reset_buffer);
 void e1000_set_tx_descriptor(uint32_t des_num);
 int  e1000_send_frame(const char* data, size_t len);
+int e1000_send_pbuf(struct pbuf *p);
 
 #endif /* !ROS_INC_E1000_H */
index 7156e62..e02763d 100644 (file)
@@ -18,6 +18,9 @@
 // Global send_frame function pointer
 // Means we can only have one network card per system right now...
 int (*send_frame)(const char *data, size_t len);
+int (*send_pbuf)(struct pbuf *p);
+
+
 
 // Global variables for managing ethernet packets over a nic
 // Again, since these are global for all network cards we are 
index 9fcc17f..61e92c9 100644 (file)
@@ -5,6 +5,7 @@
 #include <trap.h>
 #include <net.h>
 #include <pmap.h>
+#include <net/pbuf.h>
 
 // Packet sizes
 #define MTU              1500
@@ -19,6 +20,7 @@
 // Global send_frame function pointer
 // Means we can only have one network card per system right now...
 extern int (*send_frame)(const char *data, size_t len);
+extern int (*send_pbuf)(struct pbuf *p);
 
 // Global variables for managing ethernet packets over a nic
 // Again, since these are global for all network cards we are 
index 9b5450c..2bdfe9d 100644 (file)
@@ -31,6 +31,8 @@
 #include <pmap.h>
 
 #include <eth_audio.h>
+#include <net/pbuf.h>
+
 
 /** @file
  * @brief Realtek RL8168 Driver
@@ -108,6 +110,7 @@ void rl8168_init() {
        rl8168_configure();
        rl8168_setup_interrupts();
        send_frame = &rl8168_send_frame;
+       send_pbuf = &rl8168_send_pbuf;
 
        eth_up = 1;
        
@@ -179,9 +182,8 @@ void rl8168_setup_descriptors() {
        // Allocate room for the buffers. 
        // Buffers need to be on 256 byte boundries.
        // Note: We use get_cont_pages to force page alignment, and thus 256 byte aligned
-
-        uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
-        uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
+       uint32_t num_rx_pages = ROUNDUP(NUM_RX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
+       uint32_t num_tx_pages = ROUNDUP(NUM_TX_DESCRIPTORS * sizeof(struct Descriptor), PGSIZE) / PGSIZE;
        
        rx_des_kva = get_cont_pages(LOG2_UP(num_rx_pages), 0);
        tx_des_kva = get_cont_pages(LOG2_UP(num_tx_pages), 0);
@@ -600,6 +602,41 @@ void rl8168_process_frame(char *frame_buffer, uint32_t frame_size, uint32_t comm
        return;
 }
 
+/* Look into how bsd does send mbuf ? */
+int rl8168_send_pbuf(struct pbuf *p) {
+       int len = p->tot_len;
+       if (p == NULL)
+               return -1;
+       if (len == 0)
+               return 0;
+
+       if (tx_des_kva[tx_des_cur].command & DES_OWN_MASK) {
+               rl8168_frame_debug("-->TX Ring Buffer Full!\n");
+               return -1;
+       }
+       if (len > MAX_FRAME_SIZE){
+               return -1;
+       }
+       /* Copy everything out of pbuf to network buffer to be sent */
+       /* One copy! */
+       pbuf_copy_out(p, KADDR(tx_des_kva[tx_des_cur].low_buf), len, 0);
+       tx_des_kva[tx_des_cur].command = tx_des_kva[tx_des_cur].command | len | DES_OWN_MASK | DES_FS_MASK | DES_LS_MASK;
+       
+       tx_des_kva[tx_des_cur].vlan = 0;
+
+
+       tx_des_cur = (tx_des_cur + 1) % NUM_TX_DESCRIPTORS;
+       
+       rl8168_frame_debug("--> Sending Packet\n");
+       for(int i=0; i<len; i++)
+               rl8168_frame_debug("%x ", (unsigned int)(unsigned char)(data[i]));
+       rl8168_frame_debug("\n");
+       rl8168_frame_debug("--> Sent packet.\n");
+       
+       outb(rl8168_io_base_addr + RL_TX_CTRL_REG, RL_TX_SEND_MASK);
+       
+       return len;
+}
 // Main routine to send a frame. Just sends it and goes.
 // Card supports sending across multiple fragments.
 // Would we want to write a function that takes a larger packet and generates fragments?
index f5222a6..4faaf49 100644 (file)
 
 // v----- Evil line ------v
 
-char *CT(PACKET_HEADER_SIZE + len)
-rl8168_packet_wrap(const char *CT(len) data, size_t len);
+//char *CT(PACKET_HEADER_SIZE + len)
+//rl8168_packet_wrap(const char *CT(len) data, size_t len);
 
 // ^----- Evil line ------^
 
@@ -128,5 +128,6 @@ void rl8168_set_tx_descriptor(uint32_t des_num);
 void rl8168_process_frame(char *CT(frame_size) frame_buffer,
                           uint32_t frame_size, uint32_t command);
 int rl8168_send_frame(const char *CT(len) data, size_t len);
+int rl8168_send_pbuf(struct pbuf *p);
 
 #endif /* !ROS_INC_REALTEK_H */
index 67bc626..4b49e0c 100644 (file)
@@ -2,8 +2,9 @@
 #define ROS_INC_TYPES_H
 
 #include <stddef.h>
-
+#ifndef LITTLE_ENDIAN
 #define LITTLE_ENDIAN
+#endif /* !LITTLE_ENDIAN */
 
 // Represents true-or-false values
 
index b6e669e..be652ea 100644 (file)
 #define        IPPROTO_TCP                     6               /* tcp */
 #define        IPPROTO_UDP                     17              /* user datagram protocol */
 
-#define        INADDR_ANY                      (uint32_t)0x00000000
-#define        INADDR_BROADCAST        (uint32_t)0xffffffff    /* must be masked */
+#define IP_HDR_SZ 20
+
+
+/* modified to be consistent with linux and our user space tool chain 
+ * we need to make our mind up about bsd/linux structs
+ */
+#define        INADDR_ANY                      (struct in_addr) {0x00000000}
+#define        INADDR_BROADCAST        (struct in_addr) {0xffffffff}   /* must be masked */
 
 #define        htonl(x) cpu_to_be32(x)
 #define        htons(x) cpu_to_be16(x)
index 605af84..60bed3c 100644 (file)
@@ -1,5 +1,6 @@
 /* Copyright (c) 2010 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
+ * David Zhu <yuzhu@cs.berkeley.edu>
  * See LICENSE for details.
  *
  * Arch independent networking infrastructure */
 #include <bits/netinet.h>
 #include <stdio.h>
 
+/* network internal error code */
+#define ERR_BUF 
+
 /* A few other useful standard defines.  Note the IP header can change size. */
-#define ETH_HDR_SZ 14
+#define ETH_HDR_SZ 14 // without padding, 16 with padding
 #define UDP_HDR_SZ 8
 #define IP_ETH_TYPE 0x0800
-
-/* ROS defaults: */
+#define IP_HDR_SZ 20
+/* ROS defaults: They really should be netif specific*/
 #define DEFAULT_TTL 64
+#define DEFAULT_MTU 1500
+// is this network order already?
+#define IP_ADDR 0x0A000002  //lookout for address order
+
 
 /* Don't forget the bytes are in network order */
 struct ethernet_hdr {
+       /* might want to pad to increase access speed? */
+       uint8_t           padding[2];
        uint8_t                                         dst_mac[6];
        uint8_t                                         src_mac[6];
        uint16_t                                        eth_type;
        /* might be an optional 802.1q tag here */
-};
+} __attribute__((packed));
+
 
 /* For the bit-enumerated fields, note that you need to read "backwards" through
  * the byte (first bits in memory are the "LSB" of the byte).  Can't seem to be
  * able to do it with flags/fragments (3/13 bits each...). */
 struct ip_hdr {
+       /* TODO: Are these accesses slower? */
        unsigned                                        hdr_len : 4;
        unsigned                                        version : 4;
        uint8_t                                         tos;
+
        uint16_t                                        packet_len;
-       uint16_t                                        id;
+       /* ip header id is used for fragmentation reassembly */
+       uint16_t                                        id;  // 1 index this?
+       /* flags controlling fragmentation(do not fragment etc) */
        uint16_t                                        flags_frags;
+       /* statically set to a constatnt right now */
        uint8_t                                         ttl;
        uint8_t                                         protocol;
        uint16_t                                        checksum;
diff --git a/kern/include/net/dev.h b/kern/include/net/dev.h
new file mode 100644 (file)
index 0000000..0652e2b
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (c) 2010 The Regents of the University of California
+ * David Zhu <yuzhu@cs.berkeley.edu> 
+ * See LICENSE for details.
+ *
+ * Simplified network device interface */
+#ifndef ROS_KERN_NET_DEV_H
+#define ROS_KERN_NET_DEV_H
+
+struct netif {
+       /* TODO: next netif so we can build a list of them*/
+       struct ip_addr ip_addr;
+       void* init;
+       netif_init_fn input;
+       netif_output_fn send_frame;
+       netif_output_fn send_pbuf;
+       netif_output_raw output_raw;
+}; 
+
+
+
+
+
+#endif //ROS_KERN_NET_DEV_H
diff --git a/kern/include/net/ip.h b/kern/include/net/ip.h
new file mode 100644 (file)
index 0000000..408a90b
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef ROS_KERN_IP_H
+#define ROS_KERN_IP_H
+#include <net/pbuf.h>
+#include <net.h>
+#include <bits/netinet.h>
+extern struct in_addr global_ip;
+int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t proto);
+
+#endif // ROS_KERN_IP_H
diff --git a/kern/include/net/pbuf.h b/kern/include/net/pbuf.h
new file mode 100644 (file)
index 0000000..a4528d0
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef _ROS_PBUF_H_
+#define _ROS_PBUF_H_
+#include <kmalloc.h>
+#include <slab.h>
+#include <kref.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Currently, the pbuf_custom code is only needed for one specific configuration
+ * of IP_FRAG */
+#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF)
+#define ETH_PAD_SIZE 2      // padding to ensure ip packet is longword aligned.
+#define PBUF_TRANSPORT_HLEN 20
+#define PBUF_IP_HLEN        20
+#define PBUF_LINK_HLEN      14 + ETH_PAD_SIZE // Padding
+
+typedef enum {
+  PBUF_TRANSPORT,
+  PBUF_IP,
+  PBUF_LINK,
+  PBUF_RAW
+} pbuf_layer;
+
+typedef enum {
+  PBUF_RAM, /* pbuf data is stored in RAM */
+  PBUF_ROM, /* pbuf data is stored in ROM */
+  PBUF_REF, /* pbuf comes from the pbuf pool */
+  PBUF_POOL /* pbuf payload refers to RAM */
+} pbuf_type;
+
+
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH      0x01U
+/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a
+    a pbuf differently */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+/** indicates this pbuf is UDP multicast to be looped back */
+#define PBUF_FLAG_MCASTLOOP 0x04U
+
+struct pbuf {
+  /** next pbuf in singly linked pbuf chain */
+  struct pbuf *next;
+
+  /** pointer to the actual data in the buffer */
+  void *payload;
+
+  uint16_t tot_len;
+
+  /** length of this buffer */
+  uint16_t len;
+
+  /** pbuf_type as u8_t instead of enum to save space */
+  uint8_t /*pbuf_type*/ type;
+
+  /** misc flags */
+  uint8_t flags;
+
+  struct kref bufref;
+};
+
+extern struct kmem_cache *pbuf_kcache;
+/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
+void pbuf_init(void);
+void pbuf_cat(struct pbuf *head, struct pbuf *tail);
+void pbuf_chain(struct pbuf *head, struct pbuf *tail);
+void pbuf_ref(struct pbuf *p);
+int pbuf_header(struct pbuf *p, int header_size);
+struct pbuf *pbuf_alloc(pbuf_layer layer, uint16_t length, pbuf_type type);
+int pbuf_copy_out(struct pbuf *buf, void *dataptr, size_t len, uint16_t offset);
+void print_pbuf(struct pbuf *p);
+// end
+#if 0
+void pbuf_realloc(struct pbuf *p, u16_t size); 
+u8_t pbuf_free(struct pbuf *p);
+u8_t pbuf_clen(struct pbuf *p);  
+struct pbuf *pbuf_dechain(struct pbuf *p);
+err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from);
+err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
+struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
+#if LWIP_CHECKSUM_ON_COPY
+err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+                       u16_t len, u16_t *chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+u8_t pbuf_get_at(struct pbuf* p, u16_t offset);
+u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n);
+u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
+u16_t pbuf_strstr(struct pbuf* p, const char* substr);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_PBUF_H__ */
diff --git a/kern/include/net/udp.h b/kern/include/net/udp.h
new file mode 100644 (file)
index 0000000..6184e8c
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef ROS_KERN_UDP_H
+#define ROS_KERN_UDP_H
+#include <net/pbuf.h>
+#include <net.h>
+#include <bits/netinet.h>
+#include <socket.h>
+
+#define UDP_HLEN 8
+#define UDP_TTL 255
+
+struct udp_pcb {
+    struct in_addr local_ip;
+    struct in_addr remote_ip;
+    /** ports are in host byte order */
+    uint16_t local_port, remote_port;
+    uint8_t ttl;
+    uint8_t flags;
+    /* Protocol specific PCB members */
+    struct udp_pcb *next;
+#if 0
+  /** receive callback function */
+  udp_recv_fn recv;
+  /** user-supplied argument for the recv callback */
+  void *recv_arg;  
+#endif
+};
+
+extern struct udp_pcb *udp_pcbs;
+#define GLOBAL_IP 0x0A000001 // 10.0.0.1
+struct udp_pcb * udp_new(void);
+int udp_send(struct udp_pcb *pcb, struct pbuf *p);
+int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+                    struct in_addr *dst_ip, uint16_t dst_port);
+int udp_bind(struct udp_pcb *pcb, struct in_addr *ip, uint16_t port);
+#if 0
+#define UDP_FLAGS_NOCHKSUM       0x01U
+#define UDP_FLAGS_UDPLITE        0x02U
+#define UDP_FLAGS_CONNECTED      0x04U
+#define UDP_FLAGS_MULTICAST_LOOP 0x08U
+
+
+/** Function prototype for udp pcb receive callback functions
+ * addr and port are in same byte order as in the pcb
+ * The callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ *
+ * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf
+ *            makes 'addr' invalid, too.
+ *
+ * @param arg user supplied argument (udp_pcb.recv_arg)
+ * @param pcb the udp_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @param port the remote port from which the packet was received
+ */
+typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+    ip_addr_t *addr, u16_t port);
+
+
+/* udp_pcbs export for exernal reference (e.g. SNMP agent) */
+extern struct udp_pcb *udp_pcbs;
+
+/* The following functions is the application layer interface to the
+   UDP code. */
+void             udp_remove     (struct udp_pcb *pcb);
+err_t            udp_bind       (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+                                 u16_t port);
+err_t            udp_connect    (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+                                 u16_t port);
+void             udp_disconnect (struct udp_pcb *pcb);
+void             udp_recv       (struct udp_pcb *pcb, udp_recv_fn recv,
+                                 void *recv_arg);
+err_t            udp_sendto_if  (struct udp_pcb *pcb, struct pbuf *p,
+                                 ip_addr_t *dst_ip, u16_t dst_port,
+                                 struct netif *netif);
+err_t            udp_sendto     (struct udp_pcb *pcb, struct pbuf *p,
+                                 ip_addr_t *dst_ip, u16_t dst_port);
+err_t            udp_send       (struct udp_pcb *pcb, struct pbuf *p);
+
+#if LWIP_CHECKSUM_ON_COPY
+err_t            udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 ip_addr_t *dst_ip, u16_t dst_port,
+                                 struct netif *netif, u8_t have_chksum,
+                                 u16_t chksum);
+err_t            udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 ip_addr_t *dst_ip, u16_t dst_port,
+                                 u8_t have_chksum, u16_t chksum);
+err_t            udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 u8_t have_chksum, u16_t chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#define          udp_flags(pcb) ((pcb)->flags)
+#define          udp_setflags(pcb, f)  ((pcb)->flags = (f))
+
+/* The following functions are the lower layer interface to UDP. */
+void             udp_input      (struct pbuf *p, struct netif *inp);
+
+#define udp_init() /* Compatibility define, not init needed. */
+
+#if UDP_DEBUG
+void udp_debug_print(struct udp_hdr *udphdr);
+#else
+#define udp_debug_print(udphdr)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_UDP */
+
+//#endif /* if 0 , comment out */
+#endif /* __LWIP_UDP_H__ */
index 6137c0f..2634723 100644 (file)
@@ -43,6 +43,8 @@
 
 /* Socket Syscalls */
 #define SYS_socket                                     40
+#define SYS_sendto                                     41
+#define SYS_recvfrom                           42
 
 
 /* Platform specific syscalls */
index b286202..b35b202 100644 (file)
@@ -47,6 +47,7 @@ typedef size_t uintreg_t;
 })
 #endif
 
+#define ROS_MEM_ALIGN 4
 // Rounding operations (efficient when n is a power of 2)
 // Round down to the nearest multiple of n
 #define ROUNDDOWN(a, n)                                                \
@@ -61,6 +62,8 @@ typedef size_t uintreg_t;
        (typeof(a)) (ROUNDDOWN((uintptr_t) (a) + __n - 1, __n));        \
 })
 
+#define MEM_ALIGN_SIZE(size) ROUNDUP(size, ROS_MEM_ALIGN)
+
 // Round down to the nearest multiple of n
 #define PTRROUNDDOWN(a, n)                                             \
 ({                                                             \
diff --git a/kern/include/socket.h b/kern/include/socket.h
new file mode 100644 (file)
index 0000000..0f50a74
--- /dev/null
@@ -0,0 +1,111 @@
+#ifndef ROS_SOCKET_H
+#define ROS_SOCKET_H
+
+#include <ros/common.h>
+// Just a couple of AF types that we might support
+#define AF_UNSPEC      0
+#define AF_UNIX                1       /* Unix domain sockets          */
+#define AF_LOCAL       1       /* POSIX name for AF_UNIX       */
+#define AF_INET                2       /* Internet IP Protocol         */
+
+#define PF_UNSPEC      AF_UNSPEC
+#define PF_UNIX                AF_UNIX
+#define PF_LOCAL       AF_LOCAL
+#define PF_INET                AF_INET
+
+#define        SS_NOFDREF              0x0001  /* no file table ref any more */
+#define        SS_ISCONNECTED          0x0002  /* socket connected to a peer */
+#define        SS_ISCONNECTING         0x0004  /* in process of connecting to peer */
+#define        SS_ISDISCONNECTING      0x0008  /* in process of disconnecting */
+#define        SS_NBIO                 0x0100  /* non-blocking ops */
+#define        SS_ASYNC                0x0200  /* async i/o notify */
+#define        SS_ISCONFIRMING         0x0400  /* deciding to accept connection req */
+#define        SS_ISDISCONNECTED       0x2000  /* socket disconnected from peer */
+
+/* Define an range for automatic port assignment */
+#define SOCKET_PORT_START 4096
+#define SOCKET_PORT_END  0x7fff
+
+struct socket;
+struct proc;
+// These are probably defined elsewhere too..
+#ifndef socklen_t
+typedef int socklen_t;
+typedef int sa_family_t;
+#endif
+#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr)   ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr))
+enum sock_type {
+    SOCK_STREAM = 1,
+    SOCK_DGRAM  = 2,
+    SOCK_RAW    = 3,
+    SOCK_RDM    = 4,
+    SOCK_SEQPACKET  = 5,
+    SOCK_DCCP   = 6,
+    SOCK_PACKET = 10,
+};
+
+struct socket{
+  //int so_count;       /* (b) reference count */
+  short   so_type;        /* (a) generic type, see socket.h */
+       short   so_family;
+       int     so_protocol;
+  short   so_options;     /* from socket call, see socket.h */
+  //short   so_linger;      /* time to linger while closing */
+  short   so_state;       /* (b) internal state flags SS_* */
+       //int so_qstate;      /* (e) internal state flags SQ_* */
+       void    *so_pcb;        /* protocol control block */
+       //struct  vnet *so_vnet;      /* network stack instance */
+       //struct  protosw *so_proto;  /* (a) protocol handle */
+};
+
+struct in_addr {
+    uint32_t s_addr;
+};
+
+/* members are in network byte order */
+struct sockaddr_in {
+    // uint8_t sin_len; -- bsd only field
+    uint8_t sin_family;
+    uint16_t sin_port;
+    struct in_addr sin_addr;
+    char sin_zero[8];
+};
+
+
+struct sockaddr {
+       unsigned char   sa_len;         /* bsd only total length */
+       sa_family_t     sa_family;      /* address family */
+       char            sa_data[14];    /* actually longer; address value */
+};
+
+/*
+ * Message header for recvmsg and sendmsg calls.
+ * Used value-result for recvmsg, value only for sendmsg.
+ */ 
+struct msghdr {
+    void        *msg_name;      /* optional address */
+    socklen_t    msg_namelen;       /* size of address */
+    struct iovec    *msg_iov;       /* scatter/gather array */
+    int      msg_iovlen;        /* # elements in msg_iov */
+    void        *msg_control;       /* ancillary data, see below */
+    socklen_t    msg_controllen;    /* ancillary data buffer len */
+    int      msg_flags;     /* flags on received message */
+};
+
+
+
+extern struct kmem_cache *sock_kcache;
+extern struct kmem_cache *mbuf_kcache;
+extern struct kmem_cache *udp_pcb_kcache;
+
+void socket_init();
+intreg_t send_iov(struct socket* sock, struct iovec* iov, int flags);
+int send_datagram(struct socket* sock, struct iovec* iov, int flags);
+
+intreg_t sys_socket(struct proc *p, int socket_family, int socket_type, int protocol);
+intreg_t sys_sendto(struct proc *p, int socket, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
+intreg_t sys_recvfrom(struct proc *p, int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
+
+
+#endif /* ROS_SOCKET_H */
+
diff --git a/kern/include/sys/uio.h b/kern/include/sys/uio.h
new file mode 100644 (file)
index 0000000..0981f89
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _ROS_SYS_UIO_H
+#define _ROS_SYS_UIO_H
+
+#include <sys/types.h>
+
+typedef long off_t;
+
+enum uio_rw {
+       UIO_READ,
+       UIO_WRITE
+};
+
+/* Segment flag values. */
+enum uio_seg {
+       UIO_USERSPACE,          /* from user data space */
+       UIO_SYSSPACE,           /* from system space */
+       UIO_NOCOPY              /* don't copy, already in object */
+};
+
+// Straight out of bsd definition
+struct iovec {
+    void    *iov_base;  /* Base address. */
+    size_t   iov_len;   /* Length. */
+};
+
+struct uio {
+       struct  iovec *uio_iov;         /* scatter/gather list */
+       int     uio_iovcnt;             /* length of scatter/gather list */
+       off_t   uio_offset;             /* offset in target object */
+       ssize_t uio_resid;              /* remaining bytes to process */
+       enum    uio_seg uio_segflg;     /* address space */
+       enum    uio_rw uio_rw;          /* operation */
+       struct  proc *uio_proc;         /* owner */
+};
+
+
+
+#endif
index a53ebd3..6ff1af0 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <ros/common.h>
 #include <sys/queue.h>
+#include <sys/uio.h>
 #include <bitmask.h>
 #include <kref.h>
 #include <time.h>
@@ -31,7 +32,6 @@ struct vm_region;
 typedef int dev_t;
 typedef int kdev_t;
 typedef int ino_t;
-typedef long off_t; // out there in other .h's, but not in the kernel yet
 struct io_writeback    {int x;};
 struct event_poll {int x;};
 struct poll_table_struct {int x;};
@@ -50,11 +50,6 @@ struct file_operations;
 struct fs_type;
 struct vfsmount;
 
-struct iovec {
-    void *iov_base;
-    size_t iov_len;
-};
-
 /* List def's we need */
 TAILQ_HEAD(sb_tailq, super_block);
 TAILQ_HEAD(dentry_tailq, dentry);
index d86dc86..12829b3 100644 (file)
@@ -60,7 +60,11 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/ucq.c \
                  $(KERN_SRC_DIR)/console.c \
                  $(KERN_SRC_DIR)/socket.c \
-                 $(KERN_SRC_DIR)/arsc.c
+                 $(KERN_SRC_DIR)/arsc.c \
+                 $(KERN_SRC_DIR)/net/udp.c \
+                 $(KERN_SRC_DIR)/net/ip.c \
+                 $(KERN_SRC_DIR)/net/pbuf.c
+
 
 # Only build files if they exist.
 KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
index 3599045..0dcac7b 100644 (file)
@@ -43,6 +43,7 @@
 #include <ext2fs.h>
 #include <kthread.h>
 #include <net.h>
+#include <socket.h>
 #include <eth_audio.h>
 #include <console.h>
 
diff --git a/kern/src/net/ip.c b/kern/src/net/ip.c
new file mode 100644 (file)
index 0000000..5f5cda9
--- /dev/null
@@ -0,0 +1,101 @@
+#include <ros/common.h>
+#include <assert.h>
+#include <socket.h>
+#include <bits/netinet.h>
+#include <net.h>
+#include <net/ip.h>
+#include <net/udp.h>
+#include <ros/errno.h>
+#include <arch/nic_common.h>
+
+/* statically configured next gateway */
+const uint8_t GTWAY[6] = {0xda, 0x76, 0xe7, 0x4c, 0xca, 0x7e};
+
+/* TODO: ip id unique for all ip packets? or is it unique for a flow? */
+// can do atomic increment at a minimum
+static uint16_t ip_id = 0;
+struct in_addr global_ip = {IP_ADDR};
+
+/* TODO: build arp table, and look up */
+int eth_send(struct pbuf *p, struct in_addr *dest) {
+       uint32_t bytes_sent; 
+       if (pbuf_header(p, sizeof(struct ethernet_hdr)) != 0){
+               warn("eth_send buffer ran out");
+               /* unsuccessful, needs to allocate */   
+               return -ENOBUFS;
+       }
+
+       struct ethernet_hdr *ethhdr = (struct ethernet_hdr *)p->payload; 
+       // TODO: for now just forward to gateway
+       memcpy(ethhdr->dst_mac, GTWAY, 6);
+       memcpy(ethhdr->src_mac, device_mac, 6);
+       ethhdr->eth_type = htons(IP_ETH_TYPE);
+       /* The reason for not sending to send_nic for each pbuf in the chain
+        * is so that we can send from multi-buffer later.
+        */
+       if (send_pbuf){
+       #if ETH_PAD_SIZE
+               pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+       #endif
+               bytes_sent = send_pbuf(p);
+               pbuf_header(p, ETH_PAD_SIZE); /* when do we deallocate p? */
+               return bytes_sent;
+       }
+       else {
+               warn("no pbuf send function \n");
+               return -1;
+       }
+       /* is the address local , if no, search for MAC of the gateway and dest to gateway */
+       /* if address is local, use arp etc */
+
+}
+
+/* while it would be nice to write a generic send_pbuf it is impossible to do so in
+ * efficiently.
+ */
+/* Assume no ip options */
+int ip_output(struct pbuf *p, struct in_addr *src, struct in_addr *dest, uint8_t proto) {
+       struct pbuf *q;
+       struct ip_hdr *iphdr;   
+       /* TODO: Check for IP_HDRINCL */
+       if (dest->s_addr == IP_HDRINCL) {
+               /*send right away since */
+               warn("header included in the ip packets");
+               return -1;
+       }
+       if (pbuf_header(p, IP_HDR_SZ)) {
+               warn("buffer ran out");
+               /* unsuccessful, needs to allocate */   
+               return -ENOBUFS;
+       }
+       iphdr = (struct ip_hdr *) p->payload;
+
+       /* successful */
+       iphdr->version = IPPROTO_IPV4;
+       /* assume no IP options */
+       iphdr->hdr_len = IP_HDR_SZ >> 2;
+       iphdr->tos = 0;
+       iphdr->packet_len = htons(p->tot_len);
+       // TODO: NET_LOCK
+       iphdr->id = htons (ip_id); // 1
+       ip_id++;
+       iphdr->flags_frags = htons(0); // 4000  may fragment
+       iphdr->protocol = proto;
+       iphdr->ttl = htons(DEFAULT_TTL);
+       /* Eventually if we support more than one device this may change */
+       printk("src ip %x, dest ip %x \n", src->s_addr, dest->s_addr);
+       iphdr->src_addr = htonl(src->s_addr);
+       iphdr->dst_addr = (dest->s_addr);
+       /* force hardware checksum
+        * TODO: provide option to do both hardware/software checksum
+        */
+       /* Since the IP header is set already, we can compute the checksum. */
+       /* TODO: Use the card to calculate the checksum */
+       iphdr->checksum = htons(ip_checksum(iphdr)); //7ab6
+       if (p->tot_len > DEFAULT_MTU) /*MAX MTU? header included */
+               return -1;//ip_frag(p, dest);
+       else
+               return eth_send(p, dest);
+}
+
+
diff --git a/kern/src/net/pbuf.c b/kern/src/net/pbuf.c
new file mode 100644 (file)
index 0000000..2997da2
--- /dev/null
@@ -0,0 +1,987 @@
+#include <ros/common.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <kmalloc.h>
+#include <slab.h>
+#include <assert.h>
+#include <net/pbuf.h>
+
+
+#define SIZEOF_STRUCT_PBUF (ROUNDUP(sizeof(struct pbuf), ROS_MEM_ALIGN))
+
+// may need checksum later
+struct kmem_cache *pbuf_kcache;
+
+void pbuf_init(void){
+       pbuf_kcache = kmem_cache_create("pbuf", sizeof(struct pbuf),
+                                                                       __alignof__(struct pbuf), 0, 0, 0);
+}
+
+// not sure about this structure..
+static void pbuf_free_auto(struct kref *kref){
+    struct pbuf *p = container_of(kref, struct pbuf, bufref);
+    switch (p->type){
+        case PBUF_REF:
+            kmem_cache_free(pbuf_kcache, p);
+            break;
+        case PBUF_RAM:
+            kfree(p);
+            break;
+        default:
+            panic("Invalid pbuf type");
+    }
+}
+/**
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ *             chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. Additional headers must be prepended
+ *             by allocating another pbuf and chain in to the front of
+ *             the ROM pbuf. It is assumed that the memory used is really
+ *             similar to ROM in that it is immutable and will not be
+ *             changed. Memory which is dynamic should generally not
+ *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. It is assumed that the pbuf is only
+ *             being used in a single thread. If the pbuf gets queued,
+ *             then pbuf_take should be called to copy the buffer.
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *pbuf_alloc(pbuf_layer layer, uint16_t length, pbuf_type type)
+{
+  struct pbuf *p, *q, *r;
+  uint16_t offset;
+       uint16_t buf_size; 
+  int rem_len; /* remaining length */
+
+  /* determine header offset */
+  offset = 0;
+  switch (layer) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset += PBUF_TRANSPORT_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset += PBUF_IP_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset += PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW:
+    break;
+  default:
+    warn("pbuf_alloc: bad pbuf layer", 0);
+    return NULL;
+  }
+       
+  switch (type) {
+  case PBUF_RAM:
+    /* If pbuf is to be allocated in RAM, allocate memory for it. */
+
+    // why does it have to be aligned? oh so payload is aligned
+               buf_size =  (SIZEOF_STRUCT_PBUF + offset) + MEM_ALIGN_SIZE(length);
+    p = (struct pbuf*)kmalloc(buf_size, 0);
+     // p = (struct pbuf*)kmalloc(
+
+    if (p == NULL) {
+      return NULL;
+    }
+    /* Set up internal structure of the pbuf. */
+    p->payload = PTRROUNDUP(((void *)((uint8_t *)p + SIZEOF_STRUCT_PBUF + offset)), ROS_MEM_ALIGN);
+    p->len = p->tot_len = length;
+    p->next = NULL;
+    p->type = type;
+    break;
+  case PBUF_REF:
+    /* only allocate memory for the pbuf structure */
+    p = (struct pbuf *)kmem_cache_alloc(pbuf_kcache, 0);
+    if (p == NULL) {
+      return NULL;
+    }
+    p->payload = NULL;
+    p->len = p->tot_len = length;
+    p->next = NULL;
+    p->type = type;
+    break;
+  default:
+    warn("pbuf_alloc: wrong type", 0);
+    return NULL;
+  }
+  kref_init(&p->bufref, pbuf_free_auto, 1); // TODO: pbuf_free
+  /* set flags */
+  p->flags = 0;
+  return p;
+}
+
+
+void pbuf_ref(struct pbuf *p){
+    kref_get(&p->bufref, 1);
+}
+
+
+/**
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more 
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+int pbuf_copy_out(struct pbuf *buf, void *dataptr, size_t len, uint16_t offset)
+{
+  struct pbuf *p;
+  uint16_t left;
+  uint16_t buf_copy_len;
+  uint16_t copied_total = 0;
+       
+       if (dataptr == NULL || buf == NULL){
+               warn("Copying a pbuf_copy to null pointer");
+               return 0;
+       }
+
+  left = 0;
+  for(p = buf; len != 0 && p != NULL; p = p->next) {
+    if ((offset != 0) && (offset >= p->len)) {
+      /* don't copy from this buffer -> on to the next */
+      offset -= p->len;
+    } else {
+                       /* offset is 0 now, start copying */
+      buf_copy_len = p->len - offset;
+      if (buf_copy_len > len)
+          buf_copy_len = len;
+      /* copy the necessary parts of the buffer */
+      memcpy(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+      copied_total += buf_copy_len;
+      left += buf_copy_len;
+      len -= buf_copy_len;
+      offset = 0;
+    }
+  }
+  return copied_total;
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ * 
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ * 
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+  pbuf_cat(h, t);
+  /* t is now referenced by h */
+  pbuf_ref(t);
+}
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+  /* proceed to last pbuf of chain */
+  for (p = h; p->next != NULL; p = p->next) {
+    /* add total length of second chain to all totals of first chain */
+    p->tot_len += t->tot_len;
+  }
+  /* add total length of second chain to last pbuf total of first chain */
+  p->tot_len += t->tot_len;
+  /* chain last pbuf of head (p) with first of tail (t) */
+  p->next = t;
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+
+/* TODO: when do we need to lock a pbuf?? */
+int pbuf_header(struct pbuf *p, int delta){ // increase header size
+       uint8_t type = p->type;
+       void *payload = p->payload;
+       if (p == NULL || delta == 0)
+               return 0;
+       if (delta < 0) 
+               assert(-delta < p->len);
+       type = p->type;
+  /* remember current payload pointer */
+  payload = p->payload;
+
+  /* pbuf types containing payloads? */
+  if (type == PBUF_RAM || type == PBUF_POOL) {
+    /* set new payload pointer */
+    p->payload = (uint8_t *)p->payload - delta;
+    /* boundary check fails? */
+    if ((uint8_t *)p->payload < (uint8_t *)p + SIZEOF_STRUCT_PBUF) {
+      /* restore old payload pointer */
+      p->payload = payload;
+      /* bail out unsuccesfully */
+      return 1;
+    }
+  /* pbuf types refering to external payloads? */
+  } else if (type == PBUF_REF || type == PBUF_ROM) {
+               /* header was embedded in the payload, we are extracting it */
+    if ((delta < 0) && ((-delta) <= p->len)) {
+      /* increase payload pointer */
+      p->payload = (uint8_t *)p->payload - delta;
+    } else {
+      /* cannot expand payload to front (yet!)
+       * bail out unsuccesfully */
+      return 1;
+    }
+  } else {
+    /* Unknown type */
+    assert("bad pbuf type");
+    return 1;
+  }
+  /* modify pbuf length fields */
+  p->len += delta;
+  p->tot_len += delta;
+
+  return 0;
+}
+
+void print_pbuf(struct pbuf *p) {
+       struct pbuf *next = p;
+       while ( next != NULL) {
+               for(int i = 0; i< next->len; i++){
+                       printk("%x", ((uint8_t*)next->payload)[i]);
+               }
+               printk("\n");
+               next = next->next;
+       }
+}
+#if 0
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Initialize a custom pbuf (already allocated).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ *        this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ *        must be at least big enough to hold 'length' plus the header size,
+ *        may be NULL if set later
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ *        big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+                    void *payload_mem, u16_t payload_mem_len)
+{
+  u16_t offset;
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+  /* determine header offset */
+  offset = 0;
+  switch (l) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset += PBUF_TRANSPORT_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset += PBUF_IP_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset += PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW:
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+    return NULL;
+  }
+
+  if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) {
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+    return NULL;
+  }
+
+  p->pbuf.next = NULL;
+  if (payload_mem != NULL) {
+    p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset));
+  } else {
+    p->pbuf.payload = NULL;
+  }
+  p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+  p->pbuf.len = p->pbuf.tot_len = length;
+  p->pbuf.type = type;
+  p->pbuf.ref = 1;
+  return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+  struct pbuf *q;
+  u16_t rem_len; /* remaining length */
+  s32_t grow;
+
+  LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+  LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+              p->type == PBUF_ROM ||
+              p->type == PBUF_RAM ||
+              p->type == PBUF_REF);
+
+  /* desired length larger than current length? */
+  if (new_len >= p->tot_len) {
+    /* enlarging not yet supported */
+    return;
+  }
+
+  /* the pbuf chain grows by (new_len - p->tot_len) bytes
+   * (which may be negative in case of shrinking) */
+  grow = new_len - p->tot_len;
+
+  /* first, step over any pbufs that should remain in the chain */
+  rem_len = new_len;
+  q = p;
+  /* should this pbuf be kept? */
+  while (rem_len > q->len) {
+    /* decrease remaining length by pbuf length */
+    rem_len -= q->len;
+    /* decrease total length indicator */
+    LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+    q->tot_len += (u16_t)grow;
+    /* proceed to next pbuf in chain */
+    q = q->next;
+    LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+  }
+  /* we have now reached the new last pbuf (in q) */
+  /* rem_len == desired length for pbuf q */
+
+  /* shrink allocated memory for PBUF_RAM */
+  /* (other types merely adjust their length fields */
+  if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
+    /* reallocate and adjust the length of the pbuf that will be split */
+    q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+    LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+  }
+  /* adjust length fields for new last pbuf */
+  q->len = rem_len;
+  q->tot_len = q->len;
+
+  /* any remaining pbufs in chain? */
+  if (q->next != NULL) {
+    /* free remaining pbufs in chain */
+    pbuf_free(q->next);
+  }
+  /* q is last packet in chain */
+  q->next = NULL;
+
+}
+
+
+/**
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ * 
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+  u16_t type;
+  struct pbuf *q;
+  u8_t count;
+
+  if (p == NULL) {
+    LWIP_ASSERT("p != NULL", p != NULL);
+    /* if assertions are disabled, proceed with debug output */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+      ("pbuf_free(p == NULL) was called.\n"));
+    return 0;
+  }
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+  PERF_START;
+
+  LWIP_ASSERT("pbuf_free: sane type",
+    p->type == PBUF_RAM || p->type == PBUF_ROM ||
+    p->type == PBUF_REF || p->type == PBUF_POOL);
+
+  count = 0;
+  /* de-allocate all consecutive pbufs from the head of the chain that
+   * obtain a zero reference count after decrementing*/
+  while (p != NULL) {
+    u16_t ref;
+    SYS_ARCH_DECL_PROTECT(old_level);
+    /* Since decrementing ref cannot be guaranteed to be a single machine operation
+     * we must protect it. We put the new ref into a local variable to prevent
+     * further protection. */
+    SYS_ARCH_PROTECT(old_level);
+    /* all pbufs in a chain are referenced at least once */
+    LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+    /* decrease reference count (number of pointers to pbuf) */
+    ref = --(p->ref);
+    SYS_ARCH_UNPROTECT(old_level);
+    /* this pbuf is no longer referenced to? */
+    if (ref == 0) {
+      /* remember next pbuf in chain for next iteration */
+      q = p->next;
+      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+      type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+      /* is this a custom pbuf? */
+      if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+        struct pbuf_custom *pc = (struct pbuf_custom*)p;
+        LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+        pc->custom_free_function(p);
+      } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+      {
+        /* is this a pbuf from the pool? */
+        if (type == PBUF_POOL) {
+          memp_free(MEMP_PBUF_POOL, p);
+        /* is this a ROM or RAM referencing pbuf? */
+        } else if (type == PBUF_ROM || type == PBUF_REF) {
+          memp_free(MEMP_PBUF, p);
+        /* type == PBUF_RAM */
+        } else {
+          mem_free(p);
+        }
+      }
+      count++;
+      /* proceed to next pbuf */
+      p = q;
+    /* p->ref > 0, this pbuf is still referenced to */
+    /* (and so the remaining pbufs in chain as well) */
+    } else {
+      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+      /* stop walking through the chain */
+      p = NULL;
+    }
+  }
+  PERF_STOP("pbuf_free");
+  /* return number of de-allocated pbufs */
+  return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+
+u8_t
+pbuf_clen(struct pbuf *p)
+{
+  u8_t len;
+
+  len = 0;
+  while (p != NULL) {
+    ++len;
+    p = p->next;
+  }
+  return len;
+}
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+  SYS_ARCH_DECL_PROTECT(old_level);
+  /* pbuf given? */
+  if (p != NULL) {
+    SYS_ARCH_PROTECT(old_level);
+    ++(p->ref);
+    SYS_ARCH_UNPROTECT(old_level);
+  }
+}
+
+/**
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ * 
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ * 
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+
+  LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+             ((h != NULL) && (t != NULL)), return;);
+
+  /* proceed to last pbuf of chain */
+  for (p = h; p->next != NULL; p = p->next) {
+    /* add total length of second chain to all totals of first chain */
+    p->tot_len += t->tot_len;
+  }
+  /* { p is last pbuf of first h chain, p->next == NULL } */
+  LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+  LWIP_ASSERT("p->next == NULL", p->next == NULL);
+  /* add total length of second chain to last pbuf total of first chain */
+  p->tot_len += t->tot_len;
+  /* chain last pbuf of head (p) with first of tail (t) */
+  p->next = t;
+  /* p->next now references t, but the caller will drop its reference to t,
+   * so netto there is no change to the reference count of t.
+   */
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ * 
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ * 
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+  pbuf_cat(h, t);
+  /* t is now referenced by h */
+  pbuf_ref(t);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+  struct pbuf *q;
+  u8_t tail_gone = 1;
+  /* tail */
+  q = p->next;
+  /* pbuf has successor in chain? */
+  if (q != NULL) {
+    /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+    LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+    /* enforce invariant if assertion is disabled */
+    q->tot_len = p->tot_len - p->len;
+    /* decouple pbuf from remainder */
+    p->next = NULL;
+    /* total length of pbuf p is its own length only */
+    p->tot_len = p->len;
+    /* q is no longer referenced by p, free it */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+    tail_gone = pbuf_free(q);
+    if (tail_gone > 0) {
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+                  ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+    }
+    /* return remaining tail or NULL if deallocated */
+  }
+  /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+  LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+  return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ *
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ *         ERR_ARG if one of the pbufs is NULL or p_to is not big
+ *                 enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+{
+  u16_t offset_to=0, offset_from=0, len;
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+    (void*)p_to, (void*)p_from));
+
+  /* is the target big enough to hold the source? */
+  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+  /* iterate through pbuf chain */
+  do
+  {
+    LWIP_ASSERT("p_to != NULL", p_to != NULL);
+    /* copy one part of the original chain */
+    if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+      /* complete current p_from fits into current p_to */
+      len = p_from->len - offset_from;
+    } else {
+      /* current p_from does not fit into current p_to */
+      len = p_to->len - offset_to;
+    }
+    MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+    offset_to += len;
+    offset_from += len;
+    LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+    if (offset_to == p_to->len) {
+      /* on to next p_to (if any) */
+      offset_to = 0;
+      p_to = p_to->next;
+    }
+    LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+    if (offset_from >= p_from->len) {
+      /* on to next p_from (if any) */
+      offset_from = 0;
+      p_from = p_from->next;
+    }
+
+    if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+                 (p_from->next == NULL), return ERR_VAL;);
+    }
+    if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+                  (p_to->next == NULL), return ERR_VAL;);
+    }
+  } while (p_from);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+  return ERR_OK;
+}
+
+
+/**
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+  struct pbuf *p;
+  u16_t buf_copy_len;
+  u16_t total_copy_len = len;
+  u16_t copied_total = 0;
+
+  LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
+  LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
+
+  if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+    return ERR_ARG;
+  }
+
+  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+  for(p = buf; total_copy_len != 0; p = p->next) {
+    LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+    buf_copy_len = total_copy_len;
+    if (buf_copy_len > p->len) {
+      /* this pbuf cannot hold all remaining data */
+      buf_copy_len = p->len;
+    }
+    /* copy the necessary parts of the buffer */
+    MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+    total_copy_len -= buf_copy_len;
+    copied_total += buf_copy_len;
+  }
+  LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+  return ERR_OK;
+}
+
+/**
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ *          pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ *         or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+  struct pbuf *q;
+  err_t err;
+  if (p->next == NULL) {
+    return p;
+  }
+  q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+  if (q == NULL) {
+    /* @todo: what do we do now? */
+    return p;
+  }
+  err = pbuf_copy(q, p);
+  LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+  pbuf_free(p);
+  return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ *         within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+                 u16_t len, u16_t *chksum)
+{
+  u32_t acc;
+  u16_t copy_chksum;
+  char *dst_ptr;
+  LWIP_ASSERT("p != NULL", p != NULL);
+  LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+  LWIP_ASSERT("chksum != NULL", chksum != NULL);
+  LWIP_ASSERT("len != 0", len != 0);
+
+  if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+    return ERR_ARG;
+  }
+
+  dst_ptr = ((char*)p->payload) + start_offset;
+  copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+  if ((start_offset & 1) != 0) {
+    copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+  }
+  acc = *chksum;
+  acc += copy_chksum;
+  *chksum = FOLD_U32T(acc);
+  return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /** Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(struct pbuf* p, u16_t offset)
+{
+  u16_t copy_from = offset;
+  struct pbuf* q = p;
+
+  /* get the correct pbuf */
+  while ((q != NULL) && (q->len <= copy_from)) {
+    copy_from -= q->len;
+    q = q->next;
+  }
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > copy_from)) {
+    return ((u8_t*)q->payload)[copy_from];
+  }
+  return 0;
+}
+
+/** Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at wich to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ *         (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+  u16_t start = offset;
+  struct pbuf* q = p;
+
+  /* get the correct pbuf */
+  while ((q != NULL) && (q->len <= start)) {
+    start -= q->len;
+    q = q->next;
+  }
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > start)) {
+    u16_t i;
+    for(i = 0; i < n; i++) {
+      u8_t a = pbuf_get_at(q, start + i);
+      u8_t b = ((u8_t*)s2)[i];
+      if (a != b) {
+        return i+1;
+      }
+    }
+    return 0;
+  }
+  return 0xffff;
+}
+
+/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+  u16_t i;
+  u16_t max = p->tot_len - mem_len;
+  if (p->tot_len >= mem_len + start_offset) {
+    for(i = start_offset; i <= max; ) {
+      u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+      if (plus == 0) {
+        return i;
+      } else {
+        i += plus;
+      }
+    }
+  }
+  return 0xFFFF;
+}
+
+/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(struct pbuf* p, const char* substr)
+{
+  size_t substr_len;
+  if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+    return 0xFFFF;
+  }
+  substr_len = strlen(substr);
+  if (substr_len >= 0xFFFF) {
+    return 0xFFFF;
+  }
+  return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
+
+#endif /*EVERYTHING*/
diff --git a/kern/src/net/udp.c b/kern/src/net/udp.c
new file mode 100644 (file)
index 0000000..932063e
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * Contains shamelessly stolen code from BSD & lwip, both have
+ * BSD-style licenses
+ *
+ */
+#include <ros/common.h>
+#include <string.h>
+#include <kmalloc.h>
+#include <socket.h>
+#include <net.h>
+#include <sys/queue.h> //TODO: use sys/queue.h rather than implementing
+#include <arch/atomic.h>
+
+#include <bits/netinet.h>
+#include <net/ip.h>
+#include <net/udp.h>
+#include <slab.h>
+#include <socket.h>
+
+struct udp_pcb *udp_pcbs;
+uint16_t udp_port_num = SOCKET_PORT_START;
+
+
+struct udp_pcb* udp_new(void){
+       struct udp_pcb *pcb = kmem_cache_alloc(udp_pcb_kcache, 0);
+    // if pcb is only tracking ttl, then no need!
+       if (pcb!= NULL){
+               pcb->ttl = UDP_TTL;
+       memset(pcb, 0, sizeof(struct udp_pcb));
+       }
+       return pcb;
+}
+
+int udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+  /* send to the packet using remote ip and port stored in the pcb */
+  // rip and rport should be in socket not pcb?
+  return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+
+int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+                    struct in_addr *dst_ip, uint16_t dst_port){
+    // we now have one netif to send to, otherwise we need to route
+    // ip_route();
+    struct udp_hdr *udphdr;
+    struct pbuf *q;
+               /* TODO: utility to peform inet_ntop and        inet_pton for debugging*/
+               printd("udp_sendto ip %x, port %d\n", dst_ip->s_addr, dst_port); 
+    // broadcast?
+    if (pcb->local_port == 0) {
+                               /* if the PCB not bound to a port, bind it and give local ip */
+        if (udp_bind(pcb, &pcb->local_ip, pcb->local_port)!=0)
+                                       warn("udp binding failed \n");
+    }
+    if (pbuf_header(p, UDP_HLEN)){ // we could probably save this check for block.
+        // CHECK: should allocate enough for the other headers too
+        q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+        if (q == NULL)
+           panic("out of memory");
+        // if the original packet is not empty
+        if (p->tot_len !=0) {
+            pbuf_chain(q,p);
+            // check if it is chained properly ..
+        }
+    } else {
+                               /* Successfully padded the header*/
+                               q = p;
+    }
+    udphdr = (struct udp_hdr *) q->payload;
+               printd("src port %x, dst port %x \n, length %x ", pcb->local_port, dst_port, q->tot_len);
+    udphdr->src_port = htons(pcb->local_port);
+    udphdr->dst_port = (dst_port);
+    udphdr->checksum = 0x0000; //either use brho's checksum or use cards' capabilities
+    udphdr->length = htons(q->tot_len); // 630
+               // ip_output(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP);
+               ip_output(q, &global_ip, dst_ip, IPPROTO_UDP);
+
+    return 0;
+
+    // generate checksum if we need it.. check net.c
+    //src_ip = GLOBAL_IP;
+    // ip_output(blah);
+
+    // check if there is space to operate in place (likely not)
+    // allocate additional pbuf
+    // chain the two bufs together
+    // add udp headers
+    // call ip layer
+
+    // checksum calculation?
+}
+/* TODO: use the real queues we have implemented... */
+int udp_bind(struct udp_pcb *pcb, struct in_addr *ip, uint16_t port){ 
+    int rebind = pcb->local_port;
+    struct udp_pcb *ipcb;
+               assert(pcb);
+               /* trying to assign port */
+    if (port != 0)
+        pcb->local_port = port;
+
+    /* no lock needed since we are just traversing/reading */
+    /* Check for double bind and rebind of the same pcb */
+    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+        /* is this UDP PCB already on active list? */
+        if (pcb == ipcb) {
+            rebind = 1; //already on the list
+        } else if (ipcb->local_port == port){
+            warn("someone else is using the port %d\n" , port); 
+            return -1;
+        }
+    }
+    /* accept data for all interfaces */
+    if (ip == NULL || (ip->s_addr == INADDR_ANY.s_addr))
+        pcb->local_ip = INADDR_ANY;
+    /* assign a port */
+    if (port == 0) {
+        port = SOCKET_PORT_START; 
+        ipcb = udp_pcbs;
+        while ((ipcb != NULL) && (port != SOCKET_PORT_END)) {
+            if (ipcb->local_port == port) {
+                /* port is already used by another udp_pcb */
+                port++;
+                /* restart scanning all udp pcbs */
+                ipcb = udp_pcbs;
+            } else {
+                /* go on with next udp pcb */
+                ipcb = ipcb->next;
+            }
+        }
+        if (ipcb != NULL){
+            warn("No more udp ports available!");
+        }
+    }
+    if (rebind == 0) {
+        /* place the PCB on the active list if not already there */
+                               pcb->next = udp_pcbs;
+                               udp_pcbs = pcb;
+    }
+               printk("local port bound to 0x%x \n", port);
+    pcb->local_port = port;
+    return 0;
+}
+
+
+
+
diff --git a/kern/src/socket.c b/kern/src/socket.c
new file mode 100644 (file)
index 0000000..2b8793d
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2011 The Regents of the University of California
+ * David Zhu <yuzhu@cs.berkeley.edu>
+ * See LICENSE for details.
+ * 
+ * Socket layer on top of TCP abstraction. Similar to the BSD implementation.
+ *
+ */
+#include <ros/common.h>
+#include <socket.h>
+#include <vfs.h>
+#include <kref.h>
+#include <syscall.h>
+#include <sys/uio.h>
+#include <mbuf.h>
+#include <ros/errno.h>
+#include <net.h>
+#include <net/udp.h>
+#include <net/pbuf.h>
+/*
+ *TODO: Figure out which socket.h is used where
+ *There are several socket.h in kern, and a couple more in glibc. Perhaps the glibc ones
+ *should grab from here..
+ */
+
+struct kmem_cache *sock_kcache;
+struct kmem_cache *mbuf_kcache;
+struct kmem_cache *udp_pcb_kcache;
+// file ops needed to support read/write on socket fd
+static struct file_operations socket_op = {
+       0,
+       0,//soo_read,
+       0,//soo_write,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,//soo_poll,
+       0,
+       0,
+       0, // sendpage might apply here
+       0,
+};
+static struct socket* getsocket(struct proc *p, int fd){
+       /* look up fd -> file */
+       struct file *so_file = get_file_from_fd(&(p->open_files), fd);
+
+       /* get socket and verify its type */
+       if (so_file == NULL){
+               printd("getsocket() fd -> null file: fd %d\n", fd);
+               return NULL;
+       }
+       if (so_file->f_op != &socket_op) {
+               set_errno(ENOTSOCK);
+               printd("fd %d maps to non-socket file\n");
+               return NULL;
+       } else
+               return (struct socket*) so_file->f_privdata;
+}
+
+struct socket* alloc_sock(int socket_family, int socket_type, int protocol){
+       struct socket *newsock = kmem_cache_alloc(sock_kcache, 0);
+       assert(newsock);
+
+       newsock->so_family = socket_family;
+       newsock->so_type = socket_type;
+       newsock->so_protocol = protocol;
+       newsock->so_state = SS_ISDISCONNECTED;
+       if (socket_type == SOCK_DGRAM) 
+               newsock->so_pcb = udp_new();
+       return newsock;
+
+}
+// TODO: refactor vfs so we can allocate fd and do the basic initialization
+struct file *alloc_socket_file(struct socket* sock) {
+       struct file *file = alloc_file();
+       if (file == NULL) return 0;
+
+       // Linux fakes a dentry and an inode for socks, see socket.c : sock_alloc_file
+       file->f_dentry = NULL; // This might break things?
+       file->f_vfsmnt = 0;
+       file->f_flags = 0;
+
+       file->f_mode = S_IRUSR | S_IWUSR; // both read and write for socket files
+
+       file->f_pos = 0;
+       file->f_uid = 0;
+       file->f_gid = 0;
+       file->f_error = 0;
+
+       file->f_op = &socket_op;
+       file->f_privdata = sock;
+       file->f_mapping = 0;
+       return file;
+}
+
+void socket_init(){
+       
+       /* allocate buf for socket */
+       sock_kcache = kmem_cache_create("socket", sizeof(struct socket),
+                                                                       __alignof__(struct socket), 0, 0, 0);
+       udp_pcb_kcache = kmem_cache_create("udppcb", sizeof(struct udp_pcb), 
+                                                                       __alignof__(struct udp_pcb), 0, 0, 0);
+
+       pbuf_init();
+
+}
+
+intreg_t sys_socket(struct proc *p, int socket_family, int socket_type, int protocol){
+       //check validity of params
+       if (socket_family !=AF_INET && socket_type!=SOCK_DGRAM)
+               return 0;
+       struct socket *sock = alloc_sock(socket_family, socket_type, protocol);
+       struct file *file = alloc_socket_file(sock);
+       
+       if (file == NULL) return -1;
+       int fd = insert_file(&p->open_files, file, 0);
+       if (fd < 0) {
+               warn("File insertion for socket open failed");
+               return -1;
+       }
+       kref_put(&file->f_kref);
+       printk("Socket open, res = %d\n", fd);
+       return fd;
+}
+intreg_t send_iov(struct socket* sock, struct iovec* iov, int flags){
+       
+       // COPY_COUNT: for each iov, copy into mbuf, and send
+       // should not copy here, copy in the protocol..
+       // should be esomething like this sock->so_proto->pr_send(sock, iov, flags);
+       // make it datagram specific for now...
+       send_datagram(sock, iov, flags);
+       // finally time to check for validity of UA, in the protocol send
+       return 0;       
+}
+/*TODO: iov support currently broken */
+int send_datagram(struct socket* sock, struct iovec* iov, int flags){
+       // is this a connection oriented protocol? 
+       struct pbuf *prev = NULL;
+       struct pbuf *curr = NULL;
+       if (sock->so_type == SOCK_STREAM){
+               set_errno(ENOTCONN);
+               return -1;
+       }
+       
+       // possible sock locks needed
+       if ((sock->so_state & SS_ISCONNECTED) == 0){
+               set_errno(EINVAL);
+               return -1;
+       }
+    // pbuf_ref needs to map in the user ref
+       for (int i = 0; i< sizeof(iov) / sizeof (struct iovec); i++){
+               prev = curr;
+               curr = pbuf_alloc(PBUF_TRANSPORT, iov[i].iov_len, PBUF_REF);
+               if (prev!=NULL) pbuf_chain(prev, curr);
+       }
+       // struct pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, PBUF_REF);
+       udp_send(sock->so_pcb, prev);
+       return 0;
+       
+}
+
+/* sys_sendto can send SOCK_DGRAM and eventually SOCK_STREAM 
+ * SOCK_DGRAM uses PBUF_REF since UDP does not need to wait for ack
+ * SOCK_STREAM uses PBUF_
+ *
+ */
+intreg_t sys_sendto(struct proc *p_proc, int fd, const void *buffer, size_t length, 
+                       int flags, const struct sockaddr *dest_addr, socklen_t dest_len){
+       // look up the socket
+       struct socket* sock = getsocket(p_proc, fd);
+       int error;
+       struct sockaddr_in *in_addr;
+       uint16_t r_port;
+       if (sock == NULL) {
+               set_errno(EBADF);
+               return -1;      
+       }
+       if (sock->so_type == SOCK_DGRAM){
+               in_addr = (struct sockaddr_in *)dest_addr;
+               struct pbuf* buf = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_REF);
+               if (buf != NULL)
+                       buf->payload = (void*)buffer;
+               else 
+                       warn("pbuf alloc failed \n");
+               // potentially unsafe cast to udp_pcb 
+               return udp_sendto((struct udp_pcb*) sock->so_pcb, buf, &in_addr->sin_addr, in_addr->sin_port);
+       }
+
+       return -1;
+  //TODO: support for sendmsg and iovectors? Let's get the basis working first!
+       #if 0 
+       // use iovector to handle sendmsg calls too, and potentially scatter-gather
+       struct msghdr msg;
+       struct iovec iov;
+       struct uio auio;
+       
+       // checking for permission only when you are sending it
+       // potential bug TOCTOU, especially with async calls
+               
+    msg.msg_name = dest_addr;
+    msg.msg_namelen = dest_len;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = 0;
+    
+       iov.iov_base = buffer;
+    iov.iov_len = length;
+       
+
+       // this is why we need another function to populate auio
+
+       auio.uio_iov = iov;
+       auio.uio_iovcnt = 1;
+       auio.uio_offset = 0;
+       auio.uio_resid = 0;
+       auio.uio_rw = UIO_WRITE;
+       auio.uio_proc = p;
+
+       // consider changing to send_uaio, since we care about progress.
+    error = send_iov(soc, iov, flags);
+       #endif
+}
+
+intreg_t sys_recvfrom(struct proc *p, int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len){
+       warn("not implemented yet\n");
+       return -1;
+}
index 951f0ec..3817435 100644 (file)
@@ -1384,6 +1384,8 @@ const static struct sys_table_entry syscall_table[] = {
        [SYS_change_to_m] = {(syscall_t)sys_change_to_m, "change_to_m"},
        [SYS_poke_ksched] = {(syscall_t)sys_poke_ksched, "poke_ksched"},
        [SYS_socket] ={(syscall_t)sys_socket, "socket"},
+       [SYS_sendto] ={(syscall_t)sys_sendto, "sendto"},
+       [SYS_recvfrom] ={(syscall_t)sys_recvfrom, "recvfrom"},
 
        [SYS_read] = {(syscall_t)sys_read, "read"},
        [SYS_write] = {(syscall_t)sys_write, "write"},
index 397ad82..f09c2d6 100644 (file)
@@ -9,11 +9,11 @@
 #include <string.h>
 #include <stdlib.h>
 //single fragment for now
-#define BUF_SIZE 512
+#define BUF_SIZE 16
 
 int main(int argc, char* argv[]) {
        struct sockaddr_in server;
-       char buf[BUF_SIZE];
+       char buf[BUF_SIZE] = "hello world";
        int sockfd, n;
        struct  hostent* host;
        if (argc != 1) {
@@ -29,16 +29,15 @@ int main(int argc, char* argv[]) {
        server.sin_addr.s_addr = inet_addr("10.0.0.1"); //hardcoded server
        //server.sin_addr = *((struct in_addr *)host->h_addr);
 
-       if (sockfd = socket(AF_INET, SOCK_DGRAM, 0) ==-1) {
+       if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) ==-1) {
                printf("socket error\n");
                return -1;
        }
+       printf ("udp_test: sockfd %d \n", sockfd);
 
-       if (sendto(sockfd, buf, BUF_SIZE, 0, (struct sockaddr*) &server, sizeof(server)) != BUF_SIZE) {
-               printf("send failed\n");
-               return -1;
-       }
-
+       int sendsize = sendto(sockfd, buf, BUF_SIZE, 0, (struct sockaddr*) &server, sizeof(server));
+       printf ("sendto returns %d, errno %d\n", sendsize, errno);
+/*
        if ((n = recvfrom(sockfd, buf, BUF_SIZE, 0, NULL, NULL)< 2)){
                printf ("recv failed\n");
                return -1;
@@ -46,5 +45,5 @@ int main(int argc, char* argv[]) {
 
        buf[n-2] = 0; //null terminate
        printf("%s\n", buf);
-       
+*/     
 }
diff --git a/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/recvfrom.c b/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/recvfrom.c
new file mode 100644 (file)
index 0000000..2e301df
--- /dev/null
@@ -0,0 +1,14 @@
+#include <sysdep.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <ros/syscall.h>
+
+int __recvfrom (int s, void *buf, size_t len, int flags,
+                 struct sockaddr *from, socklen_t *fromlen)
+{
+       return ros_syscall(SYS_recvfrom, s, buf, len, flags, from, fromlen);
+}
+
+weak_alias (__recvfrom, recvfrom)
diff --git a/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/sendto.c b/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/sendto.c
new file mode 100644 (file)
index 0000000..8dad065
--- /dev/null
@@ -0,0 +1,13 @@
+#include <sysdep.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <ros/syscall.h>
+
+int
+__sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) {
+       return ros_syscall(SYS_sendto, s, buf, len, flags, to, tolen);
+}
+
+weak_alias (__sendto, sendto)
index 17fcd8b..509df81 100644 (file)
@@ -6,7 +6,9 @@
 
 int
 __socket(int socket_family, int socket_type, int protocol) {
-       return ros_syscall(SYS_socket, socket_family, socket_type, protocol, 0,0,0);
+       int a =  ros_syscall(SYS_socket, socket_family, socket_type, protocol, 0,0,0);
+       printf("socket call result %d\n", a);
+       return a;
 }
 
 //libc_hidden_def (__socket)