FreeBSD/Linux Kernel Cross Reference
sys/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 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;
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 }
Cache object: 6d431107ab920c667379200a8223caff
|