Getting your local IPv4 addresses, netmasks and CIDRs
Posted by jpluimers on 2017/04/11
In order to scan some local networks for unknown hosts (yes, on some sites you need to perform archeology), I needed the local IPv4 addresses, netmasks and CIDRs on my Mac running OS X.
Part of that is using ifconfig to get local inet information which however uses hexadecimal network masks and delivers no CIDRs.
SoI was a bit premature when I wrote about “This could be done by creating bash functions mask2cdr and cdr2mask, but that’s a bit too convoluted right now” in Getting the IP addresses of gmail MX servers – via Super User – dig isn’t enough.
netmask to CIDR and CIDR to netmask conversion
I need mask2cdr now, so lets start with these two bash functions and their aliases:
mask2cdr () { # Assumes there's no "255." after a non-255 byte in the mask local x=${1##*255.} set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*} x=${1%%$3*} echo $(( $2 + (${#x}/4) )) } alias mask2cidr='mask2cdr' cdr2mask () { # Number of args to shift, 255..255, first non-255 byte, zeroes set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0 [ $1 -gt 1 ] && shift $1 || shift echo ${1-0}.${2-0}.${3-0}.${4-0} } alias cidr2mask='cdr2mask'
I tracked these above functions back from bash – Explanation of convertor of cidr to netmask in linux shell netmask2cdir and cdir2netmask – Stack Overflow which – despite the question being closed – has an excellent answer by ThisSuitIsBlackOrNot explaining their inner workings.
The CIDR (or prefix) is the number of 1 bits in the network mask or subnet mask.
The code came from Convert netmask IP format Nbr of set bits (Page 1) — General Discussion — OpenWrt which was based on the thread at Gentoo Forums :: View topic – OpenRC: converting netmask/CIDR without loops!
NB: Other varieties of these functions (including netcalc and bcastcalc to calculate network addresses and broadcast addresses) are at http://rather.puzzling.org/~tconnors/code/cidr2mask
Converting a hexadecimal netmask to dotted netmask
One of the peculiarities of Mac OS X ifconfig is that it delivers the subnet mask in hex (as it’s BSD derived, and BSD ifconfig does hex netmasks):
$ ifconfig | grep -w inet inet 127.0.0.1 netmask 0xff000000 inet 192.168.71.116 netmask 0xffffff00 broadcast 192.168.71.255 inet 172.16.149.1 netmask 0xffffff00 broadcast 172.16.149.255 inet 172.16.172.1 netmask 0xffffff00 broadcast 172.16.172.255
Since mask2cdr takes a dotted quad as input, we need to do the conversion. Luckily, Tim Kennedy has written the basics of a bash script to convert hex netmask to dotted quad which I fixed and converted to this bash function:
netmaskhex2dotted() { # read the mask, and strip leading 0x if it's there hexmask=$( echo $1 | sed -e 's/^0x//' ) # loop through $hexmask in pairs # for (( i=0; i<${#hexmask}; i+=2 )); do if (( $i > 1 )); then # use a . to separate octets # but don't print a leading . printf "%s" "." fi # print decimal %d equivalent of hex octet at position $i in hexmask printf "%d" "0x${hexmask:$i:2}" done printf "\n" }
The fix is in the last printf: it printed out the $1
parameter but should print the current ${hexmask}
iteration.
Now it works (:
Parsing the ifconfig output to get IPv4, netmask, CIDR
Now we get to the final piece: the ifconfig parsing.
First it’s limiting the ifconfig output to only connected networks. Which is pretty easy as they always have both the word inet and the word broadcast in them. Actually: only broadcast will do.
Then it’s basically getting the various fields: b and d:
- a: literal inet
- b: localIPV4
- c: literal netmask
- d: netmask
- e: literal broadcast
- f: broadcast
I started with ifconfig | grep -w broadcast | tr -s ' ' | cut -d ' ' -f 2,4
but that gave problems parsing in the bash script (somehow, newlines were being converted to spaces. I need to figure out that one day).
So I used the “shell functions” solution of linux – bash: split output of command by columns – Stack Overflow (thanks Xennex81) which means you need to read up until e (anything from the broadcast literal text).
A final, but very important aspect is to preserve the multi-line behaviour of the ifconfig output. The trick here is to have double quotes around the outer echo parameter: that preserves it. Jonathan Leffler explains that very well in his answer on Capturing multiple line output to a bash variable – Stack Overflow
get-local-ip-addresses-netmasks-cidrs() { echo "$(ifconfig | grep -w broadcast | \ while read a b c d e; \ do \ echo $b $d $(netmaskhex2dotted $d) $(mask2cidr $(netmaskhex2dotted $d)) ; \ done )" }
So final output is like this on my system:
$ get-local-ip-addresses-netmasks-cidrs 192.168.71.116 0xffffff00 255.255.255.0 24 172.16.149.1 0xffffff00 255.255.255.0 24 172.16.172.1 0xffffff00 255.255.255.0 24
–jeroen
via:
Leave a Reply