xref: /plan9/sys/src/9/ip/udp.c (revision e6c6b7f8757fefe4f788e3998e8ae256bca21dc3)
17dd7cddfSDavid du Colombier #include	"u.h"
27dd7cddfSDavid du Colombier #include	"../port/lib.h"
37dd7cddfSDavid du Colombier #include	"mem.h"
47dd7cddfSDavid du Colombier #include	"dat.h"
57dd7cddfSDavid du Colombier #include	"fns.h"
67dd7cddfSDavid du Colombier #include	"../port/error.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier #include	"ip.h"
93ff48bf5SDavid du Colombier #include	"ipv6.h"
103ff48bf5SDavid du Colombier 
117dd7cddfSDavid du Colombier 
127dd7cddfSDavid du Colombier #define DPRINT if(0)print
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier enum
157dd7cddfSDavid du Colombier {
163ff48bf5SDavid du Colombier 	UDP_UDPHDR_SZ	= 8,
173ff48bf5SDavid du Colombier 
183ff48bf5SDavid du Colombier 	UDP4_PHDR_OFF = 8,
193ff48bf5SDavid du Colombier 	UDP4_PHDR_SZ = 12,
203ff48bf5SDavid du Colombier 	UDP4_IPHDR_SZ = 20,
213ff48bf5SDavid du Colombier 	UDP6_IPHDR_SZ = 40,
223ff48bf5SDavid du Colombier 	UDP6_PHDR_SZ = 40,
233ff48bf5SDavid du Colombier 	UDP6_PHDR_OFF = 0,
243ff48bf5SDavid du Colombier 
257dd7cddfSDavid du Colombier 	IP_UDPPROTO	= 17,
267dd7cddfSDavid du Colombier 	UDP_USEAD6	= 36,
277dd7cddfSDavid du Colombier 	UDP_USEAD4	= 12,
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	Udprxms		= 200,
307dd7cddfSDavid du Colombier 	Udptickms	= 100,
317dd7cddfSDavid du Colombier 	Udpmaxxmit	= 10,
327dd7cddfSDavid du Colombier };
337dd7cddfSDavid du Colombier 
343ff48bf5SDavid du Colombier typedef struct Udp4hdr Udp4hdr;
353ff48bf5SDavid du Colombier struct Udp4hdr
367dd7cddfSDavid du Colombier {
377dd7cddfSDavid du Colombier 	/* ip header */
387dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
397dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
407dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
417dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
427dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
437dd7cddfSDavid du Colombier 	uchar	Unused;
447dd7cddfSDavid du Colombier 	uchar	udpproto;	/* Protocol */
457dd7cddfSDavid du Colombier 	uchar	udpplen[2];	/* Header plus data length */
463ff48bf5SDavid du Colombier 	uchar	udpsrc[IPv4addrlen];	/* Ip source */
473ff48bf5SDavid du Colombier 	uchar	udpdst[IPv4addrlen];	/* Ip destination */
483ff48bf5SDavid du Colombier 
493ff48bf5SDavid du Colombier 	/* udp header */
503ff48bf5SDavid du Colombier 	uchar	udpsport[2];	/* Source port */
513ff48bf5SDavid du Colombier 	uchar	udpdport[2];	/* Destination port */
523ff48bf5SDavid du Colombier 	uchar	udplen[2];	/* data length */
533ff48bf5SDavid du Colombier 	uchar	udpcksum[2];	/* Checksum */
543ff48bf5SDavid du Colombier };
553ff48bf5SDavid du Colombier 
563ff48bf5SDavid du Colombier typedef struct Udp6hdr Udp6hdr;
573ff48bf5SDavid du Colombier struct Udp6hdr {
583ff48bf5SDavid du Colombier 	uchar viclfl[4];
593ff48bf5SDavid du Colombier 	uchar len[2];
603ff48bf5SDavid du Colombier 	uchar nextheader;
613ff48bf5SDavid du Colombier 	uchar hoplimit;
623ff48bf5SDavid du Colombier 	uchar udpsrc[IPaddrlen];
633ff48bf5SDavid du Colombier 	uchar udpdst[IPaddrlen];
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier 	/* udp header */
667dd7cddfSDavid du Colombier 	uchar	udpsport[2];	/* Source port */
677dd7cddfSDavid du Colombier 	uchar	udpdport[2];	/* Destination port */
687dd7cddfSDavid du Colombier 	uchar	udplen[2];	/* data length */
697dd7cddfSDavid du Colombier 	uchar	udpcksum[2];	/* Checksum */
707dd7cddfSDavid du Colombier };
717dd7cddfSDavid du Colombier 
7259cc4ca5SDavid du Colombier /* MIB II counters */
7380ee5cbfSDavid du Colombier typedef struct Udpstats Udpstats;
7480ee5cbfSDavid du Colombier struct Udpstats
7580ee5cbfSDavid du Colombier {
7680ee5cbfSDavid du Colombier 	ulong	udpInDatagrams;
7780ee5cbfSDavid du Colombier 	ulong	udpNoPorts;
7880ee5cbfSDavid du Colombier 	ulong	udpInErrors;
7980ee5cbfSDavid du Colombier 	ulong	udpOutDatagrams;
807dd7cddfSDavid du Colombier };
817dd7cddfSDavid du Colombier 
827dd7cddfSDavid du Colombier typedef struct Udppriv Udppriv;
837dd7cddfSDavid du Colombier struct Udppriv
847dd7cddfSDavid du Colombier {
8580ee5cbfSDavid du Colombier 	Ipht		ht;
8680ee5cbfSDavid du Colombier 
8780ee5cbfSDavid du Colombier 	/* MIB counters */
8880ee5cbfSDavid du Colombier 	Udpstats	ustats;
8980ee5cbfSDavid du Colombier 
9080ee5cbfSDavid du Colombier 	/* non-MIB stats */
9180ee5cbfSDavid du Colombier 	ulong		csumerr;		/* checksum errors */
9280ee5cbfSDavid du Colombier 	ulong		lenerr;			/* short packet */
937dd7cddfSDavid du Colombier };
947dd7cddfSDavid du Colombier 
953ff48bf5SDavid du Colombier void (*etherprofiler)(char *name, int qlen);
96*e6c6b7f8SDavid du Colombier void udpkick(void *x, Block *bp);
973ff48bf5SDavid du Colombier 
987dd7cddfSDavid du Colombier /*
997dd7cddfSDavid du Colombier  *  protocol specific part of Conv
1007dd7cddfSDavid du Colombier  */
1017dd7cddfSDavid du Colombier typedef struct Udpcb Udpcb;
1027dd7cddfSDavid du Colombier struct Udpcb
1037dd7cddfSDavid du Colombier {
1047dd7cddfSDavid du Colombier 	QLock;
1057dd7cddfSDavid du Colombier 	uchar	headers;
1067dd7cddfSDavid du Colombier };
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier static char*
1097dd7cddfSDavid du Colombier udpconnect(Conv *c, char **argv, int argc)
1107dd7cddfSDavid du Colombier {
1117dd7cddfSDavid du Colombier 	char *e;
11280ee5cbfSDavid du Colombier 	Udppriv *upriv;
1137dd7cddfSDavid du Colombier 
11480ee5cbfSDavid du Colombier 	upriv = c->p->priv;
1157dd7cddfSDavid du Colombier 	e = Fsstdconnect(c, argv, argc);
1167dd7cddfSDavid du Colombier 	Fsconnected(c, e);
1173ff48bf5SDavid du Colombier 	if(e != nil)
1187dd7cddfSDavid du Colombier 		return e;
1193ff48bf5SDavid du Colombier 
1203ff48bf5SDavid du Colombier 	iphtadd(&upriv->ht, c);
1213ff48bf5SDavid du Colombier 	return nil;
1227dd7cddfSDavid du Colombier }
1237dd7cddfSDavid du Colombier 
1247dd7cddfSDavid du Colombier 
1257dd7cddfSDavid du Colombier static int
1267dd7cddfSDavid du Colombier udpstate(Conv *c, char *state, int n)
1277dd7cddfSDavid du Colombier {
1287dd7cddfSDavid du Colombier 	return snprint(state, n, "%s", c->inuse?"Open":"Closed");
1297dd7cddfSDavid du Colombier }
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier static char*
1327dd7cddfSDavid du Colombier udpannounce(Conv *c, char** argv, int argc)
1337dd7cddfSDavid du Colombier {
1347dd7cddfSDavid du Colombier 	char *e;
13580ee5cbfSDavid du Colombier 	Udppriv *upriv;
1367dd7cddfSDavid du Colombier 
13780ee5cbfSDavid du Colombier 	upriv = c->p->priv;
1387dd7cddfSDavid du Colombier 	e = Fsstdannounce(c, argv, argc);
1397dd7cddfSDavid du Colombier 	if(e != nil)
1407dd7cddfSDavid du Colombier 		return e;
1417dd7cddfSDavid du Colombier 	Fsconnected(c, nil);
14280ee5cbfSDavid du Colombier 	iphtadd(&upriv->ht, c);
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	return nil;
1457dd7cddfSDavid du Colombier }
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier static void
1487dd7cddfSDavid du Colombier udpcreate(Conv *c)
1497dd7cddfSDavid du Colombier {
1503ff48bf5SDavid du Colombier 	c->rq = qopen(128*1024, Qmsg, 0, 0);
151*e6c6b7f8SDavid du Colombier 	c->wq = qbypass(udpkick, c);
1527dd7cddfSDavid du Colombier }
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier static void
1557dd7cddfSDavid du Colombier udpclose(Conv *c)
1567dd7cddfSDavid du Colombier {
1577dd7cddfSDavid du Colombier 	Udpcb *ucb;
15880ee5cbfSDavid du Colombier 	Udppriv *upriv;
15980ee5cbfSDavid du Colombier 
16080ee5cbfSDavid du Colombier 	upriv = c->p->priv;
16180ee5cbfSDavid du Colombier 	iphtrem(&upriv->ht, c);
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	c->state = 0;
1647dd7cddfSDavid du Colombier 	qclose(c->rq);
1657dd7cddfSDavid du Colombier 	qclose(c->wq);
1667dd7cddfSDavid du Colombier 	qclose(c->eq);
1677dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
1687dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
1697dd7cddfSDavid du Colombier 	c->lport = 0;
1707dd7cddfSDavid du Colombier 	c->rport = 0;
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier 	ucb = (Udpcb*)c->ptcl;
1737dd7cddfSDavid du Colombier 	ucb->headers = 0;
1743ff48bf5SDavid du Colombier 
1753ff48bf5SDavid du Colombier 	qunlock(c);
1767dd7cddfSDavid du Colombier }
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier void
179*e6c6b7f8SDavid du Colombier udpkick(void *x, Block *bp)
1807dd7cddfSDavid du Colombier {
1813ff48bf5SDavid du Colombier 	Conv *c = x;
1823ff48bf5SDavid du Colombier 	Udp4hdr *uh4;
1833ff48bf5SDavid du Colombier 	Udp6hdr *uh6;
1847dd7cddfSDavid du Colombier 	ushort rport;
1857dd7cddfSDavid du Colombier 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
1867dd7cddfSDavid du Colombier 	Udpcb *ucb;
1877dd7cddfSDavid du Colombier 	int dlen, ptcllen;
1887dd7cddfSDavid du Colombier 	Udppriv *upriv;
1897dd7cddfSDavid du Colombier 	Fs *f;
1903ff48bf5SDavid du Colombier 	int version;
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier 	upriv = c->p->priv;
1937dd7cddfSDavid du Colombier 	f = c->p->f;
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	netlog(c->p->f, Logudp, "udp: kick\n");
1967dd7cddfSDavid du Colombier 	if(bp == nil)
1977dd7cddfSDavid du Colombier 		return;
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	ucb = (Udpcb*)c->ptcl;
2007dd7cddfSDavid du Colombier 	switch(ucb->headers) {
2017dd7cddfSDavid du Colombier 	case 6:
2027dd7cddfSDavid du Colombier 		/* get user specified addresses */
2037dd7cddfSDavid du Colombier 		bp = pullupblock(bp, UDP_USEAD6);
2047dd7cddfSDavid du Colombier 		if(bp == nil)
2057dd7cddfSDavid du Colombier 			return;
2067dd7cddfSDavid du Colombier 		ipmove(raddr, bp->rp);
2077dd7cddfSDavid du Colombier 		bp->rp += IPaddrlen;
2087dd7cddfSDavid du Colombier 		ipmove(laddr, bp->rp);
2097dd7cddfSDavid du Colombier 		bp->rp += IPaddrlen;
2107dd7cddfSDavid du Colombier 		/* pick interface closest to dest */
2117dd7cddfSDavid du Colombier 		if(ipforme(f, laddr) != Runi)
2127dd7cddfSDavid du Colombier 			findlocalip(f, laddr, raddr);
2137dd7cddfSDavid du Colombier 		rport = nhgets(bp->rp);
2143ff48bf5SDavid du Colombier 		bp->rp += 2+2;			/* Ignore local port */
2157dd7cddfSDavid du Colombier 		break;
2167dd7cddfSDavid du Colombier 	case 4:
2177dd7cddfSDavid du Colombier 		bp = pullupblock(bp, UDP_USEAD4);
2187dd7cddfSDavid du Colombier 		if(bp == nil)
2197dd7cddfSDavid du Colombier 			return;
2207dd7cddfSDavid du Colombier 		v4tov6(raddr, bp->rp);
2217dd7cddfSDavid du Colombier 		bp->rp += IPv4addrlen;
2227dd7cddfSDavid du Colombier 		v4tov6(laddr, bp->rp);
2237dd7cddfSDavid du Colombier 		bp->rp += IPv4addrlen;
2247dd7cddfSDavid du Colombier 		if(ipforme(f, laddr) != Runi)
2257dd7cddfSDavid du Colombier 			findlocalip(f, laddr, raddr);
2267dd7cddfSDavid du Colombier 		rport = nhgets(bp->rp);
2277dd7cddfSDavid du Colombier 		bp->rp += 2+2;
2287dd7cddfSDavid du Colombier 		break;
2297dd7cddfSDavid du Colombier 	default:
2307dd7cddfSDavid du Colombier 		rport = 0;
2317dd7cddfSDavid du Colombier 		break;
2327dd7cddfSDavid du Colombier 	}
2337dd7cddfSDavid du Colombier 
2343ff48bf5SDavid du Colombier 	if(ucb->headers == 6) {
2353ff48bf5SDavid du Colombier 		if(memcmp(laddr, v4prefix, IPv4off) == 0 ||
2363ff48bf5SDavid du Colombier 		    ipcmp(laddr, IPnoaddr) == 0)
2373ff48bf5SDavid du Colombier 			version = 4;
2383ff48bf5SDavid du Colombier 		else
2393ff48bf5SDavid du Colombier 			version = 6;
2403ff48bf5SDavid du Colombier 	}
2413ff48bf5SDavid du Colombier 	else if(ucb->headers == 4)
2423ff48bf5SDavid du Colombier 		version = 4;
2433ff48bf5SDavid du Colombier 	else {
2443ff48bf5SDavid du Colombier 		if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
2453ff48bf5SDavid du Colombier 			memcmp(c->laddr, v4prefix, IPv4off) == 0)
2463ff48bf5SDavid du Colombier 			|| ipcmp(c->raddr, IPnoaddr) == 0)
2473ff48bf5SDavid du Colombier 			version = 4;
2483ff48bf5SDavid du Colombier 		else
2493ff48bf5SDavid du Colombier 			version = 6;
2503ff48bf5SDavid du Colombier 	}
2513ff48bf5SDavid du Colombier 
2527dd7cddfSDavid du Colombier 	dlen = blocklen(bp);
2537dd7cddfSDavid du Colombier 
2543ff48bf5SDavid du Colombier 	/* fill in pseudo header and compute checksum */
2553ff48bf5SDavid du Colombier 	switch(version){
2563ff48bf5SDavid du Colombier 	case V4:
2573ff48bf5SDavid du Colombier 		bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ);
2587dd7cddfSDavid du Colombier 		if(bp == nil)
2597dd7cddfSDavid du Colombier 			return;
2607dd7cddfSDavid du Colombier 
2613ff48bf5SDavid du Colombier 		uh4 = (Udp4hdr *)(bp->rp);
2623ff48bf5SDavid du Colombier 		ptcllen = dlen + UDP_UDPHDR_SZ;
2633ff48bf5SDavid du Colombier 		uh4->Unused = 0;
2643ff48bf5SDavid du Colombier 		uh4->udpproto = IP_UDPPROTO;
2653ff48bf5SDavid du Colombier 		uh4->frag[0] = 0;
2663ff48bf5SDavid du Colombier 		uh4->frag[1] = 0;
2673ff48bf5SDavid du Colombier 		hnputs(uh4->udpplen, ptcllen);
2683ff48bf5SDavid du Colombier 		if(ucb->headers == 4 || ucb->headers == 6) {
2693ff48bf5SDavid du Colombier 			v6tov4(uh4->udpdst, raddr);
2703ff48bf5SDavid du Colombier 			hnputs(uh4->udpdport, rport);
2713ff48bf5SDavid du Colombier 			v6tov4(uh4->udpsrc, laddr);
2723ff48bf5SDavid du Colombier 		} else {
2733ff48bf5SDavid du Colombier 			v6tov4(uh4->udpdst, c->raddr);
2743ff48bf5SDavid du Colombier 			hnputs(uh4->udpdport, c->rport);
2757dd7cddfSDavid du Colombier 			if(ipcmp(c->laddr, IPnoaddr) == 0)
2767dd7cddfSDavid du Colombier 				findlocalip(f, c->laddr, c->raddr);
2773ff48bf5SDavid du Colombier 			v6tov4(uh4->udpsrc, c->laddr);
2783ff48bf5SDavid du Colombier 		}
2793ff48bf5SDavid du Colombier 		hnputs(uh4->udpsport, c->lport);
2803ff48bf5SDavid du Colombier 		hnputs(uh4->udplen, ptcllen);
2813ff48bf5SDavid du Colombier 		uh4->udpcksum[0] = 0;
2823ff48bf5SDavid du Colombier 		uh4->udpcksum[1] = 0;
2833ff48bf5SDavid du Colombier 		hnputs(uh4->udpcksum,
2843ff48bf5SDavid du Colombier 		       ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ));
2853ff48bf5SDavid du Colombier 		uh4->vihl = IP_VER4;
2863ff48bf5SDavid du Colombier 		ipoput4(f, bp, 0, c->ttl, c->tos);
2877dd7cddfSDavid du Colombier 		break;
2883ff48bf5SDavid du Colombier 
2893ff48bf5SDavid du Colombier 	case V6:
2903ff48bf5SDavid du Colombier 		bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ);
2913ff48bf5SDavid du Colombier 		if(bp == nil)
2923ff48bf5SDavid du Colombier 			return;
2933ff48bf5SDavid du Colombier 
2943ff48bf5SDavid du Colombier 		// using the v6 ip header to create pseudo header
2953ff48bf5SDavid du Colombier 		// first then reset it to the normal ip header
2963ff48bf5SDavid du Colombier 		uh6 = (Udp6hdr *)(bp->rp);
2973ff48bf5SDavid du Colombier 		memset(uh6, 0, 8);
2983ff48bf5SDavid du Colombier 		ptcllen = dlen + UDP_UDPHDR_SZ;
2993ff48bf5SDavid du Colombier 		hnputl(uh6->viclfl, ptcllen);
3003ff48bf5SDavid du Colombier 		uh6->hoplimit = IP_UDPPROTO;
3013ff48bf5SDavid du Colombier 		if(ucb->headers == 6) {
3023ff48bf5SDavid du Colombier 			ipmove(uh6->udpdst, raddr);
3033ff48bf5SDavid du Colombier 			hnputs(uh6->udpdport, rport);
3043ff48bf5SDavid du Colombier 			ipmove(uh6->udpsrc, laddr);
3053ff48bf5SDavid du Colombier 		} else {
3063ff48bf5SDavid du Colombier 			ipmove(uh6->udpdst, c->raddr);
3073ff48bf5SDavid du Colombier 			hnputs(uh6->udpdport, c->rport);
3083ff48bf5SDavid du Colombier 			if(ipcmp(c->laddr, IPnoaddr) == 0)
3093ff48bf5SDavid du Colombier 				findlocalip(f, c->laddr, c->raddr);
3103ff48bf5SDavid du Colombier 			ipmove(uh6->udpsrc, c->laddr);
3117dd7cddfSDavid du Colombier 		}
3123ff48bf5SDavid du Colombier 		hnputs(uh6->udpsport, c->lport);
3133ff48bf5SDavid du Colombier 		hnputs(uh6->udplen, ptcllen);
3143ff48bf5SDavid du Colombier 		uh6->udpcksum[0] = 0;
3153ff48bf5SDavid du Colombier 		uh6->udpcksum[1] = 0;
3163ff48bf5SDavid du Colombier 		hnputs(uh6->udpcksum,
3173ff48bf5SDavid du Colombier 		       ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ));
3183ff48bf5SDavid du Colombier 		memset(uh6, 0, 8);
3193ff48bf5SDavid du Colombier 		uh6->viclfl[0] = IP_VER6;
3203ff48bf5SDavid du Colombier 		hnputs(uh6->len, ptcllen);
3213ff48bf5SDavid du Colombier 		uh6->nextheader = IP_UDPPROTO;
3223ff48bf5SDavid du Colombier 		ipoput6(f, bp, 0, c->ttl, c->tos);
3233ff48bf5SDavid du Colombier 		break;
3247dd7cddfSDavid du Colombier 
3253ff48bf5SDavid du Colombier 	default:
3263ff48bf5SDavid du Colombier 		panic("udpkick: version %d", version);
3273ff48bf5SDavid du Colombier 	}
32880ee5cbfSDavid du Colombier 	upriv->ustats.udpOutDatagrams++;
32980ee5cbfSDavid du Colombier }
33080ee5cbfSDavid du Colombier 
3317dd7cddfSDavid du Colombier void
3329a747e4fSDavid du Colombier udpiput(Proto *udp, Ipifc *ifc, Block *bp)
3337dd7cddfSDavid du Colombier {
3343ff48bf5SDavid du Colombier 	int len;
3353ff48bf5SDavid du Colombier 	Udp4hdr *uh4;
3363ff48bf5SDavid du Colombier 	Udp6hdr *uh6;
33780ee5cbfSDavid du Colombier 	Conv *c;
3387dd7cddfSDavid du Colombier 	Udpcb *ucb;
3397dd7cddfSDavid du Colombier 	uchar raddr[IPaddrlen], laddr[IPaddrlen];
3407dd7cddfSDavid du Colombier 	ushort rport, lport;
3417dd7cddfSDavid du Colombier 	Udppriv *upriv;
3427dd7cddfSDavid du Colombier 	Fs *f;
3433ff48bf5SDavid du Colombier 	int version;
3443ff48bf5SDavid du Colombier 	int ottl, oviclfl, olen;
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier 	upriv = udp->priv;
3477dd7cddfSDavid du Colombier 	f = udp->f;
34880ee5cbfSDavid du Colombier 	upriv->ustats.udpInDatagrams++;
3497dd7cddfSDavid du Colombier 
3503ff48bf5SDavid du Colombier 	uh4 = (Udp4hdr*)(bp->rp);
3513ff48bf5SDavid du Colombier 	version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4;
3527dd7cddfSDavid du Colombier 
3533ff48bf5SDavid du Colombier 	/* Put back pseudo header for checksum
3543ff48bf5SDavid du Colombier 	 * (remember old values for icmpnoconv()) */
3553ff48bf5SDavid du Colombier 	switch(version) {
3563ff48bf5SDavid du Colombier 	case V4:
3573ff48bf5SDavid du Colombier 		ottl = uh4->Unused;
3583ff48bf5SDavid du Colombier 		uh4->Unused = 0;
3593ff48bf5SDavid du Colombier 		len = nhgets(uh4->udplen);
3603ff48bf5SDavid du Colombier 		olen = nhgets(uh4->udpplen);
3613ff48bf5SDavid du Colombier 		hnputs(uh4->udpplen, len);
3627dd7cddfSDavid du Colombier 
3633ff48bf5SDavid du Colombier 		v4tov6(raddr, uh4->udpsrc);
3643ff48bf5SDavid du Colombier 		v4tov6(laddr, uh4->udpdst);
3653ff48bf5SDavid du Colombier 		lport = nhgets(uh4->udpdport);
3663ff48bf5SDavid du Colombier 		rport = nhgets(uh4->udpsport);
3677dd7cddfSDavid du Colombier 
3683ff48bf5SDavid du Colombier 		if(nhgets(uh4->udpcksum)) {
3693ff48bf5SDavid du Colombier 			if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) {
37080ee5cbfSDavid du Colombier 				upriv->ustats.udpInErrors++;
3717dd7cddfSDavid du Colombier 				netlog(f, Logudp, "udp: checksum error %I\n", raddr);
3727dd7cddfSDavid du Colombier 				DPRINT("udp: checksum error %I\n", raddr);
3737dd7cddfSDavid du Colombier 				freeblist(bp);
3747dd7cddfSDavid du Colombier 				return;
3757dd7cddfSDavid du Colombier 			}
3767dd7cddfSDavid du Colombier 		}
3773ff48bf5SDavid du Colombier 		uh4->Unused = ottl;
3783ff48bf5SDavid du Colombier 		hnputs(uh4->udpplen, olen);
3793ff48bf5SDavid du Colombier 		break;
3803ff48bf5SDavid du Colombier 	case V6:
3813ff48bf5SDavid du Colombier 		uh6 = (Udp6hdr*)(bp->rp);
3823ff48bf5SDavid du Colombier 		len = nhgets(uh6->udplen);
3833ff48bf5SDavid du Colombier 		oviclfl = nhgetl(uh6->viclfl);
3843ff48bf5SDavid du Colombier 		olen = nhgets(uh6->len);
3853ff48bf5SDavid du Colombier 		ottl = uh6->hoplimit;
3863ff48bf5SDavid du Colombier 		ipmove(raddr, uh6->udpsrc);
3873ff48bf5SDavid du Colombier 		ipmove(laddr, uh6->udpdst);
3883ff48bf5SDavid du Colombier 		lport = nhgets(uh6->udpdport);
3893ff48bf5SDavid du Colombier 		rport = nhgets(uh6->udpsport);
3903ff48bf5SDavid du Colombier 		memset(uh6, 0, 8);
3913ff48bf5SDavid du Colombier 		hnputl(uh6->viclfl, len);
3923ff48bf5SDavid du Colombier 		uh6->hoplimit = IP_UDPPROTO;
3933ff48bf5SDavid du Colombier 		if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) {
3943ff48bf5SDavid du Colombier 			upriv->ustats.udpInErrors++;
3953ff48bf5SDavid du Colombier 			netlog(f, Logudp, "udp: checksum error %I\n", raddr);
3963ff48bf5SDavid du Colombier 			DPRINT("udp: checksum error %I\n", raddr);
3973ff48bf5SDavid du Colombier 			freeblist(bp);
3983ff48bf5SDavid du Colombier 			return;
3993ff48bf5SDavid du Colombier 		}
4003ff48bf5SDavid du Colombier 		hnputl(uh6->viclfl, oviclfl);
4013ff48bf5SDavid du Colombier 		hnputs(uh6->len, olen);
4023ff48bf5SDavid du Colombier 		uh6->nextheader = IP_UDPPROTO;
4033ff48bf5SDavid du Colombier 		uh6->hoplimit = ottl;
4043ff48bf5SDavid du Colombier 		break;
4053ff48bf5SDavid du Colombier 	default:
4063ff48bf5SDavid du Colombier 		panic("udpiput: version %d", version);
4073ff48bf5SDavid du Colombier 		return;	/* to avoid a warning */
4083ff48bf5SDavid du Colombier 	}
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier 	qlock(udp);
4117dd7cddfSDavid du Colombier 
41280ee5cbfSDavid du Colombier 	c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
4137dd7cddfSDavid du Colombier 	if(c == nil){
41480ee5cbfSDavid du Colombier 		/* no converstation found */
41580ee5cbfSDavid du Colombier 		upriv->ustats.udpNoPorts++;
4167dd7cddfSDavid du Colombier 		qunlock(udp);
4177dd7cddfSDavid du Colombier 		netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
4187dd7cddfSDavid du Colombier 		       laddr, lport);
4193ff48bf5SDavid du Colombier 
4203ff48bf5SDavid du Colombier 		switch(version){
4213ff48bf5SDavid du Colombier 		case V4:
4227dd7cddfSDavid du Colombier 			icmpnoconv(f, bp);
4233ff48bf5SDavid du Colombier 			break;
4243ff48bf5SDavid du Colombier 		case V6:
4253ff48bf5SDavid du Colombier 			icmphostunr(f, ifc, bp, icmp6_port_unreach, 0);
4263ff48bf5SDavid du Colombier 			break;
4273ff48bf5SDavid du Colombier 		default:
4283ff48bf5SDavid du Colombier 			panic("udpiput2: version %d", version);
4293ff48bf5SDavid du Colombier 		}
4303ff48bf5SDavid du Colombier 
4317dd7cddfSDavid du Colombier 		freeblist(bp);
4327dd7cddfSDavid du Colombier 		return;
4337dd7cddfSDavid du Colombier 	}
43480ee5cbfSDavid du Colombier 	ucb = (Udpcb*)c->ptcl;
43580ee5cbfSDavid du Colombier 
43680ee5cbfSDavid du Colombier 	if(c->state == Announced){
43780ee5cbfSDavid du Colombier 		if(ucb->headers == 0){
43880ee5cbfSDavid du Colombier 			/* create a new conversation */
4393ff48bf5SDavid du Colombier 			if(ipforme(f, laddr) != Runi) {
4403ff48bf5SDavid du Colombier 				switch(version){
4413ff48bf5SDavid du Colombier 				case V4:
4429a747e4fSDavid du Colombier 					v4tov6(laddr, ifc->lifc->local);
4433ff48bf5SDavid du Colombier 					break;
4443ff48bf5SDavid du Colombier 				case V6:
4453ff48bf5SDavid du Colombier 					ipmove(laddr, ifc->lifc->local);
4463ff48bf5SDavid du Colombier 					break;
4473ff48bf5SDavid du Colombier 				default:
4483ff48bf5SDavid du Colombier 					panic("udpiput3: version %d", version);
4493ff48bf5SDavid du Colombier 				}
4503ff48bf5SDavid du Colombier 			}
4513ff48bf5SDavid du Colombier 			c = Fsnewcall(c, raddr, rport, laddr, lport, version);
45280ee5cbfSDavid du Colombier 			if(c == nil){
45380ee5cbfSDavid du Colombier 				qunlock(udp);
45480ee5cbfSDavid du Colombier 				freeblist(bp);
45580ee5cbfSDavid du Colombier 				return;
45680ee5cbfSDavid du Colombier 			}
45780ee5cbfSDavid du Colombier 			iphtadd(&upriv->ht, c);
45880ee5cbfSDavid du Colombier 			ucb = (Udpcb*)c->ptcl;
45980ee5cbfSDavid du Colombier 		}
4607dd7cddfSDavid du Colombier 	}
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	qlock(c);
4637dd7cddfSDavid du Colombier 	qunlock(udp);
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier 	/*
4667dd7cddfSDavid du Colombier 	 * Trim the packet down to data size
4677dd7cddfSDavid du Colombier 	 */
4683ff48bf5SDavid du Colombier 	len -= UDP_UDPHDR_SZ;
4693ff48bf5SDavid du Colombier 	switch(version){
4703ff48bf5SDavid du Colombier 	case V4:
4713ff48bf5SDavid du Colombier 		bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len);
4723ff48bf5SDavid du Colombier 		break;
4733ff48bf5SDavid du Colombier 	case V6:
4743ff48bf5SDavid du Colombier 		bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len);
4753ff48bf5SDavid du Colombier 		break;
4763ff48bf5SDavid du Colombier 	default:
4773ff48bf5SDavid du Colombier 		bp = nil;
4783ff48bf5SDavid du Colombier 		panic("udpiput4: version %d", version);
4793ff48bf5SDavid du Colombier 	}
4807dd7cddfSDavid du Colombier 	if(bp == nil){
4817dd7cddfSDavid du Colombier 		qunlock(c);
4827dd7cddfSDavid du Colombier 		netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
4837dd7cddfSDavid du Colombier 		       laddr, lport);
48480ee5cbfSDavid du Colombier 		upriv->lenerr++;
4857dd7cddfSDavid du Colombier 		return;
4867dd7cddfSDavid du Colombier 	}
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
4897dd7cddfSDavid du Colombier 	       laddr, lport, len);
4907dd7cddfSDavid du Colombier 
4917dd7cddfSDavid du Colombier 	switch(ucb->headers){
4927dd7cddfSDavid du Colombier 	case 6:
4937dd7cddfSDavid du Colombier 		/* pass the src address */
4947dd7cddfSDavid du Colombier 		bp = padblock(bp, UDP_USEAD6);
4957dd7cddfSDavid du Colombier 		ipmove(bp->rp, raddr);
4967dd7cddfSDavid du Colombier 		if(ipforme(f, laddr) == Runi)
4977dd7cddfSDavid du Colombier 			ipmove(bp->rp+IPaddrlen, laddr);
4987dd7cddfSDavid du Colombier 		else
4999a747e4fSDavid du Colombier 			ipmove(bp->rp+IPaddrlen, ifc->lifc->local);
5007dd7cddfSDavid du Colombier 		hnputs(bp->rp+2*IPaddrlen, rport);
5017dd7cddfSDavid du Colombier 		hnputs(bp->rp+2*IPaddrlen+2, lport);
5027dd7cddfSDavid du Colombier 		break;
5037dd7cddfSDavid du Colombier 	case 4:
5047dd7cddfSDavid du Colombier 		/* pass the src address */
5057dd7cddfSDavid du Colombier 		bp = padblock(bp, UDP_USEAD4);
5067dd7cddfSDavid du Colombier 		v6tov4(bp->rp, raddr);
5077dd7cddfSDavid du Colombier 		if(ipforme(f, laddr) == Runi)
5087dd7cddfSDavid du Colombier 			v6tov4(bp->rp+IPv4addrlen, laddr);
5097dd7cddfSDavid du Colombier 		else
5109a747e4fSDavid du Colombier 			v6tov4(bp->rp+IPv4addrlen, ifc->lifc->local);
5117dd7cddfSDavid du Colombier 		hnputs(bp->rp + 2*IPv4addrlen, rport);
5127dd7cddfSDavid du Colombier 		hnputs(bp->rp + 2*IPv4addrlen + 2, lport);
5137dd7cddfSDavid du Colombier 		break;
5147dd7cddfSDavid du Colombier 	}
5157dd7cddfSDavid du Colombier 
5167dd7cddfSDavid du Colombier 	if(bp->next)
5177dd7cddfSDavid du Colombier 		bp = concatblock(bp);
5187dd7cddfSDavid du Colombier 
5197dd7cddfSDavid du Colombier 	if(qfull(c->rq)){
5207dd7cddfSDavid du Colombier 		qunlock(c);
5217dd7cddfSDavid du Colombier 		netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
5227dd7cddfSDavid du Colombier 		       laddr, lport);
5237dd7cddfSDavid du Colombier 		freeblist(bp);
5247dd7cddfSDavid du Colombier 		return;
5257dd7cddfSDavid du Colombier 	}
5267dd7cddfSDavid du Colombier 
5277dd7cddfSDavid du Colombier 	qpass(c->rq, bp);
5287dd7cddfSDavid du Colombier 	qunlock(c);
5297dd7cddfSDavid du Colombier 
5307dd7cddfSDavid du Colombier }
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier char*
5337dd7cddfSDavid du Colombier udpctl(Conv *c, char **f, int n)
5347dd7cddfSDavid du Colombier {
5357dd7cddfSDavid du Colombier 	Udpcb *ucb;
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier 	ucb = (Udpcb*)c->ptcl;
5387dd7cddfSDavid du Colombier 	if(n == 1){
5397dd7cddfSDavid du Colombier 		if(strcmp(f[0], "headers4") == 0){
5407dd7cddfSDavid du Colombier 			ucb->headers = 4;
5417dd7cddfSDavid du Colombier 			return nil;
5427dd7cddfSDavid du Colombier 		} else if(strcmp(f[0], "headers") == 0){
5437dd7cddfSDavid du Colombier 			ucb->headers = 6;
5447dd7cddfSDavid du Colombier 			return nil;
5457dd7cddfSDavid du Colombier 		}
5467dd7cddfSDavid du Colombier 	}
5477dd7cddfSDavid du Colombier 	return "unknown control request";
5487dd7cddfSDavid du Colombier }
5497dd7cddfSDavid du Colombier 
5507dd7cddfSDavid du Colombier void
5517dd7cddfSDavid du Colombier udpadvise(Proto *udp, Block *bp, char *msg)
5527dd7cddfSDavid du Colombier {
5533ff48bf5SDavid du Colombier 	Udp4hdr *h4;
5543ff48bf5SDavid du Colombier 	Udp6hdr *h6;
5557dd7cddfSDavid du Colombier 	uchar source[IPaddrlen], dest[IPaddrlen];
5567dd7cddfSDavid du Colombier 	ushort psource, pdest;
5577dd7cddfSDavid du Colombier 	Conv *s, **p;
5583ff48bf5SDavid du Colombier 	int version;
5597dd7cddfSDavid du Colombier 
5603ff48bf5SDavid du Colombier 	h4 = (Udp4hdr*)(bp->rp);
5613ff48bf5SDavid du Colombier 	version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4;
5627dd7cddfSDavid du Colombier 
5633ff48bf5SDavid du Colombier 	switch(version) {
5643ff48bf5SDavid du Colombier 	case V4:
5653ff48bf5SDavid du Colombier 		v4tov6(dest, h4->udpdst);
5663ff48bf5SDavid du Colombier 		v4tov6(source, h4->udpsrc);
5673ff48bf5SDavid du Colombier 		psource = nhgets(h4->udpsport);
5683ff48bf5SDavid du Colombier 		pdest = nhgets(h4->udpdport);
5693ff48bf5SDavid du Colombier 		break;
5703ff48bf5SDavid du Colombier 	case V6:
5713ff48bf5SDavid du Colombier 		h6 = (Udp6hdr*)(bp->rp);
5723ff48bf5SDavid du Colombier 		ipmove(dest, h6->udpdst);
5733ff48bf5SDavid du Colombier 		ipmove(source, h6->udpsrc);
5743ff48bf5SDavid du Colombier 		psource = nhgets(h6->udpsport);
5753ff48bf5SDavid du Colombier 		pdest = nhgets(h6->udpdport);
5763ff48bf5SDavid du Colombier 		break;
5773ff48bf5SDavid du Colombier 	default:
5783ff48bf5SDavid du Colombier 		panic("udpadvise: version %d", version);
5793ff48bf5SDavid du Colombier 		return;  /* to avoid a warning */
5803ff48bf5SDavid du Colombier 	}
5817dd7cddfSDavid du Colombier 
5827dd7cddfSDavid du Colombier 	/* Look for a connection */
5837dd7cddfSDavid du Colombier 	qlock(udp);
5847dd7cddfSDavid du Colombier 	for(p = udp->conv; *p; p++) {
5857dd7cddfSDavid du Colombier 		s = *p;
5867dd7cddfSDavid du Colombier 		if(s->rport == pdest)
5877dd7cddfSDavid du Colombier 		if(s->lport == psource)
5887dd7cddfSDavid du Colombier 		if(ipcmp(s->raddr, dest) == 0)
5897dd7cddfSDavid du Colombier 		if(ipcmp(s->laddr, source) == 0){
5903ff48bf5SDavid du Colombier 			if(s->ignoreadvice)
5913ff48bf5SDavid du Colombier 				break;
5927dd7cddfSDavid du Colombier 			qlock(s);
5937dd7cddfSDavid du Colombier 			qunlock(udp);
5947dd7cddfSDavid du Colombier 			qhangup(s->rq, msg);
5957dd7cddfSDavid du Colombier 			qhangup(s->wq, msg);
5967dd7cddfSDavid du Colombier 			qunlock(s);
5977dd7cddfSDavid du Colombier 			freeblist(bp);
5987dd7cddfSDavid du Colombier 			return;
5997dd7cddfSDavid du Colombier 		}
6007dd7cddfSDavid du Colombier 	}
6017dd7cddfSDavid du Colombier 	qunlock(udp);
6027dd7cddfSDavid du Colombier 	freeblist(bp);
6037dd7cddfSDavid du Colombier }
6047dd7cddfSDavid du Colombier 
6057dd7cddfSDavid du Colombier int
6067dd7cddfSDavid du Colombier udpstats(Proto *udp, char *buf, int len)
6077dd7cddfSDavid du Colombier {
60880ee5cbfSDavid du Colombier 	Udppriv *upriv;
6097dd7cddfSDavid du Colombier 
61080ee5cbfSDavid du Colombier 	upriv = udp->priv;
6119a747e4fSDavid du Colombier 	return snprint(buf, len, "InDatagrams: %lud\nNoPorts: %lud\nInErrors: %lud\nOutDatagrams: %lud\n",
61280ee5cbfSDavid du Colombier 		upriv->ustats.udpInDatagrams,
61380ee5cbfSDavid du Colombier 		upriv->ustats.udpNoPorts,
61480ee5cbfSDavid du Colombier 		upriv->ustats.udpInErrors,
61580ee5cbfSDavid du Colombier 		upriv->ustats.udpOutDatagrams);
6167dd7cddfSDavid du Colombier }
6177dd7cddfSDavid du Colombier 
6187dd7cddfSDavid du Colombier void
6197dd7cddfSDavid du Colombier udpinit(Fs *fs)
6207dd7cddfSDavid du Colombier {
6217dd7cddfSDavid du Colombier 	Proto *udp;
6227dd7cddfSDavid du Colombier 
6237dd7cddfSDavid du Colombier 	udp = smalloc(sizeof(Proto));
6247dd7cddfSDavid du Colombier 	udp->priv = smalloc(sizeof(Udppriv));
6257dd7cddfSDavid du Colombier 	udp->name = "udp";
6267dd7cddfSDavid du Colombier 	udp->connect = udpconnect;
6277dd7cddfSDavid du Colombier 	udp->announce = udpannounce;
6287dd7cddfSDavid du Colombier 	udp->ctl = udpctl;
6297dd7cddfSDavid du Colombier 	udp->state = udpstate;
6307dd7cddfSDavid du Colombier 	udp->create = udpcreate;
6317dd7cddfSDavid du Colombier 	udp->close = udpclose;
6327dd7cddfSDavid du Colombier 	udp->rcv = udpiput;
6337dd7cddfSDavid du Colombier 	udp->advise = udpadvise;
6347dd7cddfSDavid du Colombier 	udp->stats = udpstats;
6357dd7cddfSDavid du Colombier 	udp->ipproto = IP_UDPPROTO;
6367dd7cddfSDavid du Colombier 	udp->nc = Nchans;
6377dd7cddfSDavid du Colombier 	udp->ptclsize = sizeof(Udpcb);
6387dd7cddfSDavid du Colombier 
6397dd7cddfSDavid du Colombier 	Fsproto(fs, udp);
6407dd7cddfSDavid du Colombier }
641