IPv4 addresses

This notebook shows how IPv4 addresses can be described, generated and recognized with a grammar.

References

  • Wikipedia

    • IP address: (IPv4) defines an IP address as a 32-bit number […] written and displayed in human-readable notations

    • IPv4: 32-bit address space which provides 4,294,967,296 (2^32) unique addresses

[1]:
import random
import subprocess

import alogos as al

Grammar

[2]:
bnf_text = """
<ip> ::= <number>.<number>.<number>.<number>
<number> ::= <digit> | <non-zero><digit> | 1<digit><digit> | 2<zero-four><digit> | 25<zero-five>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<non-zero> ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<zero-four> ::= 0 | 1 | 2 | 3 | 4
<zero-five> ::= 0 | 1 | 2 | 3 | 4 | 5
"""

grammar = al.Grammar(bnf_text=bnf_text)

Generate random IPv4 addresses

1) With Python’s random.choice

[3]:
def generate_random_ip():
    allowed_numbers = list(range(256))
    random_numbers = [random.choice(allowed_numbers) for _ in range(4)]
    ip = '{}.{}.{}.{}'.format(*random_numbers)
    return ip
[4]:
generate_random_ip()
[4]:
'186.174.7.182'

2) With the grammar

[5]:
grammar.generate_string()
[5]:
'105.253.133.42'

Is there a response?

Use ping to see if there’s a server responding to an ICMP ECHO_REQUEST at some random addresses

[6]:
for i in range(25):
    ip_address = grammar.generate_string()
    returned = subprocess.call(['ping', '-W', '1', '-c', '1', ip_address], stdout=subprocess.DEVNULL)
    print('{:2}:  IP: {:16} Response: {}'.format(i, ip_address, 'yes' if returned==0 else 'no'))
 0:  IP: 229.251.245.241  Response: no
 1:  IP: 254.252.249.170  Response: no
 2:  IP: 250.187.20.130   Response: no
 3:  IP: 5.67.40.31       Response: yes
 4:  IP: 165.223.188.244  Response: no
 5:  IP: 1.255.175.170    Response: yes
 6:  IP: 42.237.190.17    Response: no
 7:  IP: 116.9.255.136    Response: no
 8:  IP: 47.2.230.4       Response: no
 9:  IP: 201.2.6.217      Response: yes
10:  IP: 46.211.5.190     Response: no
11:  IP: 242.242.255.7    Response: no
12:  IP: 5.34.12.253      Response: no
13:  IP: 255.0.251.175    Response: no
14:  IP: 143.38.69.0      Response: no
15:  IP: 22.172.7.132     Response: no
16:  IP: 229.253.41.9     Response: no
17:  IP: 236.4.104.253    Response: no
18:  IP: 244.143.57.5     Response: no
19:  IP: 1.30.247.243     Response: no
20:  IP: 22.233.46.46     Response: no
21:  IP: 220.1.0.252      Response: no
22:  IP: 255.98.33.2      Response: no
23:  IP: 250.48.48.8      Response: no
24:  IP: 64.254.254.75    Response: no

Parse and recognize IPv4 addresses

[7]:
parse_tree = grammar.parse_string('0.1.2.3')
parse_tree
[7]:
%30ip1number0->12.0->23number0->34.0->45number0->56.0->67number0->78digit1->810digit3->1012digit5->1214digit7->14908->911110->1113212->1315314->15
[8]:
parse_tree = grammar.parse_string('192.168.249.255')
parse_tree
[8]:
%30ip1number0->12.0->23number0->34.0->45number0->56.0->67number0->7811->89digit1->910digit1->101313->1314digit3->1415digit3->151825->1819zero-four5->1920digit5->2023257->2324zero-five7->241199->1112210->1216614->1617815->1721419->2122920->2225524->25
[9]:
# Check that 2000 randomly generated IPv4 addresses are recognized as part of the language
for _ in range(2000):
    random_ip = generate_random_ip()
    if not grammar.recognize_string(random_ip):
        print('Valid IP was not recognized:', random_ip)

# Check that some invalid addresses are not part of the language
for invalid_address in ['255.255.255.256', '0.0.0.09', '13.014.22.101', '300.1.2.3']:
    if grammar.recognize_string(invalid_address):
        print('Invalid IP was recognized:', random_ip)

Optimization

Try to find the smallest and largest IPv4 addresses

Size of the search space: 4,294,967,296 elements

[10]:
print('Number of IPv4 addresses: 2^32 = {:,} or equivalently 256^4 = {:,}'.format(2**32, 256**4))
Number of IPv4 addresses: 2^32 = 4,294,967,296 or equivalently 256^4 = 4,294,967,296
[11]:
def objective_function(string):
    numbers = [int(s) for s in string.split('.')]
    return sum(numbers)
[12]:
ea = al.EvolutionaryAlgorithm(grammar, objective_function, 'min', max_generations=50)
best_individual = ea.run()
print(best_individual.phenotype)
0.0.0.0
[13]:
ea = al.EvolutionaryAlgorithm(grammar, objective_function, 'max', max_generations=50)
best_individual = ea.run()
print(best_individual.phenotype)
255.255.255.255