vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

udp.c (12404B)


      1 #include	"u.h"
      2 #include	"lib.h"
      3 #include	"mem.h"
      4 #include	"dat.h"
      5 #include	"fns.h"
      6 #include	"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	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 }