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