;;; -*- Mode:LISP; Package:TCP; Base:10 -*-
;;;
;;; Look at all ethernet packets with the Excelan board.
;;;

(defun netspy ()
  "Look at all ethernet packets with the Excelan board."
  (tcp-disable)
  (setq *netload-debug-p* nil)
  (start :enable-tcp nil)
  (LINK-NET-MODE :read-p t
		 :write-p t
		 :mode 'nmode-connect-promiscuous)
  (exos-stats)
  (netspy-loop))

(defun netspy-loop ()
  (terpri)
  (DO-FOREVER
    (when (send terminal-io :listen)
      (let ((c (send terminal-io :tyi)))
	(exos-stats :reset-p (char-equal c #\r))))
    (WITH-DMABUF (BUF)
      (NETSPY-RECEIVE BUF (LINK-RECV BUF)))))

(defvar *pkts* nil)
(defvar *pktn* 0)

(defun netspy-loop-2 ()
  (terpri)
  (WITH-DMABUF (BUF)
    (DO-FOREVER
      (when (send terminal-io :listen)
	(let ((c (send terminal-io :tyi)))
	  (exos-stats :reset-p (char-equal c #\r))))
      (let ((n (LINK-RECV BUF)))
	(incf *pktn* n)
	(incf *pkts*)
	(princ ".")))))

(defvar *pkt-peek* nil
  "Controls level of peeking at packets.
  NIL just shows type and length.
  T or n also shows destination and source.
  n also shows n data characters both as hex and as chars.")

(defun netspy-receive (buffer nbytes)
  (INCF *EXOS-ETHERNET-PACKETS-RECEIVED*)
  (LET ((TYPE (GRATUITOUS-BYTESWAP-2 (READ-SHORT BUFFER 0 'ETHER-TYPE))))
    (SELECT TYPE
      (ETHERNET:CHAOS-ETHERNET-TYPE
       (INCF *EXOS-ETHERNET-CHAOS-PKTS-RECEIVED*)
       (format t "**** Got a chaos pkt ~D bytes long~%" nbytes))
      (ETHERNET:ADDRESS-RESOLUTION-TYPE
       (format t "**** Got an address resolution pkt ~D bytes long~%" nbytes))
      (ethernet:ip-ethernet-type
       (format t "**** Got an internet pkt ~D bytes long~%" nbytes))
      (OTHERWISE
       (format t "**** Got an unknown pkt ~D bytes long~%" nbytes)
       (INCF *EXOS-ETHERNET-UNKNOWN-PROTOCOL-PKTS-RECEIVED*)
       (SETQ *EXOS-ETHERNET-LAST-UNKNOWN-PROTOCOL-TYPE* TYPE)
       (SETF (FILL-POINTER *LAST-BOGUS-PACKET*) NBYTES)
       (COPY-ARRAY-PORTION BUFFER 0 NBYTES *LAST-BOGUS-PACKET* 0 NBYTES))))
  (when *pkt-peek*
    (pkt-peek buffer nbytes)))

(defun pkt-peek (buffer nbytes)
  "Show further information about packet.
  Variable *pkt-peek* controls level of peeking at packets.
  If it is non-NIL, this routine is called.
  If it is a number, show n data characters both as hex and as chars."
  (flet ((hexbytes (start length &optional terpri)
		   (dotimes (j length)
		     (and terpri (zerop (mod j terpri)) (terpri))
		     (format t "~16,2,'0R " (aref buffer (+ start j))))))
    (format t "ETHER DST: ")
    (hexbytes 0 6)
    (format t "~%ETHER SRC: ")
    (hexbytes 6 6)
    (format t "~%TYPE     : ")
    (hexbytes 12 2)
    (when (and (numberp *pkt-peek*) (> *pkt-peek* 0))
      (let ((nlook (min (- nbytes 14) *pkt-peek*)))
	(format t "~%~D bytes of data in hex:" nlook)
	(hexbytes 14 nlook 32)
	(format t "~%Packet data as characters:")
	(dotimes (j nlook)
	  (and (zerop (mod j 64)) (terpri))
	  (let ((c (aref buffer (+ 14 j))))
	    (cond ((graphic-char-p c)
		   (tyo c))
		  ('else
		   (tyo #\_))))))))
  (terpri))


(defun dump-trans ()
  ;; this only works if you also have a 3com board.
  (format t "~&Ethernet Address ~,8@TType ~,8@TAddress ~12,8@THost~%")
  (dolist (e ethernet:ether-address-translations)
    (ecase (cadddr e)
      (:chaos
       (format t
	      "~16R ~6,8@T~A ~,8T#o~O ~12,8@T~A~%"
	      (cadr e)
	      (cadddr e)
	      (car e)
	      (si:get-host-from-address (car e) :chaos)))
      (:ip
       (let ((n (car e)))
	 (swapf (ldb (byte 8 0) n) (ldb (byte 8 24) n))
	 (swapf (ldb (byte 8 16) n) (ldb (byte 8 8) n))
	 (format t
	      "~16R ~6,8@T~A ~,8@T~A ~,8@T~A~%"
	      (cadr e)
	      (cadddr e)
	      (canonical-ip n)
	      (si:get-host-from-address n :internet))))
      )))

(defun canonical-ip (ip-addr)
  "Return canonical string for numeric Internet Address."
  (check-type ip-addr number)
  (format nil "~3,48D.~3,48D.~3,48D.~3,48D"
	  (ldb (byte 8 24) ip-addr)
	  (ldb (byte 8 16) ip-addr)
	  (ldb (byte 8 8) ip-addr)
	  (ldb (byte 8 0) ip-addr)
	  ))
