Reputation: 47
I have a python script that grabs all IP from the arp table and assign it to a variable. I have for loop that creates another two variables start_IP containing the first IP of a subnet and last_IP containing the last IP in that same subnet. For each loop I will have a different start and last IPs.
I am trying to do check the variable containing all IPs and see how many IPs fall under each subnet.
What would be the best way to do this? Here is a hardcoded example: count = 0
arps = ['10.20.30.130','10.20.30.131','10.20.30.132', '10.20.30.133',
'10.20.30.136', '10.20.30.137', '10.20.30.138', '10.20.30.139', '10.20.30.140', '10.20.30.141', '10.20.30.143', '10.20.30.149']
start_ip = "10.20.30.132"
end_ip = "10.20.30.142"
count = 0
for arp in arps:
if arp >= start_ip and arp <= end_ip:
count = count + 1
print count
else:
continue
print "Count: ", count
Is there a better an faster way of doing this?
Upvotes: 2
Views: 2533
Reputation: 164
I can think of two solutions as below. Method 1 has a time complexity of O(N)
and Method 2 has a time complexity of O(Nlog N)
. As suggested by Amadan, the IP addresses need to be preprocessed beforehand.
import bisect
arps = ['10.20.30.130','10.20.30.131','10.20.30.132', '10.20.30.133',
'10.20.30.136', '10.20.30.137', '10.20.30.138', '10.20.30.139', '10.20.30.140', '10.20.30.141', '10.20.30.143', '10.20.30.149']
start_ip = "10.20.30.132"
end_ip = "10.20.30.142"
# Padding zeros to the IP addresses to make sure they are directly comparable
def padding(s):
return s.zfill(3)
arps = [".".join(list(map(padding, x.split(".")))) for x in arps]
start_ip = ".".join(list(map(padding, start_ip.split("."))))
end_ip = ".".join(list(map(padding, end_ip.split("."))))
# Method 1: Pythonic one-liner
print(sum(start_ip <= x <= end_ip for x in arps))
# Method 2: Sort and binary search
def find_lt(a, x):
i = bisect.bisect_right(a, x)
if i:
return i - 1
else:
return 0
def find_gt(a, x):
i = bisect.bisect_right(a, x)
if i != len(a):
return i
else:
return i
arps.sort()
print(find_gt(arps, end_ip) - find_lt(arps, start_ip))
Upvotes: 2
Reputation: 198476
Two ways. The simple way:
IP addresses compare octet-by-octet. Interestingly, Python lists compare element-by-element. So if you just split the IP addresses by dot and map the list to int
, you can compare them correctly.
The even simpler way:
ipaddress.ip_address
is comparable, as long the compared addresses are the same version (IPv4 or IPv6).
However, string comparison does not provide a correct ordering of IP addresses:
'1.12.1.1' < '1.2.1.1'
# => True (should be False)
Except for those issues, your code is fine. It could be written more concisely:
import ipaddress
arps = ['10.20.30.130','10.20.30.131','10.20.30.132', '10.20.30.133',
'10.20.30.136', '10.20.30.137', '10.20.30.138', '10.20.30.139',
'10.20.30.140', '10.20.30.141', '10.20.30.143', '10.20.30.149']
start_ip = "10.20.30.132"
end_ip = "10.20.30.142"
start_ip_ip = ipaddress.ip_address(start_ip)
end_ip_ip = ipaddress.ip_address(end_ip)
sum(1 for ip in arps if start_ip_ip <= ipaddress.ip_address(ip) <= end_ip_ip)
# => 8
If you specifically want to see addresses in specific subnet, you don't even need to use start and end addresses, if you know the subnet specification:
ipaddress.ip_address('192.168.1.17') in ipaddress.ip_network('192.168.0.0/16')
# => True
Upvotes: 3