Find next available IP address in python - python

Using python I need to find the next IP address given a range of IP addresses I've already used. So if I have a list of IP address like...
IPs = ['10.220.1.1','10.220.1.2','10.220.1.3','10.220.1.5']
When I ask for the next IP address I need it to return '10.220.1.4'. The next request would return '10.220.1.6' and so on.

If you're using Python 3.3 (or newer), you can use the ipaddress module. Example for all hosts in the subnet 10.220.1.0/24 except for those in reserved:
from ipaddress import IPv4Network
network = IPv4Network('10.220.1.0/24')
reserved = {'10.220.1.1', '10.220.1.2', '10.220.1.3', '10.220.1.5'}
hosts_iterator = (host for host in network.hosts() if str(host) not in reserved)
# Using hosts_iterator:
print(next(hosts_iterator)) # prints 10.220.1.4
print(next(hosts_iterator)) # prints 10.220.1.6
print(next(hosts_iterator)) # prints 10.220.1.7
# Or you can iterate over hosts_iterator:
for host in hosts_iterator:
print(host)
So basically this can be done in a single line (+ imports and definition of network and reserved addresses).

If you're using Python 3 you could use ipaddress with generator:
import ipaddress
def gen_ip(start, reserved):
start = int(ipaddress.IPv4Address(start))
for i in range(start + 1, int(2 ** 32) + 1):
ip = str(ipaddress.IPv4Address(i))
if ip not in reserved:
yield ip
IPs = ['10.220.1.1','10.220.1.2','10.220.1.3','10.220.1.5']
j = 0
for ip in gen_ip(min(IPs), set(IPs)):
print(ip)
j += 1
if j == 5:
break
Output:
10.220.1.4
10.220.1.6
10.220.1.7
10.220.1.8
10.220.1.9

You can use an generator using the ipaddress module which is a bultin from python >= 3.3 or you can install with pip for earlier versions:
In [20]: from ipaddress import ip_network
In [21]: IPs = {'10.220.1.1','10.220.1.2','10.220.1.3','10.220.1.5'}
In [22]: net = ip_network(u"10.220.1.0/24")
In [23]: avail =(str(ip) for ip in net.hosts() if str(ip) not in IPs
....: )
In [24]: next(avail)
Out[24]: '10.220.1.4'
In [25]: next(avail)
Out[25]: '10.220.1.6'

Convert your list to a set, for performance:
used_ips = set(IPs)
Now generate your IP#'s however you would like, and check if they are contained in the set:
for next_ip in generate_ip_numbers():
if next_ip in used_ips:
continue
print("Next ip address is:", next_ip)

Create a basic ip object to hold a record of your current ip and to get the next ip
class IPObj(object):
def __init__(self, list_of_ips):
self.position = 0
self.ips = list_of_ips
self.current_ip = self.ips[self.position]
def next_ip(self, stop_iteration=False):
'''
return the next ip in the list
'''
if self.position >= len(self.ips)-1:
self.position = 0 # By default next_ip will do nothing when it reaches the end but it will throw an exception if stop_iteration==True
if stop_iteration:
raise StopIteration
self.position += 1
self.current_ip = self.ips[self.position]
return self.current_ip
def __repr__(self):
return repr(self.current_ip)
#Testing
ips = IPObj(['10.220.1.1','10.220.1.2','10.220.1.3','10.220.1.5'])
print ips
while True:
print ips.next_ip(True),
Output:
10.220.1.1,
10.220.1.2,
10.220.1.3,
10.220.1.5,
Traceback (most recent call last):
File "C:\Users\J10ey\workspace\SO_Help\src\ip's.py", line 32, in
print ips.next_ip(True)
File "C:\Users\J10ey\workspace\SO_Help\src\ip's.py", line 21, in next_ip
raise StopIteration
StopIteration

Related

Increment an IP address when the IP address is given in steps in python

