udp.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       udp.c (12404B)
       ---
            1 #include        "u.h"
            2 #include        "lib.h"
            3 #include        "mem.h"
            4 #include        "dat.h"
            5 #include        "fns.h"
            6 #include        "error.h"
            7 
            8 #include        "ip.h"
            9 #include        "ipv6.h"
           10 
           11 
           12 #define DPRINT if(0)print
           13 
           14 enum
           15 {
           16         UDP_UDPHDR_SZ        = 8,
           17 
           18         UDP4_PHDR_OFF = 8,
           19         UDP4_PHDR_SZ = 12,
           20         UDP4_IPHDR_SZ = 20,
           21         UDP6_IPHDR_SZ = 40,
           22         UDP6_PHDR_SZ = 40,
           23         UDP6_PHDR_OFF = 0,
           24 
           25         IP_UDPPROTO        = 17,
           26         UDP_USEAD7        = 52,
           27 
           28         Udprxms                = 200,
           29         Udptickms        = 100,
           30         Udpmaxxmit        = 10,
           31 };
           32 
           33 typedef struct Udp4hdr Udp4hdr;
           34 struct Udp4hdr
           35 {
           36         /* ip header */
           37         uchar        vihl;                /* Version and header length */
           38         uchar        tos;                /* Type of service */
           39         uchar        length[2];        /* packet length */
           40         uchar        id[2];                /* Identification */
           41         uchar        frag[2];        /* Fragment information */
           42         uchar        Unused;
           43         uchar        udpproto;        /* Protocol */
           44         uchar        udpplen[2];        /* Header plus data length */
           45         uchar        udpsrc[IPv4addrlen];        /* Ip source */
           46         uchar        udpdst[IPv4addrlen];        /* Ip destination */
           47 
           48         /* udp header */
           49         uchar        udpsport[2];        /* Source port */
           50         uchar        udpdport[2];        /* Destination port */
           51         uchar        udplen[2];        /* data length */
           52         uchar        udpcksum[2];        /* Checksum */
           53 };
           54 
           55 typedef struct Udp6hdr Udp6hdr;
           56 struct Udp6hdr {
           57         uchar viclfl[4];
           58         uchar len[2];
           59         uchar nextheader;
           60         uchar hoplimit;
           61         uchar udpsrc[IPaddrlen];
           62         uchar udpdst[IPaddrlen];
           63 
           64         /* udp header */
           65         uchar        udpsport[2];        /* Source port */
           66         uchar        udpdport[2];        /* Destination port */
           67         uchar        udplen[2];        /* data length */
           68         uchar        udpcksum[2];        /* Checksum */
           69 };
           70 
           71 /* MIB II counters */
           72 typedef struct Udpstats Udpstats;
           73 struct Udpstats
           74 {
           75         ulong        udpInDatagrams;
           76         ulong        udpNoPorts;
           77         ulong        udpInErrors;
           78         ulong        udpOutDatagrams;
           79 };
           80 
           81 typedef struct Udppriv Udppriv;
           82 struct Udppriv
           83 {
           84         Ipht                ht;
           85 
           86         /* MIB counters */
           87         Udpstats        ustats;
           88 
           89         /* non-MIB stats */
           90         ulong                csumerr;                /* checksum errors */
           91         ulong                lenerr;                        /* short packet */
           92 };
           93 
           94 void (*etherprofiler)(char *name, int qlen);
           95 void udpkick(void *x, Block *bp);
           96 
           97 /*
           98  *  protocol specific part of Conv
           99  */
          100 typedef struct Udpcb Udpcb;
          101 struct Udpcb
          102 {
          103         QLock        qlock;
          104         uchar        headers;
          105 };
          106 
          107 static char*
          108 udpconnect(Conv *c, char **argv, int argc)
          109 {
          110         char *e;
          111         Udppriv *upriv;
          112 
          113         upriv = c->p->priv;
          114         e = Fsstdconnect(c, argv, argc);
          115         Fsconnected(c, e);
          116         if(e != nil)
          117                 return e;
          118 
          119         iphtadd(&upriv->ht, c);
          120         return nil;
          121 }
          122 
          123 
          124 static int
          125 udpstate(Conv *c, char *state, int n)
          126 {
          127         return snprint(state, n, "%s qin %d qout %d\n",
          128                 c->inuse ? "Open" : "Closed",
          129                 c->rq ? qlen(c->rq) : 0,
          130                 c->wq ? qlen(c->wq) : 0
          131         );
          132 }
          133 
          134 static char*
          135 udpannounce(Conv *c, char** argv, int argc)
          136 {
          137         char *e;
          138         Udppriv *upriv;
          139 
          140         upriv = c->p->priv;
          141         e = Fsstdannounce(c, argv, argc);
          142         if(e != nil)
          143                 return e;
          144         Fsconnected(c, nil);
          145         iphtadd(&upriv->ht, c);
          146 
          147         return nil;
          148 }
          149 
          150 static void
          151 udpcreate(Conv *c)
          152 {
          153         c->rq = qopen(128*1024, Qmsg, 0, 0);
          154         c->wq = qbypass(udpkick, c);
          155 }
          156 
          157 static void
          158 udpclose(Conv *c)
          159 {
          160         Udpcb *ucb;
          161         Udppriv *upriv;
          162 
          163         upriv = c->p->priv;
          164         iphtrem(&upriv->ht, c);
          165 
          166         c->state = 0;
          167         qclose(c->rq);
          168         qclose(c->wq);
          169         qclose(c->eq);
          170         ipmove(c->laddr, IPnoaddr);
          171         ipmove(c->raddr, IPnoaddr);
          172         c->lport = 0;
          173         c->rport = 0;
          174 
          175         ucb = (Udpcb*)c->ptcl;
          176         ucb->headers = 0;
          177 }
          178 
          179 void
          180 udpkick(void *x, Block *bp)
          181 {
          182         Conv *c = x;
          183         Udp4hdr *uh4;
          184         Udp6hdr *uh6;
          185         ushort rport;
          186         uchar laddr[IPaddrlen], raddr[IPaddrlen];
          187         Udpcb *ucb;
          188         int dlen, ptcllen;
          189         Udppriv *upriv;
          190         Fs *f;
          191         int version;
          192         Conv *rc;
          193 
          194         upriv = c->p->priv;
          195         f = c->p->f;
          196 
          197         netlog(c->p->f, Logudp, "udp: kick\n");
          198         if(bp == nil)
          199                 return;
          200 
          201         ucb = (Udpcb*)c->ptcl;
          202         switch(ucb->headers) {
          203         case 7:
          204                 /* get user specified addresses */
          205                 bp = pullupblock(bp, UDP_USEAD7);
          206                 if(bp == nil)
          207                         return;
          208                 ipmove(raddr, bp->rp);
          209                 bp->rp += IPaddrlen;
          210                 ipmove(laddr, bp->rp);
          211                 bp->rp += IPaddrlen;
          212                 /* pick interface closest to dest */
          213                 if(ipforme(f, laddr) != Runi)
          214                         findlocalip(f, laddr, raddr);
          215                 bp->rp += IPaddrlen;                /* Ignore ifc address */
          216                 rport = nhgets(bp->rp);
          217                 bp->rp += 2+2;                        /* Ignore local port */
          218                 break;
          219         default:
          220                 rport = 0;
          221                 break;
          222         }
          223 
          224         if(ucb->headers) {
          225                 if(memcmp(laddr, v4prefix, IPv4off) == 0
          226                 || ipcmp(laddr, IPnoaddr) == 0)
          227                         version = 4;
          228                 else
          229                         version = 6;
          230         } else {
          231                 if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
          232                         memcmp(c->laddr, v4prefix, IPv4off) == 0)
          233                         || ipcmp(c->raddr, IPnoaddr) == 0)
          234                         version = 4;
          235                 else
          236                         version = 6;
          237         }
          238 
          239         dlen = blocklen(bp);
          240 
          241         /* fill in pseudo header and compute checksum */
          242         switch(version){
          243         case V4:
          244                 bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ);
          245                 if(bp == nil)
          246                         return;
          247 
          248                 uh4 = (Udp4hdr *)(bp->rp);
          249                 ptcllen = dlen + UDP_UDPHDR_SZ;
          250                 uh4->Unused = 0;
          251                 uh4->udpproto = IP_UDPPROTO;
          252                 uh4->frag[0] = 0;
          253                 uh4->frag[1] = 0;
          254                 hnputs(uh4->udpplen, ptcllen);
          255                 if(ucb->headers) {
          256                         v6tov4(uh4->udpdst, raddr);
          257                         hnputs(uh4->udpdport, rport);
          258                         v6tov4(uh4->udpsrc, laddr);
          259                         rc = nil;
          260                 } else {
          261                         v6tov4(uh4->udpdst, c->raddr);
          262                         hnputs(uh4->udpdport, c->rport);
          263                         if(ipcmp(c->laddr, IPnoaddr) == 0)
          264                                 findlocalip(f, c->laddr, c->raddr);
          265                         v6tov4(uh4->udpsrc, c->laddr);
          266                         rc = c;
          267                 }
          268                 hnputs(uh4->udpsport, c->lport);
          269                 hnputs(uh4->udplen, ptcllen);
          270                 uh4->udpcksum[0] = 0;
          271                 uh4->udpcksum[1] = 0;
          272                 hnputs(uh4->udpcksum,
          273                        ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ));
          274                 uh4->vihl = IP_VER4;
          275                 ipoput4(f, bp, 0, c->ttl, c->tos, rc);
          276                 break;
          277 
          278         case V6:
          279                 bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ);
          280                 if(bp == nil)
          281                         return;
          282 
          283                 /*
          284                  * using the v6 ip header to create pseudo header
          285                  * first then reset it to the normal ip header
          286                  */
          287                 uh6 = (Udp6hdr *)(bp->rp);
          288                 memset(uh6, 0, 8);
          289                 ptcllen = dlen + UDP_UDPHDR_SZ;
          290                 hnputl(uh6->viclfl, ptcllen);
          291                 uh6->hoplimit = IP_UDPPROTO;
          292                 if(ucb->headers) {
          293                         ipmove(uh6->udpdst, raddr);
          294                         hnputs(uh6->udpdport, rport);
          295                         ipmove(uh6->udpsrc, laddr);
          296                         rc = nil;
          297                 } else {
          298                         ipmove(uh6->udpdst, c->raddr);
          299                         hnputs(uh6->udpdport, c->rport);
          300                         if(ipcmp(c->laddr, IPnoaddr) == 0)
          301                                 findlocalip(f, c->laddr, c->raddr);
          302                         ipmove(uh6->udpsrc, c->laddr);
          303                         rc = c;
          304                 }
          305                 hnputs(uh6->udpsport, c->lport);
          306                 hnputs(uh6->udplen, ptcllen);
          307                 uh6->udpcksum[0] = 0;
          308                 uh6->udpcksum[1] = 0;
          309                 hnputs(uh6->udpcksum,
          310                        ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ));
          311                 memset(uh6, 0, 8);
          312                 uh6->viclfl[0] = IP_VER6;
          313                 hnputs(uh6->len, ptcllen);
          314                 uh6->nextheader = IP_UDPPROTO;
          315                 ipoput6(f, bp, 0, c->ttl, c->tos, rc);
          316                 break;
          317 
          318         default:
          319                 panic("udpkick: version %d", version);
          320         }
          321         upriv->ustats.udpOutDatagrams++;
          322 }
          323 
          324 void
          325 udpiput(Proto *udp, Ipifc *ifc, Block *bp)
          326 {
          327         int len;
          328         Udp4hdr *uh4;
          329         Udp6hdr *uh6;
          330         Conv *c;
          331         Udpcb *ucb;
          332         uchar raddr[IPaddrlen], laddr[IPaddrlen];
          333         ushort rport, lport;
          334         Udppriv *upriv;
          335         Fs *f;
          336         int version;
          337         int ottl, oviclfl, olen;
          338         uchar *p;
          339 
          340         upriv = udp->priv;
          341         f = udp->f;
          342         upriv->ustats.udpInDatagrams++;
          343 
          344         uh4 = (Udp4hdr*)(bp->rp);
          345         version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4;
          346 
          347         /* Put back pseudo header for checksum
          348          * (remember old values for icmpnoconv()) */
          349         switch(version) {
          350         case V4:
          351                 ottl = uh4->Unused;
          352                 uh4->Unused = 0;
          353                 len = nhgets(uh4->udplen);
          354                 olen = nhgets(uh4->udpplen);
          355                 hnputs(uh4->udpplen, len);
          356 
          357                 v4tov6(raddr, uh4->udpsrc);
          358                 v4tov6(laddr, uh4->udpdst);
          359                 lport = nhgets(uh4->udpdport);
          360                 rport = nhgets(uh4->udpsport);
          361 
          362                 if(nhgets(uh4->udpcksum)) {
          363                         if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) {
          364                                 upriv->ustats.udpInErrors++;
          365                                 netlog(f, Logudp, "udp: checksum error %I\n", raddr);
          366                                 DPRINT("udp: checksum error %I\n", raddr);
          367                                 freeblist(bp);
          368                                 return;
          369                         }
          370                 }
          371                 uh4->Unused = ottl;
          372                 hnputs(uh4->udpplen, olen);
          373                 break;
          374         case V6:
          375                 uh6 = (Udp6hdr*)(bp->rp);
          376                 len = nhgets(uh6->udplen);
          377                 oviclfl = nhgetl(uh6->viclfl);
          378                 olen = nhgets(uh6->len);
          379                 ottl = uh6->hoplimit;
          380                 ipmove(raddr, uh6->udpsrc);
          381                 ipmove(laddr, uh6->udpdst);
          382                 lport = nhgets(uh6->udpdport);
          383                 rport = nhgets(uh6->udpsport);
          384                 memset(uh6, 0, 8);
          385                 hnputl(uh6->viclfl, len);
          386                 uh6->hoplimit = IP_UDPPROTO;
          387                 if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) {
          388                         upriv->ustats.udpInErrors++;
          389                         netlog(f, Logudp, "udp: checksum error %I\n", raddr);
          390                         DPRINT("udp: checksum error %I\n", raddr);
          391                         freeblist(bp);
          392                         return;
          393                 }
          394                 hnputl(uh6->viclfl, oviclfl);
          395                 hnputs(uh6->len, olen);
          396                 uh6->nextheader = IP_UDPPROTO;
          397                 uh6->hoplimit = ottl;
          398                 break;
          399         default:
          400                 panic("udpiput: version %d", version);
          401                 return;        /* to avoid a warning */
          402         }
          403 
          404         QLOCK(udp);
          405 
          406         c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
          407         if(c == nil){
          408                 /* no conversation found */
          409                 upriv->ustats.udpNoPorts++;
          410                 QUNLOCK(udp);
          411                 netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
          412                        laddr, lport);
          413 
          414                 switch(version){
          415                 case V4:
          416                         icmpnoconv(f, bp);
          417                         break;
          418                 case V6:
          419                         icmphostunr(f, ifc, bp, Icmp6_port_unreach, 0);
          420                         break;
          421                 default:
          422                         panic("udpiput2: version %d", version);
          423                 }
          424 
          425                 freeblist(bp);
          426                 return;
          427         }
          428         ucb = (Udpcb*)c->ptcl;
          429 
          430         if(c->state == Announced){
          431                 if(ucb->headers == 0){
          432                         /* create a new conversation */
          433                         if(ipforme(f, laddr) != Runi) {
          434                                 switch(version){
          435                                 case V4:
          436                                         v4tov6(laddr, ifc->lifc->local);
          437                                         break;
          438                                 case V6:
          439                                         ipmove(laddr, ifc->lifc->local);
          440                                         break;
          441                                 default:
          442                                         panic("udpiput3: version %d", version);
          443                                 }
          444                         }
          445                         c = Fsnewcall(c, raddr, rport, laddr, lport, version);
          446                         if(c == nil){
          447                                 QUNLOCK(udp);
          448                                 freeblist(bp);
          449                                 return;
          450                         }
          451                         iphtadd(&upriv->ht, c);
          452                         ucb = (Udpcb*)c->ptcl;
          453                 }
          454         }
          455 
          456         QLOCK(c);
          457         QUNLOCK(udp);
          458 
          459         /*
          460          * Trim the packet down to data size
          461          */
          462         len -= UDP_UDPHDR_SZ;
          463         switch(version){
          464         case V4:
          465                 bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len);
          466                 break;
          467         case V6:
          468                 bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len);
          469                 break;
          470         default:
          471                 bp = nil;
          472                 panic("udpiput4: version %d", version);
          473         }
          474         if(bp == nil){
          475                 QUNLOCK(c);
          476                 netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
          477                        laddr, lport);
          478                 upriv->lenerr++;
          479                 return;
          480         }
          481 
          482         netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
          483                laddr, lport, len);
          484 
          485         switch(ucb->headers){
          486         case 7:
          487                 /* pass the src address */
          488                 bp = padblock(bp, UDP_USEAD7);
          489                 p = bp->rp;
          490                 ipmove(p, raddr); p += IPaddrlen;
          491                 ipmove(p, laddr); p += IPaddrlen;
          492                 ipmove(p, ifc->lifc->local); p += IPaddrlen;
          493                 hnputs(p, rport); p += 2;
          494                 hnputs(p, lport);
          495                 break;
          496         }
          497 
          498         if(bp->next)
          499                 bp = concatblock(bp);
          500 
          501         if(qfull(c->rq)){
          502                 QUNLOCK(c);
          503                 netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
          504                        laddr, lport);
          505                 freeblist(bp);
          506                 return;
          507         }
          508 
          509         qpass(c->rq, bp);
          510         QUNLOCK(c);
          511 
          512 }
          513 
          514 char*
          515 udpctl(Conv *c, char **f, int n)
          516 {
          517         Udpcb *ucb;
          518 
          519         ucb = (Udpcb*)c->ptcl;
          520         if(n == 1){
          521                 if(strcmp(f[0], "headers") == 0){
          522                         ucb->headers = 7;        /* new headers format */
          523                         return nil;
          524                 }
          525         }
          526         return "unknown control request";
          527 }
          528 
          529 void
          530 udpadvise(Proto *udp, Block *bp, char *msg)
          531 {
          532         Udp4hdr *h4;
          533         Udp6hdr *h6;
          534         uchar source[IPaddrlen], dest[IPaddrlen];
          535         ushort psource, pdest;
          536         Conv *s, **p;
          537         int version;
          538 
          539         h4 = (Udp4hdr*)(bp->rp);
          540         version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4;
          541 
          542         switch(version) {
          543         case V4:
          544                 v4tov6(dest, h4->udpdst);
          545                 v4tov6(source, h4->udpsrc);
          546                 psource = nhgets(h4->udpsport);
          547                 pdest = nhgets(h4->udpdport);
          548                 break;
          549         case V6:
          550                 h6 = (Udp6hdr*)(bp->rp);
          551                 ipmove(dest, h6->udpdst);
          552                 ipmove(source, h6->udpsrc);
          553                 psource = nhgets(h6->udpsport);
          554                 pdest = nhgets(h6->udpdport);
          555                 break;
          556         default:
          557                 panic("udpadvise: version %d", version);
          558                 return;  /* to avoid a warning */
          559         }
          560 
          561         /* Look for a connection */
          562         QLOCK(udp);
          563         for(p = udp->conv; *p; p++) {
          564                 s = *p;
          565                 if(s->rport == pdest)
          566                 if(s->lport == psource)
          567                 if(ipcmp(s->raddr, dest) == 0)
          568                 if(ipcmp(s->laddr, source) == 0){
          569                         if(s->ignoreadvice)
          570                                 break;
          571                         QLOCK(s);
          572                         QUNLOCK(udp);
          573                         qhangup(s->rq, msg);
          574                         qhangup(s->wq, msg);
          575                         QUNLOCK(s);
          576                         freeblist(bp);
          577                         return;
          578                 }
          579         }
          580         QUNLOCK(udp);
          581         freeblist(bp);
          582 }
          583 
          584 int
          585 udpstats(Proto *udp, char *buf, int len)
          586 {
          587         Udppriv *upriv;
          588 
          589         upriv = udp->priv;
          590         return snprint(buf, len, "InDatagrams: %lud\nNoPorts: %lud\nInErrors: %lud\nOutDatagrams: %lud\n",
          591                 upriv->ustats.udpInDatagrams,
          592                 upriv->ustats.udpNoPorts,
          593                 upriv->ustats.udpInErrors,
          594                 upriv->ustats.udpOutDatagrams);
          595 }
          596 
          597 void
          598 udpinit(Fs *fs)
          599 {
          600         Proto *udp;
          601 
          602         udp = smalloc(sizeof(Proto));
          603         udp->priv = smalloc(sizeof(Udppriv));
          604         udp->name = "udp";
          605         udp->connect = udpconnect;
          606         udp->announce = udpannounce;
          607         udp->ctl = udpctl;
          608         udp->state = udpstate;
          609         udp->create = udpcreate;
          610         udp->close = udpclose;
          611         udp->rcv = udpiput;
          612         udp->advise = udpadvise;
          613         udp->stats = udpstats;
          614         udp->ipproto = IP_UDPPROTO;
          615         udp->nc = Nchans;
          616         udp->ptclsize = sizeof(Udpcb);
          617 
          618         Fsproto(fs, udp);
          619 }