Related
In Python, I am trying to use the J1939 filtering as mentionned in the linux kernel docs: https://www.kernel.org/doc/html/latest/networking/j1939.html
The following code fails at the setsockopt() line (setting up filters):
import socket
import struct
def pack_J1939_filters(can_filters):
can_filter_fmt = "=" + "2Q2B2I" * len(can_filters)
filter_data = []
for can_filter in can_filters:
name = can_filter['name']
name_mask = can_filter['name_mask']
addr = can_filter['addr']
addr_mask = can_filter['addr_mask']
pgn = can_filter['pgn']
pgn_mask = can_filter['pgn_mask']
filter_data.append(name)
filter_data.append(name_mask)
filter_data.append(addr)
filter_data.append(addr_mask)
filter_data.append(pgn)
filter_data.append(pgn_mask)
return struct.pack(can_filter_fmt, *filter_data)
s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939)
interface = "vcan0"
src_name = socket.J1939_NO_NAME
src_pgn = socket.J1939_NO_PGN
src_addr = 0x81
src_sck_addr = (interface, src_name, src_pgn, src_addr)
s.bind(src_sck_addr)
filters = [{"name": 0, "name_mask":0, "addr":0, "addr_mask":0, "pgn": 0, "pgn_mask": 0}]
packed_filters = pack_J1939_filters(filters)
# socket.SOL_CAN_J1939 does not seem to exist
SOL_CAN_BASE = 100
CAN_J1939 = 7
SOL_CAN_J1939 = SOL_CAN_BASE + CAN_J1939
s.setsockopt(SOL_CAN_J1939, socket.SO_J1939_FILTER , packed_filters)
s.recvfrom(128)
s.close()
First, the kernel documentation mentions to use SOL_CAN_J1939 as the first argument. However socket.SOL_CAN_J1939 does not exist in the socket package. So looking at the code at this location I was able to understand that this int value should be 107: http://socket-can.996257.n3.nabble.com/RFC-v3-0-6-CAN-add-SAE-J1939-protocol-td7571.html
As for the setsockopt() third argument, I packed the filters to match the j1939_filter structure (26 bytes as described in the code from the previous link). This is similar to what is done in can.interfaces.socketcan.utils for raw CAN.
What am I doing wrong to cause setsockopt() to fail?
The first issue was with the struct.pack format (can_filter_fmt) being wrong. I first assumed that the kernel j1939_filter structure size was the sum of the members. This is wrong since the compiler adds padding. This can be added to the struct.pack format as x such as 2Q2I2B6x. Please see Why isn't sizeof for a struct equal to the sum of sizeof of each member?
The second issue was that can_filter_fmt is not packed as 2Q2B2I but as 2Q2I2B6x (the addr member is in the middle).
As for SOL_CAN_J1939 I was correct and needs to be created in file because it is not yet in the package.
The final code is the following:
#!/usr/bin/env python3
import socket
import struct
def pack_J1939_filters(can_filters=None):
if can_filters is None:
# Pass all messages
can_filters = [{}]
can_filter_fmt = "=" + "2Q2I2B6x" * len(can_filters)
filter_data = []
for can_filter in can_filters:
if 'name' in can_filter:
name = can_filter['name']
else:
name = 0
if 'name_mask' in can_filter:
name_mask = can_filter['name_mask']
else:
name_mask = 0
if 'pgn' in can_filter:
pgn = can_filter['pgn']
else:
pgn = 0
if 'pgn_mask' in can_filter:
pgn_mask = can_filter['pgn_mask']
else:
pgn_mask = 0
if 'addr' in can_filter:
addr = can_filter['addr']
else:
addr = 0
if 'addr_mask' in can_filter:
addr_mask = can_filter['addr_mask']
else:
addr_mask = 0
filter_data.append(name)
filter_data.append(name_mask)
filter_data.append(pgn)
filter_data.append(pgn_mask)
filter_data.append(addr)
filter_data.append(addr_mask)
return struct.pack(can_filter_fmt, *filter_data)
def print_msg(data, sck_addr):
print(f"SA:{hex(sck_addr[3])} PGN:{hex(sck_addr[2])}")
for j in range(len(data)):
if j % 8 == 0 and j != 0:
print()
if j % 8 == 0:
print(f"bytes {j} to {j+7}: ", end="")
print(f"{hex(data[j])} ", end="")
print()
print()
def main():
s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939)
# allows to receive broadcast messages
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
interface = "vcan0"
src_name = socket.J1939_NO_NAME
src_pgn = socket.J1939_NO_PGN # always no PGN for source, unless filtering is needed
src_addr = 0x81 # recvfrom() will not return destination specific messages for other addresses
src_sck_addr = (interface, src_name, src_pgn, src_addr)
s.bind(src_sck_addr)
packed_filters = pack_J1939_filters()
SOL_CAN_BASE = 100
CAN_J1939 = 7
SOL_CAN_J1939 = SOL_CAN_BASE + CAN_J1939
s.setsockopt(SOL_CAN_J1939, socket.SO_J1939_FILTER , packed_filters)
(recv_data, recv_sck_addr) = s.recvfrom(128)
print_msg(recv_data, recv_sck_addr)
s.close()
if __name__ == "__main__":
main()
Thank you.
For J1939 to work with SocketCAN you need two things:
kernel 5.4+
can-j1939 kernel module enabled
Testing for can-1939:
If you install can-utils and after sudo modprobe can-j1939 all you get is fatal error, or if you start testj1939 from can-utils and you get error that protocol is not supported, then it means that can-j1939 was not enabled in your kernel and you need to compile it manually.
Here are my instructions for enabling can-j1939 in Debian 10 kernel:
https://github.com/linux-can/can-utils/blob/master/can-j1939-install-kernel-module.md
Suppose I have a string of the of the format host:port, where :port is optional. How can I reliably extract the two components?
The host can be any of:
A hostname (localhost, www.google.com)
An IPv4 literal (1.2.3.4)
An IPv6 literal ([aaaa:bbbb::cccc]).
In other words, this is the standard format used across the internet (such as in URIs: complete grammar at https://www.rfc-editor.org/rfc/rfc3986#section-3.2, excluding the "User Information" component).
So, some possible inputs, and desired outputs:
'localhost' -> ('localhost', None)
'my-example.com:1234' -> ('my-example.com', 1234)
'1.2.3.4' -> ('1.2.3.4', None)
'[0abc:1def::1234]' -> ('[0abc:1def::1234]', None)
Well, this is Python, with batteries included. You have mention that the format is the standard one used in URIs, so how about urllib.parse?
import urllib.parse
def parse_hostport(hp):
# urlparse() and urlsplit() insists on absolute URLs starting with "//"
result = urllib.parse.urlsplit('//' + hp)
return result.hostname, result.port
This should handle any valid host:port you can throw at it.
This should handle the whole parse in a single regex
regex = re.compile(r'''
( # first capture group = Addr
\[ # literal open bracket IPv6
[:a-fA-F0-9]+ # one or more of these characters
\] # literal close bracket
| # ALTERNATELY
(?: # IPv4
\d{1,3}\. # one to three digits followed by a period
){3} # ...repeated three times
\d{1,3} # followed by one to three digits
| # ALTERNATELY
[-a-zA-Z0-9.]+ # one or more hostname chars ([-\w\d\.]) Hostname
) # end first capture group
(?:
: # a literal :
( # second capture group = PORT
\d+ # one or more digits
) # end second capture group
)? # ...or not.''', re.X)
All that's needed then is to cast the second group to int.
def parse_hostport(hp):
# regex from above should be defined here.
m = regex.match(hp)
addr, port = m.group(1, 2)
try:
return (addr, int(port))
except TypeError:
# port is None
return (addr, None)
def split_host_port(string):
if not string.rsplit(':', 1)[-1].isdigit():
return (string, None)
string = string.rsplit(':', 1)
host = string[0] # 1st index is always host
port = int(string[1])
return (host, port)
Actually confused on whether this is what you wanted, but I rewrote it up a bit and it still seems to follow the ideal output:
>>>> split_host_port("localhost")
('localhost', None)
>>>> split_host_port("example.com:1234")
('example.com', 1234)
>>>> split_host_port("1.2.3.4")
('1.2.3.4', None)
>>>> split_host_port("[0abc:1def::1234]")
('[0abc:1def::1234]', None)
>>>>
As on the first line I didn't really like the chained function calls e.g. getattr(getattr(getattr(string, 'rsplit')(':', 1), '__getitem__')(-1), 'isdigit')() for the expanded version and then it's repeated again two lines after, perhaps I should make it a variable instead so there's no need for all the calls.
But I'm nitpicking here so feel free to call me out on that, heh.
Here's my final attempt, with credit to other answerers who provided inspiration:
def parse_hostport(s, default_port=None):
if s[-1] == ']':
# ipv6 literal (with no port)
return (s, default_port)
out = s.rsplit(":", 1)
if len(out) == 1:
# No port
port = default_port
else:
try:
port = int(out[1])
except ValueError:
raise ValueError("Invalid host:port '%s'" % s)
return (out[0], port)
Came up with a dead simple regexp that seems to work in most cases:
def get_host_pair(value):
return re.search(r'^(.*?)(?::(\d+))?$', value).groups()
get_host_pair('localhost')
get_host_pair('localhost:80')
get_host_pair('[::1]')
get_host_pair('[::1]:8080')
It probably doesn't work when the base input is invalid however
Here's my attempt at this so far:
def parse_hostport(hp):
""" parse a host:port pair
"""
# start by special-casing the ipv6 literal case
x = re.match('^(\[[0-9a-fA-F:]+\])(:(\d+))?$', hp)
if x is not None:
return x.group(1, 3)
# otherwise, just split at the (hopefully only) colon
splits = hp.split(':')
if len(splits) == 1:
return splits + [None,]
elif len(splits) == 2:
return splits
raise ValueError("Invalid host:port input '%s'" % hp)
Here's a terser implementation which relies on attempting to parse the last component as an int:
def parse_hostport(s):
out = s.rsplit(":", 1)
try:
out[1] = int(out[1])
except (IndexError, ValueError):
# couldn't parse the last component as a port, so let's
# assume there isn't a port.
out = (s, None)
return out
I mock a ip list and a subnet dict as input:
# ip address list
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10',
# Edited: add some /28, /16 case
'192.168.12.39', '192.168.12.58', '10.63.11.1', '10.63.102.69',
]
# subnet dict
netsets = {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
# Edited: add some /28, /16 case
'192.168.12.32/28': 'subnet-F',
'192.168.12.48/28': 'subnet-G',
'10.63.0.0/16': 'subnet-I',
}
and then each ip address in ip_list need to find the name of subnet.
We assume that each ip address can find the corresponding subnet in netsets.
Ouput like this:
192.168.1.151 subnet-A
192.168.10.191 subnet-B
192.168.6.127 subnet-D
192.168.2.227 subnet-C
192.168.2.5 subnet-C
192.168.3.237 subnet-C
192.168.6.188 subnet-D
192.168.7.209 subnet-D
192.168.9.10 subnet-E
# add some /28, /16 case
192.168.12.39 subnet-F
192.168.12.58 subnet-G
10.63.11.1 subnet-I
10.63.102.69 subnet-I
I use netaddr to calculate CIDR, here is my code:
from netaddr import IPAddress, IPNetwork
def netaddr_test(ips, netsets):
for ip in ips:
for subnet, name in netsets.iteritems():
if IPAddress(ip) in IPNetwork(subnet):
print ip, '\t', name
break
netaddr_test(ip_list, netsets)
But this code is too too too slow, it iterate too much. the complexity of time is O(n**2).
Once we have tens of thousands of ip to iterate, this code cost too much time.
Is there any better way to solve this problem?
I can recommend use specially optimized intervaltree module for making search fast. Thus the task could be solved for O(m*log n) time. For example:
from intervaltree import Interval, IntervalTree
from ipaddress import ip_network, ip_address
# build nets tree
netstree = IntervalTree(
Interval(
ip_network(net).network_address,
ip_network(net).broadcast_address,
name
)
for
net, name
in
netsets.items()
)
# Now you may check ip intervals
for i in ip_list:
ip = ip_address(i)
nets = netstree[ip]
if nets: # set is not empty
netdata = list(nets)[0]
print(netdata.data)
# prints 'subnet-E'
# ip address list
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10'
]
# subnet dict
netsets = {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
}
new_netsets = {}
for k,v in netsets.items():
new_netsets['.'.join(k.split('.')[:3])] = v
for IP in ip_list:
newIP = '.'.join(IP.split('.')[:3])
print IP, new_netsets[newIP]
Hope this helps.
I would suggest to avoid creating new instances in the for loop. This will not decrease complexity (it will increase it) but it will speed up the netaddr_test, especially if it is called more than one times. Example:
def _init(ips, netsets):
"""Initialize all objects"""
new_ips = []
new_subs = {}
for ip in ips:
new_ips.append(IPAddress(ip))
for subnet, info in netsets.iteritems():
new_subs[subnet] = {'name': info, 'subnet': IPNetwork(subnet)}
return new_ips, new_subs
def netaddr_test(ips, netsets):
for ip in ips:
for stringnet, info in netsets.iteritems():
if ip in info['subnet']:
print ip, '\t', info['name']
break
ni, ns = _init(ip_list, netsets)
netaddr_test(ni, ns)
UPDATE: Tested the code above with
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10'
] * 1000
Results:
# Original
$ time python /tmp/test.py > /dev/null
real 0m0.357s
user 0m0.345s
sys 0m0.012s
# Modified
$ time python /tmp/test2.py > /dev/null
real 0m0.126s
user 0m0.122s
sys 0m0.005s
Now, I have never used netaddr so I am not sure about how it handles subnets internally. In your case you can see the subnet as a range of IPs and each IP is a uint_32 so you can convert everything to integers:
# IPs now are
ip_list_int = [3232235927, 3232238271, ...]
netsets_expanded = {
'192.168.1.0/24': {'name': 'subnet-A', 'start': 3232235776, 'end': 3232236031}
netaddr can be used to convert you data in the above format. Once there, your netaddr_test becomes (and works only with integer comparisons):
def netaddr_test(ips, netsets):
for ip in ips:
for subnet, subinfo in netsets.iteritems():
if ip >= subinfo['start'] and ip < subinfo['end']:
print ip, '\t', subinfo.name
break
In general case where you have N templates and M values to test for match you can do nothing better than O(N*M). But if you can reformulate the task than you can speed it up.
My suggestion is to group templates so that you have a few uplevel templates and if an IP matches it than you go down to final templates. In your examples this would be
grouped_netsets = {
"192.168.0.0/16": {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
}
}
def netaddr_test(ips, grouped_netsets):
for ip in ips:
for group, netsets in grouped_netsets.iteritems():
if IPAddress(ip) in IPNetwork(group):
for subnet, name in netsets.iteritems():
if IPAddress(ip) in IPNetwork(subnet):
print(ip, '\t', name)
break
So if ip_list would contain anything not starting with 192.168 you'll drop it with one check.
The only question remaining is to write the function for grouping the netsets with optimal configuraton.
I mock a ip list and a subnet dict as input:
# ip address list ip_list =
[ '192.168.1.151', '192.168.10.191', '192.168.6.127', '192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10' ]
# subnet dict
netsets = { '192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24':'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24':'subnet-E', }
and then each ip address in ip_list need to find the
name of subnet.
We assume that each ip address can find the corresponding subnet in
netsets.
Ouput like this:
192.168.1.151 subnet-A
192.168.10.191 subnet-B
192.168.6.127 subnet-D
192.168.2.227 subnet-C
192.168.2.5 subnet-C
192.168.3.237 subnet-C
192.168.6.188 subnet-D
192.168.7.209 subnet-D
192.168.9.10 subnet-E
[...]
Is there any better to solve this problem?
Here's a two liner that does it:
for ip_addr in ip_list:
print "{0}\t{1}".format(ip_addr,netsets[".".join(ip_addr.split('.')[0:-1])+".0/24"])
Assuming that subnets don't overlap each other you could convert the subnet to two integers, beginning and end of the range. These numbers would be added to a list which would be sorted. While doing this we'd need to build a dictionary which could be used later to retrieve the subnet name with start of the range.
def to_int(ip):
parts = map(int, ip.split('.'))
return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3]
def build(netsets):
ranges = []
subnets = {}
for net, name in netsets.iteritems():
ip, size = net.split('/')
start = to_int(ip)
end = start | 0xffffffff >> int(size)
ranges.extend([start, end])
subnets[start] = name
ranges.sort()
return ranges, subnets
When searching for an IP you'd turn it to number again and do bisect_left on the list or ranges. If result is uneven number or the IP matches on any number on the list then the IP is within a subnet. Then you'd use the star of the range to get the name of the subnet from a dictionary that was built earlier:
def find(ranges, subnets, ip):
num = to_int(ip)
pos = bisect.bisect_left(ranges, to_int(ip))
# Check if first IP in the range
if pos % 2 == 0 and ranges[pos] == num:
pos += 1
if pos % 2:
return subnets[ranges[pos - 1]]
else:
return None
With the previous building block one could easily get the subnet for each IP with following code:
ranges, subnets = build(netsets)
for ip in ip_list:
print 'ip: {0}, subnet: {1}'.format(ip, find(ranges, subnets, ip))
Building the dictionary and the range list would take O(m log m) time and going through the IP list would take O(n log m) where m is the number of subnets and n the number of IPs. Solution works with different subnets of different size and will print None in case the IP doesn't belong to any subnet.
Given an ip address (say 192.168.0.1), how do I check if it's in a network (say 192.168.0.0/24) in Python?
Are there general tools in Python for ip address manipulation? Stuff like host lookups, ip adddress to int, network address with netmask to int and so on? Hopefully in the standard Python library for 2.5.
Using ipaddress (in the stdlib since 3.3, at PyPi for 2.6/2.7):
>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24')
True
If you want to evaluate a lot of IP addresses this way, you'll probably want to calculate the netmask upfront, like
n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)
Then, for each address, calculate the binary representation with one of
a = int(ipaddress.ip_address('192.0.43.10'))
a = struct.unpack('!I', socket.inet_pton(socket.AF_INET, '192.0.43.10'))[0]
a = struct.unpack('!I', socket.inet_aton('192.0.43.10'))[0] # IPv4 only
Finally, you can simply check:
in_network = (a & mask) == netw
I like to use netaddr for that:
from netaddr import CIDR, IP
if IP("192.168.0.1") in CIDR("192.168.0.0/24"):
print "Yay!"
As arno_v pointed out in the comments, new version of netaddr does it like this:
from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
print "Yay!"
For python3
import ipaddress
ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/16')
Output :
False
True
This article shows you can do it with socket and struct modules without too much extra effort. I added a little to the article as follows:
import socket,struct
def makeMask(n):
"return a mask of n bits as a long integer"
return (2L<<n-1) - 1
def dottedQuadToNum(ip):
"convert decimal dotted quad string to long integer"
return struct.unpack('L',socket.inet_aton(ip))[0]
def networkMask(ip,bits):
"Convert a network address to a long integer"
return dottedQuadToNum(ip) & makeMask(bits)
def addressInNetwork(ip,net):
"Is an address in a network"
return ip & net == net
address = dottedQuadToNum("192.168.1.1")
networka = networkMask("10.0.0.0",24)
networkb = networkMask("192.168.0.0",24)
print (address,networka,networkb)
print addressInNetwork(address,networka)
print addressInNetwork(address,networkb)
This outputs:
False
True
If you just want a single function that takes strings it would look like this:
import socket,struct
def addressInNetwork(ip,net):
"Is an address in a network"
ipaddr = struct.unpack('L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netmask = struct.unpack('L',socket.inet_aton(netaddr))[0] & ((2L<<int(bits)-1) - 1)
return ipaddr & netmask == netmask
Using Python >= 3.7 ipaddress:
import ipaddress
address = ipaddress.ip_address("192.168.0.1")
network = ipaddress.ip_network("192.168.0.0/16")
print(network.supernet_of(ipaddress.ip_network(f"{address}/{address.max_prefixlen}")))
Explanation
You can think of an IP Address as a Network with the largest possible netmask (/32 for IPv4, /128 for IPv6)
Checking whether 192.168.0.1 is in 192.168.0.0/16 is essentially the same as checking whether 192.168.0.1/32 is a subnet of 192.168.0.0/16
This code is working for me on Linux x86. I haven't really given any thought to endianess issues, but I have tested it against the "ipaddr" module using over 200K IP addresses tested against 8 different network strings, and the results of ipaddr are the same as this code.
def addressInNetwork(ip, net):
import socket,struct
ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
netstr, bits = net.split('/')
netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
return (ipaddr & mask) == (netaddr & mask)
Example:
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16')
True
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/24')
False
Wherever possible I'd recommend the built in ipaddress module. It's only available in Python 3 though, but it is super easy to use, and supports IPv6. And why aren't you using Python 3 yet anyway, right?
The accepted answer doesn't work ... which is making me angry. Mask is backwards and doesn't work with any bits that are not a simple 8 bit block (eg /24). I adapted the answer, and it works nicely.
import socket,struct
def addressInNetwork(ip, net_n_bits):
ipaddr = struct.unpack('!L', socket.inet_aton(ip))[0]
net, bits = net_n_bits.split('/')
netaddr = struct.unpack('!L', socket.inet_aton(net))[0]
netmask = (0xFFFFFFFF >> int(bits)) ^ 0xFFFFFFFF
return ipaddr & netmask == netaddr
here is a function that returns a dotted binary string to help visualize the masking.. kind of like ipcalc output.
def bb(i):
def s = '{:032b}'.format(i)
def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]
eg:
I'm not a fan of using modules when they are not needed. This job only requires simple math, so here is my simple function to do the job:
def ipToInt(ip):
o = map(int, ip.split('.'))
res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
return res
def isIpInSubnet(ip, ipNetwork, maskLength):
ipInt = ipToInt(ip)#my test ip, in int form
maskLengthFromRight = 32 - maskLength
ipNetworkInt = ipToInt(ipNetwork) #convert the ip network into integer form
binString = "{0:b}".format(ipNetworkInt) #convert that into into binary (string format)
chopAmount = 0 #find out how much of that int I need to cut off
for i in range(maskLengthFromRight):
if i < len(binString):
chopAmount += int(binString[len(binString)-1-i]) * 2**i
minVal = ipNetworkInt-chopAmount
maxVal = minVal+2**maskLengthFromRight -1
return minVal <= ipInt and ipInt <= maxVal
Then to use it:
>>> print isIpInSubnet('66.151.97.0', '66.151.97.192',24)
True
>>> print isIpInSubnet('66.151.97.193', '66.151.97.192',29)
True
>>> print isIpInSubnet('66.151.96.0', '66.151.97.192',24)
False
>>> print isIpInSubnet('66.151.97.0', '66.151.97.192',29)
That's it, this is much faster than the solutions above with the included modules.
I tried Dave Webb's solution but hit some problems:
Most fundamentally - a match should be checked by ANDing the IP address with the mask, then checking the result matched the Network address exactly. Not ANDing the IP address with the Network address as was done.
I also noticed that just ignoring the Endian behaviour assuming that consistency will save you will only work for masks on octet boundaries (/24, /16). In order to get other masks (/23, /21) working correctly I added a "greater than" to the struct commands and changed the code for creating the binary mask to start with all "1" and shift left by (32-mask).
Finally, I added a simple check that the network address is valid for the mask and just print a warning if it is not.
Here's the result:
def addressInNetwork(ip,net):
"Is an address in a network"
ipaddr = struct.unpack('>L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netmask = struct.unpack('>L',socket.inet_aton(netaddr))[0]
ipaddr_masked = ipaddr & (4294967295<<(32-int(bits))) # Logical AND of IP address and mask will equal the network address if it matches
if netmask == netmask & (4294967295<<(32-int(bits))): # Validate network address is valid for mask
return ipaddr_masked == netmask
else:
print "***WARNING*** Network",netaddr,"not valid with mask /"+bits
return ipaddr_masked == netmask
Not in the Standard library for 2.5, but ipaddr makes this very easy. I believe it is in 3.3 under the name ipaddress.
import ipaddr
a = ipaddr.IPAddress('192.168.0.1')
n = ipaddr.IPNetwork('192.168.0.0/24')
#This will return True
n.Contains(a)
Marc's code is nearly correct. A complete version of the code is -
def addressInNetwork3(ip,net):
'''This function allows you to check if on IP belogs to a Network'''
ipaddr = struct.unpack('=L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netmask = struct.unpack('=L',socket.inet_aton(calcDottedNetmask(int(bits))))[0]
network = struct.unpack('=L',socket.inet_aton(netaddr))[0] & netmask
return (ipaddr & netmask) == (network & netmask)
def calcDottedNetmask(mask):
bits = 0
for i in xrange(32-mask,32):
bits |= (1 << i)
return "%d.%d.%d.%d" % ((bits & 0xff000000) >> 24, (bits & 0xff0000) >> 16, (bits & 0xff00) >> 8 , (bits & 0xff))
Obviously from the same sources as above...
A very Important note is that the first code has a small glitch - The IP address 255.255.255.255 also shows up as a Valid IP for any subnet. I had a heck of time getting this code to work and thanks to Marc for the correct answer.
Relying on the "struct" module can cause problems with endian-ness and type sizes, and just isn't needed. Nor is socket.inet_aton(). Python works very well with dotted-quad IP addresses:
def ip_to_u32(ip):
return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)
I need to do IP matching on each socket accept() call, against a whole set of allowable source networks, so I precompute masks and networks, as integers:
SNS_SOURCES = [
# US-EAST-1
'207.171.167.101',
'207.171.167.25',
'207.171.167.26',
'207.171.172.6',
'54.239.98.0/24',
'54.240.217.16/29',
'54.240.217.8/29',
'54.240.217.64/28',
'54.240.217.80/29',
'72.21.196.64/29',
'72.21.198.64/29',
'72.21.198.72',
'72.21.217.0/24',
]
def build_masks():
masks = [ ]
for cidr in SNS_SOURCES:
if '/' in cidr:
netstr, bits = cidr.split('/')
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
net = ip_to_u32(netstr) & mask
else:
mask = 0xffffffff
net = ip_to_u32(cidr)
masks.append((mask, net))
return masks
Then I can quickly see if a given IP is within one of those networks:
ip = ip_to_u32(ipstr)
for mask, net in cached_masks:
if ip & mask == net:
# matched!
break
else:
raise BadClientIP(ipstr)
No module imports needed, and the code is very fast at matching.
As of Python 3.7, you can use subnet_of and supernet_of helper methods, which are part of the standard library:
To just test against a single IP, you can just use the subnet mask /32 which means "only this IP address" as a subnet, or you can pass the IP address to IPv4Nework or IPv6Nework constructors and they will return a subnet value for you.
So for your example:
from ipaddress import IPv4Network, IPv4Address
# Store IP Address as variable
>>> myip = IPv4Address('192.168.0.1')
>>> myip
IPv4Address('192.168.0.1')
# This treats the IP as a subnet
>>> myip_subnet = IPv4Network(myip)
>>> myip_subnet
IPv4Network('192.168.0.1/32')
# The other subnet to test membership against
>>> other_subnet = IPv4Network('192.168.0.0/24')
>>> other_subnet
IPv4Network('192.168.0.0/24')
# Now we can test
>>> myip_subnet.subnet_of(other_subnet)
True
Are there general tools in Python for ip address manipulation? Stuff
like host lookups, ip adddress to int, network address with netmask to
int and so on? Hopefully in the standard Python library for 2.5.
In Python 3, there's the ipaddress module which has tools for IPv4 and IPv6 manipulation. You can convert them to an int, by casting, i.e. int(IPv4Address('192.168.0.1')). Lots of other useful functions in the ipaddress module for hosts, etc.
The choosen answer has a bug.
Following is the correct code:
def addressInNetwork(ip, net_n_bits):
ipaddr = struct.unpack('<L', socket.inet_aton(ip))[0]
net, bits = net_n_bits.split('/')
netaddr = struct.unpack('<L', socket.inet_aton(net))[0]
netmask = ((1L << int(bits)) - 1)
return ipaddr & netmask == netaddr & netmask
Note: ipaddr & netmask == netaddr & netmask instead of ipaddr & netmask == netmask.
I also replace ((2L<<int(bits)-1) - 1) with ((1L << int(bits)) - 1), as the latter seems more understandable.
from netaddr import all_matching_cidrs
>>> from netaddr import all_matching_cidrs
>>> all_matching_cidrs("212.11.70.34", ["192.168.0.0/24","212.11.64.0/19"] )
[IPNetwork('212.11.64.0/19')]
Here is the usage for this method:
>>> help(all_matching_cidrs)
Help on function all_matching_cidrs in module netaddr.ip:
all_matching_cidrs(ip, cidrs)
Matches an IP address or subnet against a given sequence of IP addresses and subnets.
#param ip: a single IP address or subnet.
#param cidrs: a sequence of IP addresses and/or subnets.
#return: all matching IPAddress and/or IPNetwork objects from the provided
sequence, an empty list if there was no match.
Basically you provide an ip address as the first argument and a list of cidrs as the second argument. A list of hits are returned.
#This works properly without the weird byte by byte handling
def addressInNetwork(ip,net):
'''Is an address in a network'''
# Convert addresses to host order, so shifts actually make sense
ip = struct.unpack('>L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netaddr = struct.unpack('>L',socket.inet_aton(netaddr))[0]
# Must shift left an all ones value, /32 = zero shift, /0 = 32 shift left
netmask = (0xffffffff << (32-int(bits))) & 0xffffffff
# There's no need to mask the network address, as long as its a proper network address
return (ip & netmask) == netaddr
previous solution have a bug in ip & net == net. Correct ip lookup is ip & netmask = net
bugfixed code:
import socket
import struct
def makeMask(n):
"return a mask of n bits as a long integer"
return (2L<<n-1) - 1
def dottedQuadToNum(ip):
"convert decimal dotted quad string to long integer"
return struct.unpack('L',socket.inet_aton(ip))[0]
def addressInNetwork(ip,net,netmask):
"Is an address in a network"
print "IP "+str(ip) + " NET "+str(net) + " MASK "+str(netmask)+" AND "+str(ip & netmask)
return ip & netmask == net
def humannetcheck(ip,net):
address=dottedQuadToNum(ip)
netaddr=dottedQuadToNum(net.split("/")[0])
netmask=makeMask(long(net.split("/")[1]))
return addressInNetwork(address,netaddr,netmask)
print humannetcheck("192.168.0.1","192.168.0.0/24");
print humannetcheck("192.169.0.1","192.168.0.0/24");
Here is a class I wrote for longest prefix matching:
#!/usr/bin/env python
class Node:
def __init__(self):
self.left_child = None
self.right_child = None
self.data = "-"
def setData(self, data): self.data = data
def setLeft(self, pointer): self.left_child = pointer
def setRight(self, pointer): self.right_child = pointer
def getData(self): return self.data
def getLeft(self): return self.left_child
def getRight(self): return self.right_child
def __str__(self):
return "LC: %s RC: %s data: %s" % (self.left_child, self.right_child, self.data)
class LPMTrie:
def __init__(self):
self.nodes = [Node()]
self.curr_node_ind = 0
def addPrefix(self, prefix):
self.curr_node_ind = 0
prefix_bits = ''.join([bin(int(x)+256)[3:] for x in prefix.split('/')[0].split('.')])
prefix_length = int(prefix.split('/')[1])
for i in xrange(0, prefix_length):
if (prefix_bits[i] == '1'):
if (self.nodes[self.curr_node_ind].getRight()):
self.curr_node_ind = self.nodes[self.curr_node_ind].getRight()
else:
tmp = Node()
self.nodes[self.curr_node_ind].setRight(len(self.nodes))
tmp.setData(self.nodes[self.curr_node_ind].getData());
self.curr_node_ind = len(self.nodes)
self.nodes.append(tmp)
else:
if (self.nodes[self.curr_node_ind].getLeft()):
self.curr_node_ind = self.nodes[self.curr_node_ind].getLeft()
else:
tmp = Node()
self.nodes[self.curr_node_ind].setLeft(len(self.nodes))
tmp.setData(self.nodes[self.curr_node_ind].getData());
self.curr_node_ind = len(self.nodes)
self.nodes.append(tmp)
if i == prefix_length - 1 :
self.nodes[self.curr_node_ind].setData(prefix)
def searchPrefix(self, ip):
self.curr_node_ind = 0
ip_bits = ''.join([bin(int(x)+256)[3:] for x in ip.split('.')])
for i in xrange(0, 32):
if (ip_bits[i] == '1'):
if (self.nodes[self.curr_node_ind].getRight()):
self.curr_node_ind = self.nodes[self.curr_node_ind].getRight()
else:
return self.nodes[self.curr_node_ind].getData()
else:
if (self.nodes[self.curr_node_ind].getLeft()):
self.curr_node_ind = self.nodes[self.curr_node_ind].getLeft()
else:
return self.nodes[self.curr_node_ind].getData()
return None
def triePrint(self):
n = 1
for i in self.nodes:
print n, ':'
print i
n += 1
And here is a test program:
n=LPMTrie()
n.addPrefix('10.25.63.0/24')
n.addPrefix('10.25.63.0/16')
n.addPrefix('100.25.63.2/8')
n.addPrefix('100.25.0.3/16')
print n.searchPrefix('10.25.63.152')
print n.searchPrefix('100.25.63.200')
#10.25.63.0/24
#100.25.0.3/16
Thank you for your script!
I have work quite a long on it to make everything working... So I'm sharing it here
Using netaddr Class is 10 times slower than using binary conversion, so if you'd like to use it on a big list of IP, you should consider not using netaddr class
makeMask function is not working! Only working for /8,/16,/24 Ex:
bits = "21" ; socket.inet_ntoa(struct.pack('=L',(2L << int(bits)-1) - 1))
'255.255.31.0' whereas it should be 255.255.248.0
So I have used another function calcDottedNetmask(mask) from http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/
Ex:
#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
Another problem is the process of matching if an IP belongs to a network! Basic Operation should be to compare (ipaddr & netmask) and (network & netmask).Ex: for the time being, the function is wrong
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!
So my new addressInNetwork function looks-like:
#!/usr/bin/python
import socket,struct
def addressInNetwork(ip,net):
'''This function allows you to check if on IP belogs to a Network'''
ipaddr = struct.unpack('=L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netmask = struct.unpack('=L',socket.inet_aton(calcDottedNetmask(bits)))[0]
network = struct.unpack('=L',socket.inet_aton(netaddr))[0] & netmask
return (ipaddr & netmask) == (network & netmask)
def calcDottedNetmask(mask):
bits = 0
for i in xrange(32-int(mask),32):
bits |= (1 > 24, (bits & 0xff0000) >> 16, (bits & 0xff00) >> 8 , (bits & 0xff))
And now, answer is right!!
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
False
I hope that it will help other people, saving time for them!
Relating to all of the above, I think socket.inet_aton() returns bytes in network order, so the correct way to unpack them is probably
struct.unpack('!L', ... )
import socket,struct
def addressInNetwork(ip,net):
"Is an address in a network"
ipaddr = struct.unpack('!L',socket.inet_aton(ip))[0]
netaddr,bits = net.split('/')
netaddr = struct.unpack('!L',socket.inet_aton(netaddr))[0]
netmask = ((1<<(32-int(bits))) - 1)^0xffffffff
return ipaddr & netmask == netaddr & netmask
print addressInNetwork('10.10.10.110','10.10.10.128/25')
print addressInNetwork('10.10.10.110','10.10.10.0/25')
print addressInNetwork('10.10.10.110','10.20.10.128/25')
$ python check-subnet.py
False
True
False
I don't know of anything in the standard library, but PySubnetTree is a Python library that will do subnet matching.
From various sources above, and from my own research, this is how I got subnet and address calculation working. These pieces are enough to solve the question and other related questions.
class iptools:
#staticmethod
def dottedQuadToNum(ip):
"convert decimal dotted quad string to long integer"
return struct.unpack('>L', socket.inet_aton(ip))[0]
#staticmethod
def numToDottedQuad(n):
"convert long int to dotted quad string"
return socket.inet_ntoa(struct.pack('>L', n))
#staticmethod
def makeNetmask(mask):
bits = 0
for i in xrange(32-int(mask), 32):
bits |= (1 << i)
return bits
#staticmethod
def ipToNetAndHost(ip, maskbits):
"returns tuple (network, host) dotted-quad addresses given"
" IP and mask size"
# (by Greg Jorgensen)
n = iptools.dottedQuadToNum(ip)
m = iptools.makeMask(maskbits)
net = n & m
host = n - mask
return iptools.numToDottedQuad(net), iptools.numToDottedQuad(host)
There is an API that's called SubnetTree available in python that do this job very well.
This is a simple example :
import SubnetTree
t = SubnetTree.SubnetTree()
t.insert("10.0.1.3/32")
print("10.0.1.3" in t)
This is the link
Here is my code
# -*- coding: utf-8 -*-
import socket
class SubnetTest(object):
def __init__(self, network):
self.network, self.netmask = network.split('/')
self._network_int = int(socket.inet_aton(self.network).encode('hex'), 16)
self._mask = ((1L << int(self.netmask)) - 1) << (32 - int(self.netmask))
self._net_prefix = self._network_int & self._mask
def match(self, ip):
'''
判断传入的 IP 是不是本 Network 内的 IP
'''
ip_int = int(socket.inet_aton(ip).encode('hex'), 16)
return (ip_int & self._mask) == self._net_prefix
st = SubnetTest('100.98.21.0/24')
print st.match('100.98.23.32')
If you do not want to import other modules you could go with:
def ip_matches_network(self, network, ip):
"""
'{:08b}'.format(254): Converts 254 in a string of its binary representation
ip_bits[:net_mask] == net_ip_bits[:net_mask]: compare the ip bit streams
:param network: string like '192.168.33.0/24'
:param ip: string like '192.168.33.1'
:return: if ip matches network
"""
net_ip, net_mask = network.split('/')
net_mask = int(net_mask)
ip_bits = ''.join('{:08b}'.format(int(x)) for x in ip.split('.'))
net_ip_bits = ''.join('{:08b}'.format(int(x)) for x in net_ip.split('.'))
# example: net_mask=24 -> compare strings at position 0 to 23
return ip_bits[:net_mask] == net_ip_bits[:net_mask]
I tried one subset of proposed solutions in these answers.. with no success, I finally adapted and fixed the proposed code and wrote my fixed function.
I tested it and works at least on little endian architectures--e.g.x86-- if anyone likes to try on a big endian architecture, please give me feedback.
IP2Int code comes from this post, the other method is a fully (for my test cases) working fix of previous proposals in this question.
The code:
def IP2Int(ip):
o = map(int, ip.split('.'))
res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
return res
def addressInNetwork(ip, net_n_bits):
ipaddr = IP2Int(ip)
net, bits = net_n_bits.split('/')
netaddr = IP2Int(net)
bits_num = int(bits)
netmask = ((1L << bits_num) - 1) << (32 - bits_num)
return ipaddr & netmask == netaddr & netmask
Hope useful,
Here is the solution using netaddr package
from netaddr import IPNetwork, IPAddress
def network_has_ip(network, ip):
if not isinstance(network, IPNetwork):
raise Exception("network parameter must be {0} instance".format(IPNetwork.__name__))
if not isinstance(ip, IPAddress):
raise Exception("ip parameter must be {0} instance".format(IPAddress.__name__))
return (network.cidr.ip.value & network.netmask.value) == (ip.value & network.netmask.value)
To avoid having builtin or third party modules change their syntax over time, I created my own that does this. I'm using this as an importable module. I hope this helps someone:
def subnet_lookup(subnet: str, netmask: str, ip_address: str):
"""
:param subnet: subnet to test against (as string)
:param netmask: mask of subnet
:param ip_address: ip to test against subnet and mask
:return True if a match; False if not a match
Steps:
1) convert entire subnet into one binary word
2) convert entire mask into one binary word
3) determine bcast from comparing subnet and mask
4) convert entire ip_address into one binary word
5) convert entire subnet into decimal
6) convert entire bcast into decimal
7) convert entire ip_address into decimal
8) determine if ip_address falls between subnet and bcast using range(); returns True if yes, False if no
"""
def convert_whole_to_bin(whole):
ip_dec_list = whole.split(".")
ip_bin_str = ""
for ip in ip_dec_list:
binary = dec_to_bin(int(ip))
ip_bin_str += binary
return ip_bin_str
def dec_to_bin(decimal_octet: int):
binary = bin(decimal_octet).replace("0b", "")
return binary.rjust(8, '0')
def split_binary_into_list(binary_octet: str):
bin_list = []
for s in binary_octet:
bin_list.append(s)
return bin_list
def determine_bcast(subnet, netmask):
subnet_split = split_binary_into_list(subnet)
netmask_split = split_binary_into_list(netmask)
bcast_list = []
for subnet, mask in zip(subnet_split, netmask_split):
if mask != '0':
bcast_list.append(subnet)
else:
bcast_list.append('1')
bcast_bin = "".join(bcast_list)
return bcast_bin
def bin_to_dec(binary_single_word: str):
decimal = int(binary_single_word, 2)
return decimal
def subnet_lookup(ip_address, subnet, bcast):
return ip_address in range(subnet, bcast + 1)
# 1) convert entire subnet into one binary word
subnet_single_bin = convert_whole_to_bin(whole=subnet)
# 2) convert entire mask into one binary word
mask_single_bin = convert_whole_to_bin(whole=netmask)
# 3) determine bcast from comparing subnet and mask
bcast_single_bin = determine_bcast(subnet=subnet_single_bin, netmask=mask_single_bin)
# 4) convert entire ip_address into one binary word
ip_address_single_bin = convert_whole_to_bin(whole=ip_address)
# 5) convert entire subnet into decimal
subnet_single_dec = bin_to_dec(binary_single_word=subnet_single_bin)
# 6) convert entire bcast into decimal
bcast_single_dec = bin_to_dec(binary_single_word=bcast_single_bin)
# 7) convert entire ip_address into decimal
ip_address_single_dec = bin_to_dec(binary_single_word=ip_address_single_bin)
# 8) determine if ip_address falls between subnet and bcast; returns True if yes, False if no
lookup_result = subnet_lookup(ip_address=ip_address_single_dec, subnet=subnet_single_dec, bcast=bcast_single_dec)
return lookup_result
# Testing:
subnet = "172.16.0.0"
netmask = "255.255.0.0"
ip_address = "172.16.255.255"
result = subnet_lookup(subnet=subnet, netmask=netmask, ip_address=ip_address)
print(result)
In Python, what is the best way to determine if an IP address (e.g., '127.0.0.1' or '10.98.76.6') is on a private network? The code does not sound difficult to write. But there may be more edge cases than are immediately apparent, and there's IPv6 support to consider, etc. Is there an existing library that does it?
Since Python 3.3 there is an ipaddress module in the stdlib that you can use.
>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1').is_private
True
If using Python 2.6 or higher I would strongly recommend to use a backport of this module.
Check out the IPy module. If has a function iptype() that seems to do what you want:
>>> from IPy import IP
>>> ip = IP('127.0.0.0/30')
>>> ip.iptype()
'PRIVATE'
You can check that yourself using
https://www.rfc-editor.org/rfc/rfc1918 and https://www.rfc-editor.org/rfc/rfc3330. If you have 127.0.0.1 you just need to & it with the mask (lets say 255.0.0.0) and see if the value matches any of the private network's network address. So using inet_pton you can do: 127.0.0.1 & 255.0.0.0 = 127.0.0.0
Here is the code that illustrates that:
from struct import unpack
from socket import AF_INET, inet_pton
def lookup(ip):
f = unpack('!I',inet_pton(AF_INET,ip))[0]
private = (
[ 2130706432, 4278190080 ], # 127.0.0.0, 255.0.0.0 https://www.rfc-editor.org/rfc/rfc3330
[ 3232235520, 4294901760 ], # 192.168.0.0, 255.255.0.0 https://www.rfc-editor.org/rfc/rfc1918
[ 2886729728, 4293918720 ], # 172.16.0.0, 255.240.0.0 https://www.rfc-editor.org/rfc/rfc1918
[ 167772160, 4278190080 ], # 10.0.0.0, 255.0.0.0 https://www.rfc-editor.org/rfc/rfc1918
)
for net in private:
if (f & net[1]) == net[0]:
return True
return False
# example
print(lookup("127.0.0.1"))
print(lookup("192.168.10.1"))
print(lookup("10.10.10.10"))
print(lookup("172.17.255.255"))
# outputs True True True True
another implementation is to compute the int values of all private blocks:
from struct import unpack
from socket import AF_INET, inet_pton
lookup = "127.0.0.1"
f = unpack('!I',inet_pton(AF_INET,lookup))[0]
private = (["127.0.0.0","255.0.0.0"],["192.168.0.0","255.255.0.0"],["172.16.0.0","255.240.0.0"],["10.0.0.0","255.0.0.0"])
for net in private:
mask = unpack('!I',inet_aton(net[1]))[0]
p = unpack('!I',inet_aton(net[0]))[0]
if (f & mask) == p:
print lookup + " is private"
This is the fixed version of the regex approach suggested by #Kurt including the fix recommended by #RobEvans
^127.\d{1,3}.\d{1,3}.\d{1,3}$
^10.\d{1,3}.\d{1,3}.\d{1,3}$
^192.168.\d{1,3}.\d{1,3}$
^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$
def is_ip_private(ip):
# https://en.wikipedia.org/wiki/Private_network
priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
priv_20 = re.compile("^192\.168\.\d{1,3}.\d{1,3}$")
priv_16 = re.compile("^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$")
res = priv_lo.match(ip) or priv_24.match(ip) or priv_20.match(ip) or priv_16.match(ip)
return res is not None
This will not 100.x.x.x range which is used internally in kubernetes
A few days after asking this question, I found out about this Google project, ipaddr-py, which appears to have some of the same functionality with respect to determining if an address is private (is_rfc1918). Apparently this will be standard in Python 3.1.
I find this in cuckoo.There is no need to install new modules.Just import two built-in modules: socket and struct. And use function below.
def _is_private_ip(self, ip):
"""Check if the IP belongs to private network blocks.
#param ip: IP address to verify.
#return: boolean representing whether the IP belongs or not to
a private network block.
"""
networks = [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.88.99.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"240.0.0.0/4",
"255.255.255.255/32",
"224.0.0.0/4",
]
for network in networks:
try:
ipaddr = struct.unpack(">I", socket.inet_aton(ip))[0]
netaddr, bits = network.split("/")
network_low = struct.unpack(">I", socket.inet_aton(netaddr))[0]
network_high = network_low | 1 << (32 - int(bits)) - 1
if ipaddr <= network_high and ipaddr >= network_low:
return True
except Exception,err:
continue
return False
If you want to avoid importing a module you can just apply a simple regex:
^127.\d{1,3}.\d{1,3}.\d{1,3}$
^10.\d{1,3}.\d{1,3}.\d{1,3}$
^192.168.\d{1,3}$
^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$