;;; -*- Mode:LISP; Package:I; Base:10; Readtable:CL -*- (defstruct (pkt-history (:type :list)) pkt direction time ) (defvar *pkt-history* nil) (defvar *enable-pkt-history* nil) (defun record (pkt direction) (when *enable-pkt-history* (let ((save-pkt (get-pkt))) (copy-array-contents pkt save-pkt) (let ((struct (make-pkt-history :pkt save-pkt :direction direction :time (time)))) (without-interrupts (push struct *pkt-history*)))))) (defun print-history (&optional (max 50)) (let ((history (without-interrupts (reverse *pkt-history*)))) (do ((tail history (cdr tail)) hist pkt (iss (send conn :iss)) (irs (send conn :irs)) ) ((null tail)) (setq hist (car tail)) (setq pkt (pkt-history-pkt hist)) (format t "~&") (ecase (pkt-history-direction hist) (:received (format t "R ") (format t "~5d " (32-bit-minus (tcp-sequence-number pkt) irs)) ) (:transmitted (format t "~40tT "))) ))) (defun initialize () (setq *tcp-conns* nil) (setq *ip-pkts* nil) (setq *pkts* nil) (install-ip-receiver) (start-ip-receiver) (start-ip-background) (reset-stats) ) (defvar *pkts* nil) (defun get-pkt () (let ((pkt (without-interrupts (cond ((null *pkts*) (make-array 762. :type :art-16b :leader-length 2 :named-structure-symbol 'pkt)) (t (pop *pkts*)))))) (array-initialize pkt 0))) (defun free-pkt (pkt) (when (or (not (eq (array-type pkt) 'art-16b)) (not (eq (array-leader pkt 1) 'pkt))) (ferror nil "trying to free bad pkt ~s" pkt)) (when (memq pkt *pkts*) (ferror nil "pkt already freed")) (without-interrupts (push pkt *pkts*)) nil) (defun convert-int-pkt-and-free (int-pkt &optional n-half-words) "Convert to regular pkt, and free INT-PKT" (let ((pkt (get-pkt))) (cond ((null n-half-words) (copy-array-contents int-pkt pkt)) (t (copy-array-portion int-pkt 0 n-half-words pkt 0 n-half-words))) (chaos:free-int-pkt int-pkt) pkt)) (defun copy-to-int-pkt (pkt int-pkt &optional n-half-words) (cond ((null n-half-words) (copy-array-contents pkt int-pkt)) (t (copy-array-portion pkt 0 n-half-words int-pkt 0 n-half-words))) int-pkt) (defvar *ip-stats* nil) (defmacro defstat (name initial-value) `(progn (defvar ,name ,initial-value) (when (not (assq ',name *ip-stats*)) (setq *ip-stats* (append *ip-stats* (list (cons ',name ',initial-value))))))) (defun stats () (dolist (s *ip-stats*) (when (numberp (symbol-value (car s))) (format t "~&~10d ~s" (symbol-value (car s)) (car s)))) (dolist (s *ip-stats*) (when (not (numberp (symbol-value (car s)))) (format t "~&~s ~s" (car s) (symbol-value (car s)))))) (defun reset-stats () (dolist (s *ip-stats*) (set (car s) (cdr s)))) (defun reverse-internet-address (adr) (+ (dpb (ldb (byte 8 0) adr) (byte 8 24.) 0) (dpb (ldb (byte 8 8) adr) (byte 8 16.) 0) (dpb (ldb (byte 8 16.) adr) (byte 8 8) 0) (dpb (ldb (byte 8 24.) adr) (byte 8 0) 0))) (defun setup-my-internet-address () (when (null (send si:local-host :network-address :internet)) (let ((proposed-address #x64000044)) (loop until (null (si:get-host-from-address proposed-address :internet)) do (incf proposed-address)) (nconc (symeval-in-instance si:local-host 'si:alist-elem) (list :internet (list proposed-address)))))) (setup-my-internet-address) (defconst my-internet-address (send si:local-host :network-address :internet)) (defmacro ih-version (ip) `(ldb (byte 4 4) (aref ,ip 0))) (defmacro ih-internet-header-length (ip) `(ldb (byte 4 0) (aref ,ip 0))) (defmacro ih-type-of-service (ip) `(ldb (byte 8 8) (aref ,ip 0))) (defun ih-length (ip) "In bytes" (dpb (ldb (byte 8 0) (aref ip 1)) (byte 8 8) (ldb (byte 8 8) (aref ip 1)))) (defun set-ih-length (ip val) (aset (dpb (ldb (byte 8 0) val) (byte 8 8) (ldb (byte 8 8) val)) ip 1) val) (defsetf ih-length set-ih-length) (defmacro ih-identification (ip) `(aref ,ip 2)) (defmacro ih-flags (ip) `(ldb (byte 3 13.) (aref ,ip 3))) (defmacro ih-fragment-offset (ip) `(ldb (byte 13. 0) (aref ,ip 3))) (defmacro ih-time-to-live (ip) `(ldb (byte 8 0) (aref ,ip 4))) (defmacro ih-protocol (ip) `(ldb (byte 8 8) (aref ,ip 4))) (defmacro ih-header-checksum (ip) `(aref ,ip 5)) (defun ih-source-address (ip) (+ (dpb (ldb (byte 8 0) (aref ip 6)) (byte 8 24.) 0) (dpb (ldb (byte 8 8) (aref ip 6)) (byte 8 16.) 0) (dpb (ldb (byte 8 0) (aref ip 7)) (byte 8 8) 0) (dpb (ldb (byte 8 8) (aref ip 7)) (byte 8 0) 0))) (defun set-ih-source-address (ip val) (aset (dpb (ldb (byte 8 16.) val) (byte 8 8) (ldb (byte 8 24.) val)) ip 6) (aset (dpb (ldb (byte 8 0) val) (byte 8 8) (ldb (byte 8 8) val)) ip 7) val) (defsetf ih-source-address set-ih-source-address) (defun ih-dest-address (ip) (+ (dpb (ldb (byte 8 0) (aref ip 8)) (byte 8 24.) 0) (dpb (ldb (byte 8 8) (aref ip 8)) (byte 8 16.) 0) (dpb (ldb (byte 8 0) (aref ip 9)) (byte 8 8) 0) (dpb (ldb (byte 8 8) (aref ip 9)) (byte 8 0) 0))) (defun set-ih-dest-address (ip val) (aset (dpb (ldb (byte 8 16.) val) (byte 8 8) (ldb (byte 8 24.) val)) ip 8) (aset (dpb (ldb (byte 8 0) val) (byte 8 8) (ldb (byte 8 8) val)) ip 9) val) (defsetf ih-dest-address set-ih-dest-address) (defmacro ih-sub-header-offset (ip) `(* (ih-internet-header-length ,ip) 2)) (defun describe-ih (ih) (format t "~&version: ~s" (ih-version ih)) (format t "~&header-length: ~s" (ih-internet-header-length ih)) (format t "~&type-of-service: ~s" (ih-type-of-service ih)) (format t "~&length: ~s" (ih-length ih)) (format t "~&identification: ~s" (ih-identification ih)) (format t "~&flags: ~s" (ih-flags ih)) (format t "~&fragment-offset: ~s" (ih-fragment-offset ih)) (format t "~&time-to-live: ~s" (ih-time-to-live ih)) (format t "~&protocol: ~s" (ih-protocol ih)) (format t "~&header-checksum: ~s" (ih-header-checksum ih)) (format t "~&source-address: ~16r ~s" (ih-source-address ih) (si:get-host-from-address (ih-source-address ih) :internet)) (format t "~&dest-address: ~16r ~s" (ih-dest-address ih) (si:get-host-from-address (ih-dest-address ih) :internet)) (cond ((= (ih-protocol ih) 6) (describe-tcp ih)) (t (format t "~&rest of data:") (dotimes (i (- (ceiling (ih-length ih) 2) (* (ih-internet-header-length ih) 2))) (if (zerop (ldb (byte 3 0) i)) (format t "~&")) (format t "~16,4,'0r " (aref ih (+ i (* (ih-internet-header-length ih) 2))))))) ) (defmacro icmp-type (ip) `(ldb (byte 8 0) (aref ,ip (* (ih-internet-header-length ,ip) 2)))) (defmacro icmp-code (ip) `(ldb (byte 8 8) (aref ,ip (* (ih-internet-header-length ,ip) 2)))) (defmacro icmp-checksum (ip) `(aref ,ip (+ (* (ih-internet-header-length ,ip) 2) 1))) (defmacro icmp-echo-identifier (ip) `(aref ,ip (+ (* (ih-internet-header-length ,ip) 2) 2))) (defmacro icmp-echo-sequence-number (ip) `(aref ,ip (+ (* (ih-internet-header-length ,ip) 2) 3))) (defun icmp-echo-data-offset-in-halfwords (ip) (+ (* (ih-internet-header-length ip) 2) 4)) (defun icmp-echo-data-offset-in-bytes (ip) (+ (* (ih-internet-header-length ip) 4) 8)) (defun icmp-echo-data-length (ip) (- (ih-length ip) (icmp-echo-data-offset-in-bytes ip))) (defun icmp-echo-data-string (ip) (make-array (icmp-echo-data-length ip) :type art-string :displaced-to ip :displaced-index-offset (icmp-echo-data-offset-in-bytes ip))) (defmacro swap-two-bytes (w) `(dpb (ldb (byte 8 0) ,w) (byte 8 8) (ldb (byte 8 8) ,w))) (defmacro tcp-source-port (ip) `(swap-two-bytes (aref ,ip (ih-sub-header-offset ,ip)))) (defsetf tcp-source-port (ip) (val) `(aset (swap-two-bytes ,val) ,ip (ih-sub-header-offset ,ip))) (defmacro tcp-dest-port (ip) `(swap-two-bytes (aref ,ip (+ (ih-sub-header-offset ,ip) 1)))) (defsetf tcp-dest-port (ip) (val) `(aset (swap-two-bytes ,val) ,ip (+ (ih-sub-header-offset ,ip) 1))) (defun tcp-sequence-number (ip) (let ((offset (ih-sub-header-offset ip))) (+ (dpb (ldb (byte 8 0) (aref ip (+ offset 2))) (byte 8 24.) 0) (dpb (ldb (byte 8 8) (aref ip (+ offset 2))) (byte 8 16.) 0) (dpb (ldb (byte 8 0) (aref ip (+ offset 3))) (byte 8 8) 0) (dpb (ldb (byte 8 8) (aref ip (+ offset 3))) (byte 8 0) 0)))) (defun set-tcp-sequence-number (ip val) (let ((offset (ih-sub-header-offset ip))) (aset (dpb (ldb (byte 8 16.) val) (byte 8 8) (ldb (byte 8 24.) val)) ip (+ offset 2)) (aset (dpb (ldb (byte 8 0) val) (byte 8 8) (ldb (byte 8 8) val)) ip (+ offset 3))) val) (defsetf tcp-sequence-number set-tcp-sequence-number) (defun tcp-ack-number (ip) (let ((offset (ih-sub-header-offset ip))) (+ (dpb (ldb (byte 8 0) (aref ip (+ offset 4))) (byte 8 24.) 0) (dpb (ldb (byte 8 8) (aref ip (+ offset 4))) (byte 8 16.) 0) (dpb (ldb (byte 8 0) (aref ip (+ offset 5))) (byte 8 8) 0) (dpb (ldb (byte 8 8) (aref ip (+ offset 5))) (byte 8 0) 0)))) (defun set-tcp-ack-number (ip val) (let ((offset (ih-sub-header-offset ip))) (aset (dpb (ldb (byte 8 16.) val) (byte 8 8) (ldb (byte 8 24.) val)) ip (+ offset 4)) (aset (dpb (ldb (byte 8 0) val) (byte 8 8) (ldb (byte 8 8) val)) ip (+ offset 5))) val) (defsetf tcp-ack-number set-tcp-ack-number) (defmacro tcp-data-offset (ip) "In 32 bit words" `(ldb (byte 4 4) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-urg-bit (ip) `(ldb (byte 1 13.) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-urg-bit-p (ip) `(not (zerop (tcp-urg-bit ,ip)))) (defmacro tcp-ack-bit (ip) `(ldb (byte 1 12.) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-ack-bit-p (ip) `(not (zerop (tcp-ack-bit ,ip)))) (defmacro tcp-psh-bit (ip) `(ldb (byte 1 11.) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-psh-bit-p (ip) `(not (zerop (tcp-psh-bit ,ip)))) (defmacro tcp-rst-bit (ip) `(ldb (byte 1 10.) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-rst-bit-p (ip) `(not (zerop (tcp-rst-bit ,ip)))) (defmacro tcp-syn-bit (ip) `(ldb (byte 1 9) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-syn-bit-p (ip) `(not (zerop (tcp-syn-bit ,ip)))) (defmacro tcp-fin-bit (ip) `(ldb (byte 1 8) (aref ,ip (+ (ih-sub-header-offset ,ip) 6)))) (defmacro tcp-fin-bit-p (ip) `(not (zerop (tcp-fin-bit ,ip)))) (defmacro tcp-window (ip) `(swap-two-bytes (aref ,ip (+ (ih-sub-header-offset ,ip) 7)))) (defsetf tcp-window (ip) (val) `(aset (swap-two-bytes ,val) ,ip (+ (ih-sub-header-offset ,ip) 7))) (defmacro tcp-checksum (ip) `(aref ,ip (+ (ih-sub-header-offset ,ip) 8))) (defmacro tcp-urgent-pointer (ip) `(swap-two-bytes (aref ,ip (+ (ih-sub-header-offset ,ip) 9)))) (defsetf tcp-urgent-pointer (ip) (val) `(aset (swap-two-bytes ,val) ,ip (+ (ih-sub-header-offset ,ip) 9))) (defun tcp-data-bytes (ip) (- (ih-length ip) (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)))) (defselect ((pkt si:named-structure-invoke)) (:print-self (pkt stream ignore ignore) (si:printing-random-object (pkt stream :type) (case (ih-protocol pkt) (6 (format stream "TCP Ack ~d Wnd ~d Seq [~d,~d) Len ~d" (tcp-ack-number pkt) (tcp-window pkt) (tcp-sequence-number pkt) (+ (tcp-sequence-number pkt) (compute-seg.len pkt)) (compute-seg.len pkt))) (t (format stream "Protocol ~d" (ih-protocol pkt))) ))) (:which-operations (ignore) `(:print-self))) (defun store-header-checksum (ip &aux (sum 0)) (setf (ih-header-checksum ip) 0) (dotimes (i (* 2 (ih-internet-header-length ip))) (incf sum (aref ip i)) (if (ldb-test (byte 1 16.) sum) (setq sum (1+ (ldb (byte 16. 0) sum))))) (setf (ih-header-checksum ip) (logxor #o177777 sum))) (defun check-header-checksum (ip &aux (sum 0)) (dotimes (i (* 2 (ih-internet-header-length ip))) (incf sum (aref ip i)) (if (ldb-test (byte 1 16.) sum) (setq sum (1+ (ldb (byte 16. 0) sum))))) (values (= sum 177777) sum)) (defun store-icmp-checksum (ip &aux (sum 0)) (macrolet ((add-word (word) `(progn (incf sum ,word) (if (ldb-test (byte 1 16.) sum) (setq sum (1+ (ldb (byte 16. 0) sum))))))) (when (oddp (ih-length ip)) (setf (ldb (byte 8 0) (aref ip (floor (ih-length ip) 2))) 0)) (setf (icmp-checksum ip) 0) (let ((offset (* 2 (ih-internet-header-length ip)))) (dotimes (i (- (ceiling (ih-length ip) 2) offset)) (add-word (aref ip (+ i offset))))) (setf (icmp-checksum ip) (logxor #o177777 sum)))) (defun check-icmp-checksum (ip &aux (sum 0)) (let ((offset (* 2 (ih-internet-header-length ip)))) (dotimes (i (- (ceiling (ih-length ip) 2) offset)) (incf sum (aref ip (+ i offset))) (if (ldb-test (byte 1 16.) sum) (setq sum (1+ (ldb (byte 16. 0) sum))))) (values (= sum 177777) sum))) (defun store-tcp-checksum (ip &aux (sum 0)) (macrolet ((add-word (word) `(progn (incf sum ,word) (if (ldb-test (byte 1 16.) sum) (setq sum (1+ (ldb (byte 16. 0) sum))))))) (setf (tcp-checksum ip) 0) (add-word (aref ip 6)) ;ih-source-address (add-word (aref ip 7)) ;ih-source-address (add-word (aref ip 8)) ;ih-dest-address (add-word (aref ip 9)) ;ih-dest-address (add-word (dpb (ih-protocol ip) (byte 8 8) 0)) (let ((len (- (ih-length ip) (* 4 (ih-internet-header-length ip))))) (add-word (dpb (ldb (byte 8 0) len) (byte 8 8) (ldb (byte 8 8) len)))) (let ((offset (* 2 (ih-internet-header-length ip)))) (dotimes (i (- (ceiling (ih-length ip) 2) offset)) (add-word (aref ip (+ i offset))))) (setf (tcp-checksum ip) (logxor #o177777 sum)))) (defun check-tcp-checksum (ip &aux (sum 0)) (labels ((add-word (word) (incf sum word) (if (ldb-test (byte 1 16.) sum) (setq sum (1+ (ldb (byte 16. 0) sum)))))) (add-word (ldb (byte 16. 0) (ih-source-address ip))) (add-word (ldb (byte 16. 16.) (ih-source-address ip))) (add-word (ldb (byte 16. 0) (ih-dest-address ip))) (add-word (ldb (byte 16. 16.) (ih-dest-address ip))) (add-word (- (ih-length ip) (* 4 (ih-internet-header-length ip)))) (add-word (ih-protocol ip)) (let ((offset (* 2 (ih-internet-header-length ip)))) (dotimes (i (- (ceiling (ih-length ip) 2) offset)) (add-word (aref ip (+ i offset))))) (cond ((zerop (logxor #o177777 sum)) "Good") (t "Bad")))) (defvar *ip-pkts* nil) (defstat *ip-received-ip-pkts* 0) (defun receive-ip-pkt (int-pkt) (incf *ip-received-ip-pkts*) (when (< (length *ip-pkts*) 10.) (let ((pkt (get-pkt))) (copy-array-contents int-pkt pkt) (setq *ip-pkts* (nconc *ip-pkts* (list pkt))))) (chaos:free-int-pkt int-pkt)) (defun install-ip-receiver () (setq chaos:ethernet-pkt-handler-alist (delq (assq ethernet:ip-ethernet-type chaos:ethernet-pkt-handler-alist) chaos:ethernet-pkt-handler-alist)) (push (cons ethernet:ip-ethernet-type 'receive-ip-pkt) chaos:ethernet-pkt-handler-alist)) (defvar *ip-protocols* '((1 . ip-receive-icmp) (6 . ip-receive-tcp))) (defstat *ip-unknown-protocols* nil) (defstat *ip-received-pkts* 0) (defun ip-receive-process-top-level () (let ((*package* (symbol-package 'foo))) (do-forever (process-wait "IP Pkt" #'(lambda () *ip-pkts*)) (let (ip) (without-interrupts (setq ip (pop *ip-pkts*))) (when ip ;(record ip :received) (incf *ip-received-pkts*) (let ((receive-function (assq (ih-protocol ip) *ip-protocols*))) (cond ((null receive-function) (when (not (memq (ih-protocol ip) *ip-unknown-protocols*)) (push (ih-protocol ip) *ip-unknown-protocols*)) (free-pkt ip) ) (t (funcall (cdr receive-function) ip))))))))) (defvar *ip-receiver* nil) (defun start-ip-receiver () (when (null *ip-receiver*) (setq *ip-receiver* (make-process "IP Receiver"))) (send *ip-receiver* :preset 'ip-receive-process-top-level) (process-reset-and-enable *ip-receiver*)) (defconst *tcp-background-quantum* 30.) (defun ip-background-top-level () (let ((*package* (symbol-package 'foo)) time) (do-forever (setq time (time)) (dolist (c *tcp-conns*) (send c :retransmit) (send c :send-ack-if-needed time)) (let ((next-time (time-increment time *tcp-background-quantum*))) (process-wait "IP Background" #'(lambda () (time-lessp next-time (time)))))))) (defvar *ip-background* nil) (defun start-ip-background () (when (null *ip-background*) (setq *ip-background* (make-process "IP Background"))) (send *ip-background* :preset 'ip-background-top-level) (process-reset-and-enable *ip-background*)) (defstruct (ip-icmp-request (:type :named-array)) type code identifier sequence-number pkt) (defvar *ip-icmp-requests* nil) (defstat *ip-icmp-random-replies* 0) (defun ip-receive-icmp (ip) (case (icmp-type ip) ((0 8 13. 14. 15. 16.) (dolist (request *ip-icmp-requests*) (when (and (= (icmp-type ip) (ip-icmp-request-type request)) (= (icmp-echo-identifier ip) (ip-icmp-request-identifier request)) (= (icmp-echo-sequence-number ip) (ip-icmp-request-sequence-number request))) (setf (ip-icmp-request-pkt request) ip) (setq *ip-icmp-requests* (delq request *ip-icmp-requests*)) (return-from ip-receive-icmp nil))) (free-pkt ip) (incf *ip-icmp-random-replies*)) (t (free-pkt ip) (incf *ip-icmp-random-replies*)))) (defvar *default-identification* 0) (defun get-ip-int-pkt () (let ((int-pkt (chaos:allocate-int-pkt))) (setf (ih-version int-pkt) 4) (setf (ih-internet-header-length int-pkt) 5) (setf (ih-length int-pkt) 10.) (setf (ih-type-of-service int-pkt) 0) (setf (ih-identification int-pkt) (incf *default-identification*)) (setf (ih-flags int-pkt) 0) (setf (ih-fragment-offset int-pkt) 0) (setf (ih-time-to-live int-pkt) 60.) (setf (ih-source-address int-pkt) my-internet-address) int-pkt)) (defun get-ip-pkt () (let ((ip (get-pkt))) (setf (ih-version ip) 4) (setf (ih-internet-header-length ip) 5) (setf (ih-length ip) 10.) (setf (ih-type-of-service ip) 0) (setf (ih-identification ip) (incf *default-identification*)) (setf (ih-flags ip) 0) (setf (ih-fragment-offset ip) 0) (setf (ih-time-to-live ip) 60.) (setf (ih-source-address ip) my-internet-address) ip)) (defvar *max-ack* 0) (defun send-ip-int-pkt (int-pkt) ;(recored int-pkt :transmited) (let ((dest-ether-address (ethernet:get-ethernet-address (reverse-internet-address (ih-dest-address int-pkt)) :ip))) (cond ((null dest-ether-address) (chaos:free-int-pkt int-pkt)) (t (setq *max-ack* (max *max-ack* (tcp-ack-number int-pkt))) (ethernet:send-int-pkt-via-ethernet int-pkt ethernet:my-ethernet-address dest-ether-address ethernet:ip-ethernet-type (max (ceiling (ih-length int-pkt) 2) 100.)))))) (defstat *tcp-pkts-sent* 0) (defun send-ip-pkt (ip) (incf *tcp-pkts-sent*) (send-ip-int-pkt (copy-to-int-pkt ip (chaos:allocate-int-pkt)))) (defun dot-to-address (string) (setq string (string string)) (let* ((dot1 (string-search-char #\. string 0)) (dot2 (string-search-char #\. string dot1)) (dot3 (string-search-char #\. string dot2))) (when (or (null dot1) (null dot2) (null dot3)) (ferror nil "bad address")) (+ (dpb (parse-integer string :start 0 :end dot1 :radix 10.) (byte 8 24.) 0) (dpb (parse-integer string :start (1+ dot1) :end dot2 :radix 10. :junk-allowed t) (byte 8 16.) 0) (dpb (parse-integer string :start (1+ dot2) :end dot3 :radix 10. :junk-allowed t) (byte 8 8.) 0) (dpb (parse-integer string :start (1+ dot3) :radix 10. :junk-allowed t) (byte 8 0.) 0)))) (defun make-icmp-echo-request (dest string identifier sequence-number) (when (not (numberp dest)) (setq dest (send (si:parse-host dest) :network-address :internet))) (let ((ip (get-ip-pkt))) (setf (ih-length ip) (+ (* 4 (ih-internet-header-length ip)) 8 (string-length string))) (setf (ih-protocol ip) 1) (setf (ih-dest-address ip) dest) (setf (icmp-type ip) 8) (setf (icmp-code ip) 0) (setf (icmp-echo-identifier ip) identifier) (setf (icmp-echo-sequence-number ip) sequence-number) (copy-array-contents string (make-array (string-length string) :type :art-8b :displaced-to ip :displaced-index-offset (icmp-echo-data-offset-in-bytes ip))) (store-icmp-checksum ip) (store-header-checksum ip) ip)) (defun icmp-map-request-type-to-reply-type (code) (ecase code (8 0) (13. 14.) (15. 16.) )) (defun icmp-request (ip &optional (timeout-in-60ths 60.)) "returns NIL if no response within timeout" (let ((request (make-ip-icmp-request))) (unwind-protect (progn (setf (ip-icmp-request-type request) (icmp-map-request-type-to-reply-type (icmp-type ip))) (setf (ip-icmp-request-code request) (icmp-code ip)) (setf (ip-icmp-request-identifier request) (icmp-echo-identifier ip)) (setf (ip-icmp-request-sequence-number request) (icmp-echo-sequence-number ip)) (setf (ip-icmp-request-pkt request) nil) (without-interrupts (push request *ip-icmp-requests*)) (send-ip-pkt ip) (process-wait-with-timeout "ICMP Reply" timeout-in-60ths #'(lambda () (ip-icmp-request-pkt request))) (ip-icmp-request-pkt request)) (without-interrupts (setq *ip-icmp-requests* (delq request *ip-icmp-requests*)))))) (defun send-echo-request (dest string) (let ((ip (make-icmp-echo-request dest string 123 321))) (dotimes (retries 5) (let ((response (icmp-request ip))) (when response (free-pkt ip) (return-from send-echo-request response)) (ethernet:flush-ethernet-address (reverse-internet-address (ih-dest-address ip)) :ip))) (free-pkt ip) (ferror nil "no response"))) (defun ping (host) (let ((pkt (send-echo-request host "ping"))) (cond ((null pkt) "No response") (t (free-pkt pkt) t)))) (defun 32-bit-< (a b) (ldb-test (byte 1 31.) (- a b))) (defun 32-bit-<= (a b) (or (= a b) (ldb-test (byte 1 31.) (- a b)))) (defun 32-bit->= (a b) (not (ldb-test (byte 1 31.) (- a b)))) (defun 32-bit-> (a b) (and (not (= a b)) (not (ldb-test (byte 1 31.) (- a b))))) (defun 32-bit-plus (a b) (logand #o37777777777 (+ a b))) (defun 32-bit-minus (a b) (logand #o37777777777 (- (+ a #o40000000000) b))) (defun 32-bit-max (a b) (if (32-bit-> a b) a b)) (defun 32-bit-min (a b) (if (32-bit-< a b) a b)) (defflavor tcp-conn ((lock nil) passive-open-p state source-port dest-port snd.una snd.nxt snd.wnd snd.up snd.wl1 snd.wl2 iss rcv.nxt rcv.wnd (rcv.pre.wnd 0) rcv.initial.wnd rcv.up irs transmit-list receive-list dest-address urgent-mode last-ack-sent time-last-ack-sent ) () :settable-instance-variables) (defvar *tcp-conns* nil) (defstat *unmatched-tcp-pkts* 0) (defun ip-receive-tcp (new-pkt) (dolist (conn *tcp-conns*) (when (and (= (tcp-source-port new-pkt) (send conn :dest-port)) (= (tcp-dest-port new-pkt) (send conn :source-port))) (send conn :receive-pkt new-pkt) (return-from ip-receive-tcp nil))) (incf *unmatched-tcp-pkts*) (let ((ip (chaos:allocate-int-pkt))) (setf (ih-protocol ip) 6) (setf (ih-dest-address ip) (ih-source-address new-pkt)) (array-initialize ip 0 (ih-sub-header-offset ip) (+ (ih-sub-header-offset ip) 10.)) (setf (tcp-source-port ip) (tcp-dest-port new-pkt)) (setf (tcp-dest-port ip) (tcp-source-port new-pkt)) (setf (tcp-data-offset ip) 5) (setf (tcp-sequence-number ip) (tcp-ack-number new-pkt)) (setf (tcp-ack-number ip) (tcp-sequence-number new-pkt)) (setf (tcp-ack-bit ip) 1) (setf (tcp-rst-bit ip) 1) (setf (ih-length ip) (+ (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)))) (store-tcp-checksum ip) (store-header-checksum ip) (send-ip-int-pkt ip) (free-pkt new-pkt))) (defmethod (tcp-conn :pkt-in-receive-window-p) (ip) (let* ((seg.seq (tcp-sequence-number ip)) (seg.len (compute-seg.len ip)) (seg.seq+seg.len-1 (32-bit-minus (32-bit-plus seg.seq seg.len) 1)) (rcv.nxt+rcv.wnd (32-bit-plus rcv.nxt rcv.wnd))) (when (< seg.len 0) (ferror nil "negative segment length")) (cond ((= seg.len 0) (cond ((= rcv.wnd 0) (= seg.seq rcv.nxt)) (t (and (32-bit-<= rcv.nxt seg.seq) (32-bit-< seg.seq rcv.nxt+rcv.wnd))))) (t (cond ((= rcv.wnd 0) nil) (t (or (and (32-bit-<= rcv.nxt seg.seq) (32-bit-< seg.seq rcv.nxt+rcv.wnd)) (and (32-bit-<= rcv.nxt seg.seq+seg.len-1) (32-bit-< seg.seq+seg.len-1 rcv.nxt+rcv.wnd))))))))) (defun compute-seg.len (ip) (+ (tcp-data-bytes ip) (tcp-syn-bit ip) (tcp-fin-bit ip))) (defmethod (tcp-conn :check-receive-list) () (let ((real-list (sort (copylist receive-list) #'(lambda (x y) (< (tcp-sequence-number x) (tcp-sequence-number y)))))) (when (not (equal receive-list real-list)) (ferror nil "foo")))) (defmethod (tcp-conn :insert-pkt) (ip) ;;the receive-list is sorted by increasing seq, no dup seqs (when (not (eq lock current-process)) (ferror nil "must have lock")) (let ((seg.seq (tcp-sequence-number ip)) (seg.len (compute-seg.len ip))) (cond ((null receive-list) (setq receive-list (list ip))) ((32-bit-< seg.seq (tcp-sequence-number (car receive-list))) (setq receive-list (cons ip receive-list))) ((= seg.seq (tcp-sequence-number (car receive-list))) (cond ((32-bit-<= seg.len (compute-seg.len (car receive-list))) (free-pkt ip)) (t (free-pkt (car receive-list)) (setf (car receive-list) ip)))) ((or (null (cdr receive-list)) (32-bit-< seg.seq (tcp-sequence-number (cadr receive-list)))) (setf (cdr receive-list) (cons ip (cdr receive-list)))) ((= seg.seq (tcp-sequence-number (cadr receive-list))) (cond ((32-bit-<= seg.len (compute-seg.len (cadr receive-list))) (free-pkt ip)) (t (free-pkt (cadr receive-list)) (setf (cadr receive-list) ip)))) (t (do ((pkt0 receive-list (cdr pkt0)) (pkt1 (cdr receive-list) (cdr pkt1)) (pkt2 (cddr receive-list) (cdr pkt2))) (()) (cond ((or (null pkt2) (32-bit-< seg.seq (tcp-sequence-number (car pkt2)))) (cond ((= seg.seq (tcp-sequence-number (car pkt1))) (cond ((32-bit-<= seg.len (compute-seg.len (car pkt1))) (free-pkt ip) (return nil)) (t (free-pkt (car pkt1)) (setf (car pkt1) ip) (return nil)))) (t (setf (cdr pkt1) (cons ip pkt2)) (return nil)))))))))) (defmethod (tcp-conn :insert-pkt) (ip) ;;the receive-list is sorted by increasing seq, no dup seqs (when (not (eq lock current-process)) (ferror nil "must have lock")) (let ((seg.seq (tcp-sequence-number ip)) (seg.len (compute-seg.len ip))) (setq receive-list (do ((pkts receive-list (cdr pkts)) result ) ((null pkts) (nconc (nreverse result) (list ip))) (let ((this-seq (tcp-sequence-number (car pkts)))) (cond ((32-bit-< this-seq seg.seq) (push (car pkts) result)) ((= this-seq seg.seq) (cond ((32-bit-< seg.len (compute-seg.len (car pkts))) (free-pkt ip) (return receive-list)) (t (free-pkt (car pkts)) (return (nconc (nreverse result) (list ip) (cdr pkts)))))) (t (return (nconc (nreverse result) (list ip (car pkts)) (cdr pkts)))))))))) (defmethod (tcp-conn :reset-rcv.nxt-and-rcv.wnd) () (when (not (eq lock current-process)) (ferror nil "must have lock")) (do ((pkts receive-list (cdr pkts))) ((null pkts)) (let* ((seg.seq (tcp-sequence-number (car pkts))) (seg.len (compute-seg.len (car pkts))) (seg.end (32-bit-plus seg.seq seg.len))) (cond ((32-bit-<= seg.seq rcv.nxt) (when (32-bit-< rcv.nxt seg.end) (let ((delta (32-bit-min (32-bit-minus seg.end rcv.nxt) rcv.wnd))) (setq rcv.wnd (32-bit-minus rcv.wnd delta)) (let ((old rcv.nxt)) (setq rcv.nxt (32-bit-plus rcv.nxt delta)) (when (32-bit-< rcv.nxt old) (ferror nil "rcv.nxt backed up")) (when (> (32-bit-minus rcv.nxt old) seg.len) (ferror nil "rcv.nxt skipped too much"))) ))) (t (return nil)))))) (defmethod (tcp-conn :receive-pkt) (ip) (let ((chaos:reserved-int-pkt nil)) (with-lock (lock) (case state (:listen (send self :receive-pkt-listen ip)) (:syn-sent (send self :receive-pkt-syn-sent ip)) (t (send self :receive-pkt-established ip))))) ) (defvar *seq-offset* 0) (defstat *tcp-rst-pkts-received-in-listen* 0) (defstat *tcp-ack-pkts-received-in-listen* 0) (defstat *tcp-non-syn-non-ack-pkts-recevied-in-listen* 0) (defmethod (tcp-conn :receive-pkt-listen) (ip) (block nil (let ((seg.seq (tcp-sequence-number ip)) (seg.ack (tcp-ack-number ip))) (cond ((tcp-rst-bit-p ip) (incf *tcp-rst-pkts-received-in-listen*) (free-pkt ip) (return nil)) ((tcp-ack-bit-p ip) (send self :send-rst seg.ack 0) (incf *tcp-ack-pkts-received-in-listen*) (free-pkt ip) (return nil)) ((tcp-syn-bit-p ip) (setq dest-port (tcp-source-port ip)) (setq dest-address (ih-source-address ip)) (setq state :syn-received) (setq irs seg.seq) (setq snd.wl1 seg.seq) (setq rcv.nxt irs) (setq receive-list (list ip)) (incf rcv.wnd) ;hack ... syn uses one forever (send self :reset-rcv.nxt-and-rcv.wnd) (setq iss (ldb (byte 24. 0) (time))) (setq snd.nxt (32-bit-plus iss 1)) (setq snd.una iss) (send self :send-ack) (return nil)) (t ;;"can't get here" since ACK should have been on (incf *tcp-non-syn-non-ack-pkts-recevied-in-listen*) (free-pkt ip) (return nil)))))) (defstat *tcp-bad-ack-received-in-syn-sent* 0) (defstat *tcp-rst-received-in-syn-sent* 0) (defstat *tcp-non-syn-received-in-syn-sent* 0) (defmethod (tcp-conn :receive-pkt-syn-sent) (ip) (block nil (let ((seg.ack (tcp-ack-number ip)) (seg.seq (tcp-sequence-number ip)) ) (cond ((and (tcp-ack-bit-p ip) (or (32-bit-<= seg.ack iss) (32-bit-> seg.ack snd.nxt))) (when (not (tcp-rst-bit-p ip)) (send self :send-rst seg.ack 0)) (incf *tcp-bad-ack-received-in-syn-sent*) (free-pkt ip) (return nil)) ((tcp-rst-bit-p ip) (when (tcp-ack-bit-p ip) (setq state :closed)) (incf *tcp-rst-received-in-syn-sent*) (free-pkt ip) (return nil)) ((tcp-syn-bit-p ip) (setq irs seg.seq) (setq snd.wl1 seg.seq) (setq rcv.nxt irs) (setq receive-list (list ip)) (incf rcv.wnd) ;hack ... syn uses up one forever (send self :reset-rcv.nxt-and-rcv.wnd) (when (tcp-ack-bit-p ip) (setq snd.una seg.ack) (send self :trim-transmit-list)) (cond ((32-bit-> snd.una iss) (setq state :established)) (t (setq state :syn-received))) (send self :send-ack)) (t (incf *tcp-non-syn-received-in-syn-sent*) (free-pkt ip) (return nil)))))) ;;;when the user closes, you send FIN, and go to fin-wait-1 ;;;when the ACK for the FIN comes back, change to FIN-WAIT-2 ;;; if you receive FIN before ack of FIN, go to CLOSING ;;;when you receive a FIN, change to TIME-WAIT (defstat *tcp-pkts-not-in-receive-window* 0) (defstat *tcp-rst-received* 0) (defstat *tcp-syn-received-when-established* 0) (defstat *tcp-pkt-without-ack* 0) (defstat *tcp-out-of-range-pkt-in-syn-received* 0) (defstat *tcp-ack-too-high* 0) (defvar *pkt* nil) (defvar *tcp-sequence-numbers* nil) (defmethod (tcp-conn :receive-pkt-established) (ip &aux dont-free) (block nil (let ((seg.seq (tcp-sequence-number ip)) (seg.ack (tcp-ack-number ip)) (seg.wnd (tcp-window ip)) (seg.up (tcp-urgent-pointer ip)) ) (pushnew seg.seq *tcp-sequence-numbers*) (labels ((free-pkt-and-return () (if (null dont-free) (free-pkt ip)) (return nil))) (when (not (send self :pkt-in-receive-window-p ip)) (send self :send-ack) (incf *tcp-pkts-not-in-receive-window*) (when *pkt* (copy-array-contents ip *pkt*) (setq *pkt* nil)) (free-pkt-and-return)) (when (tcp-rst-bit-p ip) (incf *tcp-rst-received*) (ecase state (:syn-received (setq state (if passive-open-p :listen :closed)) (free-pkt-and-return)) ((:established :fin-wait-1 :fin-wait-2 :close-wait :closing :last-ack :time-wait :closed) (setq state :closed) (free-pkt-and-return)))) (when (tcp-syn-bit-p ip) (incf *tcp-syn-received-when-established*) (send self :send-rst seg.ack 0) (setq state :closed) (free-pkt-and-return)) (when (not (tcp-ack-bit-p ip)) (incf *tcp-pkt-without-ack*) (free-pkt-and-return)) (when (eq state :syn-received) (cond ((and (32-bit-<= snd.una seg.ack) (32-bit-<= seg.ack snd.nxt)) (setq state :established)) (t (incf *tcp-out-of-range-pkt-in-syn-received*) (free-pkt-and-return)))) ;;update transmit-list and window based on SEG.ACK (cond ((32-bit-<= seg.ack snd.una) ;;ack too low, but be duplicate ... ignore ) ((32-bit-<= seg.ack snd.nxt) (setq snd.una seg.ack) (send self :trim-transmit-list) (when (or (32-bit-< snd.wl1 seg.seq) (and (= snd.wl1 seg.seq) (32-bit-<= snd.wl2 seg.ack))) (setq snd.wnd seg.wnd) (setq snd.wl1 seg.seq) (setq snd.wl2 seg.ack))) (t (send self :send-ack) (incf *tcp-ack-too-high*) (free-pkt-and-return))) ;;change state based on SEG.ACK (when (memq state '(:closing :last-ack :time-wait)) (case state (:closing (when (= snd.nxt seg.ack) (setq state :time-wait))) (:last-ack (when (= snd.nxt seg.ack) (setq state :closed))) (:time-wait (send self :send-ack) ;;restart 2MSL timeout )) (free-pkt-and-return)) (when (and (eq state :fin-wait-1) (= snd.nxt seg.ack)) (setq state :fin-wait-2)) ;he acks my FIN, but I haven't seen his yet (when (tcp-urg-bit-p ip) (setq rcv.up (32-bit-max rcv.up (32-bit-plus seg.seq seg.up))) (setq urgent-mode t)) (when (memq state '(:established :fin-wait-1 :fin-wait-2)) (setq dont-free t) (send self :insert-pkt ip) (send self :check-receive-list) (send self :reset-rcv.nxt-and-rcv.wnd) ;;later, delay this ;(send self :send-ack) ) (when (tcp-fin-bit-p ip) (ecase state ((:closed :listen :syn-sent)) ((:syn-received :established) (setq state :close-wait)) (:fin-wait-1 (setq state :closing)) (:fin-wait-2 (setq state :time-wait) ;;start timer ) ((:close-wait :closing :last-ack)) (:time-wait ;;restart timer ))) (free-pkt-and-return))))) (defmethod (tcp-conn :trim-transmit-list) () (do () ((or (null transmit-list) (32-bit-> (32-bit-plus (tcp-sequence-number (car transmit-list)) (compute-seg.len (car transmit-list))) snd.una))) (free-pkt (pop transmit-list)))) (defmethod (tcp-conn :make-pkt) () (let ((ip (get-ip-pkt))) (setf (ih-protocol ip) 6) (setf (ih-dest-address ip) dest-address) (array-initialize ip 0 (ih-sub-header-offset ip) (+ (ih-sub-header-offset ip) 10.)) (setf (tcp-source-port ip) source-port) (setf (tcp-dest-port ip) dest-port) (setf (tcp-data-offset ip) 5) ip)) (defstat *tcp-acks-sent* 0) (defmethod (tcp-conn :send-ack) () (with-lock (lock) (incf *tcp-acks-sent*) (let ((ip (send self :make-pkt))) (cond ((eq state :syn-sent) (setf (tcp-sequence-number ip) iss) (setf (tcp-syn-bit ip) 1)) (t (setf (tcp-sequence-number ip) snd.nxt))) (setf (tcp-ack-number ip) rcv.nxt) (setf (tcp-ack-bit ip) 1) (setf (tcp-window ip) rcv.wnd) (setf (ih-length ip) (+ (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)))) (store-tcp-checksum ip) (store-header-checksum ip) (send-ip-pkt ip) (free-pkt ip) (setq last-ack-sent rcv.nxt) (setq time-last-ack-sent (time)) ))) (defconst *time-between-acks-on-idle-conn* (* 60. 10.)) (defconst *time-between-acks-on-active-conn* (* 60. 1/2)) (defmethod (tcp-conn :send-ack-if-needed) (current-time) (cond ((= last-ack-sent rcv.nxt) (when (time-lessp (time-increment time-last-ack-sent *time-between-acks-on-idle-conn*) current-time) (send self :send-ack))) (t (when (time-lessp (time-increment time-last-ack-sent *time-between-acks-on-active-conn*) current-time) (send self :send-ack))))) (defstat *tcp-rsts-sent* 0) (defmethod (tcp-conn :send-rst) (seq ack) (incf *tcp-rsts-sent*) (let ((ip (send self :make-pkt))) (setf (tcp-sequence-number ip) seq) (setf (tcp-ack-number ip) ack) (setf (tcp-ack-bit ip) 1) (setf (tcp-rst-bit ip) 1) (setf (ih-length ip) (+ (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)))) (store-tcp-checksum ip) (store-header-checksum ip) (send-ip-pkt ip) (free-pkt ip))) (defmethod (tcp-conn :send-pkt) (ip nbytes) (with-lock (lock) (setf (tcp-sequence-number ip) snd.nxt) (setf (tcp-ack-number ip) rcv.nxt) (setf (tcp-ack-bit ip) 1) (setf (tcp-window ip) rcv.wnd) (setf (ih-length ip) (+ (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)) nbytes)) (store-tcp-checksum ip) (store-header-checksum ip) (setq transmit-list (nconc transmit-list (list ip))) (incf snd.nxt (compute-seg.len ip)))) (defmethod (tcp-conn :make-syn-pkt) () (let ((ip (send self :make-pkt))) (setf (tcp-sequence-number ip) iss) (setf (tcp-ack-number ip) 0) (setf (tcp-ack-bit ip) 0) (setf (tcp-window ip) rcv.wnd) (setf (tcp-syn-bit ip) 1) (setf (ih-length ip) (+ (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)))) (store-tcp-checksum ip) (store-header-checksum ip) (setq transmit-list (nconc transmit-list (list ip))))) (defmethod (tcp-conn :retransmit) () (when (not (memq state '(:closed))) (with-lock (lock) (dolist (ip transmit-list) (let ((int-pkt (chaos:allocate-int-pkt nil))) (cond ((null int-pkt) (return nil)) (t (incf *tcp-pkts-sent*) (send-ip-int-pkt (copy-to-int-pkt ip int-pkt))))))))) (defun describe-tcp (ip) (format t "~&TCP") (format t "~&Source port ~d." (tcp-source-port ip)) (format t "~&Dest port ~d." (tcp-dest-port ip)) (format t "~&Seq ~d." (tcp-sequence-number ip)) (format t "~&Ack ~d." (tcp-ack-number ip)) (format t "~&Data offset ~d." (tcp-data-offset ip)) (format t "~&Urg ~d Ack ~d Psh ~d Rst ~d Syn ~d Fin ~d" (tcp-urg-bit ip) (tcp-ack-bit ip) (tcp-psh-bit ip) (tcp-rst-bit ip) (tcp-syn-bit ip) (tcp-fin-bit ip)) (format t "~&Wnd ~d" (tcp-window ip)) (format t "~&Checksum ~d" (tcp-checksum ip)) (format t "~&Urg ptr ~d" (tcp-urgent-pointer ip))) (defun reset () (without-interrupts (setq *pkts* nil) (setq *tcp-conns* nil) (initialize))) ethernet: (defun send-addr-pkt (address protocol &optional his-ether-address (on-behalf-of chaos:my-address)) (incf addr-pkts-transmitted) (let* ((chaos:reserved-int-pkt nil) ;don't allocate this on if coming from scheduler (int-pkt (chaos:allocate-int-pkt nil)) pkt) (cond ((not (null int-pkt)) (setq pkt (get-addr-pkt int-pkt)) (selectq protocol (:chaos (setf (ar-hardware pkt) arp-ethernet-hardware) (setf (ar-protocol pkt) chaos-ethernet-type) (setf (ar-hardware-adr-length pkt) 6) (setf (ar-protocol-adr-length pkt) 2) (setf (ar-opcode pkt) (if his-ether-address arp-reply arp-request)) (setf (ar-hardware-sender pkt) my-ethernet-address) (setf (ar-protocol-sender pkt) on-behalf-of) (setf (ar-hardware-target pkt) (or his-ether-address 0)) (setf (ar-protocol-target pkt) address)) (:ip (setf (ar-hardware pkt) arp-ethernet-hardware) (setf (ar-protocol pkt) ip-ethernet-type) (setf (ar-hardware-adr-length pkt) 6) (setf (ar-protocol-adr-length pkt) 4) (setf (ar-opcode pkt) (if his-ether-address arp-reply arp-request)) (setf (ar-hardware-sender pkt) my-ethernet-address) (setf (ar-protocol-sender pkt) ;(logior on-behalf-of #x59000000) i:(reverse-internet-address my-internet-address) ;#x34000064 ;#x64000033 ) (setf (ar-hardware-target pkt) (or his-ether-address 0)) (setf (ar-protocol-target pkt) address)) (t (ferror nil "unknown protocol"))) (send-int-pkt-via-ethernet (ar-int-pkt pkt) my-ethernet-address (or his-ether-address -1) address-resolution-type (floor (ar-pkt-length pkt) 2)) (free-addr-pkt pkt))))) ethernet: (defun addr-res-pkt-for-me-p (pkt) (or (= (ar-protocol-target pkt) chaos:my-address) (= (ar-protocol-target pkt) i:(reverse-internet-address my-internet-address)) (and (= chaos:my-address 3140) (= (ar-protocol-target pkt) 3540)) ;hack for cadr2 (and si:*share-code-ready* (si:processor-for-host-if-on-my-nubus (ar-protocol-target pkt))))) (defun pkt->string (pkt) (make-array (* 2 (array-length pkt)) :type :art-string :displaced-to pkt)) (defun data->string (pkt) (make-array (tcp-data-bytes pkt) :type :art-string :displaced-to pkt :displaced-index-offset (+ (* 4 (ih-internet-header-length pkt)) (* 4 (tcp-data-offset pkt))))) (defvar *next-iss* 1000.) (defvar *next-local-port* 1.) ;79 finger ;23 telnet ;21 ftp (defun tcp-test (&optional (port 23.)) (declare (special conn s)) (when (and (boundp 'conn) conn) (tcp-abort conn)) (setq conn nil) (reset-stats) (setq conn (tcp-open "angel" port)) ; (setq conn (tcp-open "sol" port)) ; (setq conn (tcp-open "lmivax" 23.)) (process-wait "Establish" #'(lambda (c) (eq (send c :state) :established)) conn) (setq s (make-tcp-stream conn)) ) (defun tcp-test1 () (let ((stream (tcp-test))) (process-sleep (* 60. 5)) (send stream :string-out "pace ") (send stream :force-output) (process-sleep (* 60. 10.)) (soak stream) (process-sleep (* 60. 10.)) (soak stream) (setq *tcp-sequence-numbers* nil) (setq *max-ack* 0) (send stream :string-out "cat /lmi/pace/foo") (send stream :force-output) (process-sleep (* 60. 3)) (setq *seq-offset* (32-bit-plus (send conn :rcv.nxt) 2)) )) (defconst *tcp-default-receive-window* 401.) (defun tcp-open (host remote-port) (let* ((adr (cond ((numberp host) host) (t (send (si:parse-host host) :network-address :internet)))) (conn (make-instance 'tcp-conn :state :syn-sent :source-port (incf *next-local-port*) :dest-port remote-port :snd.una *next-iss* :snd.nxt (32-bit-plus *next-iss* 1) :snd.wnd 128. :snd.up 0 :snd.wl1 nil :snd.wl2 *next-iss* :iss *next-iss* :rcv.nxt 0 :rcv.wnd *tcp-default-receive-window* :rcv.pre.wnd 0 :rcv.initial.wnd *tcp-default-receive-window* :rcv.up 0 :irs 0 :transmit-list nil :receive-list nil :dest-address adr :last-ack-sent 0 :time-last-ack-sent 0 ))) (incf *next-iss* 1000.) (push conn *tcp-conns*) (send conn :make-syn-pkt) (send conn :retransmit) conn)) (defun tcp-abort (conn) (send conn :send-rst (send conn :snd.nxt) (send conn :rcv.nxt)) (setq *tcp-conns* (delq conn *tcp-conns*))) (defmethod (tcp-conn :data-available-after-seq) (seq) (cond ((eq current-stack-group si:scheduler-stack-group) (and receive-list (32-bit-> rcv.nxt seq))) (t (with-lock (lock) (and receive-list (32-bit-> rcv.nxt seq)))))) (defmethod (tcp-conn :read-data) (after-seq no-hang-p) (block nil (when (not (eq state :established)) (ferror nil "bad connection state")) (let ((initial-data-avail (send self :data-available-after-seq after-seq)) ip ) (when (and (null initial-data-avail) no-hang-p) (return nil)) (when (null initial-data-avail) (process-wait "Net Input" #'(lambda (conn) (or (not (eq (send conn :state) :established)) (send conn :data-available-after-seq after-seq))) self)) (with-lock (lock) (when (eq state :established) (setq ip (pop receive-list)))) (when (null ip) (ferror nil "bad connection state")) ip))) (defmethod (tcp-conn :advance-receive-window) (nbytes) (with-lock (lock) (incf rcv.pre.wnd nbytes) (when (> rcv.pre.wnd (* 1/2 rcv.initial.wnd)) (incf rcv.wnd rcv.pre.wnd) (setq rcv.pre.wnd 0)) )) (defflavor tcp-stream (conn next-seq-to-read (last-pkt-read nil) read-string-array write-string-array write-pkt ) (si:buffered-stream) :settable-instance-variables) (defun make-tcp-stream (conn) (process-wait "Conn Status" #'(lambda () (eq (send conn :state) :established))) (make-instance 'tcp-stream :conn conn)) (defmethod (tcp-stream :after :init) (ignore) (when (not (eq (send conn :state) :established)) (ferror nil "connection must be established first")) (setq next-seq-to-read (32-bit-plus (send conn :irs) 1)) (setq read-string-array (make-array 1 :type :art-string :displaced-to "foo" :displaced-index-offset 1)) (setq write-string-array (make-array 1 :type :art-string :displaced-to "foo" :displaced-index-offset 1)) ) (defmethod (tcp-stream :next-input-buffer) (&optional no-hang-p) (block top (let (ip seg.seq seg.len) ;;skip over pkts with too low a seq (do () (()) (setq ip (send conn :read-data next-seq-to-read no-hang-p)) (when (null ip) (return-from top nil)) (setq seg.seq (tcp-sequence-number ip)) (setq seg.len (compute-seg.len ip)) (when (and (not (zerop seg.len)) (32-bit-> (32-bit-plus seg.seq seg.len) next-seq-to-read)) (return nil)) (free-pkt ip)) (when (32-bit-< next-seq-to-read seg.seq) ;;we want data before the beginning of this pkt (ferror nil "wrong data")) (when (tcp-syn-bit-p ip) (incf seg.seq) (decf seg.len)) (when (not (null last-pkt-read)) (ferror nil "haven't freed last pkt")) (setq last-pkt-read ip) (let ((start (+ (* 4 (ih-internet-header-length ip)) (* 4 (tcp-data-offset ip)) (32-bit-minus next-seq-to-read seg.seq))) (end (ih-length ip))) (let ((length (- end start))) (when (<= length 0) (ferror nil "??")) (si:change-indirect-array read-string-array art-string length ip start) (setq next-seq-to-read (32-bit-plus next-seq-to-read length)) (send conn :advance-receive-window length) (values read-string-array 0 length)))))) (defmethod (tcp-stream :discard-input-buffer) (string) (when (not (eq string read-string-array)) (ferror nil "bad buffer returned")) (free-pkt last-pkt-read) (setq last-pkt-read nil)) (defmethod (tcp-stream :new-output-buffer) () (setq write-pkt (send conn :make-pkt)) (si:change-indirect-array write-string-array art-string 1024. write-pkt (+ (* 4 (ih-internet-header-length write-pkt)) (* 4 (tcp-data-offset write-pkt)))) (values write-string-array 0 1024.)) (defmethod (tcp-stream :send-output-buffer) (string end) (when (not (eq string write-string-array)) (ferror nil "bad buffer")) (send conn :send-pkt write-pkt end) (setq write-pkt nil) (send conn :retransmit) ) (defmethod (tcp-stream :discard-output-buffer) (string) (when (not (eq string write-string-array)) (ferror nil "bad buffer")) (free-pkt write-pkt)) (defun soak (s &optional hang) (send standard-output :tyo #\) (let ((message (if hang :tyi :tyi-no-hang))) (do ((c (send s message) (send s message))) ((null c)) (cond ((char= c #\ ) (send standard-output :tyo #\newline) ;(tyi) ) (t (send standard-output :tyo c)))) (send standard-output :tyo #\) nil ))