From: Ralph Ronnquist Date: Mon, 8 May 2023 00:34:25 +0000 (+1000) Subject: initial X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=cfc0556a484b427c2a0d3501980f48e5866a9097;p=rrq%2Fnewlisp%2Farper.git initial --- cfc0556a484b427c2a0d3501980f48e5866a9097 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6009693 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +BIN = arper.lsp +MAN = arper.lsp.8 + +default: ${MAN} + +%.8: %.8.adoc + asciidoctor -b manpage -o $@ $^ + +arper: arper.lsp + packnl -w $@ $^ + +clean: + rm -f arper arper.lsp.8 diff --git a/arper.conf b/arper.conf new file mode 100644 index 0000000..06b3282 --- /dev/null +++ b/arper.conf @@ -0,0 +1,2 @@ +133.133.133.133 20:ff:00:00:00:00 +133.133.0.133 20:ff:00:00:00:02 diff --git a/arper.lsp b/arper.lsp new file mode 100755 index 0000000..10ac388 --- /dev/null +++ b/arper.lsp @@ -0,0 +1,193 @@ +#!/usr/bin/newlisp +# Copyright 2017, Ralph Ronnquist + +;; This program iplements arp responding for selected IP and +;; interfaces. It listens to network traffic on a given tap, then +;; responds to any arp requests as per its configuration. +;; +;; Usage: -t +;; where is the tap to service, and is the pathname for +;; the configuration file. +;; +;; The configuration file has lines of "IP MAC whatever"; other lines +;; are ignored (as comments). + +# Exit on INT, i.e., ^C +(signal 2 (fn (x) (exit 0))) + +# The following library path is for Devuan GNU+Linux +(constant 'LIBC "/lib/x86_64-linux-gnu/libc.so.6") +(import LIBC "ioctl" "int" "int" "long" "void*" ) +(import LIBC "perror" "void" "char*" ) +(import LIBC "ntohs" "int" "int" ) +(import LIBC "htons" "int" "int" ) + +# Report low level system error and exit +(define (die s) (perror s) (exit 1)) + +# Tell about usage and exit with error. +(define (usage) + (write-line 2 [text];; Usage: -t +;; where is the tap to service, and is the pathname for +;; the configuration file. +[/text] + ) + (exit 1)) + +# Pick up tap name and handled IP from the command line, open +# the "tun" device (Devuan GNU+Linux), and initialize it for using the +# given tap name as a tap (not a tun), without packet wrapping. +(constant 'CMDARG (if (match '(* "-t" ? ?) (main-args)) (1 $it) nil)) +(when (null? CMDARG) (usage)) + +(constant + 'IFNAME (CMDARG 0) + 'IFD (open "/dev/net/tun" "u") + ) + +(unless (number? IFD) + (die "open")) +(unless (zero? (ioctl IFD 0x400454ca (pack "s16 u s22" IFNAME 0x1002 ""))) + (die (string "set " IFNAME))) + +# Set up the IP map +(define MAP:MAP nil) +(define (decimal x) (int x 0 10)) +(define (hexadecimal x) (int x 0 16)) +(dolist (X (parse (read-file (CMDARG 1)) "\n")) + (when (regex "^\s*([0-9\.]+)\\s+([0-9a-fA-F:]+)" X 0) + (let ((IP $1) (MAC $2)) + (MAP (string (map decimal (parse IP "."))) + (map hexadecimal (parse MAC ":"))) + ))) + +# Pack a pair og bytes into a 16-bit number +(define (b2u x) (+ (<< (x 0) 8) (x 1))) + +# Unpack a short number into two bytes in network order +(define (stonb x) (list (& 0xFF (>> x 8)) (& 0xFF x))) + +# Unpack a 32-bit number into two 16-bit in network order +(define (n2u x) (list (& 0xFFFF (>> x 16)) (& 0xFFFF x))) + +# Compute 16 bit checksum of a small even number of bytes +(define (checksum bytes) + (apply + (n2u (- (apply + (map b2u (explode bytes 2))))))) + +# Pack a byte sequence into a string +(define (pack-bytes bytes) (pack (dup "b" (length bytes)) bytes)) + +# Join IP address bytes into a dotted quad string. +(define (pack-ip x) (join (map string x) ".")) + +# Process an IPv4 header byte sequence without checksum by inserting one. +(define (ipv4-header-checksum h) + (flat (list (0 10 h) (stonb (checksum h)) (10 h)))) + +# Process an ICMP header byte sequence without checksum by inserting one. +(define (icmp-header-checksum t c tail) + (let ((data (unpack (dup "b" (length tail)) tail))) + (flat (list t c (stonb (checksum (flat (list t c data)))) data)))) + +# ARP request handler. Confirms the targeted IP address is one to +# handle and then issues a corresponding reply. Note: the MAC address +# is formed from the IP address. +(define (arp-request-handler) ; buffer + (letn ((IP (unpack "bbbb" (38 buffer))) + (MYMAC (flat (list 2 IP 2))) + (MAPMAC (MAP (string IP)))) + (when MAPMAC + (write IFD (pack "bbbbbb bbbbbb u u u b b u bbbbbb bbbb bbbbbb bbbb" + (flat (list (unpack "bbbbbb" (6 buffer)) + MYMAC (map htons '(0x0806 0x1 0x0800 )) + 0x06 0x04 (htons 0x2) MAPMAC IP + (unpack "bbbbbb bbbb" (22 buffer)) + )) + ))))) + +# ARP packet handler. Recognizes the ARP command involved, and for +# some of them, it dispatches to the associated handler, if any. +(define (arp-handler) ; buffer + (case (ntohs ((unpack "u" (20 buffer)) 0)) ; ARP command + (0x0001 (and arp-request-handler (arp-request-handler))) + (true nil) ; ignore + )) + +# ICMP request handler. Confirms that the targeted IP is one to +# handle, and then issues a corresponding response. +(define (icmp-request-handler) + (letn ((h (unpack "bbbb bbbb" (26 buffer))) + (n (ntohs((unpack "u" (16 buffer)) 0))) + (MYMAC (MAP (string (4 4 h)))) ) + (when MYMAC + (write IFD + (pack-bytes + (flat (list + # Ethernet header (14 bytes) + (unpack "bbbbbb" (6 buffer)) + MYMAC + (stonb 0x0800) ; Type = IPv4 + # IPv4 header (20 bytes, with header checksum) + (ipv4-header-checksum + (flat (list 0x45 0 0 n + (unpack "bbbbbb" (18 buffer)) + (4 4 h) (0 4 h)))) + # ICMP header + (icmp-header-checksum 0 0 ((+ ihl 18) buffer)) + )) + )) + ))) + +# ICMP packet handler. Recognizes the ICMP type involved and for some +# of them, it dispatches to the associated handler, if any. +(define (icmp-handler) + (case ((unpack "b" ((+ ihl 14) buffer)) 0) + (8 (and icmp-request-handler (icmp-request-handler))) + (true))) + +# IPv4 packet handler. Recognises the IPv4 protocol involved, and for +# some of them, it dispatches to the associated handler, if any. +(define (ipv4-handler) ; buffer + (let ((ihl (* (& 0x0F ((unpack "b" (14 buffer)) 0)) 4))) + (case ((unpack "b" (23 buffer)) 0) ; protocol + (0x01 (and icmp-handler (icmp-handler))) + (0x02 (and igmp-handler (igmp-handler))) + (0x04 (and ipip-handler (ipip-handler))) + (0x06 (and tcp-handler (tcp-handler))) + (0x11 (and udp-handler (udp-handler))) + (true nil) ; ignore + ) + )) + +# Ethernet packet handler. Recognises EtherTYpe involved, and for some +# of them, it dispatches to the associated handler, if any. +(define (handle-packet) ; buffer + (when (> n 14) + (case (ntohs ((unpack "u" (12 buffer)) 0)) ; Ethertype + (0x0806 (and arp-handler (arp-handler))) + (0x0800 (and ipv4-handler (ipv4-handler))) + ;(0x86DD (and ipv6-handler (ipv6-handler))) + (true nil) ; ignore all else + ))) + +# Tap handler. Reads an Ethernet packet from the tap, and invokes the +# associated handler. +(define (handle-tap) + (let ((buffer "") (n nil)) + (if (setf n (read IFD buffer 8000)) (handle-packet) + (die (string "** error reading " IFNAME))))) + +# Input handler. Waits for input on the tap or stdin, and invokes the +# associated handler. This is set up as a prompt-event handler, so as +# to multiplex tap handling with newlisp interactive command handling. +(define (ioselect s) + (letn ((fds (list 0 IFD)) (fdx nil)) + (until (member 0 (setf fdx (or (net-select fds "r" 10000000) '()))) + (when fdx (handle-tap)))) + nil) + +# "Main program" starts here +(println "IP addresses on " IFNAME ":") +(map println (MAP)) +(prompt-event ioselect) +(reset) diff --git a/arper.lsp.8.adoc b/arper.lsp.8.adoc new file mode 100644 index 0000000..0c7d53f --- /dev/null +++ b/arper.lsp.8.adoc @@ -0,0 +1,94 @@ += arper.lsp(8) +:doctype: manpage +:revdate: {sys:date "+%Y-%m-%d %H:%M:%S"} +:COLON: : +:EQUALS: = + +== NAME + +arper.lsp - Emulate ipv4 presence on a tap by responding to ARP and ICMP. + +== SYNOPSIS + +*./arper.lsp* *-t* _tap_ _conf__ + +== DESCRIPTION + +The *arper.lsp* program implements ARP and ICMP responding on a given +_tap_ interface for a selection of ipv4 addresses as specified in the +given configuration file. This emulates the presence of one or more +ipv4 hosts, as if the _tap_ was interfacing a network. + +The configuration file has lines of "ipv4 mac comment" to declare +pairs of IP and MAC addresses to emulate. + +Note that the _tap_ must be configured separately. + +== EXAMPLE + +=== Emulating a single host + +The configuration, "single", is as follows: +---- + 192.168.1.2 02:00:00:00:00:02 some "random" host +---- + +Commandline: +---- +# ./arper.lsp -t tap0 single & +# ifconfig tap0 192.168.1.1/24 up +# ping -n 192.168.1.2 +# fg^C +---- + +=== Emulating three host on local network + +The configuration, "triple", is as follows: +---- + 192.168.1.2 02:00:00:00:00:02 some "random" host + 192.168.1.20 02:00:00:00:00:02 another "random" host + 192.168.1.200 02:00:00:00:00:02 and another "random" host +---- + +Commandline: +---- +# ./arper.lsp -t tap0 single & +# ifconfig tap0 192.168.1.1/24 up +# ping -c 3 -n 192.168.1.2 +# ping -c 3 -n 192.168.1.20 +# ping -c 3 -n 192.168.1.22 +# ping -c 3 -n 192.168.1.200 +# fg^C +---- + +Note that +192.168.1.22+ is not emulated. + +=== Emulating hosts on several network + +The configuration, "triss", is as follows: +---- + 192.168.1.2 02:00:00:00:00:02 some "random" host + 192.168.2.2 02:00:00:00:00:02 another "random" host + 192.168.3.2 02:00:00:00:00:02 and another "random" host +---- + +Commandline: +---- +# ./arper.lsp -t tap0 single & +# ifconfig tap0 192.168.1.1/24 up +# ip addr add 192.168.2.1/24 dev tap0 +# ip addr add 192.168.3.1/24 dev tap0 +# ping -c 3 -n 192.168.1.2 +# ping -c 3 -n 192.168.2.2 +# ping -c 3 -n 192.168.3.2 +# fg^C +---- + +=== NOTES + +The *arper.lsp* script is prepared for handling ipv6 packets, but it +does not include any Neighbor Discovery activity. + +== AUTHOR + +Copyright 2021 Ralph Ronnquist