1*25202Skarels /*
2*25202Skarels  $Log:	rdp_prim.c,v $
3*25202Skarels  * Revision 2.15  85/03/12  08:41:13  walsh
4*25202Skarels  * Don't advise the user about ENOBUFS errors passed back from device drivers+
5*25202Skarels  * ip_send() when the protocol has a retransmission strategy.
6*25202Skarels  *
7*25202Skarels  * Revision 2.14  85/03/06  10:06:16  walsh
8*25202Skarels  * Corrected some error handling and reporting.
9*25202Skarels  *
10*25202Skarels  * Revision 2.13  85/02/26  08:26:56  walsh
11*25202Skarels  * First pass at using IP source routing information to establish connections
12*25202Skarels  * (possibly with hosts not known by the Internet gateways.)  The hooks with
13*25202Skarels  * TCP could be better done - particularly dealing with IP addresses in the
14*25202Skarels  * header for checksums and tcpdb lookups.
15*25202Skarels  *
16*25202Skarels  * Revision 2.12  84/11/15  09:56:04  walsh
17*25202Skarels  * redid how we deal with compiler padding in the RDP header structure.
18*25202Skarels  *
19*25202Skarels  * Revision 2.11  84/11/08  16:11:51  walsh
20*25202Skarels  * Added code to gather statistics on RDP traffic.  This makes the RDPCB
21*25202Skarels  * too big unles you make mbufs 512 bytes large.  RDP_CS should be turned off
22*25202Skarels  * unless you do.
23*25202Skarels  *
24*25202Skarels  * Revision 2.10  84/11/06  15:24:26  walsh
25*25202Skarels  * *** empty log message ***
26*25202Skarels  *
27*25202Skarels  * Revision 2.9  84/11/06  14:30:23  walsh
28*25202Skarels  * intorduced RDP_HLSHIFT
29*25202Skarels  *
30*25202Skarels  * Revision 2.8  84/11/06  09:09:16  walsh
31*25202Skarels  * added missing include.
32*25202Skarels  *
33*25202Skarels  * Revision 2.7  84/11/05  15:55:28  walsh
34*25202Skarels  * update_nulltimer() macro began to look inappropriate with recent
35*25202Skarels  * changes, so its been stripped out and put in-line.
36*25202Skarels  *
37*25202Skarels  * Revision 2.6  84/11/05  15:23:23  walsh
38*25202Skarels  * *** empty log message ***
39*25202Skarels  *
40*25202Skarels  * Revision 2.5  84/11/05  15:17:53  walsh
41*25202Skarels  * added comments on acknowledgement strategy.
42*25202Skarels  *
43*25202Skarels  * Revision 2.4  84/11/05  11:05:20  walsh
44*25202Skarels  * comment and adjust number for rdp_iss in a mathematically correct way
45*25202Skarels  * as a result of benchmarks (cf. operationally correct).
46*25202Skarels  *
47*25202Skarels  * Revision 2.3  84/11/02  18:24:09  walsh
48*25202Skarels  * Protocol specifiers want NULL message to have own sequence number in
49*25202Skarels  * case of slow (t>NULL msg timeout) packets.  I don't see this as a problem,
50*25202Skarels  * and even if happened (dubious) would only delay discovery, but I
51*25202Skarels  * didn't win this one.  Initially not designed for this, but fixes are
52*25202Skarels  * in almost neatly.
53*25202Skarels  *
54*25202Skarels  * Revision 2.2  84/11/02  15:28:56  walsh
55*25202Skarels  * Allow for RDP header fields not on natural boundries.  (Protocol
56*25202Skarels  * specifiers say will be part of next version in 6-12 months).
57*25202Skarels  * Until then, there goes the speed...  Yucho modifications.
58*25202Skarels  *
59*25202Skarels  * Revision 2.1  84/11/02  10:13:48  walsh
60*25202Skarels  * Fixed to include RCS comments in checked out source.
61*25202Skarels  *
62*25202Skarels  *
63*25202Skarels  * description:
64*25202Skarels  * some primitives for RDP.
65*25202Skarels  *
66*25202Skarels  * revision 1.10
67*25202Skarels  * date: 84/07/19 10:21:35;  author: walsh;  state: Exp;  lines added/del: 1/0
68*25202Skarels  * Organized macros and classified their definitions in rdp_macros.h.
69*25202Skarels  *
70*25202Skarels  * revision 1.9
71*25202Skarels  * date: 84/07/18 18:50:44;  author: walsh;  state: Exp;  lines added/del: 5/0
72*25202Skarels  * Added provision for sending of NULL messages.  These are sent on an idle
73*25202Skarels  * connection to determine that the other side still exists.
74*25202Skarels  *
75*25202Skarels  * revision 1.8
76*25202Skarels  * date: 84/07/12 20:04:16;  author: walsh;  state: Exp;  lines added/del: 1/2
77*25202Skarels  * *** empty log message ***
78*25202Skarels  *
79*25202Skarels  * revision 1.7
80*25202Skarels  * date: 84/07/12 13:48:14;  author: walsh;  state: Exp;  lines added/del: 31/16
81*25202Skarels  * Rather than in-line stuffing of IP/RDP headers, at least half of which are
82*25202Skarels  * constant, copy headers in from a template of what the headers are like.  The
83*25202Skarels  * bcopy() call is turned into a movc3 instruction on the VAX by a sed script
84*25202Skarels  * run over the assembler output of the C compiler.  Marginal speed-up.
85*25202Skarels  *
86*25202Skarels  * revision 1.6
87*25202Skarels  * date: 84/07/12 09:29:48;  author: walsh;  state: Exp;  lines added/del: 6/9
88*25202Skarels  * Found:
89*25202Skarels  * 1.  If MGET saves anything over mget, it's down in the noise, so skip it.
90*25202Skarels  * 2.  stuff_eacks as macro DOES save time.
91*25202Skarels  * 3.  Optimized stuff_eacks by timing and looking at a lot of assembler output
92*25202Skarels  *	from the C compiler.
93*25202Skarels  *
94*25202Skarels  * revision 1.5
95*25202Skarels  * date: 84/07/10 09:41:52;  author: walsh;  state: Exp;  lines added/del: 43/41
96*25202Skarels  * Corrected problem with conversion of stuff_eacks from a function to a
97*25202Skarels  * macro.
98*25202Skarels  *
99*25202Skarels  * Changed rdp_sendpkt to avoid unecessary allocation and deallocation of
100*25202Skarels  * an mbuf for syn and eack options.
101*25202Skarels  *
102*25202Skarels  * revision 1.4
103*25202Skarels  * date: 84/07/09 14:39:54;  author: walsh;  state: Exp;  lines added/del: 1/0
104*25202Skarels  * Part of ACK-delay algorithm.  Whenever send a packet with an ACK, set
105*25202Skarels  * ACK-delay timer to zero.
106*25202Skarels  *
107*25202Skarels  * revision 1.3
108*25202Skarels  * date: 84/07/06 14:10:35;  author: wjacobso;  state: Exp;  lines added/del: 34/34
109*25202Skarels  * stuff_eacks made into macro; added register var definitions
110*25202Skarels  *
111*25202Skarels  * revision 1.2
112*25202Skarels  * date: 84/07/06 09:49:37;  author: root;  state: Exp;  lines added/del: 11/4
113*25202Skarels  * This version seems to run bug-free.
114*25202Skarels  *
115*25202Skarels  * revision 1.1
116*25202Skarels  * date: 84/06/26 14:17:39;  author: walsh;  state: Exp;
117*25202Skarels  * Initial revision
118*25202Skarels  *
119*25202Skarels  */
120*25202Skarels 
121*25202Skarels 
122*25202Skarels #ifdef RDP
123*25202Skarels #include "../h/param.h"
124*25202Skarels #include "../h/dir.h"
125*25202Skarels #include "../h/user.h"
126*25202Skarels #include "../h/kernel.h"
127*25202Skarels #include "../h/inode.h"
128*25202Skarels #include "../h/mbuf.h"
129*25202Skarels #include "../h/socket.h"
130*25202Skarels #include "../h/socketvar.h"
131*25202Skarels #include "../h/protosw.h"
132*25202Skarels 
133*25202Skarels #include "../net/if.h"
134*25202Skarels #include "../net/route.h"
135*25202Skarels 
136*25202Skarels #include "../bbnnet/in.h"
137*25202Skarels #include "../bbnnet/in_var.h"
138*25202Skarels #include "../bbnnet/net.h"
139*25202Skarels #include "../bbnnet/in_pcb.h"
140*25202Skarels #include "../bbnnet/ip.h"
141*25202Skarels #include "../bbnnet/rdp.h"
142*25202Skarels #include "../bbnnet/rdp_macros.h"
143*25202Skarels 
144*25202Skarels extern struct inpcb rdp;
145*25202Skarels 
rdp_template(rdpcb)146*25202Skarels rdp_template(rdpcb)
147*25202Skarels RDPCB	*rdpcb;
148*25202Skarels {
149*25202Skarels     register struct ip	*ip;
150*25202Skarels     register RDPHDR		*pkt;
151*25202Skarels     register INPCB		*inp;
152*25202Skarels 
153*25202Skarels     ip	= (struct ip *) rdpcb->r_template;
154*25202Skarels     pkt	= (RDPHDR *) (ip+1);
155*25202Skarels     inp	= rdpcb->r_inpcb;
156*25202Skarels 
157*25202Skarels     ip->ip_p	= IPPROTO_RDP;
158*25202Skarels     ip->ip_tos	= 0;
159*25202Skarels     ip->ip_src	= inp->inp_laddr;
160*25202Skarels     ip->ip_dst	= inp->inp_faddr;
161*25202Skarels 
162*25202Skarels     pkt->rh_ver	= RDP_VERSION;
163*25202Skarels     pkt->rh_hdrlen	= RDPHDRSZ >> RDP_HLSHIFT;
164*25202Skarels     pkt->rh_sport	= inp->inp_lport;
165*25202Skarels     pkt->rh_dport	= inp->inp_fport;
166*25202Skarels     pkt->rh_flags	= 0;
167*25202Skarels     RDP_CKSUM(pkt)	= 0;
168*25202Skarels }
169*25202Skarels 
170*25202Skarels /***********************************************************************
171*25202Skarels  Comments on the Acknowledgement Strategy
172*25202Skarels 
173*25202Skarels  I.  It would be desirable to save network, CPU, and buffering resources
174*25202Skarels 	by not retransmitting packets which have arrived at the destination
175*25202Skarels 	host.  One might think of doing this by using the cumulative ack
176*25202Skarels 	(ACK) for flow control and the extended ack (EACK) for reliability.
177*25202Skarels 	[EACK upon reception by protocol module, ACK upon reception by
178*25202Skarels 	application]
179*25202Skarels 
180*25202Skarels  You can't do this.
181*25202Skarels 
182*25202Skarels  1.  The protocol specifiers state that an implementation may choose
183*25202Skarels 	to treat ACK#1 EACK#2 EACK#3 as ACK#3.  We want to avoid receiving
184*25202Skarels 	packets we can't buffer.  (see goals above)
185*25202Skarels  2.  NULL packets are sent on idle connections (all packets acknowledged)
186*25202Skarels 	and occupy sequence number space.  We want to avoid situations
187*25202Skarels 	where a NULL packet goes out of our window, doesn't get acked,
188*25202Skarels 	and the connection gets dropped.  (connection would be idle since
189*25202Skarels 	no transmissions/retransmissions would be going on if packets
190*25202Skarels 	received by RDP, but destination process busy doing something
191*25202Skarels 	else.)
192*25202Skarels  3.  Relying on ACKs to be sent to move the window's edge after the
193*25202Skarels 	datagram is picked up by the user process has to deal with the
194*25202Skarels 	fact that that ACK may be lost.  In TCP, one uses a persistence
195*25202Skarels 	timer to deal with this.  Use of ACK/EACK above implies that
196*25202Skarels 	there is no data with which to perform persistence (if buffers
197*25202Skarels 	reclaimed at EACK and not at ACK.)  One could think of using
198*25202Skarels 	NULL messages to reopen the window, but a) NULL messages only
199*25202Skarels 	start up after LONG delays,  b) implementations are only required
200*25202Skarels 	to respond to, not initiate, NULL messages.
201*25202Skarels 
202*25202Skarels  2 would be a correctness error.  1+3 would be stylistic problems.
203*25202Skarels  Therefore, the acknowledgement strategy implemented here is to
204*25202Skarels  ACK or EACK messages only after they have been received by the destination
205*25202Skarels  process.  This means that some unecessary retransmissions might occur,
206*25202Skarels  but such is life.  (Unecessary from the point of view that the
207*25202Skarels  information made it through the network a-o.k.)
208*25202Skarels 
209*25202Skarels  If the application is slow to pick up messages from the protocol,
210*25202Skarels  then that will be reflected in the round trip time estimate (and
211*25202Skarels  retransmission time) for the other end.  So, maybe this isn't all
212*25202Skarels  that bad.  Especially since most processes will be fairly interested
213*25202Skarels  and responsive to network input.
214*25202Skarels 
215*25202Skarels  ***********************************************************************/
216*25202Skarels 
217*25202Skarels /*
218*25202Skarels  * rdp_init ensures max # of options fit in mbuf
219*25202Skarels  */
220*25202Skarels #define stuff_eacks(rdpcb, optm) \
221*25202Skarels { \
222*25202Skarels 	register int		 pass;					\
223*25202Skarels 	register int		 index;					\
224*25202Skarels 		 EACKOPTIONS	*eopt;					\
225*25202Skarels  \
226*25202Skarels 	pass	= 0;							\
227*25202Skarels 	index	= rdpcb->r_rcvq.rq_front;				\
228*25202Skarels  \
229*25202Skarels 	do {								\
230*25202Skarels 		if (rdpcb->r_rcvq.rq_msgs[index] == RDP_DELIVERED){	\
231*25202Skarels 			if (optm == NULL){				\
232*25202Skarels 				optm = m_get(M_DONTWAIT, MT_HEADER);	\
233*25202Skarels 				if (optm == NULL)			\
234*25202Skarels 					break;				\
235*25202Skarels 				optm->m_len = 0;			\
236*25202Skarels 				eopt = mtod(optm, EACKOPTIONS *);	\
237*25202Skarels 			}						\
238*25202Skarels 			eopt->rh_eackno =				\
239*25202Skarels 				htonl(rdpcb->r_rcvq.rq_baseseq + pass); \
240*25202Skarels 			eopt ++;					\
241*25202Skarels 			optm->m_len += sizeof(EACKOPTIONS);		\
242*25202Skarels 		}							\
243*25202Skarels 		index = (index + 1) % rdpcb->r_rcvq.rq_maxqlen;		\
244*25202Skarels 	} while (++pass < rdpcb->r_rcvq.rq_maxqlen);			\
245*25202Skarels }
246*25202Skarels 
rdp_sendpkt(rdpcb,data,datalen,seqnum)247*25202Skarels rdp_sendpkt (rdpcb, data, datalen, seqnum)
248*25202Skarels register RDPCB	*rdpcb;
249*25202Skarels MBUF	*data;
250*25202Skarels int datalen;	/* length of data mbuf chain in bytes */
251*25202Skarels rdpsequence seqnum;	/* host order.  seq# of this packet */
252*25202Skarels {
253*25202Skarels     register MBUF	*m;
254*25202Skarels     register RDPHDR	*pkt;
255*25202Skarels 
256*25202Skarels     m = m_get(M_DONTWAIT, MT_HEADER);
257*25202Skarels     if (m == NULL)
258*25202Skarels     {
259*25202Skarels 	m_freem(data);
260*25202Skarels 	return (ENOBUFS);
261*25202Skarels     }
262*25202Skarels 
263*25202Skarels     m->m_len = sizeof(struct ip) + RDPHDRSZ;
264*25202Skarels     m->m_off = MMAXOFF
265*25202Skarels 	- (sizeof(struct ip) + RDPHDRSZ + sizeof(SYNOPTIONS));
266*25202Skarels     m->m_next = data;
267*25202Skarels 
268*25202Skarels     pkt = (RDPHDR *) (mtod(m, caddr_t) + sizeof(struct ip));
269*25202Skarels     /*
270*25202Skarels      * Fills in most IP and RDP header fields
271*25202Skarels      */
272*25202Skarels     bcopy (rdpcb->r_template, mtod(m, caddr_t), (unsigned)RDP_TEMPLSIZE);
273*25202Skarels 
274*25202Skarels     pkt->rh_dlen	= htons((u_short)datalen);
275*25202Skarels     RDP_SEQNO(pkt)	= htonl((u_long)seqnum);
276*25202Skarels 
277*25202Skarels     if (rdpcb->r_sendrst)
278*25202Skarels     {
279*25202Skarels 	pkt->rh_flags |= RDP_fRST;
280*25202Skarels #ifdef RDP_CS
281*25202Skarels 	rdpcb->r_sent.r_rstpkts ++;
282*25202Skarels #endif
283*25202Skarels     }
284*25202Skarels     else
285*25202Skarels     {
286*25202Skarels 	if (! rdpcb->r_synacked)
287*25202Skarels 	{
288*25202Skarels 	    register SYNOPTIONS *synopt;
289*25202Skarels 
290*25202Skarels 	    pkt->rh_flags	|= RDP_fSYN;
291*25202Skarels 	    pkt->rh_hdrlen	+= sizeof(SYNOPTIONS) >> RDP_HLSHIFT;
292*25202Skarels 	    m->m_len	+= sizeof(SYNOPTIONS);
293*25202Skarels 	    synopt	= RDP_OPT(pkt, SYNOPTIONS *);
294*25202Skarels 	    synopt->rh_nbuf = htons((u_short)rdpcb->r_ournbuf);
295*25202Skarels 	    synopt->rh_maxlen = htons((u_short)rdpcb->r_ourmaxlen + HDRSLOP);
296*25202Skarels 	    synopt->rh_options = 0;
297*25202Skarels 	    if (rdpcb->r_sequential)
298*25202Skarels 		synopt->rh_options |= RDP_oSEQUENTIAL;
299*25202Skarels 	    synopt->rh_options = htons(synopt->rh_options);
300*25202Skarels #ifdef RDP_CS
301*25202Skarels 	    rdpcb->r_sent.r_synpkts ++;
302*25202Skarels #endif
303*25202Skarels 	}
304*25202Skarels 	else
305*25202Skarels 	{
306*25202Skarels 	    register MBUF *optm;
307*25202Skarels 
308*25202Skarels 	    /* possible EACK */
309*25202Skarels 	    optm = NULL;
310*25202Skarels 	    stuff_eacks(rdpcb, optm);
311*25202Skarels 
312*25202Skarels 	    if (optm)
313*25202Skarels 	    {
314*25202Skarels #define OKSZ (datalen+RDPHDRSZ+sizeof(struct ip)+optm->m_len <= rdpcb->r_hismaxlen)
315*25202Skarels 		if (OKSZ)
316*25202Skarels 		{
317*25202Skarels 		    pkt->rh_flags |= RDP_fEACK;
318*25202Skarels 		    pkt->rh_hdrlen += optm->m_len >> RDP_HLSHIFT;
319*25202Skarels 		    optm->m_next = m->m_next;
320*25202Skarels 		    m->m_next = optm;
321*25202Skarels 		}
322*25202Skarels 		else
323*25202Skarels 		    m_free(optm);
324*25202Skarels #undef OKSZ
325*25202Skarels 	    }
326*25202Skarels 
327*25202Skarels 	    if (rdpcb->r_sendnull)
328*25202Skarels 	    {
329*25202Skarels 		rdpcb->r_sendnull = FALSE;
330*25202Skarels 		pkt->rh_flags |= RDP_fNULL;
331*25202Skarels #ifdef RDP_CS
332*25202Skarels 		rdpcb->r_sent.r_nullpkts ++;
333*25202Skarels #endif
334*25202Skarels 	    }
335*25202Skarels 	}
336*25202Skarels 
337*25202Skarels 	if (rdpcb->r_synrcvd)
338*25202Skarels 	{
339*25202Skarels 	    rdpcb->r_timers[RDP_tACKDELAY] = 0;
340*25202Skarels 	    pkt->rh_flags |= RDP_fACK;
341*25202Skarels 	    RDP_ACKNO(pkt) = htonl(rdpcb->r_rcvq.rq_baseseq -1);
342*25202Skarels 	}
343*25202Skarels     }
344*25202Skarels 
345*25202Skarels     RDP_CKSUM(pkt) = rdp_cksum (m);
346*25202Skarels 
347*25202Skarels #ifdef RDP_CS
348*25202Skarels     rdpcb->r_sent.r_total ++;
349*25202Skarels #endif
350*25202Skarels 
351*25202Skarels     if (debug_rdpcb(rdpcb))
352*25202Skarels 	rdp_debug (rdpcb, mtod(m, caddr_t), -1, RDP_sSAME);
353*25202Skarels 
354*25202Skarels     /*
355*25202Skarels      * and ship packet off via IP.  Remember that since this protocol
356*25202Skarels      * involves retransmissions, errors can occur asynchronous to a
357*25202Skarels      * (write) system call, and that therefore we can not send the
358*25202Skarels      * error all the way back up through subroutine return values.  We
359*25202Skarels      * must also post it back via advise_user() at some point, and this
360*25202Skarels      * looks like a good point to try it.
361*25202Skarels      */
362*25202Skarels     {
363*25202Skarels 	register int	error;
364*25202Skarels 
365*25202Skarels 	error = ip_send (rdpcb->r_inpcb, m,(int)( datalen + hdrlen(pkt)),FALSE);
366*25202Skarels 
367*25202Skarels 	if (error)
368*25202Skarels 	    /*
369*25202Skarels 	     * Since we use retransmissions, don't need to tell user
370*25202Skarels 	     * process about this.  (Can be as simple as interface
371*25202Skarels 	     * or host structure queues are too long due to current
372*25202Skarels 	     * heavy traffic.  Backing off will take care of that.)
373*25202Skarels 	     */
374*25202Skarels 	    if (error != ENOBUFS)
375*25202Skarels 		advise_user(rdpcbtoso(rdpcb), error);
376*25202Skarels 	return (error);
377*25202Skarels     }
378*25202Skarels }
379*25202Skarels 
rdp_timeo()380*25202Skarels rdp_timeo()
381*25202Skarels {
382*25202Skarels     register struct inpcb *inp, *next;
383*25202Skarels     register RDPCB	*rdpcb;
384*25202Skarels     register int	 timer;
385*25202Skarels     int s;
386*25202Skarels     register rdpstate newstate;
387*25202Skarels 
388*25202Skarels     s = splnet();
389*25202Skarels     rdp_iss += RDP_ISSINCR;
390*25202Skarels     /*
391*25202Skarels      * Remember, if CLOSEWAIT timer goes off, lose rdpcb's mbuf and
392*25202Skarels      * and next pointer in it.
393*25202Skarels      */
394*25202Skarels     inp = rdp.inp_next;
395*25202Skarels     while (inp != &rdp)
396*25202Skarels     {
397*25202Skarels 	next = inp->inp_next;
398*25202Skarels 	if (rdpcb = inptordpcb(inp))
399*25202Skarels 	{
400*25202Skarels 	    rdpcb->r_rtt ++;
401*25202Skarels 
402*25202Skarels 	    for (timer = 0; timer < RDP_NTIMERS; timer++)
403*25202Skarels 	    {
404*25202Skarels 		if (rdpcb->r_timers[timer])
405*25202Skarels 		{
406*25202Skarels 		    rdpcb->r_timers[timer] --;
407*25202Skarels 		    if (rdpcb->r_timers[timer] == 0)
408*25202Skarels 		    {
409*25202Skarels 			RDP_ACTION (RDP_iTIMER, rdpcb, timer, newstate)
410*25202Skarels 			if (newstate == RDP_sCLOSED)
411*25202Skarels 			    /* next rdpcb */
412*25202Skarels 			    break;
413*25202Skarels 		    }
414*25202Skarels 		}
415*25202Skarels 	    }
416*25202Skarels 	}
417*25202Skarels 	inp = next;
418*25202Skarels     }
419*25202Skarels     splx(s);
420*25202Skarels }
421*25202Skarels #endif
422