xref: /csrg-svn/sys/netns/spp_usrreq.c (revision 23561)
1 /*
2  * Copyright (c) 1982 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)spp_usrreq.c	6.4 (Berkeley) 06/19/85
7  */
8 
9 #include "param.h"
10 #include "dir.h"
11 #include "user.h"
12 #include "mbuf.h"
13 #include "protosw.h"
14 #include "socket.h"
15 #include "socketvar.h"
16 #include "errno.h"
17 
18 #include "../net/if.h"
19 #include "../net/route.h"
20 #include "../netinet/tcp_fsm.h"
21 #include "../netinet/tcp_timer.h"
22 
23 #include "ns.h"
24 #include "ns_pcb.h"
25 #include "idp.h"
26 #include "idp_var.h"
27 #include "ns_error.h"
28 #include "sp.h"
29 #include "spidp.h"
30 #include "spp_var.h"
31 #include "spp_debug.h"
32 
33 /*
34  * SP protocol implementation.
35  */
36 spp_init()
37 {
38 
39 	spp_iss = 1; /* WRONG !! should fish it out of TODR */
40 }
41 struct spidp spp_savesi;
42 int traceallspps = 0;
43 extern int sppconsdebug;
44 
45 int spp_hardnosed;
46 spp_input(m, nsp)
47 	register struct nspcb *nsp;
48 	register struct mbuf *m;
49 {
50 	register struct sppcb *cb;
51 	register struct spidp *si = mtod(m, struct spidp *);
52 	register struct socket *so;
53 	int len; short ostate;
54 	int dropsocket = 0;
55 
56 
57 
58 	cb = nstosppcb(nsp);
59 	if (cb == 0) goto bad;
60 
61 	si->si_seq = ntohs(si->si_seq);
62 	si->si_ack = ntohs(si->si_ack);
63 	si->si_alo = ntohs(si->si_alo);
64 
65 	so = nsp->nsp_socket;
66 	if (so->so_options & SO_DEBUG || traceallspps) {
67 		ostate = cb->s_state;
68 		spp_savesi = *si;
69 	}
70 	if (so->so_options & SO_ACCEPTCONN) {
71 		so = sonewconn(so);
72 		if (so == 0) {
73 			spp_istat.nonucn++;
74 			goto drop;
75 		}
76 		/*
77 		 * This is ugly, but ....
78 		 *
79 		 * Mark socket as temporary until we're
80 		 * committed to keeping it.  The code at
81 		 * ``drop'' and ``dropwithreset'' check the
82 		 * flag dropsocket to see if the temporary
83 		 * socket created here should be discarded.
84 		 * We mark the socket as discardable until
85 		 * we're committed to it below in TCPS_LISTEN.
86 		 */
87 		dropsocket++;
88 		nsp = (struct nspcb *)so->so_pcb;
89 		nsp->nsp_laddr = si->si_dna;
90 		cb = nstosppcb(nsp);
91 		cb->s_state = TCPS_LISTEN;
92 	}
93 
94 	/*
95 	 * Packet received on connection.
96 	 * reset idle time and keep-alive timer;
97 	 */
98 	cb->s_idle = 0;
99 	cb->s_timer[TCPT_KEEP] = TCPTV_KEEP;
100 
101 	switch (cb->s_state) {
102 
103 	case TCPS_LISTEN:{
104 		struct mbuf *am;
105 		register struct sockaddr_ns *sns;
106 		struct ns_addr laddr;
107 
108 		/*
109 		 * If somebody here was carying on a conversation
110 		 * and went away, and his pen pal thinks he can
111 		 * still talk, we get the misdirected packet.
112 		 */
113 		if (spp_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
114 			spp_istat.gonawy++;
115 			goto dropwithreset;
116 		}
117 		am = m_get(M_DONTWAIT, MT_SONAME);
118 		if (am == NULL)
119 			goto drop;
120 		am->m_len = sizeof (struct sockaddr_ns);
121 		sns = mtod(am, struct sockaddr_ns *);
122 		sns->sns_family = AF_NS;
123 		sns->sns_addr = si->si_sna;
124 		laddr = nsp->nsp_laddr;
125 		if (ns_nullhost(laddr))
126 			nsp->nsp_laddr = si->si_dna;
127 		if (ns_pcbconnect(nsp, am)) {
128 			nsp->nsp_laddr = laddr;
129 			(void) m_free(am);
130 			spp_istat.noconn++;
131 			goto drop;
132 		}
133 		(void) m_free(am);
134 		cb->s_state = TCPS_SYN_RECEIVED;
135 		spp_template(cb);
136 		cb->s_did = si->si_sid;
137 		cb->s_rack = si->si_ack;
138 		cb->s_ralo = si->si_alo;
139 		cb->s_flags |= SF_AK;
140 		cb->s_timer[TCPT_KEEP] = TCPTV_KEEP;
141 		dropsocket = 0;		/* committed to socket */
142 		}
143 		break;
144 
145 	/*
146 	 * This state means that we have gotten a response
147 	 * to our attempt to establish a connection.
148 	 * We fill in the data from the other side
149 	 * (Telling which port to send to instead of the well-
150 	 * known one we might have to in the first place )
151 	 * We also require that this is a response to our
152 	 * connection id, and that it should be a system packet,
153 	 * containing no data.
154 	 */
155 	case TCPS_SYN_SENT:
156 		if (si->si_did!=cb->s_sid) {
157 			spp_istat.notme++;
158 			goto drop;
159 		}
160 		cb->s_did = si->si_sid;
161 		cb->s_rack = si->si_ack;
162 		cb->s_ralo = si->si_alo;
163 		cb->s_dport = nsp->nsp_fport =  si->si_sport;
164 		cb->s_timer[TCPT_REXMT] = 0;
165 		cb->s_flags |= SF_AK;
166 		soisconnected(so);
167 		cb->s_state = TCPS_ESTABLISHED;
168 		break;
169 	/*
170 	 * This state means that we have heard a response
171 	 * to our acceptance of their connection
172 	 * It is probably logically unnecessary in this
173 	 * implementation.
174 	 */
175 	 case TCPS_SYN_RECEIVED:
176 		if (si->si_did!=cb->s_sid) {
177 			spp_istat.wrncon++;
178 			goto drop;
179 		}
180 		nsp->nsp_fport =  si->si_sport;
181 		cb->s_timer[TCPT_REXMT] = 0;
182 		cb->s_timer[TCPT_KEEP] = TCPTV_KEEP;
183 		soisconnected(so);
184 		cb->s_state = TCPS_ESTABLISHED;
185 	}
186 	if (so->so_options & SO_DEBUG || traceallspps)
187 		spp_trace(SA_INPUT, ostate, cb, &spp_savesi, 0);
188 
189 	m->m_len -= sizeof (struct idp);
190 	m->m_off += sizeof (struct idp);
191 
192 	if (spp_reass(cb,si)) {
193 		spp_istat.bdreas++;
194 		goto drop;
195 	}
196 	spp_output(cb,(struct mbuf *)0);
197 	return;
198 
199 dropwithreset:
200 	if (dropsocket)
201 		(void) soabort(so);
202 	si->si_seq = ntohs(si->si_seq);
203 	si->si_ack = ntohs(si->si_ack);
204 	si->si_alo = ntohs(si->si_alo);
205 	ns_error(dtom(si), NS_ERR_NOSOCK, 0);
206 	if (cb->s_nspcb->nsp_socket->so_options & SO_DEBUG || traceallspps)
207 		spp_trace(SA_DROP, ostate, cb, &spp_savesi, 0);
208 	return;
209 
210 drop:
211 bad:
212 	if (cb->s_nspcb->nsp_socket->so_options & SO_DEBUG || traceallspps)
213 		spp_trace(SA_DROP, ostate, cb, &spp_savesi, 0);
214 	m_freem(m);
215 }
216 
217 /*
218  * This is structurally similar to the tcp reassembly routine
219  * but its function is somewhat different:  It merely queues
220  * packets up, and suppresses duplicates.
221  */
222 spp_reass(cb,si)
223 register struct sppcb *cb;
224 register struct spidp *si;
225 {
226 	register struct spidp_q *q;
227 	register struct mbuf *m;
228 	struct socket *so = cb->s_nspcb->nsp_socket;
229 	struct sockbuf *sb = & (so->so_rcv);
230 	char packetp = cb->s_flags & SF_HI;
231 	char wakeup = 0;
232 
233 
234 	if (si==SI(0))
235 		goto present;
236 	/*
237 	 * Update our news from them.
238 	 */
239 	if (si->si_cc & SP_SA)
240 		cb->s_flags |= SF_DELACK;
241 	if (SSEQ_GT(si->si_ack,cb->s_rack)) {
242 		cb->s_rack = si->si_ack;
243 		cb->s_timer[TCPT_REXMT] = 0;
244 
245 		/*
246 		 * If transmit timer is running and timed sequence
247 		 * number was acked, update smoothed round trip time.
248 		 */
249 		if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
250 			if (cb->s_srtt == 0)
251 				cb->s_srtt = cb->s_rtt;
252 			else
253 				cb->s_srtt =
254 				    tcp_alpha * cb->s_srtt +
255 				    (1 - tcp_alpha) * cb->s_rtt;
256 			cb->s_rtt = 0;
257 		}
258 	}
259 	if (SSEQ_GT(si->si_alo,cb->s_ralo)) {
260 		cb->s_ralo = si->si_alo;
261 		cb->s_timer[TCPT_PERSIST] = 0;
262 	}
263 	/*
264 	 * If this is a system packet, we don't need to
265 	 * queue it up, and won't update acknowledge #
266 	 */
267 	if (si->si_cc & SP_SP) {
268 		m_freem(dtom(si));
269 		return (0);
270 	}
271 
272 	/*
273 	 * If this packet number has a sequence number less
274 	 * than that of the first packet not yet seen coming
275 	 * from them, this must be a duplicate, so drop.
276 	 */
277 	if (SSEQ_LT(si->si_seq,cb->s_ack))
278 		return (1);
279 	/*
280 	 * If this packet number is higher than that which
281 	 * we have allocated refuse it, unless urgent
282 	 */
283 	if (SSEQ_GT(si->si_seq,cb->s_alo) && (!(si->si_cc & SP_OB))) {
284 		return (1);
285 	}
286 	/*
287 	 * If this packet is urgent, inform process
288 	 */
289 	if (si->si_cc & SP_OB) {
290 		cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
291 		sohasoutofband(so);
292 	}
293 
294 	/*
295 	 * Loop through all packets queued up to insert in
296 	 * appropriate sequence.
297 	 */
298 
299 	for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) {
300 	    if (si->si_seq==SI(q)->si_seq) return (1); /*duplicate */
301 	    if (SSEQ_LT(si->si_seq,SI(q)->si_seq)) break;
302 	}
303 	insque(si,q->si_prev);
304 
305 present:
306 #define SPINC sizeof(struct sphdr)
307 	/*
308 	 * Loop through all packets queued up to update acknowledge
309 	 * number, and present all acknowledged data to user;
310 	 * If in packet interface mode, show packet headers.
311 	 */
312 	for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) {
313 		  if (SI(q)->si_seq==cb->s_ack) {
314 			cb->s_ack++;
315 			m = dtom(q);
316 			if (SI(q)->si_cc & SP_OB) {
317 				if (sb->sb_cc)
318 					so->so_oobmark = sb->sb_cc;
319 				else
320 					so->so_state |= SS_RCVATMARK;
321 			}
322 			q = q->si_prev;
323 			remque(q->si_next);
324 			wakeup = 1;
325 			if (packetp) {
326 				sbappendrecord(sb,m);
327 			} else {
328 				cb->s_rhdr = *mtod(m, struct sphdr *);
329 				m->m_off += SPINC;
330 				m->m_len -= SPINC;
331 				sbappend(sb,m);
332 			}
333 		  } else
334 			break;
335 	}
336 	if (wakeup) sorwakeup(so);
337 	return (0);
338 }
339 
340 spp_ctlinput(cmd, arg)
341 	int cmd;
342 	caddr_t arg;
343 {
344 	struct ns_addr *na;
345 	extern u_char nsctlerrmap[];
346 	extern spp_abort();
347 	int type;
348 
349 	if (cmd < 0 || cmd > PRC_NCMDS)
350 		return;
351 	type = NS_ERR_UNREACH_HOST;
352 
353 	switch (cmd) {
354 
355 	case PRC_ROUTEDEAD:
356 	case PRC_QUENCH:
357 		break;
358 
359 	case PRC_IFDOWN:
360 		na = &((struct sockaddr_ns *)arg)->sns_addr;
361 		break;
362 
363 	case PRC_HOSTDEAD:
364 	case PRC_HOSTUNREACH:
365 		na = (struct ns_addr *)arg;
366 		break;
367 
368 	default:
369 		na = &((struct ns_errp *)arg)->ns_err_idp.idp_dna;
370 		type = ((struct ns_errp *)arg)->ns_err_num;
371 		type = ntohs(type);
372 	}
373 	switch (type) {
374 
375 	case NS_ERR_UNREACH_HOST:
376 	case NS_ERR_NOSOCK:
377 		ns_pcbnotify(na, (int)nsctlerrmap[cmd],
378 				spp_abort, (long) 0);
379 		break;
380 
381 	case NS_ERR_TOO_BIG:
382 		ns_pcbnotify(na, 0, spp_abort, (long)arg);
383 	}
384 }
385 
386 int
387 spp_fixmtu(nsp)
388 register struct nspcb *nsp;
389 {
390 	register struct sppcb *cb = (struct sppcb *)(nsp->nsp_pcb);
391 	register struct mbuf *m;
392 	register struct spidp *si;
393 	struct ns_errp *ep;
394 	struct sockbuf *sb;
395 	int badseq, len;
396 	struct mbuf *firstbad, *m0;
397 
398 	if (cb) {
399 		/*
400 		 * The notification that we have sent
401 		 * too much is bad news -- we will
402 		 * have to go through queued up so far
403 		 * splitting ones which are too big and
404 		 * reassigning sequence numbers and checksums.
405 		 * we should then retransmit all packets from
406 		 * one above the offending packet to the last one
407 		 * we had sent (or our allocation)
408 		 * then the offending one so that the any queued
409 		 * data at our destination will be discarded.
410 		 */
411 		 ep = (struct ns_errp *)nsp->nsp_notify_param;
412 		 sb = &nsp->nsp_socket->so_snd;
413 		 cb->s_mtu = ep->ns_err_param;
414 		 badseq = SI(&ep->ns_err_idp)->si_seq;
415 		 for (m = sb->sb_mb; m; m = m->m_act) {
416 			si = mtod(m, struct spidp *);
417 			if (si->si_seq == badseq)
418 				break;
419 		 }
420 		 if (m==0) return;
421 		 firstbad = m;
422 		 /*for (;;) {*/
423 			/* calculate length */
424 			for (m0 = m, len = 0; m ; m = m->m_next)
425 				len += m->m_len;
426 			if (len > cb->s_mtu) {
427 			}
428 		/* FINISH THIS
429 		} */
430 	}
431 }
432 
433 int spp_output_cnt = 0;
434 spp_output(cb, m0)
435 	register struct sppcb *cb;
436 	struct mbuf *m0;
437 {
438 	struct socket *so = cb->s_nspcb->nsp_socket;
439 	register struct mbuf *m;
440 	register struct spidp *si = (struct spidp *) 0;
441 	register struct sockbuf *sb = &(so->so_snd);
442 	register int len = 0;
443 	int flags, debit, mtu = cb->s_mtu;
444 	int error = 0; u_short lookfor = 0;
445 	struct mbuf *mprev;
446 	extern int idpcksum;
447 
448 	if (m0)
449 	{
450 		for (m = m0; m ; m = m->m_next) {
451 			mprev = m;
452 			len += m->m_len;
453 		}
454 		if (len > mtu) {
455 			if (cb->s_flags & SF_PI) {
456 				m_freem(m0);
457 				return (EMSGSIZE);
458 			} else {
459 				int off = 0;
460 				while (len > mtu) {
461 					m = m_copy(m0, off, mtu);
462 					if (m==NULL) {
463 						m_freem(m0);
464 						return (ENOBUFS);
465 					}
466 					error = spp_output(cb, m);
467 					if (error) {
468 						m_freem(m0);
469 						return (error);
470 					}
471 					m_adj(m0, mtu);
472 					len -= mtu;
473 				}
474 			}
475 		}
476 		if (len & 1) {
477 			if (m->m_len + m->m_off < MMAXOFF) {
478 				m->m_len++;
479 			} else {
480 				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
481 
482 				if (m1 == 0) {
483 					m_freem(m0);
484 					return (ENOBUFS);
485 				}
486 				m1->m_len = 1;
487 				m1->m_off = MMAXOFF - 1;
488 				mprev->m_next = m1;
489 			}
490 		}
491 		m = m_get(M_DONTWAIT, MT_HEADER);
492 		if (m == 0) {
493 			m_freem(m0);
494 			return (ENOBUFS);
495 		}
496 
497 		/*
498 		 * Fill in mbuf with extended SP header
499 		 * and addresses and length put into network format.
500 		 */
501 		m->m_off = MMAXOFF - sizeof (struct spidp);
502 		m->m_len = sizeof (struct spidp);
503 		m->m_next = m0;
504 		si = mtod(m, struct spidp *);
505 		*si = cb->s_shdr;
506 		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
507 			register struct sphdr *sh = mtod(m0, struct sphdr *);
508 			si->si_dt = sh->sp_dt;
509 			si->si_cc |= sh->sp_cc & SP_EM;
510 			m0->m_len -= sizeof (*sh);
511 			m0->m_off += sizeof (*sh);
512 			len -= sizeof (*sh);
513 		}
514 		len += sizeof(*si);
515 		si->si_len = htons(len);
516 		/*
517 		 * queue stuff up for output
518 		 */
519 		sbappendrecord(sb,m);
520 		cb->s_seq++;
521 	}
522 output:
523 	/*
524 	 * update window
525 	 */
526 	{
527 		register struct sockbuf *sb = &so->so_rcv;
528 		int credit = ((sb->sb_mbmax - sb->sb_mbcnt) / cb->s_mtu);
529 		int alo = cb->s_ack + credit;
530 
531 		if (cb->s_alo < alo) cb->s_alo = alo;
532 	}
533 
534 	if (cb->s_oobflags & SF_SOOB) {
535 		/*
536 		 * must transmit this out of band packet
537 		 */
538 		cb->s_oobflags &= ~ SF_SOOB;
539 	} else {
540 		/*
541 		 * Decide what to transmit:
542 		 * If we have a new packet, send that
543 		 * (So long as it is in our allocation)
544 		 * If it is time to retransmit a packet,
545 		 * send that.
546 		 * Otherwise, see if it time to bang on them
547 		 * to ask for our current allocation.
548 		 */
549 		if (SSEQ_LT(cb->s_snt, cb->s_ralo))
550 			lookfor = cb->s_snt + 1;
551 		else if (cb->s_force==(1+TCPT_REXMT)) {
552 			lookfor = cb->s_rack;
553 		} else if (SSEQ_LT(cb->s_ralo, cb->s_seq)) {
554 			lookfor = 0;
555 			if (cb->s_timer[TCPT_PERSIST]==0) {
556 				spp_setpersist(cb);
557 			}
558 		}
559 		m = sb->sb_mb;
560 		while( m ) {
561 			si = mtod(m, struct spidp *);
562 			m = m->m_act;
563 			if (SSEQ_LT(si->si_seq, cb->s_rack)) {
564 				if ((sb->sb_flags & SB_WAIT)
565 				     || so->so_snd.sb_sel)
566 					 sowwakeup(so);
567 				sbdroprecord(sb);
568 				si = 0;
569 				continue;
570 			}
571 			if (SSEQ_LT(si->si_seq, lookfor))
572 				continue;
573 			break;
574 		}
575 		if (si && (si->si_seq != lookfor)) si = 0;
576 	}
577 	cb->s_want = lookfor;
578 
579 	if (si) {
580 		/*
581 		 * must make a copy of this packet for
582 		 * idp_output to monkey with
583 		 */
584 		 m = dtom(si);
585 		 m = m_copy(m, 0, M_COPYALL);
586 		 if (m==NULL)
587 			return (ENOBUFS);
588 		 m0 = m;
589 		 si = mtod(m, struct spidp *);
590 	} else if (cb->s_force || cb->s_flags & SF_AK) {
591 		/*
592 		 * Must send an acknowledgement or a probe
593 		 */
594 		m = m_get(M_DONTWAIT, MT_HEADER);
595 		if (m == 0)
596 			return (ENOBUFS);
597 		/*
598 		 * Fill in mbuf with extended SP header
599 		 * and addresses and length put into network format.
600 		 */
601 		m->m_off = MMAXOFF - sizeof (struct spidp);
602 		m->m_len = sizeof (*si);
603 		m->m_next = 0;
604 		si = mtod(m, struct spidp *);
605 		*si = cb->s_shdr;
606 		si->si_seq = cb->s_snt + 1;
607 		len = sizeof (*si);
608 		si->si_len = htons((u_short)len);
609 		si->si_cc |= SP_SP;
610 		cb->s_flags &= ~SF_AK;
611 	}
612 	/*
613 	 * Stuff checksum and output datagram.
614 	 */
615 	if (si) {
616 		/*
617 		 * If we are almost out of allocation
618 		 * or one of the timers has gone off
619 		 * request an ack.
620 		 */
621 		if (SSEQ_GEQ(cb->s_seq,cb->s_ralo))
622 			si->si_cc |= SP_SA;
623 		if (cb->s_force) {
624 			si->si_cc |= SP_SA;
625 			cb->s_force = 0;
626 		}
627 		/* if this is a new packet (and not a system packet)
628 		 * and we are not currently timing anything
629 		 * time this one and ask for an ack
630 		 */
631 		if (SSEQ_LT(cb->s_snt,si->si_seq) &&
632 		   (!(si->si_cc & SP_SP))) {
633 			cb->s_snt = si->si_seq;
634 			if (cb->s_rtt==0) {
635 				cb->s_rtseq = si->si_seq;
636 				cb->s_rtt = 1;
637 				si->si_cc |= SP_SA;
638 			}
639 			/*
640 			 * If the retransmit timer has not been set
641 			 * and this is a real packet
642 			 * then start the retransmit timer
643 			 */
644 			if (cb->s_timer[TCPT_REXMT]==0) {
645 				TCPT_RANGESET(cb->s_timer[TCPT_REXMT],
646 					tcp_beta * cb->s_srtt, TCPTV_MIN,
647 					TCPTV_MAX);
648 				cb->s_rxtshift = 0;
649 			}
650 		}
651 		si->si_seq = htons(si->si_seq);
652 		si->si_alo = htons(cb->s_alo);
653 		si->si_ack = htons(cb->s_ack);
654 
655 		if (idpcksum) {
656 			si->si_sum = 0;
657 			len = ((len - 1) | 1) + 1;
658 			si->si_sum = ns_cksum(dtom(si), len);
659 		} else
660 			si->si_sum = 0xffff;
661 
662 		if (so->so_options & SO_DEBUG || traceallspps)
663 			spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
664 		spp_output_cnt++;
665 		if (so->so_options & SO_DONTROUTE)
666 			error = ns_output(m, (struct route *)0, NS_ROUTETOIF);
667 		else
668 			error = ns_output(m, &cb->s_nspcb->nsp_route, 0);
669 		if (traceallspps && sppconsdebug) {
670 			printf("spp_out: %x\n", error);
671 		}
672 		return (error);
673 	}
674 	if (so->so_options & SO_DEBUG || traceallspps)
675 		spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
676 	return (error);
677 }
678 
679 /*ARGSUSED*/
680 spp_ctloutput(req, so, level, name, value)
681 	int req;
682 	struct socket *so;
683 	int name;
684 	struct mbuf **value;
685 {
686 	register struct mbuf *m;
687 	struct nspcb *nsp = sotonspcb(so);
688 	register struct sppcb *cb;
689 	int mask, error = 0;
690 
691 	if (level != NSPROTO_SPP) {
692 		/* This will have to be changed when we do more general
693 		   stacking of protocols */
694 		return (idp_ctloutput(req, so, level, name, value));
695 	}
696 	if (nsp == NULL) {
697 		error = EINVAL;
698 		goto release;
699 	} else
700 		cb = nstosppcb(nsp);
701 
702 	switch (req) {
703 
704 	case PRCO_GETOPT:
705 		if (value==NULL)
706 			return (EINVAL);
707 		m = m_get(M_DONTWAIT, MT_DATA);
708 		if (m==NULL)
709 			return (ENOBUFS);
710 		switch (name) {
711 
712 		case SO_HEADERS_ON_INPUT:
713 			mask = SF_HI;
714 			goto get_flags;
715 
716 		case SO_HEADERS_ON_OUTPUT:
717 			mask = SF_HO;
718 		get_flags:
719 			m->m_len = sizeof(short);
720 			m->m_off = MMAXOFF - sizeof(short);
721 			*mtod(m, short *) = cb->s_flags & mask;
722 			break;
723 
724 		case SO_LAST_HEADER:
725 			m->m_len = sizeof(struct sphdr);
726 			m->m_off = MMAXOFF - sizeof(struct sphdr);
727 			*mtod(m, struct sphdr *) = cb->s_rhdr;
728 			break;
729 
730 		case SO_DEFAULT_HEADERS:
731 			m->m_len = sizeof(struct spidp);
732 			m->m_off = MMAXOFF - sizeof(struct sphdr);
733 			*mtod(m, struct sphdr *) = cb->s_shdr.si_s;
734 		}
735 		*value = m;
736 		break;
737 
738 	case PRCO_SETOPT:
739 		switch (name) {
740 			int mask, *ok;
741 
742 		case SO_HEADERS_ON_INPUT:
743 			mask = SF_HI;
744 			goto set_head;
745 
746 		case SO_HEADERS_ON_OUTPUT:
747 			mask = SF_HO;
748 		set_head:
749 			if (value && *value) {
750 				ok = mtod(*value, int *);
751 				if (*ok)
752 					cb->s_flags |= mask;
753 				else
754 					cb->s_flags &= ~mask;
755 			} else error = EINVAL;
756 			break;
757 
758 		case SO_DEFAULT_HEADERS:
759 			{
760 				register struct sphdr *sp
761 						= mtod(*value, struct sphdr *);
762 				cb->s_dt = sp->sp_dt;
763 				cb->s_cc = sp->sp_cc & SP_EM;
764 			}
765 		}
766 		if (value && *value)
767 			m_freem(*value);
768 		break;
769 	}
770 	release:
771 		return (error);
772 }
773 
774 /*ARGSUSED*/
775 spp_usrreq(so, req, m, nam, rights)
776 	struct socket *so;
777 	int req;
778 	struct mbuf *m, *nam, *rights;
779 {
780 	struct nspcb *nsp = sotonspcb(so);
781 	register struct sppcb *cb;
782 	int s = splnet();
783 	int error = 0, ostate;
784 
785 	if (req == PRU_CONTROL)
786                 return (ns_control(so, (int)m, (caddr_t)nam,
787 			(struct ifnet *)rights));
788 	if (rights && rights->m_len) {
789 		error = EINVAL;
790 		goto release;
791 	}
792 	if (nsp == NULL) {
793 		if (req != PRU_ATTACH) {
794 			error = EINVAL;
795 			goto release;
796 		}
797 	} else
798 		cb = nstosppcb(nsp);
799 
800 	ostate = cb ? cb->s_state : 0;
801 
802 	switch (req) {
803 
804 	case PRU_ATTACH:
805 		if (nsp != NULL) {
806 			error = EISCONN;
807 			break;
808 		}
809 		error = ns_pcballoc(so, &nspcb);
810 		if (error)
811 			break;
812 		error = soreserve(so, 2048, 2048);
813 		if (error)
814 			break;
815 		nsp = sotonspcb(so);
816 		{
817 			struct mbuf *mm = m_getclr(M_DONTWAIT,MT_PCB);
818 
819 			if (mm==NULL) {
820 				error = ENOBUFS;
821 				break;
822 			}
823 			cb = mtod(mm, struct sppcb *);
824 			cb->s_state = TCPS_LISTEN;
825 			cb->s_flags = SF_HI | SF_HO;
826 			cb->s_snt = -1;
827 			cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
828 			cb->s_nspcb = nsp;
829 			nsp->nsp_pcb = (caddr_t) cb;
830 		}
831 		break;
832 
833 	case PRU_DETACH:
834 		if (nsp == NULL) {
835 			error = ENOTCONN;
836 			break;
837 		}
838 		if (cb->s_state > TCPS_LISTEN)
839 			cb = spp_disconnect(cb);
840 		else
841 			cb = spp_close(cb);
842 		break;
843 
844 	case PRU_BIND:
845 		error = ns_pcbbind(nsp, nam);
846 		break;
847 
848 	case PRU_LISTEN:
849 		if (nsp->nsp_lport == 0)
850 			error = ns_pcbbind(nsp, (struct mbuf *)0);
851 		if (error == 0)
852 			cb->s_state = TCPS_LISTEN;
853 		break;
854 
855 	/*
856 	 * Initiate connection to peer.
857 	 * Enter SYN_SENT state, and mark socket as connecting.
858 	 * Start keep-alive timer, setup prototype header,
859 	 * Send initial system packet requesting connection.
860 	 */
861 	case PRU_CONNECT:
862 		if (nsp->nsp_lport == 0) {
863 			error = ns_pcbbind(nsp, (struct mbuf *)0);
864 			if (error)
865 				break;
866 		}
867 		error = ns_pcbconnect(nsp, nam);
868 		if (error)
869 			break;
870 		soisconnecting(so);
871 		cb->s_state = TCPS_SYN_SENT;
872 		cb->s_did = 0;
873 		spp_template(cb);
874 		cb->s_timer[TCPT_KEEP] = TCPTV_KEEP;
875 		cb->s_force = 1 + TCPTV_KEEP;
876 		/*
877 		 * Other party is required to respond to
878 		 * the port I send from, but he is not
879 		 * required to answer from where I am sending to,
880 		 * so allow wildcarding.
881 		 * original port I am sending to is still saved in
882 		 * cb->s_dport.
883 		 */
884 		nsp->nsp_fport = 0;
885 		error = spp_output(cb, (struct mbuf *) 0);
886 		break;
887 
888 	case PRU_CONNECT2:
889 		error = EOPNOTSUPP;
890 		break;
891 
892 	/*
893 	 * We may decide later to implement connection closing
894 	 * handshaking at the spp level optionally.
895 	 * here is the hook to do it:
896 	 */
897 	case PRU_DISCONNECT:
898 		cb = spp_disconnect(cb);
899 		break;
900 
901 	/*
902 	 * Accept a connection.  Essentially all the work is
903 	 * done at higher levels; just return the address
904 	 * of the peer, storing through addr.
905 	 */
906 	case PRU_ACCEPT: {
907 		struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *);
908 
909 		nam->m_len = sizeof (struct sockaddr_ns);
910 		sns->sns_family = AF_NS;
911 		sns->sns_addr = nsp->nsp_faddr;
912 		break;
913 		}
914 
915 	case PRU_SHUTDOWN:
916 		socantsendmore(so);
917 		cb = spp_usrclosed(cb);
918 		if (cb)
919 			error = spp_output(cb, (struct mbuf *) 0);
920 		break;
921 
922 	/*
923 	 * After a receive, possibly send acknowledgment
924 	 * updating allocation.
925 	 */
926 	case PRU_RCVD:
927 		(void) spp_output(cb, (struct mbuf *) 0);
928 		break;
929 
930 	case PRU_SEND:
931 		error = spp_output(cb, m);
932 		m = NULL;
933 		break;
934 
935 	case PRU_ABORT:
936 		spp_drop(cb, ECONNABORTED);
937 		break;
938 
939 	case PRU_SENSE:
940 	case PRU_CONTROL:
941 		m = NULL;
942 		error = EOPNOTSUPP;
943 		break;
944 
945 	case PRU_RCVOOB:
946 		if ( ! (cb->s_oobflags & SF_IOOB) ) {
947 			error = EWOULDBLOCK;
948 			break;
949 		}
950 		m->m_len = 1;
951 		*mtod(m, caddr_t) = cb->s_iobc;
952 		cb->s_oobflags &= ~ SF_IOOB;
953 		break;
954 
955 	case PRU_SENDOOB:
956 		if (sbspace(&so->so_snd) < -512) {
957 			m_freem(m);
958 			error = ENOBUFS;
959 			break;
960 		}
961 		cb->s_oobflags |= SF_SOOB;
962 		error = spp_output(cb, m);
963 		m = NULL;
964 		cb->s_oobflags &= ~SF_SOOB;
965 		break;
966 
967 	case PRU_SOCKADDR:
968 		ns_setsockaddr(nsp, nam);
969 		break;
970 
971 	case PRU_PEERADDR:
972 		ns_setpeeraddr(nsp, nam);
973 		break;
974 
975 	case PRU_SLOWTIMO:
976 		cb = spp_timers(cb, (int)nam);
977 		break;
978 
979 	case PRU_FASTTIMO:
980 	case PRU_PROTORCV:
981 	case PRU_PROTOSEND:
982 		error =  EOPNOTSUPP;
983 		break;
984 
985 	default:
986 		panic("sp_usrreq");
987 	}
988 	if (cb && (so->so_options & SO_DEBUG || traceallspps))
989 		spp_trace(SA_USER, ostate, cb, (struct sphdr *)0, req);
990 release:
991 	if (m != NULL)
992 		m_freem(m);
993 	splx(s);
994 	return (error);
995 }
996 
997 spp_usrreq_sp(so, req, m, nam, rights)
998 	struct socket *so;
999 	int req;
1000 	struct mbuf *m, *nam, *rights;
1001 {
1002 	int error = spp_usrreq(so, req, m, nam, rights);
1003 
1004 	if (req==PRU_ATTACH && error==0) {
1005 		struct nspcb *nsp = sotonspcb(so);
1006 		((struct sppcb *)nsp->nsp_pcb)->s_flags |=
1007 					(SF_HI | SF_HO | SF_PI);
1008 	}
1009 	return (error);
1010 }
1011 
1012 /*
1013  * Create template to be used to send spp packets on a connection.
1014  * Called after host entry created, fills
1015  * in a skeletal spp header (choosing connection id),
1016  * minimizing the amount of work necessary when the connection is used.
1017  */
1018 spp_template(cb)
1019 	struct sppcb *cb;
1020 {
1021 	register struct nspcb *nsp = cb->s_nspcb;
1022 	register struct spidp *n = &(cb->s_shdr);
1023 
1024 	cb->s_mtu = 1024;
1025 	n->si_pt = NSPROTO_SPP;
1026 	n->si_sna = nsp->nsp_laddr;
1027 	n->si_dna = nsp->nsp_faddr;
1028 	n->si_sid = htons(spp_iss);
1029 	spp_iss += SPP_ISSINCR/2;
1030 	n->si_alo = 1;
1031 }
1032 
1033 /*
1034  * Close a SPIP control block:
1035  *	discard spp control block itself
1036  *	discard ns protocol control block
1037  *	wake up any sleepers
1038  */
1039 struct sppcb *
1040 spp_close(cb)
1041 	register struct sppcb *cb;
1042 {
1043 	register struct spidp_q *s;
1044 	struct nspcb *nsp = cb->s_nspcb;
1045 	struct socket *so = nsp->nsp_socket;
1046 	register struct mbuf *m;
1047 
1048 	s = cb->s_q.si_next;
1049 	while (s != &(cb->s_q)) {
1050 		s = s->si_next;
1051 		m = dtom(s->si_prev);
1052 		remque(s->si_prev);
1053 		m_freem(m);
1054 	}
1055 	(void) m_free(dtom(cb));
1056 	nsp->nsp_pcb = 0;
1057 	soisdisconnected(so);
1058 	ns_pcbdetach(nsp);
1059 	return ((struct sppcb *)0);
1060 }
1061 /*
1062  *	Someday we may do level 3 handshaking
1063  *	to close a connection or send a xerox style error.
1064  *	For now, just close.
1065  */
1066 struct sppcb *
1067 spp_usrclosed(cb)
1068 	register struct sppcb *cb;
1069 {
1070 	return (spp_close(cb));
1071 }
1072 struct sppcb *
1073 spp_disconnect(cb)
1074 	register struct sppcb *cb;
1075 {
1076 	return (spp_close(cb));
1077 }
1078 /*
1079  * Drop connection, reporting
1080  * the specified error.
1081  */
1082 struct sppcb *
1083 spp_drop(cb, errno)
1084 	register struct sppcb *cb;
1085 	int errno;
1086 {
1087 	struct socket *so = cb->s_nspcb->nsp_socket;
1088 
1089 	/*
1090 	 * someday, in the xerox world
1091 	 * we will generate error protocol packets
1092 	 * announcing that the socket has gone away.
1093 	 */
1094 	/*if (TCPS_HAVERCVDSYN(tp->t_state)) {
1095 		tp->t_state = TCPS_CLOSED;
1096 		(void) tcp_output(tp);
1097 	}*/
1098 	so->so_error = errno;
1099 	return (spp_close(cb));
1100 }
1101 
1102 spp_abort(nsp)
1103 	struct nspcb *nsp;
1104 {
1105 
1106 	spp_close((struct sppcb *)nsp->nsp_pcb);
1107 }
1108 
1109 spp_setpersist(cb)
1110 	register struct sppcb *cb;
1111 {
1112 
1113 	/*if (cb->s_timer[TCPT_REXMT])
1114 		panic("spp_output REXMT");*/
1115 	/*
1116 	 * Start/restart persistance timer.
1117 	 */
1118 	TCPT_RANGESET(cb->s_timer[TCPT_PERSIST],
1119 	    ((int)(tcp_beta * cb->s_srtt)) << cb->s_rxtshift,
1120 	    TCPTV_PERSMIN, TCPTV_MAX);
1121 	cb->s_rxtshift++;
1122 	if (cb->s_rxtshift >= TCP_MAXRXTSHIFT)
1123 		cb->s_rxtshift = 0;
1124 }
1125 /*
1126  * Fast timeout routine for processing delayed acks
1127  */
1128 int spp_ftcnt;
1129 spp_fasttimo()
1130 {
1131 	register struct nspcb *nsp;
1132 	register struct sppcb *cb;
1133 	int s = splnet();
1134 
1135 	nsp = nspcb.nsp_next;
1136 	spp_ftcnt++;
1137 	if (nsp)
1138 	for (; nsp != &nspcb; nsp = nsp->nsp_next)
1139 		if ((cb = (struct sppcb *)nsp->nsp_pcb) &&
1140 		    (cb->s_flags & SF_DELACK)) {
1141 			cb->s_flags &= ~SF_DELACK;
1142 			cb->s_flags |= SF_AK;
1143 			(void) spp_output(cb, (struct mbuf *) 0);
1144 		}
1145 	splx(s);
1146 }
1147 
1148 /*
1149  * spp protocol timeout routine called every 500 ms.
1150  * Updates the timers in all active pcb's and
1151  * causes finite state machine actions if timers expire.
1152  */
1153 spp_slowtimo()
1154 {
1155 	register struct nspcb *ip, *ipnxt;
1156 	register struct sppcb *cb;
1157 	int s = splnet();
1158 	register int i;
1159 
1160 	/*
1161 	 * Search through tcb's and update active timers.
1162 	 */
1163 	ip = nspcb.nsp_next;
1164 	if (ip == 0) {
1165 		splx(s);
1166 		return;
1167 	}
1168 	while (ip != &nspcb) {
1169 		cb = nstosppcb(ip);
1170 		ipnxt = ip->nsp_next;
1171 		if (cb == 0)
1172 			goto tpgone;
1173 		for (i = 0; i < TCPT_NTIMERS; i++) {
1174 			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1175 				(void) spp_usrreq(cb->s_nspcb->nsp_socket,
1176 				    PRU_SLOWTIMO, (struct mbuf *)0,
1177 				    (struct mbuf *)i, (struct mbuf *)0);
1178 				if (ipnxt->nsp_prev != ip)
1179 					goto tpgone;
1180 			}
1181 		}
1182 		cb->s_idle++;
1183 		if (cb->s_rtt)
1184 			cb->s_rtt++;
1185 tpgone:
1186 		ip = ipnxt;
1187 	}
1188 	spp_iss += SPP_ISSINCR/PR_SLOWHZ;		/* increment iss */
1189 	splx(s);
1190 }
1191 
1192 float	spp_backoff[TCP_MAXRXTSHIFT] =
1193     { 1.0, 1.2, 1.4, 1.7, 2.0, 3.0, 5.0, 8.0, 16.0, 32.0 };
1194 extern int tcpexprexmtbackoff;
1195 /*
1196  * TCP timer processing.
1197  */
1198 struct sppcb *
1199 spp_timers(cb, timer)
1200 	register struct sppcb *cb;
1201 	int timer;
1202 {
1203 
1204 	cb->s_force = 1 + timer;
1205 	switch (timer) {
1206 
1207 	/*
1208 	 * 2 MSL timeout in shutdown went off.  Delete connection
1209 	 * control block.
1210 	 */
1211 	case TCPT_2MSL:
1212 		cb = spp_close(cb);
1213 		break;
1214 
1215 	/*
1216 	 * Retransmission timer went off.  Message has not
1217 	 * been acked within retransmit interval.  Back off
1218 	 * to a longer retransmit interval and retransmit all
1219 	 * unacknowledged messages in the window.
1220 	 */
1221 	case TCPT_REXMT:
1222 		cb->s_rxtshift++;
1223 		if (cb->s_rxtshift > TCP_MAXRXTSHIFT) {
1224 			cb = spp_drop(cb, ETIMEDOUT);
1225 			break;
1226 		}
1227 		(void) spp_output(cb, (struct mbuf *) 0);
1228 		TCPT_RANGESET(cb->s_timer[TCPT_REXMT],
1229 		    (int)cb->s_srtt, TCPTV_MIN, TCPTV_MAX);
1230 		if (tcpexprexmtbackoff) {
1231 			TCPT_RANGESET(cb->s_timer[TCPT_REXMT],
1232 			    cb->s_timer[TCPT_REXMT] << cb->s_rxtshift,
1233 			    TCPTV_MIN, TCPTV_MAX);
1234 		} else {
1235 			TCPT_RANGESET(cb->s_timer[TCPT_REXMT],
1236 			    cb->s_timer[TCPT_REXMT] *
1237 			        spp_backoff[cb->s_rxtshift - 1],
1238 			    TCPTV_MIN, TCPTV_MAX);
1239 		}
1240 		break;
1241 
1242 	/*
1243 	 * Persistance timer into zero window.
1244 	 * Force a probe to be sent.
1245 	 */
1246 	case TCPT_PERSIST:
1247 		(void) spp_output(cb, (struct mbuf *) 0);
1248 		spp_setpersist(cb);
1249 		break;
1250 
1251 	/*
1252 	 * Keep-alive timer went off; send something
1253 	 * or drop connection if idle for too long.
1254 	 */
1255 	case TCPT_KEEP:
1256 		if (cb->s_state < TCPS_ESTABLISHED)
1257 			goto dropit;
1258 		if (cb->s_nspcb->nsp_socket->so_options & SO_KEEPALIVE) {
1259 		    	if (cb->s_idle >= TCPTV_MAXIDLE)
1260 				goto dropit;
1261 			(void) spp_output(cb, (struct mbuf *) 0);
1262 		} else
1263 			cb->s_idle = 0;
1264 		cb->s_timer[TCPT_KEEP] = TCPTV_KEEP;
1265 		break;
1266 	dropit:
1267 		cb = spp_drop(cb, ETIMEDOUT);
1268 		break;
1269 	}
1270 	return (cb);
1271 }
1272