* Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)spp_usrreq.c 7.9 (Berkeley) %G%
#include "../net/route.h"
#include "../netinet/tcp_fsm.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_mtu
= ocb
->s_mtu
; /* preserve sockopts */
cb
->s_flags
= ocb
->s_flags
; /* preserve sockopts */
cb
->s_state
= TCPS_LISTEN
;
* Packet received on connection.
* reset idle time and keep-alive timer;
cb
->s_timer
[SPPT_KEEP
] = SPPTV_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 + SPPT_KEEP
;
cb
->s_timer
[SPPT_KEEP
] = SPPTV_KEEP
;
* This state means that we have heard a response
* to our acceptance of their connection
* It is probably logically unnecessary in this
case TCPS_SYN_RECEIVED
: {
if (si
->si_did
!=cb
->s_sid
) {
nsp
->nsp_fport
= si
->si_sport
;
cb
->s_timer
[SPPT_REXMT
] = 0;
cb
->s_timer
[SPPT_KEEP
] = SPPTV_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
[SPPT_REXMT
] = 0;
cb
->s_flags
|= SF_ACKNOW
;
cb
->s_state
= TCPS_ESTABLISHED
;
/* Use roundtrip time of connection request for initial rtt */
cb
->s_srtt
= cb
->s_rtt
<< 3;
cb
->s_rttvar
= cb
->s_rtt
<< 1;
SPPT_RANGESET(cb
->s_rxtcur
,
((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1,
SPPTV_MIN
, SPPTV_REXMTMAX
);
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
);
if (cb
->s_force
|| (cb
->s_flags
& (SF_ACKNOW
|SF_WIN
|SF_RXT
)))
(void) spp_output(cb
, (struct mbuf
*)0);
cb
->s_flags
&= ~(SF_WIN
|SF_RXT
);
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
||
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
;
register struct socket
*so
= cb
->s_nspcb
->nsp_socket
;
char packetp
= cb
->s_flags
& SF_HI
;
* Update our news from them.
cb
->s_flags
|= (spp_use_delack
? SF_DELACK
: SF_ACKNOW
);
if (SSEQ_GT(si
->si_alo
, cb
->s_ralo
))
if (SSEQ_LEQ(si
->si_ack
, cb
->s_rack
)) {
if ((si
->si_cc
& SP_SP
) && cb
->s_rack
!= (cb
->s_smax
+ 1)) {
sppstat
.spps_rcvdupack
++;
* If this is a completely duplicate ack
* and other conditions hold, we assume
* a packet has been dropped and retransmit
* it exactly as in tcp_input().
if (si
->si_ack
!= cb
->s_rack
||
si
->si_alo
!= cb
->s_ralo
)
else if (++cb
->s_dupacks
== spprexmtthresh
) {
u_short onxt
= cb
->s_snxt
;
cb
->s_force
= 1 + SPPT_REXMT
;
(void) spp_output(cb
, (struct mbuf
*)0);
cb
->s_timer
[SPPT_REXMT
] = cb
->s_rxtcur
;
if (SSEQ_GT(onxt
, cb
->s_snxt
))
* If our correspondent acknowledges data we haven't sent
* TCP would drop the packet after acking. We'll be a little
if (SSEQ_GT(si
->si_ack
, (cb
->s_smax
+ 1))) {
sppstat
.spps_rcvacktoomuch
++;
si
->si_ack
= cb
->s_smax
+ 1;
sppstat
.spps_rcvackpack
++;
* If transmit timer is running and timed sequence
* number was acked, update smoothed round trip time.
* See discussion of algorithm in tcp_input.c
if (cb
->s_rtt
&& SSEQ_GT(si
->si_ack
, cb
->s_rtseq
)) {
sppstat
.spps_rttupdated
++;
delta
= cb
->s_rtt
- (cb
->s_srtt
>> 3);
if ((cb
->s_srtt
+= delta
) <= 0)
delta
-= (cb
->s_rttvar
>> 2);
if ((cb
->s_rttvar
+= delta
) <= 0)
cb
->s_srtt
= cb
->s_rtt
<< 3;
cb
->s_rttvar
= cb
->s_rtt
<< 1;
SPPT_RANGESET(cb
->s_rxtcur
,
((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1,
SPPTV_MIN
, SPPTV_REXMTMAX
);
* If all outstanding data is acked, stop retransmit
* timer and remember to restart (more output or persist).
* If there is more data to be acked, restart retransmit
* timer, using current (possibly backed-off) value;
if (si
->si_ack
== cb
->s_smax
+ 1) {
cb
->s_timer
[SPPT_REXMT
] = 0;
} else if (cb
->s_timer
[SPPT_PERSIST
] == 0)
cb
->s_timer
[SPPT_REXMT
] = cb
->s_rxtcur
;
* When new data is acked, open the congestion window.
* If the window gives us less than ssthresh packets
* in flight, open exponentially (maxseg at a time).
* Otherwise open linearly (maxseg^2 / cwnd at a time).
if (cb
->s_cwnd
> cb
->s_ssthresh
)
incr
= MAX(incr
* incr
/ cb
->s_cwnd
, 1);
cb
->s_cwnd
= MIN(cb
->s_cwnd
+ incr
, cb
->s_cwmx
);
* Trim Acked data from output queue.
while ((m
= so
->so_snd
.sb_mb
) != NULL
) {
if (SSEQ_LT((mtod(m
, struct spidp
*))->si_seq
, si
->si_ack
))
sbdroprecord(&so
->so_snd
);
if ((so
->so_snd
.sb_flags
& SB_WAIT
) || so
->so_snd
.sb_sel
)
if (SSEQ_LT(cb
->s_snxt
, cb
->s_rack
))
if (SSEQ_LT(cb
->s_swl1
, si
->si_seq
) || cb
->s_swl1
== si
->si_seq
&&
(SSEQ_LT(cb
->s_swl2
, si
->si_ack
) ||
cb
->s_swl2
== si
->si_ack
&& SSEQ_LT(cb
->s_ralo
, si
->si_alo
))) {
/* keep track of pure window updates */
if ((si
->si_cc
& SP_SP
) && cb
->s_swl2
== si
->si_ack
&& SSEQ_LT(cb
->s_ralo
, si
->si_alo
)) {
sppstat
.spps_rcvwinupd
++;
sppstat
.spps_rcvdupack
--;
cb
->s_swnd
= (1 + si
->si_alo
- si
->si_ack
);
if (cb
->s_swnd
> cb
->s_smxw
)
* 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
)) {
sppstat
.spps_rcvwinprobe
++;
sppstat
.spps_rcvpackafterwin
++;
if (SSEQ_GT(si
->si_seq
, cb
->s_alo
+ 60)) {
ns_error(dtom(si
), NS_ERR_FULLUP
, 0);
} /* else queue this packet; */
/*register struct socket *so = cb->s_nspcb->nsp_socket;
if (so->so_state && SS_NOFDREF) {
ns_error(dtom(si), NS_ERR_NOSOCK, 0);
ns_error(dtom(si
), NS_ERR_FULLUP
, 0);
* If this is a system packet, we don't need to
* queue it up, and won't update acknowledge #
* We have already seen this packet, so drop.
if (SSEQ_LT(si
->si_seq
, cb
->s_ack
)) {
sppstat
.spps_rcvduppack
++;
if (si
->si_seq
== cb
->s_ack
- 1)
* 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
) {
sppstat
.spps_rcvduppack
++;
if (SSEQ_LT(si
->si_seq
, SI(q
)->si_seq
)) {
sppstat
.spps_rcvoopack
++;
* 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
= so
->so_rcv
.sb_cc
;
so
->so_state
|= SS_RCVATMARK
;
sbappendrecord(&so
->so_rcv
, m
);
cb
->s_rhdr
= *mtod(m
, struct sphdr
*);
sbappend(&so
->so_rcv
, m
);
if (wakeup
) sorwakeup(so
);
extern u_char nsctlerrmap
[];
extern spp_abort(), spp_quench();
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
]);
ns_pcbnotify(na
, 0, spp_quench
, (long) 0);
* When a source quench is received, close congestion window
* to one packet. We will gradually open it again as we proceed.
struct sppcb
*cb
= nstosppcb(nsp
);
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
;
int len
= 0, win
, rcv_win
;
* 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
;
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.
* Long align so prepended ip headers will work on Gould.
m
->m_off
= MMAXOFF
- sizeof (struct spidp
) - 2;
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
idle
= (cb
->s_smax
== (cb
->s_rack
- 1));
off
= cb
->s_snxt
- cb
->s_rack
;
win
= MIN(cb
->s_swnd
, (cb
->s_cwnd
/CUNIT
));
* If in persist timeout with window of 0, send a probe.
* Otherwise, if window is small but nonzero
* and timer expired, send what we can and go into
if (cb
->s_force
== 1 + SPPT_PERSIST
) {
cb
->s_timer
[SPPT_PERSIST
] = 0;
span
= cb
->s_seq
- cb
->s_rack
;
len
= MIN(span
, win
) - off
;
* Window shrank after we went into it.
* If window shrank to 0, cancel pending
* restransmission and pull s_snxt back
* to (closed) window. We will enter persist
* state below. If the widndow didn't close completely,
cb
->s_timer
[SPPT_REXMT
] = 0;
rcv_win
= sbspace(&so
->so_rcv
);
* Send if we owe peer an ACK.
if (cb
->s_oobflags
& SF_SOOB
) {
* must transmit this out of band packet
cb
->s_oobflags
&= ~ SF_SOOB
;
if (cb
->s_flags
& SF_ACKNOW
)
if (cb
->s_state
< TCPS_ESTABLISHED
)
* Silly window can't happen in spp.
* Compare available window to amount of window
* known to peer (as advertised window less
* next expected input.) If the difference is at least two
* packets or at least 35% of the mximum possible window,
* then want to send a window update to peer.
u_short delta
= 1 + cb
->s_alo
- cb
->s_ack
;
int adv
= rcv_win
- (delta
* cb
->s_mtu
);
if ((so
->so_rcv
.sb_cc
== 0 && adv
>= (2 * cb
->s_mtu
)) ||
(100 * adv
/ so
->so_rcv
.sb_hiwat
>= 35)) {
cb
->s_flags
|= SF_ACKNOW
;
* Many comments from tcp_output.c are appropriate here
* If send window is too small, there is data to transmit, and no
* retransmit or persist is pending, then go to persist state.
* If nothing happens soon, send when timer expires:
* if window is nonzero, transmit what we can,
* otherwise send a probe.
if (so
->so_snd
.sb_cc
&& cb
->s_timer
[SPPT_REXMT
] == 0 &&
cb
->s_timer
[SPPT_PERSIST
] == 0) {
* No reason to send a packet, just return.
for (m
= sb
->sb_mb
; m
; m
= m
->m_act
) {
si
= mtod(m
, struct spidp
*);
if (SSEQ_LEQ(cb
->s_snxt
, si
->si_seq
))
if (si
->si_seq
== cb
->s_snxt
)
sppstat
.spps_sndvoid
++, si
= 0;
alo
= cb
->s_ack
- 1 + (rcv_win
/ ((short)cb
->s_mtu
));
if (SSEQ_LT(alo
, cb
->s_alo
))
* 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
*);
if (SSEQ_LT(si
->si_seq
, cb
->s_smax
))
sppstat
.spps_sndrexmitpack
++;
} else if (cb
->s_force
|| cb
->s_flags
& SF_ACKNOW
) {
* Must send an acknowledgement or a probe
if (cb
->s_flags
& SF_ACKNOW
)
m
= m_get(M_DONTWAIT
, MT_HEADER
);
* Fill in mbuf with extended SP header
* and addresses and length put into network format.
* Allign beginning of packet to long to prepend
* ifp's on loopback, or NSIP encaspulation for fussy cpu's.
m
->m_off
= MMAXOFF
- sizeof (struct spidp
) - 2;
si
= mtod(m
, struct spidp
*);
si
->si_seq
= cb
->s_smax
+ 1;
si
->si_len
= htons(sizeof (*si
));
if (so
->so_options
& SO_DEBUG
|| traceallspps
)
spp_trace(SA_OUTPUT
, cb
->s_state
, cb
, si
, 0);
* Stuff checksum and output datagram.
if ((si
->si_cc
& SP_SP
) == 0) {
if (cb
->s_force
!= (1 + SPPT_PERSIST
) ||
cb
->s_timer
[SPPT_PERSIST
] == 0) {
* If this is a new packet and we are not currently
* timing anything, time this one.
if (SSEQ_LT(cb
->s_smax
, si
->si_seq
)) {
sppstat
.spps_segstimed
++;
cb
->s_rtseq
= si
->si_seq
;
* Set rexmt timer if not currently set,
* Initial value for retransmit timer is smoothed
* round-trip time + 2 * round-trip time variance.
* Initialize shift counter which is used for backoff
if (cb
->s_timer
[SPPT_REXMT
] == 0 &&
cb
->s_snxt
!= cb
->s_rack
) {
cb
->s_timer
[SPPT_REXMT
] = cb
->s_rxtcur
;
if (cb
->s_timer
[SPPT_PERSIST
]) {
cb
->s_timer
[SPPT_PERSIST
] = 0;
} else if (SSEQ_LT(cb
->s_smax
, si
->si_seq
)) {
} else if (cb
->s_state
< TCPS_ESTABLISHED
) {
cb
->s_rtt
= 1; /* Time initial handshake */
if (cb
->s_timer
[SPPT_REXMT
] == 0)
cb
->s_timer
[SPPT_REXMT
] = cb
->s_rxtcur
;
* Do not request acks when we ack their data packets or
* when we do a gratuitous window update.
if (((si
->si_cc
& SP_SP
) == 0) || cb
->s_force
)
si
->si_seq
= htons(si
->si_seq
);
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);
* Data sent (as far as we can tell).
* If this advertises a larger window than any other segment,
* then remember the size of the advertized window.
* Any pending ACK has now been sent.
cb
->s_flags
&= ~(SF_ACKNOW
|SF_DELACK
);
if (SSEQ_GT(alo
, cb
->s_alo
))
int spp_do_persist_panics
= 0;
register struct sppcb
*cb
;
register t
= ((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1;
extern int spp_backoff
[];
if (cb
->s_timer
[SPPT_REXMT
] && spp_do_persist_panics
)
panic("spp_output REXMT");
* Start/restart persistance timer.
SPPT_RANGESET(cb
->s_timer
[SPPT_PERSIST
],
t
*spp_backoff
[cb
->s_rxtshift
],
SPPTV_PERSMIN
, SPPTV_PERSMAX
);
if (cb
->s_rxtshift
< SPP_MAXRXTSHIFT
)
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
;
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
;
register struct sockbuf
*sb
;
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
);
if (so
->so_snd
.sb_hiwat
== 0 || so
->so_rcv
.sb_hiwat
== 0) {
error
= soreserve(so
, (u_long
) 3072, (u_long
) 3072);
mm
= m_getclr(M_DONTWAIT
, MT_PCB
);
cb
= mtod(mm
, struct sppcb
*);
mm
= m_getclr(M_DONTWAIT
, MT_HEADER
);
cb
->s_idp
= mtod(mm
, struct idp
*);
cb
->s_state
= TCPS_LISTEN
;
cb
->s_q
.si_next
= cb
->s_q
.si_prev
= &cb
->s_q
;
cb
->s_mtu
= 576 - sizeof (struct spidp
);
cb
->s_cwnd
= sbspace(sb
) * CUNIT
/ cb
->s_mtu
;
cb
->s_ssthresh
= cb
->s_cwnd
;
cb
->s_cwmx
= sb
->sb_mbmax
* CUNIT
/
(2 * sizeof (struct spidp
));
/* Above is recomputed when connecting to account
for changed buffering or mtu's */
cb
->s_rtt
= SPPTV_SRTTBASE
;
cb
->s_rttvar
= SPPTV_SRTTDFLT
<< 2;
SPPT_RANGESET(cb
->s_rxtcur
,
((SPPTV_SRTTBASE
>> 2) + (SPPTV_SRTTDFLT
<< 2)) >> 1,
SPPTV_MIN
, SPPTV_REXMTMAX
);
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
);
sppstat
.spps_connattempt
++;
cb
->s_state
= TCPS_SYN_SENT
;
cb
->s_timer
[SPPT_KEEP
] = SPPTV_KEEP
;
cb
->s_force
= 1 + SPPTV_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);
(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 sppcb
*cb
;
register struct nspcb
*nsp
= cb
->s_nspcb
;
register struct idp
*idp
= cb
->s_idp
;
register struct sockbuf
*sb
= &(nsp
->nsp_socket
->so_snd
);
idp
->idp_pt
= NSPROTO_SPP
;
idp
->idp_sna
= nsp
->nsp_laddr
;
idp
->idp_dna
= nsp
->nsp_faddr
;
cb
->s_sid
= htons(spp_iss
);
spp_iss
+= SPP_ISSINCR
/2;
cb
->s_cwnd
= (sbspace(sb
) * CUNIT
) / cb
->s_mtu
;
cb
->s_ssthresh
= cb
->s_cwnd
; /* Try to expand fast to full complement
cb
->s_cwmx
= (sb
->sb_mbmax
* CUNIT
) / (2 * sizeof(struct spidp
));
cb
->s_cwmx
= MAX(cb
->s_cwmx
, cb
->s_cwnd
);
/* But allow for lots of little packets as well */
* 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
)) {
(void) m_free(dtom(cb
->s_idp
));
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(cb
->s_state
)) {
cb
->s_state
= TCPS_CLOSED
;
/*(void) tcp_output(cb);*/
sppstat
.spps_conndrops
++;
(void) spp_close((struct sppcb
*)nsp
->nsp_pcb
);
int spp_backoff
[SPP_MAXRXTSHIFT
+1] =
{ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
* 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
;
cb
->s_flags
|= SF_ACKNOW
;
(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
< SPPT_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 */
register struct sppcb
*cb
;
* 2 MSL timeout in shutdown went off. TCP deletes connection
printf("spp: SPPT_2MSL went off for no reason\n");
* Retransmission timer went off. Message has not
* been acked within retransmit interval. Back off
* to a longer retransmit interval and retransmit one packet.
if (++cb
->s_rxtshift
> SPP_MAXRXTSHIFT
) {
cb
->s_rxtshift
= SPP_MAXRXTSHIFT
;
sppstat
.spps_timeoutdrop
++;
cb
= spp_drop(cb
, ETIMEDOUT
);
sppstat
.spps_rexmttimeo
++;
rexmt
= ((cb
->s_srtt
>> 2) + cb
->s_rttvar
) >> 1;
rexmt
*= spp_backoff
[cb
->s_rxtshift
];
SPPT_RANGESET(cb
->s_rxtcur
, rexmt
, SPPTV_MIN
, SPPTV_REXMTMAX
);
cb
->s_timer
[SPPT_REXMT
] = cb
->s_rxtcur
;
* If we have backed off fairly far, our srtt
* estimate is probably bogus. Clobber it
* so we'll take the next rtt measurement as our srtt;
* move the current srtt into rttvar to keep the current
* retransmit times until then.
if (cb
->s_rxtshift
> SPP_MAXRXTSHIFT
/ 4 ) {
cb
->s_rttvar
+= (cb
->s_srtt
>> 2);
* If timing a packet, stop the timer.
* See very long discussion in tcp_timer.c about congestion
win
= MIN(cb
->s_swnd
, (cb
->s_cwnd
/CUNIT
)) / 2;
cb
->s_ssthresh
= win
* CUNIT
;
(void) spp_output(cb
, (struct mbuf
*) 0);
* Persistance timer into zero window.
* Force a probe to be sent.
sppstat
.spps_persisttimeo
++;
(void) spp_output(cb
, (struct mbuf
*) 0);
* Keep-alive timer went off; send something
* or drop connection if idle for too long.
sppstat
.spps_keeptimeo
++;
if (cb
->s_state
< TCPS_ESTABLISHED
)
if (cb
->s_nspcb
->nsp_socket
->so_options
& SO_KEEPALIVE
) {
if (cb
->s_idle
>= SPPTV_MAXIDLE
)
sppstat
.spps_keepprobe
++;
(void) spp_output(cb
, (struct mbuf
*) 0);
cb
->s_timer
[SPPT_KEEP
] = SPPTV_KEEP
;
sppstat
.spps_keepdrops
++;
cb
= spp_drop(cb
, ETIMEDOUT
);
int SppcbSize
= sizeof (struct sppcb
);
int NspcbSize
= sizeof (struct nspcb
);