|  | /* 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 | 
|  | * | 
|  | * 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 <slab.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 <net/ip.h> | 
|  |  | 
|  | #include <slab.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 <net/ip.h> | 
|  |  | 
|  | #define DPRINT if(0)print | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | static void udpbind(struct conv *c, char **argv, int argc) | 
|  | { | 
|  | Udppriv *upriv; | 
|  |  | 
|  | upriv = c->p->priv; | 
|  | Fsstdbind(c, argv, argc); | 
|  | iphtadd(&upriv->ht, c); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | static void udpannounce(struct conv *c, char **argv, int argc) | 
|  | { | 
|  | Udppriv *upriv; | 
|  |  | 
|  | upriv = c->p->priv; | 
|  | Fsstdannounce(c, argv, argc); | 
|  | Fsconnected(c, NULL); | 
|  | iphtadd(&upriv->ht, c); | 
|  | } | 
|  |  | 
|  | static void udpbypass(struct conv *cv, char **argv, int argc) | 
|  | { | 
|  | Udppriv *upriv = cv->p->priv; | 
|  |  | 
|  | Fsstdbypass(cv, argv, argc); | 
|  | iphtadd(&upriv->ht, cv); | 
|  | } | 
|  |  | 
|  | static void udpcreate(struct conv *c) | 
|  | { | 
|  | c->rq = qopen(128 * 1024, Qmsg, 0, 0); | 
|  | c->wq = qbypass(udpkick, c); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | bp->network_offset = 0; | 
|  | bp->transport_offset = offsetof(Udp4hdr, udpsport); | 
|  | assert(bp->transport_offset == UDP4_IPHDR_SZ); | 
|  | hnputs(uh4->udpcksum, | 
|  | ~ptclcsum(bp, UDP4_PHDR_OFF, UDP4_PHDR_SZ)); | 
|  | bp->tx_csum_offset = uh4->udpcksum - uh4->udpsport; | 
|  | 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->network_offset = 0; | 
|  | bp->transport_offset = offsetof(Udp6hdr, udpsport); | 
|  | 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++; | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  |  | 
|  | 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 */ | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | freeblist(bp); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (c->state == Bypass) { | 
|  | bypass_or_drop(c, bp); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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 | 
|  | */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | } | 
|  | 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); | 
|  | } |