How to get first/last IP address of CIDR using ipaddr module - python

The brute force approach:
from ipaddr import IPv4Network
n = IPv4Network('10.10.128.0/17')
all = list(n.iterhosts()) # will give me all hosts in network
first,last = all[0],all[-1] # first and last IP
I was wondering how I would get the first and last IP address from a CIDR without having to iterate over a potentially very large list to get the first and last element?
I want this so I can then generate a random ip address in this range using something like this:
socket.inet_ntoa(struct.pack('>I', random.randint(int(first),int(last))))

From Python 3.3, you can use the ipaddress module
You could use it like this:
import ipaddress
n = ipaddress.IPv4Network('10.10.128.0/17')
first, last = n[0], n[-1]
__getitem__ is implemented, so it won't generate any large lists.
https://github.com/python/cpython/blob/3.6/Lib/ipaddress.py#L634

Maybe try netaddr instead, in particular the indexing section.
https://pythonhosted.org/netaddr/tutorial_01.html#indexing
from netaddr import *
import pprint
ip = IPNetwork('10.10.128.0/17')
print "ip.cidr = %s" % ip.cidr
print "ip.first.ip = %s" % ip[0]
print "ip.last.ip = %s" % ip[-1]

The python 3 ipaddress module is the more elegant solution, imho. And, by the way, it works fine, but the ipaddress module doesn't return exactly the first and last free ip addresses at indexes [0,-1], but respectively the network address and the broadcast address.
The first and last free and assignable addresses are rather
import ipaddress
n = ipaddress.IPv4Network('10.10.128.0/17')
first, last = n[1], n[-2]
which will return 10.10.128.1 as first and 10.10.255.254 instead of 10.10.128.0 and 10.10.255.255

Related

How do I slice the last 11 IP's off of this 192.168.0.0/24 network using the ipaddress module?

Completely new to coding.
What I'm trying to do: Return IP addresses in the 192.168.0.0/24 network that doesn't include even numbered IP's or the last 10 (so dont include 192.168.0.244 to 192.168.0.254)
Some images:
In this image, each IP is considered as 4 separate integers, is that right?
Here is where I've tried to put in an empty list and append only the uneven IP's to valid_net, but it came back with weird results. So I tried splitting it again but it comes back with multiple lists (an array?) and I can't use valid_net = valid_net[:-11] to cut out the last 10 IP's because it comes back with an error
The error, (only returns empty [])
Some testing I was doing to see how it works...
The code for you if needed:
import ipaddress
net = ipaddress.IPv4Network('192.168.0.0/24')
for addresses in net:
allowable_ip = int(addresses.exploded.split('.')[-1]) % 2 == 0
if not allowable_ip:
print(addresses)
and
import ipaddress
net = ipaddress.IPv4Network('192.168.0.0/24')
valid_net = []
for addresses in net:
allowable_ip = int(addresses.exploded.split('.')[-1]) % 2 == 0
if not allowable_ip:
valid_net = (addresses.exploded.split(','))
print(valid_net[:-11])
Thank you to anyone who helps!
net = ipaddress.IPv4Network('192.168.0.0/24') is a generator, you can make a list of the values it generates by calling list on it, then slice it.
You can then keep only the addresses whose int value is even:
import ipaddress
net = ipaddress.IPv4Network('192.168.0.0/24')
# we make a list of the addresses and exclude the last 11 ones
addresses = list(net)[:-12]
# and only keep the odd ones
addresses = [address for address in addresses if int(address) % 2 == 1]
print(addresses)
# [IPv4Address('192.168.0.1'), IPv4Address('192.168.0.3'), IPv4Address('192.168.0.5'),
# ...
# IPv4Address('192.168.0.239'), IPv4Address('192.168.0.241'), IPv4Address('192.168.0.243')]

Suggesting the next available IP network block

