halloICKEbins
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
Mahlzeit, ich habe mehre IP's (1.0.87.3, 1.0.106.7, 1.0.120.33) und möchte jetzt prüfen, ob sie in den jeweiligen Bereich meiner Liste fallen network,id
1.0.0.0/24,1
1.0.1.0/24,2
1.0.2.0/23,3
1.0.4.0/22,4
1.0.87.0/24,5
1.0.88.0/22,6
1.0.92.0/22,7
1.0.116.0/22,8
1.0.120.0/23,9 Kann ich das mit ipaddress — IPv4/IPv6 manipulation library machen oder sollte ich was anderes verwenden. Nachfolgend mein bisheriger Quellcode: 1
2
3
4
5
6
7
8
9
10
11
12 | data_file = open('/home/user1/data.csv')
networks, ids = [], []
for index, line in enumerate(data_file.readlines()):
if index > 1:
networks.append(str(line.split(',')[0]))
ids.append(float(line.split(',')[1]))
# eingabe ip
for network, id in zip(networks, ids):
# wenn ip im bereich network, dann print (id, network, ip)
|
|
frostschutz
Anmeldungsdatum: 18. November 2010
Beiträge: 7658
|
Die Library klingt doch gut? Auf der von dir verlinkten Seite: >>> IPv4Address('192.0.2.6') in IPv4Network('192.0.2.0/28')
True
>>> IPv4Address('192.0.3.6') in IPv4Network('192.0.2.0/28')
False ist doch genau das was du suchst? Das selbst zu prüfen ist ja auch kein Hexenwerk, aber wozu das Rad neu erfinden.
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
😊 ... den Punkt "21.28.3.3.3. Networks as containers of addresses" hatte ich glatt überlesen. Den Rest bekomme ich hin...Schönen Dank
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
Mahlzeit, Update: Ich bekomme es leider nicht alleine hin ☹ Habe den nachfolgende Quellcode mit der Datei network.csv: 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | from ipaddress import IPv4Address, IPv4Network, IPv4Interface
from collections import defaultdict
data_file = open('/home/user1/networks.csv')
entry_ip = '223.255.244.10'
networks = defaultdict(list)
for index, line in enumerate(data_file.readlines()):
networks[index] = str(line.split(',')[0])
for i in range(len(networks)):
if IPv4Address(entry_ip) in IPv4Network(networks[i]):
print (networks[i], entry_ip)
|
...
223.255.236.0/22,stuff1,stuff2
223.255.240.0/22,stuff3,stuff4
223.255.244.0/22,stuff5,stuff6
223.255.248.0/22,stuff7,stuff8
... Die Datei networks.csv hat ca. 3 Millionen Einträge. Ziel ist es jetzt möglichst schnell die IP's (später wird es eine Liste) mit der Netzwerkliste abzugleichen. Später will ich die IP mit den Bereichen und den zusätzlichen Infos seperat speichern. Habe zuerst versucht mit normalen Listen zu arbeiten aber das dauert beim Speichern schon ewig. Mit defaultdict geht es. Leider habe ich bei der neuen Variante jetzt auch noch den zusätzlichen Fehler: AddressValueError: Expected 4 octets in 'network'
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Muss man die 3 Millionen IPv4Network() Instanzen unbedingt zwischenspeichern? 1
2
3
4
5
6
7
8
9
10
11
12
13 | from ipaddress import IPv4Address, IPv4Network, IPv4Interface
data_file = '/home/user1/networks.csv'
ip_entries = [IPv4Address('223.255.244.10'), ]
with open(data_file) as f:
for line in f:
netmask, *_ = line.split(',', maxsplit=1)
network = IPv4Network(netmask)
for ip in ip_entries:
if ip in network:
print(netmask, ip)
|
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
Nein, sie müssen nicht unbedingt zwischengespeichert werden, allerdings werden am Ende regelmäßig 1000 - 10000 IP's mit der Liste verglichen und die entsprechende Werte + die "Stuff"-Werte in eine Datei bzw. Datenbank gespeichert.
Ist die Frage, was dann schneller ist: Zwischenspeicherung oder wiederholtes Neuladen. Sollten hierbei die IP's aus einer Datei oder aus dem Speicher geladen werden. Ziel ist hierbei auch die schnellstmöglich Abarbeitung der Aufgabe!
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Effektiv bleibt dir ja nur den Suchraum einzuschränken, um die Zahl der Vergleiche zu minimieren (wenn man ein Dictionary mit jeder möglichen öffentlichen IP-Addresse als Key erstellen würde, das als Wert das Netzsegment hat, bräuchte man eine Menge Arbeitsspeicher). Du könntest also erst mal ein Set aus den möglichen Längen der Netzwerkmasken erstellen und dir dann überlegen, ob du per Bit, Byte oder einem anderen Teilstück einer IPv4-Addresse eine eindeutige Untergruppe ableiten kannst. Wenn man die Netwerksegmente nach ganzen Bytes auftrennen und nach dem ersten Match für eine IP aufhören wollte, könnte das z.B. so aussehen: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 | #!/usr/bin/env python3
from ipaddress import IPv4Address, IPv4Network, IPv4Interface
from collections import deque
ip_entries = [IPv4Address('223.255.244.10'), ]
networks = {
223: {
255: [
(IPv4Network("223.255.236.0/22"), "stuff1", "stuff2"),
(IPv4Network("223.255.240.0/22"), "stuff3", "stuff4"),
(IPv4Network("223.255.244.0/22"), "stuff5", "stuff6"),
(IPv4Network("223.255.248.0/22"), "stuff7", "stuff8"),
]
}
}
def check_dict(d, keys):
c = d.get(keys.popleft(), [])
if not c:
raise KeyError
elif isinstance(c, dict):
return check_dict(c, keys)
elif isinstance(c, list):
return c
for ip in ip_entries:
try:
for network in check_dict(networks, deque(ip.packed)):
print("check if ip", ip, "is in network", network[0])
if ip in network[0]:
print("found", ip, "in", network)
break
except KeyError as e:
pass
|
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
Mahlzeit, die 2te Variante von heute morgen funktioniert. Bei der 1sten kommt die Fehlermeldung:
AddressValueError: Expected 4 octets in 'network' Wieso?
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
seahawk1986 schrieb: | networks = {
223: {
255: [
(IPv4Network("223.255.236.0/22"), "stuff1", "stuff2"),
(IPv4Network("223.255.240.0/22"), "stuff3", "stuff4"),
(IPv4Network("223.255.244.0/22"), "stuff5", "stuff6"),
(IPv4Network("223.255.248.0/22"), "stuff7", "stuff8"),
]
}
}
|
Gibt es eine Möglichkeit, diesen Teil aus einer Datei generieren zu lassen, da es ca. 3 Millionen Einträge sind, also von 1.0.0.0/24 bis 223.255.255.0/24.
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
halloICKEbins schrieb: die 2te Variante von heute morgen funktioniert. Bei der 1sten kommt die Fehlermeldung:
AddressValueError: Expected 4 octets in 'network' Wieso?
Hast du mal geprüft, ob alle Adressräume in deiner /home/user1/networks.csv syntaktisch korrekt sind? | import ipaddress
with open('/home/user1/networks.csv') as f:
for n, line in enumerate(f, start=1):
netmask, *_ = line.split(',', maxsplit=1)
try:
network = ipaddress.IPv4Network(netmask)
except ipaddress.AddressValueError:
print("could not parse Address", netmask, "in line", n)
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
halloICKEbins schrieb: Gibt es eine Möglichkeit, diesen Teil aus einer Datei generieren zu lassen, da es ca. 3 Millionen Einträge sind, also von 1.0.0.0/24 bis 223.255.255.0/24.
Als erstes würde ich mir alle vorkommenden Längen der Netzmasken in den Daten ansehen, um zu wissen, wie die Addressräume aufgetrennt sind:
| import re
netmask_len_re = re.compile(r'(?<=/)\d+(?=,)')
with open('/home/user1/networks.csv') as f:
print(set(re.search(netmask_len_re, line).group() for line in f))
|
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
Den falschen Eintrag hab ich gefunden allerdings habe ich mich vielleicht unklar ausgedrückt mit den networks...Die Datei hat folgendes Schema: network,eintrag1,eintrag2,usw 1.0.0.0/24,eintrag1,eintrag2,usw
1.0.1.0/24,eintrag1,eintrag2,usw
1.0.2.0/23,eintrag1,eintrag2,usw
1.0.4.0/22,eintrag1,eintrag2,usw
1.0.8.0/21,eintrag1,eintrag2,usw
1.0.16.0/20,eintrag1,eintrag2,usw
1.0.32.0/19,eintrag1,eintrag2,usw
1.0.64.0/20,eintrag1,eintrag2,usw
1.0.80.0/22,eintrag1,eintrag2,usw
1.0.84.0/23,eintrag1,eintrag2,usw
...
223.255.231.0/24,eintrag1,eintrag2,usw
223.255.232.0/23,eintrag1,eintrag2,usw
223.255.234.0/23,eintrag1,eintrag2,usw
223.255.236.0/22,eintrag1,eintrag2,usw
223.255.240.0/22,eintrag1,eintrag2,usw
223.255.244.0/22,eintrag1,eintrag2,usw
223.255.248.0/22,eintrag1,eintrag2,usw
223.255.252.0/23,eintrag1,eintrag2,usw
223.255.254.0/24,eintrag1,eintrag2,usw
223.255.255.0/24,eintrag1,eintrag2,usw
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Zeigt doch mal die vorkommenden Längen der Netzmasken (Code in diesem Post) - mich interessiert, wie viele Bits bzw. Bytes man für die Netzwerkeinträge als eindeutig ansehen kann.
|
halloICKEbins
(Themenstarter)
Anmeldungsdatum: 12. September 2017
Beiträge: 226
|
{'12', '17', '29', '26', '15', '13', '9', '32', '11', '8', '10', '21', '24', '14', '16', '31', '30', '22', '28', '20', '18', '25', '27', '19', '23'}
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Ok, dann würde ich mal versuchen die Daten so aufzubereiten:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | #/usr/bin/env python3
from ipaddress import IPv4Network
from collections import defaultdict, deque
networks = defaultdict(dict)
with open('/home/user1/networks.csv') as f:
for ln, line in enumerate(f):
if not line: continue
n, *data = line.split(',')
try:
network = IPv4Network(n)
except ValueError:
print("invalid network", n, "in line", ln)
continue
octets = deque(network.network_address.packed)
if network.prefixlen >=24:
networks[octets.popleft(),][network] = data
elif n.prefixlen >=16:
networks[octets.popleft(), octets.popleft()][network] = data
else:
networks[octets.popleft(), octets.popleft(), octets.popleft()][network] = data
|
Wenn man kann man das mit Daten befüllte networks Objekt mit pickle in eine Datei schreiben und bei Bedarf wieder laden lassen (das musst du ausprobieren, ob das einen messbaren Zeitvorteil bringt).
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import pickle
def load(filename):
with open(filename, 'rb') as f:
return pickle.load(f)
def save(filename, obj):
with open(filename, 'wb') as f:
pickle.dump(obj, f, protocol=pickle.HIGHEST_PROTOCOL)
# Objekt in Datei speichern
save('networks.pickled', networks)
# Objekt aus Datei laden
networks = load('networks.pickled')
|
Und bei der Abfrage würde ich dann die möglichen Tuples der Oktette beginnend mit der Folge aus 3 Oktetten als Key durchgehen:
1
2
3
4
5
6
7
8
9
10
11
12 | from ipaddress import IPv4Address
ip_entries = [IPv4Address('223.255.244.10'), ]
for ip in ip_entries:
octets = tuple(ip.packed)
for i in 3, 2, 1:
r = networks.get(octets[:i])
if r:
for network, data in r.items():
if ip in network:
print(network, data)
break
|
Edit: lambda rausgenommen, das vereinfacht das Arbeiten mit pickle.
|