From 1113751ff65d228221c006cb8bc12b1db0f6b0d2 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 19 Feb 2024 02:23:40 +0100 Subject: [PATCH 01/40] tcp: Remove redundant :ack-p t :ack-p t is the default. --- net/tcp.lisp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 60dea013..add47ffd 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -383,7 +383,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (setf (tcp-connection-last-ack-time connection) (get-internal-run-time)) (when (not *netmangler-force-local-retransmit*) - (tcp4-send-packet connection iss (+u32 irs 1) nil :ack-p t :syn-p t)))) + (tcp4-send-packet connection iss (+u32 irs 1) nil :syn-p t)))) ((logtest flags +tcp4-flag-rst+)) ; Do nothing for resets addressed to nobody. (t (let* ((seq (if (logtest flags +tcp4-flag-ack+) @@ -474,8 +474,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (tcp4-send-packet connection (tcp-connection-snd.nxt connection) (tcp-connection-rcv.nxt connection) - nil - :ack-p t))) + nil))) (defun tcp-packet-sequence-number (packet start end) (declare (ignore end)) @@ -593,7 +592,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (tcp-connection-rcv.nxt connection) (+u32 seq 1)) (when (not *netmangler-force-local-retransmit*) (tcp4-send-packet connection ack (tcp-connection-rcv.nxt connection) nil - :ack-p t :syn-p t)) + :syn-p t)) ;; Cancel retransmit (disarm-retransmit-timer connection) (disarm-timeout-timer connection)) @@ -637,8 +636,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (tcp4-send-packet connection (tcp-connection-snd.nxt connection) (tcp-connection-rcv.nxt connection) - nil - :ack-p t))) + nil))) ((logtest flags +tcp4-flag-rst+) (setf (tcp-connection-pending-error connection) (make-condition 'connection-reset @@ -700,7 +698,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (setf (mezzano.supervisor:event-state (tcp-connection-receive-event connection)) t) - (tcp4-send-packet connection ack (+u32 seq 1) nil :ack-p t)) + (tcp4-send-packet connection ack (+u32 seq 1) nil)) (tcp4-receive-data connection data-length end header-length packet seq start))) ((eql (tcp-connection-snd.una connection) ack) ;; TODO: slow start/duplicate ack detection/fast retransmit/etc. From 3b0fe687f3973cfe42bd08888f7fb7b609ef315d Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 26 Feb 2024 00:30:05 +0100 Subject: [PATCH 02/40] tcp: Update the protocol specification link to rfc9293 --- net/tcp.lisp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index add47ffd..6631901d 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -1,14 +1,8 @@ ;;; TCP ;;; ;;; Transmission Control Protocol - Protocol Specification -;;; https://tools.ietf.org/html/rfc793 +;;; https://datatracker.ietf.org/doc/html/rfc9293 ;;; -;;; EFSM/SDL modeling of the original TCP standard (RFC793) and the -;;; Congestion Control Mechanism of TCP Reno -;;; http://www.medianet.kent.edu/techreports/TR2005-07-22-tcp-EFSM.pdf -;;; -;;; Computing TCP's Retransmission Timer -;;; https://tools.ietf.org/html/rfc6298 (in-package :mezzano.network.tcp) From ef54dfb443c5525d426d055148c8111f1dc219e9 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 26 Feb 2024 00:33:24 +0100 Subject: [PATCH 03/40] tcp: Do nothing to finish segments when in state :closed or :listen --- net/tcp.lisp | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tcp.lisp b/net/tcp.lisp index 6631901d..4b4e6f91 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -379,6 +379,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (when (not *netmangler-force-local-retransmit*) (tcp4-send-packet connection iss (+u32 irs 1) nil :syn-p t)))) ((logtest flags +tcp4-flag-rst+)) ; Do nothing for resets addressed to nobody. + ((logtest flags +tcp4-flag-fin+)) ; Do nothing for finish since the SEG.SEQ cannot be validated (t (let* ((seq (if (logtest flags +tcp4-flag-ack+) (tcp-packet-acknowledgment-number packet start end) From c140f05c83cc17cbfa5999a5b332d1bf09e98f23 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 26 Feb 2024 00:37:27 +0100 Subject: [PATCH 04/40] tcp: Fix errata --- net/tcp.lisp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 4b4e6f91..a08d621b 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -645,7 +645,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :port (tcp-connection-remote-port connection))) (detach-tcp-connection connection) (tcp4-send-packet connection - (tcp-connection-snd.next connection) + (tcp-connection-snd.nxt connection) 0 ; ??? nil :ack-p nil From d2c38744be5ea942006e901397dbfc2c749d0821 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 26 Feb 2024 00:41:42 +0100 Subject: [PATCH 05/40] tcp: Refactor acceptable-segment-p --- net/tcp.lisp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index a08d621b..a21257d8 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -490,16 +490,15 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (defun tcp-packet-data-length (packet start end) (- end (+ start (tcp-packet-header-length packet start end)))) -(defun acceptable-segment-p (connection packet start end) +(defun acceptable-segment-p (connection seg.seq seg.len) (let ((rcv.wnd (tcp-connection-rcv.wnd connection)) - (rcv.nxt (tcp-connection-rcv.nxt connection)) - (seg.seq (tcp-packet-sequence-number packet start end)) - (seg.len (tcp-packet-data-length packet start end))) + (rcv.nxt (tcp-connection-rcv.nxt connection))) (if (eql rcv.wnd 0) (and (eql seg.len 0) (eql seg.seq rcv.nxt)) ;; Arithmetic here is not wrapping, so as to avoid wrap-around problems. - (and (and (<= rcv.nxt seg.seq) (< seg.seq (+ rcv.nxt rcv.wnd))) + (and (<= rcv.nxt seg.seq) + (< seg.seq (+ rcv.nxt rcv.wnd)) (or (eql seg.len 0) (let ((seq-end (+ seg.seq seg.len -1))) (and (<= rcv.nxt seq-end) (< seq-end (+ rcv.nxt rcv.wnd))))))))) @@ -626,7 +625,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (remhash connection (tcp-listener-pending-connections listener)) (decf (tcp-listener-n-pending-connections listener)))))) (:established - (cond ((not (acceptable-segment-p connection packet start end)) + (cond ((not (acceptable-segment-p connection seq data-length)) (when (not (logtest flags +tcp4-flag-rst+)) (tcp4-send-packet connection (tcp-connection-snd.nxt connection) From 8c0a1c15443b95dfaaab973be1d7499520b2d9ce Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 13:04:30 +0100 Subject: [PATCH 06/40] tcp: Add tcp4-send-ack --- net/tcp.lisp | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index a21257d8..eab0d674 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -466,10 +466,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (when (<= seq (tcp-connection-rcv.nxt connection)) ;; Don't check *netmangler-force-local-retransmit* here, ;; or no acks will ever get through. - (tcp4-send-packet connection - (tcp-connection-snd.nxt connection) - (tcp-connection-rcv.nxt connection) - nil))) + (tcp4-send-ack connection))) (defun tcp-packet-sequence-number (packet start end) (declare (ignore end)) @@ -627,10 +624,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (:established (cond ((not (acceptable-segment-p connection seq data-length)) (when (not (logtest flags +tcp4-flag-rst+)) - (tcp4-send-packet connection - (tcp-connection-snd.nxt connection) - (tcp-connection-rcv.nxt connection) - nil))) + (tcp4-send-ack connection))) ((logtest flags +tcp4-flag-rst+) (setf (tcp-connection-pending-error connection) (make-condition 'connection-reset @@ -714,10 +708,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (cond ((logtest flags +tcp4-flag-fin+) (setf (tcp-connection-rcv.nxt connection) (+u32 (tcp-connection-rcv.nxt connection) 1)) - (tcp4-send-packet connection - (tcp-connection-snd.nxt connection) - (tcp-connection-rcv.nxt connection) - nil) + (tcp4-send-ack connection) (if (logtest flags +tcp4-flag-ack+) ;; Remote saw our FIN and closed as well. (detach-tcp-connection connection) @@ -735,10 +726,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") ;; Remote has sent FIN and waiting for ACK (setf (tcp-connection-rcv.nxt connection) (+u32 (tcp-connection-rcv.nxt connection) 1)) - (tcp4-send-packet connection - (tcp-connection-snd.nxt connection) - (tcp-connection-rcv.nxt connection) - nil) + (tcp4-send-ack connection) (detach-tcp-connection connection)) (tcp4-receive-data connection data-length end header-length packet seq start))) (:closing @@ -779,6 +767,12 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (when errors-escape (error c)))))) +(defun tcp4-send-ack (connection) + (tcp4-send-packet connection + (tcp-connection-snd.nxt connection) + (tcp-connection-rcv.nxt connection) + nil)) + (defun compute-ip-pseudo-header-partial-checksum (src-ip dst-ip protocol length) (+ (logand src-ip #xFFFF) (logand (ash src-ip -16) #xFFFF) From e8b7f35e8710fb37aa6350af9254222a675fb59b Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 13:12:55 +0100 Subject: [PATCH 07/40] tcp: Add challenge-ack --- net/tcp.lisp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/tcp.lisp b/net/tcp.lisp index eab0d674..45dacdd7 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -773,6 +773,12 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (tcp-connection-rcv.nxt connection) nil)) +(defun challenge-ack (connection) + (tcp4-send-packet connection + (tcp-connection-snd.nxt connection) + (tcp-connection-rcv.nxt connection) + nil)) + (defun compute-ip-pseudo-header-partial-checksum (src-ip dst-ip protocol length) (+ (logand src-ip #xFFFF) (logand (ash src-ip -16) #xFFFF) From 0e6ca64712eae978230c108e52944e2b44909b0e Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 14:18:28 +0100 Subject: [PATCH 08/40] tcp: Check the sequence numbers before accepting RST in :syn-sent --- net/tcp.lisp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 45dacdd7..6267605d 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -500,6 +500,16 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (let ((seq-end (+ seg.seq seg.len -1))) (and (<= rcv.nxt seq-end) (< seq-end (+ rcv.nxt rcv.wnd))))))))) +(defun acceptable-ack-p (connection seg.ack) + "If SND.UNA < SEG.ACK <= SND.NXT, then the ACK is acceptable." + (if (< (tcp-connection-snd.una connection) + (tcp-connection-snd.nxt connection)) + (and (< (tcp-connection-snd.una connection) seg.ack) + (<= seg.ack (tcp-connection-snd.nxt connection))) + ;; Sequence numbers wrapped. + (or (< (tcp-connection-snd.una connection) seg.ack) + (<= seg.ack (tcp-connection-snd.nxt connection))))) + (defun update-timeout-timer (connection) (when (not (eql (tcp-connection-state connection) :syn-sent)) (disarm-timeout-timer connection) @@ -546,12 +556,13 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (flags (tcp-packet-flags packet start end)) (header-length (tcp-packet-header-length packet start end)) (data-length (tcp-packet-data-length packet start end))) - (when (and (not (eql (tcp-connection-state connection) :established)) - (logtest flags +tcp4-flag-rst+)) + (when (and (logtest flags +tcp4-flag-rst+) + (not (or (eql (tcp-connection-state connection) :syn-sent) + (eql (tcp-connection-state connection) :established)))) ;; FIXME: This code isn't correct, it needs to check the sequence numbers ;; before accepting this packet and resetting the connection. This is - ;; currently only done correctly in the :ESTABLISHED state, but should - ;; be done for the other states too. + ;; currently only done correctly in the states: :SYN-SENT, :ESTABLISHED + ;; But should be done for the other states too. ;; Remote has sent RST, aborting connection (setf (tcp-connection-pending-error connection) (make-condition 'connection-reset @@ -563,11 +574,17 @@ Set to a value near 2^32 to test SND sequence number wrapping.") ;; :CLOSED should never be seen here (ecase (tcp-connection-state connection) (:syn-sent - ;; Active open - (cond ((and (logtest flags +tcp4-flag-ack+) + (cond ((logtest flags +tcp4-flag-rst+) + (when (acceptable-ack-p connection ack) + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection))) + ((and (logtest flags +tcp4-flag-ack+) (logtest flags +tcp4-flag-syn+) (eql ack (tcp-connection-snd.nxt connection))) - ;; Remote has sent SYN+ACK and waiting for ACK + ;; Active open (initial-rtt-measurement connection) (setf (tcp-connection-state connection) :established) (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) From bce312d90b8100388f29587983be8d7a7c4c375b Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 14:31:20 +0100 Subject: [PATCH 09/40] tcp: Send RST when package is from old connection in :syn-sent state --- net/tcp.lisp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 6267605d..604da7bf 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -581,6 +581,11 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :host (tcp-connection-remote-ip connection) :port (tcp-connection-remote-port connection))) (detach-tcp-connection connection))) + ((and (logtest flags +tcp4-flag-ack+) + (not (acceptable-ack-p connection ack))) + ;; Segment comes from an old connection + (unless *netmangler-force-local-retransmit* + (tcp4-send-packet connection ack seq nil :ack-p nil :rst-p t))) ((and (logtest flags +tcp4-flag-ack+) (logtest flags +tcp4-flag-syn+) (eql ack (tcp-connection-snd.nxt connection))) @@ -603,15 +608,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :syn-p t)) ;; Cancel retransmit (disarm-retransmit-timer connection) - (disarm-timeout-timer connection)) - (t - ;; Aborting connection - (tcp4-send-packet connection ack seq nil :rst-p t) - (setf (tcp-connection-pending-error connection) - (make-condition 'connection-aborted - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - (detach-tcp-connection connection)))) + (disarm-timeout-timer connection)))) (:syn-received ;; Pasive open (cond ((and (eql flags +tcp4-flag-ack+) From fb4e21540a3fc8a53045626837962b6b8818f3ff Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 14:49:42 +0100 Subject: [PATCH 10/40] tcp: Refactor tcp4-connection-receive :syn-sent state --- net/tcp.lisp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 604da7bf..cefa134d 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -591,11 +591,11 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (eql ack (tcp-connection-snd.nxt connection))) ;; Active open (initial-rtt-measurement connection) - (setf (tcp-connection-state connection) :established) - (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - (setf (tcp-connection-snd.una connection) ack) - (when (not *netmangler-force-local-retransmit*) - (tcp4-send-packet connection ack (tcp-connection-rcv.nxt connection) nil)) + (setf (tcp-connection-state connection) :established + (tcp-connection-rcv.nxt connection) (+u32 seq 1) + (tcp-connection-snd.una connection) ack) + (unless *netmangler-force-local-retransmit* + (tcp4-send-ack connection)) ;; Cancel retransmit (disarm-retransmit-timer connection) (disarm-timeout-timer connection)) @@ -603,7 +603,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") ;; Simultaneous open (setf (tcp-connection-state connection) :syn-received (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - (when (not *netmangler-force-local-retransmit*) + ;; TODO: Update window + (unless *netmangler-force-local-retransmit* (tcp4-send-packet connection ack (tcp-connection-rcv.nxt connection) nil :syn-p t)) ;; Cancel retransmit From e91e108f273b9b72584e4131e21a21841b4465ff Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 15:22:34 +0100 Subject: [PATCH 11/40] tcp: ACK non RST incoming unacceptable segments --- net/tcp.lisp | 89 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index cefa134d..31f4f26e 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -611,11 +611,13 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (disarm-retransmit-timer connection) (disarm-timeout-timer connection)))) (:syn-received - ;; Pasive open - (cond ((and (eql flags +tcp4-flag-ack+) + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))) + ((and (eql flags +tcp4-flag-ack+) (eql seq (tcp-connection-rcv.nxt connection)) (eql ack (tcp-connection-snd.nxt connection))) - ;; Remote has sent ACK, connection established + ;; Pasive open (initial-rtt-measurement connection) (setf (tcp-connection-state connection) :established) (when listener @@ -638,7 +640,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (decf (tcp-listener-n-pending-connections listener)))))) (:established (cond ((not (acceptable-segment-p connection seq data-length)) - (when (not (logtest flags +tcp4-flag-rst+)) + (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) ((logtest flags +tcp4-flag-rst+) (setf (tcp-connection-pending-error connection) @@ -709,47 +711,60 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (tcp4-receive-data connection data-length end header-length packet seq start))))) (:close-wait ;; Remote has closed, local can still send data. - ;; Not much to do here, just waiting for the application to close. - ) + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))))) (:last-ack - ;; Local closed, waiting for remote to ACK. - (when (logtest flags +tcp4-flag-ack+) - ;; Remote has sent ACK, connection closed - (detach-tcp-connection connection))) + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-ack+) + (detach-tcp-connection connection)))) (:fin-wait-1 ;; Local closed, waiting for remote to close. - (if (zerop data-length) - (when (= seq (tcp-connection-rcv.nxt connection)) - (cond ((logtest flags +tcp4-flag-fin+) + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))) + (t + (if (zerop data-length) + (when (= seq (tcp-connection-rcv.nxt connection)) + (cond ((logtest flags +tcp4-flag-fin+) + (setf (tcp-connection-rcv.nxt connection) + (+u32 (tcp-connection-rcv.nxt connection) 1)) + (tcp4-send-ack connection) + (if (logtest flags +tcp4-flag-ack+) + ;; Remote saw our FIN and closed as well. + (detach-tcp-connection connection) + ;; Simultaneous close + (setf (tcp-connection-state connection) :closing))) + ((logtest flags +tcp4-flag-ack+) + ;; Remote saw our FIN + (setf (tcp-connection-state connection) :fin-wait-2)))) + (tcp4-receive-data connection data-length end header-length packet seq start))))) + (:fin-wait-2 + ;; Local closed, still waiting for remote to close. + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))) + (t + (if (zerop data-length) + (when (and (= seq (tcp-connection-rcv.nxt connection)) + (logtest flags +tcp4-flag-fin+)) + ;; Remote has sent FIN and waiting for ACK (setf (tcp-connection-rcv.nxt connection) (+u32 (tcp-connection-rcv.nxt connection) 1)) (tcp4-send-ack connection) - (if (logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN and closed as well. - (detach-tcp-connection connection) - ;; Simultaneous close - (setf (tcp-connection-state connection) :closing))) - ((logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN - (setf (tcp-connection-state connection) :fin-wait-2)))) - (tcp4-receive-data connection data-length end header-length packet seq start))) - (:fin-wait-2 - ;; Local closed, still waiting for remote to close. - (if (zerop data-length) - (when (and (= seq (tcp-connection-rcv.nxt connection)) - (logtest flags +tcp4-flag-fin+)) - ;; Remote has sent FIN and waiting for ACK - (setf (tcp-connection-rcv.nxt connection) - (+u32 (tcp-connection-rcv.nxt connection) 1)) - (tcp4-send-ack connection) - (detach-tcp-connection connection)) - (tcp4-receive-data connection data-length end header-length packet seq start))) + (detach-tcp-connection connection)) + (tcp4-receive-data connection data-length end header-length packet seq start))))) (:closing ;; Waiting for ACK - (when (and (eql seq (tcp-connection-rcv.nxt connection)) - (logtest flags +tcp4-flag-ack+)) - ;; Remote has sent ACK, connection closed - (detach-tcp-connection connection))))) + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))) + ((and (eql seq (tcp-connection-rcv.nxt connection)) + (logtest flags +tcp4-flag-ack+)) + ;; Remote has sent ACK, connection closed + (detach-tcp-connection connection)))))) (update-timeout-timer connection) ;; Notify any waiters that something may have changed. (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t))) From 5a6d095229688e28a74e22c2f8e8495b6b338451 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 15:43:43 +0100 Subject: [PATCH 12/40] tcp: Check incomming RST segments --- net/tcp.lisp | 84 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 31f4f26e..8ca7fde1 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -556,21 +556,6 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (flags (tcp-packet-flags packet start end)) (header-length (tcp-packet-header-length packet start end)) (data-length (tcp-packet-data-length packet start end))) - (when (and (logtest flags +tcp4-flag-rst+) - (not (or (eql (tcp-connection-state connection) :syn-sent) - (eql (tcp-connection-state connection) :established)))) - ;; FIXME: This code isn't correct, it needs to check the sequence numbers - ;; before accepting this packet and resetting the connection. This is - ;; currently only done correctly in the states: :SYN-SENT, :ESTABLISHED - ;; But should be done for the other states too. - ;; Remote has sent RST, aborting connection - (setf (tcp-connection-pending-error connection) - (make-condition 'connection-reset - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - (detach-tcp-connection connection) - (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t) - (return-from tcp4-connection-receive)) ;; :CLOSED should never be seen here (ecase (tcp-connection-state connection) (:syn-sent @@ -614,6 +599,22 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (cond ((not (eql seq (tcp-connection-rcv.nxt connection))) + (challenge-ack connection)) + ((and listener + (gethash connection (tcp-listener-pending-connections listener))) + ;; Connection comes from pasive OPEN + (remhash connection (tcp-listener-pending-connections listener)) + (decf (tcp-listener-n-pending-connections listener)) + (detach-tcp-connection connection)) + (t + ;; Connection comes from active OPEN + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-refused + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection)))) ((and (eql flags +tcp4-flag-ack+) (eql seq (tcp-connection-rcv.nxt connection)) (eql ack (tcp-connection-snd.nxt connection))) @@ -643,11 +644,14 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) ((logtest flags +tcp4-flag-rst+) - (setf (tcp-connection-pending-error connection) - (make-condition 'connection-reset - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - (detach-tcp-connection connection)) + (cond ((eql seq (tcp-connection-rcv.nxt connection)) + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection)) + (t + (challenge-ack connection)))) ((logtest flags +tcp4-flag-syn+) (setf (tcp-connection-pending-error connection) (make-condition 'connection-reset @@ -713,11 +717,24 @@ Set to a value near 2^32 to test SND sequence number wrapping.") ;; Remote has closed, local can still send data. (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) - (tcp4-send-ack connection))))) + (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (cond ((eql seq (tcp-connection-rcv.nxt connection)) + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection)) + (t + (challenge-ack connection)))))) (:last-ack (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (if (eql seq (tcp-connection-rcv.nxt connection)) + (detach-tcp-connection connection) + (challenge-ack connection))) ((logtest flags +tcp4-flag-ack+) (detach-tcp-connection connection)))) (:fin-wait-1 @@ -725,6 +742,15 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (cond ((eql seq (tcp-connection-rcv.nxt connection)) + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection)) + (t + (challenge-ack connection)))) (t (if (zerop data-length) (when (= seq (tcp-connection-rcv.nxt connection)) @@ -746,6 +772,15 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (cond ((eql seq (tcp-connection-rcv.nxt connection)) + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection)) + (t + (challenge-ack connection)))) (t (if (zerop data-length) (when (and (= seq (tcp-connection-rcv.nxt connection)) @@ -761,6 +796,10 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (if (eql seq (tcp-connection-rcv.nxt connection)) + (detach-tcp-connection connection) + (challenge-ack connection))) ((and (eql seq (tcp-connection-rcv.nxt connection)) (logtest flags +tcp4-flag-ack+)) ;; Remote has sent ACK, connection closed @@ -881,6 +920,9 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (define-condition connection-closed (connection-error) ()) +(define-condition connection-refused (connection-error) + ()) + (define-condition connection-aborted (connection-error) ()) From 53363c98f1de3a660d5980b295202b21beddee72 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 16:27:23 +0100 Subject: [PATCH 13/40] tcp: Don't abort connection when resiving segment in :syn-received state --- net/tcp.lisp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 8ca7fde1..b826785e 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -626,19 +626,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))) ;; Ignore duplicated SYN packets ((and (logtest flags +tcp4-flag-syn+) - (eql seq (-u32 (tcp-connection-rcv.nxt connection) 1)))) - (t - ;; Aborting connection - (tcp4-send-packet connection ack seq nil :rst-p t) - (setf (tcp-connection-pending-error connection) - (make-condition 'connection-aborted - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - (detach-tcp-connection connection) - (when (and listener - (tcp-listener-backlog listener)) - (remhash connection (tcp-listener-pending-connections listener)) - (decf (tcp-listener-n-pending-connections listener)))))) + (eql seq (-u32 (tcp-connection-rcv.nxt connection) 1)))))) (:established (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) From cc921f76d47d96e1ee489c8ccd6e288eea3234ef Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 16:40:36 +0100 Subject: [PATCH 14/40] tcp: Remove connection from listener when getting SYN in state :syn-received --- net/tcp.lisp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/tcp.lisp b/net/tcp.lisp index b826785e..bc4812d9 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -615,6 +615,12 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :host (tcp-connection-remote-ip connection) :port (tcp-connection-remote-port connection))) (detach-tcp-connection connection)))) + ((logtest flags +tcp4-flag-syn+) + (cond ((and listener + (gethash connection (tcp-listener-pending-connections listener))) + ;; Connection comes from pasive OPEN + (remhash connection (tcp-listener-pending-connections listener)) + (decf (tcp-listener-n-pending-connections listener))))) ((and (eql flags +tcp4-flag-ack+) (eql seq (tcp-connection-rcv.nxt connection)) (eql ack (tcp-connection-snd.nxt connection))) From d6029818104ab34649ddbc0c3fcaa78bebcc0e48 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 16:42:40 +0100 Subject: [PATCH 15/40] tcp: Challenge any SYN segment when not in :syn-sent state --- net/tcp.lisp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index bc4812d9..39eb3a5c 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -620,7 +620,10 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (gethash connection (tcp-listener-pending-connections listener))) ;; Connection comes from pasive OPEN (remhash connection (tcp-listener-pending-connections listener)) - (decf (tcp-listener-n-pending-connections listener))))) + (decf (tcp-listener-n-pending-connections listener))) + (t + ;; Connection comes from active OPEN + (challenge-ack connection)))) ((and (eql flags +tcp4-flag-ack+) (eql seq (tcp-connection-rcv.nxt connection)) (eql ack (tcp-connection-snd.nxt connection))) @@ -629,10 +632,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (setf (tcp-connection-state connection) :established) (when listener (remhash connection (tcp-listener-pending-connections listener)) - (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))) - ;; Ignore duplicated SYN packets - ((and (logtest flags +tcp4-flag-syn+) - (eql seq (-u32 (tcp-connection-rcv.nxt connection) 1)))))) + (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))))) (:established (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) @@ -647,17 +647,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (t (challenge-ack connection)))) ((logtest flags +tcp4-flag-syn+) - (setf (tcp-connection-pending-error connection) - (make-condition 'connection-reset - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - (detach-tcp-connection connection) - (tcp4-send-packet connection - (tcp-connection-snd.nxt connection) - 0 ; ??? - nil - :ack-p nil - :rst-p t)) + (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((if (< (tcp-connection-snd.una connection) (tcp-connection-snd.nxt connection)) (and (< (tcp-connection-snd.una connection) ack) @@ -720,7 +710,9 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :port (tcp-connection-remote-port connection))) (detach-tcp-connection connection)) (t - (challenge-ack connection)))))) + (challenge-ack connection)))) + ((logtest flags +tcp4-flag-syn+) + (challenge-ack connection)))) (:last-ack (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) @@ -729,6 +721,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (if (eql seq (tcp-connection-rcv.nxt connection)) (detach-tcp-connection connection) (challenge-ack connection))) + ((logtest flags +tcp4-flag-syn+) + (challenge-ack connection)) ((logtest flags +tcp4-flag-ack+) (detach-tcp-connection connection)))) (:fin-wait-1 @@ -745,6 +739,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (detach-tcp-connection connection)) (t (challenge-ack connection)))) + ((logtest flags +tcp4-flag-syn+) + (challenge-ack connection)) (t (if (zerop data-length) (when (= seq (tcp-connection-rcv.nxt connection)) @@ -775,6 +771,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (detach-tcp-connection connection)) (t (challenge-ack connection)))) + ((logtest flags +tcp4-flag-syn+) + (challenge-ack connection)) (t (if (zerop data-length) (when (and (= seq (tcp-connection-rcv.nxt connection)) @@ -794,6 +792,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (if (eql seq (tcp-connection-rcv.nxt connection)) (detach-tcp-connection connection) (challenge-ack connection))) + ((logtest flags +tcp4-flag-syn+) + (challenge-ack connection)) ((and (eql seq (tcp-connection-rcv.nxt connection)) (logtest flags +tcp4-flag-ack+)) ;; Remote has sent ACK, connection closed From 352172cf336fbac7c70175940c5423546e9cd72a Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 20 Mar 2024 18:39:52 +0100 Subject: [PATCH 16/40] tcp: Add :time-wait state --- net/tcp.lisp | 56 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 39eb3a5c..48825620 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -58,7 +58,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :last-ack :fin-wait-1 :fin-wait-2 - :closing)) + :closing + :time-wait)) (deftype tcp-port-number () '(unsigned-byte 16)) @@ -258,7 +259,8 @@ Set to a value near 2^32 to test SND sequence number wrapping.") :last-ack :fin-wait-1 :fin-wait-2 - :closing) + :closing + :time-wait) (let ((packet (first (tcp-connection-retransmit-queue connection)))) (apply #'tcp4-send-packet connection packet) (setf (tcp-connection-rto connection) @@ -743,16 +745,17 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (challenge-ack connection)) (t (if (zerop data-length) - (when (= seq (tcp-connection-rcv.nxt connection)) + (when (eql seq (tcp-connection-rcv.nxt connection)) (cond ((logtest flags +tcp4-flag-fin+) - (setf (tcp-connection-rcv.nxt connection) - (+u32 (tcp-connection-rcv.nxt connection) 1)) + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) (tcp4-send-ack connection) - (if (logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN and closed as well. - (detach-tcp-connection connection) - ;; Simultaneous close - (setf (tcp-connection-state connection) :closing))) + (cond ((logtest flags +tcp4-flag-ack+) + ;; Remote saw our FIN + ;; TODO: Start the time-wait timer, turn off the other timers. + (setf (tcp-connection-state connection) :time-wait)) + (t + ;; Simultaneous close + (setf (tcp-connection-state connection) :closing)))) ((logtest flags +tcp4-flag-ack+) ;; Remote saw our FIN (setf (tcp-connection-state connection) :fin-wait-2)))) @@ -775,13 +778,13 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (challenge-ack connection)) (t (if (zerop data-length) - (when (and (= seq (tcp-connection-rcv.nxt connection)) - (logtest flags +tcp4-flag-fin+)) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) ;; Remote has sent FIN and waiting for ACK - (setf (tcp-connection-rcv.nxt connection) - (+u32 (tcp-connection-rcv.nxt connection) 1)) - (tcp4-send-ack connection) - (detach-tcp-connection connection)) + ;; TODO: Start the time-wait timer, turn off the other timers. + (setf (tcp-connection-state connection) :time-wait + (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection)) (tcp4-receive-data connection data-length end header-length packet seq start))))) (:closing ;; Waiting for ACK @@ -796,8 +799,23 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (challenge-ack connection)) ((and (eql seq (tcp-connection-rcv.nxt connection)) (logtest flags +tcp4-flag-ack+)) - ;; Remote has sent ACK, connection closed - (detach-tcp-connection connection)))))) + (setf (tcp-connection-state connection) :time-wait)))) + (:time-wait + (cond ((not (acceptable-segment-p connection seq data-length)) + (unless (logtest flags +tcp4-flag-rst+) + (tcp4-send-ack connection))) + ((logtest flags +tcp4-flag-rst+) + (if (eql seq (tcp-connection-rcv.nxt connection)) + (detach-tcp-connection connection) + (challenge-ack connection))) + ((logtest flags +tcp4-flag-syn+) + (challenge-ack connection)) + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + ;; TODO: Restart the 2 MSL timeout. + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection)))))) (update-timeout-timer connection) ;; Notify any waiters that something may have changed. (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t))) @@ -1234,7 +1252,7 @@ Set to a value near 2^32 to test SND sequence number wrapping.") nil :fin-p t :errors-escape t))) - ((:last-ack :fin-wait-1 :fin-wait-2 :closed)))) + ((:last-ack :fin-wait-1 :fin-wait-2 :closed :time-wait)))) (defmethod close ((stream tcp-octet-stream) &key abort) ;; TODO: ABORT should abort the connection entirely. From 663c7995925dcf89b82d2728889712f3fef83c44 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 17:46:50 +0100 Subject: [PATCH 17/40] tcp: Add =< --- net/tcp.lisp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 48825620..5861c5d6 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -73,6 +73,14 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (defun -u32 (x y) (ldb (byte 32 0) (- x y))) +(defun =< (a b c) + "a <= b <= c" + (if (< a c) + (<= a b c) + ;; Sequence numbers wrapped. + (or (<= a b) + (<= b c)))) + ;; FIXME: Inbound connections need to timeout if state :syn-received don't change. ;; TODO: Better locking on this is probably needed. It looks like it is accesed ;; from the network serial queue and from user threads. @@ -662,23 +670,20 @@ Set to a value near 2^32 to test SND sequence number wrapping.") ;; TODO: Update the send window. ;; Remove from the retransmit queue any segments that ;; were fully acknowledged by this ACK. - (flet ((seq-cmp (x) - "Test SND.UNA =< X =< SEG.ACK" - (if (< (tcp-connection-snd.una connection) ack) - (<= (tcp-connection-snd.una connection) x ack) - ;; Sequence numbers wrapped. - (or (<= (tcp-connection-snd.una connection) x) - (<= x ack))))) - (loop - (when (endp (tcp-connection-retransmit-queue connection)) - (return)) - (let* ((rtx-start-seq (first (first (tcp-connection-retransmit-queue connection)))) - (rtx-end-seq (+u32 rtx-start-seq (length (third (first (tcp-connection-retransmit-queue connection))))))) - (when (not (and (seq-cmp rtx-start-seq) - (seq-cmp rtx-end-seq))) - ;; This segment not fully acked. - (return))) - (pop (tcp-connection-retransmit-queue connection)))) + (loop + (when (endp (tcp-connection-retransmit-queue connection)) + (return)) + (let* ((rtx-start-seq (first (first (tcp-connection-retransmit-queue connection)))) + (rtx-end-seq (+u32 rtx-start-seq (length (third (first (tcp-connection-retransmit-queue connection))))))) + (unless (and (=< (tcp-connection-snd.una connection) + rtx-start-seq + ack) + (=< (tcp-connection-snd.una connection) + rtx-end-seq + ack)) + ;; This segment not fully acked. + (return))) + (pop (tcp-connection-retransmit-queue connection))) (if (endp (tcp-connection-retransmit-queue connection)) (disarm-retransmit-timer connection) (arm-retransmit-timer connection)) From 9688f2510034359672da2f5312b3489bc3121a47 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 18:13:37 +0100 Subject: [PATCH 18/40] tcp: Deal with wrap around sequence numbers correctly --- net/tcp.lisp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 5861c5d6..dfa8e01c 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -73,6 +73,32 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (defun -u32 (x y) (ldb (byte 32 0) (- x y))) +(defun x y) + (> (- x y) + (ash 1 31))))) + +(defun >u32 (x y) + "Bigger wrapped y number may actually be considered smaller than x due +to wrap around logic" + (=u32 (x y) + "Bigger wrapped y number may actually be considered smaller than x due +to wrap around logic" + (<=u32 y x)) + (defun =< (a b c) "a <= b <= c" (if (< a c) @@ -469,11 +495,11 @@ Set to a value near 2^32 to test SND sequence number wrapping.") (tcp-connection-receive-event connection)) t)) ;; Add future packet to tcp-connection-rx-data-unordered - ((> seq (tcp-connection-rcv.nxt connection)) + ((>u32 seq (tcp-connection-rcv.nxt connection)) (unless (gethash seq (tcp-connection-rx-data-unordered connection)) (setf (gethash seq (tcp-connection-rx-data-unordered connection)) (list packet (+ start header-length) end data-length))))) - (when (<= seq (tcp-connection-rcv.nxt connection)) + (when (<=u32 seq (tcp-connection-rcv.nxt connection)) ;; Don't check *netmangler-force-local-retransmit* here, ;; or no acks will ever get through. (tcp4-send-ack connection))) From 08e01fb99cc21afa286775c0e3fce35789930fbf Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 18:16:19 +0100 Subject: [PATCH 19/40] tcp: Small refactor --- net/tcp.lisp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index dfa8e01c..313a1f70 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -685,12 +685,7 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. - ((if (< (tcp-connection-snd.una connection) (tcp-connection-snd.nxt connection)) - (and (< (tcp-connection-snd.una connection) ack) - (<= ack (tcp-connection-snd.nxt connection))) - ;; In the middle of wraparound. - (or (< (tcp-connection-snd.una connection) ack) - (<= ack (tcp-connection-snd.nxt connection)))) + ((acceptable-ack-p connection ack) (when (tcp-connection-last-ack-time connection) (subsequent-rtt-measurement connection)) ;; TODO: Update the send window. From f6fa477da18f521c8d7115804a4445c14d962f97 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 19:34:22 +0100 Subject: [PATCH 20/40] tcp: Send RST to segments of old connections in :syn-received state --- net/tcp.lisp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 313a1f70..ac939eaf 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -660,15 +660,19 @@ to wrap around logic" (t ;; Connection comes from active OPEN (challenge-ack connection)))) - ((and (eql flags +tcp4-flag-ack+) - (eql seq (tcp-connection-rcv.nxt connection)) - (eql ack (tcp-connection-snd.nxt connection))) - ;; Pasive open - (initial-rtt-measurement connection) - (setf (tcp-connection-state connection) :established) - (when listener - (remhash connection (tcp-listener-pending-connections listener)) - (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))))) + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + (t + (cond ((acceptable-ack-p connection ack) + ;; Pasive open + (initial-rtt-measurement connection) + (setf (tcp-connection-state connection) :established) + ;; TODO: Update window + (when listener + (remhash connection (tcp-listener-pending-connections listener)) + (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))) + (t + ;; Segment from an old connection + (tcp4-send-packet connection ack seq nil :ack-p nil :rst-p t)))))) (:established (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) From 327843401707f8f8b671a67c729394586c9dbf8a Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 19:41:37 +0100 Subject: [PATCH 21/40] tcp: Handle FIN in :syn-received state --- net/tcp.lisp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index ac939eaf..93f8059c 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -672,7 +672,12 @@ to wrap around logic" (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))) (t ;; Segment from an old connection - (tcp4-send-packet connection ack seq nil :ack-p nil :rst-p t)))))) + (tcp4-send-packet connection ack seq nil :ack-p nil :rst-p t))) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1) + (tcp-connection-state connection) :close-wait) + (tcp4-send-ack connection))))) (:established (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) From 180b7b471e45ade4121243e2de7dccc22e73c4a0 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 19:53:39 +0100 Subject: [PATCH 22/40] tcp: Chenck ACK sequence number in :last-ack state before ending it --- net/tcp.lisp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 93f8059c..d7082ea5 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -760,8 +760,10 @@ to wrap around logic" (challenge-ack connection))) ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) - ((logtest flags +tcp4-flag-ack+) - (detach-tcp-connection connection)))) + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + (t + (when (eql ack (tcp-connection-snd.nxt connection)) + (detach-tcp-connection connection))))) (:fin-wait-1 ;; Local closed, waiting for remote to close. (cond ((not (acceptable-segment-p connection seq data-length)) From 3334d7913d0dbc117eef75232c5e2eccaf996359 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 19:57:16 +0100 Subject: [PATCH 23/40] tcp: Hangle FIN in :last-ack state --- net/tcp.lisp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index d7082ea5..880b1bcf 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -763,7 +763,11 @@ to wrap around logic" ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. (t (when (eql ack (tcp-connection-snd.nxt connection)) - (detach-tcp-connection connection))))) + (detach-tcp-connection connection)) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection))))) (:fin-wait-1 ;; Local closed, waiting for remote to close. (cond ((not (acceptable-segment-p connection seq data-length)) From 0c28348a6a6dc82371c0980a78093e335f19972a Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 21:27:39 +0100 Subject: [PATCH 24/40] tcp: Ignore SYN or RST packets without ACK --- net/tcp.lisp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 880b1bcf..ff5c3f3f 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -749,7 +749,8 @@ to wrap around logic" (t (challenge-ack connection)))) ((logtest flags +tcp4-flag-syn+) - (challenge-ack connection)))) + (challenge-ack connection)) + ((not (logtest flags +tcp4-flag-ack+))))) ; Ignore packets without ACK set. (:last-ack (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) @@ -784,6 +785,7 @@ to wrap around logic" (challenge-ack connection)))) ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. (t (if (zerop data-length) (when (eql seq (tcp-connection-rcv.nxt connection)) @@ -817,6 +819,7 @@ to wrap around logic" (challenge-ack connection)))) ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. (t (if (zerop data-length) (when (and (logtest flags +tcp4-flag-fin+) @@ -838,6 +841,7 @@ to wrap around logic" (challenge-ack connection))) ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((and (eql seq (tcp-connection-rcv.nxt connection)) (logtest flags +tcp4-flag-ack+)) (setf (tcp-connection-state connection) :time-wait)))) From c9a2b0dd8d1995105e420d591cac3f822e69cd82 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 22 Mar 2024 23:53:40 +0100 Subject: [PATCH 25/40] tcp: Allow sending data in half open connection --- net/tcp.lisp | 69 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index ff5c3f3f..1efeef69 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -583,6 +583,32 @@ to wrap around logic" (max 0.01 (* 4 (tcp-connection-rttvar connection)))))) (tcp-connection-last-ack-time connection) nil))) +(defun when-acceptable-ack-p (connection ack seq) + (when (acceptable-ack-p connection ack) + (when (tcp-connection-last-ack-time connection) + (subsequent-rtt-measurement connection)) + (setf (tcp-connection-snd.una connection) ack) + ;; Remove from the retransmit queue any segments that were fully acknowledged by this ACK. + (loop + (when (endp (tcp-connection-retransmit-queue connection)) + (return)) + (let* ((rtx-start-seq (first (first (tcp-connection-retransmit-queue connection)))) + (rtx-end-seq (+u32 rtx-start-seq (length (third (first (tcp-connection-retransmit-queue connection))))))) + (unless (and (=< (tcp-connection-snd.una connection) + rtx-start-seq + ack) + (=< (tcp-connection-snd.una connection) + rtx-end-seq + ack)) + ;; This segment not fully acked. + (return))) + (pop (tcp-connection-retransmit-queue connection))) + (if (endp (tcp-connection-retransmit-queue connection)) + (disarm-retransmit-timer connection) + (arm-retransmit-timer connection))) + ;; TODO: Update window + ) + (defun tcp4-connection-receive (connection packet start end listener) ;; Don't use WITH-TCP-CONNECTION-LOCKED here. No errors should occur ;; in here, so this avoids truncating the backtrace with :resignal-errors. @@ -694,30 +720,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. - ((acceptable-ack-p connection ack) - (when (tcp-connection-last-ack-time connection) - (subsequent-rtt-measurement connection)) - ;; TODO: Update the send window. - ;; Remove from the retransmit queue any segments that - ;; were fully acknowledged by this ACK. - (loop - (when (endp (tcp-connection-retransmit-queue connection)) - (return)) - (let* ((rtx-start-seq (first (first (tcp-connection-retransmit-queue connection)))) - (rtx-end-seq (+u32 rtx-start-seq (length (third (first (tcp-connection-retransmit-queue connection))))))) - (unless (and (=< (tcp-connection-snd.una connection) - rtx-start-seq - ack) - (=< (tcp-connection-snd.una connection) - rtx-end-seq - ack)) - ;; This segment not fully acked. - (return))) - (pop (tcp-connection-retransmit-queue connection))) - (if (endp (tcp-connection-retransmit-queue connection)) - (disarm-retransmit-timer connection) - (arm-retransmit-timer connection)) - (setf (tcp-connection-snd.una connection) ack) + (t + (when-acceptable-ack-p connection ack seq) (if (zerop data-length) (when (and (eql seq (tcp-connection-rcv.nxt connection)) (logtest flags +tcp4-flag-fin+)) @@ -750,7 +754,9 @@ to wrap around logic" (challenge-ack connection)))) ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) - ((not (logtest flags +tcp4-flag-ack+))))) ; Ignore packets without ACK set. + ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + (t + (when-acceptable-ack-p connection ack seq)))) (:last-ack (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) @@ -787,6 +793,7 @@ to wrap around logic" (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. (t + (when-acceptable-ack-p connection ack seq) (if (zerop data-length) (when (eql seq (tcp-connection-rcv.nxt connection)) (cond ((logtest flags +tcp4-flag-fin+) @@ -821,6 +828,7 @@ to wrap around logic" (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. (t + (when-acceptable-ack-p connection ack seq) (if (zerop data-length) (when (and (logtest flags +tcp4-flag-fin+) (eql seq (tcp-connection-rcv.nxt connection))) @@ -842,9 +850,10 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. - ((and (eql seq (tcp-connection-rcv.nxt connection)) - (logtest flags +tcp4-flag-ack+)) - (setf (tcp-connection-state connection) :time-wait)))) + (t + (when-acceptable-ack-p connection ack seq) + (when (eql seq (tcp-connection-rcv.nxt connection)) + (setf (tcp-connection-state connection) :time-wait))))) (:time-wait (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) @@ -1090,8 +1099,8 @@ to wrap around logic" (check-connection-error connection) (update-timeout-timer connection) ;; No sending when the connection is closing. - ;; Half-closed connections seem too weird to be worth dealing with. - (when (not (eql (tcp-connection-state connection) :established)) + (when (not (or (eql (tcp-connection-state connection) :established) + (eql (tcp-connection-state connection) :close-wait))) (error 'connection-closed :host (tcp-connection-remote-ip connection) :port (tcp-connection-remote-port connection))) From d4bf1371c8edf9c4b20c6a07e34a4851b830d83b Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Sat, 23 Mar 2024 00:17:38 +0100 Subject: [PATCH 26/40] tcp: Add rfc5961 mitigation --- net/tcp.lisp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/net/tcp.lisp b/net/tcp.lisp index 1efeef69..81f62f83 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -546,6 +546,12 @@ to wrap around logic" (or (< (tcp-connection-snd.una connection) seg.ack) (<= seg.ack (tcp-connection-snd.nxt connection))))) +(defun rfc5961-mitigation-check-p (connection seg.ack) + "If ((SND.UNA - MAX.SND.WND) =< SEG.ACK =< SND.NXT) the ACK is acceptable." + (let ((x (- (tcp-connection-snd.una connection) + (tcp-connection-max.snd.wnd connection)))) + (=< x seg.ack (tcp-connection-snd.nxt connection)))) + (defun update-timeout-timer (connection) (when (not (eql (tcp-connection-state connection) :syn-sent)) (disarm-timeout-timer connection) @@ -687,6 +693,8 @@ to wrap around logic" ;; Connection comes from active OPEN (challenge-ack connection)))) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (cond ((acceptable-ack-p connection ack) ;; Pasive open @@ -720,6 +728,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (if (zerop data-length) @@ -755,6 +765,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq)))) (:last-ack @@ -768,6 +780,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (when (eql ack (tcp-connection-snd.nxt connection)) (detach-tcp-connection connection)) @@ -792,6 +806,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (if (zerop data-length) @@ -827,6 +843,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (if (zerop data-length) @@ -850,6 +868,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (when (eql seq (tcp-connection-rcv.nxt connection)) @@ -865,6 +885,8 @@ to wrap around logic" ((logtest flags +tcp4-flag-syn+) (challenge-ack connection)) ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. + ((not (rfc5961-mitigation-check-p connection ack)) + (tcp4-send-ack connection)) ((and (logtest flags +tcp4-flag-fin+) (eql seq (tcp-connection-rcv.nxt connection))) ;; TODO: Restart the 2 MSL timeout. From 98af63635aea222fed77f2f1b072ca2a842407df Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Sat, 23 Mar 2024 00:27:51 +0100 Subject: [PATCH 27/40] tcp: Send ACK to segments that acknowledges something not yet sent --- net/tcp.lisp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/tcp.lisp b/net/tcp.lisp index 81f62f83..50a74d05 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -730,6 +730,9 @@ to wrap around logic" ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((not (rfc5961-mitigation-check-p connection ack)) (tcp4-send-ack connection)) + ((>u32 ack (tcp-connection-snd.nxt connection)) + ;; Remote acks something not yet sent + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (if (zerop data-length) @@ -767,6 +770,9 @@ to wrap around logic" ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((not (rfc5961-mitigation-check-p connection ack)) (tcp4-send-ack connection)) + ((>u32 ack (tcp-connection-snd.nxt connection)) + ;; Remote acks something not yet sent + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq)))) (:last-ack @@ -808,6 +814,9 @@ to wrap around logic" ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((not (rfc5961-mitigation-check-p connection ack)) (tcp4-send-ack connection)) + ((>u32 ack (tcp-connection-snd.nxt connection)) + ;; Remote acks something not yet sent + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (if (zerop data-length) @@ -845,6 +854,9 @@ to wrap around logic" ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((not (rfc5961-mitigation-check-p connection ack)) (tcp4-send-ack connection)) + ((>u32 ack (tcp-connection-snd.nxt connection)) + ;; Remote acks something not yet sent + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (if (zerop data-length) @@ -870,6 +882,9 @@ to wrap around logic" ((not (logtest flags +tcp4-flag-ack+))) ; Ignore packets without ACK set. ((not (rfc5961-mitigation-check-p connection ack)) (tcp4-send-ack connection)) + ((>u32 ack (tcp-connection-snd.nxt connection)) + ;; Remote acks something not yet sent + (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) (when (eql seq (tcp-connection-rcv.nxt connection)) From bfb920a0fd42d45593e780c18b02e99f959b2b79 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Sat, 23 Mar 2024 00:56:05 +0100 Subject: [PATCH 28/40] tcp: Hangle FIN in :closing state --- net/tcp.lisp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 50a74d05..2717e1d1 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -888,7 +888,11 @@ to wrap around logic" (t (when-acceptable-ack-p connection ack seq) (when (eql seq (tcp-connection-rcv.nxt connection)) - (setf (tcp-connection-state connection) :time-wait))))) + (setf (tcp-connection-state connection) :time-wait)) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection))))) (:time-wait (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) From dd953470c1a648ebdf43b3499f0eca07ca1e28f3 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Sat, 23 Mar 2024 01:03:38 +0100 Subject: [PATCH 29/40] tcp: Hangle FIN in :close-wait state --- net/tcp.lisp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 2717e1d1..49cc9a88 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -774,7 +774,11 @@ to wrap around logic" ;; Remote acks something not yet sent (tcp4-send-ack connection)) (t - (when-acceptable-ack-p connection ack seq)))) + (when-acceptable-ack-p connection ack seq) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection))))) (:last-ack (cond ((not (acceptable-segment-p connection seq data-length)) (unless (logtest flags +tcp4-flag-rst+) From 0d07fa77b7d7736917c2d28b5c3beb912eb52465 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Sat, 23 Mar 2024 02:09:36 +0100 Subject: [PATCH 30/40] tcp: Allow to resive data and control in the same segment --- net/tcp.lisp | 79 ++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 49cc9a88..3df681f9 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -709,8 +709,8 @@ to wrap around logic" (tcp4-send-packet connection ack seq nil :ack-p nil :rst-p t))) (when (and (logtest flags +tcp4-flag-fin+) (eql seq (tcp-connection-rcv.nxt connection))) - (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1) - (tcp-connection-state connection) :close-wait) + (setf (tcp-connection-state connection) :close-wait + (tcp-connection-rcv.nxt connection) (+u32 seq 1)) (tcp4-send-ack connection))))) (:established (cond ((not (acceptable-segment-p connection seq data-length)) @@ -735,22 +735,14 @@ to wrap around logic" (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) - (if (zerop data-length) - (when (and (eql seq (tcp-connection-rcv.nxt connection)) - (logtest flags +tcp4-flag-fin+)) - ;; Remote has sent FIN and waiting for ACK - (setf (tcp-connection-state connection) :close-wait - (tcp-connection-rcv.nxt connection) - (+u32 seq 1)) - (setf (mezzano.supervisor:event-state - (tcp-connection-receive-event connection)) - t) - (tcp4-send-packet connection ack (+u32 seq 1) nil)) - (tcp4-receive-data connection data-length end header-length packet seq start))) - ((eql (tcp-connection-snd.una connection) ack) - ;; TODO: slow start/duplicate ack detection/fast retransmit/etc. - (when (not (eql data-length 0)) - (tcp4-receive-data connection data-length end header-length packet seq start))))) + (unless (zerop data-length) + (tcp4-receive-data connection data-length end header-length packet seq start)) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + ;; Remote has sent FIN and waiting for ACK + (setf (tcp-connection-state connection) :close-wait + (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection))))) (:close-wait ;; Remote has closed, local can still send data. (cond ((not (acceptable-segment-p connection seq data-length)) @@ -823,22 +815,23 @@ to wrap around logic" (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) - (if (zerop data-length) - (when (eql seq (tcp-connection-rcv.nxt connection)) - (cond ((logtest flags +tcp4-flag-fin+) - (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - (tcp4-send-ack connection) - (cond ((logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN - ;; TODO: Start the time-wait timer, turn off the other timers. - (setf (tcp-connection-state connection) :time-wait)) - (t - ;; Simultaneous close - (setf (tcp-connection-state connection) :closing)))) - ((logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN - (setf (tcp-connection-state connection) :fin-wait-2)))) - (tcp4-receive-data connection data-length end header-length packet seq start))))) + (unless (zerop data-length) + (tcp4-receive-data connection data-length end header-length packet seq start)) + (when (eql seq (tcp-connection-rcv.nxt connection)) + (cond ((logtest flags +tcp4-flag-fin+) + (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection) + (cond ((logtest flags +tcp4-flag-ack+) + ;; Remote saw our FIN + (setf (tcp-connection-state connection) :time-wait) + ;; TODO: Start the time-wait timer, turn off the other timers. + ) + (t + ;; Simultaneous close + (setf (tcp-connection-state connection) :closing)))) + ((logtest flags +tcp4-flag-ack+) + ;; Remote saw our FIN + (setf (tcp-connection-state connection) :fin-wait-2))))))) (:fin-wait-2 ;; Local closed, still waiting for remote to close. (cond ((not (acceptable-segment-p connection seq data-length)) @@ -863,15 +856,15 @@ to wrap around logic" (tcp4-send-ack connection)) (t (when-acceptable-ack-p connection ack seq) - (if (zerop data-length) - (when (and (logtest flags +tcp4-flag-fin+) - (eql seq (tcp-connection-rcv.nxt connection))) - ;; Remote has sent FIN and waiting for ACK - ;; TODO: Start the time-wait timer, turn off the other timers. - (setf (tcp-connection-state connection) :time-wait - (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - (tcp4-send-ack connection)) - (tcp4-receive-data connection data-length end header-length packet seq start))))) + (unless (zerop data-length) + (tcp4-receive-data connection data-length end header-length packet seq start)) + (when (and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + (setf (tcp-connection-state connection) :time-wait + (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection) + ;; TODO: Start the time-wait timer, turn off the other timers. + )))) (:closing ;; Waiting for ACK (cond ((not (acceptable-segment-p connection seq data-length)) From 5a15692eee1e6c602b18c233f874944c6d49f38e Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Sat, 23 Mar 2024 02:40:21 +0100 Subject: [PATCH 31/40] tcp: Refactor tcp4-connection-receive :fin-wait-1 state --- net/tcp.lisp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 3df681f9..8b1277cf 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -817,21 +817,21 @@ to wrap around logic" (when-acceptable-ack-p connection ack seq) (unless (zerop data-length) (tcp4-receive-data connection data-length end header-length packet seq start)) - (when (eql seq (tcp-connection-rcv.nxt connection)) - (cond ((logtest flags +tcp4-flag-fin+) - (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - (tcp4-send-ack connection) - (cond ((logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN - (setf (tcp-connection-state connection) :time-wait) - ;; TODO: Start the time-wait timer, turn off the other timers. - ) - (t - ;; Simultaneous close - (setf (tcp-connection-state connection) :closing)))) - ((logtest flags +tcp4-flag-ack+) - ;; Remote saw our FIN - (setf (tcp-connection-state connection) :fin-wait-2))))))) + (cond ((and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + (setf (tcp-connection-state connection) :time-wait + (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection) + ;; TODO: Start the time-wait timer, turn off the other timers. + ) + ((eql seq (tcp-connection-rcv.nxt connection)) + (setf (tcp-connection-state connection) :fin-wait-2)) + ((and (logtest flags +tcp4-flag-fin+) + (eql seq (tcp-connection-rcv.nxt connection))) + ;; Simultaneous close + (setf (tcp-connection-state connection) :closing + (tcp-connection-rcv.nxt connection) (+u32 seq 1)) + (tcp4-send-ack connection)))))) (:fin-wait-2 ;; Local closed, still waiting for remote to close. (cond ((not (acceptable-segment-p connection seq data-length)) From 7cf7956db939bf9de0da46b2fc8cd54f851d85ad Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 27 Mar 2024 18:44:05 +0100 Subject: [PATCH 32/40] tcp: Update window size --- net/tcp.lisp | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 8b1277cf..4ba1b208 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -224,6 +224,14 @@ to wrap around logic" :type tcp-sequence-number) (%snd.una :accessor tcp-connection-snd.una :initarg :snd.una) + (%snd.wnd :accessor tcp-connection-snd.wnd + :initarg :snd.wnd) + (%max.snd.wnd :accessor tcp-connection-max.snd.wnd + :initarg :max.snd.wnd) + (%snd.wl1 :accessor tcp-connection-snd.wl1 + :initarg :snd.wl1) + (%snd.wl2 :accessor tcp-connection-snd.wl2 + :initarg :snd.wl2) (%rcv.nxt :accessor tcp-connection-rcv.nxt :initarg :rcv.nxt :type tcp-sequence-number) @@ -251,6 +259,7 @@ to wrap around logic" :initarg :boot-id)) (:default-initargs :max-seg-size 1000 + :max.snd.wnd 0 :last-ack-time nil :srtt nil :rttvar nil @@ -520,6 +529,10 @@ to wrap around logic" (declare (ignore end)) (* (ldb (byte 4 12) (ub16ref/be packet (+ start +tcp4-header-flags-and-data-offset+))) 4)) +(defun tcp-packet-window-size (packet start end) + (declare (ignore end)) + (ub16ref/be packet (+ start +tcp4-header-window-size+))) + (defun tcp-packet-data-length (packet start end) (- end (+ start (tcp-packet-header-length packet start end)))) @@ -552,6 +565,13 @@ to wrap around logic" (tcp-connection-max.snd.wnd connection)))) (=< x seg.ack (tcp-connection-snd.nxt connection)))) +(defun update-window (connection seg.wnd seg.seq seg.ack) + (when (> seg.wnd (tcp-connection-max.snd.wnd connection)) + (setf (tcp-connection-max.snd.wnd connection) seg.wnd)) + (setf (tcp-connection-snd.wnd connection) seg.wnd + (tcp-connection-snd.wl1 connection) seg.seq + (tcp-connection-snd.wl2 connection) seg.ack)) + (defun update-timeout-timer (connection) (when (not (eql (tcp-connection-state connection) :syn-sent)) (disarm-timeout-timer connection) @@ -589,7 +609,7 @@ to wrap around logic" (max 0.01 (* 4 (tcp-connection-rttvar connection)))))) (tcp-connection-last-ack-time connection) nil))) -(defun when-acceptable-ack-p (connection ack seq) +(defun when-acceptable-ack-p (connection ack seq wnd) (when (acceptable-ack-p connection ack) (when (tcp-connection-last-ack-time connection) (subsequent-rtt-measurement connection)) @@ -612,8 +632,7 @@ to wrap around logic" (if (endp (tcp-connection-retransmit-queue connection)) (disarm-retransmit-timer connection) (arm-retransmit-timer connection))) - ;; TODO: Update window - ) + (update-window connection wnd seq ack)) (defun tcp4-connection-receive (connection packet start end listener) ;; Don't use WITH-TCP-CONNECTION-LOCKED here. No errors should occur @@ -623,6 +642,7 @@ to wrap around logic" (ack (tcp-packet-acknowledgment-number packet start end)) (flags (tcp-packet-flags packet start end)) (header-length (tcp-packet-header-length packet start end)) + (wnd (tcp-packet-window-size packet start end)) (data-length (tcp-packet-data-length packet start end))) ;; :CLOSED should never be seen here (ecase (tcp-connection-state connection) @@ -656,7 +676,7 @@ to wrap around logic" ;; Simultaneous open (setf (tcp-connection-state connection) :syn-received (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - ;; TODO: Update window + (update-window connection wnd seq ack) (unless *netmangler-force-local-retransmit* (tcp4-send-packet connection ack (tcp-connection-rcv.nxt connection) nil :syn-p t)) @@ -700,7 +720,7 @@ to wrap around logic" ;; Pasive open (initial-rtt-measurement connection) (setf (tcp-connection-state connection) :established) - ;; TODO: Update window + (update-window connection wnd seq ack) (when listener (remhash connection (tcp-listener-pending-connections listener)) (mezzano.sync:mailbox-send connection (tcp-listener-connections listener)))) @@ -734,7 +754,7 @@ to wrap around logic" ;; Remote acks something not yet sent (tcp4-send-ack connection)) (t - (when-acceptable-ack-p connection ack seq) + (when-acceptable-ack-p connection ack seq wnd) (unless (zerop data-length) (tcp4-receive-data connection data-length end header-length packet seq start)) (when (and (logtest flags +tcp4-flag-fin+) @@ -766,7 +786,7 @@ to wrap around logic" ;; Remote acks something not yet sent (tcp4-send-ack connection)) (t - (when-acceptable-ack-p connection ack seq) + (when-acceptable-ack-p connection ack seq wnd) (when (and (logtest flags +tcp4-flag-fin+) (eql seq (tcp-connection-rcv.nxt connection))) (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) @@ -814,7 +834,7 @@ to wrap around logic" ;; Remote acks something not yet sent (tcp4-send-ack connection)) (t - (when-acceptable-ack-p connection ack seq) + (when-acceptable-ack-p connection ack seq wnd) (unless (zerop data-length) (tcp4-receive-data connection data-length end header-length packet seq start)) (cond ((and (logtest flags +tcp4-flag-fin+) @@ -855,7 +875,7 @@ to wrap around logic" ;; Remote acks something not yet sent (tcp4-send-ack connection)) (t - (when-acceptable-ack-p connection ack seq) + (when-acceptable-ack-p connection ack seq wnd) (unless (zerop data-length) (tcp4-receive-data connection data-length end header-length packet seq start)) (when (and (logtest flags +tcp4-flag-fin+) @@ -883,7 +903,7 @@ to wrap around logic" ;; Remote acks something not yet sent (tcp4-send-ack connection)) (t - (when-acceptable-ack-p connection ack seq) + (when-acceptable-ack-p connection ack seq wnd) (when (eql seq (tcp-connection-rcv.nxt connection)) (setf (tcp-connection-state connection) :time-wait)) (when (and (logtest flags +tcp4-flag-fin+) From b3659158dc9ae6add76938f456f0bbe6358073df Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 1 Jul 2024 18:13:46 +0200 Subject: [PATCH 33/40] arp: Use correct function to get the time --- net/arp.lisp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/arp.lisp b/net/arp.lisp index 2fc8e089..a1557437 100644 --- a/net/arp.lisp +++ b/net/arp.lisp @@ -130,7 +130,7 @@ Returns NIL if there is no entry currently in the cache, this will trigger a loo nil) (defun arp-expiration () - (let ((time (1+ (get-internal-real-time)))) + (let ((time (1+ (get-universal-time)))) (mezzano.supervisor:with-mutex (*arp-lock*) (setf *arp-table* (remove-if #'(lambda (arp) (>= time (fourth arp))) From 4f2bd69e7fbc39901947553ca946cad3544af926 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 1 Jul 2024 18:53:53 +0200 Subject: [PATCH 34/40] tcp: Report connection-closing in tcp-send when connetion is closing --- net/tcp.lisp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 4ba1b208..7fe45abd 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -1044,6 +1044,9 @@ to wrap around logic" (define-condition connection-closed (connection-error) ()) +(define-condition connection-closing (connection-error) + ()) + (define-condition connection-refused (connection-error) ()) @@ -1156,12 +1159,21 @@ to wrap around logic" (with-tcp-connection-locked connection (check-connection-error connection) (update-timeout-timer connection) - ;; No sending when the connection is closing. - (when (not (or (eql (tcp-connection-state connection) :established) - (eql (tcp-connection-state connection) :close-wait))) + (when (or (eql (tcp-connection-state connection) :syn-sent) + (eql (tcp-connection-state connection) :syn-received)) + ;; TODO: If in state :syn-sent or :syn-received queue the data for processing after the ESTABLISHED state has been reached (error 'connection-closed :host (tcp-connection-remote-ip connection) :port (tcp-connection-remote-port connection))) + ;; No sending when the connection is closing. + (when (or (eql (tcp-connection-state connection) :fin-wait-1) + (eql (tcp-connection-state connection) :fin-wait-2) + (eql (tcp-connection-state connection) :closing) + (eql (tcp-connection-state connection) :last-ack) + (eql (tcp-connection-state connection) :time-wait)) + (error 'connection-closing + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) (unless (tcp-connection-last-ack-time connection) (setf (tcp-connection-last-ack-time connection) (get-internal-run-time))) From c229bc9f0647f3d642ec4d0a495cb2bbb390e460 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Mon, 1 Jul 2024 20:01:37 +0200 Subject: [PATCH 35/40] tcp: Implemented abort close TCP specification (rfc9293) says to send a reset segment --- net/tcp.lisp | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 7fe45abd..c136cf82 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -1026,17 +1026,6 @@ to wrap around logic" :do (unless (get-tcp-connection ip port local-ip local-port) (return local-port)))) -(defun abort-connection (connection) - (mezzano.sync.dispatch:dispatch-async - (lambda () - (tcp4-send-packet connection - (tcp-connection-snd.nxt connection) - (tcp-connection-rcv.nxt connection) - nil - :rst-p t) - (detach-tcp-connection connection)) - net::*network-serial-queue*)) - (define-condition connection-error (net:network-error) ((host :initarg :host :reader connection-error-host) (port :initarg :port :reader connection-error-port))) @@ -1341,6 +1330,33 @@ to wrap around logic" (defmethod gray:stream-write-sequence ((stream tcp-octet-stream) sequence &optional (start 0) end) (tcp-send (tcp-stream-connection stream) sequence start end)) +(defun abort-connection (connection) + (ecase (tcp-connection-state connection) + (:syn-sent + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection)))) + ((:syn-received :established :fin-wait-1 :fin-wait-2 :close-wait) + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-reset + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (tcp4-send-packet connection + (tcp-connection-snd.nxt connection) + (tcp-connection-rcv.nxt connection) + nil + :ack-p nil + :rst-p t)) + ((:closing :last-ack :time-wait) + (tcp4-send-packet connection + (tcp-connection-snd.nxt connection) + (tcp-connection-rcv.nxt connection) + nil)) + (:closed)) + (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t) + (detach-tcp-connection connection)) + (defun close-connection (connection) (ecase (tcp-connection-state connection) (:established @@ -1379,12 +1395,11 @@ to wrap around logic" ((:last-ack :fin-wait-1 :fin-wait-2 :closed :time-wait)))) (defmethod close ((stream tcp-octet-stream) &key abort) - ;; TODO: ABORT should abort the connection entirely. - ;; Don't even bother sending RST packets, just detatch the connection. - (declare (ignore abort)) (let ((connection (tcp-stream-connection stream))) (with-tcp-connection-locked connection - (close-connection connection)))) + (if abort + (abort-connection connection) + (close-connection connection))))) (defmethod open-stream-p ((stream tcp-octet-stream)) (with-tcp-connection-locked (tcp-stream-connection stream) From 24ea270da42ed129e49964c97b4a68f3b6a0dd71 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Tue, 9 Jul 2024 12:47:37 +0200 Subject: [PATCH 36/40] tcp: Refactor tcp-send --- net/tcp.lisp | 62 +++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index c136cf82..c3381042 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -1148,38 +1148,36 @@ to wrap around logic" (with-tcp-connection-locked connection (check-connection-error connection) (update-timeout-timer connection) - (when (or (eql (tcp-connection-state connection) :syn-sent) - (eql (tcp-connection-state connection) :syn-received)) - ;; TODO: If in state :syn-sent or :syn-received queue the data for processing after the ESTABLISHED state has been reached - (error 'connection-closed - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - ;; No sending when the connection is closing. - (when (or (eql (tcp-connection-state connection) :fin-wait-1) - (eql (tcp-connection-state connection) :fin-wait-2) - (eql (tcp-connection-state connection) :closing) - (eql (tcp-connection-state connection) :last-ack) - (eql (tcp-connection-state connection) :time-wait)) - (error 'connection-closing - :host (tcp-connection-remote-ip connection) - :port (tcp-connection-remote-port connection))) - (unless (tcp-connection-last-ack-time connection) - (setf (tcp-connection-last-ack-time connection) - (get-internal-run-time))) - (let ((mss (tcp-connection-max-seg-size connection))) - (cond ((>= start end)) - ((> (- end start) mss) - ;; Send multiple packets. - (loop - for offset from start by mss - while (> (- end offset) mss) - do - (tcp-send-1 connection data offset (+ offset mss)) - finally - (tcp-send-1 connection data offset end :psh-p t))) - (t - ;; Send one packet. - (tcp-send-1 connection data start end :psh-p t)))))) + (ecase (tcp-connection-state connection) + ((:syn-sent :syn-received) + ;; Data associated with SEND may be sent with SYN segment or queued for transmission after entering ESTABLISHED state + ;; TODO: If in state :syn-sent or :syn-received queue the data for processing after the ESTABLISHED state has been reached + (error 'connection-closed + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + ((:established :close-wait) + (unless (tcp-connection-last-ack-time connection) + (setf (tcp-connection-last-ack-time connection) + (get-internal-run-time))) + (let ((mss (tcp-connection-max-seg-size connection))) + (cond ((>= start end)) + ((> (- end start) mss) + ;; Send multiple packets. + (loop :for offset :from start :by mss + :while (> (- end offset) mss) + :do (tcp-send-1 connection data offset (+ offset mss)) + :finally (tcp-send-1 connection data offset end :psh-p t))) + (t + ;; Send one packet. + (tcp-send-1 connection data start end :psh-p t))))) + ((:fin-wait-1 :fin-wait-2 :closing :last-ack :time-wait) + (error 'connection-closing + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (:closed + (error 'connection-closed + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection)))))) (defclass tcp-octet-stream (gray:fundamental-binary-input-stream gray:fundamental-binary-output-stream) From 94ebcb65dce00264ddc776b88c3d04d5e38383a0 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Tue, 9 Jul 2024 14:52:05 +0200 Subject: [PATCH 37/40] tcp: Add missing :closed case Make sure the Mezzano compilation does not fail due to missing :closed case --- net/tcp.lisp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index c3381042..5e3be6dc 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -308,7 +308,8 @@ to wrap around logic" (apply #'tcp4-send-packet connection packet) (setf (tcp-connection-rto connection) (min *maximum-rto* (* 2 (tcp-connection-rto connection)))) - (arm-retransmit-timer connection)))))) + (arm-retransmit-timer connection))) + (:closed)))) (defun arm-timeout-timer (seconds connection) (mezzano.supervisor:timer-arm seconds From dc3eced22e0ebbf8a5cc09b2af040ade9622d151 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Wed, 10 Jul 2024 14:08:46 +0200 Subject: [PATCH 38/40] tcp: Implemented time-wait timeout --- net/tcp.lisp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 5e3be6dc..5d65bce0 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -32,6 +32,7 @@ (defparameter *tcp-initial-retransmit-time* 1) (defparameter *minimum-rto* 1) ;; in seconds (defparameter *maximum-rto* 60) ;; in seconds +(defparameter *msl* 120) ;; in seconds (defparameter *initial-window-size* 8192) @@ -336,7 +337,7 @@ to wrap around logic" :port (tcp-connection-remote-port connection))) (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t) (case (tcp-connection-state connection) - (:syn-sent + ((:syn-sent :time-wait) (detach-tcp-connection connection)) (:closed) (t @@ -574,13 +575,14 @@ to wrap around logic" (tcp-connection-snd.wl2 connection) seg.ack)) (defun update-timeout-timer (connection) - (when (not (eql (tcp-connection-state connection) :syn-sent)) - (disarm-timeout-timer connection) - (let ((timeout (tcp-connection-timeout connection))) - (when (and timeout - (not (member (tcp-connection-state connection) - '(:fin-wait-1 :fin-wait-2 :last-ack :closed)))) - (arm-timeout-timer timeout connection))))) + (case (tcp-connection-state connection) + ((:fin-wait-1 :fin-wait-2) + (disarm-timeout-timer connection)) + ((:syn-sent :syn-received :established :closing) + (disarm-timeout-timer connection) + (let ((timeout (tcp-connection-timeout connection))) + (when timeout + (arm-timeout-timer timeout connection)))))) (defun initial-rtt-measurement (connection) (let ((delta-time (float (/ (- (get-internal-run-time) (tcp-connection-last-ack-time connection)) @@ -843,8 +845,7 @@ to wrap around logic" (setf (tcp-connection-state connection) :time-wait (tcp-connection-rcv.nxt connection) (+u32 seq 1)) (tcp4-send-ack connection) - ;; TODO: Start the time-wait timer, turn off the other timers. - ) + (arm-timeout-timer (* 2 *msl*) connection)) ((eql seq (tcp-connection-rcv.nxt connection)) (setf (tcp-connection-state connection) :fin-wait-2)) ((and (logtest flags +tcp4-flag-fin+) @@ -884,8 +885,7 @@ to wrap around logic" (setf (tcp-connection-state connection) :time-wait (tcp-connection-rcv.nxt connection) (+u32 seq 1)) (tcp4-send-ack connection) - ;; TODO: Start the time-wait timer, turn off the other timers. - )))) + (arm-timeout-timer (* 2 *msl*) connection))))) (:closing ;; Waiting for ACK (cond ((not (acceptable-segment-p connection seq data-length)) @@ -906,7 +906,9 @@ to wrap around logic" (t (when-acceptable-ack-p connection ack seq wnd) (when (eql seq (tcp-connection-rcv.nxt connection)) - (setf (tcp-connection-state connection) :time-wait)) + (setf (tcp-connection-state connection) :time-wait) + (disarm-timeout-timer connection) + (arm-timeout-timer (* 2 *msl*) connection)) (when (and (logtest flags +tcp4-flag-fin+) (eql seq (tcp-connection-rcv.nxt connection))) (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) @@ -926,9 +928,10 @@ to wrap around logic" (tcp4-send-ack connection)) ((and (logtest flags +tcp4-flag-fin+) (eql seq (tcp-connection-rcv.nxt connection))) - ;; TODO: Restart the 2 MSL timeout. (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) - (tcp4-send-ack connection)))))) + (tcp4-send-ack connection) + (disarm-timeout-timer connection) + (arm-timeout-timer (* 2 *msl*) connection)))))) (update-timeout-timer connection) ;; Notify any waiters that something may have changed. (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t))) From 1690e24378260f7524ef313c880d61f595d472d1 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 12 Jul 2024 15:32:35 +0200 Subject: [PATCH 39/40] tcp: Add :closed case to tcp4-connection-receive --- net/tcp.lisp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 5d65bce0..752497d0 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -647,7 +647,6 @@ to wrap around logic" (header-length (tcp-packet-header-length packet start end)) (wnd (tcp-packet-window-size packet start end)) (data-length (tcp-packet-data-length packet start end))) - ;; :CLOSED should never be seen here (ecase (tcp-connection-state connection) (:syn-sent (cond ((logtest flags +tcp4-flag-rst+) @@ -931,7 +930,8 @@ to wrap around logic" (setf (tcp-connection-rcv.nxt connection) (+u32 seq 1)) (tcp4-send-ack connection) (disarm-timeout-timer connection) - (arm-timeout-timer (* 2 *msl*) connection)))))) + (arm-timeout-timer (* 2 *msl*) connection)))) + (:closed))) (update-timeout-timer connection) ;; Notify any waiters that something may have changed. (mezzano.supervisor:condition-notify (tcp-connection-cvar connection) t))) From fe6ae64e019fbff86817c4c72e95a81e93f13b20 Mon Sep 17 00:00:00 2001 From: Bruno Cichon Date: Fri, 12 Jul 2024 19:42:04 +0200 Subject: [PATCH 40/40] tcp: Add close for :syn-sent and :syn-received cases --- net/tcp.lisp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/net/tcp.lisp b/net/tcp.lisp index 752497d0..53d6b26d 100644 --- a/net/tcp.lisp +++ b/net/tcp.lisp @@ -1361,6 +1361,30 @@ to wrap around logic" (defun close-connection (connection) (ecase (tcp-connection-state connection) + (:syn-sent + (setf (tcp-connection-pending-error connection) + (make-condition 'connection-closing + :host (tcp-connection-remote-ip connection) + :port (tcp-connection-remote-port connection))) + (detach-tcp-connection connection)) + (:syn-received + ;; TODO: If there is data to send queue for processing after entering ESTABLISHED state. + (setf (tcp-connection-state connection) :fin-wait-1) + (setf (tcp-connection-retransmit-queue connection) + (append (tcp-connection-retransmit-queue connection) + (list (list (tcp-connection-snd.nxt connection) + (tcp-connection-rcv.nxt connection) + nil + :fin-p t + :errors-escape t)))) + (arm-retransmit-timer connection) + (when (not *netmangler-force-local-retransmit*) + (tcp4-send-packet connection + (tcp-connection-snd.nxt connection) + (tcp-connection-rcv.nxt connection) + nil + :fin-p t + :errors-escape t))) (:established (setf (tcp-connection-state connection) :fin-wait-1) (setf (tcp-connection-retransmit-queue connection) @@ -1368,7 +1392,8 @@ to wrap around logic" (list (list (tcp-connection-snd.nxt connection) (tcp-connection-rcv.nxt connection) nil - :fin-p t)))) + :fin-p t + :errors-escape t)))) (arm-retransmit-timer connection) (when (not *netmangler-force-local-retransmit*) (tcp4-send-packet connection @@ -1394,7 +1419,7 @@ to wrap around logic" nil :fin-p t :errors-escape t))) - ((:last-ack :fin-wait-1 :fin-wait-2 :closed :time-wait)))) + ((:last-ack :fin-wait-1 :fin-wait-2 :closing :time-wait :closed)))) (defmethod close ((stream tcp-octet-stream) &key abort) (let ((connection (tcp-stream-connection stream)))