How can I generate all possible IPs from a list of ip ranges in Python?
Asked Answered
L

4

7

Let's say I have a text file contains a bunch of ip ranges like this:

x.x.x.x-y.y.y.y
x.x.x.x-y.y.y.y
x.x.x.x-y.y.y.y
x.x.x.x-y.y.y.y
x.x.x.x-y.y.y.y

x.x.x.x is start value and y.y.y.y is end value of range.

How can I convert these ip ranges to all possible IPs in a new text file in python?

PS: This question is not same as any of my previous questions. I asked "how to generate all possible ips from cidr notations" in my previous question. But in here I ask "how to generate from ip range list". These are different things.

Lofty answered 14/7, 2013 at 16:52 Comment(1)
Is it just a file of IPv4 addresses? Or are there IPv6s in there too?Perfectionism
E
15

This function returns all ip addresses like from start to end:

def ips(start, end):
    import socket, struct
    start = struct.unpack('>I', socket.inet_aton(start))[0]
    end = struct.unpack('>I', socket.inet_aton(end))[0]
    return [socket.inet_ntoa(struct.pack('>I', i)) for i in range(start, end)]

These are the building blocks to build it on your own:

>>> import socket, struct
>>> ip = '0.0.0.5'
>>> i = struct.unpack('>I', socket.inet_aton(ip))[0]
>>> i
5
>>> i += 1
>>> socket.inet_ntoa(struct.pack('>I', i))
'0.0.0.6'

Example:

ips('1.2.3.4', '1.2.4.5')
['1.2.3.4', '1.2.3.5', '1.2.3.6', '1.2.3.7', ..., '1.2.3.253', '1.2.3.254', '1.2.3.255', '1.2.4.0', '1.2.4.1', '1.2.4.2', '1.2.4.3', '1.2.4.4']

Read from file

In your case you can read from a file like this:

with open('file') as f:
    for line in f:
        start, end = line.strip().split('-')
        # ....
Eponym answered 14/7, 2013 at 17:2 Comment(2)
why is this not marked as accepted answer? @Leadri reading text from files in python is a basic task that you can find hundreds of other posts that can show you how to accomplish just that. The original matter in question is clearly answered here and for the rest you'll need to make an effort with google/duckduckgo.Corkage
This is great- thanks. Suggest that the inputs for start and end would probably be expected to be inclusive, in which case last line would be range(start, end + 1)].Acetometer
T
10

Python 3 only, for IPv4, same idea with @User but use new Python3 standard library: ipaddress

IPv4 is represented by 4 bytes. So next IP is actually next number, a range of IPs can be represented as a range of integer numbers.

0.0.0.1 is 1

0.0.0.2 is 2

...

0.0.0.255 is 255

0.0.1.0 is 256

0.0.1.1 is 257

By code (ignore the In []: and Out []:)

In [68]: from ipaddress import ip_address

In [69]: ip_address('0.0.0.1')
Out[69]: IPv4Address('0.0.0.1')

In [70]: ip_address('0.0.0.1').packed
Out[70]: b'\x00\x00\x00\x01'

In [71]: int(ip_address('0.0.0.1').packed.hex(), 16)
Out[71]: 1

In [72]: int(ip_address('0.0.1.0').packed.hex(), 16)
Out[72]: 256

In [73]: int(ip_address('0.0.1.1').packed.hex(), 16)
Out[73]: 257

ip.packed.hex() returns the hexadecimal form of 4 bytes, as it is in hexadecimal, it is shorter (e.g: 0xff hex == 255 decimal == 0b11111111 binary), and thus, often used for representing bytes. int(hex, 16) returns integer value corresponding to the hex value as it is more human friendly, and can be used as input for ip_address.

from ipaddress import ip_address

def ips(start, end):
    '''Return IPs in IPv4 range, inclusive.'''
    start_int = int(ip_address(start).packed.hex(), 16)
    end_int = int(ip_address(end).packed.hex(), 16)
    return [ip_address(ip).exploded for ip in range(start_int, end_int)]


ips('192.168.1.240', '192.168.2.5')

Returns:

['192.168.1.240',
 '192.168.1.241',
 '192.168.1.242',
 '192.168.1.243',
 '192.168.1.244',
 '192.168.1.245',
 '192.168.1.246',
 '192.168.1.247',
 '192.168.1.248',
 '192.168.1.249',
 '192.168.1.250',
 '192.168.1.251',
 '192.168.1.252',
 '192.168.1.253',
 '192.168.1.254',
 '192.168.1.255',
 '192.168.2.0',
 '192.168.2.1',
 '192.168.2.2',
 '192.168.2.3',
 '192.168.2.4']
Tetraploid answered 18/10, 2017 at 2:20 Comment(1)
Note that this approach isn't inclusive of the end of the ip range. That will yield [] for a range of, say, 1.1.1.1 - 1.1.1.1 rather than a potentially expected ['1.1.1.1']. To be inclusive, change the range in the list comprehension to range(start_int, end_int + 1)Algorithm
F
3

try This:

def ip_range(start_ip,end_ip):
start = list(map(int,start_ip.split('.')))
end = list(map(int,end_ip.split('.')))
iprange=[]
while start!=list(map(int,end_ip.split('.'))):
    for i in range(len(start)-1,-1,-1):
        if start[i]<255:
            start[i]+=1
            break
        else:
            start[i]=0
    iprange.append('.'.join(map(str,start)))
return iprange
Friedly answered 17/9, 2014 at 0:42 Comment(0)
E
2

Use ipaddress and range()

import ipaddress

print([ipaddress.ip_address(i).exploded for i in range(int(ipaddress.ip_address(first_ip)), int(ipaddress.ip_address(last_ip)))])
Entelechy answered 16/2, 2022 at 7:58 Comment(3)
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Grimy
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewSanskritic
Your answer is the best and the easiest one to understand, comparing to the others. No need to write one more words. This website is supposed to serve developers/engineers. If one can't understand such a simple line of code, please shutdown the computer and go do something else. (however, your answer is also not inclusive of the end, need to plus 1 to the end, like this int(ipaddress.ip_address(last_ip)) + 1.)Mccrae

© 2022 - 2024 — McMap. All rights reserved.