I was wondering if there's a good way to find the next available gap to create a network block given a list of existing ones?
For example, I have these networks in my list:
[
'10.0.0.0/24',
'10.0.0.0/20',
'10.10.0.0/20',
]
and then someone comes along and ask: "Do you have have enough space for 1 /22 for me?"
I'd like to be able to suggest something along the line:
"Here's a space: x.x.x.x/22" (x.x.x.x is something that comes before 10.0.0.0)
or
"Here's a space: x.x.x.x/22" (x.x.x.x is something in between 10.0.0.255 and 10.10.0.0)
or
"Here's a space: x.x.x.x/22" (x.x.x.x is something that comes after 10.10.15.255)
I'd really appreciate any suggestions.
The ipaddress library is good for this sort of use case. You can use the IPv4Network class to define subnet ranges, and the IPv4Address objects it can return can be converted into integers for comparison.
What I do below:
Establish your given list as a list of IPv4Networks
Determine the size of the block we're looking for
Iterate through the list, computing the amount of space between consecutive blocks, and checking if our wanted block fits.
You could also return an IPv4Network with the subnet built into it, instead of an IPv4Address, but I'll leave that as an exercise to the reader.
from ipaddress import IPv4Network, IPv4Address
networks = [
IPv4Network('10.0.0.0/24')
IPv4Network('10.0.0.0/20')
IPv4Network('10.0.10.0/20')
]
wanted = 22
wanted_size = 2 ** (32 - wanted) # number of addresses in a /22
space_found = None
for i in range(1, len(networks):
previous_network_end = int(networks[i-1].network_address + int(networks[i-1].hostmask))
next_network_start = int(networks[i].network_address)
free_space_size = next_network_start - previous_network_end
if free_space_size >= wanted_size:
return IPv4Address(networks[i-1] + 1) # first available address

Getting the first three bytes of an IP address

I want to use an IP address string, ie: 192.168.1.23 but only keep the first three bytes of the IP address and then append 0-255. I want to transform that IP address into a range of IP address' I can pass to NMAP to conduct a sweep scan.
The easiest solution of course is to simply trim off the last two characters of the string, but of course this won't work if the IP is 192.168.1.1 or 192.168.1.123
Here is the solution I came up with:
lhost = "192.168.1.23"
# Split the lhost on each '.' then re-assemble the first three parts
lip = self.lhost.split('.')
trange = ""
for i, val in enumerate(lip):
if (i < len(lip) - 1):
trange += val + "."
# Append "0-255" at the end, we now have target range trange = "XX.XX.XX.0-255"
trange += "0-255"
It works fine but feels ugly and not efficient to me. What is a better way to do this?
You could use the rfind function of string object.
>>> lhost = "192.168.1.23"
>>> lhost[:lhost.rfind(".")] + ".0-255"
'192.168.1.0-255'
The rfind function is similar with find() but searching from the end.
rfind(...)
S.rfind(sub [,start [,end]]) -> int
Return the highest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Return -1 on failure.
A more complicate solution could use regular express as:
>>> import re
>>> re.sub("\d{1,3}$","0-255",lhost)
'192.168.1.0-255'
Hope it be helpful!
You could split and get the first three values, join by a '.', and then add ".0-255"
>>> lhost = "192.168.1.23"
>>> '.'.join(lhost.split('.')[0:-1]) + ".0-255"
'192.168.1.0-255'
>>>
Not all IPs belong to class C. I think that the code must be flexible to accommodate various IP ranges and their masks,
I had previously written a tiny python module to calculate network ID< broadcast ID for a given IP address with any network mask.
code can be found here : https://github.com/brownbytes/tamepython/blob/master/subnet_calculator.py
I think networkSubnet() and hostRange() are functions which can be of some help to you.
I like this:
#!/usr/bin/python3
ip_address = '128.200.34.1'
list_ = ip_address.split('.')
assert len(list_) == 4
list_[3] = '0-255'
print('.'.join(list_))

Best approach to detect subnet overlap in a postgresql db

I've got a postgres db with nearly 200'000 network address types.
I'd like to detect if some subnets are overlapping themselves, for ex, detect 123.0.0.0/16, 123.2.0.0/24 and 123.3.4.128/30 and report them.
I'm already using a lot of python scripts and netaddr library.
Considering the number of entries, what would be the best approach/algorithm to detect overlaps?
I'm pretty sure there's a better way than comparing each entry to the whole database.
I think the following should be a fairly efficient approach:
import netaddr
import bisect
def subnets_overlap(subnets):
# ranges will be a sorted list of alternating start and end addresses
ranges = []
for subnet in subnets:
# find indices to insert start and end addresses
first = bisect.bisect_left(ranges, subnet.first)
last = bisect.bisect_right(ranges, subnet.last)
# check the overlap conditions and return if one is met
if first != last or first % 2 == 1:
return True
ranges[first:first] = [subnet.first, subnet.last]
return False
Examples:
>>> subnets_overlap([netaddr.IPNetwork('1.0.0.0/24'), netaddr.IPNetwork('1.0.0.252/30')])
True
>>> subnets_overlap([netaddr.IPNetwork('1.0.0.0/24'), netaddr.IPNetwork('1.0.1.0/24')])
False
import sys
import ipaddr
from pprint import pprint
from netaddr import IPNetwork, IPAddress
matching_subent=[]
def cidrsOverlap(cidr0):
subnets_list = [IPNetwork('123.0.0.0/16'),
IPNetwork('123.2.0.0/24'),
IPNetwork('123.132.0.0/20'),
IPNetwork('123.142.0.0/20')]
flag = False
for subnet in subnets_list:
if (subnet.first <= cidr0.last and subnet.last >= cidr0.last):
matching_subent.append(subnet)
print "Matching subnets for given %s are %s" %(cidr0, matching_subent)
pprint(subnets_list)
cidrsOverlap(IPNetwork(sys.argv[1]))

python increment ipaddress

I would like to increment an ip address by a fixed value.
Precisely this is what I am trying to achieve, I have an ip address say, 192.168.0.3 and I want to increment it by 1 which would result in 192.168.0.4 or even by a fixed value, x so that it will increment my ip address by that number. so, I can have a host like 192.168.0.3+x.
I just want to know if any modules already exist for this conversion.
I tried socket.inet_aton and then socket.inet_ntoa, but I don't know how to get that working properly. Need some help or advice on that.
In Python 3:
>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.4') # accept both IPv4 and IPv6 addresses
IPv4Address('192.168.0.4')
>>> int(_)
3232235524
>>> ipaddress.ip_address('192.168.0.4') + 256
IPv4Address('192.168.1.4')
In reverse:
>>> ipaddress.ip_address(3232235524)
IPv4Address('192.168.0.4')
>>> str(_)
'192.168.0.4'
>>> ipaddress.ip_address('192.168.0.4') -1
IPv4Address('192.168.0.3')
Python 2/3
You could use struct module to unpack the result of inet_aton() e.g.,
import struct, socket
# x.x.x.x string -> integer
ip2int = lambda ipstr: struct.unpack('!I', socket.inet_aton(ipstr))[0]
print(ip2int("192.168.0.4"))
# -> 3232235524
In reverse:
int2ip = lambda n: socket.inet_ntoa(struct.pack('!I', n))
print(int2ip(3232235525))
# -> 192.168.0.5
From python 3.4 onwards:
>>> import ipaddress
>>> a = ipaddress.IPv4Address('192.168.0.1')
>>> a+500
IPv4Address('192.168.1.245')
>>> a = ipaddress.IPv6Address('2001:1900:2254:206a::50:0')
>>> a+200
IPv6Address('2001:1900:2254:206a::50:c8')
>>>
There's a module that makes this and other tasks very easy: pip install iptools.
In [1]: import iptools
In [3]: iptools.ip2long('127.0.0.1')
Out[3]: 2130706433
In [4]: p = iptools.ip2long('127.0.0.1') + 1
In [6]: iptools.long2ip(p)
Out[6]: '127.0.0.2'
Convert the last part of your IP address into a number, add 1 to it, and call ifconfig.
I think the approach of incrementing the last bit will not scale well as we span across networks. –OP
I thought of mentioning that in my original answer, but didn't, for various reasons. These reasons are as follows:
I thought it is unlikely you would need to do this, and could not guess why you'd want to.
Even if you did need to do this, you could just parse the second-to-last number.
This is only valid for those bits where the netmask is 0.
You also have to worry about "special" reserved IP ranges, such as 192.168.etc.etc. Also hex doublets with 0 and possibly ff/255 have special meaning. There are different rules in IPv6.
It might be quicker to just use simple addition and iteration, something like:
ip = [192,168,0,0]
ip_dict = {}
ip_list = []
for i in range(100):
new_ip = ip[3]+=1
ip_dict[i]=new_ip
ip_list.append(new_ip)
EDIT: This is buggy and shouldn't be used as is.
I would use ipaddr for this
>>> import ipaddr
>>> a = ipaddr.IPAddress('192.168.0.3')
>>> a
IPv4Address('192.168.0.3')
>>> a + 1
IPv4Address('192.168.0.4')
The library ipcalc has routines to make math on ip addresses fairly easy. As an example an iterator for an address range can be done like:
Code:
import ipcalc
network = ipcalc.Network('10.1.0.0/16')
host_first = network.host_first()
addresses = (host_first + i for i in range(network.size()-2))
Test Code:
print(next(addresses))
print(next(addresses))
print(next(addresses))
print(max(list(addresses)))
Results:
10.1.0.1
10.1.0.2
10.1.0.3
10.1.255.254
def FunIncrementIp(IPADDRESS,IPADDRESSES):
#import the ipaddress module and also check whether it is an ipv6 or ipv4
import ipaddress
if ':' in IPADDRESS:
IPADDRESSMOD = ipaddress.IPv6Address(IPADDRESS)
print ('this is ipv6 address')
else:
IPADDRESSMOD = ipaddress.IPv4Address(IPADDRESS)
print ('this is ipv4 address')
IPADDRESSES = int(c)
IPADDRESSES = IPADDRESSMOD+IPADDRESSES
while IPADDRESSMOD < IPADDRESSES:
IPADDRESSMOD += 1
print(IPADDRESSMOD)
This should do it.
FunIncrementIp('1.1.1.1','10')
This will increment your ipv4 addresses to 10 more
FunIncrementIp('2001:db8:0:1:1:1:1:1','10')
This will increment your ipv6 addresses to 10 more
This will also tell auto detect the type of ip address so that you don't have to have separate script for ipv4 & ipv6.

Categories

Resources