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