1 #!/usr/local/bin/newlisp
4 # This program attaches to a tap interface for the purpose of noticing
5 # activity via network traffic. The program serves as a virtual host
6 # that receives duplicated packets, and analyses them to select those
7 # that indicate activity.
9 # -t tap = use the given tap rather than "tap0".
11 (signal 2 (fn (x) (exit 0)))
13 # The following is for Devuan GNU+Linux
14 (constant 'LIBC (exists file? '("/lib/x86_64-linux-gnu/libc.so.6"
15 "/lib/i386-linux-gnu/libc.so.6"
17 (import LIBC "ioctl" "int" "int" "long" "void*" )
18 (import LIBC "perror" "void" "char*" )
19 (import LIBC "ntohl" "int" "int" )
20 (import LIBC "ntohs" "int" "int" )
21 (import LIBC "htons" "int" "int" )
22 (import LIBC "htonl" "int" "int" )
24 # Report low level system error and exit
25 (define (die s) (perror s) (exit 1))
27 # Utility function to find a command line argument key and optionally
28 # the subsequent value, if a non-nil default value is given.
29 (define (mainarg k (v nil))
30 (let ((a (member k (main-args))))
31 (if (null? a) v (nil? v) true (null? (1 a)) v (a 1))))
34 (constant 'listener-log-ip (mainarg "-l" nil))
36 # Open the tap named by "-t tapX" on the command line, or "tap0" byt
37 # default. Then make a TUNSETIFF call to initialize it (as
39 (constant 'IFNAME (mainarg "-t" "tap0") 'IFD (open "/dev/net/tun" "u") )
40 (unless (number? IFD) (die "open"))
41 (unless (zero? (ioctl IFD 0x400454ca (pack "s16 u s22" IFNAME 0x1002 "")))
42 (die (string "set " IFNAME)))
44 # The TCP ports of interest
45 (constant 'PORTS '(80 443))
47 # Set up for optional tracking of IP addresses
48 (define counter:counter nil)
50 # This function accumulates packet size per ip+port, for monitored the
51 # ports. This accumulates traffic in both directions.
52 (define (track-data) ; buffer
53 (write-line 2 "track-data")
54 (let ((ips (explode (unpack "bbbb bbbb" ((+ 12 14) buffer)) 4)))
56 (when (member (ports i) PORTS)
57 (let ((k (string (join (map string (ips i)) ".") "." (ports i))))
58 (counter x (+ (length buffer) (or (counter x) 0))))))))
60 (define (track-data-reset)
61 (map delete (symbols counter)))
65 # Mark the minute of t as an active minute. More exactly, it issues a
66 # mark if it now is more than 60 seconds from the last issued mark.
67 # This funcion collates all given ips, and it extends the log line
68 # with the list of ips used during the minute.
69 (setf next-mark 0 packet-count 0)
70 (define (mark-active t) ; buffer
71 (when listener-log-ip (track-data))
73 ;(write-line 2 (string (list t packet-count ports (counter))))
74 (when (>= t next-mark)
75 (let ((d (format "activity/%d%02d%02d-network.dat" (0 3 (date-list t))))
76 (c (map string (counter))))
77 (append-file d (string t " " packet-count " " (join c " ") "\n"))
78 (setf next-mark (+ t 60) packet-count 0)
79 (when listener-log-ip (track-data-reset))
82 # Handle an ARP request. This picks up IP address from the request.
83 # The MAC address is formed from the IP address with 2 before and 2
85 (define (arp-request-handler) ; buffer
86 (letn ((MYIP (unpack "bbbb" (38 buffer))) (MYMAC (flat (list 2 MYIP 2))))
87 (write IFD (pack "bbbbbb bbbbbb u u u b b u bbbbbb bbbb bbbbbb bbbb"
88 (flat (list (unpack "bbbbbb" (6 buffer))
90 (map htons '(0x0806 0x1 0x0800 ))
94 (unpack "bbbbbb bbbb" (22 buffer))
98 # Handle an ARP packet. It recognizes the ARP command involved, and
99 # dispatches to the associated handler, if any.
100 (define (arp-handler) ; buffer
101 (case (ntohs ((unpack "u" (20 buffer)) 0)) ; ARP command
102 (0x0001 (and arp-request-handler (arp-request-handler)))
106 # Handle a TCP packet. It reviews the ports involved, and if any is
107 # among the interesting ports, then it marks activity together with ip
108 # and port of sender and receiver.
109 (define (tcp-handler) ; buffer ihl
110 (let ((ports (map ntohs (unpack "uu" ((+ ihl 14) buffer)))))
111 (when (intersect ports PORTS) (mark-active (date-value)))))
113 (define (udp-handler) ; buffer ihl
114 (let ((ports (map ntohs (unpack "uu" ((+ ihl 14) buffer)))))
115 (when (intersect ports PORTS) (mark-active (date-value)))))
117 # Handle an IPv4 packet. It recognises the IPv4 protocol concerned,
118 # and dispatches to the associated handler, if any.
119 (define (ipv4-handler) ; buffer
120 (let ((ihl (* (& 0x0F ((unpack "b" (14 buffer)) 0)) 4)))
121 (case ((unpack "b" (23 buffer)) 0) ; protocol
122 (0x01 (and icmp-handler (icmp-handler)))
123 (0x02 (and igmp-handler (igmp-handler)))
124 (0x04 (and ipip-handler (ipip-handler)))
125 (0x06 (and tcp-handler (tcp-handler)))
126 (0x11 (and udp-handler (udp-handler)))
131 # This function handles an Ethernet packet by recognising the packet
132 # type, and dispatch to the associated handler, if any.
133 (define (handle-packet) ; buffer
135 (case (ntohs ((unpack "u" (12 buffer)) 0)) ; Ethertype
136 (0x0806 (and arp-handler (arp-handler)))
137 (0x0800 (and ipv4-handler (ipv4-handler)))
138 (0x86DD (and ipv6-handler (ipv6-handler)))
139 (true nil) ; ignore all else
142 # Read and handle a packet from the tap. The program handles ARP
143 # requests by emitting an appropriate ARP response, and it handles TCP
144 # packets to certain ports, which are seen as indications of activity.
146 (let ((buffer "")(n nil))
147 (if (setf n (read IFD buffer 8000)) (handle-packet)
148 (begin (write-line 2 (format "IFD error")) (exit 1)))))
150 # This function gets invoked prior to the interactive prompt. It'll
151 # listen for data on the tap, and handle that, and also wake up every
152 # second, so as to allow a timer effect to be set up.
154 (letn ((fds (list IFD)) (fdx nil))
155 (until (member 0 (setf fdx (or (net-select fds "r" 10000000) '())))
156 (when fdx (handle-tap))))
159 (prompt-event ioselect)
161 (while true (ioselect))