net: tcp: Don't increment snd.nxt
[akaros.git] / kern / src / net / udp.c
index 3c2d27e..da58226 100644 (file)
-/**
- * Contains shamelessly stolen code from BSD & lwip, both have
- * BSD-style licenses
+/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+ * Portions Copyright © 1997-1999 Vita Nuova Limited
+ * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+ *                                (www.vitanuova.com)
+ * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
  *
  *
- */
-#include <ros/common.h>
-#include <string.h>
+ * Modified for the Akaros operating system:
+ * Copyright (c) 2013-2014 The Regents of the University of California
+ * Copyright (c) 2013-2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#define DEBUG
+#include <vfs.h>
+#include <kfs.h>
+#include <slab.h>
 #include <kmalloc.h>
 #include <kmalloc.h>
-#include <socket.h>
-#include <net.h>
-#include <sys/queue.h>
-#include <atomic.h>
-
-#include <bits/netinet.h>
-#include <net/ip.h>
-#include <net/udp.h>
+#include <kref.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <error.h>
+#include <cpio.h>
+#include <pmap.h>
+#include <smp.h>
+#include <ip.h>
+
+#include <vfs.h>
+#include <kfs.h>
 #include <slab.h>
 #include <slab.h>
-#include <socket.h>
+#include <kmalloc.h>
+#include <kref.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <error.h>
+#include <cpio.h>
+#include <pmap.h>
+#include <smp.h>
+#include <ip.h>
 
 
-struct udp_pcb *udp_pcbs;
-uint16_t udp_port_num = SOCKET_PORT_START;
+#define DPRINT if(0)print
 
 
-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;
+enum {
+       UDP_UDPHDR_SZ = 8,
+
+       UDP4_PHDR_OFF = 8,
+       UDP4_PHDR_SZ = 12,
+       UDP4_IPHDR_SZ = 20,
+       UDP6_IPHDR_SZ = 40,
+       UDP6_PHDR_SZ = 40,
+       UDP6_PHDR_OFF = 0,
+
+       IP_UDPPROTO = 17,
+       UDP_USEAD7 = 52,
+       UDP_USEAD6 = 36,
+
+       Udprxms = 200,
+       Udptickms = 100,
+       Udpmaxxmit = 10,
+};
+
+typedef struct Udp4hdr Udp4hdr;
+struct Udp4hdr {
+       /* ip header */
+       uint8_t vihl;                           /* Version and header length */
+       uint8_t tos;                            /* Type of service */
+       uint8_t length[2];                      /* packet length */
+       uint8_t id[2];                          /* Identification */
+       uint8_t frag[2];                        /* Fragment information */
+       uint8_t Unused;
+       uint8_t udpproto;                       /* Protocol */
+       uint8_t udpplen[2];                     /* Header plus data length */
+       uint8_t udpsrc[IPv4addrlen];    /* Ip source */
+       uint8_t udpdst[IPv4addrlen];    /* Ip destination */
+
+       /* udp header */
+       uint8_t udpsport[2];            /* Source port */
+       uint8_t udpdport[2];            /* Destination port */
+       uint8_t udplen[2];                      /* data length */
+       uint8_t udpcksum[2];            /* Checksum */
+};
+
+typedef struct Udp6hdr Udp6hdr;
+struct Udp6hdr {
+       uint8_t viclfl[4];
+       uint8_t len[2];
+       uint8_t nextheader;
+       uint8_t hoplimit;
+       uint8_t udpsrc[IPaddrlen];
+       uint8_t udpdst[IPaddrlen];
+
+       /* udp header */
+       uint8_t udpsport[2];            /* Source port */
+       uint8_t udpdport[2];            /* Destination port */
+       uint8_t udplen[2];                      /* data length */
+       uint8_t udpcksum[2];            /* Checksum */
+};
+
+/* MIB II counters */
+typedef struct Udpstats Udpstats;
+struct Udpstats {
+       uint32_t udpInDatagrams;
+       uint32_t udpNoPorts;
+       uint32_t udpInErrors;
+       uint32_t udpOutDatagrams;
+};
+
+typedef struct Udppriv Udppriv;
+struct Udppriv {
+       struct Ipht ht;
+
+       /* MIB counters */
+       Udpstats ustats;
+
+       /* non-MIB stats */
+       uint32_t csumerr;                       /* checksum errors */
+       uint32_t lenerr;                        /* short packet */
+};
+
+void (*etherprofiler) (char *name, int qlen);
+void udpkick(void *x, struct block *bp);
+
+/*
+ *  protocol specific part of Conv
+ */
+typedef struct Udpcb Udpcb;
+struct Udpcb {
+       uint8_t headers;
+};
+
+static void udpconnect(struct conv *c, char **argv, int argc)
+{
+       Udppriv *upriv;
+
+       upriv = c->p->priv;
+       Fsstdconnect(c, argv, argc);
+       Fsconnected(c, 0);
+
+       iphtadd(&upriv->ht, c);
 }
 
 }
 
