Finding all Public IPv4 and IPv6 addresses in a UNIX shell script

For monitoring purposes, I'd like to find out all public IPv4 and IPv6 addresses of a mobile-warrior UNIX box.

Note that this is different from Finding the Public IP address in a shell script because of the following extra requirements:

  • the mobile warrior itself probably does not have any public IPv4 addresses at all;
  • it may or may not have IPv6 (but we're only interested in active ones that would be used in actual outgoing connections);
  • the underlying internet connection might be load-balanced, details unknown, where a combination of UDP, TCP, ICMP and source/destination IP address, may determine which upstream will be used; we need to try our best to find out all such IP addresses for a complete picture on the underlying internet connectivity of the gateway, without having direct access to the gateway itself.

Explanation

We can determine the public IP address through DNS with dig (the DNS lookup utility from BIND), this lets us try out both UDP (with the +notcp option) and TCP (+tcp option), leaving only ICMP behind. However, we can try sending all these queries to multiple independent destination IPv4 and IPv6 addresses, making it more likely that the load-balancing of the connection will take place, returning more unique responses.

It would appear that the positioning of the -4 and -6 options with dig might also be handled differently depending on the order of the arguments — if positioned right after dig before the @ specifier, then it's enforced as a hard requirement; if positioned past the @ specifier and/or as a final argument, then it's enforced as a soft requirement (IPv4 will be used if IPv6 connectivity is missing); the snippet below uses it as a soft requirement to avoid having to implement error-handling.

We can use GNU Parallel to mix-and-match several commands and options that are at stake here.

Solution

Here's the full solution:

parallel -kj16 dig -t txt o-o.myaddr.l.google.com +short \
::: @ns{1,2,3,4}.google.com ::: -4 -6 ::: +notcp +tcp

Here's the same snippet as a single line:

parallel -kj16 dig -t txt o-o.myaddr.l.google.com +short ::: @ns{1,2,3,4}.google.com ::: -4 -6 ::: +notcp +tcp

Here's the same snippet SO inline:

parallel -kj16 dig -t txt o-o.myaddr.l.google.com +short ::: @ns{1,2,3,4}.google.com ::: -4 -6 ::: +notcp +tcp


Testing

Here's a demonstration of what the above parallel invocation would work to accomplish:

% parallel -k echo dig -t txt o-o.myaddr.l.google.com +short \
? ::: @ns{1,2,3,4}.google.com ::: -4 -6 ::: +notcp +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns1.google.com -4 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns1.google.com -4 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns1.google.com -6 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns1.google.com -6 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns2.google.com -4 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns2.google.com -4 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns2.google.com -6 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns2.google.com -6 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns3.google.com -4 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns3.google.com -4 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns3.google.com -6 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns3.google.com -6 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns4.google.com -4 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns4.google.com -4 +tcp
dig -t txt o-o.myaddr.l.google.com +short @ns4.google.com -6 +notcp
dig -t txt o-o.myaddr.l.google.com +short @ns4.google.com -6 +tcp
%

P.S. More of GNU Parallel

To extend on our solution above, the following could be used to perform whois and rDNS lookups on all found addresses; note that for IPv6 lookups to work on BSDs and macOS, one might have to specify -a for ARIN, -A for APNIC or -r for RIPE as an option to whois:

parallel -kj16 dig -t txt o-o.myaddr.l.google.com +short ::: @ns{1,2,3,4}.google.com ::: -4 -6 ::: +notcp +tcp | sort -n | uniq | parallel -vk ::: "echo" "host" "whois -a" :::: /dev/stdin