How do I increment the IP address in python when we have start_ip in 4 octets format and the step also in 4 octet format.
Let's suppose I start with an IP address of 225.1.1.1, and the step as 0.0.0.1
When I say next_ip = start_ip + step, it should actually evaluate the expand produce the result.
I know that the ipaddr module does this with just an addition, but that does not seem to work when the step is also given in the ipv4 format.
Any known steps to do this:
import ipaddr
a = ipaddr.ipaddress('225.1.1.1')
b = a +1
This actually returns the desired result. but when increment like this:
b = a + 0.0.0.1 it does not seem to work.
Any known solutions for this?
Not refined but works i guess, here is the python snippet
def increment_ip_addr(ip_addr):
ip_arr = ip_addr.split('.')
def increment_by_1(digit):
return str(int(digit) + 1)
if int(ip_arr[3]) > 254:
ip_arr[3] = "1"
if int(ip_arr[2]) > 254:
ip_arr[2] = "1"
if int(ip_arr[1]) > 254:
ip_arr[1] = "1"
if int(ip_arr[0]) > 254:
ip_arr[0] = "1"
else:
ip_arr[0] = increment_by_1(ip_arr[0])
else:
ip_arr[1] = increment_by_1(ip_arr[1])
else:
ip_arr[2] = increment_by_1(ip_arr[2])
else:
ip_arr[3] = increment_by_1(ip_arr[3])
print(f"for ip address: {ip_addr} The incremented ip is: {'.'.join(ip_arr)}")
[increment_ip_addr(ip_addr) for ip_addr in ['1.1.1.1', "1.1.1.255", "1.255.1.255", "255.255.255.255"]]
corresponding console output:
for ip address: 1.1.1.1 The incremented ip is: 1.1.1.2
for ip address: 1.1.1.255 The incremented ip is: 1.1.2.1
for ip address: 1.255.1.255 The incremented ip is: 1.255.2.1
for ip address: 255.255.255.255 The incremented ip is: 1.1.1.1
Let me know in case of optimized version
this piece of code can add two 4 octets :
first = '192.168.0.4'
second = '0.0.0.1'
ipaddress.ip_address(first) + int(ipaddress.ip_address(second))
this will result in: IPv4Address('192.168.0.5')

get next subnet in CIDR format