-int udp_send(struct udp_pcb *pcb, struct pbuf *p)
+static void udpbind(struct conv *c, char **argv, int argc)
 {
 {
-  /* 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);
+       Udppriv *upriv;
+
+       upriv = c->p->priv;
+       Fsstdbind(c, argv, argc);
+       iphtadd(&upriv->ht, c);
 }
 
 }
 
-typedef unsigned char u16;
-typedef unsigned long u32;
+static int udpstate(struct conv *c, char *state, int n)
+{
+       return snprintf(state, n, "%s qin %d qout %d\n",
+                                       c->inuse ? "Open" : "Closed",
+                                       c->rq ? qlen(c->rq) : 0, c->wq ? qlen(c->wq) : 0);
+}
 
 
-u16 udp_sum_calc(u16 len_udp, u16 src_addr[],u16 dest_addr[],  int padding, u16 buff[])
+static void udpannounce(struct conv *c, char **argv, int argc)
 {
 {
-u16 prot_udp=17;
-u16 padd=0;
-u16 word16;
-u32 sum;
-int i;
-       
-       // Find out if the length of data is even or odd number. If odd,
-       // add a padding byte = 0 at the end of packet
-       if ((padding&1)==1){
-               padd=1;
-               buff[len_udp]=0;
-       }
-       
-       //initialize sum to zero
-       sum=0;
-       
-       // make 16 bit words out of every two adjacent 8 bit words and 
-       // calculate the sum of all 16 vit words
-       for (i=0;i<len_udp+padd;i=i+2){
-               word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
-               sum = sum + (unsigned long)word16;
-       }       
-       // add the UDP pseudo header which contains the IP source and destinationn addresses
-       for (i=0;i<4;i=i+2){
-               word16 =((src_addr[i]<<8)&0xFF00)+(src_addr[i+1]&0xFF);
-               sum=sum+word16; 
-       }
-       for (i=0;i<4;i=i+2){
-               word16 =((dest_addr[i]<<8)&0xFF00)+(dest_addr[i+1]&0xFF);
-               sum=sum+word16;         
-       }
-       // the protocol number and the length of the UDP packet
-       sum = sum + prot_udp + len_udp;
+       Udppriv *upriv;
+
+       upriv = c->p->priv;
+       Fsstdannounce(c, argv, argc);
+       Fsconnected(c, NULL);
+       iphtadd(&upriv->ht, c);
+}
 
 
-       // keep only the last 16 bits of the 32 bit calculated sum and add the carries
-       while (sum>>16)
-               sum = (sum & 0xFFFF)+(sum >> 16);
-               
-       // Take the one's complement of sum
-       sum = ~sum;
+static void udpbypass(struct conv *cv, char **argv, int argc)
+{
+       Udppriv *upriv = cv->p->priv;
 
 
-return ((u16) sum);
+       Fsstdbypass(cv, argv, argc);
+       iphtadd(&upriv->ht, cv);
 }
 
 }
 
-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;
-               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 %d, dst port %d \n, length %d ", pcb->local_port, ntohs(dst_port), q->tot_len);
-    udphdr->src_port = htons(pcb->local_port);
-    udphdr->dst_port = (dst_port);
-    udphdr->length = htons(q->tot_len); 
-               udphdr->checksum = 0; // just to be sure.
-               // printd("checksum inet_chksum %x \n", udphdr->checksum);
-               printd("params src addr %x, dst addr %x, length %x \n", global_ip.s_addr, (dst_ip->s_addr), 
-                                         q->tot_len);
-
-    udphdr->checksum = inet_chksum_pseudo(q, htonl(global_ip.s_addr), dst_ip->s_addr,
-                                                                                        IPPROTO_UDP, q->tot_len);
-               printd ("method ours %x\n", udphdr->checksum);
-               // 0x0000; //either use brho's checksum or use cards' capabilities
-               // 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;
+static void udpcreate(struct conv *c)
+{
+       c->rq = qopen(128 * 1024, Qmsg, 0, 0);
+       c->wq = qbypass(udpkick, c);
 }
 }
-/* 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))
-               /* true right now */
-        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;
+
+static void udpclose(struct conv *c)
+{
+       Udpcb *ucb;
+       Udppriv *upriv;
+
+       upriv = c->p->priv;
+       iphtrem(&upriv->ht, c);
+
+       c->state = 0;
+       qclose(c->rq);
+       qclose(c->wq);
+       qclose(c->eq);
+       ipmove(c->laddr, IPnoaddr);
+       ipmove(c->raddr, IPnoaddr);
+       c->lport = 0;
+       c->rport = 0;
+
+       ucb = (Udpcb *) c->ptcl;
+       ucb->headers = 0;
+
+       qunlock(&c->qlock);
 }
 
 }
 
-/* port are in host order, ips are in network order */
-/* Think: a pcb is here, if someone is waiting for a connection or the udp conn
- * has been established */
-static struct udp_pcb* find_pcb(struct udp_pcb* list, uint16_t src_port, uint16_t dst_port,
-                                                               uint16_t srcip, uint16_t dstip) {
-       struct udp_pcb* uncon_pcb = NULL;
-       struct udp_pcb* pcb = NULL;
-       uint8_t local_match = 0;
-
-       for (pcb = list; pcb != NULL; pcb = pcb->next) {
-               local_match = 0;
-               if ((pcb->local_port == dst_port) 
-                       && (pcb->local_ip.s_addr == dstip 
-                       || ip_addr_isany(&pcb->local_ip))){
-                               local_match = 1;
-        if ((uncon_pcb == NULL) && 
-            ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
-          /* the first unconnected matching PCB */
-          uncon_pcb = pcb;
-        }
-               }
+void udpkick(void *x, struct block *bp)
+{
+       struct conv *c = x;
+       Udp4hdr *uh4;
+       Udp6hdr *uh6;
+       uint16_t rport;
+       uint8_t laddr[IPaddrlen], raddr[IPaddrlen];
+       Udpcb *ucb;
+       int dlen, ptcllen;
+       Udppriv *upriv;
+       struct Fs *f;
+       int version;
+       struct conv *rc;
+
+       upriv = c->p->priv;
+       assert(upriv);
+       f = c->p->f;
+
+       netlog(c->p->f, Logudp, "udp: kick\n");
+       if (bp == NULL)
+               return;
 
 
-               if (local_match && (pcb->remote_port == src_port) &&
-                               (ip_addr_isany(&pcb->remote_ip) ||
-                                pcb->remote_ip.s_addr == srcip))
-                       /* perfect match */
-                       return pcb;
+       ucb = (Udpcb *) c->ptcl;
+       switch (ucb->headers) {
+               case 7:
+                       /* get user specified addresses */
+                       bp = pullupblock(bp, UDP_USEAD7);
+                       if (bp == NULL)
+                               return;
+                       ipmove(raddr, bp->rp);
+                       bp->rp += IPaddrlen;
+                       ipmove(laddr, bp->rp);
+                       bp->rp += IPaddrlen;
+                       /* pick interface closest to dest */
+                       if (ipforme(f, laddr) != Runi)
+                               findlocalip(f, laddr, raddr);
+                       bp->rp += IPaddrlen;    /* Ignore ifc address */
+                       rport = nhgets(bp->rp);
+                       bp->rp += 2 + 2;        /* Ignore local port */
+                       break;
+               case 6:
+                       /* get user specified addresses */
+                       bp = pullupblock(bp, UDP_USEAD6);
+                       if (bp == NULL)
+                               return;
+                       ipmove(raddr, bp->rp);
+                       bp->rp += IPaddrlen;
+                       ipmove(laddr, bp->rp);
+                       bp->rp += IPaddrlen;
+                       /* pick interface closest to dest */
+                       if (ipforme(f, laddr) != Runi)
+                               findlocalip(f, laddr, raddr);
+                       rport = nhgets(bp->rp);
+                       bp->rp += 2 + 2;        /* Ignore local port */
+                       break;
+               default:
+                       rport = 0;
+                       break;
        }
        }
-       return uncon_pcb;
+
+       if (ucb->headers) {
+               if (memcmp(laddr, v4prefix, IPv4off) == 0 ||
+                       ipcmp(laddr, IPnoaddr) == 0)
+                       version = V4;
+               else
+                       version = V6;
+       } else {
+               if ((memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
+                        memcmp(c->laddr, v4prefix, IPv4off) == 0)
+                       || ipcmp(c->raddr, IPnoaddr) == 0)
+                       version = V4;
+               else
+                       version = V6;
+       }
+
+       dlen = blocklen(bp);
+
+       /* fill in pseudo header and compute checksum */
+       switch (version) {
+               case V4:
+                       bp = padblock(bp, UDP4_IPHDR_SZ + UDP_UDPHDR_SZ);
+                       if (bp == NULL)
+                               return;
+
+                       uh4 = (Udp4hdr *) (bp->rp);
+                       ptcllen = dlen + UDP_UDPHDR_SZ;
+                       uh4->Unused = 0;
+                       uh4->udpproto = IP_UDPPROTO;
+                       uh4->frag[0] = 0;
+                       uh4->frag[1] = 0;
+                       hnputs(uh4->udpplen, ptcllen);
+                       if (ucb->headers) {
+                               v6tov4(uh4->udpdst, raddr);
+                               hnputs(uh4->udpdport, rport);
+                               v6tov4(uh4->udpsrc, laddr);
+                               rc = NULL;
+                       } else {
+                               v6tov4(uh4->udpdst, c->raddr);
+                               hnputs(uh4->udpdport, c->rport);
+                               if (ipcmp(c->laddr, IPnoaddr) == 0)
+                                       findlocalip(f, c->laddr, c->raddr);
+                               v6tov4(uh4->udpsrc, c->laddr);
+                               rc = c;
+                       }
+                       hnputs(uh4->udpsport, c->lport);
+                       hnputs(uh4->udplen, ptcllen);
+                       uh4->udpcksum[0] = 0;
+                       uh4->udpcksum[1] = 0;
+                       hnputs(uh4->udpcksum,
+                                  ~ptclcsum(bp, UDP4_PHDR_OFF, UDP4_PHDR_SZ));
+                       bp->checksum_start = UDP4_IPHDR_SZ;
+                       bp->checksum_offset = uh4->udpcksum - uh4->udpsport;
+                       bp->transport_header_end = UDP4_IPHDR_SZ;
+                       bp->flag |= Budpck;
+                       uh4->vihl = IP_VER4;
+                       ipoput4(f, bp, 0, c->ttl, c->tos, rc);
+                       break;
+
+               case V6:
+                       bp = padblock(bp, UDP6_IPHDR_SZ + UDP_UDPHDR_SZ);
+                       if (bp == NULL)
+                               return;
+
+                       // using the v6 ip header to create pseudo header
+                       // first then reset it to the normal ip header
+                       uh6 = (Udp6hdr *) (bp->rp);
+                       memset(uh6, 0, 8);
+                       ptcllen = dlen + UDP_UDPHDR_SZ;
+                       hnputl(uh6->viclfl, ptcllen);
+                       uh6->hoplimit = IP_UDPPROTO;
+                       if (ucb->headers) {
+                               ipmove(uh6->udpdst, raddr);
+                               hnputs(uh6->udpdport, rport);
+                               ipmove(uh6->udpsrc, laddr);
+                               rc = NULL;
+                       } else {
+                               ipmove(uh6->udpdst, c->raddr);
+                               hnputs(uh6->udpdport, c->rport);
+                               if (ipcmp(c->laddr, IPnoaddr) == 0)
+                                       findlocalip(f, c->laddr, c->raddr);
+                               ipmove(uh6->udpsrc, c->laddr);
+                               rc = c;
+                       }
+                       hnputs(uh6->udpsport, c->lport);
+                       hnputs(uh6->udplen, ptcllen);
+                       uh6->udpcksum[0] = 0;
+                       uh6->udpcksum[1] = 0;
+                       hnputs(uh6->udpcksum,
+                                  ptclcsum(bp, UDP6_PHDR_OFF,
+                                                       dlen + UDP_UDPHDR_SZ + UDP6_PHDR_SZ));
+                       memset(uh6, 0, 8);
+                       bp->transport_header_end = UDP6_IPHDR_SZ;
+                       uh6->viclfl[0] = IP_VER6;
+                       hnputs(uh6->len, ptcllen);
+                       uh6->nextheader = IP_UDPPROTO;
+                       ipoput6(f, bp, 0, c->ttl, c->tos, rc);
+                       break;
+
+               default:
+                       panic("udpkick: version %d", version);
+       }
+       upriv->ustats.udpOutDatagrams++;
 }
 
 }
 
-#if 0 // not working yet
-// need to have pbuf queue support
-int udp_attach(struct pbuf *p, struct sock *socket) {
-       // pretend the attaching of packet is succesful
+void udpiput(struct Proto *udp, struct Ipifc *ifc, struct block *bp)
+{
+       int len;
+       Udp4hdr *uh4;
+       Udp6hdr *uh6;
+       struct conv *c;
+       Udpcb *ucb;
+       uint8_t raddr[IPaddrlen], laddr[IPaddrlen];
+       uint16_t rport, lport;
+       Udppriv *upriv;
+       struct Fs *f;
+       int version;
+       int ottl, oviclfl, olen;
+       uint8_t *p;
+
+       upriv = udp->priv;
+       f = udp->f;
+       upriv->ustats.udpInDatagrams++;
+
+       uh4 = (Udp4hdr *) (bp->rp);
+       version = ((uh4->vihl & 0xF0) == IP_VER6) ? V6 : V4;
+
        /*
        /*
-       recv_q->last->next = p;
-       recv_q->last=p->last
-       */ 
-}
+        * Put back pseudo header for checksum
+        * (remember old values for icmpnoconv())
+        */
+       switch (version) {
+               case V4:
+                       ottl = uh4->Unused;
+                       uh4->Unused = 0;
+                       len = nhgets(uh4->udplen);
+                       olen = nhgets(uh4->udpplen);
+                       hnputs(uh4->udpplen, len);
+
+                       v4tov6(raddr, uh4->udpsrc);
+                       v4tov6(laddr, uh4->udpdst);
+                       lport = nhgets(uh4->udpdport);
+                       rport = nhgets(uh4->udpsport);
 
 
-#endif 
+                       if (!(bp->flag & Budpck) &&
+                           (uh4->udpcksum[0] || uh4->udpcksum[1]) &&
+                           ptclcsum(bp, UDP4_PHDR_OFF, len + UDP4_PHDR_SZ)) {
+                               upriv->ustats.udpInErrors++;
+                               netlog(f, Logudp, "udp: checksum error %I\n",
+                                      raddr);
+                               printd("udp: checksum error %I\n", raddr);
+                               freeblist(bp);
+                               return;
+                       }
+                       uh4->Unused = ottl;
+                       hnputs(uh4->udpplen, olen);
+                       break;
+               case V6:
+                       uh6 = (Udp6hdr *) (bp->rp);
+                       len = nhgets(uh6->udplen);
+                       oviclfl = nhgetl(uh6->viclfl);
+                       olen = nhgets(uh6->len);
+                       ottl = uh6->hoplimit;
+                       ipmove(raddr, uh6->udpsrc);
+                       ipmove(laddr, uh6->udpdst);
+                       lport = nhgets(uh6->udpdport);
+                       rport = nhgets(uh6->udpsport);
+                       memset(uh6, 0, 8);
+                       hnputl(uh6->viclfl, len);
+                       uh6->hoplimit = IP_UDPPROTO;
+                       if (ptclcsum(bp, UDP6_PHDR_OFF, len + UDP6_PHDR_SZ)) {
+                               upriv->ustats.udpInErrors++;
+                               netlog(f, Logudp, "udp: checksum error %I\n", raddr);
+                               printd("udp: checksum error %I\n", raddr);
+                               freeblist(bp);
+                               return;
+                       }
+                       hnputl(uh6->viclfl, oviclfl);
+                       hnputs(uh6->len, olen);
+                       uh6->nextheader = IP_UDPPROTO;
+                       uh6->hoplimit = ottl;
+                       break;
+               default:
+                       panic("udpiput: version %d", version);
+                       return; /* to avoid a warning */
+       }
 
 
-/** Process an incoming UDP datagram. 
- * Given an incoming UDP datagram, this function finds the right PCB
- * which links to the right socket buffer, and attaches the datagram
- * to the right socket. 
- * If no appropriate PCB is found, the pbuf is freed.
- */ 
+       c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
+       if (c == NULL) {
+               /* no converstation found */
+               upriv->ustats.udpNoPorts++;
+               netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
+                          laddr, lport);
 
 
-/** TODO: think about combining udp_input and ip_input together */
-// TODO: figure out if we even need a PCB? or just socket buff. 
-// TODO: test out looking up pcbs.. since matching function may fail
+               switch (version) {
+                       case V4:
+                               icmpnoconv(f, bp);
+                               break;
+                       case V6:
+                               icmphostunr(f, ifc, bp, icmp6_port_unreach, 0);
+                               break;
+                       default:
+                               panic("udpiput2: version %d", version);
+               }
 
 
-void wrap_restart_kthread(struct trapframe *tf, uint32_t srcid,
-                                       long a0, long a1, long a2){
-       restart_kthread((struct kthread*) a0);
-}
+               freeblist(bp);
+               return;
+       }
 
 
-int udp_input(struct pbuf *p){
-       struct udp_hdr *udphdr;
-
-       struct udp_pcb *pcb, uncon_pcb;
-       struct ip_hdr *iphdr;
-       uint16_t src, dst;
-       bool local_match = 0;
-       iphdr = (struct ip_hdr *)p->payload;
-       /* Move the header to where the udp header is */
-       if (pbuf_header(p, - PBUF_IP_HLEN)){
-               warn("udp_input: Did not find a matching PCB for a udp packet\n");
-               pbuf_free(p);
-               return -1;
+       if (c->state == Bypass) {
+               bypass_or_drop(c, bp);
+               return;
        }
        }
-       printk("start of udp %p\n", p->payload);
-       udphdr = (struct udp_hdr *)p->payload;
-       /* convert the src port and dst port to host order */
-       src = ntohs(udphdr->src_port);
-       dst = ntohs(udphdr->dst_port);
-       pcb = find_pcb(udp_pcbs, src, dst, iphdr->src_addr, iphdr->dst_addr);
-       /* TODO: Possibly adjust the pcb to the head of the queue? */
-       /* TODO: Linux uses a set of hashtables to lookup PCBs 
-        * Look at __udp4_lib_lookup function in Linux kernel 2.6.21.1
+
+       ucb = (Udpcb *) c->ptcl;
+
+       if (c->state == Announced) {
+               if (ucb->headers == 0) {
+                       /* create a new conversation */
+                       if (ipforme(f, laddr) != Runi) {
+                               switch (version) {
+                                       case V4:
+                                               v4tov6(laddr, ifc->lifc->local);
+                                               break;
+                                       case V6:
+                                               ipmove(laddr, ifc->lifc->local);
+                                               break;
+                                       default:
+                                               panic("udpiput3: version %d", version);
+                               }
+                       }
+                       qlock(&udp->qlock);
+                       c = Fsnewcall(c, raddr, rport, laddr, lport, version);
+                       qunlock(&udp->qlock);
+                       if (c == NULL) {
+                               freeblist(bp);
+                               return;
+                       }
+                       iphtadd(&upriv->ht, c);
+                       ucb = (Udpcb *) c->ptcl;
+               }
+       }
+
+       qlock(&c->qlock);
+
+       /*
+        * Trim the packet down to data size
         */
         */
