xref: /inferno-os/os/ip/udp.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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 }
650