‹ projects

netscan

tool for scanning networks and calculating available subnets
Log | Files | Refs | README

iface-groups (8375B)


      1 #!/bin/bash
      2 # written by me on October 1, 2022.
      3 
      4 # enumerates the properties of each network interface
      5 # eventually, it may be possible to assign rules on a per-network basis
      6 # i.e., all ifaces on 10.111.13/24 should request DHCP
      7 # unless, that's better done as a set of logic outside this program
      8 
      9 # we need to know:
     10 # - which interfaces are wired (compatible with PXE boots)
     11 # - which interfaces are connected
     12 # - which interfaces are on the same L2 network
     13 
     14 # ASSUMPTION: one interface is NOT connected to multiple L2 switches
     15 
     16 set -e
     17 
     18 sudo -v
     19 
     20 help () {
     21 
     22     echo -e "usage:"
     23     echo -e "\t-i , --ifacetype=[TYPE]"
     24     echo -e "\t-t , --timelimit=[seconds]"
     25     echo -e "Multiple interface types may be specified, like so:"
     26     echo -e "\tiface-groups -i eth -i tun -t 5"
     27     echo -e "Available interface types (at least one must be specified):"
     28     echo -e "\teth, wlan, bridge, vlan, bond, tap, dummy, ib, ibchild, ppp,"
     29     echo -e "\tipip, ip6tnl, lo, sit, gre, irda, wlan_aux, tun, isdn, mip6mnha"
     30 
     31 }
     32 
     33 eecho () { echo "$@" 1>&2 ; }
     34 
     35 # timelimit="$1" ; [ -z "$timelimit" ] && timelimit='4'
     36 
     37 ip_address_prefix='203.0.113'
     38 
     39 #         +----------------------+----------------------------+
     40 #         | Attribute            | Value                      |
     41 #         +----------------------+----------------------------+
     42 #         | Address Block        | 203.0.113.0/24             |
     43 #         | Name                 | Documentation (TEST-NET-3) |
     44 #         | RFC                  | [RFC5737]                  |
     45 #         | Allocation Date      | January 2010               |
     46 #         | Termination Date     | N/A                        |
     47 #         | Source               | False                      |
     48 #         | Destination          | False                      |
     49 #         | Forwardable          | False                      |
     50 #         | Global               | False                      |
     51 #         | Reserved-by-Protocol | False                      |
     52 #         +----------------------+----------------------------+
     53 # 
     54 #         Table 14: TEST-NET-3
     55 
     56 ifacetype=eth timelimit=3 lopts=ifacetype:,timelimit: sopts=i:t:
     57 
     58 PARSED=$(getopt --options=$sopts --longoptions=$lopts --name "$0" -- "$@")
     59 
     60 eval set -- "$PARSED"
     61 
     62 while true; do case "$1" in
     63 
     64     -i|--ifacetype) 
     65 
     66         allowed_iface_types="${allowed_iface_types:+$allowed_iface_types } $2"
     67         shift 2
     68         ;;
     69 
     70     -t|--timelimit) 
     71     
     72         timelimit="$2"
     73         shift 2
     74         ;;
     75 
     76     --) shift ; break ;;
     77     *)  help ; exit 1 ;;
     78 
     79 esac done
     80 
     81 [ -z "$allowed_iface_types" ] && help && exit 1
     82 
     83 tempfile=/tmp/ifacegroups
     84 groupingfolder=/tmp/ifacegrouping
     85 
     86 rm -rf $tempfile $groupingfolder
     87 mkdir -p $tempfile
     88 mkdir -p $groupingfolder
     89 
     90 if ! command -v tcpdump &>/dev/null ; then echo "tcpdump required" ; exit 2 ; fi
     91 
     92 get_iface_type () {
     93     
     94     # function came from a dead link via stackoverflow
     95     # http://gitorious.org/opensuse/sysconfig/blobs/master/scripts/functions
     96     # https://github.com/openSUSE/sysconfig/blob/master/scripts/functions
     97     # (GPL v2)
     98     # (may want to rewrite this in TCL or lisp)
     99 
    100     local IF=$1 TYPE
    101     test -n "$IF" || return 1
    102     test -d /sys/class/net/$IF || return 2
    103     case "`cat /sys/class/net/$IF/type`" in
    104             1)
    105                 TYPE=eth
    106                 # Ethernet, may also be wireless, ...
    107                 if test -d /sys/class/net/$IF/wireless -o \
    108                         -L /sys/class/net/$IF/phy80211 ; then
    109                     TYPE=wlan
    110                 elif test -d /sys/class/net/$IF/bridge ; then
    111                     TYPE=bridge
    112                 elif test -f /proc/net/vlan/$IF ; then
    113                     TYPE=vlan
    114                 elif test -d /sys/class/net/$IF/bonding ; then
    115                     TYPE=bond
    116                 elif test -f /sys/class/net/$IF/tun_flags ; then
    117                     TYPE=tap
    118                 elif test -d /sys/devices/virtual/net/$IF ; then
    119                     case $IF in
    120                       (dummy*) TYPE=dummy ;;
    121                     esac
    122                 fi
    123                 ;;
    124            24)  TYPE=eth ;; # firewire ;; # IEEE 1394 IPv4 - RFC 2734
    125            32)  # InfiniBand
    126             if test -d /sys/class/net/$IF/bonding ; then
    127                 TYPE=bond
    128             elif test -d /sys/class/net/$IF/create_child ; then
    129                 TYPE=ib
    130             else
    131                 TYPE=ibchild
    132             fi
    133                 ;;
    134           512)  TYPE=ppp ;;
    135           768)  TYPE=ipip ;; # IPIP tunnel
    136           769)  TYPE=ip6tnl ;; # IP6IP6 tunnel
    137           772)  TYPE=lo ;;
    138           776)  TYPE=sit ;; # sit0 device - IPv6-in-IPv4
    139           778)  TYPE=gre ;; # GRE over IP
    140           783)  TYPE=irda ;; # Linux-IrDA
    141           801)  TYPE=wlan_aux ;;
    142         65534)  TYPE=tun ;;
    143     esac
    144     # The following case statement still has to be replaced by something
    145     # which does not rely on the interface names.
    146     case $IF in
    147         ippp*|isdn*) TYPE=isdn;;
    148         mip6mnha*)   TYPE=mip6mnha;;
    149     esac
    150     test -n "$TYPE" && echo $TYPE && return 0
    151     return 3
    152 }
    153 
    154 eecho "analysing available physical L2 networks..."
    155 
    156 # for every interface on the system
    157 for interface in /sys/class/net/*
    158 do
    159 
    160     interface=$(basename $interface)
    161 
    162     # figure out what it is: wifi? ethernet? virtual bridge? infiniband?
    163     iface_type=$(get_iface_type $interface)
    164 
    165     # for each of the useful interface types, specified above (or in an arg)
    166     for allowed_iface_type in ${allowed_iface_types[@]}
    167     do
    168 
    169         # if the interface we're looking at is one of those allowed
    170         if [ "$iface_type" == "$allowed_iface_type" ]
    171         then
    172 
    173             # if the interface is connected to something: switch, router, etc
    174             if tcpdump --list-interfaces | grep $interface | grep -q 'Running'
    175             then
    176 
    177                 # add it do the list of viable interfaces
    178                 viable_ifaces="${viable_ifaces:+$viable_ifaces } $interface"
    179 
    180                 eecho "found relevant iface: $interface"
    181 
    182             fi
    183 
    184             # don't check more types after a match: "there can be only one"
    185             break
    186 
    187         fi
    188 
    189     done
    190 
    191 done
    192 
    193 [ -z "${viable_ifaces[@]}" ] && echo '' && exit 1
    194 
    195 ip_iterator=0
    196 
    197 declare -A iface_ip
    198 
    199 starttime=$(date +%s)
    200 
    201 # eecho "broadcasting and sniffing ARP on all ifaces for $timelimit seconds..."
    202 
    203 # for each viable interface we found
    204 for interface in ${viable_ifaces[@]}
    205 do
    206 
    207     ip_iterator=$((ip_iterator+1))
    208 
    209     # create a unique, unused ip address (taken from TEST-NET-3, RFC 6890)
    210     ip_address="$ip_address_prefix.$ip_iterator"
    211 
    212     # associate that ip address with the current interface
    213     iface_ip+=(["$ip_address"]="$interface")
    214 
    215     eecho "$interface broadcasting ARP with $ip_address and dumping results to $tempfile/$interface"
    216 
    217     # start listening on that interface for ARP requests
    218     sudo timeout $timelimit tcpdump --immediate-mode -i $interface arp > $tempfile/$interface 2>/dev/null &
    219 
    220     # start sending ARP requests for the unique IP address on that interface
    221     sudo arping -I $interface -w $timelimit $ip_address &>/dev/null &
    222 
    223 done
    224 
    225 # total time it took to start the listeners and senders
    226 endtime=$(date +%s)
    227 initializationtime=$(( $endtime - $starttime ))
    228 
    229 # wait for the listeners and senders to finish
    230 sleep $(($timelimit + $initializationtime + 1))
    231 
    232 # for each viable interface, get the file with the results from the listener
    233 for ifacefile in $tempfile/*
    234 do
    235 
    236     ifacename=$(basename $ifacefile)
    237 
    238     # if the iface is not already in a group of ifaces that can see each other
    239     if ! grep -q $ifacename $groupingfolder/* 2>/dev/null
    240     then
    241 
    242         # create a new group with just that interface
    243         echo $ifacename > $groupingfolder/$(date +%s%N)
    244 
    245     fi
    246 
    247     # for each ARP request seen by this interface, get the IP address requested
    248     grep $ip_address_prefix $ifacefile | cut -d ' ' -f 5 | while read ipaddress
    249     do
    250 
    251         # get the group file of the interface that saw the request
    252         groupfile=$(grep -l $ifacename $groupingfolder/* 2>/dev/null)
    253 
    254         # convert the ip address to the sender interface and add it to the group
    255         echo ${iface_ip[$ipaddress]} >> $groupfile
    256 
    257     done
    258 
    259 done
    260 
    261 eecho "scan results:"
    262 
    263 # for each group of interfaces that we collected
    264 for group in $groupingfolder/*
    265 do 
    266 
    267     # filter out duplicate interfaces within the group and print to stdout 
    268     echo $(sort $group | uniq | xargs)
    269 
    270 done
    271