xref: /plan9/sys/src/9/ip/udp.c (revision 5e27dea93c37a6944933876218b2bfb60ef34178)
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*
udpconnect(Conv * c,char ** argv,int argc)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
udpstate(Conv * c,char * state,int n)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*
udpannounce(Conv * c,char ** argv,int argc)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
udpcreate(Conv * c)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
udpclose(Conv * c)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
udpkick(void * x,Block * bp)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
udpiput(Proto * udp,Ipifc * ifc,Block * bp)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*
udpctl(Conv * c,char ** f,int n)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
udpadvise(Proto * udp,Block * bp,char * msg)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
udpstats(Proto * udp,char * buf,int len)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
udpinit(Fs * fs)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 }
621