Wie kann ich bei einer IP-Adresse (z. B. 192.168.0.1) überprüfen, ob sie sich in einem Netzwerk (z. B. 192.168.0.0/24) in Python befindet?
Gibt es in Python allgemeine Werkzeuge zur Manipulation von IP-Adressen? Sachen wie Host-Lookups, IP-Adresse an Int, Netzwerkadresse mit Netzmaske an Int und so weiter? Hoffentlich in der Standard-Python-Bibliothek für 2.5.
Dieser Artikel zeigt, dass Sie dies mit socket
und struct
Modulen ohne viel zusätzlichen Aufwand tun können. Ich habe dem Artikel ein wenig hinzugefügt:
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)
Dies gibt aus:
False
True
Wenn Sie nur eine einzige Funktion wünschen, die Zeichenfolgen benötigt, würde dies folgendermaßen aussehen:
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
Ich benutze gerne netaddr dafür:
from netaddr import CIDR, IP
if IP("192.168.0.1") in CIDR("192.168.0.0/24"):
print "Yay!"
Wie arno_v in den Kommentaren darauf hingewiesen hat, macht es die neue Version von netaddr folgendermaßen:
from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
print "Yay!"
Verwendung von ipaddress ( in der stdlib seit 3.3 , am PyPi für 2.6/2.7 ):
>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24')
True
Wenn Sie ein lot von IP-Adressen auf diese Weise auswerten möchten, möchten Sie wahrscheinlich die Netzmaske vorab berechnen, z
n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)
Berechnen Sie dann für jede Adresse die binäre Darstellung mit einer von
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
Zum Schluss können Sie einfach überprüfen:
in_network = (a & mask) == netw
Dieser Code funktioniert für mich unter Linux x86. Ich habe nicht wirklich über Endianess-Probleme nachgedacht, aber ich habe es gegen das "ipaddr" -Modul getestet, bei dem über 200K-IP-Adressen mit 8 verschiedenen Netzwerkstrings getestet wurden.
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)
Beispiel:
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16')
True
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/24')
False
Für Python3
In [64]: ipaddress.IPv4Address('192.168.1.1') in ipaddress.IPv4Network('192.168.0.0/24')
Out[64]: False
Ich bin kein Fan von Modulen, wenn sie nicht benötigt werden. Dieser Job erfordert nur einfache Berechnungen, daher ist hier meine einfache Funktion:
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
Dann verwenden Sie es:
>>> 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)
Das ist es, das ist viel schneller als die oben genannten Lösungen mit den enthaltenen Modulen.
Ich habe die Lösung von Dave Webb ausprobiert, aber einige Probleme festgestellt:
Grundsätzlich gilt: Eine Übereinstimmung sollte durch UND-Verknüpfung der IP-Adresse mit der Maske und anschließender Überprüfung des Ergebnisses mit der Netzwerkadresse überprüft werden. Die IP-Adresse wurde NICHT mit der Netzwerkadresse AND-verknüpft, wie dies geschehen ist.
Ich habe auch bemerkt, dass das Ignorieren des Endian-Verhaltens unter der Annahme, dass Konsistenz sparen wird, nur für Masken an Oktettgrenzen (/ 24,/16) funktioniert. Damit andere Masken (/ 23,/21) korrekt funktionieren, habe ich den struct-Befehlen ein "Größer als" hinzugefügt und den Code zum Erstellen der Binärmaske so geändert, dass er mit allen "1" beginnt und nach links verschoben wird (32-Maske) ).
Schließlich fügte ich eine einfache Überprüfung hinzu, dass die Netzwerkadresse für die Maske gültig ist, und drucke nur eine Warnung, falls dies nicht der Fall ist.
Hier ist das Ergebnis:
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
Die akzeptierte Antwort funktioniert nicht ... was mich wütend macht. Die Maske ist rückwärts und funktioniert nicht mit Bits, die keine einfachen 8-Bit-Blöcke sind (z. B./24). Ich habe die Antwort angepasst und es funktioniert gut.
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
hier ist eine Funktion, die eine gepunktete binäre Zeichenfolge zurückgibt, um die Maskierung zu erleichtern. Art von ipcalc
-Ausgabe.
def bb(i):
def s = '{:032b}'.format(i)
def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]
z.B:
Marc's Code ist fast korrekt. Eine vollständige Version des Codes ist -
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))
Offensichtlich aus den gleichen Quellen wie oben ...
Ein sehr wichtiger Hinweis ist, dass der erste Code eine kleine Störung aufweist. Die IP-Adresse 255.255.255.255 wird auch als gültige IP-Adresse für jedes Subnetz angezeigt. Ich hatte eine Menge Zeit, diesen Code zum Laufen zu bringen und danke Marc für die richtige Antwort.
Nicht in der Standard-Bibliothek für 2.5, aber ipaddr macht dies sehr einfach. Ich glaube es ist in 3.3 unter dem Namen 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)
Wenn Sie sich auf das "struct" -Modul verlassen, kann dies zu Problemen mit der Endianität und der Schriftgröße führen und wird nicht benötigt. Noch ist socket.inet_aton (). Python funktioniert sehr gut mit gepunkteten Quad-IP-Adressen:
def ip_to_u32(ip):
return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)
Ich muss den IP-Abgleich für jeden Socket-Accept () - Aufruf gegen eine ganze Reihe zulässiger Quellennetzwerke durchführen, sodass ich Masken und Netzwerke als Ganzzahlen vorberechne:
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
Dann kann ich schnell sehen, ob sich eine bestimmte IP in einem dieser Netzwerke befindet:
ip = ip_to_u32(ipstr)
for mask, net in cached_masks:
if ip & mask == net:
# matched!
break
else:
raise BadClientIP(ipstr)
Es sind keine Modulimporte erforderlich, und der Code ist sehr schnell beim Abgleichen.
Die gewählte Antwort hat einen Fehler.
Folgendes ist der korrekte 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
Hinweis: ipaddr & netmask == netaddr & netmask
statt ipaddr & netmask == netmask
.
Ich ersetze auch ((2L<<int(bits)-1) - 1)
durch ((1L << int(bits)) - 1)
, da letzteres verständlicher erscheint.
Hier ist eine Klasse, die ich für den längsten Präfixvergleich geschrieben habe:
#!/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
Und hier ist ein Testprogramm:
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
# Dies funktioniert einwandfrei ohne die seltsame byteweise Behandlung von Def addressInNetwork (ip, net): '' 'Ist eine Adresse in einem Netzwerk' '' # Adressen in Host-Reihenfolge konvertieren, so dass Verschiebungen tatsächlich sinnvoll sind ip = struct.unpack ('> L', socket.inet_aton (ip)) [0] netaddr, bits = net.split ('/') netaddr = struct.unpack ('> L', socket.inet_aton (netaddr)) [0] # Muss einen Alle-Wert nach links verschieben,/32 = Nullpunktverschiebung,/0 = 32 nach links verschieben Netzmaske = (0xffffffff & lt (32-int (Bits))) & 0xffffffff # Es ist nicht erforderlich, die Netzwerkadresse zu maskieren, sofern es sich um eine korrekte Netzwerkadresse handelt return (ip & netmask) == netaddr
>>> 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')]
Hier ist die Verwendung für diese Methode:
>>> 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.
Grundsätzlich geben Sie eine IP-Adresse als erstes Argument und eine Liste von cidrs als zweites Argument an. Eine Trefferliste wird zurückgegeben.
Danke für dein Skript!
Ich habe ziemlich lange daran gearbeitet, um alles zum Laufen zu bringen ... Also teile ich es hier
die makeMask-Funktion funktioniert nicht! Nur für/8,/16,/24 arbeiten
Ex:
bits = "21"; socket.inet_ntoa (struct.pack ('= L', (2L << int (bits) -1) - 1))
'255.255.31.0', wobei es 255.255.248.0 sein sollte
Also habe ich eine andere Funktion verwendet, die calcDottedNetmask (mask) von http://code.activestate.com/recipes/576483-convert-subnetmask-von-cidr-notation-to-dotdecima/ verwendet.
Ex:
#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!
So sieht meine neue addressInNetwork-Funktion so aus:
#!/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))
Und jetzt ist die Antwort richtig !!
#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
False
Ich hoffe, es hilft anderen Menschen und spart ihnen Zeit!
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
Falsch
Wahr
Falsch
Ich kenne nichts in der Standardbibliothek, aber PySubnetTree ist eine Python-Bibliothek, die Subnetzabgleich durchführen wird.
Es gibt eine API mit dem Namen SubnetTree, die in Python verfügbar ist und diese Aufgabe sehr gut erfüllt. Dies ist ein einfaches Beispiel:
import SubnetTree
t = SubnetTree.SubnetTree()
t.insert("10.0.1.3/32")
print("10.0.1.3" in t)
vorherige Lösung hat einen Fehler in ip & net == net. Richtige IP-Suche ist 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");
In Bezug auf all das denke ich, dass socket.inet_aton () Bytes in der Reihenfolge des Netzwerks zurückgibt, also ist der korrekte Weg, sie zu entpacken, wahrscheinlich
struct.unpack('!L', ... )
Hier ist mein 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')
Ich habe einen Teil der vorgeschlagenen Lösungen in diesen Antworten ausprobiert. Ohne Erfolg habe ich schließlich den vorgeschlagenen Code angepasst und repariert und meine feste Funktion geschrieben.
Ich habe es getestet und arbeitet zumindest mit kleinen Endian-Architekturen - z. B. x86 -. Wenn jemand eine Big-Endian-Architektur ausprobieren möchte, geben Sie mir bitte ein Feedback.
IP2Int
-Code stammt aus diesem Beitrag , die andere Methode ist eine vollständige (für meine Testfälle) funktionierende Korrektur früherer Vorschläge in dieser Frage.
Der 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
Hoffe nützlich,
Aus verschiedenen Quellen oben und aus meiner eigenen Recherche habe ich so das Subnetz und die Adressberechnung zum Laufen gebracht. Diese Stücke reichen aus, um die Frage und andere verwandte Fragen zu lösen.
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)
Wenn Sie keine anderen Module importieren möchten, können Sie Folgendes tun:
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]