python increment ipaddress - python

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.

Related

Create an IP Address as a hex value

I am running the below function in python (3x) to generate a random IP Address
def get_ip_address(self):
x = ".".join(map(str, (random.randint(0, 255)
for _ in range(4))))
return x
However I need to convert the IP address generated into a hex value, I dont need to do anything complicated and am happy to either convert x post creation or do it in the create Ip address function. I need to be able to easily see what the IP address is I am creating and converting as this is part of a test Suite and at the other end the Hex IP Address is converted back. I also need it in the format 0xb15150ca
You're complicating things. Just take the IP address as an int and convert it into hex:
# Generate a random
>>> hex(random.randint(0,(1<<32)-1))
'0x85c90851'
>>> hex(random.randint(0,(1<<32)-1))
'0xfb4f592d'
If you always wish for it to be exactly 8 hex digits, and strip the 0x up front, you may as well format it straight like this:
>>> "{:0X}".format(random.randint(0,(1<<32)-1))
'4CC27A5E'
If you wish to know the IP, use the ipaddress module like so:
import ipaddress
>>> ip = random.randint(0,(1<<32)-1)
>>> ipaddress.ip_address(ip)
IPv4Address('238.53.246.162')
>>> "{:0X}".format(ip)
'EE35F6A2'
You can extend you function as follows:
def get_ip_address():
x = ".".join(map(str, (random.randint(0, 255)
for _ in range(4))))
# Split you created decimal IP in single numbers
ip_split_dec = str(x).split('.')
# Convert to hex
ip_split_hex = [int(t) for t in ip_split_dec]
# Construct hex IP adress
# ip_hex = '.'.join([hex(X)[2:] for X in ip_split_hex]) # Format hh.hh.hh.hh
ip_hex = '0x' + ''.join([hex(X)[2:] for X in ip_split_hex]) # Format 0xhhhhhhhh
return ip_hex
which will give you
>>> address = get_ip_address()
>>> 0xa535f08b
You can also combine this with the construction of your decimal IP to spare some code lines
Btw: As long as your function is no method of a class, theres is no need for the self in your function definition

Check if user's IP address is in a range of IP's

In my Python application I have an array of IP address strings which looks something like this:
[
"50.28.85.81-140", // Matches any IP address that matches the first 3 octets, and has its final octet somewhere between 81 and 140
"26.83.152.12-194" // Same idea: 26.83.152.12 would match, 26.83.152.120 would match, 26.83.152.195 would not match
]
I installed netaddr and although the documentation seems great, I can't wrap my head around it. This must be really simple - how do I check if a given IP address matches one of these ranges? Don't need to use netaddr in particular - any simple Python solution will do.
The idea is to split the IP and check every component separately.
mask = "26.83.152.12-192"
IP = "26.83.152.19"
def match(mask, IP):
splitted_IP = IP.split('.')
for index, current_range in enumerate(mask.split('.')):
if '-' in current_range:
mini, maxi = map(int,current_range.split('-'))
else:
mini = maxi = int(current_range)
if not (mini <= int(splitted_IP[index]) <= maxi):
return False
return True
Not sure this is the most optimal, but this is base python, no need for extra packages.
parse the ip_range, creating a list with 1 element if simple value, and a range if range. So it creates a list of 4 int/range objects.
then zip it with a split version of your address and test each value in range of the other
Note: Using range ensures super-fast in test (in Python 3) (Why is "1000000000000000 in range(1000000000000001)" so fast in Python 3?)
ip_range = "50.28.85.81-140"
toks = [[int(d)] if d.isdigit() else range(int(d.split("-")[0]),int(d.split("-")[1]+1)) for d in ip_range.split(".")]
print(toks) # debug
for test_ip in ("50.28.85.86","50.284.85.200","1.2.3.4"):
print (all(int(a) in b for a,b in zip(test_ip.split("."),toks)))
result (as expected):
[[50], [28], [85], range(81, 140)]
True
False
False

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

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

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]))

Categories

Resources