123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- #include "ip.h"
- #include "ipv6.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 */
- uchar vihl; /* Version and header length */
- uchar tos; /* Type of service */
- uchar length[2]; /* packet length */
- uchar id[2]; /* Identification */
- uchar frag[2]; /* Fragment information */
- uchar Unused;
- uchar udpproto; /* Protocol */
- uchar udpplen[2]; /* Header plus data length */
- uchar udpsrc[IPv4addrlen]; /* Ip source */
- uchar udpdst[IPv4addrlen]; /* Ip destination */
- /* udp header */
- uchar udpsport[2]; /* Source port */
- uchar udpdport[2]; /* Destination port */
- uchar udplen[2]; /* data length */
- uchar udpcksum[2]; /* Checksum */
- };
- typedef struct Udp6hdr Udp6hdr;
- struct Udp6hdr {
- uchar viclfl[4];
- uchar len[2];
- uchar nextheader;
- uchar hoplimit;
- uchar udpsrc[IPaddrlen];
- uchar udpdst[IPaddrlen];
- /* udp header */
- uchar udpsport[2]; /* Source port */
- uchar udpdport[2]; /* Destination port */
- uchar udplen[2]; /* data length */
- uchar udpcksum[2]; /* Checksum */
- };
- /* MIB II counters */
- typedef struct Udpstats Udpstats;
- struct Udpstats
- {
- ulong udpInDatagrams;
- ulong udpNoPorts;
- ulong udpInErrors;
- ulong udpOutDatagrams;
- };
- typedef struct Udppriv Udppriv;
- struct Udppriv
- {
- Ipht ht;
- /* MIB counters */
- Udpstats ustats;
- /* non-MIB stats */
- ulong csumerr; /* checksum errors */
- ulong lenerr; /* short packet */
- };
- void (*etherprofiler)(char *name, int qlen);
- void udpkick(void *x, Block *bp);
- /*
- * protocol specific part of Conv
- */
- typedef struct Udpcb Udpcb;
- struct Udpcb
- {
- QLock;
- uchar headers;
- };
- static char*
- udpconnect(Conv *c, char **argv, int argc)
- {
- char *e;
- Udppriv *upriv;
- upriv = c->p->priv;
- e = Fsstdconnect(c, argv, argc);
- Fsconnected(c, e);
- if(e != nil)
- return e;
- iphtadd(&upriv->ht, c);
- return nil;
- }
- static int
- udpstate(Conv *c, char *state, int n)
- {
- return snprint(state, n, "%s", c->inuse?"Open":"Closed");
- }
- static char*
- udpannounce(Conv *c, char** argv, int argc)
- {
- char *e;
- Udppriv *upriv;
- upriv = c->p->priv;
- e = Fsstdannounce(c, argv, argc);
- if(e != nil)
- return e;
- Fsconnected(c, nil);
- iphtadd(&upriv->ht, c);
- return nil;
- }
- static void
- udpcreate(Conv *c)
- {
- c->rq = qopen(128*1024, Qmsg, 0, 0);
- c->wq = qbypass(udpkick, c);
- }
- static void
- udpclose(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);
- }
- void
- udpkick(void *x, Block *bp)
- {
- Conv *c = x;
- Udp4hdr *uh4;
- Udp6hdr *uh6;
- ushort rport;
- uchar laddr[IPaddrlen], raddr[IPaddrlen];
- Udpcb *ucb;
- int dlen, ptcllen;
- Udppriv *upriv;
- Fs *f;
- int version;
- Conv *rc;
- upriv = c->p->priv;
- f = c->p->f;
- netlog(c->p->f, Logudp, "udp: kick\n");
- if(bp == nil)
- return;
- ucb = (Udpcb*)c->ptcl;
- switch(ucb->headers) {
- case 7:
- /* get user specified addresses */
- bp = pullupblock(bp, UDP_USEAD7);
- if(bp == nil)
- 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 == nil)
- 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 = 4;
- else
- version = 6;
- } else {
- if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
- memcmp(c->laddr, v4prefix, IPv4off) == 0)
- || ipcmp(c->raddr, IPnoaddr) == 0)
- version = 4;
- else
- version = 6;
- }
- 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 == nil)
- 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 = nil;
- } 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, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ));
- 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 == nil)
- 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 = nil;
- } 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);
- 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(Proto *udp, Ipifc *ifc, Block *bp)
- {
- int len;
- Udp4hdr *uh4;
- Udp6hdr *uh6;
- Conv *c;
- Udpcb *ucb;
- uchar raddr[IPaddrlen], laddr[IPaddrlen];
- ushort rport, lport;
- Udppriv *upriv;
- Fs *f;
- int version;
- int ottl, oviclfl, olen;
- uchar *p;
- upriv = udp->priv;
- f = udp->f;
- upriv->ustats.udpInDatagrams++;
- uh4 = (Udp4hdr*)(bp->rp);
- version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4;
- /* 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(nhgets(uh4->udpcksum)) {
- if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) {
- upriv->ustats.udpInErrors++;
- netlog(f, Logudp, "udp: checksum error %I\n", raddr);
- DPRINT("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);
- DPRINT("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 */
- }
- qlock(udp);
- c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
- if(c == nil){
- /* no converstation found */
- upriv->ustats.udpNoPorts++;
- qunlock(udp);
- 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;
- }
- 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);
- }
- }
- c = Fsnewcall(c, raddr, rport, laddr, lport, version);
- if(c == nil){
- qunlock(udp);
- freeblist(bp);
- return;
- }
- iphtadd(&upriv->ht, c);
- ucb = (Udpcb*)c->ptcl;
- }
- }
- qlock(c);
- qunlock(udp);
- /*
- * 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 = nil;
- panic("udpiput4: version %d", version);
- }
- if(bp == nil){
- qunlock(c);
- 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);
- netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
- laddr, lport);
- freeblist(bp);
- return;
- }
- qpass(c->rq, bp);
- qunlock(c);
- }
- char*
- udpctl(Conv *c, char **f, int n)
- {
- Udpcb *ucb;
- ucb = (Udpcb*)c->ptcl;
- if(n == 1){
- if(strcmp(f[0], "oldheaders") == 0){
- ucb->headers = 6;
- return nil;
- } else if(strcmp(f[0], "headers") == 0){
- ucb->headers = 7;
- return nil;
- }
- }
- return "unknown control request";
- }
- void
- udpadvise(Proto *udp, Block *bp, char *msg)
- {
- Udp4hdr *h4;
- Udp6hdr *h6;
- uchar source[IPaddrlen], dest[IPaddrlen];
- ushort psource, pdest;
- Conv *s, **p;
- int version;
- h4 = (Udp4hdr*)(bp->rp);
- version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4;
- 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 */
- qlock(udp);
- 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);
- qunlock(udp);
- qhangup(s->rq, msg);
- qhangup(s->wq, msg);
- qunlock(s);
- freeblist(bp);
- return;
- }
- }
- qunlock(udp);
- freeblist(bp);
- }
- int
- udpstats(Proto *udp, char *buf, int len)
- {
- Udppriv *upriv;
- upriv = udp->priv;
- return snprint(buf, len, "InDatagrams: %lud\nNoPorts: %lud\nInErrors: %lud\nOutDatagrams: %lud\n",
- upriv->ustats.udpInDatagrams,
- upriv->ustats.udpNoPorts,
- upriv->ustats.udpInErrors,
- upriv->ustats.udpOutDatagrams);
- }
- void
- udpinit(Fs *fs)
- {
- Proto *udp;
- udp = smalloc(sizeof(Proto));
- udp->priv = smalloc(sizeof(Udppriv));
- udp->name = "udp";
- udp->connect = udpconnect;
- udp->announce = udpannounce;
- 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 = Nchans;
- udp->ptclsize = sizeof(Udpcb);
- Fsproto(fs, udp);
- }
|