-       /* Anything that is not directed at this pcb should have been dropped */
-       if (pcb == NULL){
-               warn("udp_input: Did not find a matching PCB for a udp packet\n");
-               pbuf_free(p);
-               return -1;
+       len -= UDP_UDPHDR_SZ;
+       switch (version) {
+               case V4:
+                       bp = trimblock(bp, UDP4_IPHDR_SZ + UDP_UDPHDR_SZ, len);
+                       break;
+               case V6:
+                       bp = trimblock(bp, UDP6_IPHDR_SZ + UDP_UDPHDR_SZ, len);
+                       break;
+               default:
+                       bp = NULL;
+                       panic("udpiput4: version %d", version);
+       }
+       if (bp == NULL) {
+               qunlock(&c->qlock);
+               netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
+                          laddr, lport);
+               upriv->lenerr++;
+               return;
+       }
+
+       netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
+                  laddr, lport, len);
+
+       switch (ucb->headers) {
+               case 7:
+                       /* pass the src address */
+                       bp = padblock(bp, UDP_USEAD7);
+                       p = bp->rp;
+                       ipmove(p, raddr);
+                       p += IPaddrlen;
+                       ipmove(p, laddr);
+                       p += IPaddrlen;
+                       ipmove(p, ifc->lifc->local);
+                       p += IPaddrlen;
+                       hnputs(p, rport);
+                       p += 2;
+                       hnputs(p, lport);
+                       break;
+               case 6:
+                       /* pass the src address */
+                       bp = padblock(bp, UDP_USEAD6);
+                       p = bp->rp;
+                       ipmove(p, raddr);
+                       p += IPaddrlen;
+                       ipmove(p, ipforme(f, laddr) == Runi ? laddr : ifc->lifc->local);
+                       p += IPaddrlen;
+                       hnputs(p, rport);
+                       p += 2;
+                       hnputs(p, lport);
+                       break;
        }
 
        }
 
