2 * Copyright (c) 1995, Mike Mitchell
3 * Copyright (c) 1984, 1985, 1986, 1987, 1993
4 * The Regents of the University of California. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * $FreeBSD: src/sys/netipx/spx_usrreq.c,v 1.27.2.1 2001/02/22 09:44:18 bp Exp $
37 * $DragonFly: src/sys/netproto/ipx/spx_usrreq.c,v 1.20 2007/04/22 01:13:15 dillon Exp $
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 #include <sys/thread2.h>
51 #include <net/route.h>
52 #include <netinet/tcp_fsm.h>
58 #include "spx_timer.h"
60 #include "spx_debug.h"
63 * SPX protocol implementation.
65 static u_short spx_iss
;
66 static u_short spx_newchecks
[50];
67 static int spx_hardnosed
;
68 static int spx_use_delack
= 0;
69 static int traceallspxs
= 0;
70 static struct spx spx_savesi
;
71 static struct spx_istat spx_istat
;
73 /* Following was struct spxstat spxstat; */
75 #define spxstat spx_istat.newstats
78 static int spx_backoff
[SPX_MAXRXTSHIFT
+1] =
79 { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
81 static struct spxpcb
*spx_close(struct spxpcb
*cb
);
82 static struct spxpcb
*spx_disconnect(struct spxpcb
*cb
);
83 static struct spxpcb
*spx_drop(struct spxpcb
*cb
, int errno
);
84 static int spx_output(struct spxpcb
*cb
, struct mbuf
*m0
);
85 static int spx_reass(struct spxpcb
*cb
, struct spx
*si
, struct mbuf
*si_m
);
86 static void spx_setpersist(struct spxpcb
*cb
);
87 static void spx_template(struct spxpcb
*cb
);
88 static struct spxpcb
*spx_timers(struct spxpcb
*cb
, int timer
);
89 static struct spxpcb
*spx_usrclosed(struct spxpcb
*cb
);
91 static int spx_usr_abort(struct socket
*so
);
92 static int spx_accept(struct socket
*so
, struct sockaddr
**nam
);
93 static int spx_attach(struct socket
*so
, int proto
,
94 struct pru_attach_info
*ai
);
95 static int spx_bind(struct socket
*so
, struct sockaddr
*nam
,
97 static int spx_connect(struct socket
*so
, struct sockaddr
*nam
,
99 static int spx_detach(struct socket
*so
);
100 static int spx_usr_disconnect(struct socket
*so
);
101 static int spx_listen(struct socket
*so
, struct thread
*td
);
102 static int spx_rcvd(struct socket
*so
, int flags
);
103 static int spx_rcvoob(struct socket
*so
, struct mbuf
*m
, int flags
);
104 static int spx_send(struct socket
*so
, int flags
, struct mbuf
*m
,
105 struct sockaddr
*addr
, struct mbuf
*control
,
107 static int spx_shutdown(struct socket
*so
);
108 static int spx_sp_attach(struct socket
*so
, int proto
,
109 struct pru_attach_info
*ai
);
111 struct pr_usrreqs spx_usrreqs
= {
112 .pru_abort
= spx_usr_abort
,
113 .pru_accept
= spx_accept
,
114 .pru_attach
= spx_attach
,
115 .pru_bind
= spx_bind
,
116 .pru_connect
= spx_connect
,
117 .pru_connect2
= pru_connect2_notsupp
,
118 .pru_control
= ipx_control
,
119 .pru_detach
= spx_detach
,
120 .pru_disconnect
= spx_usr_disconnect
,
121 .pru_listen
= spx_listen
,
122 .pru_peeraddr
= ipx_peeraddr
,
123 .pru_rcvd
= spx_rcvd
,
124 .pru_rcvoob
= spx_rcvoob
,
125 .pru_send
= spx_send
,
126 .pru_sense
= pru_sense_null
,
127 .pru_shutdown
= spx_shutdown
,
128 .pru_sockaddr
= ipx_sockaddr
,
129 .pru_sosend
= sosend
,
130 .pru_soreceive
= soreceive
,
134 struct pr_usrreqs spx_usrreq_sps
= {
135 .pru_abort
= spx_usr_abort
,
136 .pru_accept
= spx_accept
,
137 .pru_attach
= spx_sp_attach
,
138 .pru_bind
= spx_bind
,
139 .pru_connect
= spx_connect
,
140 .pru_connect2
= pru_connect2_notsupp
,
141 .pru_control
= ipx_control
,
142 .pru_detach
= spx_detach
,
143 .pru_disconnect
= spx_usr_disconnect
,
144 .pru_listen
= spx_listen
,
145 .pru_peeraddr
= ipx_peeraddr
,
146 .pru_rcvd
= spx_rcvd
,
147 .pru_rcvoob
= spx_rcvoob
,
148 .pru_send
= spx_send
,
149 .pru_sense
= pru_sense_null
,
150 .pru_shutdown
= spx_shutdown
,
151 .pru_sockaddr
= ipx_sockaddr
,
152 .pru_sosend
= sosend
,
153 .pru_soreceive
= soreceive
,
157 static MALLOC_DEFINE(M_SPX_Q
, "ipx_spx_q", "IPX Packet Management");
163 spx_iss
= 1; /* WRONG !! should fish it out of TODR */
167 spx_input(struct mbuf
*m
, struct ipxpcb
*ipxp
)
175 spxstat
.spxs_rcvtotal
++;
177 panic("No ipxpcb in spx_input\n");
181 cb
= ipxtospxpcb(ipxp
);
185 if (m
->m_len
< sizeof(struct spx
)) {
186 if ((m
= m_pullup(m
, sizeof(*si
))) == NULL
) {
187 spxstat
.spxs_rcvshort
++;
191 si
= mtod(m
, struct spx
*);
192 si
->si_seq
= ntohs(si
->si_seq
);
193 si
->si_ack
= ntohs(si
->si_ack
);
194 si
->si_alo
= ntohs(si
->si_alo
);
196 so
= ipxp
->ipxp_socket
;
198 if (so
->so_options
& SO_DEBUG
|| traceallspxs
) {
199 ostate
= cb
->s_state
;
202 if (so
->so_options
& SO_ACCEPTCONN
) {
203 struct spxpcb
*ocb
= cb
;
205 so
= sonewconn(so
, 0);
210 * This is ugly, but ....
212 * Mark socket as temporary until we're
213 * committed to keeping it. The code at
214 * ``drop'' and ``dropwithreset'' check the
215 * flag dropsocket to see if the temporary
216 * socket created here should be discarded.
217 * We mark the socket as discardable until
218 * we're committed to it below in TCPS_LISTEN.
221 ipxp
= (struct ipxpcb
*)so
->so_pcb
;
222 ipxp
->ipxp_laddr
= si
->si_dna
;
223 cb
= ipxtospxpcb(ipxp
);
224 cb
->s_mtu
= ocb
->s_mtu
; /* preserve sockopts */
225 cb
->s_flags
= ocb
->s_flags
; /* preserve sockopts */
226 cb
->s_flags2
= ocb
->s_flags2
; /* preserve sockopts */
227 cb
->s_state
= TCPS_LISTEN
;
231 * Packet received on connection.
232 * reset idle time and keep-alive timer;
235 cb
->s_timer
[SPXT_KEEP
] = SPXTV_KEEP
;
237 switch (cb
->s_state
) {
240 struct sockaddr_ipx
*sipx
, ssipx
;
241 struct ipx_addr laddr
;
244 * If somebody here was carying on a conversation
245 * and went away, and his pen pal thinks he can
246 * still talk, we get the misdirected packet.
248 if (spx_hardnosed
&& (si
->si_did
!= 0 || si
->si_seq
!= 0)) {
253 bzero(sipx
, sizeof *sipx
);
254 sipx
->sipx_len
= sizeof(*sipx
);
255 sipx
->sipx_family
= AF_IPX
;
256 sipx
->sipx_addr
= si
->si_sna
;
257 laddr
= ipxp
->ipxp_laddr
;
258 if (ipx_nullhost(laddr
))
259 ipxp
->ipxp_laddr
= si
->si_dna
;
260 if (ipx_pcbconnect(ipxp
, (struct sockaddr
*)sipx
, &thread0
)) {
261 ipxp
->ipxp_laddr
= laddr
;
266 dropsocket
= 0; /* committed to socket */
267 cb
->s_did
= si
->si_sid
;
268 cb
->s_rack
= si
->si_ack
;
269 cb
->s_ralo
= si
->si_alo
;
270 #define THREEWAYSHAKE
272 cb
->s_state
= TCPS_SYN_RECEIVED
;
273 cb
->s_force
= 1 + SPXT_KEEP
;
274 spxstat
.spxs_accepts
++;
275 cb
->s_timer
[SPXT_KEEP
] = SPXTV_KEEP
;
279 * This state means that we have heard a response
280 * to our acceptance of their connection
281 * It is probably logically unnecessary in this
284 case TCPS_SYN_RECEIVED
: {
285 if (si
->si_did
!= cb
->s_sid
) {
290 ipxp
->ipxp_fport
= si
->si_sport
;
291 cb
->s_timer
[SPXT_REXMT
] = 0;
292 cb
->s_timer
[SPXT_KEEP
] = SPXTV_KEEP
;
294 cb
->s_state
= TCPS_ESTABLISHED
;
295 spxstat
.spxs_accepts
++;
300 * This state means that we have gotten a response
301 * to our attempt to establish a connection.
302 * We fill in the data from the other side,
303 * telling us which port to respond to, instead of the well-
304 * known one we might have sent to in the first place.
305 * We also require that this is a response to our
309 if (si
->si_did
!= cb
->s_sid
) {
313 spxstat
.spxs_connects
++;
314 cb
->s_did
= si
->si_sid
;
315 cb
->s_rack
= si
->si_ack
;
316 cb
->s_ralo
= si
->si_alo
;
317 cb
->s_dport
= ipxp
->ipxp_fport
= si
->si_sport
;
318 cb
->s_timer
[SPXT_REXMT
] = 0;
319 cb
->s_flags
|= SF_ACKNOW
;
321 cb
->s_state
= TCPS_ESTABLISHED
;
322 /* Use roundtrip time of connection request for initial rtt */
324 cb
->s_srtt
= cb
->s_rtt
<< 3;
325 cb
->s_rttvar
= cb
->s_rtt
<< 1;
326 SPXT_RANGESET(cb
->s_rxtcur
,
327 ((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1,
328 SPXTV_MIN
, SPXTV_REXMTMAX
);
332 if (so
->so_options
& SO_DEBUG
|| traceallspxs
)
333 spx_trace(SA_INPUT
, (u_char
)ostate
, cb
, &spx_savesi
, 0);
335 m
->m_len
-= sizeof(struct ipx
);
336 m
->m_pkthdr
.len
-= sizeof(struct ipx
);
337 m
->m_data
+= sizeof(struct ipx
);
339 if (spx_reass(cb
, si
, m
)) {
342 if (cb
->s_force
|| (cb
->s_flags
& (SF_ACKNOW
|SF_WIN
|SF_RXT
)))
343 spx_output(cb
, (struct mbuf
*)NULL
);
344 cb
->s_flags
&= ~(SF_WIN
|SF_RXT
);
350 si
->si_seq
= ntohs(si
->si_seq
);
351 si
->si_ack
= ntohs(si
->si_ack
);
352 si
->si_alo
= ntohs(si
->si_alo
);
354 if (cb
->s_ipxpcb
->ipxp_socket
->so_options
& SO_DEBUG
|| traceallspxs
)
355 spx_trace(SA_DROP
, (u_char
)ostate
, cb
, &spx_savesi
, 0);
360 if (cb
== 0 || cb
->s_ipxpcb
->ipxp_socket
->so_options
& SO_DEBUG
||
362 spx_trace(SA_DROP
, (u_char
)ostate
, cb
, &spx_savesi
, 0);
366 static int spxrexmtthresh
= 3;
369 * This is structurally similar to the tcp reassembly routine
370 * but its function is somewhat different: It merely queues
371 * packets up, and suppresses duplicates.
374 spx_reass(struct spxpcb
*cb
, struct spx
*si
, struct mbuf
*si_m
)
379 struct socket
*so
= cb
->s_ipxpcb
->ipxp_socket
;
380 char packetp
= cb
->s_flags
& SF_HI
;
387 * Update our news from them.
389 if (si
->si_cc
& SPX_SA
)
390 cb
->s_flags
|= (spx_use_delack
? SF_DELACK
: SF_ACKNOW
);
391 if (SSEQ_GT(si
->si_alo
, cb
->s_ralo
))
392 cb
->s_flags
|= SF_WIN
;
393 if (SSEQ_LEQ(si
->si_ack
, cb
->s_rack
)) {
394 if ((si
->si_cc
& SPX_SP
) && cb
->s_rack
!= (cb
->s_smax
+ 1)) {
395 spxstat
.spxs_rcvdupack
++;
397 * If this is a completely duplicate ack
398 * and other conditions hold, we assume
399 * a packet has been dropped and retransmit
400 * it exactly as in tcp_input().
402 if (si
->si_ack
!= cb
->s_rack
||
403 si
->si_alo
!= cb
->s_ralo
)
405 else if (++cb
->s_dupacks
== spxrexmtthresh
) {
406 u_short onxt
= cb
->s_snxt
;
407 int cwnd
= cb
->s_cwnd
;
409 cb
->s_snxt
= si
->si_ack
;
411 cb
->s_force
= 1 + SPXT_REXMT
;
412 spx_output(cb
, (struct mbuf
*)NULL
);
413 cb
->s_timer
[SPXT_REXMT
] = cb
->s_rxtcur
;
415 if (cwnd
>= 4 * CUNIT
)
416 cb
->s_cwnd
= cwnd
/ 2;
417 if (SSEQ_GT(onxt
, cb
->s_snxt
))
427 * If our correspondent acknowledges data we haven't sent
428 * TCP would drop the packet after acking. We'll be a little
431 if (SSEQ_GT(si
->si_ack
, (cb
->s_smax
+ 1))) {
432 spxstat
.spxs_rcvacktoomuch
++;
433 si
->si_ack
= cb
->s_smax
+ 1;
435 spxstat
.spxs_rcvackpack
++;
437 * If transmit timer is running and timed sequence
438 * number was acked, update smoothed round trip time.
439 * See discussion of algorithm in tcp_input.c
441 if (cb
->s_rtt
&& SSEQ_GT(si
->si_ack
, cb
->s_rtseq
)) {
442 spxstat
.spxs_rttupdated
++;
443 if (cb
->s_srtt
!= 0) {
445 delta
= cb
->s_rtt
- (cb
->s_srtt
>> 3);
446 if ((cb
->s_srtt
+= delta
) <= 0)
450 delta
-= (cb
->s_rttvar
>> 2);
451 if ((cb
->s_rttvar
+= delta
) <= 0)
455 * No rtt measurement yet
457 cb
->s_srtt
= cb
->s_rtt
<< 3;
458 cb
->s_rttvar
= cb
->s_rtt
<< 1;
462 SPXT_RANGESET(cb
->s_rxtcur
,
463 ((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1,
464 SPXTV_MIN
, SPXTV_REXMTMAX
);
467 * If all outstanding data is acked, stop retransmit
468 * timer and remember to restart (more output or persist).
469 * If there is more data to be acked, restart retransmit
470 * timer, using current (possibly backed-off) value;
472 if (si
->si_ack
== cb
->s_smax
+ 1) {
473 cb
->s_timer
[SPXT_REXMT
] = 0;
474 cb
->s_flags
|= SF_RXT
;
475 } else if (cb
->s_timer
[SPXT_PERSIST
] == 0)
476 cb
->s_timer
[SPXT_REXMT
] = cb
->s_rxtcur
;
478 * When new data is acked, open the congestion window.
479 * If the window gives us less than ssthresh packets
480 * in flight, open exponentially (maxseg at a time).
481 * Otherwise open linearly (maxseg^2 / cwnd at a time).
484 if (cb
->s_cwnd
> cb
->s_ssthresh
)
485 incr
= max(incr
* incr
/ cb
->s_cwnd
, 1);
486 cb
->s_cwnd
= min(cb
->s_cwnd
+ incr
, cb
->s_cwmx
);
488 * Trim Acked data from output queue.
490 while ((m
= so
->so_snd
.ssb_mb
) != NULL
) {
491 if (SSEQ_LT((mtod(m
, struct spx
*))->si_seq
, si
->si_ack
))
492 sbdroprecord(&so
->so_snd
.sb
);
497 cb
->s_rack
= si
->si_ack
;
499 if (SSEQ_LT(cb
->s_snxt
, cb
->s_rack
))
500 cb
->s_snxt
= cb
->s_rack
;
501 if (SSEQ_LT(cb
->s_swl1
, si
->si_seq
) || ((cb
->s_swl1
== si
->si_seq
&&
502 (SSEQ_LT(cb
->s_swl2
, si
->si_ack
))) ||
503 (cb
->s_swl2
== si
->si_ack
&& SSEQ_LT(cb
->s_ralo
, si
->si_alo
)))) {
504 /* keep track of pure window updates */
505 if ((si
->si_cc
& SPX_SP
) && cb
->s_swl2
== si
->si_ack
506 && SSEQ_LT(cb
->s_ralo
, si
->si_alo
)) {
507 spxstat
.spxs_rcvwinupd
++;
508 spxstat
.spxs_rcvdupack
--;
510 cb
->s_ralo
= si
->si_alo
;
511 cb
->s_swl1
= si
->si_seq
;
512 cb
->s_swl2
= si
->si_ack
;
513 cb
->s_swnd
= (1 + si
->si_alo
- si
->si_ack
);
514 if (cb
->s_swnd
> cb
->s_smxw
)
515 cb
->s_smxw
= cb
->s_swnd
;
516 cb
->s_flags
|= SF_WIN
;
519 * If this packet number is higher than that which
520 * we have allocated refuse it, unless urgent
522 if (SSEQ_GT(si
->si_seq
, cb
->s_alo
)) {
523 if (si
->si_cc
& SPX_SP
) {
524 spxstat
.spxs_rcvwinprobe
++;
527 spxstat
.spxs_rcvpackafterwin
++;
528 if (si
->si_cc
& SPX_OB
) {
529 if (SSEQ_GT(si
->si_seq
, cb
->s_alo
+ 60)) {
532 } /* else queue this packet; */
534 /*register struct socket *so = cb->s_ipxpcb->ipxp_socket;
535 if (so->so_state && SS_NOFDREF) {
545 * If this is a system packet, we don't need to
546 * queue it up, and won't update acknowledge #
548 if (si
->si_cc
& SPX_SP
) {
552 * We have already seen this packet, so drop.
554 if (SSEQ_LT(si
->si_seq
, cb
->s_ack
)) {
556 spxstat
.spxs_rcvduppack
++;
557 if (si
->si_seq
== cb
->s_ack
- 1)
562 * Loop through all packets queued up to insert in
563 * appropriate sequence.
565 for (q
= cb
->s_q
.si_next
; q
!= &cb
->s_q
; q
= q
->si_next
) {
566 if (si
->si_seq
== SI(q
)->si_seq
) {
567 spxstat
.spxs_rcvduppack
++;
570 if (SSEQ_LT(si
->si_seq
, SI(q
)->si_seq
)) {
571 spxstat
.spxs_rcvoopack
++;
575 nq
= kmalloc(sizeof(struct spx_q
), M_SPX_Q
, M_INTNOWAIT
);
580 insque(nq
, q
->si_prev
);
583 * If this packet is urgent, inform process
585 if (si
->si_cc
& SPX_OB
) {
586 cb
->s_iobc
= ((char *)si
)[1 + sizeof(*si
)];
588 cb
->s_oobflags
|= SF_IOOB
;
591 #define SPINC sizeof(struct spxhdr)
593 * Loop through all packets queued up to update acknowledge
594 * number, and present all acknowledged data to user;
595 * If in packet interface mode, show packet headers.
597 for (q
= cb
->s_q
.si_next
; q
!= &cb
->s_q
; q
= q
->si_next
) {
598 if (SI(q
)->si_seq
== cb
->s_ack
) {
601 if (SI(q
)->si_cc
& SPX_OB
) {
602 cb
->s_oobflags
&= ~SF_IOOB
;
603 if (so
->so_rcv
.ssb_cc
)
604 so
->so_oobmark
= so
->so_rcv
.ssb_cc
;
606 so
->so_state
|= SS_RCVATMARK
;
613 spxstat
.spxs_rcvpack
++;
615 if (cb
->s_flags2
& SF_NEWCALL
) {
616 struct spxhdr
*sp
= mtod(m
, struct spxhdr
*);
617 u_char dt
= sp
->spx_dt
;
619 if (dt
!= cb
->s_rhdr
.spx_dt
) {
621 m_getclr(MB_DONTWAIT
, MT_CONTROL
);
626 cb
->s_rhdr
.spx_dt
= dt
;
627 mm
->m_len
= 5; /*XXX*/
630 *(u_char
*)(&s
[2]) = dt
;
631 sbappend(&so
->so_rcv
.sb
, mm
);
634 if (sp
->spx_cc
& SPX_OB
) {
635 m_chtype(m
, MT_OOBDATA
);
638 so
->so_state
&= ~SS_RCVATMARK
;
643 m
->m_pkthdr
.len
-= SPINC
;
645 if ((sp
->spx_cc
& SPX_EM
) || packetp
) {
646 sbappendrecord(&so
->so_rcv
.sb
, m
);
649 sbappend(&so
->so_rcv
.sb
, m
);
653 sbappendrecord(&so
->so_rcv
.sb
, m
);
655 cb
->s_rhdr
= *mtod(m
, struct spxhdr
*);
658 m
->m_pkthdr
.len
-= SPINC
;
659 sbappend(&so
->so_rcv
.sb
, m
);
670 spx_ctlinput(int cmd
, struct sockaddr
*arg_as_sa
, void *dummy
)
672 caddr_t arg
= (/* XXX */ caddr_t
)arg_as_sa
;
674 struct sockaddr_ipx
*sipx
;
676 if (cmd
< 0 || cmd
> PRC_NCMDS
)
686 case PRC_HOSTUNREACH
:
687 sipx
= (struct sockaddr_ipx
*)arg
;
688 if (sipx
->sipx_family
!= AF_IPX
)
690 na
= &sipx
->sipx_addr
;
700 spx_fixmtu(struct ipxpcb
*ipxp
)
702 struct spxpcb
*cb
= (struct spxpcb
*)(ipxp
->ipxp_pcb
);
706 struct signalsockbuf
*ssb
;
708 struct mbuf
*firstbad
, *m0
;
712 * The notification that we have sent
713 * too much is bad news -- we will
714 * have to go through queued up so far
715 * splitting ones which are too big and
716 * reassigning sequence numbers and checksums.
717 * we should then retransmit all packets from
718 * one above the offending packet to the last one
719 * we had sent (or our allocation)
720 * then the offending one so that the any queued
721 * data at our destination will be discarded.
723 ep
= (struct ipx_errp
*)ipxp
->ipxp_notify_param
;
724 ssb
= &ipxp
->ipxp_socket
->so_snd
;
725 cb
->s_mtu
= ep
->ipx_err_param
;
726 badseq
= ep
->ipx_err_ipx
.si_seq
;
727 for (m
= ssb
->ssb_mb
; m
!= NULL
; m
= m
->m_nextpkt
) {
728 si
= mtod(m
, struct spx
*);
729 if (si
->si_seq
== badseq
)
736 /* calculate length */
737 for (m0
= m
, len
= 0; m
!= NULL
; m
= m
->m_next
)
739 if (len
> cb
->s_mtu
) {
748 spx_output(struct spxpcb
*cb
, struct mbuf
*m0
)
750 struct socket
*so
= cb
->s_ipxpcb
->ipxp_socket
;
751 struct mbuf
*m
= NULL
;
752 struct spx
*si
= NULL
;
753 struct signalsockbuf
*ssb
= &so
->so_snd
;
754 int len
= 0, win
, rcv_win
;
755 short span
, off
, recordp
= 0;
757 int error
= 0, sendalot
;
767 * Make sure that packet isn't too big.
769 for (m
= m0
; m
!= NULL
; m
= m
->m_next
) {
772 if (m
->m_flags
& M_EOR
)
775 datalen
= (cb
->s_flags
& SF_HO
) ?
776 len
- sizeof(struct spxhdr
) : len
;
778 if (cb
->s_flags
& SF_PI
) {
782 int oldEM
= cb
->s_cc
& SPX_EM
;
787 * Here we are only being called
788 * from usrreq(), so it is OK to
791 m
= m_copym(m0
, 0, mtu
, MB_WAIT
);
792 if (cb
->s_flags
& SF_NEWCALL
) {
796 mm
->m_flags
&= ~M_EOR
;
800 error
= spx_output(cb
, m
);
813 * Force length even, by adding a "garbage byte" if
818 if (M_TRAILINGSPACE(m
) >= 1)
821 struct mbuf
*m1
= m_get(MB_DONTWAIT
, MT_DATA
);
828 *(mtod(m1
, u_char
*)) = 0;
832 m
= m_gethdr(MB_DONTWAIT
, MT_HEADER
);
838 * Fill in mbuf with extended SP header
839 * and addresses and length put into network format.
841 MH_ALIGN(m
, sizeof(struct spx
));
842 m
->m_len
= sizeof(struct spx
);
844 si
= mtod(m
, struct spx
*);
845 si
->si_i
= *cb
->s_ipx
;
846 si
->si_s
= cb
->s_shdr
;
847 if ((cb
->s_flags
& SF_PI
) && (cb
->s_flags
& SF_HO
)) {
849 if (m0
->m_len
< sizeof(*sh
)) {
850 if((m0
= m_pullup(m0
, sizeof(*sh
))) == NULL
) {
857 sh
= mtod(m0
, struct spxhdr
*);
858 si
->si_dt
= sh
->spx_dt
;
859 si
->si_cc
|= sh
->spx_cc
& SPX_EM
;
860 m0
->m_len
-= sizeof(*sh
);
861 m0
->m_data
+= sizeof(*sh
);
865 if ((cb
->s_flags2
& SF_NEWCALL
) && recordp
) {
869 if (cb
->s_oobflags
& SF_SOOB
) {
872 * make sure OB packets convey exactly 1 byte.
873 * If the packet is 1 byte or larger, we
874 * have already guaranted there to be at least
875 * one garbage byte for the checksum, and
876 * extra bytes shouldn't hurt!
878 if (len
> sizeof(*si
)) {
880 len
= (1 + sizeof(*si
));
883 si
->si_len
= htons((u_short
)len
);
884 m
->m_pkthdr
.len
= ((len
- 1) | 1) + 1;
886 * queue stuff up for output
888 sbappendrecord(&ssb
->sb
, m
);
892 idle
= (cb
->s_smax
== (cb
->s_rack
- 1));
896 off
= cb
->s_snxt
- cb
->s_rack
;
897 win
= min(cb
->s_swnd
, (cb
->s_cwnd
/ CUNIT
));
900 * If in persist timeout with window of 0, send a probe.
901 * Otherwise, if window is small but nonzero
902 * and timer expired, send what we can and go into
905 if (cb
->s_force
== 1 + SPXT_PERSIST
) {
907 cb
->s_timer
[SPXT_PERSIST
] = 0;
911 span
= cb
->s_seq
- cb
->s_rack
;
912 len
= min(span
, win
) - off
;
916 * Window shrank after we went into it.
917 * If window shrank to 0, cancel pending
918 * restransmission and pull s_snxt back
919 * to (closed) window. We will enter persist
920 * state below. If the widndow didn't close completely,
921 * just wait for an ACK.
925 cb
->s_timer
[SPXT_REXMT
] = 0;
926 cb
->s_snxt
= cb
->s_rack
;
931 rcv_win
= ssb_space(&so
->so_rcv
);
934 * Send if we owe peer an ACK.
936 if (cb
->s_oobflags
& SF_SOOB
) {
938 * must transmit this out of band packet
940 cb
->s_oobflags
&= ~ SF_SOOB
;
942 spxstat
.spxs_sndurg
++;
945 if (cb
->s_flags
& SF_ACKNOW
)
947 if (cb
->s_state
< TCPS_ESTABLISHED
)
950 * Silly window can't happen in spx.
951 * Code from tcp deleted.
956 * Compare available window to amount of window
957 * known to peer (as advertised window less
958 * next expected input.) If the difference is at least two
959 * packets or at least 35% of the mximum possible window,
960 * then want to send a window update to peer.
963 u_short delta
= 1 + cb
->s_alo
- cb
->s_ack
;
964 int adv
= rcv_win
- (delta
* cb
->s_mtu
);
966 if ((so
->so_rcv
.ssb_cc
== 0 && adv
>= (2 * cb
->s_mtu
)) ||
967 (100 * adv
/ so
->so_rcv
.ssb_hiwat
>= 35)) {
968 spxstat
.spxs_sndwinup
++;
969 cb
->s_flags
|= SF_ACKNOW
;
975 * Many comments from tcp_output.c are appropriate here
977 * If send window is too small, there is data to transmit, and no
978 * retransmit or persist is pending, then go to persist state.
979 * If nothing happens soon, send when timer expires:
980 * if window is nonzero, transmit what we can,
981 * otherwise send a probe.
983 if (so
->so_snd
.ssb_cc
&& cb
->s_timer
[SPXT_REXMT
] == 0 &&
984 cb
->s_timer
[SPXT_PERSIST
] == 0) {
989 * No reason to send a packet, just return.
996 * Find requested packet.
1000 cb
->s_want
= cb
->s_snxt
;
1001 for (m
= ssb
->ssb_mb
; m
!= NULL
; m
= m
->m_nextpkt
) {
1002 si
= mtod(m
, struct spx
*);
1003 if (SSEQ_LEQ(cb
->s_snxt
, si
->si_seq
))
1008 if (si
->si_seq
== cb
->s_snxt
)
1011 spxstat
.spxs_sndvoid
++, si
= 0;
1019 alo
= cb
->s_ack
- 1 + (rcv_win
/ ((short)cb
->s_mtu
));
1020 if (SSEQ_LT(alo
, cb
->s_alo
))
1025 * must make a copy of this packet for
1026 * ipx_output to monkey with
1028 m
= m_copy(m
, 0, (int)M_COPYALL
);
1032 si
= mtod(m
, struct spx
*);
1033 if (SSEQ_LT(si
->si_seq
, cb
->s_smax
))
1034 spxstat
.spxs_sndrexmitpack
++;
1036 spxstat
.spxs_sndpack
++;
1037 } else if (cb
->s_force
|| cb
->s_flags
& SF_ACKNOW
) {
1039 * Must send an acknowledgement or a probe
1042 spxstat
.spxs_sndprobe
++;
1043 if (cb
->s_flags
& SF_ACKNOW
)
1044 spxstat
.spxs_sndacks
++;
1045 m
= m_gethdr(MB_DONTWAIT
, MT_HEADER
);
1049 * Fill in mbuf with extended SP header
1050 * and addresses and length put into network format.
1052 MH_ALIGN(m
, sizeof(struct spx
));
1053 m
->m_len
= sizeof(*si
);
1054 m
->m_pkthdr
.len
= sizeof(*si
);
1055 si
= mtod(m
, struct spx
*);
1056 si
->si_i
= *cb
->s_ipx
;
1057 si
->si_s
= cb
->s_shdr
;
1058 si
->si_seq
= cb
->s_smax
+ 1;
1059 si
->si_len
= htons(sizeof(*si
));
1060 si
->si_cc
|= SPX_SP
;
1063 if (so
->so_options
& SO_DEBUG
|| traceallspxs
)
1064 spx_trace(SA_OUTPUT
, cb
->s_state
, cb
, si
, 0);
1068 * Stuff checksum and output datagram.
1070 if ((si
->si_cc
& SPX_SP
) == 0) {
1071 if (cb
->s_force
!= (1 + SPXT_PERSIST
) ||
1072 cb
->s_timer
[SPXT_PERSIST
] == 0) {
1074 * If this is a new packet and we are not currently
1075 * timing anything, time this one.
1077 if (SSEQ_LT(cb
->s_smax
, si
->si_seq
)) {
1078 cb
->s_smax
= si
->si_seq
;
1079 if (cb
->s_rtt
== 0) {
1080 spxstat
.spxs_segstimed
++;
1081 cb
->s_rtseq
= si
->si_seq
;
1086 * Set rexmt timer if not currently set,
1087 * Initial value for retransmit timer is smoothed
1088 * round-trip time + 2 * round-trip time variance.
1089 * Initialize shift counter which is used for backoff
1090 * of retransmit time.
1092 if (cb
->s_timer
[SPXT_REXMT
] == 0 &&
1093 cb
->s_snxt
!= cb
->s_rack
) {
1094 cb
->s_timer
[SPXT_REXMT
] = cb
->s_rxtcur
;
1095 if (cb
->s_timer
[SPXT_PERSIST
]) {
1096 cb
->s_timer
[SPXT_PERSIST
] = 0;
1100 } else if (SSEQ_LT(cb
->s_smax
, si
->si_seq
)) {
1101 cb
->s_smax
= si
->si_seq
;
1103 } else if (cb
->s_state
< TCPS_ESTABLISHED
) {
1105 cb
->s_rtt
= 1; /* Time initial handshake */
1106 if (cb
->s_timer
[SPXT_REXMT
] == 0)
1107 cb
->s_timer
[SPXT_REXMT
] = cb
->s_rxtcur
;
1111 * Do not request acks when we ack their data packets or
1112 * when we do a gratuitous window update.
1114 if (((si
->si_cc
& SPX_SP
) == 0) || cb
->s_force
)
1115 si
->si_cc
|= SPX_SA
;
1116 si
->si_seq
= htons(si
->si_seq
);
1117 si
->si_alo
= htons(alo
);
1118 si
->si_ack
= htons(cb
->s_ack
);
1121 si
->si_sum
= ipx_cksum(m
, ntohs(si
->si_len
));
1123 si
->si_sum
= 0xffff;
1126 if (so
->so_options
& SO_DEBUG
|| traceallspxs
)
1127 spx_trace(SA_OUTPUT
, cb
->s_state
, cb
, si
, 0);
1129 if (so
->so_options
& SO_DONTROUTE
)
1130 error
= ipx_outputfl(m
, (struct route
*)NULL
, IPX_ROUTETOIF
);
1132 error
= ipx_outputfl(m
, &cb
->s_ipxpcb
->ipxp_route
, 0);
1137 spxstat
.spxs_sndtotal
++;
1139 * Data sent (as far as we can tell).
1140 * If this advertises a larger window than any other segment,
1141 * then remember the size of the advertized window.
1142 * Any pending ACK has now been sent.
1145 cb
->s_flags
&= ~(SF_ACKNOW
|SF_DELACK
);
1146 if (SSEQ_GT(alo
, cb
->s_alo
))
1154 static int spx_do_persist_panics
= 0;
1157 spx_setpersist(struct spxpcb
*cb
)
1159 int t
= ((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1;
1161 if (cb
->s_timer
[SPXT_REXMT
] && spx_do_persist_panics
)
1162 panic("spx_output REXMT");
1164 * Start/restart persistance timer.
1166 SPXT_RANGESET(cb
->s_timer
[SPXT_PERSIST
],
1167 t
*spx_backoff
[cb
->s_rxtshift
],
1168 SPXTV_PERSMIN
, SPXTV_PERSMAX
);
1169 if (cb
->s_rxtshift
< SPX_MAXRXTSHIFT
)
1174 spx_ctloutput(struct socket
*so
, struct sockopt
*sopt
)
1176 struct ipxpcb
*ipxp
= sotoipxpcb(so
);
1185 if (sopt
->sopt_level
!= IPXPROTO_SPX
) {
1186 /* This will have to be changed when we do more general
1187 stacking of protocols */
1188 return (ipx_ctloutput(so
, sopt
));
1193 cb
= ipxtospxpcb(ipxp
);
1195 switch (sopt
->sopt_dir
) {
1197 switch (sopt
->sopt_name
) {
1198 case SO_HEADERS_ON_INPUT
:
1202 case SO_HEADERS_ON_OUTPUT
:
1205 soptval
= cb
->s_flags
& mask
;
1206 error
= sooptcopyout(sopt
, &soptval
, sizeof soptval
);
1210 usoptval
= cb
->s_mtu
;
1211 error
= sooptcopyout(sopt
, &usoptval
, sizeof usoptval
);
1214 case SO_LAST_HEADER
:
1215 error
= sooptcopyout(sopt
, &cb
->s_rhdr
,
1219 case SO_DEFAULT_HEADERS
:
1220 error
= sooptcopyout(sopt
, &cb
->s_shdr
,
1225 error
= ENOPROTOOPT
;
1230 switch (sopt
->sopt_name
) {
1231 /* XXX why are these shorts on get and ints on set?
1232 that doesn't make any sense... */
1233 case SO_HEADERS_ON_INPUT
:
1237 case SO_HEADERS_ON_OUTPUT
:
1240 error
= sooptcopyin(sopt
, &optval
, sizeof optval
,
1245 if (cb
->s_flags
& SF_PI
) {
1247 cb
->s_flags
|= mask
;
1249 cb
->s_flags
&= ~mask
;
1250 } else error
= EINVAL
;
1254 error
= sooptcopyin(sopt
, &usoptval
, sizeof usoptval
,
1258 cb
->s_mtu
= usoptval
;
1263 error
= sooptcopyin(sopt
, &optval
, sizeof optval
,
1268 cb
->s_flags2
|= SF_NEWCALL
;
1271 cb
->s_flags2
&= ~SF_NEWCALL
;
1277 case SO_DEFAULT_HEADERS
:
1281 error
= sooptcopyin(sopt
, &sp
, sizeof sp
,
1285 cb
->s_dt
= sp
.spx_dt
;
1286 cb
->s_cc
= sp
.spx_cc
& SPX_EM
;
1291 error
= ENOPROTOOPT
;
1299 spx_usr_abort(struct socket
*so
)
1301 struct ipxpcb
*ipxp
;
1304 ipxp
= sotoipxpcb(so
);
1305 cb
= ipxtospxpcb(ipxp
);
1308 spx_drop(cb
, ECONNABORTED
);
1314 * Accept a connection. Essentially all the work is
1315 * done at higher levels; just return the address
1316 * of the peer, storing through addr.
1319 spx_accept(struct socket
*so
, struct sockaddr
**nam
)
1321 struct ipxpcb
*ipxp
;
1322 struct sockaddr_ipx
*sipx
, ssipx
;
1324 ipxp
= sotoipxpcb(so
);
1326 bzero(sipx
, sizeof *sipx
);
1327 sipx
->sipx_len
= sizeof *sipx
;
1328 sipx
->sipx_family
= AF_IPX
;
1329 sipx
->sipx_addr
= ipxp
->ipxp_faddr
;
1330 *nam
= dup_sockaddr((struct sockaddr
*)sipx
);
1335 spx_attach(struct socket
*so
, int proto
, struct pru_attach_info
*ai
)
1338 struct ipxpcb
*ipxp
;
1341 struct signalsockbuf
*ssb
;
1343 ipxp
= sotoipxpcb(so
);
1344 cb
= ipxtospxpcb(ipxp
);
1349 error
= ipx_pcballoc(so
, &ipxpcb
);
1351 goto spx_attach_end
;
1352 if (so
->so_snd
.ssb_hiwat
== 0 || so
->so_rcv
.ssb_hiwat
== 0) {
1353 error
= soreserve(so
, (u_long
) 3072, (u_long
) 3072,
1356 goto spx_attach_end
;
1358 ipxp
= sotoipxpcb(so
);
1360 MALLOC(cb
, struct spxpcb
*, sizeof *cb
, M_PCB
, M_INTWAIT
| M_ZERO
);
1363 mm
= m_getclr(MB_DONTWAIT
, MT_HEADER
);
1367 goto spx_attach_end
;
1370 cb
->s_ipx
= mtod(mm
, struct ipx
*);
1371 cb
->s_state
= TCPS_LISTEN
;
1374 cb
->s_q
.si_next
= cb
->s_q
.si_prev
= &cb
->s_q
;
1375 cb
->s_ipxpcb
= ipxp
;
1376 cb
->s_mtu
= 576 - sizeof(struct spx
);
1377 cb
->s_cwnd
= ssb_space(ssb
) * CUNIT
/ cb
->s_mtu
;
1378 cb
->s_ssthresh
= cb
->s_cwnd
;
1379 cb
->s_cwmx
= ssb_space(ssb
) * CUNIT
/ (2 * sizeof(struct spx
));
1380 /* Above is recomputed when connecting to account
1381 for changed buffering or mtu's */
1382 cb
->s_rtt
= SPXTV_SRTTBASE
;
1383 cb
->s_rttvar
= SPXTV_SRTTDFLT
<< 2;
1384 SPXT_RANGESET(cb
->s_rxtcur
,
1385 ((SPXTV_SRTTBASE
>> 2) + (SPXTV_SRTTDFLT
<< 2)) >> 1,
1386 SPXTV_MIN
, SPXTV_REXMTMAX
);
1387 ipxp
->ipxp_pcb
= (caddr_t
)cb
;
1394 spx_bind(struct socket
*so
, struct sockaddr
*nam
, struct thread
*td
)
1396 struct ipxpcb
*ipxp
;
1398 ipxp
= sotoipxpcb(so
);
1400 return (ipx_pcbbind(ipxp
, nam
, td
));
1404 * Initiate connection to peer.
1405 * Enter SYN_SENT state, and mark socket as connecting.
1406 * Start keep-alive timer, setup prototype header,
1407 * Send initial system packet requesting connection.
1410 spx_connect(struct socket
*so
, struct sockaddr
*nam
, struct thread
*td
)
1413 struct ipxpcb
*ipxp
;
1416 ipxp
= sotoipxpcb(so
);
1417 cb
= ipxtospxpcb(ipxp
);
1420 if (ipxp
->ipxp_lport
== 0) {
1421 error
= ipx_pcbbind(ipxp
, (struct sockaddr
*)NULL
, td
);
1423 goto spx_connect_end
;
1425 error
= ipx_pcbconnect(ipxp
, nam
, td
);
1427 goto spx_connect_end
;
1429 spxstat
.spxs_connattempt
++;
1430 cb
->s_state
= TCPS_SYN_SENT
;
1433 cb
->s_timer
[SPXT_KEEP
] = SPXTV_KEEP
;
1434 cb
->s_force
= 1 + SPXTV_KEEP
;
1436 * Other party is required to respond to
1437 * the port I send from, but he is not
1438 * required to answer from where I am sending to,
1439 * so allow wildcarding.
1440 * original port I am sending to is still saved in
1443 ipxp
->ipxp_fport
= 0;
1444 error
= spx_output(cb
, (struct mbuf
*)NULL
);
1451 spx_detach(struct socket
*so
)
1453 struct ipxpcb
*ipxp
;
1456 ipxp
= sotoipxpcb(so
);
1457 cb
= ipxtospxpcb(ipxp
);
1462 if (cb
->s_state
> TCPS_LISTEN
)
1471 * We may decide later to implement connection closing
1472 * handshaking at the spx level optionally.
1473 * here is the hook to do it:
1476 spx_usr_disconnect(struct socket
*so
)
1478 struct ipxpcb
*ipxp
;
1481 ipxp
= sotoipxpcb(so
);
1482 cb
= ipxtospxpcb(ipxp
);
1491 spx_listen(struct socket
*so
, struct thread
*td
)
1494 struct ipxpcb
*ipxp
;
1498 ipxp
= sotoipxpcb(so
);
1499 cb
= ipxtospxpcb(ipxp
);
1501 if (ipxp
->ipxp_lport
== 0)
1502 error
= ipx_pcbbind(ipxp
, (struct sockaddr
*)NULL
, td
);
1504 cb
->s_state
= TCPS_LISTEN
;
1509 * After a receive, possibly send acknowledgment
1510 * updating allocation.
1513 spx_rcvd(struct socket
*so
, int flags
)
1515 struct ipxpcb
*ipxp
;
1518 ipxp
= sotoipxpcb(so
);
1519 cb
= ipxtospxpcb(ipxp
);
1522 cb
->s_flags
|= SF_RVD
;
1523 spx_output(cb
, (struct mbuf
*)NULL
);
1524 cb
->s_flags
&= ~SF_RVD
;
1530 spx_rcvoob(struct socket
*so
, struct mbuf
*m
, int flags
)
1532 struct ipxpcb
*ipxp
;
1535 ipxp
= sotoipxpcb(so
);
1536 cb
= ipxtospxpcb(ipxp
);
1538 if ((cb
->s_oobflags
& SF_IOOB
) || so
->so_oobmark
||
1539 (so
->so_state
& SS_RCVATMARK
)) {
1541 *mtod(m
, caddr_t
) = cb
->s_iobc
;
1548 spx_send(struct socket
*so
, int flags
, struct mbuf
*m
, struct sockaddr
*addr
,
1549 struct mbuf
*controlp
, struct thread
*td
)
1552 struct ipxpcb
*ipxp
;
1556 ipxp
= sotoipxpcb(so
);
1557 cb
= ipxtospxpcb(ipxp
);
1560 if (flags
& PRUS_OOB
) {
1561 if (ssb_space(&so
->so_snd
) < -512) {
1565 cb
->s_oobflags
|= SF_SOOB
;
1567 if (controlp
!= NULL
) {
1568 u_short
*p
= mtod(controlp
, u_short
*);
1570 if ((p
[0] == 5) && (p
[1] == 1)) { /* XXXX, for testing */
1571 cb
->s_shdr
.spx_dt
= *(u_char
*)(&p
[2]);
1577 error
= spx_output(cb
, m
);
1580 if (controlp
!= NULL
)
1589 spx_shutdown(struct socket
*so
)
1592 struct ipxpcb
*ipxp
;
1596 ipxp
= sotoipxpcb(so
);
1597 cb
= ipxtospxpcb(ipxp
);
1601 cb
= spx_usrclosed(cb
);
1603 error
= spx_output(cb
, (struct mbuf
*)NULL
);
1609 spx_sp_attach(struct socket
*so
, int proto
, struct pru_attach_info
*ai
)
1612 struct ipxpcb
*ipxp
;
1614 error
= spx_attach(so
, proto
, ai
);
1616 ipxp
= sotoipxpcb(so
);
1617 ((struct spxpcb
*)ipxp
->ipxp_pcb
)->s_flags
|=
1618 (SF_HI
| SF_HO
| SF_PI
);
1624 * Create template to be used to send spx packets on a connection.
1625 * Called after host entry created, fills
1626 * in a skeletal spx header (choosing connection id),
1627 * minimizing the amount of work necessary when the connection is used.
1630 spx_template(struct spxpcb
*cb
)
1632 struct ipxpcb
*ipxp
= cb
->s_ipxpcb
;
1633 struct ipx
*ipx
= cb
->s_ipx
;
1634 struct signalsockbuf
*ssb
= &(ipxp
->ipxp_socket
->so_snd
);
1636 ipx
->ipx_pt
= IPXPROTO_SPX
;
1637 ipx
->ipx_sna
= ipxp
->ipxp_laddr
;
1638 ipx
->ipx_dna
= ipxp
->ipxp_faddr
;
1639 cb
->s_sid
= htons(spx_iss
);
1640 spx_iss
+= SPX_ISSINCR
/2;
1642 cb
->s_cwnd
= (ssb_space(ssb
) * CUNIT
) / cb
->s_mtu
;
1643 cb
->s_ssthresh
= cb
->s_cwnd
; /* Try to expand fast to full complement
1645 cb
->s_cwmx
= (ssb_space(ssb
) * CUNIT
) / (2 * sizeof(struct spx
));
1646 cb
->s_cwmx
= max(cb
->s_cwmx
, cb
->s_cwnd
);
1647 /* But allow for lots of little packets as well */
1651 * Close a SPIP control block:
1652 * discard spx control block itself
1653 * discard ipx protocol control block
1654 * wake up any sleepers
1656 static struct spxpcb
*
1657 spx_close(struct spxpcb
*cb
)
1661 struct ipxpcb
*ipxp
= cb
->s_ipxpcb
;
1662 struct socket
*so
= ipxp
->ipxp_socket
;
1665 q
= cb
->s_q
.si_next
;
1666 while (q
!= &(cb
->s_q
)) {
1674 m_free(cb
->s_ipx_m
);
1677 soisdisconnected(so
);
1678 ipx_pcbdetach(ipxp
);
1679 spxstat
.spxs_closed
++;
1680 return ((struct spxpcb
*)NULL
);
1684 * Someday we may do level 3 handshaking
1685 * to close a connection or send a xerox style error.
1686 * For now, just close.
1688 static struct spxpcb
*
1689 spx_usrclosed(struct spxpcb
*cb
)
1691 return (spx_close(cb
));
1694 static struct spxpcb
*
1695 spx_disconnect(struct spxpcb
*cb
)
1697 return (spx_close(cb
));
1701 * Drop connection, reporting
1702 * the specified error.
1704 static struct spxpcb
*
1705 spx_drop(struct spxpcb
*cb
, int errno
)
1707 struct socket
*so
= cb
->s_ipxpcb
->ipxp_socket
;
1710 * someday, in the xerox world
1711 * we will generate error protocol packets
1712 * announcing that the socket has gone away.
1714 if (TCPS_HAVERCVDSYN(cb
->s_state
)) {
1715 spxstat
.spxs_drops
++;
1716 cb
->s_state
= TCPS_CLOSED
;
1719 spxstat
.spxs_conndrops
++;
1720 so
->so_error
= errno
;
1721 return (spx_close(cb
));
1725 * Fast timeout routine for processing delayed acks
1730 struct ipxpcb
*ipxp
;
1734 ipxp
= ipxpcb
.ipxp_next
;
1736 for (; ipxp
!= &ipxpcb
; ipxp
= ipxp
->ipxp_next
) {
1737 if ((cb
= (struct spxpcb
*)ipxp
->ipxp_pcb
) != NULL
&&
1738 (cb
->s_flags
& SF_DELACK
)) {
1739 cb
->s_flags
&= ~SF_DELACK
;
1740 cb
->s_flags
|= SF_ACKNOW
;
1741 spxstat
.spxs_delack
++;
1742 spx_output(cb
, (struct mbuf
*)NULL
);
1750 * spx protocol timeout routine called every 500 ms.
1751 * Updates the timers in all active pcb's and
1752 * causes finite state machine actions if timers expire.
1757 struct ipxpcb
*ip
, *ipnxt
;
1762 * Search through tcb's and update active timers.
1765 ip
= ipxpcb
.ipxp_next
;
1770 while (ip
!= &ipxpcb
) {
1771 cb
= ipxtospxpcb(ip
);
1772 ipnxt
= ip
->ipxp_next
;
1775 for (i
= 0; i
< SPXT_NTIMERS
; i
++) {
1776 if (cb
->s_timer
[i
] && --cb
->s_timer
[i
] == 0) {
1778 if (ipnxt
->ipxp_prev
!= ip
)
1788 spx_iss
+= SPX_ISSINCR
/PR_SLOWHZ
; /* increment iss */
1793 * SPX timer processing.
1795 static struct spxpcb
*
1796 spx_timers(struct spxpcb
*cb
, int timer
)
1801 cb
->s_force
= 1 + timer
;
1805 * 2 MSL timeout in shutdown went off. TCP deletes connection
1809 kprintf("spx: SPXT_2MSL went off for no reason\n");
1810 cb
->s_timer
[timer
] = 0;
1814 * Retransmission timer went off. Message has not
1815 * been acked within retransmit interval. Back off
1816 * to a longer retransmit interval and retransmit one packet.
1819 if (++cb
->s_rxtshift
> SPX_MAXRXTSHIFT
) {
1820 cb
->s_rxtshift
= SPX_MAXRXTSHIFT
;
1821 spxstat
.spxs_timeoutdrop
++;
1822 cb
= spx_drop(cb
, ETIMEDOUT
);
1825 spxstat
.spxs_rexmttimeo
++;
1826 rexmt
= ((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1;
1827 rexmt
*= spx_backoff
[cb
->s_rxtshift
];
1828 SPXT_RANGESET(cb
->s_rxtcur
, rexmt
, SPXTV_MIN
, SPXTV_REXMTMAX
);
1829 cb
->s_timer
[SPXT_REXMT
] = cb
->s_rxtcur
;
1831 * If we have backed off fairly far, our srtt
1832 * estimate is probably bogus. Clobber it
1833 * so we'll take the next rtt measurement as our srtt;
1834 * move the current srtt into rttvar to keep the current
1835 * retransmit times until then.
1837 if (cb
->s_rxtshift
> SPX_MAXRXTSHIFT
/ 4 ) {
1838 cb
->s_rttvar
+= (cb
->s_srtt
>> 2);
1841 cb
->s_snxt
= cb
->s_rack
;
1843 * If timing a packet, stop the timer.
1847 * See very long discussion in tcp_timer.c about congestion
1848 * window and sstrhesh
1850 win
= min(cb
->s_swnd
, (cb
->s_cwnd
/CUNIT
)) / 2;
1854 cb
->s_ssthresh
= win
* CUNIT
;
1855 spx_output(cb
, (struct mbuf
*)NULL
);
1859 * Persistance timer into zero window.
1860 * Force a probe to be sent.
1863 spxstat
.spxs_persisttimeo
++;
1865 spx_output(cb
, (struct mbuf
*)NULL
);
1869 * Keep-alive timer went off; send something
1870 * or drop connection if idle for too long.
1873 spxstat
.spxs_keeptimeo
++;
1874 if (cb
->s_state
< TCPS_ESTABLISHED
)
1876 if (cb
->s_ipxpcb
->ipxp_socket
->so_options
& SO_KEEPALIVE
) {
1877 if (cb
->s_idle
>= SPXTV_MAXIDLE
)
1879 spxstat
.spxs_keepprobe
++;
1880 spx_output(cb
, (struct mbuf
*)NULL
);
1883 cb
->s_timer
[SPXT_KEEP
] = SPXTV_KEEP
;
1886 spxstat
.spxs_keepdrops
++;
1887 cb
= spx_drop(cb
, ETIMEDOUT
);