xref: /plan9/sys/src/9/ip/rudp.c (revision 03a1fc68fdb4308e3fd64ada3b55a74e8e698e60)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  *  This protocol is compatible with UDP's packet format.
37dd7cddfSDavid du Colombier  *  It could be done over UDP if need be.
47dd7cddfSDavid du Colombier  */
57dd7cddfSDavid du Colombier #include	"u.h"
67dd7cddfSDavid du Colombier #include	"../port/lib.h"
77dd7cddfSDavid du Colombier #include	"mem.h"
87dd7cddfSDavid du Colombier #include	"dat.h"
97dd7cddfSDavid du Colombier #include	"fns.h"
107dd7cddfSDavid du Colombier #include	"../port/error.h"
117dd7cddfSDavid du Colombier 
127dd7cddfSDavid du Colombier #include	"ip.h"
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier #define DEBUG	0
157dd7cddfSDavid du Colombier #define DPRINT if(DEBUG)print
167dd7cddfSDavid du Colombier 
177dd7cddfSDavid du Colombier #define SEQDIFF(a,b) ( (a)>=(b)?\
187dd7cddfSDavid du Colombier 			(a)-(b):\
197dd7cddfSDavid du Colombier 			0xffffffffUL-((b)-(a)) )
207dd7cddfSDavid du Colombier #define INSEQ(a,start,end) ( (start)<=(end)?\
217dd7cddfSDavid du Colombier 				((a)>(start)&&(a)<=(end)):\
227dd7cddfSDavid du Colombier 				((a)>(start)||(a)<=(end)) )
237dd7cddfSDavid du Colombier #define UNACKED(r) SEQDIFF(r->sndseq, r->ackrcvd)
247dd7cddfSDavid du Colombier #define NEXTSEQ(a) ( (a)+1 == 0 ? 1 : (a)+1 )
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier enum
277dd7cddfSDavid du Colombier {
287dd7cddfSDavid du Colombier 	UDP_HDRSIZE	= 20,	/* pseudo header + udp header */
297dd7cddfSDavid du Colombier 	UDP_PHDRSIZE	= 12,	/* pseudo header */
307dd7cddfSDavid du Colombier 	UDP_RHDRSIZE	= 36,	/* pseudo header + udp header + rudp header */
317dd7cddfSDavid du Colombier 	UDP_IPHDR	= 8,	/* ip header */
327dd7cddfSDavid du Colombier 	IP_UDPPROTO	= 254,
33dc5a79c1SDavid du Colombier 	UDP_USEAD7	= 52,
347dd7cddfSDavid du Colombier 	UDP_USEAD6	= 36,
357dd7cddfSDavid du Colombier 	UDP_USEAD4	= 12,
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier 	Rudprxms	= 200,
387dd7cddfSDavid du Colombier 	Rudptickms	= 50,
397dd7cddfSDavid du Colombier 	Rudpmaxxmit	= 10,
407dd7cddfSDavid du Colombier 	Maxunacked	= 100,
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier };
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier #define Hangupgen	0xffffffff	/* used only in hangup messages */
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier typedef struct Udphdr Udphdr;
477dd7cddfSDavid du Colombier struct Udphdr
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier 	/* ip header */
507dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
517dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
527dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
537dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
547dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier 	/* pseudo header starts here */
577dd7cddfSDavid du Colombier 	uchar	Unused;
587dd7cddfSDavid du Colombier 	uchar	udpproto;	/* Protocol */
597dd7cddfSDavid du Colombier 	uchar	udpplen[2];	/* Header plus data length */
607dd7cddfSDavid du Colombier 	uchar	udpsrc[4];	/* Ip source */
617dd7cddfSDavid du Colombier 	uchar	udpdst[4];	/* Ip destination */
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 	/* udp header */
647dd7cddfSDavid du Colombier 	uchar	udpsport[2];	/* Source port */
657dd7cddfSDavid du Colombier 	uchar	udpdport[2];	/* Destination port */
667dd7cddfSDavid du Colombier 	uchar	udplen[2];	/* data length */
677dd7cddfSDavid du Colombier 	uchar	udpcksum[2];	/* Checksum */
687dd7cddfSDavid du Colombier };
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier typedef struct Rudphdr Rudphdr;
717dd7cddfSDavid du Colombier struct Rudphdr
727dd7cddfSDavid du Colombier {
737dd7cddfSDavid du Colombier 	/* ip header */
747dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
757dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
767dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
777dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
787dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier 	/* pseudo header starts here */
817dd7cddfSDavid du Colombier 	uchar	Unused;
827dd7cddfSDavid du Colombier 	uchar	udpproto;	/* Protocol */
837dd7cddfSDavid du Colombier 	uchar	udpplen[2];	/* Header plus data length */
847dd7cddfSDavid du Colombier 	uchar	udpsrc[4];	/* Ip source */
857dd7cddfSDavid du Colombier 	uchar	udpdst[4];	/* Ip destination */
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier 	/* udp header */
887dd7cddfSDavid du Colombier 	uchar	udpsport[2];	/* Source port */
897dd7cddfSDavid du Colombier 	uchar	udpdport[2];	/* Destination port */
907dd7cddfSDavid du Colombier 	uchar	udplen[2];	/* data length (includes rudp header) */
917dd7cddfSDavid du Colombier 	uchar	udpcksum[2];	/* Checksum */
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	/* rudp header */
947dd7cddfSDavid du Colombier 	uchar	relseq[4];	/* id of this packet (or 0) */
957dd7cddfSDavid du Colombier 	uchar	relsgen[4];	/* generation/time stamp */
967dd7cddfSDavid du Colombier 	uchar	relack[4];	/* packet being acked (or 0) */
977dd7cddfSDavid du Colombier 	uchar	relagen[4];	/* generation/time stamp */
987dd7cddfSDavid du Colombier };
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier /*
1027dd7cddfSDavid du Colombier  *  one state structure per destination
1037dd7cddfSDavid du Colombier  */
1047dd7cddfSDavid du Colombier typedef struct Reliable Reliable;
1057dd7cddfSDavid du Colombier struct Reliable
1067dd7cddfSDavid du Colombier {
1077dd7cddfSDavid du Colombier 	Ref;
1087dd7cddfSDavid du Colombier 
1097dd7cddfSDavid du Colombier 	Reliable *next;
1107dd7cddfSDavid du Colombier 
1117dd7cddfSDavid du Colombier 	uchar	addr[IPaddrlen];	/* always V6 when put here */
1127dd7cddfSDavid du Colombier 	ushort	port;
1137dd7cddfSDavid du Colombier 
1147dd7cddfSDavid du Colombier 	Block	*unacked;	/* unacked msg list */
1157dd7cddfSDavid du Colombier 	Block	*unackedtail;	/*  and its tail */
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	int	timeout;	/* time since first unacked msg sent */
1187dd7cddfSDavid du Colombier 	int	xmits;		/* number of times first unacked msg sent */
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier 	ulong	sndseq;		/* next packet to be sent */
1217dd7cddfSDavid du Colombier 	ulong	sndgen;		/*  and its generation */
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	ulong	rcvseq;		/* last packet received */
1247dd7cddfSDavid du Colombier 	ulong	rcvgen;		/*  and its generation */
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	ulong	acksent;	/* last ack sent */
1277dd7cddfSDavid du Colombier 	ulong	ackrcvd;	/* last msg for which ack was rcvd */
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier 	/* flow control */
1307dd7cddfSDavid du Colombier 	QLock	lock;
1317dd7cddfSDavid du Colombier 	Rendez	vous;
1327dd7cddfSDavid du Colombier 	int	blocked;
1337dd7cddfSDavid du Colombier };
1347dd7cddfSDavid du Colombier 
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier /* MIB II counters */
1387dd7cddfSDavid du Colombier typedef struct Rudpstats Rudpstats;
1397dd7cddfSDavid du Colombier struct Rudpstats
1407dd7cddfSDavid du Colombier {
1417dd7cddfSDavid du Colombier 	ulong	rudpInDatagrams;
1427dd7cddfSDavid du Colombier 	ulong	rudpNoPorts;
1437dd7cddfSDavid du Colombier 	ulong	rudpInErrors;
1447dd7cddfSDavid du Colombier 	ulong	rudpOutDatagrams;
1457dd7cddfSDavid du Colombier };
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier typedef struct Rudppriv Rudppriv;
1487dd7cddfSDavid du Colombier struct Rudppriv
1497dd7cddfSDavid du Colombier {
15080ee5cbfSDavid du Colombier 	Ipht	ht;
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	/* MIB counters */
1537dd7cddfSDavid du Colombier 	Rudpstats	ustats;
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier 	/* non-MIB stats */
1567dd7cddfSDavid du Colombier 	ulong	csumerr;		/* checksum errors */
1577dd7cddfSDavid du Colombier 	ulong	lenerr;			/* short packet */
1587dd7cddfSDavid du Colombier 	ulong	rxmits;			/* # of retransmissions */
1597dd7cddfSDavid du Colombier 	ulong	orders;			/* # of out of order pkts */
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	/* keeping track of the ack kproc */
1627dd7cddfSDavid du Colombier 	int	ackprocstarted;
1637dd7cddfSDavid du Colombier 	QLock	apl;
1647dd7cddfSDavid du Colombier };
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 
1677dd7cddfSDavid du Colombier static ulong generation = 0;
1687dd7cddfSDavid du Colombier static Rendez rend;
1697dd7cddfSDavid du Colombier 
1707dd7cddfSDavid du Colombier /*
1717dd7cddfSDavid du Colombier  *  protocol specific part of Conv
1727dd7cddfSDavid du Colombier  */
1737dd7cddfSDavid du Colombier typedef struct Rudpcb Rudpcb;
1747dd7cddfSDavid du Colombier struct Rudpcb
1757dd7cddfSDavid du Colombier {
1767dd7cddfSDavid du Colombier 	QLock;
1777dd7cddfSDavid du Colombier 	uchar	headers;
1787dd7cddfSDavid du Colombier 	uchar	randdrop;
1797dd7cddfSDavid du Colombier 	Reliable *r;
1807dd7cddfSDavid du Colombier };
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier /*
1837dd7cddfSDavid du Colombier  * local functions
1847dd7cddfSDavid du Colombier  */
1857dd7cddfSDavid du Colombier void	relsendack(Conv*, Reliable*, int);
1867dd7cddfSDavid du Colombier int	reliput(Conv*, Block*, uchar*, ushort);
1877dd7cddfSDavid du Colombier Reliable *relstate(Rudpcb*, uchar*, ushort, char*);
1887dd7cddfSDavid du Colombier void	relput(Reliable*);
1897dd7cddfSDavid du Colombier void	relforget(Conv *, uchar*, int, int);
1907dd7cddfSDavid du Colombier void	relackproc(void *);
1917dd7cddfSDavid du Colombier void	relackq(Reliable *, Block*);
1927dd7cddfSDavid du Colombier void	relhangup(Conv *, Reliable*);
1937dd7cddfSDavid du Colombier void	relrexmit(Conv *, Reliable*);
1947dd7cddfSDavid du Colombier void	relput(Reliable*);
1953ff48bf5SDavid du Colombier void	rudpkick(void *x);
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier static void
1987dd7cddfSDavid du Colombier rudpstartackproc(Proto *rudp)
1997dd7cddfSDavid du Colombier {
2007dd7cddfSDavid du Colombier 	Rudppriv *rpriv;
2019a747e4fSDavid du Colombier 	char kpname[KNAMELEN];
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 	rpriv = rudp->priv;
2047dd7cddfSDavid du Colombier 	if(rpriv->ackprocstarted == 0){
2057dd7cddfSDavid du Colombier 		qlock(&rpriv->apl);
2067dd7cddfSDavid du Colombier 		if(rpriv->ackprocstarted == 0){
2077dd7cddfSDavid du Colombier 			sprint(kpname, "#I%drudpack", rudp->f->dev);
2087dd7cddfSDavid du Colombier 			kproc(kpname, relackproc, rudp);
2097dd7cddfSDavid du Colombier 			rpriv->ackprocstarted = 1;
2107dd7cddfSDavid du Colombier 		}
2117dd7cddfSDavid du Colombier 		qunlock(&rpriv->apl);
2127dd7cddfSDavid du Colombier 	}
2137dd7cddfSDavid du Colombier }
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier static char*
2167dd7cddfSDavid du Colombier rudpconnect(Conv *c, char **argv, int argc)
2177dd7cddfSDavid du Colombier {
2187dd7cddfSDavid du Colombier 	char *e;
21980ee5cbfSDavid du Colombier 	Rudppriv *upriv;
2207dd7cddfSDavid du Colombier 
22180ee5cbfSDavid du Colombier 	upriv = c->p->priv;
2227dd7cddfSDavid du Colombier 	rudpstartackproc(c->p);
2237dd7cddfSDavid du Colombier 	e = Fsstdconnect(c, argv, argc);
2247dd7cddfSDavid du Colombier 	Fsconnected(c, e);
22580ee5cbfSDavid du Colombier 	iphtadd(&upriv->ht, c);
2267dd7cddfSDavid du Colombier 
2277dd7cddfSDavid du Colombier 	return e;
2287dd7cddfSDavid du Colombier }
2297dd7cddfSDavid du Colombier 
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier static int
2327dd7cddfSDavid du Colombier rudpstate(Conv *c, char *state, int n)
2337dd7cddfSDavid du Colombier {
2347dd7cddfSDavid du Colombier 	Rudpcb *ucb;
2357dd7cddfSDavid du Colombier 	Reliable *r;
2367dd7cddfSDavid du Colombier 	int m;
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier 	m = snprint(state, n, "%s", c->inuse?"Open":"Closed");
2397dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
2407dd7cddfSDavid du Colombier 	qlock(ucb);
2417dd7cddfSDavid du Colombier 	for(r = ucb->r; r; r = r->next)
2427dd7cddfSDavid du Colombier 		m += snprint(state+m, n-m, " %I/%ld", r->addr, UNACKED(r));
243*03a1fc68SDavid du Colombier 	m += snprint(state+m, n-m, "\n");
2447dd7cddfSDavid du Colombier 	qunlock(ucb);
2457dd7cddfSDavid du Colombier 	return m;
2467dd7cddfSDavid du Colombier }
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier static char*
2497dd7cddfSDavid du Colombier rudpannounce(Conv *c, char** argv, int argc)
2507dd7cddfSDavid du Colombier {
2517dd7cddfSDavid du Colombier 	char *e;
25280ee5cbfSDavid du Colombier 	Rudppriv *upriv;
2537dd7cddfSDavid du Colombier 
25480ee5cbfSDavid du Colombier 	upriv = c->p->priv;
2557dd7cddfSDavid du Colombier 	rudpstartackproc(c->p);
2567dd7cddfSDavid du Colombier 	e = Fsstdannounce(c, argv, argc);
2577dd7cddfSDavid du Colombier 	if(e != nil)
2587dd7cddfSDavid du Colombier 		return e;
2597dd7cddfSDavid du Colombier 	Fsconnected(c, nil);
26080ee5cbfSDavid du Colombier 	iphtadd(&upriv->ht, c);
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier 	return nil;
2637dd7cddfSDavid du Colombier }
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier static void
2667dd7cddfSDavid du Colombier rudpcreate(Conv *c)
2677dd7cddfSDavid du Colombier {
2683ff48bf5SDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, 0);
2693ff48bf5SDavid du Colombier 	c->wq = qopen(64*1024, Qkick, rudpkick, c);
2707dd7cddfSDavid du Colombier }
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier static void
2737dd7cddfSDavid du Colombier rudpclose(Conv *c)
2747dd7cddfSDavid du Colombier {
2757dd7cddfSDavid du Colombier 	Rudpcb *ucb;
2767dd7cddfSDavid du Colombier 	Reliable *r, *nr;
27780ee5cbfSDavid du Colombier 	Rudppriv *upriv;
27880ee5cbfSDavid du Colombier 
27980ee5cbfSDavid du Colombier 	upriv = c->p->priv;
28080ee5cbfSDavid du Colombier 	iphtrem(&upriv->ht, c);
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	/* force out any delayed acks */
2837dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
2847dd7cddfSDavid du Colombier 	qlock(ucb);
2857dd7cddfSDavid du Colombier 	for(r = ucb->r; r; r = r->next){
2867dd7cddfSDavid du Colombier 		if(r->acksent != r->rcvseq)
2877dd7cddfSDavid du Colombier 			relsendack(c, r, 0);
2887dd7cddfSDavid du Colombier 	}
2897dd7cddfSDavid du Colombier 	qunlock(ucb);
2907dd7cddfSDavid du Colombier 
2917dd7cddfSDavid du Colombier 	qclose(c->rq);
2927dd7cddfSDavid du Colombier 	qclose(c->wq);
2937dd7cddfSDavid du Colombier 	qclose(c->eq);
2947dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
2957dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
2967dd7cddfSDavid du Colombier 	c->lport = 0;
2977dd7cddfSDavid du Colombier 	c->rport = 0;
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	ucb->headers = 0;
3007dd7cddfSDavid du Colombier 	ucb->randdrop = 0;
3017dd7cddfSDavid du Colombier 	qlock(ucb);
3027dd7cddfSDavid du Colombier 	for(r = ucb->r; r; r = nr){
3037dd7cddfSDavid du Colombier 		if(r->acksent != r->rcvseq)
3047dd7cddfSDavid du Colombier 			relsendack(c, r, 0);
3057dd7cddfSDavid du Colombier 		nr = r->next;
3067dd7cddfSDavid du Colombier 		relhangup(c, r);
3077dd7cddfSDavid du Colombier 		relput(r);
3087dd7cddfSDavid du Colombier 	}
3097dd7cddfSDavid du Colombier 	ucb->r = 0;
3107dd7cddfSDavid du Colombier 
3117dd7cddfSDavid du Colombier 	qunlock(ucb);
3127dd7cddfSDavid du Colombier }
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier /*
3157dd7cddfSDavid du Colombier  *  randomly don't send packets
3167dd7cddfSDavid du Colombier  */
3177dd7cddfSDavid du Colombier static void
3187dd7cddfSDavid du Colombier doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos)
3197dd7cddfSDavid du Colombier {
3207dd7cddfSDavid du Colombier 	Rudpcb *ucb;
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
3237dd7cddfSDavid du Colombier 	if(ucb->randdrop && nrand(100) < ucb->randdrop)
3247dd7cddfSDavid du Colombier 		freeblist(bp);
3257dd7cddfSDavid du Colombier 	else
326a6a9e072SDavid du Colombier 		ipoput4(f, bp, x, ttl, tos, nil);
3277dd7cddfSDavid du Colombier }
3287dd7cddfSDavid du Colombier 
3297dd7cddfSDavid du Colombier int
3307dd7cddfSDavid du Colombier flow(void *v)
3317dd7cddfSDavid du Colombier {
3327dd7cddfSDavid du Colombier 	Reliable *r = v;
3337dd7cddfSDavid du Colombier 
3347dd7cddfSDavid du Colombier 	return UNACKED(r) <= Maxunacked;
3357dd7cddfSDavid du Colombier }
3367dd7cddfSDavid du Colombier 
3377dd7cddfSDavid du Colombier void
3383ff48bf5SDavid du Colombier rudpkick(void *x)
3397dd7cddfSDavid du Colombier {
3403ff48bf5SDavid du Colombier 	Conv *c = x;
3417dd7cddfSDavid du Colombier 	Udphdr *uh;
3427dd7cddfSDavid du Colombier 	ushort rport;
3437dd7cddfSDavid du Colombier 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
3447dd7cddfSDavid du Colombier 	Block *bp;
3457dd7cddfSDavid du Colombier 	Rudpcb *ucb;
3467dd7cddfSDavid du Colombier 	Rudphdr *rh;
3477dd7cddfSDavid du Colombier 	Reliable *r;
3487dd7cddfSDavid du Colombier 	int dlen, ptcllen;
3497dd7cddfSDavid du Colombier 	Rudppriv *upriv;
3507dd7cddfSDavid du Colombier 	Fs *f;
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 	upriv = c->p->priv;
3537dd7cddfSDavid du Colombier 	f = c->p->f;
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier 	netlog(c->p->f, Logrudp, "rudp: kick\n");
3567dd7cddfSDavid du Colombier 	bp = qget(c->wq);
3577dd7cddfSDavid du Colombier 	if(bp == nil)
3587dd7cddfSDavid du Colombier 		return;
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
3617dd7cddfSDavid du Colombier 	switch(ucb->headers) {
362dc5a79c1SDavid du Colombier 	case 7:
363dc5a79c1SDavid du Colombier 		/* get user specified addresses */
364dc5a79c1SDavid du Colombier 		bp = pullupblock(bp, UDP_USEAD7);
365dc5a79c1SDavid du Colombier 		if(bp == nil)
366dc5a79c1SDavid du Colombier 			return;
367dc5a79c1SDavid du Colombier 		ipmove(raddr, bp->rp);
368dc5a79c1SDavid du Colombier 		bp->rp += IPaddrlen;
369dc5a79c1SDavid du Colombier 		ipmove(laddr, bp->rp);
370dc5a79c1SDavid du Colombier 		bp->rp += IPaddrlen;
371dc5a79c1SDavid du Colombier 		/* pick interface closest to dest */
372dc5a79c1SDavid du Colombier 		if(ipforme(f, laddr) != Runi)
373dc5a79c1SDavid du Colombier 			findlocalip(f, laddr, raddr);
374dc5a79c1SDavid du Colombier 		bp->rp += IPaddrlen;		/* Ignore ifc address */
375dc5a79c1SDavid du Colombier 		rport = nhgets(bp->rp);
376dc5a79c1SDavid du Colombier 		bp->rp += 2+2;			/* Ignore local port */
377dc5a79c1SDavid du Colombier 		break;
378f2c197d9SDavid du Colombier 	case 6:					/* OBS */
3797dd7cddfSDavid du Colombier 		/* get user specified addresses */
3807dd7cddfSDavid du Colombier 		bp = pullupblock(bp, UDP_USEAD6);
3817dd7cddfSDavid du Colombier 		if(bp == nil)
3827dd7cddfSDavid du Colombier 			return;
3837dd7cddfSDavid du Colombier 		ipmove(raddr, bp->rp);
3847dd7cddfSDavid du Colombier 		bp->rp += IPaddrlen;
3857dd7cddfSDavid du Colombier 		ipmove(laddr, bp->rp);
3867dd7cddfSDavid du Colombier 		bp->rp += IPaddrlen;
3877dd7cddfSDavid du Colombier 		/* pick interface closest to dest */
3887dd7cddfSDavid du Colombier 		if(ipforme(f, laddr) != Runi)
3897dd7cddfSDavid du Colombier 			findlocalip(f, laddr, raddr);
3907dd7cddfSDavid du Colombier 		rport = nhgets(bp->rp);
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier 		bp->rp += 4;			/* Igonore local port */
3937dd7cddfSDavid du Colombier 		break;
3947dd7cddfSDavid du Colombier 	default:
3957dd7cddfSDavid du Colombier 		ipmove(raddr, c->raddr);
3967dd7cddfSDavid du Colombier 		ipmove(laddr, c->laddr);
3977dd7cddfSDavid du Colombier 		rport = c->rport;
3987dd7cddfSDavid du Colombier 
3997dd7cddfSDavid du Colombier 		break;
4007dd7cddfSDavid du Colombier 	}
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier 	dlen = blocklen(bp);
4037dd7cddfSDavid du Colombier 
4047dd7cddfSDavid du Colombier 	/* Make space to fit rudp & ip header */
4057dd7cddfSDavid du Colombier 	bp = padblock(bp, UDP_IPHDR+UDP_RHDRSIZE);
4067dd7cddfSDavid du Colombier 	if(bp == nil)
4077dd7cddfSDavid du Colombier 		return;
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 	uh = (Udphdr *)(bp->rp);
4103ff48bf5SDavid du Colombier 	uh->vihl = IP_VER4;
4117dd7cddfSDavid du Colombier 
4127dd7cddfSDavid du Colombier 	rh = (Rudphdr*)uh;
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier 	ptcllen = dlen + (UDP_RHDRSIZE-UDP_PHDRSIZE);
4157dd7cddfSDavid du Colombier 	uh->Unused = 0;
4167dd7cddfSDavid du Colombier 	uh->udpproto = IP_UDPPROTO;
4177dd7cddfSDavid du Colombier 	uh->frag[0] = 0;
4187dd7cddfSDavid du Colombier 	uh->frag[1] = 0;
4197dd7cddfSDavid du Colombier 	hnputs(uh->udpplen, ptcllen);
4207dd7cddfSDavid du Colombier 	switch(ucb->headers){
421f2c197d9SDavid du Colombier 	case 6:					/* OBS */
422dc5a79c1SDavid du Colombier 	case 7:
4237dd7cddfSDavid du Colombier 		v6tov4(uh->udpdst, raddr);
4247dd7cddfSDavid du Colombier 		hnputs(uh->udpdport, rport);
4257dd7cddfSDavid du Colombier 		v6tov4(uh->udpsrc, laddr);
4267dd7cddfSDavid du Colombier 		break;
4277dd7cddfSDavid du Colombier 	default:
4287dd7cddfSDavid du Colombier 		v6tov4(uh->udpdst, c->raddr);
4297dd7cddfSDavid du Colombier 		hnputs(uh->udpdport, c->rport);
4307dd7cddfSDavid du Colombier 		if(ipcmp(c->laddr, IPnoaddr) == 0)
4317dd7cddfSDavid du Colombier 			findlocalip(f, c->laddr, c->raddr);
4327dd7cddfSDavid du Colombier 		v6tov4(uh->udpsrc, c->laddr);
4337dd7cddfSDavid du Colombier 		break;
4347dd7cddfSDavid du Colombier 	}
4357dd7cddfSDavid du Colombier 	hnputs(uh->udpsport, c->lport);
4367dd7cddfSDavid du Colombier 	hnputs(uh->udplen, ptcllen);
4377dd7cddfSDavid du Colombier 	uh->udpcksum[0] = 0;
4387dd7cddfSDavid du Colombier 	uh->udpcksum[1] = 0;
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier 	qlock(ucb);
4417dd7cddfSDavid du Colombier 	r = relstate(ucb, raddr, rport, "kick");
4427dd7cddfSDavid du Colombier 	r->sndseq = NEXTSEQ(r->sndseq);
4437dd7cddfSDavid du Colombier 	hnputl(rh->relseq, r->sndseq);
4447dd7cddfSDavid du Colombier 	hnputl(rh->relsgen, r->sndgen);
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier 	hnputl(rh->relack, r->rcvseq);  /* ACK last rcvd packet */
4477dd7cddfSDavid du Colombier 	hnputl(rh->relagen, r->rcvgen);
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 	if(r->rcvseq != r->acksent)
4507dd7cddfSDavid du Colombier 		r->acksent = r->rcvseq;
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier 	hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_RHDRSIZE));
4537dd7cddfSDavid du Colombier 
4547dd7cddfSDavid du Colombier 	relackq(r, bp);
4557dd7cddfSDavid du Colombier 	qunlock(ucb);
4567dd7cddfSDavid du Colombier 
4577dd7cddfSDavid du Colombier 	upriv->ustats.rudpOutDatagrams++;
4587dd7cddfSDavid du Colombier 
4597dd7cddfSDavid du Colombier 	DPRINT("sent: %lud/%lud, %lud/%lud\n",
4607dd7cddfSDavid du Colombier 		r->sndseq, r->sndgen, r->rcvseq, r->rcvgen);
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	doipoput(c, f, bp, 0, c->ttl, c->tos);
4637dd7cddfSDavid du Colombier 
4647dd7cddfSDavid du Colombier 	if(waserror()) {
4657dd7cddfSDavid du Colombier 		relput(r);
4667dd7cddfSDavid du Colombier 		qunlock(&r->lock);
4677dd7cddfSDavid du Colombier 		nexterror();
4687dd7cddfSDavid du Colombier 	}
4697dd7cddfSDavid du Colombier 
4707dd7cddfSDavid du Colombier 	/* flow control of sorts */
4717dd7cddfSDavid du Colombier 	qlock(&r->lock);
4727dd7cddfSDavid du Colombier 	if(UNACKED(r) > Maxunacked){
4737dd7cddfSDavid du Colombier 		r->blocked = 1;
4747dd7cddfSDavid du Colombier 		sleep(&r->vous, flow, r);
4757dd7cddfSDavid du Colombier 		r->blocked = 0;
4767dd7cddfSDavid du Colombier 	}
4777dd7cddfSDavid du Colombier 
4787dd7cddfSDavid du Colombier 	qunlock(&r->lock);
4797dd7cddfSDavid du Colombier 	relput(r);
4807dd7cddfSDavid du Colombier 	poperror();
4817dd7cddfSDavid du Colombier }
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier void
4849a747e4fSDavid du Colombier rudpiput(Proto *rudp, Ipifc *ifc, Block *bp)
4857dd7cddfSDavid du Colombier {
4867dd7cddfSDavid du Colombier 	int len, olen, ottl;
4877dd7cddfSDavid du Colombier 	Udphdr *uh;
48880ee5cbfSDavid du Colombier 	Conv *c;
4897dd7cddfSDavid du Colombier 	Rudpcb *ucb;
4907dd7cddfSDavid du Colombier 	uchar raddr[IPaddrlen], laddr[IPaddrlen];
4917dd7cddfSDavid du Colombier 	ushort rport, lport;
4927dd7cddfSDavid du Colombier 	Rudppriv *upriv;
4937dd7cddfSDavid du Colombier 	Fs *f;
494dc5a79c1SDavid du Colombier 	uchar *p;
4957dd7cddfSDavid du Colombier 
4967dd7cddfSDavid du Colombier 	upriv = rudp->priv;
4977dd7cddfSDavid du Colombier 	f = rudp->f;
4987dd7cddfSDavid du Colombier 
4997dd7cddfSDavid du Colombier 	upriv->ustats.rudpInDatagrams++;
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 	uh = (Udphdr*)(bp->rp);
5027dd7cddfSDavid du Colombier 
5037dd7cddfSDavid du Colombier 	/* Put back pseudo header for checksum
5047dd7cddfSDavid du Colombier 	 * (remember old values for icmpnoconv())
5057dd7cddfSDavid du Colombier 	 */
5067dd7cddfSDavid du Colombier 	ottl = uh->Unused;
5077dd7cddfSDavid du Colombier 	uh->Unused = 0;
5087dd7cddfSDavid du Colombier 	len = nhgets(uh->udplen);
5097dd7cddfSDavid du Colombier 	olen = nhgets(uh->udpplen);
5107dd7cddfSDavid du Colombier 	hnputs(uh->udpplen, len);
5117dd7cddfSDavid du Colombier 
5127dd7cddfSDavid du Colombier 	v4tov6(raddr, uh->udpsrc);
5137dd7cddfSDavid du Colombier 	v4tov6(laddr, uh->udpdst);
5147dd7cddfSDavid du Colombier 	lport = nhgets(uh->udpdport);
5157dd7cddfSDavid du Colombier 	rport = nhgets(uh->udpsport);
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier 	if(nhgets(uh->udpcksum)) {
5187dd7cddfSDavid du Colombier 		if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) {
5197dd7cddfSDavid du Colombier 			upriv->ustats.rudpInErrors++;
5207dd7cddfSDavid du Colombier 			upriv->csumerr++;
5217dd7cddfSDavid du Colombier 			netlog(f, Logrudp, "rudp: checksum error %I\n", raddr);
5227dd7cddfSDavid du Colombier 			DPRINT("rudp: checksum error %I\n", raddr);
5237dd7cddfSDavid du Colombier 			freeblist(bp);
5247dd7cddfSDavid du Colombier 			return;
5257dd7cddfSDavid du Colombier 		}
5267dd7cddfSDavid du Colombier 	}
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	qlock(rudp);
5297dd7cddfSDavid du Colombier 
53080ee5cbfSDavid du Colombier 	c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
53180ee5cbfSDavid du Colombier 	if(c == nil){
53280ee5cbfSDavid du Colombier 		/* no converstation found */
5337dd7cddfSDavid du Colombier 		upriv->ustats.rudpNoPorts++;
53480ee5cbfSDavid du Colombier 		qunlock(rudp);
53580ee5cbfSDavid du Colombier 		netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
5367dd7cddfSDavid du Colombier 			laddr, lport);
5377dd7cddfSDavid du Colombier 		uh->Unused = ottl;
5387dd7cddfSDavid du Colombier 		hnputs(uh->udpplen, olen);
5397dd7cddfSDavid du Colombier 		icmpnoconv(f, bp);
5407dd7cddfSDavid du Colombier 		freeblist(bp);
5417dd7cddfSDavid du Colombier 		return;
5427dd7cddfSDavid du Colombier 	}
5437dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
5447dd7cddfSDavid du Colombier 	qlock(ucb);
5457dd7cddfSDavid du Colombier 	qunlock(rudp);
5467dd7cddfSDavid du Colombier 
5477dd7cddfSDavid du Colombier 	if(reliput(c, bp, raddr, rport) < 0){
5487dd7cddfSDavid du Colombier 		qunlock(ucb);
5497dd7cddfSDavid du Colombier 		freeb(bp);
5507dd7cddfSDavid du Colombier 		return;
5517dd7cddfSDavid du Colombier 	}
5527dd7cddfSDavid du Colombier 
5537dd7cddfSDavid du Colombier 	/*
5547dd7cddfSDavid du Colombier 	 * Trim the packet down to data size
5557dd7cddfSDavid du Colombier 	 */
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	len -= (UDP_RHDRSIZE-UDP_PHDRSIZE);
5587dd7cddfSDavid du Colombier 	bp = trimblock(bp, UDP_IPHDR+UDP_RHDRSIZE, len);
5597dd7cddfSDavid du Colombier 	if(bp == nil) {
5607dd7cddfSDavid du Colombier 		netlog(f, Logrudp, "rudp: len err %I.%d -> %I.%d\n",
5617dd7cddfSDavid du Colombier 			raddr, rport, laddr, lport);
5627dd7cddfSDavid du Colombier 		DPRINT("rudp: len err %I.%d -> %I.%d\n",
5637dd7cddfSDavid du Colombier 			raddr, rport, laddr, lport);
5647dd7cddfSDavid du Colombier 		upriv->lenerr++;
5657dd7cddfSDavid du Colombier 		return;
5667dd7cddfSDavid du Colombier 	}
5677dd7cddfSDavid du Colombier 
5687dd7cddfSDavid du Colombier 	netlog(f, Logrudpmsg, "rudp: %I.%d -> %I.%d l %d\n",
5697dd7cddfSDavid du Colombier 		raddr, rport, laddr, lport, len);
5707dd7cddfSDavid du Colombier 
5717dd7cddfSDavid du Colombier 	switch(ucb->headers){
572dc5a79c1SDavid du Colombier 	case 7:
573dc5a79c1SDavid du Colombier 		/* pass the src address */
574dc5a79c1SDavid du Colombier 		bp = padblock(bp, UDP_USEAD7);
575dc5a79c1SDavid du Colombier 		p = bp->rp;
576dc5a79c1SDavid du Colombier 		ipmove(p, raddr); p += IPaddrlen;
577dc5a79c1SDavid du Colombier 		ipmove(p, laddr); p += IPaddrlen;
578dc5a79c1SDavid du Colombier 		ipmove(p, ifc->lifc->local); p += IPaddrlen;
579dc5a79c1SDavid du Colombier 		hnputs(p, rport); p += 2;
580dc5a79c1SDavid du Colombier 		hnputs(p, lport);
581dc5a79c1SDavid du Colombier 		break;
582f2c197d9SDavid du Colombier 	case 6:					/* OBS */
5837dd7cddfSDavid du Colombier 		/* pass the src address */
5847dd7cddfSDavid du Colombier 		bp = padblock(bp, UDP_USEAD6);
585dc5a79c1SDavid du Colombier 		p = bp->rp;
586dc5a79c1SDavid du Colombier 		ipmove(p, raddr); p += IPaddrlen;
587dc5a79c1SDavid du Colombier 		ipmove(p, ipforme(f, laddr)==Runi ? laddr : ifc->lifc->local); p += IPaddrlen;
588dc5a79c1SDavid du Colombier 		hnputs(p, rport); p += 2;
589dc5a79c1SDavid du Colombier 		hnputs(p, lport);
5907dd7cddfSDavid du Colombier 		break;
5917dd7cddfSDavid du Colombier 	default:
5927dd7cddfSDavid du Colombier 		/* connection oriented rudp */
5937dd7cddfSDavid du Colombier 		if(ipcmp(c->raddr, IPnoaddr) == 0){
5947dd7cddfSDavid du Colombier 			/* save the src address in the conversation */
5957dd7cddfSDavid du Colombier 		 	ipmove(c->raddr, raddr);
5967dd7cddfSDavid du Colombier 			c->rport = rport;
5977dd7cddfSDavid du Colombier 
5987dd7cddfSDavid du Colombier 			/* reply with the same ip address (if not broadcast) */
5997dd7cddfSDavid du Colombier 			if(ipforme(f, laddr) == Runi)
6007dd7cddfSDavid du Colombier 				ipmove(c->laddr, laddr);
6017dd7cddfSDavid du Colombier 			else
6029a747e4fSDavid du Colombier 				v4tov6(c->laddr, ifc->lifc->local);
6037dd7cddfSDavid du Colombier 		}
6047dd7cddfSDavid du Colombier 		break;
6057dd7cddfSDavid du Colombier 	}
6067dd7cddfSDavid du Colombier 	if(bp->next)
6077dd7cddfSDavid du Colombier 		bp = concatblock(bp);
6087dd7cddfSDavid du Colombier 
6097dd7cddfSDavid du Colombier 	if(qfull(c->rq)) {
6107dd7cddfSDavid du Colombier 		netlog(f, Logrudp, "rudp: qfull %I.%d -> %I.%d\n", raddr, rport,
6117dd7cddfSDavid du Colombier 			laddr, lport);
6127dd7cddfSDavid du Colombier 		freeblist(bp);
6137dd7cddfSDavid du Colombier 	}
6147dd7cddfSDavid du Colombier 	else
6157dd7cddfSDavid du Colombier 		qpass(c->rq, bp);
6167dd7cddfSDavid du Colombier 
6177dd7cddfSDavid du Colombier 	qunlock(ucb);
6187dd7cddfSDavid du Colombier }
6197dd7cddfSDavid du Colombier 
6207dd7cddfSDavid du Colombier static char *rudpunknown = "unknown rudp ctl request";
6217dd7cddfSDavid du Colombier 
6227dd7cddfSDavid du Colombier char*
6237dd7cddfSDavid du Colombier rudpctl(Conv *c, char **f, int n)
6247dd7cddfSDavid du Colombier {
6257dd7cddfSDavid du Colombier 	Rudpcb *ucb;
6267dd7cddfSDavid du Colombier 	uchar ip[IPaddrlen];
6277dd7cddfSDavid du Colombier 	int x;
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
6307dd7cddfSDavid du Colombier 	if(n < 1)
6317dd7cddfSDavid du Colombier 		return rudpunknown;
6327dd7cddfSDavid du Colombier 
633dc5a79c1SDavid du Colombier 	if(strcmp(f[0], "headers++4") == 0){
634f2c197d9SDavid du Colombier 		ucb->headers = 7;		/* new headers format */
6357dd7cddfSDavid du Colombier 		return nil;
636f2c197d9SDavid du Colombier 	} else if(strcmp(f[0], "headers") == 0){	/* OBS */
6377dd7cddfSDavid du Colombier 		ucb->headers = 6;
6387dd7cddfSDavid du Colombier 		return nil;
6397dd7cddfSDavid du Colombier 	} else if(strcmp(f[0], "hangup") == 0){
6407dd7cddfSDavid du Colombier 		if(n < 3)
6417dd7cddfSDavid du Colombier 			return "bad syntax";
6427dd7cddfSDavid du Colombier 		parseip(ip, f[1]);
6437dd7cddfSDavid du Colombier 		x = atoi(f[2]);
6447dd7cddfSDavid du Colombier 		qlock(ucb);
6457dd7cddfSDavid du Colombier 		relforget(c, ip, x, 1);
6467dd7cddfSDavid du Colombier 		qunlock(ucb);
6477dd7cddfSDavid du Colombier 		return nil;
6487dd7cddfSDavid du Colombier 	} else if(strcmp(f[0], "randdrop") == 0){
6497dd7cddfSDavid du Colombier 		x = 10;			/* default is 10% */
6507dd7cddfSDavid du Colombier 		if(n > 1)
6517dd7cddfSDavid du Colombier 			x = atoi(f[1]);
6527dd7cddfSDavid du Colombier 		if(x > 100 || x < 0)
6537dd7cddfSDavid du Colombier 			return "illegal rudp drop rate";
6547dd7cddfSDavid du Colombier 		ucb->randdrop = x;
6557dd7cddfSDavid du Colombier 		return nil;
6567dd7cddfSDavid du Colombier 	}
6577dd7cddfSDavid du Colombier 	return rudpunknown;
6587dd7cddfSDavid du Colombier }
6597dd7cddfSDavid du Colombier 
6607dd7cddfSDavid du Colombier void
6617dd7cddfSDavid du Colombier rudpadvise(Proto *rudp, Block *bp, char *msg)
6627dd7cddfSDavid du Colombier {
6637dd7cddfSDavid du Colombier 	Udphdr *h;
6647dd7cddfSDavid du Colombier 	uchar source[IPaddrlen], dest[IPaddrlen];
6657dd7cddfSDavid du Colombier 	ushort psource, pdest;
6667dd7cddfSDavid du Colombier 	Conv *s, **p;
6677dd7cddfSDavid du Colombier 
6687dd7cddfSDavid du Colombier 	h = (Udphdr*)(bp->rp);
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier 	v4tov6(dest, h->udpdst);
6717dd7cddfSDavid du Colombier 	v4tov6(source, h->udpsrc);
6727dd7cddfSDavid du Colombier 	psource = nhgets(h->udpsport);
6737dd7cddfSDavid du Colombier 	pdest = nhgets(h->udpdport);
6747dd7cddfSDavid du Colombier 
6757dd7cddfSDavid du Colombier 	/* Look for a connection */
6767dd7cddfSDavid du Colombier 	for(p = rudp->conv; *p; p++) {
6777dd7cddfSDavid du Colombier 		s = *p;
6787dd7cddfSDavid du Colombier 		if(s->rport == pdest)
6797dd7cddfSDavid du Colombier 		if(s->lport == psource)
6807dd7cddfSDavid du Colombier 		if(ipcmp(s->raddr, dest) == 0)
6817dd7cddfSDavid du Colombier 		if(ipcmp(s->laddr, source) == 0){
6827dd7cddfSDavid du Colombier 			qhangup(s->rq, msg);
6837dd7cddfSDavid du Colombier 			qhangup(s->wq, msg);
6847dd7cddfSDavid du Colombier 			break;
6857dd7cddfSDavid du Colombier 		}
6867dd7cddfSDavid du Colombier 	}
6877dd7cddfSDavid du Colombier 	freeblist(bp);
6887dd7cddfSDavid du Colombier }
6897dd7cddfSDavid du Colombier 
6907dd7cddfSDavid du Colombier int
6917dd7cddfSDavid du Colombier rudpstats(Proto *rudp, char *buf, int len)
6927dd7cddfSDavid du Colombier {
6937dd7cddfSDavid du Colombier 	Rudppriv *upriv;
6947dd7cddfSDavid du Colombier 
6957dd7cddfSDavid du Colombier 	upriv = rudp->priv;
6967dd7cddfSDavid du Colombier 	return snprint(buf, len, "%lud %lud %lud %lud %lud %lud\n",
6977dd7cddfSDavid du Colombier 		upriv->ustats.rudpInDatagrams,
6987dd7cddfSDavid du Colombier 		upriv->ustats.rudpNoPorts,
6997dd7cddfSDavid du Colombier 		upriv->ustats.rudpInErrors,
7007dd7cddfSDavid du Colombier 		upriv->ustats.rudpOutDatagrams,
7017dd7cddfSDavid du Colombier 		upriv->rxmits,
7027dd7cddfSDavid du Colombier 		upriv->orders);
7037dd7cddfSDavid du Colombier }
7047dd7cddfSDavid du Colombier 
7057dd7cddfSDavid du Colombier void
7067dd7cddfSDavid du Colombier rudpinit(Fs *fs)
7077dd7cddfSDavid du Colombier {
7087dd7cddfSDavid du Colombier 
7097dd7cddfSDavid du Colombier 	Proto *rudp;
7107dd7cddfSDavid du Colombier 
7117dd7cddfSDavid du Colombier 	rudp = smalloc(sizeof(Proto));
7127dd7cddfSDavid du Colombier 	rudp->priv = smalloc(sizeof(Rudppriv));
7137dd7cddfSDavid du Colombier 	rudp->name = "rudp";
7147dd7cddfSDavid du Colombier 	rudp->connect = rudpconnect;
7157dd7cddfSDavid du Colombier 	rudp->announce = rudpannounce;
7167dd7cddfSDavid du Colombier 	rudp->ctl = rudpctl;
7177dd7cddfSDavid du Colombier 	rudp->state = rudpstate;
7187dd7cddfSDavid du Colombier 	rudp->create = rudpcreate;
7197dd7cddfSDavid du Colombier 	rudp->close = rudpclose;
7207dd7cddfSDavid du Colombier 	rudp->rcv = rudpiput;
7217dd7cddfSDavid du Colombier 	rudp->advise = rudpadvise;
7227dd7cddfSDavid du Colombier 	rudp->stats = rudpstats;
7237dd7cddfSDavid du Colombier 	rudp->ipproto = IP_UDPPROTO;
7247dd7cddfSDavid du Colombier 	rudp->nc = 16;
7257dd7cddfSDavid du Colombier 	rudp->ptclsize = sizeof(Rudpcb);
7267dd7cddfSDavid du Colombier 
7277dd7cddfSDavid du Colombier 	Fsproto(fs, rudp);
7287dd7cddfSDavid du Colombier }
7297dd7cddfSDavid du Colombier 
7307dd7cddfSDavid du Colombier /*********************************************/
7317dd7cddfSDavid du Colombier /* Here starts the reliable helper functions */
7327dd7cddfSDavid du Colombier /*********************************************/
7337dd7cddfSDavid du Colombier /*
7347dd7cddfSDavid du Colombier  *  Enqueue a copy of an unacked block for possible retransmissions
7357dd7cddfSDavid du Colombier  */
7367dd7cddfSDavid du Colombier void
7377dd7cddfSDavid du Colombier relackq(Reliable *r, Block *bp)
7387dd7cddfSDavid du Colombier {
7397dd7cddfSDavid du Colombier 	Block *np;
7407dd7cddfSDavid du Colombier 
7417dd7cddfSDavid du Colombier 	np = copyblock(bp, blocklen(bp));
7427dd7cddfSDavid du Colombier 	if(r->unacked)
7437dd7cddfSDavid du Colombier 		r->unackedtail->list = np;
7447dd7cddfSDavid du Colombier 	else {
7457dd7cddfSDavid du Colombier 		/* restart timer */
7467dd7cddfSDavid du Colombier 		r->timeout = 0;
7477dd7cddfSDavid du Colombier 		r->xmits = 1;
7487dd7cddfSDavid du Colombier 		r->unacked = np;
7497dd7cddfSDavid du Colombier 	}
7507dd7cddfSDavid du Colombier 	r->unackedtail = np;
7517dd7cddfSDavid du Colombier 	np->list = nil;
7527dd7cddfSDavid du Colombier }
7537dd7cddfSDavid du Colombier 
7547dd7cddfSDavid du Colombier /*
7557dd7cddfSDavid du Colombier  *  retransmit unacked blocks
7567dd7cddfSDavid du Colombier  */
7577dd7cddfSDavid du Colombier void
7587dd7cddfSDavid du Colombier relackproc(void *a)
7597dd7cddfSDavid du Colombier {
7607dd7cddfSDavid du Colombier 	Rudpcb *ucb;
7617dd7cddfSDavid du Colombier 	Proto *rudp;
7627dd7cddfSDavid du Colombier 	Reliable *r;
7637dd7cddfSDavid du Colombier 	Conv **s, *c;
7647dd7cddfSDavid du Colombier 
7657dd7cddfSDavid du Colombier 	rudp = (Proto *)a;
7667dd7cddfSDavid du Colombier 
7677dd7cddfSDavid du Colombier loop:
768dc5a79c1SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Rudptickms);
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier 	for(s = rudp->conv; *s; s++) {
7717dd7cddfSDavid du Colombier 		c = *s;
7727dd7cddfSDavid du Colombier 		ucb = (Rudpcb*)c->ptcl;
7737dd7cddfSDavid du Colombier 		qlock(ucb);
7747dd7cddfSDavid du Colombier 
7757dd7cddfSDavid du Colombier 		for(r = ucb->r; r; r = r->next) {
7767dd7cddfSDavid du Colombier 			if(r->unacked != nil){
7777dd7cddfSDavid du Colombier 				r->timeout += Rudptickms;
7787dd7cddfSDavid du Colombier 				if(r->timeout > Rudprxms*r->xmits)
7797dd7cddfSDavid du Colombier 					relrexmit(c, r);
7807dd7cddfSDavid du Colombier 			}
7817dd7cddfSDavid du Colombier 			if(r->acksent != r->rcvseq)
7827dd7cddfSDavid du Colombier 				relsendack(c, r, 0);
7837dd7cddfSDavid du Colombier 		}
7847dd7cddfSDavid du Colombier 		qunlock(ucb);
7857dd7cddfSDavid du Colombier 	}
7867dd7cddfSDavid du Colombier 	goto loop;
7877dd7cddfSDavid du Colombier }
7887dd7cddfSDavid du Colombier 
7897dd7cddfSDavid du Colombier /*
7907dd7cddfSDavid du Colombier  *  get the state record for a conversation
7917dd7cddfSDavid du Colombier  */
7927dd7cddfSDavid du Colombier Reliable*
7937dd7cddfSDavid du Colombier relstate(Rudpcb *ucb, uchar *addr, ushort port, char *from)
7947dd7cddfSDavid du Colombier {
7957dd7cddfSDavid du Colombier 	Reliable *r, **l;
7967dd7cddfSDavid du Colombier 
7977dd7cddfSDavid du Colombier 	l = &ucb->r;
7987dd7cddfSDavid du Colombier 	for(r = *l; r; r = *l){
7997dd7cddfSDavid du Colombier 		if(memcmp(addr, r->addr, IPaddrlen) == 0 &&
8007dd7cddfSDavid du Colombier 		    port == r->port)
8017dd7cddfSDavid du Colombier 			break;
8027dd7cddfSDavid du Colombier 		l = &r->next;
8037dd7cddfSDavid du Colombier 	}
8047dd7cddfSDavid du Colombier 
8057dd7cddfSDavid du Colombier 	/* no state for this addr/port, create some */
8067dd7cddfSDavid du Colombier 	if(r == nil){
8077dd7cddfSDavid du Colombier 		while(generation == 0)
8087dd7cddfSDavid du Colombier 			generation = rand();
8097dd7cddfSDavid du Colombier 
8107dd7cddfSDavid du Colombier 		DPRINT("from %s new state %lud for %I!%ud\n",
8117dd7cddfSDavid du Colombier 		        from, generation, addr, port);
8127dd7cddfSDavid du Colombier 
8137dd7cddfSDavid du Colombier 		r = smalloc(sizeof(Reliable));
8147dd7cddfSDavid du Colombier 		memmove(r->addr, addr, IPaddrlen);
8157dd7cddfSDavid du Colombier 		r->port = port;
8167dd7cddfSDavid du Colombier 		r->unacked = 0;
8177dd7cddfSDavid du Colombier 		if(generation == Hangupgen)
8187dd7cddfSDavid du Colombier 			generation++;
8197dd7cddfSDavid du Colombier 		r->sndgen = generation++;
8207dd7cddfSDavid du Colombier 		r->sndseq = 0;
8217dd7cddfSDavid du Colombier 		r->ackrcvd = 0;
8227dd7cddfSDavid du Colombier 		r->rcvgen = 0;
8237dd7cddfSDavid du Colombier 		r->rcvseq = 0;
8247dd7cddfSDavid du Colombier 		r->acksent = 0;
8257dd7cddfSDavid du Colombier 		r->xmits = 0;
8267dd7cddfSDavid du Colombier 		r->timeout = 0;
8277dd7cddfSDavid du Colombier 		r->ref = 0;
8287dd7cddfSDavid du Colombier 		incref(r);	/* one reference for being in the list */
8297dd7cddfSDavid du Colombier 
8307dd7cddfSDavid du Colombier 		*l = r;
8317dd7cddfSDavid du Colombier 	}
8327dd7cddfSDavid du Colombier 
8337dd7cddfSDavid du Colombier 	incref(r);
8347dd7cddfSDavid du Colombier 	return r;
8357dd7cddfSDavid du Colombier }
8367dd7cddfSDavid du Colombier 
8377dd7cddfSDavid du Colombier void
8387dd7cddfSDavid du Colombier relput(Reliable *r)
8397dd7cddfSDavid du Colombier {
8407dd7cddfSDavid du Colombier 	if(decref(r) == 0)
8417dd7cddfSDavid du Colombier 		free(r);
8427dd7cddfSDavid du Colombier }
8437dd7cddfSDavid du Colombier 
8447dd7cddfSDavid du Colombier /*
8457dd7cddfSDavid du Colombier  *  forget a Reliable state
8467dd7cddfSDavid du Colombier  */
8477dd7cddfSDavid du Colombier void
8487dd7cddfSDavid du Colombier relforget(Conv *c, uchar *ip, int port, int originator)
8497dd7cddfSDavid du Colombier {
8507dd7cddfSDavid du Colombier 	Rudpcb *ucb;
8517dd7cddfSDavid du Colombier 	Reliable *r, **l;
8527dd7cddfSDavid du Colombier 
8537dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
8547dd7cddfSDavid du Colombier 
8557dd7cddfSDavid du Colombier 	l = &ucb->r;
8567dd7cddfSDavid du Colombier 	for(r = *l; r; r = *l){
8577dd7cddfSDavid du Colombier 		if(ipcmp(ip, r->addr) == 0 && port == r->port){
8587dd7cddfSDavid du Colombier 			*l = r->next;
8597dd7cddfSDavid du Colombier 			if(originator)
8607dd7cddfSDavid du Colombier 				relsendack(c, r, 1);
8617dd7cddfSDavid du Colombier 			relhangup(c, r);
8627dd7cddfSDavid du Colombier 			relput(r);	/* remove from the list */
8637dd7cddfSDavid du Colombier 			break;
8647dd7cddfSDavid du Colombier 		}
8657dd7cddfSDavid du Colombier 		l = &r->next;
8667dd7cddfSDavid du Colombier 	}
8677dd7cddfSDavid du Colombier }
8687dd7cddfSDavid du Colombier 
8697dd7cddfSDavid du Colombier /*
8707dd7cddfSDavid du Colombier  *  process a rcvd reliable packet. return -1 if not to be passed to user process,
8717dd7cddfSDavid du Colombier  *  0 therwise.
8727dd7cddfSDavid du Colombier  *
8737dd7cddfSDavid du Colombier  *  called with ucb locked.
8747dd7cddfSDavid du Colombier  */
8757dd7cddfSDavid du Colombier int
8767dd7cddfSDavid du Colombier reliput(Conv *c, Block *bp, uchar *addr, ushort port)
8777dd7cddfSDavid du Colombier {
8787dd7cddfSDavid du Colombier 	Block *nbp;
8797dd7cddfSDavid du Colombier 	Rudpcb *ucb;
8807dd7cddfSDavid du Colombier 	Rudppriv *upriv;
8817dd7cddfSDavid du Colombier 	Udphdr *uh;
8827dd7cddfSDavid du Colombier 	Reliable *r;
8837dd7cddfSDavid du Colombier 	Rudphdr *rh;
8847dd7cddfSDavid du Colombier 	ulong seq, ack, sgen, agen, ackreal;
8857dd7cddfSDavid du Colombier 	int rv = -1;
8867dd7cddfSDavid du Colombier 
8877dd7cddfSDavid du Colombier 	/* get fields */
8887dd7cddfSDavid du Colombier 	uh = (Udphdr*)(bp->rp);
8897dd7cddfSDavid du Colombier 	rh = (Rudphdr*)uh;
8907dd7cddfSDavid du Colombier 	seq = nhgetl(rh->relseq);
8917dd7cddfSDavid du Colombier 	sgen = nhgetl(rh->relsgen);
8927dd7cddfSDavid du Colombier 	ack = nhgetl(rh->relack);
8937dd7cddfSDavid du Colombier 	agen = nhgetl(rh->relagen);
8947dd7cddfSDavid du Colombier 
8957dd7cddfSDavid du Colombier 	upriv = c->p->priv;
8967dd7cddfSDavid du Colombier 	ucb = (Rudpcb*)c->ptcl;
8977dd7cddfSDavid du Colombier 	r = relstate(ucb, addr, port, "input");
8987dd7cddfSDavid du Colombier 
8997dd7cddfSDavid du Colombier 	DPRINT("rcvd %lud/%lud, %lud/%lud, r->sndgen = %lud\n",
9007dd7cddfSDavid du Colombier 		seq, sgen, ack, agen, r->sndgen);
9017dd7cddfSDavid du Colombier 
9027dd7cddfSDavid du Colombier 	/* if acking an incorrect generation, ignore */
9037dd7cddfSDavid du Colombier 	if(ack && agen != r->sndgen)
9047dd7cddfSDavid du Colombier 		goto out;
9057dd7cddfSDavid du Colombier 
9067dd7cddfSDavid du Colombier 	/* Look for a hangup */
9077dd7cddfSDavid du Colombier 	if(sgen == Hangupgen) {
9087dd7cddfSDavid du Colombier 		if(agen == r->sndgen)
9097dd7cddfSDavid du Colombier 			relforget(c, addr, port, 0);
9107dd7cddfSDavid du Colombier 		goto out;
9117dd7cddfSDavid du Colombier 	}
9127dd7cddfSDavid du Colombier 
9137dd7cddfSDavid du Colombier 	/* make sure we're not talking to a new remote side */
9147dd7cddfSDavid du Colombier 	if(r->rcvgen != sgen){
9157dd7cddfSDavid du Colombier 		if(seq != 0 && seq != 1)
9167dd7cddfSDavid du Colombier 			goto out;
9177dd7cddfSDavid du Colombier 
9187dd7cddfSDavid du Colombier 		/* new connection */
9197dd7cddfSDavid du Colombier 		if(r->rcvgen != 0){
9207dd7cddfSDavid du Colombier 			DPRINT("new con r->rcvgen = %lud, sgen = %lud\n", r->rcvgen, sgen);
9217dd7cddfSDavid du Colombier 			relhangup(c, r);
9227dd7cddfSDavid du Colombier 		}
9237dd7cddfSDavid du Colombier 		r->rcvgen = sgen;
9247dd7cddfSDavid du Colombier 	}
9257dd7cddfSDavid du Colombier 
9267dd7cddfSDavid du Colombier 	/* dequeue acked packets */
9277dd7cddfSDavid du Colombier 	if(ack && agen == r->sndgen){
9287dd7cddfSDavid du Colombier 		ackreal = 0;
9297dd7cddfSDavid du Colombier 		while(r->unacked != nil && INSEQ(ack, r->ackrcvd, r->sndseq)){
9307dd7cddfSDavid du Colombier 			nbp = r->unacked;
9317dd7cddfSDavid du Colombier 			r->unacked = nbp->list;
9327dd7cddfSDavid du Colombier 			DPRINT("%lud/%lud acked, r->sndgen = %lud\n",
9337dd7cddfSDavid du Colombier 			       ack, agen, r->sndgen);
9347dd7cddfSDavid du Colombier 			freeb(nbp);
9357dd7cddfSDavid du Colombier 			r->ackrcvd = NEXTSEQ(r->ackrcvd);
9367dd7cddfSDavid du Colombier 			ackreal = 1;
9377dd7cddfSDavid du Colombier 		}
9387dd7cddfSDavid du Colombier 
9397dd7cddfSDavid du Colombier 		/* flow control */
9407dd7cddfSDavid du Colombier 		if(UNACKED(r) < Maxunacked/8 && r->blocked)
9417dd7cddfSDavid du Colombier 			wakeup(&r->vous);
9427dd7cddfSDavid du Colombier 
9437dd7cddfSDavid du Colombier 		/*
9447dd7cddfSDavid du Colombier 		 *  retransmit next packet if the acked packet
9457dd7cddfSDavid du Colombier 		 *  was transmitted more than once
9467dd7cddfSDavid du Colombier 		 */
9477dd7cddfSDavid du Colombier 		if(ackreal && r->unacked != nil){
9487dd7cddfSDavid du Colombier 			r->timeout = 0;
9497dd7cddfSDavid du Colombier 			if(r->xmits > 1){
9507dd7cddfSDavid du Colombier 				r->xmits = 1;
9517dd7cddfSDavid du Colombier 				relrexmit(c, r);
9527dd7cddfSDavid du Colombier 			}
9537dd7cddfSDavid du Colombier 		}
9547dd7cddfSDavid du Colombier 
9557dd7cddfSDavid du Colombier 	}
9567dd7cddfSDavid du Colombier 
9577dd7cddfSDavid du Colombier 	/* no message or input queue full */
9587dd7cddfSDavid du Colombier 	if(seq == 0 || qfull(c->rq))
9597dd7cddfSDavid du Colombier 		goto out;
9607dd7cddfSDavid du Colombier 
9617dd7cddfSDavid du Colombier 	/* refuse out of order delivery */
9627dd7cddfSDavid du Colombier 	if(seq != NEXTSEQ(r->rcvseq)){
9637dd7cddfSDavid du Colombier 		relsendack(c, r, 0);	/* tell him we got it already */
9647dd7cddfSDavid du Colombier 		upriv->orders++;
9657dd7cddfSDavid du Colombier 		DPRINT("out of sequence %lud not %lud\n", seq, NEXTSEQ(r->rcvseq));
9667dd7cddfSDavid du Colombier 		goto out;
9677dd7cddfSDavid du Colombier 	}
9687dd7cddfSDavid du Colombier 	r->rcvseq = seq;
9697dd7cddfSDavid du Colombier 
9707dd7cddfSDavid du Colombier 	rv = 0;
9717dd7cddfSDavid du Colombier out:
9727dd7cddfSDavid du Colombier 	relput(r);
9737dd7cddfSDavid du Colombier 	return rv;
9747dd7cddfSDavid du Colombier }
9757dd7cddfSDavid du Colombier 
9767dd7cddfSDavid du Colombier void
9777dd7cddfSDavid du Colombier relsendack(Conv *c, Reliable *r, int hangup)
9787dd7cddfSDavid du Colombier {
9797dd7cddfSDavid du Colombier 	Udphdr *uh;
9807dd7cddfSDavid du Colombier 	Block *bp;
9817dd7cddfSDavid du Colombier 	Rudphdr *rh;
9827dd7cddfSDavid du Colombier 	int ptcllen;
9837dd7cddfSDavid du Colombier 	Fs *f;
9847dd7cddfSDavid du Colombier 
9857dd7cddfSDavid du Colombier 	bp = allocb(UDP_IPHDR + UDP_RHDRSIZE);
9867dd7cddfSDavid du Colombier 	if(bp == nil)
9877dd7cddfSDavid du Colombier 		return;
9887dd7cddfSDavid du Colombier 	bp->wp += UDP_IPHDR + UDP_RHDRSIZE;
9897dd7cddfSDavid du Colombier 	f = c->p->f;
9907dd7cddfSDavid du Colombier 	uh = (Udphdr *)(bp->rp);
9913ff48bf5SDavid du Colombier 	uh->vihl = IP_VER4;
9927dd7cddfSDavid du Colombier 	rh = (Rudphdr*)uh;
9937dd7cddfSDavid du Colombier 
9947dd7cddfSDavid du Colombier 	ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE);
9957dd7cddfSDavid du Colombier 	uh->Unused = 0;
9967dd7cddfSDavid du Colombier 	uh->udpproto = IP_UDPPROTO;
9977dd7cddfSDavid du Colombier 	uh->frag[0] = 0;
9987dd7cddfSDavid du Colombier 	uh->frag[1] = 0;
9997dd7cddfSDavid du Colombier 	hnputs(uh->udpplen, ptcllen);
10007dd7cddfSDavid du Colombier 
10017dd7cddfSDavid du Colombier 	v6tov4(uh->udpdst, r->addr);
10027dd7cddfSDavid du Colombier 	hnputs(uh->udpdport, r->port);
10037dd7cddfSDavid du Colombier 	hnputs(uh->udpsport, c->lport);
10047dd7cddfSDavid du Colombier 	if(ipcmp(c->laddr, IPnoaddr) == 0)
10057dd7cddfSDavid du Colombier 		findlocalip(f, c->laddr, c->raddr);
10067dd7cddfSDavid du Colombier 	v6tov4(uh->udpsrc, c->laddr);
10077dd7cddfSDavid du Colombier 	hnputs(uh->udplen, ptcllen);
10087dd7cddfSDavid du Colombier 
10097dd7cddfSDavid du Colombier 	if(hangup)
10107dd7cddfSDavid du Colombier 		hnputl(rh->relsgen, Hangupgen);
10117dd7cddfSDavid du Colombier 	else
10127dd7cddfSDavid du Colombier 		hnputl(rh->relsgen, r->sndgen);
10137dd7cddfSDavid du Colombier 	hnputl(rh->relseq, 0);
10147dd7cddfSDavid du Colombier 	hnputl(rh->relagen, r->rcvgen);
10157dd7cddfSDavid du Colombier 	hnputl(rh->relack, r->rcvseq);
10167dd7cddfSDavid du Colombier 
10177dd7cddfSDavid du Colombier 	if(r->acksent < r->rcvseq)
10187dd7cddfSDavid du Colombier 		r->acksent = r->rcvseq;
10197dd7cddfSDavid du Colombier 
10207dd7cddfSDavid du Colombier 	uh->udpcksum[0] = 0;
10217dd7cddfSDavid du Colombier 	uh->udpcksum[1] = 0;
10227dd7cddfSDavid du Colombier 	hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, UDP_RHDRSIZE));
10237dd7cddfSDavid du Colombier 
10247dd7cddfSDavid du Colombier 	DPRINT("sendack: %lud/%lud, %lud/%lud\n", 0L, r->sndgen, r->rcvseq, r->rcvgen);
10257dd7cddfSDavid du Colombier 	doipoput(c, f, bp, 0, c->ttl, c->tos);
10267dd7cddfSDavid du Colombier }
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier 
10297dd7cddfSDavid du Colombier /*
10307dd7cddfSDavid du Colombier  *  called with ucb locked (and c locked if user initiated close)
10317dd7cddfSDavid du Colombier  */
10327dd7cddfSDavid du Colombier void
10337dd7cddfSDavid du Colombier relhangup(Conv *c, Reliable *r)
10347dd7cddfSDavid du Colombier {
10357dd7cddfSDavid du Colombier 	int n;
10367dd7cddfSDavid du Colombier 	Block *bp;
10379a747e4fSDavid du Colombier 	char hup[ERRMAX];
10387dd7cddfSDavid du Colombier 
10397dd7cddfSDavid du Colombier 	n = snprint(hup, sizeof(hup), "hangup %I!%d", r->addr, r->port);
10407dd7cddfSDavid du Colombier 	qproduce(c->eq, hup, n);
10417dd7cddfSDavid du Colombier 
10427dd7cddfSDavid du Colombier 	/*
10437dd7cddfSDavid du Colombier 	 *  dump any unacked outgoing messages
10447dd7cddfSDavid du Colombier 	 */
10457dd7cddfSDavid du Colombier 	for(bp = r->unacked; bp != nil; bp = r->unacked){
10467dd7cddfSDavid du Colombier 		r->unacked = bp->list;
10477dd7cddfSDavid du Colombier 		bp->list = nil;
10487dd7cddfSDavid du Colombier 		freeb(bp);
10497dd7cddfSDavid du Colombier 	}
10507dd7cddfSDavid du Colombier 
10517dd7cddfSDavid du Colombier 	r->rcvgen = 0;
10527dd7cddfSDavid du Colombier 	r->rcvseq = 0;
10537dd7cddfSDavid du Colombier 	r->acksent = 0;
10547dd7cddfSDavid du Colombier 	if(generation == Hangupgen)
10557dd7cddfSDavid du Colombier 		generation++;
10567dd7cddfSDavid du Colombier 	r->sndgen = generation++;
10577dd7cddfSDavid du Colombier 	r->sndseq = 0;
10587dd7cddfSDavid du Colombier 	r->ackrcvd = 0;
10597dd7cddfSDavid du Colombier 	r->xmits = 0;
10607dd7cddfSDavid du Colombier 	r->timeout = 0;
10617dd7cddfSDavid du Colombier 	wakeup(&r->vous);
10627dd7cddfSDavid du Colombier }
10637dd7cddfSDavid du Colombier 
10647dd7cddfSDavid du Colombier /*
10657dd7cddfSDavid du Colombier  *  called with ucb locked
10667dd7cddfSDavid du Colombier  */
10677dd7cddfSDavid du Colombier void
10687dd7cddfSDavid du Colombier relrexmit(Conv *c, Reliable *r)
10697dd7cddfSDavid du Colombier {
10707dd7cddfSDavid du Colombier 	Rudppriv *upriv;
10717dd7cddfSDavid du Colombier 	Block *np;
10727dd7cddfSDavid du Colombier 	Fs *f;
10737dd7cddfSDavid du Colombier 
10747dd7cddfSDavid du Colombier 	upriv = c->p->priv;
10757dd7cddfSDavid du Colombier 	f = c->p->f;
10767dd7cddfSDavid du Colombier 	r->timeout = 0;
10777dd7cddfSDavid du Colombier 	if(r->xmits++ > Rudpmaxxmit){
10787dd7cddfSDavid du Colombier 		relhangup(c, r);
10797dd7cddfSDavid du Colombier 		return;
10807dd7cddfSDavid du Colombier 	}
10817dd7cddfSDavid du Colombier 
10827dd7cddfSDavid du Colombier 	upriv->rxmits++;
10837dd7cddfSDavid du Colombier 	np = copyblock(r->unacked, blocklen(r->unacked));
10847dd7cddfSDavid du Colombier 	DPRINT("rxmit r->ackrvcd+1 = %lud\n", r->ackrcvd+1);
10857dd7cddfSDavid du Colombier 	doipoput(c, f, np, 0, c->ttl, c->tos);
10867dd7cddfSDavid du Colombier }
1087