\ Layer 3 and 4 shacham
\ ARP, IP, ICMP, UDP, TCP, telnetd
only
net definitions \ include in net vocaculary
extensions also os also drivers also net definitions
4 constant #IP 32 constant #ARPS
1 constant IPICMP 6 constant IPTCP 17 constant IPUDP
struct stats intcell rcv intcell ok intcell bad intcell xmt endstruct
struct iphost int32 addr int32 gw int32 mask int32 bcst endstruct
\ create iphst $C0A80020 , $C0A80001 , $FFFFFF00 , 0 , \ inside NAT
create iphst $40A11614 , $40A11611 , $FFFFFFF8 , 0 , \ outside - s20
\ Layer 3 and 4 shacham
\ ARP
20 constant ARPTIME \ 20 min
5 constant ARPRETRY \ 5 min
struct arpentry #MAC bytes ha #IP bytes pa int16 filler intcell timer
endstruct
struct arp int16 hrd int16 pro int8 hln int8 pln int16 op
#MAC bytes sha #IP bytes spa #MAC bytes tha #IP bytes tpa endstruct
create arpst stats.size allot
create arptab arpentry.size #ARPS * allot
\ Layer 3 and 4 shacham
: arpget ( -- m ) ether.size arp.size + m_get ;
: arpbuild ( tha m -- a )
tuck macaddr ENARP rot m_data enhdr \ Ethernet header
m_data ether.size + >r \ Ethernet load (arp packet)
1 r@ arp>hrd htons! ENIP r@ arp>pro htons! \ build generic arp packet
#MAC r@ arp>hln c! #IP r@ arp>pln c!
macaddr r@ arp>sha #MAC cmove
iphst iphost>addr @ htonl r@ arp>spa ! r> ;
: arptab$ ( -- a ) arptab arpentry.size #ARPS * + ;
: arpha ( pa -- false | arpentry ) arptab$ arptab do
dup i arpentry>pa @ = if drop i unloop exit then
arpentry.size +loop drop false ;
: arpupdt ( pa ha -- ) swap arpha ?dup 0= if drop else
tuck arpentry>ha #MAC cmove ARPTIME swap arpentry>timer ! then ;
: >arptab ( pa ha -- ) arptab$ arptab do
i arpentry>pa @ 0= if i arpentry>ha #MAC cmove i arpentry>pa !
ARPTIME i arpentry>timer ! unloop exit then
arpentry.size +loop ." arp table is full" ;
\ Layer 3 and 4 shacham
: arpreq ( pa ha -- ) arpget tuck arpbuild 1 over arp>op htons!
rot htonl swap arp>tpa ! enoutraw arpst stats>xmt inc ;
: arpresp ( pa ha -- ) arpget 2dup arpbuild rot over arp>tha #MAC cmove
rot htonl over arp>tpa ! 2 swap arp>op htons! enoutraw
arpst stats>xmt inc ;
: (arpbad) ( m arp -- ) drop m_freem arpst stats>bad inc ;
:noname ( m -- ) arpst stats>rcv inc dup m_data
dup arp>hrd @ ntohs 1 <> if (arpbad) exit then \ verify Ethernet hw
dup arp>pro @ ntohs ENIP <> if (arpbad) exit then \ and IP with
dup arp>hln c@ #MAC <> if (arpbad) exit then \ proper addr len
dup arp>pln c@ #IP <> if (arpbad) exit then \ of hrd and pro.
dup arp>spa @ ntoh dup arpha 0= if drop false \ if spa known (in cache)
else over arp>sha arpupdt true then \ update hwa in cache.
over arp>tpa @ ntoh iphst iphost>addr @ = if not if \ if local, cache,
dup arp>spa @ ntoh over arp>sha >arptab then \ and reply, if asked
dup arp>op @ ntohs 1 = if dup arp>spa @ ntoh over arp>sha arpresp then
else drop then drop m_freem \ merge flag not needed
arpst stats>ok inc ; is arpinput
defer in_broadcast
:noname ( pa -- false | ha ) dup in_broadcast if drop enbrdcst exit then
dup arpha dup 0= if swap ( keep false for ret ) enbrdcst arpreq else
nip arpentry>ha then ; is arprslv
\ Layer 3 and 4 shacham
create tsc1min 0 , 0 ,
create arp1min 0 , 0 ,
: (60sec) ( -- ) tsc1sec 2@ 60 0 do tsc1sec 2@ d+ loop tsc1min 2! ;
: arptimer ( -- ) tsc1min 2@ arp1min 2@ dnegate rdtsc d+ d< if \ every 1 min
arptab$ arptab do \ scan all arp table
i arpentry>timer @ ?dup 0<> if \ if timer is active
1- ?dup 0= if i arpentry.size erase else \ rm entry if timer is 0
dup i arpentry>timer ! ARPRETRY < if \ dec timer and
i arpentry>pa @ i arpentry>ha arpreq \ send arpreq before exp
then
then
then
arpentry.size +loop
tsc1min 2@ arp1min 2@ d+ arp1min 2! \ avoid timer drift
then ;
:noname ( u -- ) tsc1msec @ um* rdtsc d+ begin pause 2dup rdtsc d< until
2drop ; is delay
\ Layer 3 and 4 shacham
\ IP
struct ip int8 ip_vhl int8 ip_tos int16 ip_len int16 ip_id
int16 ip_off int8 ip_ttl int8 ip_p int16 ip_cksum int32 ip_src
int32 ip_dst endstruct
variable ipid
create ipst stats.size allot
: (bcstinit) ( -- ) iphst iphost>addr @ iphst iphost>mask @ not or
iphst iphost>bcst ! ;
: in_cksum ( a n -- u ) dup 1 and 0= if 0 else $FFFE and 2dup + c@ then
rot rot over + swap do i w@ + 2 +loop
begin dup 16 rshift dup 0<> while swap $FFFF and + repeat
drop not ( negate 1 - ) $FFFF and ;
:noname ( dst -- bool ) ?dup 0= if true exit then
dup $FFFFFFFF = if drop true exit then
iphst iphost>bcst @ = if true else false then ; is in_broadcast
: rtalloc ( dst -- dst' ) dup in_broadcast if exit then
iphst iphost>mask @ 2dup and iphst iphost>gw @ rot and <> if
drop iphst iphost>gw @ then ;
\ Layer 3 and 4 shacham
: (ipbad) ( m m -- ) drop m_freem ipst stats>bad inc ;
: (ipid) ( ip -- ) ip>ip_id ipid @ swap htons! ipid inc ;
: ipbuild ( m dst p -- ) rot >r r@ m_hdr>mh_len @ ip.size +
r@ ip.size negate m_adj r> m_data >r 0 r@ ip>ip_cksum w!
$45 r@ ip>ip_vhl c! 128 r@ ip>ip_ttl c! r@ ip>ip_len htons!
$10 r@ ip>ip_tos c! $4000 r@ ip>ip_off htons! ( don't fragment )
r@ ip>ip_p c! htonl r@ ip>ip_dst !
iphst iphost>addr @ htonl r@ ip>ip_src !
r@ (ipid) r@ ip.size in_cksum r> ip>ip_cksum w! ;
: ip_output ( m dst p -- ) >r 2dup r> ipbuild rtalloc enout
ipst stats>xmt inc ;
defer icmpinput defer tcp_input defer udp_input
:noname ( m -- ) ipst stats>rcv inc dup dup m_data
dup ip>ip_vhl c@ dup $45 <> if 2drop (ipbad) exit then
rot m_hdr.size + m_pkthdr>csum_flags @ $300 and dup $100 and 0<> if
nip $200 and $200 xor else
drop $F and 2 lshift over swap in_cksum then
0<> if (ipbad) exit then
ipst stats>ok inc over ip.size m_adj dup ip>ip_p c@
dup IPICMP = if drop icmpinput exit then
dup IPTCP = if drop tcp_input exit then
IPUDP = if udp_input exit then
drop m_freem ; is ip_input
\ Layer 3 and 4 shacham
\ ICMP
struct icmp int8 icmp_type int8 icmp_code int16 icmp_cksum
int16 icd_id int16 icd_seq 56 bytes icmp_data endstruct
create icmpst stats.size allot
: icmpxmt ( m dst -- ) IPICMP ip_output icmpst stats>xmt inc ;
: icmpbuild ( len -- m ) ether.size ip.size + tuck + m_get tuck swap m_adj ;
: icmpreply ( dst icmp len -- ) dup icmpbuild swap over m_data >r
rot over r@ swap cmove 0 r@ icmp>icmp_type c! \ org icmp packet, type ECHO
0 r@ icmp>icmp_cksum w! r@ swap in_cksum r> icmp>icmp_cksum w!
swap icmpxmt ;
defer icmprpld
:noname ( m ip -- ) over >r ip>ip_src @ ntoh swap \ ip_src is ip_dst for reply
dup m_hdr>mh_len @ swap m_data tuck over
in_cksum 0<> if r> m_freem drop 2drop icmpst stats>bad inc exit then
icmpst stats>ok inc over icmp>icmp_type c@ ?dup
0= if icmprpld r> m_freem icmpst stats>rcv inc exit then \ replied
8 = if icmpreply r> m_freem exit then \ reply to echo request
r> m_freem drop 2drop ( unsupported, for now ) ; is icmpinput
\ Layer 3 and 4 shacham
16 constant PINGS 1023 constant PINGMAX
struct pingcb int32 dst int32 count intcell pingt intcell a endstruct
struct pingt intcell xmt_h intcell xmt_l intcell rcv_h intcell rcv_l
intcell done endstruct
create pings PINGS pingcb.size * allot
: pings$ ( -- a ) pings PINGS pingcb.size * + ;
: pinginit ( dst n -- a | false ) pings$ pings do
i pingcb>dst @ 0= if i pingcb.size erase
dup pingt.size * dup bkalloc dup rot erase i pingcb>pingt !
i pingcb>count ! i pingcb>dst ! i unloop exit then
pingcb.size +loop 2drop false ;
: pingdone ( pingcb -- ) dup pingcb>pingt @ bkfree pingcb.size erase ;
: pingi>t ( pingcb i -- pingt ) pingt.size * swap pingcb>pingt @ + ;
: ping>cb ( dst icmp -- pingcb | false ) pings$ pings do
over i pingcb>dst @ = if i $FFFF and over icmp>icd_id @ ntohs = if
i pingcb>count over icmp>icd_seq @ ntohs > if
2drop i unloop exit then then then pingcb.size +loop 2drop false ;
:noname ( dst icmp len -- ) drop tuck ping>cb ?dup 0= if drop exit then
swap icmp>icd_seq @ ntohs pingi>t pingt>rcv_h rdtsc rot 2! ; is icmprpld
: pingsend ( seq pingcb -- ) icmp.size icmpbuild tuck m_data >r
8 r@ icmp>icmp_type c! \ implied - icmp_code is 0
dup $FFFF and r@ icmp>icd_id htons! rot r@ icmp>icd_seq htons!
r@ icmp.size in_cksum r> icmp>icmp_cksum w! pingcb>dst @ icmpxmt ;
\ Layer 3 and 4 shacham
: ping ( dst n -- ) dup PINGMAX > if 2drop ." too many. " exit then
pinginit ?dup 0= if ." too many ping sessions. " exit then ( pingcb )
dup pingcb>count @ 0 do
dup rdtsc rot i pingi>t pingt>xmt_h 2! i over pingsend
begin pause
dup i 1+ pingi>t over 0 pingi>t do
i pingt>done @ 0= if
i pingt>rcv_h 2@ or 0<> if cr
." seq=" i over 0 pingi>t - pingt.size / u.
i pingt>xmt_h 2@ dnegate i pingt>rcv_h 2@ d+ ." cycles=" u. u.
i pingt>xmt_h 2@ dnegate i pingt>rcv_h 2@ d+ tsc1msec @ um/mod
." time=" u. u. ." /" tsc1msec @ u. ." msec"
true i pingt>done !
then
then
pingt.size +loop
dup tsc1sec 2@ rot i pingi>t pingt>xmt_h 2@ dnegate rdtsc d+ d<
until
loop
pingdone ;
\ Layer 3 and 4 shacham
\ UDP
struct ipovly int32 ih_next int32 ih_prev int8 ih_xl int8 ih_pr
int16 ih_len int32 ih_src int32 ih_dst endstruct
struct udphdr int16 uh_sport int16 uh_dport int16 uh_ulen int16 uh_sum
endstruct
struct udpiphdr int32 ui_next int32 ui_prev int8 ui_xl int8 ui_pr
int16 ui_len int32 ui_src int32 ui_dst
int16 ui_sport int16 ui_dport int16 ui_ulen int16 ui_sum endstruct
create udpst stats.size allot
\ Layer 3 and 4 shacham
: (udpbad) ( m ip -- ) drop m_freem udpst stats>bad inc ;
:noname ( m ip -- ) udpst stats>rcv inc
over m_data udphdr>uh_ulen @ ntohs \ udp len
over ip>ip_len w@ ntohs ip.size - \ ip packet len, without ip header
over <> if drop (udpbad) exit then
over ip>ip_dst @ ntoh in_broadcast if then
>r over r> swap m_hdr.size + m_pkthdr>csum_flags @ $C00 and $C00 <> if
over ipovly>ih_xl @ >r \ keep org ip header fields
over >r dup 0 r@ ipovly>ih_xl c! r> ipovly>ih_len htons!
over ipovly>ih_xl swap ip.size + 8 - in_cksum
over ipovly>ih_xl r> swap ! \ restore ip header
else drop 0 then 0<> if (udpbad) exit then
drop m_freem ( TODO deliver to user, expected to issue m_freem )
udpst stats>ok inc ; is udp_input
\ Layer 3 and 4 shacham
\ TCP
struct tcphdr int16 th_sport int16 th_dport int32 th_seq int32 th_ack
int8 th_off int8 th_flags int16 th_win int16 th_sum int16 th_urp
endstruct
struct tcpiphdr int32 ti_next int32 ti_prev int8 ti_xl int8 ti_pr
int16 ti_len int32 ti_src int32 ti_dst
int16 ti_sport int16 ti_dport int32 ti_seq int32 ti_ack
int8 ti_off int8 ti_flags int16 ti_win int16 ti_sum int16 ti_urp
endstruct
struct sockbuf intcell sb_cc intcell sb_hiwat intcell sb_lowat
intcell sb_buf intcell sb_tail intcell sb_head intcell sb_next
endstruct
#ENDATA tcpiphdr.size - constant #MSS
\ Layer 3 and 4 shacham
struct tcpcb intcell seq_next intcell seq_prev int16 t_state
int16 tt_rxmt int16 t_rxtshift int16 t_rxtcur
intcell tt_persist intcell tt_keep intcell tt_idle intcell tt_2msl
intcell tt_delsnd intcell tt_delsnd0
intcell tt_delack intcell tt_delack0
int16 t_dupacks int16 t_maxseg int8 t_force int8 a0 int16 t_flags
intcell t_template intcell t_inpcb
int32 snd_una int32 snd_nxt int32 snd_up int32 snd_wl1 int32 snd_wl2
int32 iss int32 snd_wnd
int32 rcv_wnd int32 rcv_nxt int32 rcv_up int32 irs
int32 rcv_adv int32 snd_max int32 snd_cwnd int32 snd_ssthresh
int16 t_idle int16 t_rtt int32 t_rtseg int16 t_srtt int16 t_rttvar
int16 t_rttmin int16 snd_cnt int32 max_sndwnd
int8 t_oobflags int8 t_iobc int16 t_softerror
int8 snd_scale int8 rcv_scale int8 request_r_scale
int8 requested_s_scale int32 ts_recent int32 ts_recent_age
int32 last_ack_sent
intcell control intcell receive intcell headers intcell ipheaders
int16 sport int16 dport int32 daddr int16 mss int8 optln
int8 synoptln sockbuf.size bytes so_snd
intcell snd_maxcnt intcell snd_maxcwnd
endstruct
\ Layer 3 and 4 shacham
struct tcpstats intcell tcps_accepts intcell tcps_closed
intcell tcps_connattempts intcell tcps_conndrops intcell tcps_connects
intcell tcps_delack intcell tcps_drops intcell tcps_keepdrops
intcell tcps_keepprobe intcell tcps_keeptimeo intcell tcps_pawsdrop
intcell tcps_pcbcachemiss intcell tcps_persisttimeo intcell tcps_predack
intcell tcps_preddat intcell tcps_rcvackbyte intcell tcps_rcvackpack
intcell tcps_rcvacktoomuch intcell tcps_rcvafterclose
intcell tcps_rcvbadoff intcell tcps_rcvbadsum intcell tcps_rcvbyte
intcell tcps_rcvbyteafterwin intcell tcps_rcvdupack
intcell tcps_rcvdupbyte intcell tcps_rcvduppack
intcell tcps_rcvoobbyte intcell tcps_rcvoopack intcell tcps_rcvpack
intcell tcps_rcvackafterwin intcell tcps_rcvpartdupbyte
intcell tcps_rcvshort intcell tcps_rcvtotal intcell tcps_rcvwinprobe
intcell tcps_rcvwinupd intcell tcps_rexmttimeo intcell tcps_rttupdated
intcell tcps_segstimed intcell tcps_sndacks intcell tcps_sndbyte
intcell tcps_sndctrl intcell tcps_sndpack intcell tcps_sndprobe
intcell tcps_sndrexmitbyte intcell tcps_sendrexmitpack
intcell tcps_sndtotal intcell tcps_sndurg intcell tcps_sndwinup
intcell tcps_timeoutdrop intcell tcps_rcvseqnotnxt
intcell tcps_rcvwndzero intcell tcps_connrej intcell tcps_3dupack
intcell tcps_delsnd intcell f1 intcell f2
endstruct
\ Layer 3 and 4 shacham
$20 constant TH_URG $10 constant TH_ACK 8 constant TH_PSH
4 constant TH_RST 2 constant TH_SYN 1 constant TH_FIN
$3F constant TH_FLAGS
0 constant TCPS_CLOSED 1 constant TCPS_LISTEN 2 constant TCPS_SYN_SENT
3 constant TCPS_SYN_RECEIVED 4 constant TCPS_ESTABLISHED
5 constant TCPS_CLOSE_WAIT 6 constant TCPS_FIN_WAIT_1
7 constant TCPS_CLOSING 8 constant TCPS_LAST_ACK 9 constant TCPS_FIN_WAIT_2
10 constant TCPS_TIME_WAIT
23 constant PORT_TELNET
12 constant TELNET_OPTLEN 20 constant TELNET_SYNOPTLEN
1 constant TCP_NODELAY
12 constant TCP_MAXRXTSHIFT 128 constant TCPTV_REXMTMAX
300 constant TCP_KEEPIDLE 150 constant TCP_KEEPINTVL 4 constant TCP_MAXIDLE
1 constant API_ACCEPT 2 constant API_CLOSE 3 constant API_CLOSED
4 constant API_ABORT
create tcpstat tcpstats.size allot
3 constant #TCPRXMT 16 constant #TCPCB
create tcpcbs #TCPCB cells allot
variable tcp_now
\ Layer 3 and 4 shacham
: seq_lt ( a b -- bool ) - 0< ;
: seq_leq ( a b -- bool ) - ?dup 0= if true else 0< then ;
: seq_gt ( a b -- bool ) - 0> ;
: seq_geq ( a b -- bool ) - ?dup 0= if true else 0> then ;
: >iss ( a -- ) rdtsc 16 lshift swap 16 rshift or swap ! ;
\ Layer 3 and 4 shacham
: so_cc ( so_snd -- len ) sockbuf>sb_cc @ ;
: so_empty ( so_snd -- ) dup sockbuf>sb_buf @ tuck ( sb_buf so_snd sb_buf )
over sockbuf>sb_tail ! ( sb_buf so_snd ) 2dup sockbuf>sb_next !
( sb_buf so_snd ) sockbuf>sb_head ! ;
: so_next ( len tcpcb -- ) tcpcb>so_snd sockbuf>sb_next +! ;
: so_rxmt ( tcpcb -- ) tcpcb>so_snd dup sockbuf>sb_tail @ ( so_snd tail )
swap sockbuf>sb_next ! ;
: so_add ( data len tcpcb -- bool ) tcpcb>so_snd >r ( data len )
r@ sockbuf>sb_cc @ over + $FFFC > if r> drop 2drop true else
dup r@ sockbuf>sb_cc +! ( data len )
r@ so_cc r@ sockbuf>sb_hiwat @ > if r@ so_cc r@ sockbuf>sb_hiwat ! then
tuck r@ sockbuf>sb_head @ swap cmove ( len )
r> sockbuf>sb_head +! false then ;
: so_ack ( ack tcpcb -- ) dup tcpcb>so_snd so_cc 0> if
tuck tcpcb>snd_una @ - swap ( len tcpcb )
tcpcb>so_snd tuck over negate over sockbuf>sb_cc +! ( so_snd len so_snd )
sockbuf>sb_tail +! dup so_cc 0= if so_empty else drop then
else 2drop then ;
: so_data ( tcpcb -- data ) tcpcb>so_snd sockbuf>sb_next @ ;
: so_left ( tcpcb -- len ) dup tcpcb>so_snd sockbuf>sb_head @ swap so_data - ;
: so_get ( len m d-in -- len m ) >r 2dup m_data r> ( len m lem d-out d-in )
swap rot ( len m d-in d-out len ) cmove ( len m ) ;
\ Layer 3 and 4 shacham
: (tcpdone) ( m ip -- ) drop m_freem ;
: tcpxmt ( m dst -- ) tcpstat tcpstats>tcps_sndtotal inc IPTCP ip_output ;
: tcpsnd ( tcpcb m -- ) dup ip.size m_adj swap tcpcb>daddr @ tcpxmt ;
: tcpbuild ( len -- m ) ether.size ip.size + + m_get dup ether.size m_adj ;
: tcpdlen ( ip tcphdr -- dlen )
tcphdr>th_off c@ 2 rshift swap ip>ip_len w@ ntohs ip.size - swap - ;
: tcp_add_opt_noop ( opt -- opt' ) dup 1 swap c! 1+ ;
: tcp_add_opt_mss ( tcpcb opt -- tcpcb opt' )
2 over c! 1+ 4 over c! 1+ over tcpcb>t_maxseg w@ over htons! 2 + ;
: tcp_add_opt_ws ( opt -- opt' ) tcp_add_opt_noop
3 over c! 1+ 3 over c! 1+ 0 over c! 1+ ;
: tcp_add_opt_ts ( tcpcb opt -- tcpcb opt' )
tcp_add_opt_noop tcp_add_opt_noop 8 over c! 1+ 10 over c! 1+
tcp_now @ hton over ! 4 + over tcpcb>ts_recent @ hton over ! 4 + ;
: tcp_build_syn_opt ( tcpcb opt -- ) tcp_add_opt_mss tcp_add_opt_ws
tcp_add_opt_ts 2drop ;
\ Layer 3 and 4 shacham
: >sndmax ( tcpcb -- ) dup >r tcpcb>snd_nxt @ ( nxt )
r@ tcpcb>snd_max @ over ( nxt max nxt )
seq_lt if r> tcpcb>snd_max ! else r> 2drop then ;
: >sndnxt ( len tcpcb -- ) tuck tcpcb>snd_nxt +! >sndmax ;
: tcpheader ( tcplen tcpcb m bool -- ) swap m_data >r ( tcplen tcpcb bool )
if dup tcpcb>snd_nxt @ hton r@ tcpiphdr>ti_seq ! then ( tcplen tcpcb )
IPTCP r@ tcpiphdr>ti_pr c! ( tcplen tcpcb )
over r@ tcpiphdr>ti_len htons! ( tcplen tcpcb )
iphst iphost>addr @ htonl r@ tcpiphdr>ti_src ! ( tcplen tcpcb )
dup tcpcb>daddr @ htonl r@ tcpiphdr>ti_dst !
dup tcpcb>sport w@ r@ tcpiphdr>ti_sport htons!
dup tcpcb>dport w@ r@ tcpiphdr>ti_dport htons!
dup tcpcb>rcv_nxt @ hton r@ tcpiphdr>ti_ack ! ( tcplen tcpcb )
r@ tcpiphdr>ti_flags c@ TH_ACK or r@ tcpiphdr>ti_flags c!
tcpcb>snd_wnd @ r@ tcpiphdr>ti_win htons!
ip.size + r@ swap in_cksum r> tcpiphdr>ti_sum w! ;
\ Layer 3 and 4 shacham
: tcpctl ( tcpcb seq flags -- ) rot >r ( seq flags )
tcphdr.size r@ tcpcb>optln c@ + tcpbuild ( seq flags m )
dup m_data ( seq flags m tcpip )
rot over tcpiphdr>ti_flags c! ( seq m tcpip )
rot over tcpiphdr>ti_seq ! ( m tcpip )
r@ tcpcb>optln c@ tcphdr.size + 2 lshift over tcpiphdr>ti_off c!
r@ tcpcb>optln c@ 0= if drop else
tcpiphdr.size + tcp_add_opt_ts drop then ( m )
tcphdr.size r@ tcpcb>optln c@ + over r@ swap false tcpheader ( m )
r> swap tcpsnd ;
\ Layer 3 and 4 shacham
defer rxmt_tstart defer delackclr defer delsndclr
: tcp_send_syn_ack ( tcpcb -- )
dup tcpcb>synoptln c@ tcphdr.size + tcpbuild >r ( tcpcb )
r@ m_data ( tcpcb tcpip )
TH_SYN over tcpiphdr>ti_flags c! ( tcpcb tcpip )
over tcpcb>synoptln c@ tcphdr.size + 2 lshift over tcpiphdr>ti_off c!
over tcpcb>synoptln c@ 0= if drop else
over swap tcpiphdr.size + tcp_build_syn_opt then ( tcpcb )
dup tcpcb>synoptln c@ tcphdr.size + over r@ true tcpheader
tcpstat tcpstats>tcps_sndctrl inc
1 over >sndnxt dup rxmt_tstart
r> tcpsnd ;
\ Layer 3 and 4 shacham
: tcp_send_ack ( tcpcb -- )
dup delackclr dup tcpcb>snd_nxt @ hton 0 ( tcpcb seq flags ) tcpctl
tcpstat tcpstats>tcps_sndacks inc ;
: tcp_send_fin ( tcpcb -- )
dup tcpcb>snd_nxt @ hton TH_FIN tcpctl
tcpstat tcpstats>tcps_sndctrl inc ;
: tcp_send_keepalive ( tcpcb -- )
dup tcpcb>snd_una @ 1- hton 0 tcpctl
tcpstat tcpstats>tcps_keepprobe inc ;
: rst>ctl ( m-in tcpip-in tcpip-out -- tcpip-in tcpip-out )
>r ( m-in tcpip-in ) swap m_data ( tcpip-in tcp-in )
2dup tcpdlen ( tcpip-in tcp-in dlen ) nip ( tcpip-in dlen )
over tcpiphdr>ti_seq @ + r@ tcpiphdr>ti_ack ! ( tcpip-in )
r> ( tcpip-in tcpip-out ) ;
\ Layer 3 and 4 shacham
: tcp_send_rst ( m tcpip-in -- )
tcphdr.size tcpbuild >r r@ m_data ( m tcpip-in tcpip-out )
over tcpiphdr>ti_flags c@ TH_ACK and 0= if
dup tcpcb>snd_una @ 1- hton 0 tcpctl
0 over tcpiphdr>ti_seq ! rst>ctl
TH_RST TH_ACK + over tcpiphdr>ti_flags c! ( tcpip-in tcpip-out )
else rot drop
TH_RST over tcpiphdr>ti_flags c!
over tcpiphdr>ti_ack @ over tcpiphdr>ti_seq ! then
tcphdr.size 2 lshift over tcpiphdr>ti_off c! ( tcpip-in tcpip-out )
IPTCP over tcpiphdr>ti_pr c!
tcphdr.size over tcpiphdr>ti_len htons!
iphst iphost>addr @ htonl over tcpiphdr>ti_src !
over tcpiphdr>ti_src @ over tcpiphdr>ti_dst !
over tcpiphdr>ti_sport w@ over tcpiphdr>ti_dport w!
over tcpiphdr>ti_dport w@ over tcpiphdr>ti_sport w!
$FFFF over tcpiphdr>ti_win htons! ( tcpip-in tcpip-out )
dup ip.size tcphdr.size + in_cksum ( tcpip-in tcpip-out cksum )
swap tcpiphdr>ti_sum w! ( tcpip-in )
tcpstat tcpstats>tcps_sndctrl inc
tcpiphdr>ti_src @ ntohl r@ ip.size m_adj r> swap tcpxmt ;
\ Layer 3 and 4 shacham
defer delsndset
: >sndcnt ( tcpcb -- ) dup tcpcb>snd_cnt inc
dup tcpcb>snd_maxcnt @ over tcpcb>snd_cnt w@ max swap tcpcb>snd_maxcnt ! ;
: tcpdbuild ( dlen tcpcb -- m ) tcpcb>headers @ tuck + m_get dup rot m_adj ;
: tcp_send ( m tcpcb -- )
over m_hdr>mh_len @ >r ( m tcpcb )
2dup tcpcb>ipheaders @ negate m_adj ( m tcpcb )
over m_data over tcpcb>optln c@ tcphdr.size + ( m tcpcb hdr-len )
2 lshift swap tcpiphdr>ti_off c! ( m tcpcb )
dup tcpcb>optln c@ 0<> if
over m_data tcpiphdr.size + tcp_add_opt_ts drop then ( m tcpcb )
dup >sndcnt
2dup swap dup m_hdr>mh_len @ ip.size - rot rot true tcpheader
tcpstat tcpstats>tcps_sndpack inc
tuck tcpcb>daddr @ over ip.size m_adj tcpxmt dup rxmt_tstart
dup delackclr dup delsndclr r> swap 2dup >sndnxt so_next ;
\ Layer 3 and 4 shacham
: keeprst ( tcpcb -- ) TCP_KEEPIDLE over tcpcb>tt_keep !
TCP_MAXIDLE swap tcpcb>tt_idle ! ;
: sndlen ( tcpcb -- len ) dup so_left ( tcpcb len )
over tcpcb>t_maxseg w@ min ( tcpcb len ) swap tcpcb>rcv_wnd @ min ( len ) ;
: snd1seg ( tcpcb -- len ) >r r@ sndlen ( len )
dup if ( len ) dup r@ tcpdbuild ( len m ) r@ ( len m tcpcb )
so_data ( len m d-in ) so_get ( len m ) r> tcp_send ( len )
else r> drop ( len ) then ;
: snd_left ( tcpcb -- ) dup sndlen 0= if drop exit then
dup so_left ?dup 0> if ( tcpcb len )
dup 0 do ( tcpcb len )
over snd1seg ( tcpcb len len-sent )
+loop drop ( tcpcb )
then drop ;
\ Layer 3 and 4 shacham
: tcp_rxmt ( tcpcb -- )
dup tcpcb>t_state w@ ( tcpcb state )
dup TCPS_SYN_SENT = if 2drop exit then
dup TCPS_SYN_RECEIVED = if drop
tcpstat tcpstats>tcps_sndrexmitbyte inc
tcpstat tcpstats>tcps_sendrexmitpack inc
tcp_send_syn_ack exit then
dup TCPS_ESTABLISHED = if drop ( tcpcb )
dup tcpcb>rcv_wnd @ 0= if
tcpstat tcpstats>tcps_rcvwndzero inc drop exit then
dup tcpcb>so_snd so_cc ?dup 0> if ( tcpcb cc )
dup 0 do ( tcpcb cc )
over snd1seg ( tcpcb cc len )
dup tcpstat tcpstats>tcps_sndrexmitbyte +!
tcpstat tcpstats>tcps_sendrexmitpack inc
+loop drop ( tcpcb )
then drop exit then
2drop ( all other states, for now ) ;
\ Layer 3 and 4 shacham
: mss>maxseg ( mss tcpcb -- ) tcpcb>t_maxseg tuck w@ min swap w! ;
: optlen ( opt -- opt-len) 1+ c@ ;
: tcp_get_opt ( tcpcb opt -- opt-len ) dup c@
dup 0= if nip nip exit then \ kind=0 end of option list
dup 1 = if nip nip exit then \ kind=1 noop
dup 2 = if drop dup 2 + w@ ntohs
rot 2dup mss>maxseg tcpcb>mss w! optlen exit then
dup 3 = if drop dup 2 + c@ rot tcpcb>snd_scale ! optlen exit then
8 = if dup 2 + @ ntoh rot tcpcb>ts_recent ! optlen exit then
nip optlen ; \ unknow options - ignore
: tcp_opts ( tcphdr len tcpcb -- ) rot tcphdr.size + rot over + swap do
dup i tcp_get_opt ?dup 0= if unloop exit then +loop drop ;
\ Layer 3 and 4 shacham
: tcpcbget ( -- tcpcb | false ) tcpcbs dup #TCPCB cells + swap do
i @ 0= if tcpcb.size bkalloc dup i ! unloop exit then
1 cells +loop false ;
: tcpcbfree ( tcpcb -- ) tcpcbs dup #TCPCB cells + swap do
dup i @ = if dup tcpcb>so_snd sockbuf>sb_buf @ bkfree bkfree
0 i ! unloop exit then 1 cells +loop drop ;
: tcpsock>cb ( tcpiphdr -- tcpcb | false )
dup tcpiphdr>ti_sport w@ ntohs swap tcpiphdr>ti_src @ ntoh ( sport src )
tcpcbs dup #TCPCB cells + swap do ( sport src )
i @ 0<> if 2dup ( sport src sport src )
i @ tcpcb>daddr @ = if ( sport src sport )
i @ tcpcb>dport w@ = if 2drop i @ unloop exit then
else drop then ( sport src )
then 1 cells +loop 2drop false ;
: tcpsock>lsn ( tcpiphdr -- tcpcb | false )
tcpiphdr>ti_dport w@ ntohs ( dport )
tcpcbs dup #TCPCB cells + swap do
i @ 0<> if i @ tcpcb>t_state @ TCPS_LISTEN = if
i @ tcpcb>sport w@ over = if drop i @ unloop exit then
then then 1 cells +loop drop false ;
\ Layer 3 and 4 shacham
: rxmt_treset ( tcpcb -- ) 12 over tcpcb>t_rxtcur w!
0 over tcpcb>tt_rxmt w! 0 swap tcpcb>t_rxtshift w! ;
: tcpt_rangeset ( tcpcb -- ) dup tcpcb>t_rxtcur w@ 1 lshift
TCPTV_REXMTMAX min swap 2dup tcpcb>t_rxtcur w! tcpcb>tt_rxmt w! ;
: more2ack? ( tcpcb -- bool ) dup tcpcb>snd_una @ swap tcpcb>snd_nxt @ <> ;
: cur>txmt ( tcpcb -- ) dup tcpcb>t_rxtcur w@ swap tcpcb>tt_rxmt w! ;
: rxmt_tset ( tcpcb -- ) dup more2ack? if dup tcpcb>t_rxtcur w@
swap tcpcb>tt_rxmt w! else rxmt_treset then ;
:noname ( tcpcb -- ) dup more2ack? if dup tcpcb>tt_rxmt w@ 0= if
dup cur>txmt then then drop ; is rxmt_tstart
\ Layer 3 and 4 shacham
: badack? ( ack tcpcb -- bool )
2dup tcpcb>snd_una @ = if 2drop false exit then
2dup tcpcb>snd_una @ seq_gt if ( ack tcpcb )
2dup tcpcb>snd_nxt @ seq_leq if ( ack tcpcb )
tcpstat tcpstats>tcps_rcvackpack inc 0 over tcpcb>snd_cnt w!
2dup so_ack ( ack tcpcb ) tuck tcpcb>snd_una ! ( tcpcb )
rxmt_tset ( )
false exit else tcpstats>tcps_rcvacktoomuch inc
( ack tcpcb ) nip tcp_send_ack true exit then
else 2drop true then ;
: badsynack? ( ack tcpcb -- bool )
2dup tcpcb>snd_una @ seq_lt if 2drop true exit then
tcpcb>snd_nxt @ seq_gt if true else false then ;
\ Layer 3 and 4 shacham
: rxmtinit ( tcpcb -- ) dup tcpcb>snd_una @ over tcpcb>snd_nxt ! so_rxmt ;
: >maxcwnd ( tcpcb -- ) dup tcpcb>snd_maxcwnd @ over tcpcb>snd_cwnd @ max
swap tcpcb>snd_maxcwnd ! ;
: dup>cwnd ( tcpcb -- ) dup tcpcb>t_dupacks w@ over tcpcb>snd_cwnd @ max
over tcpcb>snd_cwnd ! 0 over tcpcb>t_dupacks w! >maxcwnd ;
: dupack? ( ip tcp tcpcb -- bool ) >r ( ip tcp )
dup tcphdr>th_ack @ ntoh r@ tcpcb>snd_una @ = if ( ip tcp )
tcpdlen 0= if ( ) ( TODO check for rcv_wnd change )
tcpstat tcpstats>tcps_rcvdupack inc
r@ more2ack? if r@ tcpcb>t_dupacks inc
r@ tcpcb>t_dupacks w@ #TCPRXMT > if r> drop false exit then
r@ tcpcb>t_dupacks w@ #TCPRXMT 5 within if
tcpstat tcpstats>tcps_3dupack inc
r@ rxmtinit 0 r@ tcpcb>tt_rxmt w!
r> tcp_rxmt else r> drop then true exit
then then else 2drop then r> dup>cwnd false ;
\ Layer 3 and 4 shacham
: upsndwnd? ( tcphdr tcpcb -- bool ) >r
r@ tcpcb>snd_wl1 @ over tcphdr>th_seq @ ntoh 2dup seq_lt if
2drop r> 2drop true exit then
= if r@ tcpcb>snd_wl2 @ over tcphdr>th_ack @ ntoh seq_leq if
r> 2drop true exit then then r> 2drop false ;
: upsndwnd ( tcphdr tcpcb -- ) 2dup upsndwnd? if >r
dup tcphdr>th_win w@ ntohs r@ tcpcb>snd_wnd !
dup tcphdr>th_seq @ ntoh r@ tcpcb>snd_wl1 !
tcphdr>th_ack @ ntoh r> tcpcb>snd_wl2 ! else 2drop then ;
\ Layer 3 and 4 shacham
: badseq? ( tcphdr tcpcb -- bool ) tuck ( tcpcb tcphdr tcpcb )
tcpcb>rcv_nxt @ swap tcphdr>th_seq @ ntoh <> if ( tcpcb )
tcp_send_ack tcpstat tcpstats>tcps_rcvseqnotnxt inc true
else drop false then ;
: (len?) ( tcphdr tcpcb len -- bool ) swap >r ( tcphdr len )
swap tcphdr>th_seq @ ntoh over + tuck ( seq+len len seq+len )
r@ tcpcb>rcv_nxt @ r@ tcpcb>rcv_wnd + ( seq+len len seq+len nxt+wnd )
seq_gt if ( seq+len len )
2drop r> drop tcpstat tcpstats>tcps_rcvbyteafterwin inc false
else
tcpstat tcpstats>tcps_rcvbyte +! r> tcpcb>rcv_nxt ! true
then ;
: len? ( ip tcphdr tcpcb -- bool )
>r tuck tcpdlen r> swap ?dup 0= if 2drop false else (len?) then ;
\ Layer 3 and 4 shacham
defer tcp_state defer tcp_accept defer delackset
:noname ( m ip -- ) tcpstat tcpstats>tcps_rcvtotal inc
dup ip>ip_len w@ ntohs ip.size - \ tcp len
>r over r> swap m_hdr.size + m_pkthdr>csum_flags @ $C00 and $C00 <> if
over ipovly>ih_xl @ >r \ keep org ip header fields
over >r dup 0 r@ ipovly>ih_xl c! r> ipovly>ih_len htons!
over ipovly>ih_xl swap ip.size + 8 - in_cksum
over ipovly>ih_xl r> swap ! \ restore ip header
else drop 0 then
0<> if drop m_freem 1 tcpstat tcpstats>tcps_rcvbadsum exit then
over m_data tcphdr>th_flags c@ TH_FLAGS and TH_SYN = if tcp_accept else
tcp_state then ; is tcp_input
: tcpclrt ( tcpcb -- ) 0 over tcpcb>tt_rxmt w! 0 over tcpcb>tt_persist !
0 over tcpcb>tt_keep ! 0 over tcpcb>tt_idle !
0 over tcpcb>tt_2msl ! dup delackclr delsndclr ;
: >tcpidle ( tcpcb state -- ) over tcpcb>t_state w! 0 over tcpcb>daddr !
0 over tcpcb>dport w! 0 over tcpcb>snd_nxt ! 0 over tcpcb>snd_una !
0 over tcpcb>snd_cwnd ! 0 over tcpcb>t_dupacks w!
0 over tcpcb>rcv_nxt ! 0 over tcpcb>rcv_wnd ! tcpclrt ;
\ Layer 3 and 4 shacham
:noname ( m ip -- )
dup tcpsock>cb ?dup 0= if
dup tcpiphdr>ti_flags c@ TH_RST and 0= if 2dup tcp_send_rst then
(tcpdone) exit then
( m ip tcpcb )
>r r@ keeprst r@ tcpcb>t_state w@ ( m ip state )
\ Layer 3 and 4 shacham
dup TCPS_SYN_SENT = if r> 2drop (tcpdone) exit then
\ Layer 3 and 4 shacham
dup TCPS_SYN_RECEIVED = if drop over m_data ( m ip tcp )
dup r@ badseq? if r> 2drop (tcpdone) exit then
dup r@ upsndwnd ( m ip tcp )
dup tcphdr>th_flags c@ TH_RST and 0<> if ( m ip tcp )
r> TCPS_LISTEN >tcpidle drop (tcpdone) exit then
dup tcphdr>th_flags c@ TH_SYN and 0<> if ( m ip tcp )
drop 2dup tcp_send_rst ( m ip )
r> TCPS_LISTEN >tcpidle (tcpdone) exit then
dup tcphdr>th_flags c@ TH_ACK and 0= if r> 2drop (tcpdone) exit then
dup tcphdr>th_ack @ ntoh r@ badack? if r> 2drop ( m ip )
2dup tcp_send_rst (tcpdone) exit then
r@ API_ACCEPT over tcpcb>control @ ?dup if execute else 2drop then
TCPS_ESTABLISHED r@ tcpcb>t_state w! tcpstat tcpstats>tcps_connects inc
dup tcphdr>th_win w@ ntohs r@ tcpcb>rcv_wnd ! ( m ip tcp )
dup tcphdr>th_off c@ 2 rshift tcphdr.size - ?dup if
over swap r@ tcp_opts then ( m ip tcp )
nip dup tcphdr>th_flags c@ TH_FIN and 0<> if ( m tcp )
tcphdr>th_seq @ ntoh 1+ r@ tcpcb>rcv_nxt ! m_freem ( )
r@ tcp_send_ack TCPS_CLOSE_WAIT r@ tcpcb>t_state w! ( )
r> API_CLOSE over tcpcb>control @ ?dup if execute else 2drop then
else r> drop (tcpdone) then ( )
exit then
\ Layer 3 and 4 shacham
dup TCPS_ESTABLISHED = if drop over m_data ( m ip tcp )
dup r@ badseq? if r> 2drop (tcpdone) exit then
dup r@ upsndwnd ( m ip tcp )
dup tcphdr>th_flags c@ TH_RST TH_SYN or and 0<> if ( m ip tcp )
r@ TCPS_CLOSED >tcpidle ( m ip tcp )
r> API_ABORT over tcpcb>control @ ?dup if execute else 2drop then
drop (tcpdone) exit then
dup tcphdr>th_flags c@ TH_ACK and 0= if r> 2drop (tcpdone) exit then
2dup r@ dupack? if r> 2drop (tcpdone) exit then
dup tcphdr>th_ack @ ntoh r@ badack? if r> 2drop (tcpdone) exit then
dup tcphdr>th_win w@ ntohs r@ tcpcb>rcv_wnd ! ( m ip tcp )
dup tcphdr>th_off c@ 2 rshift tcphdr.size - ?dup if
over swap r@ tcp_opts then ( m ip tcp )
tuck r@ ( m tcp ip tcp tcpcb ) len? if ( m tcp )
r@ delackset 2dup tcphdr>th_off c@ 2 rshift m_adj over ( m tcp m )
r@ dup tcpcb>receive @ ?dup if execute else 2drop then then
dup tcphdr>th_flags c@ TH_FIN and 0<> if ( m tcp )
tcphdr>th_seq @ ntoh 1+ r@ tcpcb>rcv_nxt ! m_freem ( )
r@ tcp_send_ack TCPS_CLOSE_WAIT r@ tcpcb>t_state w! ( )
r> API_CLOSE over tcpcb>control @ ?dup if execute else 2drop then
else r> drop (tcpdone) then ( )
exit then
\ Layer 3 and 4 shacham
dup TCPS_CLOSE_WAIT = if r> 2drop (tcpdone) exit then
\ Layer 3 and 4 shacham
dup TCPS_FIN_WAIT_1 = if r> 2drop (tcpdone) exit then
\ Layer 3 and 4 shacham
dup TCPS_CLOSING = if r> 2drop (tcpdone) exit then
\ Layer 3 and 4 shacham
dup TCPS_LAST_ACK = if drop over m_data ( m ip tcp )
dup r@ badseq? if r> 2drop (tcpdone) exit then
dup r@ upsndwnd ( m ip tcp )
dup tcphdr>th_flags c@ TH_RST TH_SYN or and 0<> if ( m ip tcp )
r@ TCPS_CLOSED >tcpidle ( m ip tcp )
r> API_ABORT over tcpcb>control @ ?dup if execute else 2drop then
drop (tcpdone) exit then
dup tcphdr>th_flags c@ TH_ACK and 0= if r> 2drop (tcpdone) exit then
r@ TCPS_CLOSED >tcpidle ( m ip tcp )
r> API_CLOSED over tcpcb>control @ ?dup if execute else 2drop then
drop (tcpdone) tcpstat tcpstats>tcps_closed inc
exit then
\ Layer 3 and 4 shacham
dup TCPS_FIN_WAIT_2 = if r> 2drop (tcpdone) exit then
\ Layer 3 and 4 shacham
TCPS_TIME_WAIT = if r> 2drop (tcpdone) exit then
r> drop (tcpdone) ( unknown states should never happen ) ; is tcp_state
\ Layer 3 and 4 shacham
create ip_ok $C0A80000 , 0 , $FFFFFF00 , 0 ,
$40A11610 , 0 , $FFFFFFF8 , 0 ,
$AB404E00 , 0 , $FFFFFF00 , 0 , \ Hovi
$CFFE6400 , 0 , $FFFFFF00 , 0 , \ vandys cafe
$3FF14100 , 0 , $FFFFFF00 , 0 , \ vandys t-mobile
0 , 0 , 0 , 0 , \ must be last line
: ipfw? ( ip -- bool ) ip>ip_src @ ntoh >r ip_ok
begin dup iphost>addr @ ?dup 0= if r> 2drop false exit then
over iphost>mask @ r@ and = if r> 2drop true exit then
iphost.size + again ;
:noname ( m ip -- )
dup ipfw? not if (tcpdone) tcpstat tcpstats>tcps_connrej inc exit then
dup tcpsock>lsn ?dup 0= if (tcpdone) exit then >r
r@ dup tcpcb>iss @ tuck over tcpcb>snd_nxt ! tcpcb>snd_una !
ip>ip_src @ ntoh r@ tcpcb>daddr ! dup ( m m )
m_data dup tcphdr>th_sport w@ ntohs r@ tcpcb>dport w!
dup tcphdr>th_seq @ ntoh dup r@ tcpcb>irs ! 1+ r@ tcpcb>rcv_nxt !
dup tcphdr>th_win w@ ntohs r@ tcpcb>rcv_wnd !
dup tcphdr>th_off c@ 2 rshift tcphdr.size - ?dup if r@ tcp_opts then
r@ tcp_send_syn_ack
m_freem TCPS_SYN_RECEIVED r> tcpcb>t_state w!
tcpstat tcpstats>tcps_accepts inc ; is tcp_accept
\ Layer 3 and 4 shacham
: tcp_init ( ctrl rcv -- tcpcb | false )
tcpcbget ?dup 0= if 2drop false exit then >r
r@ tcpcb.size erase $FFFF r@ tcpcb>snd_wnd ! r@ tcpcb>iss >iss
#MSS r@ tcpcb>t_maxseg w! r@ tcpcb>receive ! r@ tcpcb>control !
tcpiphdr.size dup r@ tcpcb>ipheaders !
$FFFC bkalloc r@ tcpcb>so_snd tuck sockbuf>sb_buf ! \ bkalloc adds a cell
so_empty r@ rxmt_treset
ether.size + r@ tcpcb>headers ! r> ;
: tcp_listen ( tcpcb port -- ) swap >r dup r@ tcpcb>sport w!
PORT_TELNET = if TELNET_OPTLEN dup r@ tcpcb>optln c!
dup r@ tcpcb>headers +! r@ tcpcb>ipheaders +!
r@ tcpcb>t_maxseg w@ TELNET_OPTLEN - r@ tcpcb>t_maxseg w!
TELNET_SYNOPTLEN r@ tcpcb>synoptln c! then
TCPS_LISTEN r> tcpcb>t_state w! ;
: tcp_close ( tcpcb -- ) TCPS_LAST_ACK over tcpcb>t_state w! tcp_send_fin ;
: tcp_send_buf ( data dlen tcpcb -- bool ) >r r@ so_add dup if r> drop then
r@ tcpcb>t_flags w@ TCP_NODELAY and 0<> if r> snd_left drop else
r> delsndset then ;
: tcp_done ( tcpcb -- ) tcpcbfree ;
\ Layer 3 and 4 shacham
create tsc200m 0 , 0 , create tsc500m 0 , 0 , create tcp500m 0 , 0 ,
create tsc8m 0 , 0 ,
: (200m) ( -- ) tsc1sec 2@ 5 um/mod nip 0 tsc200m 2! ;
: (500m) ( -- ) tsc1sec 2@ 2 um/mod nip 0 tsc500m 2! ;
: (8m) ( -- ) tsc1sec 2@ 125 um/mod nip 0 tsc8m 2! ;
:noname ( tcpcb -- ) 0 0 rot tcpcb>tt_delack 2! ; is delackclr
:noname ( tcpcb -- ) dup tcpcb>tt_delack 2@ or 0= if
rdtsc tsc200m 2@ d+ rot tcpcb>tt_delack 2! else drop then ; is delackset
:noname ( tcpcb -- ) 0 0 rot tcpcb>tt_delsnd 2! ; is delsndclr
:noname ( tcpcb -- ) dup tcpcb>tt_delsnd 2@ or 0= if
rdtsc tsc8m 2@ d+ rot tcpcb>tt_delsnd 2! else drop then ; is delsndset
: (tcp_drop) ( tcpcb -- ) tcpstat tcpstats>tcps_keepdrops inc
dup TCPS_CLOSED >tcpidle
API_ABORT over tcpcb>control @ ?dup if execute else 2drop then ;
\ Layer 3 and 4 shacham
: tcp_fasttimo ( -- ) tcpcbs dup #TCPCB cells + swap do
i @ 0<> if
i @ tcpcb>tt_delsnd 2@ or 0<> if
i @ tcpcb>tt_delsnd 2@ rdtsc d< if
i @ snd_left 0> if tcpstat tcpstats>tcps_delsnd inc then
then then
i @ tcpcb>tt_delack 2@ or 0<> if
i @ tcpcb>tt_delack 2@ rdtsc d< if
tcpstat tcpstats>tcps_delack inc i @ tcp_send_ack then then
then
1 cells +loop ;
\ Layer 3 and 4 shacham
: tcp_slowtimo ( -- ) tsc500m 2@ tcp500m 2@ dnegate rdtsc d+ d< if
tcp_now inc
tcpcbs dup #TCPCB cells + swap do
i @ 0<> if
i @ tcpcb>tt_rxmt w@ ?dup 0> if
-1 + ?dup 0= if tcpstat tcpstats>tcps_rexmttimeo inc
i @ tcpcb>t_rxtshift inc i @ tcpt_rangeset
i @ rxmtinit i @ tcp_rxmt
else i @ tcpcb>tt_rxmt w! then then
i @ tcpcb>tt_keep @ ?dup 0> if
-1 + ?dup 0= if tcpstat tcpstats>tcps_keeptimeo inc
TCPS_ESTABLISHED i @ tcpcb>t_state w@ > if i @ (tcp_drop) else
TCPS_FIN_WAIT_1 i @ tcpcb>t_state w@ > if
i @ tcpcb>tt_idle @ 1- ?dup 0= if i @ (tcp_drop) else
i @ tcpcb>tt_idle ! TCP_KEEPINTVL i @ tcpcb>tt_keep !
i @ tcp_send_keepalive then
else TCP_KEEPIDLE i @ tcpcb>tt_keep ! then
then
else i @ tcpcb>tt_keep ! then
then then
1 cells +loop
tsc500m 2@ tcp500m 2@ d+ tcp500m 2! \ avoid timer drift
then ;
\ Layer 3 and 4 shacham
\ telnetd
struct tcmd int8 iac int8 command int8 id endstruct
251 constant WILL 252 constant WONT 253 constant DO
254 constant DONT 255 constant IAC
variable telnetdcb variable telnetd_rcvd
create telnetdbuf 256 allot
\ telnet init message - will suppress go ahead, will echo, \r\n
create telnetd_msg IAC c, WILL c, 3 c, IAC c, WILL c, 1 c, 13 c, 10 c,
8 constant #TELNETDMSG
defer telnetd-init
: telnetd_ctrl ( tcpcb event -- )
dup API_ACCEPT = if drop ( tcpcb )
telnetd_msg #TELNETDMSG rot tcp_send_buf drop exit then
dup API_CLOSE = if drop tcp_close exit then
dup API_CLOSED = if drop tcp_done 0 telnetdcb ! telnetd-init exit then
API_ABORT = if tcp_done 0 telnetdcb ! telnetd-init exit then
drop ;
\ Layer 3 and 4 shacham
: tcmd? ( tcmd -- bool ) tcmd>iac c@ IAC = ;
: tcmdid@ ( tcmd -- id ) tcmd>id c@ ;
: tcmdc@ ( tcmd -- command ) tcmd>command c@ ;
: tcmdget ( d-out d-in -- len )
dup tcmdid@ 3 > if >r \ ignoe our options 'echo' and 'suppres go ahead'
IAC over tcmd>iac c! r@ tcmdid@ over tcmd>id c!
r@ tcmdc@ DO = if WONT over tcmd>command c! then
r@ tcmdc@ WILL = if DONT over tcmd>command c! then
r> 3 else 0 then nip nip ;
defer >t_buf
: telnetd_rcv ( mbuf tcpcb -- ) telnetd_rcvd inc ( m-in tcpcb )
telnetdbuf rot ( tcpcb d-out m-in )
dup m_hdr>mh_len @ swap m_data dup rot + swap do ( tcpcb d-out )
i tcmd? if dup i tcmdget + 3
else i c@ ?dup 0<> if >t_buf then 1 then
+loop
telnetdbuf - ( tcpcb len ) ?dup 0= if drop else ( tcpcb len )
telnetdbuf swap rot ( d-out len tcpcb ) tcp_send_buf drop then ;
\ Layer 3 and 4 shacham
:noname ( -- ) ['] telnetd_ctrl ['] telnetd_rcv tcp_init ?dup 0<> if
dup telnetdcb ! PORT_TELNET tcp_listen then ; is telnetd-init
: telnetd_sendc ( c -- bool ) telnetdbuf tuck c! 1 telnetdcb @ tcp_send_buf ;
\ Layer 3 and 4 shacham
: l3l4-init ( -- )
calc_tscfreq \ TODO part of generic os, right?
(60sec) (200m) (500m) (8m) (bcstinit)
arptab arpentry.size #ARPS * erase rdtsc arp1min 2!
ipst stats.size erase
arpst stats.size erase
icmpst stats.size erase
pings PINGS pingcb.size * erase
udpst stats.size erase
tcpcbs #TCPCB cells erase
tcpstat tcpstats.size erase 0 tcp_now !
telnetd-init ;
: net-show ( -- )
xlst xlstats.size dump
enst enstats.size dump
ipst stats.size dump
arpst stats.size dump
icmpst stats.size dump
udpst stats.size dump
mstat m_st.size dump ;
\ Layer 3 and 4 shacham
128 constant TELBUF
create t_typing TELBUF allot variable t_ntyped
: t_typing_deq ( -- c ) t_typing c@ ( c )
t_ntyped @ 1- dup t_ntyped ! ( c u )
?dup if t_typing dup 1+ swap rot move then ;
:noname ( c -- )
t_ntyped @ dup TELBUF = if 2drop else ( c u )
t_typing + c! t_ntyped inc then ; is >t_buf
\ Layer 3 and 4 shacham
\ Note bulk put only works for system standard console geometry (80x25)
: esc ( -- ) 27 emit ;
: ansi_put_scr ( a -- ) CONS_ROWS 0 do
esc [char] [ emit i 1+ 1 u.r ." ;1H"
esc ." [K" dup CONS_COLS -trailing type
CONS_COLS + loop drop ;
: ansi_cons_op ( ... op -- ... op F | T )
dup 3 = if drop
esc ." [m" esc ." [7h" esc ." [2J" esc ." [H"
true exit then
dup 4 = if drop swap
esc [char] [ emit 1+ 1 u.r [char] ; emit
1+ 1 u.r [char] H emit true exit then
dup 5 = if drop esc ." [K" true exit then
dup 6 = if drop esc [char] [ emit
if [char] 1 emit then [char] m emit true exit then
dup 8 = if drop ansi_put_scr true exit then
false ;
\ Layer 3 and 4 shacham
: t_cons_op ( op -- ... ) pause dup 2 = if drop
t_ntyped @ if t_typing_deq true else false then
exit then
dup 1 = if drop begin dup telnetd_sendc if pause else
drop exit then repeat
ansi_cons_op if exit then
1 abort" Bad t_cons_op" ;
\ Layer 3 and 4 shacham
variable _signal 0 ,
: >signal ( bool -- ) _signal ! ;
: signal? ( -- bool ) _signal @ ;
: net-init ( -- ) pci-en-init l3l4-init xlattach xlinit ;
: (netrun) ( -- ) xltimer arptimer tcp_slowtimo tcp_fasttimo ;
: netfg ( -- ) begin (netrun) pause signal? if exit then again ;
: netproc ( -- ) begin (netrun) pause again ;
: >'ttyops ( a-user -- a-'ttyops ) [ 'ttyops up @ - ] literal + ;
: net-start ( -- ) net-init
fork ?dup 0= if netproc then setrun
fork ?dup 0= if quit then
['] t_cons_op over >'ttyops ! 0 over >ttchan ! setrun ;
only
A single set of <addr, gw, mask> is supported per host.
shacham
shacham
: arpget Get an mbuf for arp packet.
: arpbuild Build the common part of ARP packet: Always Ethernet and IP.
: arptab$ End of arp table.
: arpha Locate a protocol address in ARP cache.
TODO Faster cache access, i.e. hash?
: arpupdt Update the hardware address on an existing <pa, ha> entry
in ARP cache.
: >arptab Add <pa, ha> entry to ARP cache in first unused location.
TODO Faster access, i.e. hash?
shacham
: arpreq Build an ARP request. Input protocol address is in host order.
: arpresp Build an ARP response. Again, convert input protocol address
to network order.
: (arpbad) Cleanup for bad ARP scenario.
: arpinput Handle input ARP packet - Verify validity, even if optional per
RFC 826. Follow RFC 826 algorithm to the letter.
: arprslv Locate an protocol address in arp table, and trigger arpreq
if not found.
shacham
: (60sec) A crude calculation of 1 minute in cpu cycles.
: arptimer arp timer
Scan arp table every minute. (*BSD scans every 5 min.)
Send arpreq once watchdog is under 5 minute, trying to follow RFC-826
simplistic approach. (*BSD has some more complicated retry mechanism.)
Avoid time drifting by setting the next threshold per the current reading.
: delay Wait u msecs with pause.
TODO - Is waiting usec units, a la *BSD, needed?
TODO - Generic service (?)
shacham
: (bcstinit) Derive broadcast address from <host ip address, netmask>
: in_cksum Per Steven's book - incoming ip header checksum is 0, 16-bit calc.
Assumptions - Input buffer is contiguous in memory.
Note - 1's complement is " negate 1 - " but " not " seems identical.
Note - 32-bit calculates FFFE, not FFFF as expected.
: in_broadcast Look for INADDR_ANUCAST, INADDR_BROADCAST, or local broadcast.
: rtalloc Trivial IP routing, based on a single set of <gateway, netmask>
per host.
shacham
: (ipbad) Common handling of bad IP scenario.
: (ipid) Maintain a global IP id, incremented by 1 per outgoing packet.
TODO more secure id.
: ipbuild Build an IP header.
Note: Clear cksum field first, as TCP and UDP use the field.
tos and flags (DF) tcp.
Set ip>ip_off to "don't fragment"
TODO ttl per session, incoming packet (?)
: ip_output Add IP header and send via Ethernet.
Assume mbuf has enough room for both IP and ethernet headers.
: ipinput Handle incoming IP packets, v4 only.
Save in_cksum cycles if NIC already checked validity of IP header.
Multiplex packet to higher layer, one of ICPM, TCP, and UDP.
shacham
: icmpxmt Send ICMP packet over IP.
: icmpbuild Very generic icmp buildup, mostly mbuf related.
: icmpreply Build an ICMP reply from an input packet.
: icmpinput Handle ECHO_REPLY.
TODO other ICMP types?
shacham
PINGS Max simultaneous ping sessions. (Sure, could be more flexible
by malloc-ing session cb and maintaining a linked-list, but why?)
PINGMAX Max count.
: pings$ End of pings table.
: pinginit Init a ping control-block, if available.
: pingdone Clear leftovers once ping session is done.
: pingi>t Locate i-th pingt.
: ping>cb Locate ping control-block from an icmp packet.
: icmprpld Handle a reply for our ping.
: pingsend Send i-th ICMP echo packet.
Use LS 16-bit of pingcb as icmp id, enabling multiple sessions between
the same hosts.
shacham
: ping Ping a given destination.
A trivial PING application with trivial user display.
shacham
struct ipovly Usage of IP header.
struct udphdr UDP header from RFC-768.
struct udpiphdr UDP/IP header, with pointers.
shacham
: (udpbad) Handle common bad UDP scenario.
: udpinput Handle incoming UDP packets.
For all packets, ip len (minus ip header) must equal udp header.
Compute cksum per RFC 768, not per *BSD and Stevens, thus keeping
the original ip header in place, while cksum-ing less 4 16-bit zeros.
Save in_cksum cycles if NIC already checked validity of UDP packet.
Note - when xl status indicates udp cksum validity, xlrx always marks
both csum_flags and sets csum_data to $FFFF, thus a shortcut is fair.
TODO For now, just verify cksum.
TODO cksum-modified ipovly fields needed by user? ipovly ptrs used?
shacham
struct tcphdr TCP header, with no options, per RFC 793
struct tcpiphdr IP/TCP header.
struct sockbuf Uses plain buffer instead of mbuf chain.
sb_tail is oldest un-ACK-ed data and new data is added starting at sb_head.
Next segment to be sent starts at sb_next.
#MSS Max Segment Size in an Ethernet packet.
TODO - What this _simple_ TCP implementation does not (nor should) do -
1. Client side, aka out-going-session, with tcp_open, tcp_send_syn,
the related state machine states, related timers, et al.
2. Handle PSH, URG flags.
3. OOB
4. RTT for rexmit.
5. persist timer (for 0 win).
6. Store out-of-order segments. (Well, even RFC-793 calls that a MAY.)
7. Use partial segments, i.e. where part is post rcv window.
8. Check security and precedence.
TODO - What couldn't be tested due to lack of OS services (I think) -
1. Spawn multiple background sessions.
shacham
struct tcpcb TCP control block:
send sequence variables, per RFC 793.
receive sequence variables, per RFC 793.
additional variables: receive variables; congestion control.
transmit timimg.
out-of-band data.
RFC 1323 variables.
shacham
struct tcpstats TCP stats, mostly per *BSD.
shacham
TH_ are TCP header flags.
TCPS_ are TCP states.
TCP retransmission - TCP_MAXRXTSHIFT, TCPTV_REXMTMAX (64 sec)
TCP keepalive - TCP_KEEPIDLE RFC-793 says 2 hours, but start lower, for now.
TCP_KEEPINTVL is 75 sec.
TCP_MAXIDLE RFC-793 defines max 10 reties, but start lower, for now.
API_ are TCP control messages to user.
tcpcbs Up to #TCPCB tcp sessions.
TODO - more flexible storage, once a generic mechanism is available.
shacham
: seq_lt TCP sequence comparison, identical to timestamp (tstmp_X)
: seq_leq and connection count (cc_X)
: seq_gt
: seq_geq
: >iss Fetch ISS from rdtsc (bit 16-47).
TODO - ~4 usec tick per machine.
shacham
: so_cc Get current char count of socket buffer.
: so_empty Set socket buffer to empty.
: so_next Move sb_next by len to next char to send.
: so_rxmt Set socket buffer for rexmit.
: so_add Add data to socket buffer.
: so_ack Remove acked data from socket buffer.
: so_data Define address of send socket data next to be sent.
: so_left Length of data not sent yet from send socket buffer.
: so_get Fetch dlen chars from send socket buffer starting at sb_next.
shacham
: (tcpdone) Some common tcp exit work.
: tcpxmt Send TCP packet over IP.
: tcpsnd Adjust packet for IP, then send.
: tcpbuild Prepare an mbuf for len size segment.
: tcpdlen Calculate TCP data length, i.e. witout TCP/IP headers.
Build supported TCP otions -
: tcp_add_opt_noop
: tcp_add_opt_mss
: tcp_add_opt_ws
: tcp_add_opt_ts
: tcp_build_syn_opt Add tcp options per Mac OS X telnet.
TODO - add options per incoming SYN packet.
shacham
: >sndmax Set snd_max.
: >sndnxt Set snd_nxt.
: tcpheader Build a generic TCP header, i.e. fields the caller didn't set.
Calculate TCP cksum, so no further content changes are due.
TODO c+! is not available, but could be nice to have.
shacham
: tcpctl Build a generic TCP control packet and send.
shacham
: tcp_send_syn_ack Build, then send, SYN+ACK packet.
shacham
: tcp_send_ack Build and send an ACK packet, with no data.
: tcp_send_fin Build and send FIN+ACK packet.
: tcp_send_keepalive Build and send a keepalive ACK packet.
: rst>ctl Build fields in RST packet per incoming data packet.
shacham
: tcp_send_rst Build RST packet per rcf 793 (pp 65) -
If ACK bit is off <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
If ACK bit is on <SEQ=SEQ.ACK><CTL=RST>
Note - as the RST packet is built from an incoming packet, no ntoX is due.
Note - as no active tcpcb is used, tcpheader isn't used.
TODO - test with ACK bit off.
shacham
: >sndcnt Handle snd_cnt for Dup ACK.
: tcpdbuild Build an mbuf for dlen data length.
: tcp_send Build and send a TCP data packet.
shacham
: keeprst Restart keepalive timers.
: sndlen Define data length of current to-be-sent segment.
: snd1seg Build and send a single data segment.
: snd_left Build and send all yet-to-be-sent data from send socket buffer.
shacham
: tcp_rxmt
TCPS_SYN_RECEIVED - rexmit SYN+ACK
TCPS_ESTABLISHED - Rexmit all of send socket buffer data content.
All other states - no-op.
shacham
: mss>maxseg Define t_maxseg.
: optlen Get TCP option length.
: tcp_get_opt Parse a TCP option.
Supported options are -
0 end of option list
1 noop
2 mss
3 scale
8 ts
: tcp_opts Parse TCP options from TCP header.
shacham
: tcpcbget Get a free slot for a tcpcb, if available, and allocate tcpcb.
: tcpcbfree Free tcpcb and the slot pointing to that tcpcb.
: tcpsock>cb Locate tcpcb for the socket (src addr and src port), if any.
: tcpsock>lsn Locate a listening tcpcb for dst port, if any.
shacham
: rxmt_treset Reset retransmission timer et al.
: tcpt_rangeset Adjust (increase) range of rexmit timer.
: more2ack? Are there more data waiting to be ACKed?
: cur>txmt Load rexmit timer with current value.
: rxmt_tset Set rexmit timer, if needed.
: rxmt_tstart Start rexmit timer, if needed.
shacham
: badack? Verify and handle received ack -
(ack == snd_una) - no new ack, but packet is ok
(ack > snd_una) and (ack <= snd_nxt) - rm acked data and move snd_una
else - mark error, send ack for unsent data, and dump packet
: badsynack? For SYN-ACK segments - verify SND.UNA <= SEG.ACK <= SND.NXT
shacham
: rxmtinit Prepare for rexmit by setting snd_nxt back to snd_una
and moving back send socket buffer indicators.
: >maxcwnd Keep high-water mark for Dup ACK count.
: dup>cwnd Keep score of snd_cwnd.
: dupack? look for repeated acks to trigger fast retransmit.
Note - trigger rexmit of _all_ socket send buffer, but only upon
receiving the 3rd duplicate ACK. This policy differs from the code
at Stevens/*BSD, where a segment is sent for each dup ACK up to snd_cwnd.
Howevr, this _simple_ policy seems to work pretty well, considering
the polling mechanism for incoming packets.
Note - Actually, rexmit is done twice, for 3rd and 4th packet, as it worked
better in some scenarios. (And didn't do any difference in others.)
Note - slow start and congestion avoidance starts _after_ first packet
loss, so will it help in telnetd scenario of multiple single char packets
to avoid sending too many packets thus avoiding overwehlming the
connection? Plus. snd_cwnd is in bytes, not segments.
The approach - NODELAY OFF for telnet.
Note - seg following rexmit is marked "previous segment lost data"
due to loss in the pipe (here, the NAT box seems like the cause)
and _not_ due to loss by the NIC.
shacham
: upsndwnd? Verify received sequence for updating send window -
(snd_wl1 < seq) or ((snd_wl1 == seq) and (snd_wl2 <- ack)) - update
else - don't update
: upsndwnd Update send window variable, if necessary -
snd_wnd = th_win
snd_wl1 = th_seq
snd_wl2 = th_ack
shacham
: badseq? Expect next received packet to be contiguous to already received
packets, for now. In other words, for now, out of order packets are dumped,
and expected to rearrive following the missing packet(s). However,
Fast Recovery is started by sending an undelayed ack, signaling a missing
packet.
: (len?) Verify received data falls within window. For now, dump packets
that ends after rcv_wnd. (Note - such event should be unlikely, as a stats
counter is dedicated for that scenario.)
: len? Verify received packets data length is within window.
shacham
: tcp_input Handle an incoming tcp packet -
Verify checksum.
Forward SYN-only packets to tcp_accept.
All other packets are handled by TCP state machine.
: tcpclrt Clear all timeout timers of a connection.
: >tcpidle Set a connection (i.e. tcpcb) to CLOSED or LISTEN
and remove all leftovers of currect connection, especially the socket.
shacham
: tcp_state TCP state machine
Segments for unknown socket - discard segment with RST bit, else send RST.
TCPS_CLOSED Handled as a generic first step of state machine,
i.e. TCPCB does not exist, thus no need for a separate case.
TCPS_LISTEN Step (1) (check for RST) and (2) (check for ACK) are handled
as part of the case for unknown socket, i.e. TCPCB doesn't exist.
Further steps are handled in tcp_accept.
First, restart keepalive timeout
shacham
TCPS_SYN_SENT
(1) Check for ACK bit
(2) Process RST bit.
(3) Check security and precedence TODO.
(4) Check for SYN bit -
If acceptable ACK - connection established.
Else - other party sent SYN, so move to SYN-RECEIVED state.
(5) If neither SYN or RST bits is set - drop segment.
shacham
TCPS_SYN_RECEIVED Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al.
(2) Process RST bit.
(3) Check security and precedence TODO.
(4) Check for SYN bit.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al.
Note - no need to check for duplicate ACK.
Move to ESTABLISHED state.
(6) Check URG bit TODO.
Note - No processing of segment text (RFC-793 doesn't mention seg text)
(7) Process FIN bit.
shacham
TCPS_ESTABLISHED Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al.
(2) Process RST bit.
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al.
Note - first look for duplicate ACK for fast recovery.
(6) Check URG bit TODO.
(7) Process segment text.
(8) Process FIN bit.
shacham
TCPS_CLOSE_WAIT Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al, as for ESTABLISHED.
(2) Process RST bit, as for ESTABLISHED
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST, and ESTABLISHED.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al.
Note - first look for duplicate ACK for fast recovery.
(6) URG bit - should not occur.
(7) Process segment text - should not occur.
(8) FIN bit - stay in CLOSE-WAIT state.
TODO - should user be notified?
shacham
TCPS_FIN_WAIT_1 Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al, same as ESTABLISHED.
(2) Process RST bit, same as ESTABLISHED.
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST, as ESTABLISHED.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al,
again, as ESTABLISHED.
If our FIN is now acknowledged then enter FIN-WAIT-2 and continue
processing in that state.
shacham
TCPS_CLOSING Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al, same as ESTABLISHED.
(2) Process RST bit - if set, enter the CLOSED state, and return.
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST, as ESTABLISHED.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al,
again, as ESTABLISHED.
If our FIN is now acknowledged then enter TIME-WAIT,
otherwise ignore the segment.
(6) Ignore URG bit.
(7) Ignore segment text.
(8) FIN bit - remain in closing state, i.e. no-op.
shacham
TCPS_LAST_ACK Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al.
(2) RST bit - enter CLOSED.
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST.
(5) Check for ACK bit - Our FIN is ACKED - enter CLOSED.
Note - RFC-793 does NOT specify verification of ACK a-la ESTABLISHED.
(6) URG bit - should not occur.
(7) Segment text - should not occur.
(8) FIN bit - RFC-793 says "stay in state" -- but if we should return
when ACK bit is ON or OFF, how could this stage be reached?
shacham
TCPS_FIN_WAIT_2 Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al, same as ESTABLISHED.
(2) Process RST bit, same as ESTABLISHED.
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST, as ESTABLISHED.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al,
again, as ESTABLISHED.
If rexmit is empty, the user's CLOSE can be acknowledged,
but do not delete the tcpcb.
(6) Check URG bit TODO.
(7) Process segment text.
(8) Process FIN bit - Enter the TIME-WAIT state. Start the timer-wait
timer, turn off the other timers TODO.
shacham
TCPS_TIME_WAIT Follow RFC-793 segment processing steps -
(1) Check SEG.SEQ and update RCV.NXT et al, same as ESTABLISHED.
(2) Process RST bit - if set, enter the CLOSED state, and return.
(3) Check security and precedence TODO.
(4) Check for SYN bit. Note - same process as for RST, as ESTABLISHED.
(5) Check for ACK bit, process SEG.ACK and update SND.UNA et al,
again, as ESTABLISHED.
If rexmit is empty, the user's CLOSE can be acknowledged,
but do not delete the tcpcb.
(6) URG bit - should not occur, ignore.
(7) Segment text should not occur, ignore.
(8) Process FIN bit - Remain in TIME-WAIT state.
Restart the 2 MSL time wait timeout TODO.
shacham
ip_ok null terminated list of <ip_addr, netmask> pairs of acceptable
incoming connections.
Note - ip_addr in table is assumed to be already and-ed with netmask.
: ipfw? A trivial firewall, looking for src-addr in ip_ok list.
: tcp_accept Accept incoming connections.
(3) Check for SYN
We reach here only if only SYN bit is set in control.
Handle security/compartment - TODO.
Set RCV.NXT to SEG.SEQ+1 and IRS to SEG.SEQ.
Select ISS and send SY-ACK segment <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
(4) Other text or control
TODO - not handled.
shacham
: tcp_init Allocate tcpcb, if available, for a new session.
Init all fields.
: tcp_listen Set a session to LISTEN.
Note - Interactive connection like telnet should NOT set TCP_NODELAY,
enabling accumulating of data before sending, thus reducing
the number of packets of the wire and avoiding congestion.
: tcp_close Close a connection.
: tcp_send_buf Load buffer into send socket then send or trigger delayed send.
: tcp_done Free a session.
shacham
: (200m) Trivial way to save few cycles every tick.
With no d2/ assuming 200m fit in 32-bit cpu cycles seems fair.
: (500m) Trivial way to save few cycles every tick.
With no d2/ assuming 500m fit in 32-bit cpu cycles seems fair.
: (8m) Trivial way to save few cycles every tick.
: delackclr Reset (clear) delayed-ACK timer.
: delackset Restart delayed-ACK timer, if not already running.
: delsndclr Reset (clear) delayed-send timer.
: delackset Restart delayed-send timer, if not already running.
: (tcp_drop) Drop a TCP connection and inform user.
shacham
: tcp_fasttimo
The 200 msec timer. Unlike the slow timer, the fast timer
does not use 200m ticks but rdtsc counter. In order to wait 200ms,
tick of 1 expires within 0-200 msec, thus on the average ack is delayed
less than the requested 200m.
Also, the delayed send timer, operating in a similar way to 200 msec timer.
shacham
: tcp_slowtimo
Increment tcp_now, TCP timer that starts at system init.
The 500 msec timer. Scans all tcpcb for active timers,
decrement and trigger rexmit and keepalive when counter is zero.
shacham
telnetd
TODO as telnet is at layer 7, move to another file?
TODO telnetd cb, for multiple sessions
struct tcmd Telnet command from RFC-854
telnetd_msg telnet init message - will suppress go ahead, will echo, \r\n
: telnetd_ctrl Handle control events from TCP.
shacham
: tcmd? Have we found a command?
: tcmdid@ Get ID from telnet command.
: tcmdc@ Get command from telnet command.
: tcmdget Fetch and decode telnet command.
: telnetd_rcv Handle incoming telnet packets.
Assumptions -
telnet commands may arrive with data ( TODO test )
IAC is not in data stream, as 7-bit US ASCII is used.
Ignore \000 in data stream, as telnet-client sends cr as \r\000 but
cli and vi need only \r
No password mode, for now.
Behavior -
Only supported telnet options are - will suppress go ahead, will echo
All other telnet commands by client are rejected.
Our options (do suppress ga, will echo) are ignored in arriving packets.
Each character is sent in a separate segment.
Note - Don't m_freem the input mbuf, as TCP continue processing.
TODO - send socket buffer overflow is ignored for control reply,
as assumed to be exchanged in session start, when socket is empty.
shacham
: telnetd-init Init a telnetd session.
: telnetd_sendc Send a single char over TCP.
shacham
: l3l4-init Init stack variables.
: net-show Show accumulated stats.
Note - Use with struct definitions to read the results.
shacham
Note - adapation from vandys' COM port term.
# chars of input we buffer from telnetd
telnetd input buffer, and current count
: t_typing_deq Pull a char off the input FIFO
Decrement count of buffered typing
Shuffle down the queue of chars if there are any more
shacham
Note: Taken as-is from vandys' util/term
TODO - Avoid said duplication.
: esc Emit an escape
: ansi_put_scr Bulk put of screen contents; not so friendly on an actual
RS-232 TTY. If we really cared, should do update optimization ala curses,
but for now just paint the screen top to bottom.
: ansi_cons_op Common code for ANSI-driven terminals, factored out
so that, for instance, Avram can hook to it from the telnetd code.
Opcode 3: Clear screen, cancel standout, home cursor
Opcode 4: cursor addressing
shacham
: t_cons_op TTY operations for telnetd TTYs
Opcode 2: Get char
Opcode 1: Put char send char over telnetd
Pause for send failure due to send buffer overflow.
Stuff handled by ANSI escape sequences
Anything else--very bad
shacham
: >signal A trivial way to stop a forground infinite loop.
: signal? Was signal set by user?
: net-init Init stack.
: (netrun) The real work.
: netfg A forground mode stack.
: netproc Child process, forked by net-start.
: >'ttyops From vandy's COM.
TODO - Remove duplication.
: net-start Called from screen 0. fork ret value is passed to setrun,
which recalls, thus invoking the child process.