]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/udp.c
merge
[plan9front.git] / sys / src / 9 / ip / udp.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/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         uvlong  udpInDatagrams;
76         ulong   udpNoPorts;
77         ulong   udpInErrors;
78         uvlong  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;
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"); /* frequent and uninteresting */
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: %llud\nNoPorts: %lud\n"
591                 "InErrors: %lud\nOutDatagrams: %llud\n",
592                 upriv->ustats.udpInDatagrams,
593                 upriv->ustats.udpNoPorts,
594                 upriv->ustats.udpInErrors,
595                 upriv->ustats.udpOutDatagrams);
596 }
597
598 void
599 udpinit(Fs *fs)
600 {
601         Proto *udp;
602
603         udp = smalloc(sizeof(Proto));
604         udp->priv = smalloc(sizeof(Udppriv));
605         udp->name = "udp";
606         udp->connect = udpconnect;
607         udp->announce = udpannounce;
608         udp->ctl = udpctl;
609         udp->state = udpstate;
610         udp->create = udpcreate;
611         udp->close = udpclose;
612         udp->rcv = udpiput;
613         udp->advise = udpadvise;
614         udp->stats = udpstats;
615         udp->ipproto = IP_UDPPROTO;
616         udp->nc = Nchans;
617         udp->ptclsize = sizeof(Udpcb);
618
619         Fsproto(fs, udp);
620 }