* Copyright (c) 1982 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)spp_usrreq.c 6.15 (Berkeley) %G%
#include "../net/route.h"
#include "../netinet/tcp_fsm.h"
#include "../netinet/tcp_timer.h"
* SP protocol implementation.
spp_iss
= 1; /* WRONG !! should fish it out of TODR */
register struct nspcb
*nsp
;
register struct sppcb
*cb
;
register struct spidp
*si
= mtod(m
, struct spidp
*);
register struct socket
*so
;
panic("No nspcb in spp_input\n");
if (m
->m_len
< sizeof(*si
)) {
if ((m
= m_pullup(m
, sizeof(*si
))) == 0) {
si
= mtod(m
, struct spidp
*);
si
->si_seq
= ntohs(si
->si_seq
);
si
->si_ack
= ntohs(si
->si_ack
);
si
->si_alo
= ntohs(si
->si_alo
);
if (so
->so_options
& SO_DEBUG
|| traceallspps
) {
if (so
->so_options
& SO_ACCEPTCONN
) {
* Mark socket as temporary until we're
* committed to keeping it. The code at
* ``drop'' and ``dropwithreset'' check the
* flag dropsocket to see if the temporary
* socket created here should be discarded.
* We mark the socket as discardable until
* we're committed to it below in TCPS_LISTEN.
nsp
= (struct nspcb
*)so
->so_pcb
;
nsp
->nsp_laddr
= si
->si_dna
;
cb
->s_state
= TCPS_LISTEN
;
* Packet received on connection.
* reset idle time and keep-alive timer;
cb
->s_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
register struct sockaddr_ns
*sns
;
* If somebody here was carying on a conversation
* and went away, and his pen pal thinks he can
* still talk, we get the misdirected packet.
if (spp_hardnosed
&& (si
->si_did
!= 0 || si
->si_seq
!= 0)) {
am
= m_get(M_DONTWAIT
, MT_SONAME
);
am
->m_len
= sizeof (struct sockaddr_ns
);
sns
= mtod(am
, struct sockaddr_ns
*);
sns
->sns_addr
= si
->si_sna
;
nsp
->nsp_laddr
= si
->si_dna
;
if (ns_pcbconnect(nsp
, am
)) {
dropsocket
= 0; /* committed to socket */
cb
->s_state
= TCPS_SYN_RECEIVED
;
cb
->s_force
= 1 + TCPT_REXMT
;
cb
->s_timer
[TCPT_REXMT
] = 2 * TCPTV_MIN
;
* This state means that we have heard a response
* to our acceptance of their connection
* It is probably logically unnecessary in this
if (si
->si_did
!=cb
->s_sid
) {
nsp
->nsp_fport
= si
->si_sport
;
cb
->s_timer
[TCPT_REXMT
] = 0;
cb
->s_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
cb
->s_state
= TCPS_ESTABLISHED
;
* This state means that we have gotten a response
* to our attempt to establish a connection.
* We fill in the data from the other side,
* telling us which port to respond to, instead of the well-
* known one we might have sent to in the first place.
* We also require that this is a response to our
if (si
->si_did
!=cb
->s_sid
) {
cb
->s_dport
= nsp
->nsp_fport
= si
->si_sport
;
cb
->s_timer
[TCPT_REXMT
] = 0;
cb
->s_state
= TCPS_ESTABLISHED
;
if (so
->so_options
& SO_DEBUG
|| traceallspps
)
spp_trace(SA_INPUT
, (u_char
)ostate
, cb
, &spp_savesi
, 0);
m
->m_len
-= sizeof (struct idp
);
m
->m_off
+= sizeof (struct idp
);
(void) spp_output(cb
, (struct mbuf
*)0);
si
->si_seq
= ntohs(si
->si_seq
);
si
->si_ack
= ntohs(si
->si_ack
);
si
->si_alo
= ntohs(si
->si_alo
);
ns_error(dtom(si
), NS_ERR_NOSOCK
, 0);
if (cb
->s_nspcb
->nsp_socket
->so_options
& SO_DEBUG
|| traceallspps
)
spp_trace(SA_DROP
, (u_char
)ostate
, cb
, &spp_savesi
, 0);
if (cb
== 0 || cb
->s_nspcb
->nsp_socket
->so_options
& SO_DEBUG
|| traceallspps
)
spp_trace(SA_DROP
, (u_char
)ostate
, cb
, &spp_savesi
, 0);
* This is structurally similar to the tcp reassembly routine
* but its function is somewhat different: It merely queues
* packets up, and suppresses duplicates.
register struct sppcb
*cb
;
register struct spidp
*si
;
register struct spidp_q
*q
;
struct socket
*so
= cb
->s_nspcb
->nsp_socket
;
struct sockbuf
*sb
= & (so
->so_rcv
);
char packetp
= cb
->s_flags
& SF_HI
;
* Update our news from them.
cb
->s_flags
|= (spp_use_delack
? SF_DELACK
: SF_AK
);
if (SSEQ_GT(si
->si_ack
, cb
->s_rack
)) {
* If there are other packets outstanding,
* restart the timer for them.
if (SSEQ_GEQ(cb
->s_snt
, si
->si_ack
)) {
TCPT_RANGESET(cb
->s_timer
[TCPT_REXMT
],
tcp_beta
* cb
->s_srtt
, TCPTV_MIN
,
cb
->s_timer
[TCPT_REXMT
] = 0;
* If transmit timer is running and timed sequence
* number was acked, update smoothed round trip time.
if (cb
->s_rtt
&& SSEQ_GT(si
->si_ack
, cb
->s_rtseq
)) {
(1 - tcp_alpha
) * cb
->s_rtt
;
if (SSEQ_GT(si
->si_alo
, cb
->s_ralo
)) {
cb
->s_timer
[TCPT_PERSIST
] = 0;
* If this is a system packet, we don't need to
* queue it up, and won't update acknowledge #
* If this packet number has a sequence number less
* than that of the first packet not yet seen coming
* from them, this must be a duplicate, so drop.
if (SSEQ_LT(si
->si_seq
, cb
->s_ack
)) {
if (si
->si_seq
== cb
->s_ack
-1)
* If this packet number is higher than that which
* we have allocated refuse it, unless urgent
if (SSEQ_GT(si
->si_seq
, cb
->s_alo
)) {
if (SSEQ_GT(si
->si_seq
, cb
->s_alo
+ 60)) {
ns_error(dtom(si
), NS_ERR_FULLUP
, 0);
} /* else queue this packet; */
* Loop through all packets queued up to insert in
for (q
= cb
->s_q
.si_next
; q
!=&cb
->s_q
; q
= q
->si_next
) {
if (si
->si_seq
== SI(q
)->si_seq
) return (1); /*duplicate */
if (SSEQ_LT(si
->si_seq
, SI(q
)->si_seq
)) break;
* If this packet is urgent, inform process
cb
->s_iobc
= ((char *)si
)[1 + sizeof(*si
)];
cb
->s_oobflags
|= SF_IOOB
;
#define SPINC sizeof(struct sphdr)
* Loop through all packets queued up to update acknowledge
* number, and present all acknowledged data to user;
* If in packet interface mode, show packet headers.
for (q
= cb
->s_q
.si_next
; q
!=&cb
->s_q
; q
= q
->si_next
) {
if (SI(q
)->si_seq
== cb
->s_ack
) {
if (SI(q
)->si_cc
& SP_OB
) {
cb
->s_oobflags
&= ~SF_IOOB
;
so
->so_oobmark
= sb
->sb_cc
;
so
->so_state
|= SS_RCVATMARK
;
cb
->s_rhdr
= *mtod(m
, struct sphdr
*);
if (wakeup
) sorwakeup(so
);
extern u_char nsctlerrmap
[];
extern struct nspcb
*idp_drop();
if (cmd
< 0 || cmd
> PRC_NCMDS
)
type
= NS_ERR_UNREACH_HOST
;
sns
= (struct sockaddr_ns
*)arg
;
if (sns
->sns_family
!= AF_NS
)
errp
= (struct ns_errp
*)arg
;
na
= &errp
->ns_err_idp
.idp_dna
;
type
= ntohs((u_short
)type
);
case NS_ERR_UNREACH_HOST
:
ns_pcbnotify(na
, (int)nsctlerrmap
[cmd
], spp_abort
, (long) 0);
nsp
= ns_pcblookup(na
, errp
->ns_err_idp
.idp_sna
.x_port
,
(void) spp_drop((struct sppcb
*)nsp
->nsp_pcb
,
(void) idp_drop(nsp
, (int)nsctlerrmap
[cmd
]);
register struct nspcb
*nsp
;
register struct sppcb
*cb
= (struct sppcb
*)(nsp
->nsp_pcb
);
register struct spidp
*si
;
struct mbuf
*firstbad
, *m0
;
* The notification that we have sent
* too much is bad news -- we will
* have to go through queued up so far
* splitting ones which are too big and
* reassigning sequence numbers and checksums.
* we should then retransmit all packets from
* one above the offending packet to the last one
* we had sent (or our allocation)
* then the offending one so that the any queued
* data at our destination will be discarded.
ep
= (struct ns_errp
*)nsp
->nsp_notify_param
;
sb
= &nsp
->nsp_socket
->so_snd
;
cb
->s_mtu
= ep
->ns_err_param
;
badseq
= SI(&ep
->ns_err_idp
)->si_seq
;
for (m
= sb
->sb_mb
; m
; m
= m
->m_act
) {
si
= mtod(m
, struct spidp
*);
if (si
->si_seq
== badseq
)
for (m0
= m
, len
= 0; m
; m
= m
->m_next
)
register struct sppcb
*cb
;
struct socket
*so
= cb
->s_nspcb
->nsp_socket
;
register struct spidp
*si
= (struct spidp
*) 0;
register struct sockbuf
*sb
= &(so
->so_snd
);
* Make sure that packet isn't too big.
for (m
= m0
; m
; m
= m
->m_next
) {
datalen
= (cb
->s_flags
& SF_HO
) ?
len
- sizeof (struct sphdr
) : len
;
if (cb
->s_flags
& SF_PI
) {
int oldEM
= cb
->s_cc
& SP_EM
;
m
= m_copy(m0
, off
, mtu
);
error
= spp_output(cb
, m
);
* Force length even, by adding a "garbage byte" if
if (m
->m_len
+ m
->m_off
< MMAXOFF
)
struct mbuf
*m1
= m_get(M_DONTWAIT
, MT_DATA
);
m
= m_get(M_DONTWAIT
, MT_HEADER
);
* Fill in mbuf with extended SP header
* and addresses and length put into network format.
m
->m_off
= MMAXOFF
- sizeof (struct spidp
);
m
->m_len
= sizeof (struct spidp
);
si
= mtod(m
, struct spidp
*);
if ((cb
->s_flags
& SF_PI
) && (cb
->s_flags
& SF_HO
)) {
register struct sphdr
*sh
;
if (m0
->m_len
< sizeof (*sh
)) {
if((m0
= m_pullup(m0
, sizeof(*sh
))) == NULL
) {
sh
= mtod(m0
, struct sphdr
*);
si
->si_cc
|= sh
->sp_cc
& SP_EM
;
m0
->m_len
-= sizeof (*sh
);
m0
->m_off
+= sizeof (*sh
);
if (cb
->s_oobflags
& SF_SOOB
) {
* make sure OB packets convey exactly 1 byte.
* If the packet is 1 byte or larger, we
* have already guaranted there to be at least
* one garbage byte for the checksum, and
* extra bytes shouldn't hurt!
si
->si_len
= htons((u_short
)len
);
* queue stuff up for output
register struct sockbuf
*sb2
= &so
->so_rcv
;
int credit
= ((sb2
->sb_mbmax
- sb2
->sb_mbcnt
) /
int alo
= cb
->s_ack
+ (credit
> 0 ? credit
: 0) - 1;
/* If the amount we are raising the window
is more than his remaining headroom, tell
him about it. In particular, if he is at
his limit, any amount at all will do! */
u_short raise
= alo
- cb
->s_alo
;
u_short headroom
= 1 + cb
->s_alo
- cb
->s_ack
;
if(SSEQ_LT(headroom
, raise
))
if (cb
->s_oobflags
& SF_SOOB
) {
* must transmit this out of band packet
cb
->s_oobflags
&= ~ SF_SOOB
;
* Decide what to transmit:
* If it is time to retransmit a packet,
* If we have a new packet, send that
* (So long as it is in our allocation)
* Otherwise, see if it time to bang on them
* to ask for our current allocation.
if (cb
->s_force
== (1+TCPT_REXMT
)) {
} else if (SSEQ_LT(cb
->s_snt
, cb
->s_ralo
)) {
} else if (SSEQ_LT(cb
->s_ralo
, cb
->s_seq
)) {
if (cb
->s_timer
[TCPT_PERSIST
] == 0) {
/* tcp has cb->s_rxtshift = 0; here */
si
= mtod(m
, struct spidp
*);
if (SSEQ_LT(si
->si_seq
, cb
->s_rack
)) {
if ((sb
->sb_flags
& SB_WAIT
)
if (SSEQ_LT(si
->si_seq
, lookfor
))
if (si
&& (si
->si_seq
!= lookfor
))
* must make a copy of this packet for
* idp_output to monkey with
m
= m_copy(dtom(si
), 0, (int)M_COPYALL
);
si
= mtod(m
, struct spidp
*);
} else if (cb
->s_force
|| cb
->s_flags
& SF_AK
) {
* Must send an acknowledgement or a probe
m
= m_get(M_DONTWAIT
, MT_HEADER
);
* Fill in mbuf with extended SP header
* and addresses and length put into network format.
m
->m_off
= MMAXOFF
- sizeof (struct spidp
);
si
= mtod(m
, struct spidp
*);
si
->si_seq
= cb
->s_snt
+ 1;
si
->si_len
= htons(sizeof (*si
));
* Stuff checksum and output datagram.
if (cb
->s_flags
& (SF_AK
|SF_DELACK
))
cb
->s_flags
&= ~(SF_AK
|SF_DELACK
);
* If we are almost out of allocation
* or one of the timers has gone off
if (SSEQ_GEQ(cb
->s_seq
, cb
->s_ralo
))
* If this is a new packet (and not a system packet),
* and we are not currently timing anything,
* time this one and ask for an ack.
if (SSEQ_LT(cb
->s_snt
, si
->si_seq
) && (!(si
->si_cc
& SP_SP
))) {
cb
->s_rtseq
= si
->si_seq
;
* If the retransmit timer has not been set
* and this is a real packet
* then start the retransmit timer
if (cb
->s_timer
[TCPT_REXMT
] == 0) {
TCPT_RANGESET(cb
->s_timer
[TCPT_REXMT
],
tcp_beta
* cb
->s_srtt
, TCPTV_MIN
,
si
->si_seq
= htons(si
->si_seq
);
si
->si_alo
= htons(cb
->s_alo
);
si
->si_ack
= htons(cb
->s_ack
);
si
->si_sum
= ns_cksum(dtom(si
), len
);
if (so
->so_options
& SO_DEBUG
|| traceallspps
)
spp_trace(SA_OUTPUT
, cb
->s_state
, cb
, si
, 0);
if (so
->so_options
& SO_DONTROUTE
)
error
= ns_output(m
, (struct route
*)0, NS_ROUTETOIF
);
error
= ns_output(m
, &cb
->s_nspcb
->nsp_route
, 0);
if (traceallspps
&& sppconsdebug
) {
printf("spp_out: %x\n", error
);
if (so
->so_options
& SO_DEBUG
|| traceallspps
)
spp_trace(SA_OUTPUT
, cb
->s_state
, cb
, si
, 0);
spp_ctloutput(req
, so
, level
, name
, value
)
struct nspcb
*nsp
= sotonspcb(so
);
register struct sppcb
*cb
;
if (level
!= NSPROTO_SPP
) {
/* This will have to be changed when we do more general
return (idp_ctloutput(req
, so
, level
, name
, value
));
m
= m_get(M_DONTWAIT
, MT_DATA
);
case SO_HEADERS_ON_INPUT
:
case SO_HEADERS_ON_OUTPUT
:
m
->m_len
= sizeof(short);
m
->m_off
= MMAXOFF
- sizeof(short);
*mtod(m
, short *) = cb
->s_flags
& mask
;
m
->m_len
= sizeof(u_short
);
m
->m_off
= MMAXOFF
- sizeof(short);
*mtod(m
, short *) = cb
->s_mtu
;
m
->m_len
= sizeof(struct sphdr
);
m
->m_off
= MMAXOFF
- sizeof(struct sphdr
);
*mtod(m
, struct sphdr
*) = cb
->s_rhdr
;
m
->m_len
= sizeof(struct spidp
);
m
->m_off
= MMAXOFF
- sizeof(struct sphdr
);
*mtod(m
, struct sphdr
*) = cb
->s_shdr
.si_s
;
if (value
== 0 || *value
== 0) {
case SO_HEADERS_ON_INPUT
:
case SO_HEADERS_ON_OUTPUT
:
if (cb
->s_flags
& SF_PI
) {
ok
= mtod(*value
, int *);
cb
->s_mtu
= *(mtod(*value
, u_short
*));
register struct sphdr
*sp
= mtod(*value
, struct sphdr
*);
cb
->s_cc
= sp
->sp_cc
& SP_EM
;
spp_usrreq(so
, req
, m
, nam
, rights
)
struct mbuf
*m
, *nam
, *rights
;
struct nspcb
*nsp
= sotonspcb(so
);
register struct sppcb
*cb
;
return (ns_control(so
, (int)m
, (caddr_t
)nam
,
(struct ifnet
*)rights
));
if (rights
&& rights
->m_len
) {
ostate
= cb
? cb
->s_state
: 0;
error
= ns_pcballoc(so
, &nspcb
);
error
= soreserve(so
, 2048, 2048);
struct mbuf
*mm
= m_getclr(M_DONTWAIT
, MT_PCB
);
cb
= mtod(mm
, struct sppcb
*);
cb
->s_state
= TCPS_LISTEN
;
cb
->s_q
.si_next
= cb
->s_q
.si_prev
= &cb
->s_q
;
nsp
->nsp_pcb
= (caddr_t
) cb
;
if (cb
->s_state
> TCPS_LISTEN
)
error
= ns_pcbbind(nsp
, nam
);
error
= ns_pcbbind(nsp
, (struct mbuf
*)0);
cb
->s_state
= TCPS_LISTEN
;
* Initiate connection to peer.
* Enter SYN_SENT state, and mark socket as connecting.
* Start keep-alive timer, setup prototype header,
* Send initial system packet requesting connection.
if (nsp
->nsp_lport
== 0) {
error
= ns_pcbbind(nsp
, (struct mbuf
*)0);
error
= ns_pcbconnect(nsp
, nam
);
cb
->s_state
= TCPS_SYN_SENT
;
cb
->s_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
cb
->s_force
= 1 + TCPTV_KEEP
;
* Other party is required to respond to
* the port I send from, but he is not
* required to answer from where I am sending to,
* original port I am sending to is still saved in
error
= spp_output(cb
, (struct mbuf
*) 0);
* We may decide later to implement connection closing
* handshaking at the spp level optionally.
* here is the hook to do it:
* Accept a connection. Essentially all the work is
* done at higher levels; just return the address
* of the peer, storing through addr.
struct sockaddr_ns
*sns
= mtod(nam
, struct sockaddr_ns
*);
nam
->m_len
= sizeof (struct sockaddr_ns
);
sns
->sns_addr
= nsp
->nsp_faddr
;
error
= spp_output(cb
, (struct mbuf
*) 0);
* After a receive, possibly send acknowledgment
(void) spp_output(cb
, (struct mbuf
*) 0);
error
= spp_output(cb
, m
);
(void) spp_drop(cb
, ECONNABORTED
);
if ((cb
->s_oobflags
& SF_IOOB
) || so
->so_oobmark
||
(so
->so_state
& SS_RCVATMARK
)) {
*mtod(m
, caddr_t
) = cb
->s_iobc
;
if (sbspace(&so
->so_snd
) < -512) {
cb
->s_oobflags
|= SF_SOOB
;
error
= spp_output(cb
, m
);
ns_setsockaddr(nsp
, nam
);
ns_setpeeraddr(nsp
, nam
);
cb
= spp_timers(cb
, (int)nam
);
if (cb
&& (so
->so_options
& SO_DEBUG
|| traceallspps
))
spp_trace(SA_USER
, (u_char
)ostate
, cb
, (struct spidp
*)0, req
);
spp_usrreq_sp(so
, req
, m
, nam
, rights
)
struct mbuf
*m
, *nam
, *rights
;
int error
= spp_usrreq(so
, req
, m
, nam
, rights
);
if (req
== PRU_ATTACH
&& error
== 0) {
struct nspcb
*nsp
= sotonspcb(so
);
((struct sppcb
*)nsp
->nsp_pcb
)->s_flags
|=
* Create template to be used to send spp packets on a connection.
* Called after host entry created, fills
* in a skeletal spp header (choosing connection id),
* minimizing the amount of work necessary when the connection is used.
register struct nspcb
*nsp
= cb
->s_nspcb
;
register struct spidp
*n
= &(cb
->s_shdr
);
cb
->s_mtu
= 576 - sizeof (struct spidp
);
n
->si_sna
= nsp
->nsp_laddr
;
n
->si_dna
= nsp
->nsp_faddr
;
n
->si_sid
= htons(spp_iss
);
spp_iss
+= SPP_ISSINCR
/2;
* Close a SPIP control block:
* discard spp control block itself
* discard ns protocol control block
register struct sppcb
*cb
;
register struct spidp_q
*s
;
struct nspcb
*nsp
= cb
->s_nspcb
;
struct socket
*so
= nsp
->nsp_socket
;
while (s
!= &(cb
->s_q
)) {
return ((struct sppcb
*)0);
* Someday we may do level 3 handshaking
* to close a connection or send a xerox style error.
register struct sppcb
*cb
;
register struct sppcb
*cb
;
* Drop connection, reporting
register struct sppcb
*cb
;
struct socket
*so
= cb
->s_nspcb
->nsp_socket
;
* someday, in the xerox world
* we will generate error protocol packets
* announcing that the socket has gone away.
/*if (TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_state = TCPS_CLOSED;
(void) spp_close((struct sppcb
*)nsp
->nsp_pcb
);
register struct sppcb
*cb
;
/*if (cb->s_timer[TCPT_REXMT])
panic("spp_output REXMT");*/
* Start/restart persistance timer.
TCPT_RANGESET(cb
->s_timer
[TCPT_PERSIST
],
((int)(tcp_beta
* cb
->s_srtt
)) << cb
->s_rxtshift
,
TCPTV_PERSMIN
, TCPTV_MAX
);
if (cb
->s_rxtshift
>= TCP_MAXRXTSHIFT
)
* Fast timeout routine for processing delayed acks
register struct nspcb
*nsp
;
register struct sppcb
*cb
;
for (; nsp
!= &nspcb
; nsp
= nsp
->nsp_next
)
if ((cb
= (struct sppcb
*)nsp
->nsp_pcb
) &&
(cb
->s_flags
& SF_DELACK
)) {
cb
->s_flags
&= ~SF_DELACK
;
(void) spp_output(cb
, (struct mbuf
*) 0);
* spp protocol timeout routine called every 500 ms.
* Updates the timers in all active pcb's and
* causes finite state machine actions if timers expire.
register struct nspcb
*ip
, *ipnxt
;
register struct sppcb
*cb
;
* Search through tcb's and update active timers.
for (i
= 0; i
< TCPT_NTIMERS
; i
++) {
if (cb
->s_timer
[i
] && --cb
->s_timer
[i
] == 0) {
(void) spp_usrreq(cb
->s_nspcb
->nsp_socket
,
PRU_SLOWTIMO
, (struct mbuf
*)0,
(struct mbuf
*)i
, (struct mbuf
*)0);
if (ipnxt
->nsp_prev
!= ip
)
spp_iss
+= SPP_ISSINCR
/PR_SLOWHZ
; /* increment iss */
float spp_backoff
[TCP_MAXRXTSHIFT
] =
{ 1.0, 1.2, 1.4, 1.7, 2.0, 3.0, 5.0, 8.0, 16.0, 32.0 };
int sppexprexmtbackoff
= 0;
register struct sppcb
*cb
;
* 2 MSL timeout in shutdown went off. Delete connection
* Retransmission timer went off. Message has not
* been acked within retransmit interval. Back off
* to a longer retransmit interval and retransmit all
* unacknowledged messages in the window.
if (cb
->s_rxtshift
> TCP_MAXRXTSHIFT
) {
cb
= spp_drop(cb
, ETIMEDOUT
);
(void) spp_output(cb
, (struct mbuf
*) 0);
TCPT_RANGESET(cb
->s_timer
[TCPT_REXMT
],
(int)cb
->s_srtt
, TCPTV_MIN
, TCPTV_MAX
);
if (sppexprexmtbackoff
) {
TCPT_RANGESET(cb
->s_timer
[TCPT_REXMT
],
cb
->s_timer
[TCPT_REXMT
] << cb
->s_rxtshift
,
TCPT_RANGESET(cb
->s_timer
[TCPT_REXMT
],
cb
->s_timer
[TCPT_REXMT
] *
spp_backoff
[cb
->s_rxtshift
- 1],
* Persistance timer into zero window.
* Force a probe to be sent.
(void) spp_output(cb
, (struct mbuf
*) 0);
* Keep-alive timer went off; send something
* or drop connection if idle for too long.
if (cb
->s_state
< TCPS_ESTABLISHED
)
if (cb
->s_nspcb
->nsp_socket
->so_options
& SO_KEEPALIVE
) {
if (cb
->s_idle
>= TCPTV_MAXIDLE
)
(void) spp_output(cb
, (struct mbuf
*) 0);
cb
->s_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
cb
= spp_drop(cb
, ETIMEDOUT
);