I have the following:-
start_range = "10.40.0.0/16"
end_range = "10.100.0.0/16"
I have to write a logic to iterate over all possible ranges(with same subnet mask /16) from start to end. For each subnet I will be doing some processing and then continuing to the next one.
I could achieve this in trivial way where I know, I have to increment last network octate(i.e increment 40 to 41 -> 42 -> 43 and so on).
start_subnet = "10.40.0.0/16"
end_subnet = "10.100.0.0/16"
start_val = int(start_subnet.split(".")[1])
end_val = int(end_subnet.split('.')[1])
subnet_split = start_subnet.split(".")
subnet_split[1] = "{}"
subnet_proto = ".".join(subnet_split) # "10.{}.0.0/16"
for i in range(start_val, end_val+1): # iterate from 40 to 100
cur_subnet = subnet_proto.format(i) # "10.40.0.0/16", "10.41.0.0/16" etc
# do processing on cur_subnet
Is there a better(Pythonic) way to get the next subnet(in CIDR format). May be netaddr module has something I am not aware of?
Following usage of netaddr helped me to get the expected result.
from netaddr import IPNetwork
start_range = IPNetwork("10.40.0.0/16")
end_range = IPNetwork("10.45.0.0/16")
allowed_range = []
while start_range<=end_range:
allowed_range.append(start_range)
start_range = start_range.next()
print allowed_range
This would print the following:-
[IPNetwork('10.40.0.0/16'), IPNetwork('10.41.0.0/16'),
IPNetwork('10.42.0.0/16'), IPNetwork('10.43.0.0/16'),
IPNetwork('10.44.0.0/16'), IPNetwork('10.45.0.0/16')]
This might be what you have in mind (not really well thought-through or tested, you've been warned!).
The ipaddress module is from python3 but it has a backport, just run
pip install ipaddress
to get it.
import ipaddress
def subnet_range(start_subnet, end_subnet):
start = ipaddress.ip_network(unicode(start_subnet))
end = ipaddress.ip_network(unicode(end_subnet))
assert start.prefixlen == end.prefixlen
ranges = [
n
for ipaddr in ipaddress.summarize_address_range(
list(start)[0],
list(end)[0])
for n in ipaddr.subnets(new_prefix=start.prefixlen)][:-1]
ranges.append(end)
return ranges
if __name__ == "__main__":
start_subnet = "9.250.0.0/16"
end_subnet = "10.100.0.0/16"
for r in subnet_range(start_subnet, end_subnet):
print r

Python script to print nslookup

import os
ipRange = []
for i in range(1, 254):
ipRange.append('192.168.5' + '.' + str(i))
for e in ipRange:
print os.system('nslookup ' + str(e))
This outputs the full output of nslookup for each ip - is there a way to discard the empty results and make the output look more like this?
192.168.5.5 testbox4
192.168.5.6 box3
192.168.5.8 hellobox
192.168.5.9 server2012
192.168.5.18 dnsbox
192.168.5.19 sallysbox
192.168.5.20 bobsbox
192.168.5.21 serverx
Do you need to use system? This would do without system calls:
import socket
for i in range(0, 255):
ipa = "130.233.192." + str(i)
try:
a = socket.gethostbyaddr(ipa)
print (ipa, a[0])
except socket.herror:
pass
EDIT: change 255 to 256 if you want to query .255 as well but in class C networks this is the broadcast address and not in DNS. If you are trawling through class A or B networks, then .255 could be valid as well

Python - how do you get broadcast from ip?

I'm using python ipaddr it's working but if I wanna open a text file where i'm getting an ip address i'm getting an error.
This is What I have:
import ipaddr
from itertools import islice
def address1():
with open('address.txt','r+') as file:
lines = islice(file, 1, 5)
for line in lines:
found_address = line.find('Address')
if found_address != -1:
address = line[found_address+len('Address:'):] #address = 192.168.0.9/25
mask = ipaddr.IPv4Network(address)
resultado = mask.broadcast
print resultado
return resultado
def get_network():
addr = '192.168.0.9/25'
mask = ipaddr.IPv4Network(addr)
resultado_broadcast = mask.broadcast
print resultado_broadcast
return resultado_broadcast
#address1() #if I comment out this line and I run the next one works...
get_network() #if I run this one works...
My ip address from address.txt :
Address 192.168.0.9/25
the same I used in get_network()
So why I'm getting error in address1()?
output of get_network()
As you can see works... but if I run address1()
I got this error.
Does anyone know how to make address1() work? is it possible?
thanks...!

Using a RegEx to match IP addresses

I'm trying to make a test for checking whether a sys.argv input matches the RegEx for an IP address...
As a simple test, I have the following...
import re
pat = re.compile("\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}")
test = pat.match(hostIP)
if test:
print "Acceptable ip address"
else:
print "Unacceptable ip address"
However when I pass random values into it, it returns "Acceptable IP address" in most cases, except when I have an "address" that is basically equivalent to \d+.
Using regex to validate IP address is a bad idea - this will pass 999.999.999.999 as valid. Try this approach using socket instead - much better validation and just as easy, if not easier to do.
import socket
def valid_ip(address):
try:
socket.inet_aton(address)
return True
except:
return False
print valid_ip('10.10.20.30')
print valid_ip('999.10.20.30')
print valid_ip('gibberish')
If you really want to use parse-the-host approach instead, this code will do it exactly:
def valid_ip(address):
try:
host_bytes = address.split('.')
valid = [int(b) for b in host_bytes]
valid = [b for b in valid if b >= 0 and b<=255]
return len(host_bytes) == 4 and len(valid) == 4
except:
return False
You have to modify your regex in the following way
pat = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
that's because . is a wildcard that stands for "every character"
regex for ip v4:
^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
otherwise you take not valid ip address like 999.999.999.999, 256.0.0.0 etc
I came across the same situation, I found the answer with use of socket library helpful but it doesn't provide support for ipv6 addresses. Found a better way for it:
Unfortunately, it Works for python3 only
import ipaddress
def valid_ip(address):
try:
print (ipaddress.ip_address(address))
return True
except:
return False
print (valid_ip('10.10.20.30'))
print (valid_ip('2001:DB8::1'))
print (valid_ip('gibberish'))
You are trying to use . as a . not as the wildcard for any character. Use \. instead to indicate a period.
def ipcheck():
# 1.Validate the ip adderess
input_ip = input('Enter the ip:')
flag = 0
pattern = "^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$"
match = re.match(pattern, input_ip)
if (match):
field = input_ip.split(".")
for i in range(0, len(field)):
if (int(field[i]) < 256):
flag += 1
else:
flag = 0
if (flag == 4):
print("valid ip")
else:
print('No match for ip or not a valid ip')
import re
ipv=raw_input("Enter an ip address")
a=ipv.split('.')
s=str(bin(int(a[0]))+bin(int(a[1]))+bin(int(a[2]))+bin(int(a[3])))
s=s.replace("0b",".")
m=re.search('\.[0,1]{1,8}\.[0,1]{1,8}\.[0,1]{1,8}\.[0,1]{1,8}$',s)
if m is not None:
print "Valid sequence of input"
else :
print "Invalid input sequence"
Just to keep it simple I have used this approach.
Simple as in to explain how really ipv4 address is evaluated.
Checking whether its a binary number is although not required.
Hope you like this.
str = "255.255.255.255"
print(str.split('.'))
list1 = str.split('.')
condition=0
if len(list1)==4:
for i in list1:
if int(i)>=0 and int(i)<=255:
condition=condition+1
if condition!=4:
print("Given number is not IP address")
else:
print("Given number is valid IP address")
If you really want to use RegExs, the following code may filter the non-valid ip addresses in a file, no matter the organiqation of the file, one or more per line, even if there are more text (concept itself of RegExs) :
def getIps(filename):
ips = []
with open(filename) as file:
for line in file:
ipFound = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$").findall(line)
hasIncorrectBytes = False
try:
for ipAddr in ipFound:
for byte in ipAddr:
if int(byte) not in range(1, 255):
hasIncorrectBytes = True
break
else:
pass
if not hasIncorrectBytes:
ips.append(ipAddr)
except:
hasIncorrectBytes = True
return ips
re.sub('((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])', '--', '127.0.0.1')
With this regular expression, only numbers from 0 to 255 could compose the address. It also handles leading zeros, so 127.00.0.1 would no pass.
IP address uses following authentication :
255 ---> 250-255
249 ---> 200-249
199 ---> 100-199
99 ---> 10-99
9 ---> 1-9
import re
k = 0
while k < 5 :
i = input("\nEnter Ip address : ")
ip = re.match("^([1][0-9][0-9].|^[2][5][0-5].|^[2][0-4][0-9].|^[1][0-9][0-9].|^[0-9][0-9].|^[0-9].)([1][0-9][0-9].|[2][5][0-5].|[2][0-4][0-9].|[1][0-9][0-9].|[0-9][0-9].|[0-9].)([1][0-9][0-9].|[2][5][0-5].|[2][0-4][0-9].|[1][0-9][0-9].|[0-9][0-9].|[0-9].)([1][0-9][0-9]|[2][5][0-5]|[2][0-4][0-9]|[1][0-9][0-9]|[0-9][0-9]|[0-9])$",i)
k = k + 1
if ip:
print ("\n=====================")
print ("Valid IP address")
print ("=====================")
break
else :
print ("\nInvalid IP")
else :
print ("\nAllowed Max 5 times")
Reply me if you have doubt?
import re
st1 = 'This is my IP Address10.123.56.25 789.356.441.561 127 255 123.55 192.168.1.2.3 192.168.2.2 str1'
Here my valid IP Address is only 192.168.2.2 and assuming 10.123.56.25 is not a valid one as it is combined with some string and 192.168.1.2.3 not valid.
pat = r'\s(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\s|$))'
match = re.search(pat,st1)
print match.group()
================ RESTART: C:/Python27/Srujan/re_practice.py ================
192.168.2.2
This will grep the exact IP Address, we can ignore any pattern look like an IP Address but not a valid one. Ex: 'Address10.123.56.25', '789.356.441.561' '192.168.1.2.3'.
Please comment if any modifications are required.
This works for python 2.7:
import re
a=raw_input("Enter a valid IP_Address:")
b=("[0-9]+"+".")+"{3}"
if re.match(b,a) and b<255:
print "Valid"
else:
print "invalid"
""" regex for finding valid ip address """
import re
IPV4 = re.fullmatch('([0-2][0-5]{2}|\d{2}|\d).([0-2][0-5]{2}|\d{2}|\d).([0-2][0-5]{2}|\d{2}|\d).([0-2][0-5]{2}|\d{2}|\d)', '100.1.1.2')
if IPV4:
print ("Valid IP address")
else:
print("Invalid IP address")

Categories

Resources