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