-       /* checksum check */
-       // HERE!
-  if (udphdr->checksum != 0) {
-    if (inet_chksum_pseudo(p, (iphdr->src_addr), (iphdr->dst_addr), 
-                                IPPROTO_UDP, p->tot_len) != 0){
-                       warn("udp_input: UPD datagram discarded due to failed chksum!");
-                       pbuf_free(p);
-                       return -1;
-    }
+       if (bp->next)
+               bp = concatblock(bp);
+
+       if (qfull(c->rq)) {
+               qunlock(&c->qlock);
+               netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
+                          laddr, lport);
+               freeblist(bp);
+               return;
        }
        }
-  /* ignore SO_REUSE */
-       if (pcb != NULL && pcb->pcbsock != NULL){
-               /* For each in the pbuf chain, disconnect from the chain and add it to the
-                * recv_buff of the correct socket 
-                */ 
-               struct socket *sock = pcb->pcbsock;
-               attach_pbuf(p, &sock->recv_buff);
-               struct kthread *kthread;
-               // multiple people might be waiting on the socket here..
-               kthread = __up_sem(&(sock->sem), FALSE);
-               if (kthread) {
-                        send_kernel_message(core_id(), (amr_t)wrap_restart_kthread, (long)kthread, 0, 0,
-                                                                                                 KMSG_ROUTINE);
-               }
-               // the attaching of pbuf should have increfed pbuf ref, so free is simply a decref
-               pbuf_free(p);
+
+       qpass(c->rq, bp);
+       qunlock(&c->qlock);
+
+}
+
+static void udpctl(struct conv *c, char **f, int n)
+{
+       Udpcb *ucb = (Udpcb*)c->ptcl;
+
+       if ((n == 1) && strcmp(f[0], "oldheaders") == 0)
+               ucb->headers = 6;
+       else if ((n == 1) && strcmp(f[0], "headers") == 0)
+               ucb->headers = 7;
+       else
+               error(EINVAL, "unknown command to %s", __func__);
+}
+
+void udpadvise(struct Proto *udp, struct block *bp, char *msg)
+{
+       Udp4hdr *h4;
+       Udp6hdr *h6;
+       uint8_t source[IPaddrlen], dest[IPaddrlen];
+       uint16_t psource, pdest;
+       struct conv *s, **p;
+       int version;
+
+       h4 = (Udp4hdr *) (bp->rp);
+       version = ((h4->vihl & 0xF0) == IP_VER6) ? V6 : V4;
+
+       switch (version) {
+               case V4:
+                       v4tov6(dest, h4->udpdst);
+                       v4tov6(source, h4->udpsrc);
+                       psource = nhgets(h4->udpsport);
+                       pdest = nhgets(h4->udpdport);
+                       break;
+               case V6:
+                       h6 = (Udp6hdr *) (bp->rp);
+                       ipmove(dest, h6->udpdst);
+                       ipmove(source, h6->udpsrc);
+                       psource = nhgets(h6->udpsport);
+                       pdest = nhgets(h6->udpdport);
+                       break;
+               default:
+                       panic("udpadvise: version %d", version);
+                       return; /* to avoid a warning */
+       }
+
+       /* Look for a connection */
+       for (p = udp->conv; *p; p++) {
+               s = *p;
+               if (s->rport == pdest)
+                       if (s->lport == psource)
+                               if (ipcmp(s->raddr, dest) == 0)
+                                       if (ipcmp(s->laddr, source) == 0) {
+                                               if (s->ignoreadvice)
+                                                       break;
+                                               qlock(&s->qlock);
+                                               qhangup(s->rq, msg);
+                                               qhangup(s->wq, msg);
+                                               qunlock(&s->qlock);
+                                               freeblist(bp);
+                                               return;
+                                       }
        }
        }
