]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/udp.c
gre: don't drop pptp packets when smaller than v4 header
[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(512*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                 uh4 = (Udp4hdr *)(bp->rp);
246                 ptcllen = dlen + UDP_UDPHDR_SZ;
247                 uh4->Unused = 0;
248                 uh4->udpproto = IP_UDPPROTO;
249                 uh4->frag[0] = 0;
250                 uh4->frag[1] = 0;
251                 hnputs(uh4->udpplen, ptcllen);
252                 if(ucb->headers) {
253                         v6tov4(uh4->udpdst, raddr);
254                         hnputs(uh4->udpdport, rport);
255                         v6tov4(uh4->udpsrc, laddr);
256                         rc = nil;
257                 } else {
258                         v6tov4(uh4->udpdst, c->raddr);
259                         hnputs(uh4->udpdport, c->rport);
260                         if(ipcmp(c->laddr, IPnoaddr) == 0)
261                                 findlocalip(f, c->laddr, c->raddr);
262                         v6tov4(uh4->udpsrc, c->laddr);
263                         rc = c;
264                 }
265                 hnputs(uh4->udpsport, c->lport);
266                 hnputs(uh4->udplen, ptcllen);
267                 uh4->udpcksum[0] = 0;
268                 uh4->udpcksum[1] = 0;
269                 hnputs(uh4->udpcksum,
270                        ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ));
271                 uh4->vihl = IP_VER4;
272                 ipoput4(f, bp, 0, c->ttl, c->tos, rc);
273                 break;
274
275         case V6:
276                 /*
277                  * using the v6 ip header to create pseudo header
278                  * first then reset it to the normal ip header
279                  */
280                 bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ);
281                 uh6 = (Udp6hdr *)(bp->rp);
282                 memset(uh6, 0, 8);
283                 ptcllen = dlen + UDP_UDPHDR_SZ;
284                 hnputl(uh6->viclfl, ptcllen);
285                 uh6->hoplimit = IP_UDPPROTO;
286                 if(ucb->headers) {
287                         ipmove(uh6->udpdst, raddr);
288                         hnputs(uh6->udpdport, rport);
289                         ipmove(uh6->udpsrc, laddr);
290                         rc = nil;
291                 } else {
292                         ipmove(uh6->udpdst, c->raddr);
293                         hnputs(uh6->udpdport, c->rport);
294                         if(ipcmp(c->laddr, IPnoaddr) == 0)
295                                 findlocalip(f, c->laddr, c->raddr);
296                         ipmove(uh6->udpsrc, c->laddr);
297                         rc = c;
298                 }
299                 hnputs(uh6->udpsport, c->lport);
300                 hnputs(uh6->udplen, ptcllen);
301                 uh6->udpcksum[0] = 0;
302                 uh6->udpcksum[1] = 0;
303                 hnputs(uh6->udpcksum,
304                        ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ));
305                 memset(uh6, 0, 8);
306                 uh6->viclfl[0] = IP_VER6;
307                 hnputs(uh6->len, ptcllen);
308                 uh6->nextheader = IP_UDPPROTO;
309                 ipoput6(f, bp, 0, c->ttl, c->tos, rc);
310                 break;
311
312         default:
313                 panic("udpkick: version %d", version);
314         }
315         upriv->ustats.udpOutDatagrams++;
316 }
317
318 void
319 udpiput(Proto *udp, Ipifc *ifc, Block *bp)
320 {
321         int len;
322         Udp4hdr *uh4;
323         Udp6hdr *uh6;
324         Conv *c;
325         Udpcb *ucb;
326         uchar raddr[IPaddrlen], laddr[IPaddrlen];
327         ushort rport, lport;
328         Udppriv *upriv;
329         Fs *f;
330         int version;
331         int ottl, oviclfl, olen;
332         uchar *p;
333
334         upriv = udp->priv;
335         f = udp->f;
336         upriv->ustats.udpInDatagrams++;
337
338         uh4 = (Udp4hdr*)(bp->rp);
339         version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4;
340
341         /* Put back pseudo header for checksum
342          * (remember old values for icmpnoconv()) */
343         switch(version) {
344         case V4:
345                 ottl = uh4->Unused;
346                 uh4->Unused = 0;
347                 len = nhgets(uh4->udplen);
348                 olen = nhgets(uh4->udpplen);
349                 hnputs(uh4->udpplen, len);
350
351                 v4tov6(raddr, uh4->udpsrc);
352                 v4tov6(laddr, uh4->udpdst);
353                 lport = nhgets(uh4->udpdport);
354                 rport = nhgets(uh4->udpsport);
355
356                 if(nhgets(uh4->udpcksum)) {
357                         if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) {
358                                 upriv->ustats.udpInErrors++;
359                                 netlog(f, Logudp, "udp: checksum error %I\n", raddr);
360                                 DPRINT("udp: checksum error %I\n", raddr);
361                                 freeblist(bp);
362                                 return;
363                         }
364                 }
365                 uh4->Unused = ottl;
366                 hnputs(uh4->udpplen, olen);
367                 break;
368         case V6:
369                 uh6 = (Udp6hdr*)(bp->rp);
370                 len = nhgets(uh6->udplen);
371                 oviclfl = nhgetl(uh6->viclfl);
372                 olen = nhgets(uh6->len);
373                 ottl = uh6->hoplimit;
374                 ipmove(raddr, uh6->udpsrc);
375                 ipmove(laddr, uh6->udpdst);
376                 lport = nhgets(uh6->udpdport);
377                 rport = nhgets(uh6->udpsport);
378                 memset(uh6, 0, 8);
379                 hnputl(uh6->viclfl, len);
380                 uh6->hoplimit = IP_UDPPROTO;
381                 if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_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                 hnputl(uh6->viclfl, oviclfl);
389                 hnputs(uh6->len, olen);
390                 uh6->nextheader = IP_UDPPROTO;
391                 uh6->hoplimit = ottl;
392                 break;
393         default:
394                 panic("udpiput: version %d", version);
395                 return; /* to avoid a warning */
396         }
397
398         qlock(udp);
399
400         c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
401         if(c == nil){
402                 /* no conversation found */
403                 upriv->ustats.udpNoPorts++;
404                 qunlock(udp);
405                 netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
406                        laddr, lport);
407
408                 switch(version){
409                 case V4:
410                         icmpnoconv(f, bp);
411                         break;
412                 case V6:
413                         icmphostunr(f, ifc, bp, Icmp6_port_unreach, 0);
414                         break;
415                 default:
416                         panic("udpiput2: version %d", version);
417                 }
418
419                 freeblist(bp);
420                 return;
421         }
422         ucb = (Udpcb*)c->ptcl;
423
424         if(c->state == Announced){
425                 if(ucb->headers == 0){
426                         /* create a new conversation */
427                         if(ipforme(f, laddr) != Runi) {
428                                 switch(version){
429                                 case V4:
430                                         v4tov6(laddr, ifc->lifc->local);
431                                         break;
432                                 case V6:
433                                         ipmove(laddr, ifc->lifc->local);
434                                         break;
435                                 default:
436                                         panic("udpiput3: version %d", version);
437                                 }
438                         }
439                         c = Fsnewcall(c, raddr, rport, laddr, lport, version);
440                         if(c == nil){
441                                 qunlock(udp);
442                                 freeblist(bp);
443                                 return;
444                         }
445                         iphtadd(&upriv->ht, c);
446                         ucb = (Udpcb*)c->ptcl;
447                 }
448         }
449
450         qlock(c);
451         qunlock(udp);
452
453         /*
454          * Trim the packet down to data size
455          */
456         len -= UDP_UDPHDR_SZ;
457         switch(version){
458         case V4:
459                 bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len);
460                 break;
461         case V6:
462                 bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len);
463                 break;
464         default:
465                 bp = nil;
466                 panic("udpiput4: version %d", version);
467         }
468         if(bp == nil){
469                 qunlock(c);
470                 netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
471                        laddr, lport);
472                 upriv->lenerr++;
473                 return;
474         }
475
476         netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
477                laddr, lport, len);
478
479         switch(ucb->headers){
480         case 7:
481                 /* pass the src address */
482                 bp = padblock(bp, UDP_USEAD7);
483                 p = bp->rp;
484                 ipmove(p, raddr); p += IPaddrlen;
485                 ipmove(p, laddr); p += IPaddrlen;
486                 ipmove(p, ifc->lifc->local); p += IPaddrlen;
487                 hnputs(p, rport); p += 2;
488                 hnputs(p, lport);
489                 break;
490         }
491
492         if(qfull(c->rq)){
493                 netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
494                        laddr, lport);
495                 freeblist(bp);
496         } else {
497                 if(bp->next)
498                         bp = concatblock(bp);
499                 qpass(c->rq, bp);
500         }
501         qunlock(c);
502
503 }
504
505 char*
506 udpctl(Conv *c, char **f, int n)
507 {
508         Udpcb *ucb;
509
510         ucb = (Udpcb*)c->ptcl;
511         if(n == 1){
512                 if(strcmp(f[0], "hangup") == 0){
513                         qhangup(c->rq, nil);
514                         qhangup(c->wq, nil);
515                         return nil;
516                 }
517                 if(strcmp(f[0], "headers") == 0){
518                         ucb->headers = 7;       /* new headers format */
519                         return nil;
520                 }
521         }
522         return "unknown control request";
523 }
524
525 void
526 udpadvise(Proto *udp, Block *bp, char *msg)
527 {
528         Udp4hdr *h4;
529         Udp6hdr *h6;
530         uchar source[IPaddrlen], dest[IPaddrlen];
531         ushort psource, pdest;
532         Conv *s, **p;
533         int version;
534
535         h4 = (Udp4hdr*)(bp->rp);
536         version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4;
537
538         switch(version) {
539         case V4:
540                 v4tov6(dest, h4->udpdst);
541                 v4tov6(source, h4->udpsrc);
542                 psource = nhgets(h4->udpsport);
543                 pdest = nhgets(h4->udpdport);
544                 break;
545         case V6:
546                 h6 = (Udp6hdr*)(bp->rp);
547                 ipmove(dest, h6->udpdst);
548                 ipmove(source, h6->udpsrc);
549                 psource = nhgets(h6->udpsport);
550                 pdest = nhgets(h6->udpdport);
551                 break;
552         default:
553                 panic("udpadvise: version %d", version);
554                 return;  /* to avoid a warning */
555         }
556
557         /* Look for a connection */
558         qlock(udp);
559         for(p = udp->conv; *p; p++) {
560                 s = *p;
561                 if(s->rport == pdest)
562                 if(s->lport == psource)
563                 if(ipcmp(s->raddr, dest) == 0)
564                 if(ipcmp(s->laddr, source) == 0){
565                         if(s->ignoreadvice)
566                                 break;
567                         qlock(s);
568                         qunlock(udp);
569                         qhangup(s->rq, msg);
570                         qhangup(s->wq, msg);
571                         qunlock(s);
572                         freeblist(bp);
573                         return;
574                 }
575         }
576         qunlock(udp);
577         freeblist(bp);
578 }
579
580 int
581 udpstats(Proto *udp, char *buf, int len)
582 {
583         Udppriv *upriv;
584
585         upriv = udp->priv;
586         return snprint(buf, len, "InDatagrams: %llud\nNoPorts: %lud\n"
587                 "InErrors: %lud\nOutDatagrams: %llud\n",
588                 upriv->ustats.udpInDatagrams,
589                 upriv->ustats.udpNoPorts,
590                 upriv->ustats.udpInErrors,
591                 upriv->ustats.udpOutDatagrams);
592 }
593
594 void
595 udpinit(Fs *fs)
596 {
597         Proto *udp;
598
599         udp = smalloc(sizeof(Proto));
600         udp->priv = smalloc(sizeof(Udppriv));
601         udp->name = "udp";
602         udp->connect = udpconnect;
603         udp->announce = udpannounce;
604         udp->ctl = udpctl;
605         udp->state = udpstate;
606         udp->create = udpcreate;
607         udp->close = udpclose;
608         udp->rcv = udpiput;
609         udp->advise = udpadvise;
610         udp->stats = udpstats;
611         udp->ipproto = IP_UDPPROTO;
612         udp->nc = Nchans;
613         udp->ptclsize = sizeof(Udpcb);
614
615         Fsproto(fs, udp);
616 }