move imp stuff to netimp; formalize op_opts storage; add new options
[unix-history] / usr / src / sys / netns / spp_usrreq.c
CommitLineData
8ae0e4b4 1/*
64b784d2 2 * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California.
240edf1f 3 * All rights reserved.
8ae0e4b4 4 *
240edf1f 5 * Redistribution and use in source and binary forms are permitted
616d42db
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
240edf1f 16 *
a737003a 17 * @(#)spp_usrreq.c 7.13 (Berkeley) %G%
8ae0e4b4 18 */
4258e601
KS
19
20#include "param.h"
c356f777 21#include "systm.h"
4258e601 22#include "user.h"
bf8d7cff 23#include "malloc.h"
4258e601
KS
24#include "mbuf.h"
25#include "protosw.h"
26#include "socket.h"
27#include "socketvar.h"
28#include "errno.h"
29
30#include "../net/if.h"
31#include "../net/route.h"
32#include "../netinet/tcp_fsm.h"
4258e601
KS
33
34#include "ns.h"
35#include "ns_pcb.h"
36#include "idp.h"
37#include "idp_var.h"
38#include "ns_error.h"
39#include "sp.h"
40#include "spidp.h"
863250c1 41#include "spp_timer.h"
4258e601
KS
42#include "spp_var.h"
43#include "spp_debug.h"
44
45/*
46 * SP protocol implementation.
47 */
48spp_init()
49{
50
51 spp_iss = 1; /* WRONG !! should fish it out of TODR */
52}
53struct spidp spp_savesi;
54int traceallspps = 0;
55extern int sppconsdebug;
4258e601 56int spp_hardnosed;
0314fc80 57int spp_use_delack = 0;
bf8d7cff 58u_short spp_newchecks[50];
8e189bed
KS
59
60/*ARGSUSED*/
bf8d7cff 61spp_input(m, nsp)
4258e601 62 register struct mbuf *m;
fe594349 63 register struct nspcb *nsp;
e3c149da 64{
4258e601
KS
65 register struct sppcb *cb;
66 register struct spidp *si = mtod(m, struct spidp *);
67 register struct socket *so;
f97be0c9 68 short ostate;
4258e601
KS
69 int dropsocket = 0;
70
71
64b784d2 72 sppstat.spps_rcvtotal++;
99e0091e 73 if (nsp == 0) {
e3c149da 74 panic("No nspcb in spp_input\n");
fe594349
KS
75 return;
76 }
4258e601
KS
77
78 cb = nstosppcb(nsp);
79 if (cb == 0) goto bad;
80
e3c149da 81 if (m->m_len < sizeof(*si)) {
99e0091e 82 if ((m = m_pullup(m, sizeof(*si))) == 0) {
64b784d2 83 sppstat.spps_rcvshort++;
e3c149da
KS
84 return;
85 }
86 si = mtod(m, struct spidp *);
87 }
4258e601
KS
88 si->si_seq = ntohs(si->si_seq);
89 si->si_ack = ntohs(si->si_ack);
90 si->si_alo = ntohs(si->si_alo);
91
92 so = nsp->nsp_socket;
93 if (so->so_options & SO_DEBUG || traceallspps) {
94 ostate = cb->s_state;
95 spp_savesi = *si;
96 }
97 if (so->so_options & SO_ACCEPTCONN) {
64b784d2 98 struct sppcb *ocb = cb;
4f5156ea 99
f8749f8c 100 so = sonewconn(so, 0);
4258e601 101 if (so == 0) {
4258e601
KS
102 goto drop;
103 }
104 /*
105 * This is ugly, but ....
106 *
107 * Mark socket as temporary until we're
108 * committed to keeping it. The code at
109 * ``drop'' and ``dropwithreset'' check the
110 * flag dropsocket to see if the temporary
111 * socket created here should be discarded.
112 * We mark the socket as discardable until
113 * we're committed to it below in TCPS_LISTEN.
114 */
115 dropsocket++;
116 nsp = (struct nspcb *)so->so_pcb;
117 nsp->nsp_laddr = si->si_dna;
118 cb = nstosppcb(nsp);
64b784d2
KS
119 cb->s_mtu = ocb->s_mtu; /* preserve sockopts */
120 cb->s_flags = ocb->s_flags; /* preserve sockopts */
bf8d7cff 121 cb->s_flags2 = ocb->s_flags2; /* preserve sockopts */
4258e601
KS
122 cb->s_state = TCPS_LISTEN;
123 }
124
125 /*
126 * Packet received on connection.
127 * reset idle time and keep-alive timer;
128 */
129 cb->s_idle = 0;
863250c1 130 cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
4258e601
KS
131
132 switch (cb->s_state) {
9ad31c34 133
4258e601
KS
134 case TCPS_LISTEN:{
135 struct mbuf *am;
136 register struct sockaddr_ns *sns;
137 struct ns_addr laddr;
138
139 /*
140 * If somebody here was carying on a conversation
141 * and went away, and his pen pal thinks he can
142 * still talk, we get the misdirected packet.
143 */
144 if (spp_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
145 spp_istat.gonawy++;
146 goto dropwithreset;
147 }
148 am = m_get(M_DONTWAIT, MT_SONAME);
149 if (am == NULL)
150 goto drop;
151 am->m_len = sizeof (struct sockaddr_ns);
152 sns = mtod(am, struct sockaddr_ns *);
4dcdd98e 153 sns->sns_len = sizeof(*sns);
4258e601
KS
154 sns->sns_family = AF_NS;
155 sns->sns_addr = si->si_sna;
156 laddr = nsp->nsp_laddr;
157 if (ns_nullhost(laddr))
158 nsp->nsp_laddr = si->si_dna;
159 if (ns_pcbconnect(nsp, am)) {
160 nsp->nsp_laddr = laddr;
161 (void) m_free(am);
162 spp_istat.noconn++;
163 goto drop;
164 }
165 (void) m_free(am);
4258e601 166 spp_template(cb);
d8e9f818 167 dropsocket = 0; /* committed to socket */
4258e601
KS
168 cb->s_did = si->si_sid;
169 cb->s_rack = si->si_ack;
170 cb->s_ralo = si->si_alo;
d8e9f818
KS
171#define THREEWAYSHAKE
172#ifdef THREEWAYSHAKE
173 cb->s_state = TCPS_SYN_RECEIVED;
863250c1 174 cb->s_force = 1 + SPPT_KEEP;
64b784d2 175 sppstat.spps_accepts++;
863250c1 176 cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
4258e601
KS
177 }
178 break;
d8e9f818
KS
179 /*
180 * This state means that we have heard a response
181 * to our acceptance of their connection
182 * It is probably logically unnecessary in this
183 * implementation.
184 */
64b784d2 185 case TCPS_SYN_RECEIVED: {
d8e9f818
KS
186 if (si->si_did!=cb->s_sid) {
187 spp_istat.wrncon++;
188 goto drop;
189 }
190#endif
191 nsp->nsp_fport = si->si_sport;
863250c1
MK
192 cb->s_timer[SPPT_REXMT] = 0;
193 cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
d8e9f818
KS
194 soisconnected(so);
195 cb->s_state = TCPS_ESTABLISHED;
64b784d2
KS
196 sppstat.spps_accepts++;
197 }
d8e9f818 198 break;
4258e601
KS
199
200 /*
201 * This state means that we have gotten a response
202 * to our attempt to establish a connection.
fe594349
KS
203 * We fill in the data from the other side,
204 * telling us which port to respond to, instead of the well-
205 * known one we might have sent to in the first place.
4258e601 206 * We also require that this is a response to our
fe594349 207 * connection id.
4258e601
KS
208 */
209 case TCPS_SYN_SENT:
210 if (si->si_did!=cb->s_sid) {
211 spp_istat.notme++;
212 goto drop;
213 }
64b784d2 214 sppstat.spps_connects++;
4258e601
KS
215 cb->s_did = si->si_sid;
216 cb->s_rack = si->si_ack;
217 cb->s_ralo = si->si_alo;
218 cb->s_dport = nsp->nsp_fport = si->si_sport;
863250c1 219 cb->s_timer[SPPT_REXMT] = 0;
64b784d2 220 cb->s_flags |= SF_ACKNOW;
4258e601
KS
221 soisconnected(so);
222 cb->s_state = TCPS_ESTABLISHED;
64b784d2
KS
223 /* Use roundtrip time of connection request for initial rtt */
224 if (cb->s_rtt) {
225 cb->s_srtt = cb->s_rtt << 3;
226 cb->s_rttvar = cb->s_rtt << 1;
863250c1 227 SPPT_RANGESET(cb->s_rxtcur,
64b784d2 228 ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
863250c1 229 SPPTV_MIN, SPPTV_REXMTMAX);
64b784d2
KS
230 cb->s_rtt = 0;
231 }
4258e601
KS
232 }
233 if (so->so_options & SO_DEBUG || traceallspps)
f97be0c9 234 spp_trace(SA_INPUT, (u_char)ostate, cb, &spp_savesi, 0);
4258e601
KS
235
236 m->m_len -= sizeof (struct idp);
bf8d7cff
KS
237 m->m_pkthdr.len -= sizeof (struct idp);
238 m->m_data += sizeof (struct idp);
4258e601 239
0314fc80 240 if (spp_reass(cb, si)) {
ea516bcb 241 (void) m_freem(m);
4258e601 242 }
64b784d2
KS
243 if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
244 (void) spp_output(cb, (struct mbuf *)0);
245 cb->s_flags &= ~(SF_WIN|SF_RXT);
4258e601
KS
246 return;
247
248dropwithreset:
249 if (dropsocket)
250 (void) soabort(so);
251 si->si_seq = ntohs(si->si_seq);
252 si->si_ack = ntohs(si->si_ack);
253 si->si_alo = ntohs(si->si_alo);
254 ns_error(dtom(si), NS_ERR_NOSOCK, 0);
255 if (cb->s_nspcb->nsp_socket->so_options & SO_DEBUG || traceallspps)
f97be0c9 256 spp_trace(SA_DROP, (u_char)ostate, cb, &spp_savesi, 0);
4258e601
KS
257 return;
258
259drop:
260bad:
64b784d2
KS
261 if (cb == 0 || cb->s_nspcb->nsp_socket->so_options & SO_DEBUG ||
262 traceallspps)
f97be0c9 263 spp_trace(SA_DROP, (u_char)ostate, cb, &spp_savesi, 0);
4258e601
KS
264 m_freem(m);
265}
266
64b784d2
KS
267int spprexmtthresh = 3;
268
4258e601
KS
269/*
270 * This is structurally similar to the tcp reassembly routine
271 * but its function is somewhat different: It merely queues
272 * packets up, and suppresses duplicates.
273 */
0314fc80 274spp_reass(cb, si)
4258e601
KS
275register struct sppcb *cb;
276register struct spidp *si;
277{
278 register struct spidp_q *q;
279 register struct mbuf *m;
64b784d2 280 register struct socket *so = cb->s_nspcb->nsp_socket;
4258e601 281 char packetp = cb->s_flags & SF_HI;
64b784d2 282 int incr;
4258e601
KS
283 char wakeup = 0;
284
99e0091e 285 if (si == SI(0))
4258e601
KS
286 goto present;
287 /*
288 * Update our news from them.
289 */
290 if (si->si_cc & SP_SA)
64b784d2
KS
291 cb->s_flags |= (spp_use_delack ? SF_DELACK : SF_ACKNOW);
292 if (SSEQ_GT(si->si_alo, cb->s_ralo))
293 cb->s_flags |= SF_WIN;
294 if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
ea516bcb 295 if ((si->si_cc & SP_SP) && cb->s_rack != (cb->s_smax + 1)) {
64b784d2
KS
296 sppstat.spps_rcvdupack++;
297 /*
298 * If this is a completely duplicate ack
299 * and other conditions hold, we assume
300 * a packet has been dropped and retransmit
301 * it exactly as in tcp_input().
302 */
303 if (si->si_ack != cb->s_rack ||
304 si->si_alo != cb->s_ralo)
305 cb->s_dupacks = 0;
306 else if (++cb->s_dupacks == spprexmtthresh) {
307 u_short onxt = cb->s_snxt;
308 int cwnd = cb->s_cwnd;
309
310 cb->s_snxt = si->si_ack;
311 cb->s_cwnd = CUNIT;
863250c1 312 cb->s_force = 1 + SPPT_REXMT;
ea516bcb 313 (void) spp_output(cb, (struct mbuf *)0);
863250c1 314 cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
64b784d2
KS
315 cb->s_rtt = 0;
316 if (cwnd >= 4 * CUNIT)
317 cb->s_cwnd = cwnd / 2;
318 if (SSEQ_GT(onxt, cb->s_snxt))
319 cb->s_snxt = onxt;
320 return (1);
321 }
0314fc80 322 } else
64b784d2
KS
323 cb->s_dupacks = 0;
324 goto update_window;
4258e601 325 }
64b784d2
KS
326 cb->s_dupacks = 0;
327 /*
328 * If our correspondent acknowledges data we haven't sent
329 * TCP would drop the packet after acking. We'll be a little
330 * more permissive
331 */
332 if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
333 sppstat.spps_rcvacktoomuch++;
334 si->si_ack = cb->s_smax + 1;
4258e601 335 }
64b784d2 336 sppstat.spps_rcvackpack++;
4258e601 337 /*
64b784d2
KS
338 * If transmit timer is running and timed sequence
339 * number was acked, update smoothed round trip time.
340 * See discussion of algorithm in tcp_input.c
4258e601 341 */
64b784d2
KS
342 if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
343 sppstat.spps_rttupdated++;
344 if (cb->s_srtt != 0) {
345 register short delta;
346 delta = cb->s_rtt - (cb->s_srtt >> 3);
347 if ((cb->s_srtt += delta) <= 0)
348 cb->s_srtt = 1;
349 if (delta < 0)
350 delta = -delta;
351 delta -= (cb->s_rttvar >> 2);
352 if ((cb->s_rttvar += delta) <= 0)
353 cb->s_rttvar = 1;
354 } else {
355 /*
356 * No rtt measurement yet
357 */
358 cb->s_srtt = cb->s_rtt << 3;
359 cb->s_rttvar = cb->s_rtt << 1;
360 }
361 cb->s_rtt = 0;
362 cb->s_rxtshift = 0;
863250c1 363 SPPT_RANGESET(cb->s_rxtcur,
64b784d2 364 ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
863250c1 365 SPPTV_MIN, SPPTV_REXMTMAX);
ca1bb209 366 }
4258e601 367 /*
64b784d2
KS
368 * If all outstanding data is acked, stop retransmit
369 * timer and remember to restart (more output or persist).
370 * If there is more data to be acked, restart retransmit
371 * timer, using current (possibly backed-off) value;
4258e601 372 */
64b784d2 373 if (si->si_ack == cb->s_smax + 1) {
863250c1 374 cb->s_timer[SPPT_REXMT] = 0;
64b784d2 375 cb->s_flags |= SF_RXT;
863250c1
MK
376 } else if (cb->s_timer[SPPT_PERSIST] == 0)
377 cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
64b784d2
KS
378 /*
379 * When new data is acked, open the congestion window.
380 * If the window gives us less than ssthresh packets
381 * in flight, open exponentially (maxseg at a time).
382 * Otherwise open linearly (maxseg^2 / cwnd at a time).
383 */
384 incr = CUNIT;
385 if (cb->s_cwnd > cb->s_ssthresh)
bf8d7cff
KS
386 incr = max(incr * incr / cb->s_cwnd, 1);
387 cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
64b784d2
KS
388 /*
389 * Trim Acked data from output queue.
390 */
ea516bcb 391 while ((m = so->so_snd.sb_mb) != NULL) {
64b784d2
KS
392 if (SSEQ_LT((mtod(m, struct spidp *))->si_seq, si->si_ack))
393 sbdroprecord(&so->so_snd);
394 else
395 break;
396 }
bf8d7cff 397 sowwakeup(so);
64b784d2
KS
398 cb->s_rack = si->si_ack;
399update_window:
400 if (SSEQ_LT(cb->s_snxt, cb->s_rack))
401 cb->s_snxt = cb->s_rack;
402 if (SSEQ_LT(cb->s_swl1, si->si_seq) || cb->s_swl1 == si->si_seq &&
403 (SSEQ_LT(cb->s_swl2, si->si_ack) ||
404 cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo))) {
405 /* keep track of pure window updates */
406 if ((si->si_cc & SP_SP) && cb->s_swl2 == si->si_ack
407 && SSEQ_LT(cb->s_ralo, si->si_alo)) {
408 sppstat.spps_rcvwinupd++;
409 sppstat.spps_rcvdupack--;
410 }
411 cb->s_ralo = si->si_alo;
412 cb->s_swl1 = si->si_seq;
413 cb->s_swl2 = si->si_ack;
414 cb->s_swnd = (1 + si->si_alo - si->si_ack);
415 if (cb->s_swnd > cb->s_smxw)
416 cb->s_smxw = cb->s_swnd;
417 cb->s_flags |= SF_WIN;
fe594349 418 }
4258e601
KS
419 /*
420 * If this packet number is higher than that which
421 * we have allocated refuse it, unless urgent
422 */
0314fc80 423 if (SSEQ_GT(si->si_seq, cb->s_alo)) {
64b784d2
KS
424 if (si->si_cc & SP_SP) {
425 sppstat.spps_rcvwinprobe++;
426 return (1);
427 } else
428 sppstat.spps_rcvpackafterwin++;
0314fc80
KS
429 if (si->si_cc & SP_OB) {
430 if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) {
431 ns_error(dtom(si), NS_ERR_FULLUP, 0);
432 return (0);
433 } /* else queue this packet; */
434 } else {
64b784d2
KS
435 /*register struct socket *so = cb->s_nspcb->nsp_socket;
436 if (so->so_state && SS_NOFDREF) {
437 ns_error(dtom(si), NS_ERR_NOSOCK, 0);
438 (void)spp_close(cb);
439 } else
440 would crash system*/
0314fc80 441 spp_istat.notyet++;
64b784d2
KS
442 ns_error(dtom(si), NS_ERR_FULLUP, 0);
443 return (0);
0314fc80 444 }
4258e601 445 }
64b784d2
KS
446 /*
447 * If this is a system packet, we don't need to
448 * queue it up, and won't update acknowledge #
449 */
450 if (si->si_cc & SP_SP) {
451 return (1);
452 }
453 /*
454 * We have already seen this packet, so drop.
455 */
456 if (SSEQ_LT(si->si_seq, cb->s_ack)) {
457 spp_istat.bdreas++;
458 sppstat.spps_rcvduppack++;
459 if (si->si_seq == cb->s_ack - 1)
460 spp_istat.lstdup++;
461 return (1);
462 }
4258e601
KS
463 /*
464 * Loop through all packets queued up to insert in
465 * appropriate sequence.
466 */
4258e601 467 for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) {
64b784d2
KS
468 if (si->si_seq == SI(q)->si_seq) {
469 sppstat.spps_rcvduppack++;
470 return (1);
471 }
472 if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
473 sppstat.spps_rcvoopack++;
474 break;
475 }
0314fc80
KS
476 }
477 insque(si, q->si_prev);
478 /*
479 * If this packet is urgent, inform process
480 */
481 if (si->si_cc & SP_OB) {
482 cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
483 sohasoutofband(so);
484 cb->s_oobflags |= SF_IOOB;
4258e601 485 }
4258e601
KS
486present:
487#define SPINC sizeof(struct sphdr)
488 /*
489 * Loop through all packets queued up to update acknowledge
490 * number, and present all acknowledged data to user;
491 * If in packet interface mode, show packet headers.
492 */
493 for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) {
99e0091e 494 if (SI(q)->si_seq == cb->s_ack) {
4258e601
KS
495 cb->s_ack++;
496 m = dtom(q);
497 if (SI(q)->si_cc & SP_OB) {
0314fc80 498 cb->s_oobflags &= ~SF_IOOB;
64b784d2
KS
499 if (so->so_rcv.sb_cc)
500 so->so_oobmark = so->so_rcv.sb_cc;
4258e601
KS
501 else
502 so->so_state |= SS_RCVATMARK;
503 }
504 q = q->si_prev;
505 remque(q->si_next);
506 wakeup = 1;
64b784d2 507 sppstat.spps_rcvpack++;
bf8d7cff
KS
508#ifdef SF_NEWCALL
509 if (cb->s_flags2 & SF_NEWCALL) {
510 struct sphdr *sp = mtod(m, struct sphdr *);
511 u_char dt = sp->sp_dt;
512 spp_newchecks[4]++;
513 if (dt != cb->s_rhdr.sp_dt) {
514 struct mbuf *mm =
515 m_getclr(M_DONTWAIT, MT_CONTROL);
516 spp_newchecks[0]++;
517 if (mm != NULL) {
518 u_short *s =
519 mtod(mm, u_short *);
520 cb->s_rhdr.sp_dt = dt;
521 mm->m_len = 5; /*XXX*/
522 s[0] = 5;
523 s[1] = 1;
524 *(u_char *)(&s[2]) = dt;
525 sbappend(&so->so_rcv, mm);
526 }
527 }
528 if (sp->sp_cc & SP_OB) {
529 MCHTYPE(m, MT_OOBDATA);
530 spp_newchecks[1]++;
531 so->so_oobmark = 0;
532 so->so_state &= ~SS_RCVATMARK;
533 }
534 if (packetp == 0) {
535 m->m_data += SPINC;
536 m->m_len -= SPINC;
537 m->m_pkthdr.len -= SPINC;
538 }
539 if ((sp->sp_cc & SP_EM) || packetp) {
540 sbappendrecord(&so->so_rcv, m);
541 spp_newchecks[9]++;
542 } else
543 sbappend(&so->so_rcv, m);
544 } else
545#endif
4258e601 546 if (packetp) {
64b784d2 547 sbappendrecord(&so->so_rcv, m);
4258e601
KS
548 } else {
549 cb->s_rhdr = *mtod(m, struct sphdr *);
bf8d7cff 550 m->m_data += SPINC;
4258e601 551 m->m_len -= SPINC;
bf8d7cff 552 m->m_pkthdr.len -= SPINC;
64b784d2 553 sbappend(&so->so_rcv, m);
4258e601
KS
554 }
555 } else
556 break;
557 }
558 if (wakeup) sorwakeup(so);
9ad31c34 559 return (0);
4258e601
KS
560}
561
562spp_ctlinput(cmd, arg)
563 int cmd;
564 caddr_t arg;
565{
566 struct ns_addr *na;
567 extern u_char nsctlerrmap[];
64b784d2 568 extern spp_abort(), spp_quench();
f97be0c9 569 extern struct nspcb *idp_drop();
fe594349
KS
570 struct ns_errp *errp;
571 struct nspcb *nsp;
8e189bed 572 struct sockaddr_ns *sns;
4258e601
KS
573 int type;
574
575 if (cmd < 0 || cmd > PRC_NCMDS)
576 return;
577 type = NS_ERR_UNREACH_HOST;
578
579 switch (cmd) {
9ad31c34 580
4258e601 581 case PRC_ROUTEDEAD:
64b784d2 582 return;
4258e601
KS
583
584 case PRC_IFDOWN:
4258e601
KS
585 case PRC_HOSTDEAD:
586 case PRC_HOSTUNREACH:
8e189bed
KS
587 sns = (struct sockaddr_ns *)arg;
588 if (sns->sns_family != AF_NS)
589 return;
590 na = &sns->sns_addr;
4258e601
KS
591 break;
592
593 default:
fe594349
KS
594 errp = (struct ns_errp *)arg;
595 na = &errp->ns_err_idp.idp_dna;
596 type = errp->ns_err_num;
f97be0c9 597 type = ntohs((u_short)type);
4258e601
KS
598 }
599 switch (type) {
9ad31c34 600
4258e601 601 case NS_ERR_UNREACH_HOST:
fe594349 602 ns_pcbnotify(na, (int)nsctlerrmap[cmd], spp_abort, (long) 0);
4258e601 603 break;
9ad31c34 604
4258e601 605 case NS_ERR_TOO_BIG:
fe594349
KS
606 case NS_ERR_NOSOCK:
607 nsp = ns_pcblookup(na, errp->ns_err_idp.idp_sna.x_port,
608 NS_WILDCARD);
609 if (nsp) {
610 if(nsp->nsp_pcb)
f97be0c9
KS
611 (void) spp_drop((struct sppcb *)nsp->nsp_pcb,
612 (int)nsctlerrmap[cmd]);
fe594349 613 else
f97be0c9 614 (void) idp_drop(nsp, (int)nsctlerrmap[cmd]);
fe594349 615 }
64b784d2
KS
616 break;
617
618 case NS_ERR_FULLUP:
619 ns_pcbnotify(na, 0, spp_quench, (long) 0);
4258e601
KS
620 }
621}
64b784d2
KS
622/*
623 * When a source quench is received, close congestion window
624 * to one packet. We will gradually open it again as we proceed.
625 */
626spp_quench(nsp)
627 struct nspcb *nsp;
628{
629 struct sppcb *cb = nstosppcb(nsp);
630
631 if (cb)
632 cb->s_cwnd = CUNIT;
633}
4258e601 634
f97be0c9 635#ifdef notdef
4258e601
KS
636int
637spp_fixmtu(nsp)
638register struct nspcb *nsp;
639{
640 register struct sppcb *cb = (struct sppcb *)(nsp->nsp_pcb);
641 register struct mbuf *m;
642 register struct spidp *si;
643 struct ns_errp *ep;
644 struct sockbuf *sb;
645 int badseq, len;
646 struct mbuf *firstbad, *m0;
647
648 if (cb) {
649 /*
650 * The notification that we have sent
651 * too much is bad news -- we will
652 * have to go through queued up so far
653 * splitting ones which are too big and
654 * reassigning sequence numbers and checksums.
655 * we should then retransmit all packets from
656 * one above the offending packet to the last one
657 * we had sent (or our allocation)
658 * then the offending one so that the any queued
659 * data at our destination will be discarded.
660 */
661 ep = (struct ns_errp *)nsp->nsp_notify_param;
662 sb = &nsp->nsp_socket->so_snd;
663 cb->s_mtu = ep->ns_err_param;
664 badseq = SI(&ep->ns_err_idp)->si_seq;
665 for (m = sb->sb_mb; m; m = m->m_act) {
666 si = mtod(m, struct spidp *);
667 if (si->si_seq == badseq)
668 break;
669 }
99e0091e 670 if (m == 0) return;
4258e601
KS
671 firstbad = m;
672 /*for (;;) {*/
673 /* calculate length */
674 for (m0 = m, len = 0; m ; m = m->m_next)
675 len += m->m_len;
676 if (len > cb->s_mtu) {
677 }
678 /* FINISH THIS
679 } */
680 }
681}
f97be0c9 682#endif
4258e601 683
4258e601
KS
684spp_output(cb, m0)
685 register struct sppcb *cb;
686 struct mbuf *m0;
687{
688 struct socket *so = cb->s_nspcb->nsp_socket;
689 register struct mbuf *m;
690 register struct spidp *si = (struct spidp *) 0;
64b784d2
KS
691 register struct sockbuf *sb = &so->so_snd;
692 int len = 0, win, rcv_win;
bf8d7cff 693 short span, off, recordp = 0;
ea516bcb 694 u_short alo;
4f5156ea
MK
695 int error = 0, sendalot;
696#ifdef notdef
697 int idle;
698#endif
4258e601
KS
699 struct mbuf *mprev;
700 extern int idpcksum;
701
99e0091e 702 if (m0) {
8e189bed
KS
703 int mtu = cb->s_mtu;
704 int datalen;
705 /*
706 * Make sure that packet isn't too big.
707 */
4258e601
KS
708 for (m = m0; m ; m = m->m_next) {
709 mprev = m;
710 len += m->m_len;
bf8d7cff
KS
711 if (m->m_flags & M_EOR)
712 recordp = 1;
4258e601 713 }
8e189bed
KS
714 datalen = (cb->s_flags & SF_HO) ?
715 len - sizeof (struct sphdr) : len;
716 if (datalen > mtu) {
4258e601
KS
717 if (cb->s_flags & SF_PI) {
718 m_freem(m0);
9ad31c34 719 return (EMSGSIZE);
4258e601 720 } else {
8e189bed
KS
721 int oldEM = cb->s_cc & SP_EM;
722
723 cb->s_cc &= ~SP_EM;
4258e601 724 while (len > mtu) {
4dcdd98e
KS
725 /*
726 * Here we are only being called
727 * from usrreq(), so it is OK to
728 * block.
729 */
730 m = m_copym(m0, 0, mtu, M_WAIT);
bf8d7cff
KS
731 if (cb->s_flags & SF_NEWCALL) {
732 struct mbuf *mm = m;
733 spp_newchecks[7]++;
734 while (mm) {
735 mm->m_flags &= ~M_EOR;
736 mm = mm->m_next;
737 }
738 }
4258e601
KS
739 error = spp_output(cb, m);
740 if (error) {
8e189bed 741 cb->s_cc |= oldEM;
4258e601 742 m_freem(m0);
8e189bed 743 return(error);
4258e601
KS
744 }
745 m_adj(m0, mtu);
746 len -= mtu;
747 }
8e189bed 748 cb->s_cc |= oldEM;
4258e601
KS
749 }
750 }
99e0091e
KS
751 /*
752 * Force length even, by adding a "garbage byte" if
753 * necessary.
754 */
4258e601 755 if (len & 1) {
fe594349 756 m = mprev;
bf8d7cff 757 if (M_TRAILINGSPACE(m) >= 1)
4258e601 758 m->m_len++;
99e0091e 759 else {
4258e601
KS
760 struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
761
762 if (m1 == 0) {
763 m_freem(m0);
764 return (ENOBUFS);
765 }
766 m1->m_len = 1;
bf8d7cff 767 *(mtod(m1, u_char *)) = 0;
99e0091e 768 m->m_next = m1;
4258e601
KS
769 }
770 }
bf8d7cff 771 m = m_gethdr(M_DONTWAIT, MT_HEADER);
4258e601
KS
772 if (m == 0) {
773 m_freem(m0);
9ad31c34 774 return (ENOBUFS);
4258e601 775 }
4258e601
KS
776 /*
777 * Fill in mbuf with extended SP header
778 * and addresses and length put into network format.
779 */
bf8d7cff 780 MH_ALIGN(m, sizeof (struct spidp));
4258e601
KS
781 m->m_len = sizeof (struct spidp);
782 m->m_next = m0;
783 si = mtod(m, struct spidp *);
64b784d2
KS
784 si->si_i = *cb->s_idp;
785 si->si_s = cb->s_shdr;
4258e601 786 if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
99e0091e
KS
787 register struct sphdr *sh;
788 if (m0->m_len < sizeof (*sh)) {
789 if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
790 (void) m_free(m);
791 m_freem(m0);
792 return (EINVAL);
793 }
794 m->m_next = m0;
795 }
796 sh = mtod(m0, struct sphdr *);
4258e601
KS
797 si->si_dt = sh->sp_dt;
798 si->si_cc |= sh->sp_cc & SP_EM;
799 m0->m_len -= sizeof (*sh);
bf8d7cff 800 m0->m_data += sizeof (*sh);
4258e601
KS
801 len -= sizeof (*sh);
802 }
803 len += sizeof(*si);
bf8d7cff
KS
804 if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
805 si->si_cc |= SP_EM;
806 spp_newchecks[8]++;
807 }
fe594349
KS
808 if (cb->s_oobflags & SF_SOOB) {
809 /*
810 * Per jqj@cornell:
811 * make sure OB packets convey exactly 1 byte.
812 * If the packet is 1 byte or larger, we
813 * have already guaranted there to be at least
814 * one garbage byte for the checksum, and
815 * extra bytes shouldn't hurt!
fe594349
KS
816 */
817 if (len > sizeof(*si)) {
818 si->si_cc |= SP_OB;
819 len = (1 + sizeof(*si));
820 }
821 }
f97be0c9 822 si->si_len = htons((u_short)len);
bf8d7cff 823 m->m_pkthdr.len = ((len - 1) | 1) + 1;
4258e601
KS
824 /*
825 * queue stuff up for output
826 */
0314fc80 827 sbappendrecord(sb, m);
4258e601
KS
828 cb->s_seq++;
829 }
4f5156ea 830#ifdef notdef
64b784d2 831 idle = (cb->s_smax == (cb->s_rack - 1));
4f5156ea 832#endif
64b784d2
KS
833again:
834 sendalot = 0;
835 off = cb->s_snxt - cb->s_rack;
bf8d7cff 836 win = min(cb->s_swnd, (cb->s_cwnd/CUNIT));
64b784d2 837
4258e601 838 /*
64b784d2
KS
839 * If in persist timeout with window of 0, send a probe.
840 * Otherwise, if window is small but nonzero
841 * and timer expired, send what we can and go into
842 * transmit state.
4258e601 843 */
863250c1 844 if (cb->s_force == 1 + SPPT_PERSIST) {
64b784d2 845 if (win != 0) {
863250c1 846 cb->s_timer[SPPT_PERSIST] = 0;
64b784d2
KS
847 cb->s_rxtshift = 0;
848 }
849 }
850 span = cb->s_seq - cb->s_rack;
bf8d7cff 851 len = min(span, win) - off;
64b784d2
KS
852
853 if (len < 0) {
854 /*
855 * Window shrank after we went into it.
856 * If window shrank to 0, cancel pending
857 * restransmission and pull s_snxt back
858 * to (closed) window. We will enter persist
859 * state below. If the widndow didn't close completely,
860 * just wait for an ACK.
861 */
862 len = 0;
863 if (win == 0) {
863250c1 864 cb->s_timer[SPPT_REXMT] = 0;
64b784d2 865 cb->s_snxt = cb->s_rack;
d8e9f818 866 }
4258e601 867 }
64b784d2
KS
868 if (len > 1)
869 sendalot = 1;
870 rcv_win = sbspace(&so->so_rcv);
4258e601 871
64b784d2
KS
872 /*
873 * Send if we owe peer an ACK.
874 */
4258e601
KS
875 if (cb->s_oobflags & SF_SOOB) {
876 /*
877 * must transmit this out of band packet
878 */
879 cb->s_oobflags &= ~ SF_SOOB;
64b784d2
KS
880 sendalot = 1;
881 sppstat.spps_sndurg++;
882 goto found;
883 }
884 if (cb->s_flags & SF_ACKNOW)
885 goto send;
886 if (cb->s_state < TCPS_ESTABLISHED)
887 goto send;
888 /*
889 * Silly window can't happen in spp.
890 * Code from tcp deleted.
891 */
892 if (len)
893 goto send;
894 /*
895 * Compare available window to amount of window
896 * known to peer (as advertised window less
897 * next expected input.) If the difference is at least two
898 * packets or at least 35% of the mximum possible window,
899 * then want to send a window update to peer.
900 */
901 if (rcv_win > 0) {
902 u_short delta = 1 + cb->s_alo - cb->s_ack;
903 int adv = rcv_win - (delta * cb->s_mtu);
904
905 if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
906 (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
907 sppstat.spps_sndwinup++;
908 cb->s_flags |= SF_ACKNOW;
909 goto send;
4258e601 910 }
64b784d2
KS
911
912 }
913 /*
914 * Many comments from tcp_output.c are appropriate here
915 * including . . .
916 * If send window is too small, there is data to transmit, and no
917 * retransmit or persist is pending, then go to persist state.
918 * If nothing happens soon, send when timer expires:
919 * if window is nonzero, transmit what we can,
920 * otherwise send a probe.
921 */
863250c1
MK
922 if (so->so_snd.sb_cc && cb->s_timer[SPPT_REXMT] == 0 &&
923 cb->s_timer[SPPT_PERSIST] == 0) {
64b784d2
KS
924 cb->s_rxtshift = 0;
925 spp_setpersist(cb);
926 }
927 /*
928 * No reason to send a packet, just return.
929 */
930 cb->s_outx = 1;
931 return (0);
932
933send:
934 /*
935 * Find requested packet.
936 */
937 si = 0;
938 if (len > 0) {
939 cb->s_want = cb->s_snxt;
940 for (m = sb->sb_mb; m; m = m->m_act) {
4258e601 941 si = mtod(m, struct spidp *);
64b784d2
KS
942 if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
943 break;
944 }
945 found:
946 if (si) {
947 if (si->si_seq == cb->s_snxt)
948 cb->s_snxt++;
949 else
950 sppstat.spps_sndvoid++, si = 0;
4258e601 951 }
4258e601 952 }
64b784d2
KS
953 /*
954 * update window
955 */
956 if (rcv_win < 0)
957 rcv_win = 0;
ea516bcb 958 alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
64b784d2
KS
959 if (SSEQ_LT(alo, cb->s_alo))
960 alo = cb->s_alo;
4258e601
KS
961
962 if (si) {
963 /*
964 * must make a copy of this packet for
965 * idp_output to monkey with
966 */
64b784d2
KS
967 m = m_copy(dtom(si), 0, (int)M_COPYALL);
968 if (m == NULL) {
9ad31c34 969 return (ENOBUFS);
64b784d2 970 }
64b784d2
KS
971 si = mtod(m, struct spidp *);
972 if (SSEQ_LT(si->si_seq, cb->s_smax))
973 sppstat.spps_sndrexmitpack++;
974 else
975 sppstat.spps_sndpack++;
976 } else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
4258e601
KS
977 /*
978 * Must send an acknowledgement or a probe
979 */
64b784d2
KS
980 if (cb->s_force)
981 sppstat.spps_sndprobe++;
982 if (cb->s_flags & SF_ACKNOW)
983 sppstat.spps_sndacks++;
bf8d7cff
KS
984 m = m_gethdr(M_DONTWAIT, MT_HEADER);
985 if (m == 0)
9ad31c34 986 return (ENOBUFS);
4258e601
KS
987 /*
988 * Fill in mbuf with extended SP header
989 * and addresses and length put into network format.
990 */
bf8d7cff 991 MH_ALIGN(m, sizeof (struct spidp));
4258e601 992 m->m_len = sizeof (*si);
bf8d7cff 993 m->m_pkthdr.len = sizeof (*si);
4258e601 994 si = mtod(m, struct spidp *);
64b784d2
KS
995 si->si_i = *cb->s_idp;
996 si->si_s = cb->s_shdr;
997 si->si_seq = cb->s_smax + 1;
fe594349 998 si->si_len = htons(sizeof (*si));
4258e601 999 si->si_cc |= SP_SP;
64b784d2
KS
1000 } else {
1001 cb->s_outx = 3;
1002 if (so->so_options & SO_DEBUG || traceallspps)
1003 spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1004 return (0);
4258e601
KS
1005 }
1006 /*
1007 * Stuff checksum and output datagram.
1008 */
64b784d2 1009 if ((si->si_cc & SP_SP) == 0) {
863250c1
MK
1010 if (cb->s_force != (1 + SPPT_PERSIST) ||
1011 cb->s_timer[SPPT_PERSIST] == 0) {
64b784d2
KS
1012 /*
1013 * If this is a new packet and we are not currently
1014 * timing anything, time this one.
1015 */
1016 if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1017 cb->s_smax = si->si_seq;
1018 if (cb->s_rtt == 0) {
1019 sppstat.spps_segstimed++;
1020 cb->s_rtseq = si->si_seq;
1021 cb->s_rtt = 1;
1022 }
4258e601
KS
1023 }
1024 /*
64b784d2
KS
1025 * Set rexmt timer if not currently set,
1026 * Initial value for retransmit timer is smoothed
1027 * round-trip time + 2 * round-trip time variance.
1028 * Initialize shift counter which is used for backoff
1029 * of retransmit time.
4258e601 1030 */
863250c1 1031 if (cb->s_timer[SPPT_REXMT] == 0 &&
64b784d2 1032 cb->s_snxt != cb->s_rack) {
863250c1
MK
1033 cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
1034 if (cb->s_timer[SPPT_PERSIST]) {
1035 cb->s_timer[SPPT_PERSIST] = 0;
64b784d2
KS
1036 cb->s_rxtshift = 0;
1037 }
4258e601 1038 }
64b784d2
KS
1039 } else if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1040 cb->s_smax = si->si_seq;
4258e601 1041 }
64b784d2
KS
1042 } else if (cb->s_state < TCPS_ESTABLISHED) {
1043 if (cb->s_rtt == 0)
1044 cb->s_rtt = 1; /* Time initial handshake */
863250c1
MK
1045 if (cb->s_timer[SPPT_REXMT] == 0)
1046 cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
64b784d2
KS
1047 }
1048 {
1049 /*
1050 * Do not request acks when we ack their data packets or
1051 * when we do a gratuitous window update.
1052 */
1053 if (((si->si_cc & SP_SP) == 0) || cb->s_force)
1054 si->si_cc |= SP_SA;
4258e601 1055 si->si_seq = htons(si->si_seq);
64b784d2 1056 si->si_alo = htons(alo);
4258e601
KS
1057 si->si_ack = htons(cb->s_ack);
1058
1059 if (idpcksum) {
1060 si->si_sum = 0;
fe594349 1061 len = ntohs(si->si_len);
99e0091e
KS
1062 if (len & 1)
1063 len++;
bf8d7cff 1064 si->si_sum = ns_cksum(m, len);
4258e601
KS
1065 } else
1066 si->si_sum = 0xffff;
1067
64b784d2 1068 cb->s_outx = 4;
4258e601
KS
1069 if (so->so_options & SO_DEBUG || traceallspps)
1070 spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
64b784d2 1071
4258e601
KS
1072 if (so->so_options & SO_DONTROUTE)
1073 error = ns_output(m, (struct route *)0, NS_ROUTETOIF);
1074 else
1075 error = ns_output(m, &cb->s_nspcb->nsp_route, 0);
4258e601 1076 }
64b784d2
KS
1077 if (error) {
1078 return (error);
1079 }
1080 sppstat.spps_sndtotal++;
1081 /*
1082 * Data sent (as far as we can tell).
1083 * If this advertises a larger window than any other segment,
1084 * then remember the size of the advertized window.
1085 * Any pending ACK has now been sent.
1086 */
1087 cb->s_force = 0;
1088 cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
1089 if (SSEQ_GT(alo, cb->s_alo))
1090 cb->s_alo = alo;
1091 if (sendalot)
1092 goto again;
1093 cb->s_outx = 5;
1094 return (0);
4258e601
KS
1095}
1096
64b784d2
KS
1097int spp_do_persist_panics = 0;
1098
1099spp_setpersist(cb)
1100 register struct sppcb *cb;
1101{
1102 register t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1103 extern int spp_backoff[];
1104
863250c1 1105 if (cb->s_timer[SPPT_REXMT] && spp_do_persist_panics)
64b784d2
KS
1106 panic("spp_output REXMT");
1107 /*
1108 * Start/restart persistance timer.
1109 */
863250c1 1110 SPPT_RANGESET(cb->s_timer[SPPT_PERSIST],
64b784d2 1111 t*spp_backoff[cb->s_rxtshift],
863250c1
MK
1112 SPPTV_PERSMIN, SPPTV_PERSMAX);
1113 if (cb->s_rxtshift < SPP_MAXRXTSHIFT)
64b784d2
KS
1114 cb->s_rxtshift++;
1115}
4258e601
KS
1116/*ARGSUSED*/
1117spp_ctloutput(req, so, level, name, value)
1118 int req;
1119 struct socket *so;
1120 int name;
1121 struct mbuf **value;
1122{
1123 register struct mbuf *m;
1124 struct nspcb *nsp = sotonspcb(so);
1125 register struct sppcb *cb;
1126 int mask, error = 0;
1127
1128 if (level != NSPROTO_SPP) {
1129 /* This will have to be changed when we do more general
1130 stacking of protocols */
9ad31c34 1131 return (idp_ctloutput(req, so, level, name, value));
4258e601
KS
1132 }
1133 if (nsp == NULL) {
1134 error = EINVAL;
1135 goto release;
1136 } else
1137 cb = nstosppcb(nsp);
1138
1139 switch (req) {
9ad31c34 1140
4258e601 1141 case PRCO_GETOPT:
99e0091e 1142 if (value == NULL)
9ad31c34 1143 return (EINVAL);
4258e601 1144 m = m_get(M_DONTWAIT, MT_DATA);
99e0091e 1145 if (m == NULL)
9ad31c34 1146 return (ENOBUFS);
4258e601 1147 switch (name) {
9ad31c34 1148
4258e601
KS
1149 case SO_HEADERS_ON_INPUT:
1150 mask = SF_HI;
1151 goto get_flags;
9ad31c34 1152
4258e601
KS
1153 case SO_HEADERS_ON_OUTPUT:
1154 mask = SF_HO;
1155 get_flags:
1156 m->m_len = sizeof(short);
4258e601
KS
1157 *mtod(m, short *) = cb->s_flags & mask;
1158 break;
9ad31c34 1159
8e189bed
KS
1160 case SO_MTU:
1161 m->m_len = sizeof(u_short);
8e189bed
KS
1162 *mtod(m, short *) = cb->s_mtu;
1163 break;
1164
4258e601
KS
1165 case SO_LAST_HEADER:
1166 m->m_len = sizeof(struct sphdr);
4258e601
KS
1167 *mtod(m, struct sphdr *) = cb->s_rhdr;
1168 break;
9ad31c34 1169
4258e601
KS
1170 case SO_DEFAULT_HEADERS:
1171 m->m_len = sizeof(struct spidp);
64b784d2 1172 *mtod(m, struct sphdr *) = cb->s_shdr;
a7abd6ba
KS
1173 break;
1174
1175 default:
1176 error = EINVAL;
4258e601
KS
1177 }
1178 *value = m;
1179 break;
9ad31c34 1180
4258e601 1181 case PRCO_SETOPT:
8e189bed
KS
1182 if (value == 0 || *value == 0) {
1183 error = EINVAL;
1184 break;
1185 }
4258e601 1186 switch (name) {
f97be0c9 1187 int *ok;
4258e601
KS
1188
1189 case SO_HEADERS_ON_INPUT:
1190 mask = SF_HI;
1191 goto set_head;
9ad31c34 1192
4258e601
KS
1193 case SO_HEADERS_ON_OUTPUT:
1194 mask = SF_HO;
1195 set_head:
8e189bed 1196 if (cb->s_flags & SF_PI) {
4258e601
KS
1197 ok = mtod(*value, int *);
1198 if (*ok)
1199 cb->s_flags |= mask;
1200 else
1201 cb->s_flags &= ~mask;
1202 } else error = EINVAL;
1203 break;
9ad31c34 1204
8e189bed
KS
1205 case SO_MTU:
1206 cb->s_mtu = *(mtod(*value, u_short *));
1207 break;
1208
bf8d7cff
KS
1209#ifdef SF_NEWCALL
1210 case SO_NEWCALL:
1211 ok = mtod(*value, int *);
1212 if (*ok) {
1213 cb->s_flags2 |= SF_NEWCALL;
1214 spp_newchecks[5]++;
1215 } else {
1216 cb->s_flags2 &= ~SF_NEWCALL;
1217 spp_newchecks[6]++;
1218 }
1219 break;
1220#endif
1221
4258e601
KS
1222 case SO_DEFAULT_HEADERS:
1223 {
1224 register struct sphdr *sp
1225 = mtod(*value, struct sphdr *);
1226 cb->s_dt = sp->sp_dt;
1227 cb->s_cc = sp->sp_cc & SP_EM;
1228 }
a7abd6ba
KS
1229 break;
1230
1231 default:
1232 error = EINVAL;
4258e601 1233 }
8e189bed 1234 m_freem(*value);
4258e601
KS
1235 break;
1236 }
1237 release:
9ad31c34 1238 return (error);
4258e601
KS
1239}
1240
1241/*ARGSUSED*/
f8749f8c 1242spp_usrreq(so, req, m, nam, controlp)
4258e601
KS
1243 struct socket *so;
1244 int req;
f8749f8c 1245 struct mbuf *m, *nam, *controlp;
4258e601
KS
1246{
1247 struct nspcb *nsp = sotonspcb(so);
1248 register struct sppcb *cb;
1249 int s = splnet();
1250 int error = 0, ostate;
64b784d2
KS
1251 struct mbuf *mm;
1252 register struct sockbuf *sb;
4258e601
KS
1253
1254 if (req == PRU_CONTROL)
1255 return (ns_control(so, (int)m, (caddr_t)nam,
f8749f8c 1256 (struct ifnet *)controlp));
4258e601
KS
1257 if (nsp == NULL) {
1258 if (req != PRU_ATTACH) {
1259 error = EINVAL;
1260 goto release;
1261 }
1262 } else
1263 cb = nstosppcb(nsp);
1264
1265 ostate = cb ? cb->s_state : 0;
1266
1267 switch (req) {
9ad31c34 1268
4258e601
KS
1269 case PRU_ATTACH:
1270 if (nsp != NULL) {
1271 error = EISCONN;
1272 break;
1273 }
1274 error = ns_pcballoc(so, &nspcb);
1275 if (error)
1276 break;
4f5156ea
MK
1277 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
1278 error = soreserve(so, (u_long) 3072, (u_long) 3072);
1279 if (error)
1280 break;
1281 }
4258e601 1282 nsp = sotonspcb(so);
4258e601 1283
64b784d2
KS
1284 mm = m_getclr(M_DONTWAIT, MT_PCB);
1285 sb = &so->so_snd;
1286
1287 if (mm == NULL) {
1288 error = ENOBUFS;
1289 break;
1290 }
1291 cb = mtod(mm, struct sppcb *);
1292 mm = m_getclr(M_DONTWAIT, MT_HEADER);
1293 if (mm == NULL) {
4f5156ea 1294 (void) m_free(dtom(m));
64b784d2
KS
1295 error = ENOBUFS;
1296 break;
4258e601 1297 }
64b784d2
KS
1298 cb->s_idp = mtod(mm, struct idp *);
1299 cb->s_state = TCPS_LISTEN;
1300 cb->s_smax = -1;
1301 cb->s_swl1 = -1;
1302 cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
1303 cb->s_nspcb = nsp;
1304 cb->s_mtu = 576 - sizeof (struct spidp);
1305 cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
1306 cb->s_ssthresh = cb->s_cwnd;
bf8d7cff 1307 cb->s_cwmx = sbspace(sb) * CUNIT /
64b784d2
KS
1308 (2 * sizeof (struct spidp));
1309 /* Above is recomputed when connecting to account
1310 for changed buffering or mtu's */
863250c1
MK
1311 cb->s_rtt = SPPTV_SRTTBASE;
1312 cb->s_rttvar = SPPTV_SRTTDFLT << 2;
1313 SPPT_RANGESET(cb->s_rxtcur,
1314 ((SPPTV_SRTTBASE >> 2) + (SPPTV_SRTTDFLT << 2)) >> 1,
1315 SPPTV_MIN, SPPTV_REXMTMAX);
64b784d2 1316 nsp->nsp_pcb = (caddr_t) cb;
4258e601
KS
1317 break;
1318
1319 case PRU_DETACH:
1320 if (nsp == NULL) {
1321 error = ENOTCONN;
1322 break;
1323 }
1324 if (cb->s_state > TCPS_LISTEN)
1325 cb = spp_disconnect(cb);
1326 else
1327 cb = spp_close(cb);
1328 break;
1329
1330 case PRU_BIND:
1331 error = ns_pcbbind(nsp, nam);
1332 break;
1333
1334 case PRU_LISTEN:
1335 if (nsp->nsp_lport == 0)
1336 error = ns_pcbbind(nsp, (struct mbuf *)0);
1337 if (error == 0)
1338 cb->s_state = TCPS_LISTEN;
1339 break;
1340
1341 /*
1342 * Initiate connection to peer.
1343 * Enter SYN_SENT state, and mark socket as connecting.
1344 * Start keep-alive timer, setup prototype header,
1345 * Send initial system packet requesting connection.
1346 */
1347 case PRU_CONNECT:
1348 if (nsp->nsp_lport == 0) {
1349 error = ns_pcbbind(nsp, (struct mbuf *)0);
1350 if (error)
1351 break;
1352 }
1353 error = ns_pcbconnect(nsp, nam);
1354 if (error)
1355 break;
1356 soisconnecting(so);
64b784d2 1357 sppstat.spps_connattempt++;
4258e601
KS
1358 cb->s_state = TCPS_SYN_SENT;
1359 cb->s_did = 0;
1360 spp_template(cb);
863250c1
MK
1361 cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
1362 cb->s_force = 1 + SPPTV_KEEP;
4258e601
KS
1363 /*
1364 * Other party is required to respond to
1365 * the port I send from, but he is not
1366 * required to answer from where I am sending to,
1367 * so allow wildcarding.
1368 * original port I am sending to is still saved in
1369 * cb->s_dport.
1370 */
1371 nsp->nsp_fport = 0;
1372 error = spp_output(cb, (struct mbuf *) 0);
1373 break;
1374
1375 case PRU_CONNECT2:
1376 error = EOPNOTSUPP;
1377 break;
1378
1379 /*
1380 * We may decide later to implement connection closing
1381 * handshaking at the spp level optionally.
1382 * here is the hook to do it:
1383 */
1384 case PRU_DISCONNECT:
1385 cb = spp_disconnect(cb);
1386 break;
1387
1388 /*
1389 * Accept a connection. Essentially all the work is
1390 * done at higher levels; just return the address
1391 * of the peer, storing through addr.
1392 */
1393 case PRU_ACCEPT: {
1394 struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *);
1395
1396 nam->m_len = sizeof (struct sockaddr_ns);
1397 sns->sns_family = AF_NS;
1398 sns->sns_addr = nsp->nsp_faddr;
1399 break;
1400 }
1401
1402 case PRU_SHUTDOWN:
1403 socantsendmore(so);
1404 cb = spp_usrclosed(cb);
1405 if (cb)
1406 error = spp_output(cb, (struct mbuf *) 0);
1407 break;
1408
1409 /*
1410 * After a receive, possibly send acknowledgment
1411 * updating allocation.
1412 */
1413 case PRU_RCVD:
64b784d2 1414 cb->s_flags |= SF_RVD;
4258e601 1415 (void) spp_output(cb, (struct mbuf *) 0);
64b784d2 1416 cb->s_flags &= ~SF_RVD;
4258e601
KS
1417 break;
1418
4258e601 1419 case PRU_ABORT:
f97be0c9 1420 (void) spp_drop(cb, ECONNABORTED);
4258e601
KS
1421 break;
1422
1423 case PRU_SENSE:
1424 case PRU_CONTROL:
1425 m = NULL;
1426 error = EOPNOTSUPP;
1427 break;
1428
1429 case PRU_RCVOOB:
0314fc80
KS
1430 if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1431 (so->so_state & SS_RCVATMARK)) {
1432 m->m_len = 1;
1433 *mtod(m, caddr_t) = cb->s_iobc;
d8e9f818
KS
1434 break;
1435 }
0314fc80 1436 error = EINVAL;
4258e601
KS
1437 break;
1438
1439 case PRU_SENDOOB:
1440 if (sbspace(&so->so_snd) < -512) {
4258e601
KS
1441 error = ENOBUFS;
1442 break;
1443 }
1444 cb->s_oobflags |= SF_SOOB;
90721ca4
KS
1445 /* fall into */
1446 case PRU_SEND:
bf8d7cff 1447 if (controlp) {
4dcdd98e 1448 u_short *p = mtod(controlp, u_short *);
bf8d7cff 1449 spp_newchecks[2]++;
4dcdd98e
KS
1450 if ((p[0] == 5) && p[1] == 1) { /* XXXX, for testing */
1451 cb->s_shdr.sp_dt = *(u_char *)(&p[2]);
bf8d7cff
KS
1452 spp_newchecks[3]++;
1453 }
a737003a 1454 m_freem(controlp);
bf8d7cff 1455 }
a737003a 1456 controlp = NULL;
4258e601
KS
1457 error = spp_output(cb, m);
1458 m = NULL;
4258e601
KS
1459 break;
1460
1461 case PRU_SOCKADDR:
1462 ns_setsockaddr(nsp, nam);
1463 break;
1464
1465 case PRU_PEERADDR:
1466 ns_setpeeraddr(nsp, nam);
1467 break;
1468
1469 case PRU_SLOWTIMO:
1470 cb = spp_timers(cb, (int)nam);
64b784d2 1471 req |= ((int)nam) << 8;
4258e601
KS
1472 break;
1473
1474 case PRU_FASTTIMO:
1475 case PRU_PROTORCV:
1476 case PRU_PROTOSEND:
1477 error = EOPNOTSUPP;
1478 break;
1479
1480 default:
1481 panic("sp_usrreq");
1482 }
1483 if (cb && (so->so_options & SO_DEBUG || traceallspps))
f97be0c9 1484 spp_trace(SA_USER, (u_char)ostate, cb, (struct spidp *)0, req);
4258e601 1485release:
a737003a
KS
1486 if (controlp != NULL)
1487 m_freem(controlp);
4258e601
KS
1488 if (m != NULL)
1489 m_freem(m);
1490 splx(s);
1491 return (error);
1492}
1493
f8749f8c 1494spp_usrreq_sp(so, req, m, nam, controlp)
4258e601
KS
1495 struct socket *so;
1496 int req;
f8749f8c 1497 struct mbuf *m, *nam, *controlp;
4258e601 1498{
f8749f8c 1499 int error = spp_usrreq(so, req, m, nam, controlp);
4258e601 1500
99e0091e 1501 if (req == PRU_ATTACH && error == 0) {
4258e601
KS
1502 struct nspcb *nsp = sotonspcb(so);
1503 ((struct sppcb *)nsp->nsp_pcb)->s_flags |=
1504 (SF_HI | SF_HO | SF_PI);
1505 }
9ad31c34 1506 return (error);
4258e601
KS
1507}
1508
1509/*
1510 * Create template to be used to send spp packets on a connection.
1511 * Called after host entry created, fills
1512 * in a skeletal spp header (choosing connection id),
1513 * minimizing the amount of work necessary when the connection is used.
1514 */
1515spp_template(cb)
64b784d2 1516 register struct sppcb *cb;
4258e601
KS
1517{
1518 register struct nspcb *nsp = cb->s_nspcb;
64b784d2
KS
1519 register struct idp *idp = cb->s_idp;
1520 register struct sockbuf *sb = &(nsp->nsp_socket->so_snd);
4258e601 1521
64b784d2
KS
1522 idp->idp_pt = NSPROTO_SPP;
1523 idp->idp_sna = nsp->nsp_laddr;
1524 idp->idp_dna = nsp->nsp_faddr;
1525 cb->s_sid = htons(spp_iss);
4258e601 1526 spp_iss += SPP_ISSINCR/2;
64b784d2
KS
1527 cb->s_alo = 1;
1528 cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1529 cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement
1530 of large packets */
bf8d7cff
KS
1531 cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spidp));
1532 cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
64b784d2 1533 /* But allow for lots of little packets as well */
4258e601
KS
1534}
1535
1536/*
1537 * Close a SPIP control block:
1538 * discard spp control block itself
1539 * discard ns protocol control block
1540 * wake up any sleepers
1541 */
1542struct sppcb *
1543spp_close(cb)
1544 register struct sppcb *cb;
1545{
1546 register struct spidp_q *s;
1547 struct nspcb *nsp = cb->s_nspcb;
1548 struct socket *so = nsp->nsp_socket;
1549 register struct mbuf *m;
1550
1551 s = cb->s_q.si_next;
1552 while (s != &(cb->s_q)) {
1553 s = s->si_next;
1554 m = dtom(s->si_prev);
1555 remque(s->si_prev);
1556 m_freem(m);
1557 }
64b784d2 1558 (void) m_free(dtom(cb->s_idp));
4258e601
KS
1559 (void) m_free(dtom(cb));
1560 nsp->nsp_pcb = 0;
1561 soisdisconnected(so);
1562 ns_pcbdetach(nsp);
64b784d2 1563 sppstat.spps_closed++;
9ad31c34 1564 return ((struct sppcb *)0);
4258e601
KS
1565}
1566/*
1567 * Someday we may do level 3 handshaking
1568 * to close a connection or send a xerox style error.
1569 * For now, just close.
1570 */
1571struct sppcb *
1572spp_usrclosed(cb)
1573 register struct sppcb *cb;
1574{
9ad31c34 1575 return (spp_close(cb));
4258e601
KS
1576}
1577struct sppcb *
1578spp_disconnect(cb)
1579 register struct sppcb *cb;
1580{
9ad31c34 1581 return (spp_close(cb));
4258e601
KS
1582}
1583/*
1584 * Drop connection, reporting
1585 * the specified error.
1586 */
1587struct sppcb *
1588spp_drop(cb, errno)
1589 register struct sppcb *cb;
1590 int errno;
1591{
1592 struct socket *so = cb->s_nspcb->nsp_socket;
1593
1594 /*
1595 * someday, in the xerox world
1596 * we will generate error protocol packets
1597 * announcing that the socket has gone away.
1598 */
64b784d2
KS
1599 if (TCPS_HAVERCVDSYN(cb->s_state)) {
1600 sppstat.spps_drops++;
1601 cb->s_state = TCPS_CLOSED;
1602 /*(void) tcp_output(cb);*/
1603 } else
1604 sppstat.spps_conndrops++;
4258e601
KS
1605 so->so_error = errno;
1606 return (spp_close(cb));
1607}
1608
1609spp_abort(nsp)
1610 struct nspcb *nsp;
1611{
1612
f97be0c9 1613 (void) spp_close((struct sppcb *)nsp->nsp_pcb);
4258e601
KS
1614}
1615
863250c1 1616int spp_backoff[SPP_MAXRXTSHIFT+1] =
64b784d2 1617 { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
4258e601
KS
1618/*
1619 * Fast timeout routine for processing delayed acks
1620 */
4258e601
KS
1621spp_fasttimo()
1622{
1623 register struct nspcb *nsp;
1624 register struct sppcb *cb;
1625 int s = splnet();
1626
1627 nsp = nspcb.nsp_next;
4258e601
KS
1628 if (nsp)
1629 for (; nsp != &nspcb; nsp = nsp->nsp_next)
1630 if ((cb = (struct sppcb *)nsp->nsp_pcb) &&
1631 (cb->s_flags & SF_DELACK)) {
1632 cb->s_flags &= ~SF_DELACK;
64b784d2
KS
1633 cb->s_flags |= SF_ACKNOW;
1634 sppstat.spps_delack++;
4258e601
KS
1635 (void) spp_output(cb, (struct mbuf *) 0);
1636 }
1637 splx(s);
1638}
1639
1640/*
1641 * spp protocol timeout routine called every 500 ms.
1642 * Updates the timers in all active pcb's and
1643 * causes finite state machine actions if timers expire.
1644 */
1645spp_slowtimo()
1646{
1647 register struct nspcb *ip, *ipnxt;
1648 register struct sppcb *cb;
1649 int s = splnet();
1650 register int i;
1651
1652 /*
1653 * Search through tcb's and update active timers.
1654 */
1655 ip = nspcb.nsp_next;
1656 if (ip == 0) {
1657 splx(s);
1658 return;
1659 }
1660 while (ip != &nspcb) {
1661 cb = nstosppcb(ip);
1662 ipnxt = ip->nsp_next;
1663 if (cb == 0)
1664 goto tpgone;
863250c1 1665 for (i = 0; i < SPPT_NTIMERS; i++) {
4258e601
KS
1666 if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1667 (void) spp_usrreq(cb->s_nspcb->nsp_socket,
1668 PRU_SLOWTIMO, (struct mbuf *)0,
4dcdd98e
KS
1669 (struct mbuf *)i, (struct mbuf *)0,
1670 (struct mbuf *)0);
4258e601
KS
1671 if (ipnxt->nsp_prev != ip)
1672 goto tpgone;
1673 }
1674 }
1675 cb->s_idle++;
1676 if (cb->s_rtt)
1677 cb->s_rtt++;
1678tpgone:
1679 ip = ipnxt;
1680 }
1681 spp_iss += SPP_ISSINCR/PR_SLOWHZ; /* increment iss */
1682 splx(s);
1683}
4258e601 1684/*
8e189bed 1685 * SPP timer processing.
4258e601
KS
1686 */
1687struct sppcb *
1688spp_timers(cb, timer)
1689 register struct sppcb *cb;
1690 int timer;
1691{
64b784d2
KS
1692 long rexmt;
1693 int win;
4258e601
KS
1694
1695 cb->s_force = 1 + timer;
1696 switch (timer) {
1697
1698 /*
64b784d2 1699 * 2 MSL timeout in shutdown went off. TCP deletes connection
4258e601
KS
1700 * control block.
1701 */
863250c1
MK
1702 case SPPT_2MSL:
1703 printf("spp: SPPT_2MSL went off for no reason\n");
64b784d2 1704 cb->s_timer[timer] = 0;
4258e601
KS
1705 break;
1706
1707 /*
1708 * Retransmission timer went off. Message has not
1709 * been acked within retransmit interval. Back off
64b784d2 1710 * to a longer retransmit interval and retransmit one packet.
4258e601 1711 */
863250c1
MK
1712 case SPPT_REXMT:
1713 if (++cb->s_rxtshift > SPP_MAXRXTSHIFT) {
1714 cb->s_rxtshift = SPP_MAXRXTSHIFT;
64b784d2 1715 sppstat.spps_timeoutdrop++;
4258e601
KS
1716 cb = spp_drop(cb, ETIMEDOUT);
1717 break;
1718 }
64b784d2
KS
1719 sppstat.spps_rexmttimeo++;
1720 rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1721 rexmt *= spp_backoff[cb->s_rxtshift];
863250c1
MK
1722 SPPT_RANGESET(cb->s_rxtcur, rexmt, SPPTV_MIN, SPPTV_REXMTMAX);
1723 cb->s_timer[SPPT_REXMT] = cb->s_rxtcur;
64b784d2
KS
1724 /*
1725 * If we have backed off fairly far, our srtt
1726 * estimate is probably bogus. Clobber it
1727 * so we'll take the next rtt measurement as our srtt;
1728 * move the current srtt into rttvar to keep the current
1729 * retransmit times until then.
1730 */
863250c1 1731 if (cb->s_rxtshift > SPP_MAXRXTSHIFT / 4 ) {
64b784d2
KS
1732 cb->s_rttvar += (cb->s_srtt >> 2);
1733 cb->s_srtt = 0;
4258e601 1734 }
64b784d2
KS
1735 cb->s_snxt = cb->s_rack;
1736 /*
1737 * If timing a packet, stop the timer.
1738 */
1739 cb->s_rtt = 0;
1740 /*
1741 * See very long discussion in tcp_timer.c about congestion
1742 * window and sstrhesh
1743 */
bf8d7cff 1744 win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
64b784d2
KS
1745 if (win < 2)
1746 win = 2;
1747 cb->s_cwnd = CUNIT;
ea516bcb 1748 cb->s_ssthresh = win * CUNIT;
64b784d2 1749 (void) spp_output(cb, (struct mbuf *) 0);
4258e601
KS
1750 break;
1751
1752 /*
1753 * Persistance timer into zero window.
1754 * Force a probe to be sent.
1755 */
863250c1 1756 case SPPT_PERSIST:
64b784d2 1757 sppstat.spps_persisttimeo++;
4258e601 1758 spp_setpersist(cb);
64b784d2 1759 (void) spp_output(cb, (struct mbuf *) 0);
4258e601
KS
1760 break;
1761
1762 /*
1763 * Keep-alive timer went off; send something
1764 * or drop connection if idle for too long.
1765 */
863250c1 1766 case SPPT_KEEP:
64b784d2 1767 sppstat.spps_keeptimeo++;
4258e601
KS
1768 if (cb->s_state < TCPS_ESTABLISHED)
1769 goto dropit;
1770 if (cb->s_nspcb->nsp_socket->so_options & SO_KEEPALIVE) {
863250c1 1771 if (cb->s_idle >= SPPTV_MAXIDLE)
4258e601 1772 goto dropit;
64b784d2 1773 sppstat.spps_keepprobe++;
4258e601
KS
1774 (void) spp_output(cb, (struct mbuf *) 0);
1775 } else
1776 cb->s_idle = 0;
863250c1 1777 cb->s_timer[SPPT_KEEP] = SPPTV_KEEP;
4258e601
KS
1778 break;
1779 dropit:
64b784d2 1780 sppstat.spps_keepdrops++;
4258e601
KS
1781 cb = spp_drop(cb, ETIMEDOUT);
1782 break;
1783 }
1784 return (cb);
1785}
ea516bcb 1786#ifndef lint
64b784d2
KS
1787int SppcbSize = sizeof (struct sppcb);
1788int NspcbSize = sizeof (struct nspcb);
ea516bcb 1789#endif lint