-       return 0;
+       freeblist(bp);
+}
+
+int udpstats(struct Proto *udp, char *buf, int len)
+{
+       Udppriv *upriv;
+       char *p, *e;
+
+       upriv = udp->priv;
+       p = buf;
+       e = p + len;
+       p = seprintf(p, e, "InDatagrams: %u\n", upriv->ustats.udpInDatagrams);
+       p = seprintf(p, e, "NoPorts: %u\n", upriv->ustats.udpNoPorts);
+       p = seprintf(p, e, "InErrors: %u\n", upriv->ustats.udpInErrors);
+       p = seprintf(p, e, "OutDatagrams: %u\n", upriv->ustats.udpOutDatagrams);
+       return p - buf;
+}
+
+void udpinit(struct Fs *fs)
+{
+       struct Proto *udp;
+
+       udp = kzmalloc(sizeof(struct Proto), 0);
+       udp->priv = kzmalloc(sizeof(Udppriv), 0);
+       udp->name = "udp";
+       udp->connect = udpconnect;
+       udp->bind = udpbind;
+       udp->announce = udpannounce;
+       udp->bypass = udpbypass;
+       udp->ctl = udpctl;
+       udp->state = udpstate;
+       udp->create = udpcreate;
+       udp->close = udpclose;
+       udp->rcv = udpiput;
+       udp->advise = udpadvise;
+       udp->stats = udpstats;
+       udp->ipproto = IP_UDPPROTO;
+       udp->nc = 4096;
+       udp->ptclsize = sizeof(Udpcb);
+
+       Fsproto(fs, udp);
 }
 }