En introduktion till modulen ipaddress

författare:

Peter Moody

författare:

Nick Coghlan

Skapa Adress/Nätverk/Interface-objekt

Eftersom ipaddress är en modul för att inspektera och manipulera IP-adresser är det första du behöver göra att skapa några objekt. Du kan använda ipaddress för att skapa objekt från strängar och heltal.

En anmärkning om IP-versioner

För läsare som inte är särskilt bekanta med IP-adressering är det viktigt att veta att Internetprotokollet (IP) för närvarande håller på att övergå från version 4 av protokollet till version 6. Denna övergång sker till stor del på grund av att version 4 av protokollet inte ger tillräckligt med adresser för att hantera hela världens behov, särskilt med tanke på det ökande antalet enheter med direktanslutningar till internet.

Att förklara detaljerna i skillnaderna mellan de två versionerna av protokollet ligger utanför ramen för denna introduktion, men läsarna måste åtminstone vara medvetna om att dessa två versioner finns, och det kommer ibland att bli nödvändigt att tvinga fram användningen av den ena eller den andra versionen.

IP-värdadresser

Adresser, som ofta kallas ”värdadresser”, är den mest grundläggande enheten när man arbetar med IP-adressering. Det enklaste sättet att skapa adresser är att använda fabriksfunktionen ipaddress.ip_address(), som automatiskt avgör om en IPv4- eller IPv6-adress ska skapas baserat på det värde som skickas in:

>>> ipaddress.ip_address('192.0.2.1')
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address('2001:DB8::1')
IPv6Address('2001:db8::1')

Adresser kan också skapas direkt från heltal. Värden som ryms inom 32 bitar antas vara IPv4-adresser:

>>> ipaddress.ip_address(3221225985)
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address(42540766411282592856903984951653826561)
IPv6Address('2001:db8::1')

För att tvinga fram användning av IPv4- eller IPv6-adresser kan de relevanta klasserna anropas direkt. Detta är särskilt användbart för att tvinga fram skapandet av IPv6-adresser för små heltal:

>>> ipaddress.ip_address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv4Address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv6Address(1)
IPv6Address('::1')

Definiera nätverk

Värdadresser grupperas vanligtvis i IP-nätverk, så ipaddress ger ett sätt att skapa, inspektera och manipulera nätverksdefinitioner. IP-nätverksobjekt konstrueras av strängar som definierar det intervall av värdadresser som ingår i nätverket. Den enklaste formen för denna information är ett par ”nätverksadress/nätverksprefix”, där prefixet definierar antalet ledande bitar som jämförs för att avgöra om en adress ingår i nätverket eller inte och nätverksadressen definierar det förväntade värdet för dessa bitar.

När det gäller adresser finns det en fabriksfunktion som automatiskt bestämmer rätt IP-version:

>>> ipaddress.ip_network('192.0.2.0/24')
IPv4Network('192.0.2.0/24')
>>> ipaddress.ip_network('2001:db8::0/96')
IPv6Network('2001:db8::/96')

Nätverksobjekt kan inte ha några host-bitar inställda. Den praktiska effekten av detta är att 192.0.2.1/24 inte beskriver ett nätverk. Sådana definitioner kallas interface-objekt eftersom notationen ip-on-a-network vanligen används för att beskriva nätverksgränssnitt för en dator i ett visst nätverk och beskrivs närmare i nästa avsnitt.

Som standard kommer försök att skapa ett nätverksobjekt med host-bitar inställda att resultera i ValueError. För att begära att de extra bitarna istället ska tvingas till noll, kan flaggan strict=False skickas till konstruktören:

>>> ipaddress.ip_network('192.0.2.1/24')
Traceback (most recent call last):
   ...
ValueError: 192.0.2.1/24 has host bits set
>>> ipaddress.ip_network('192.0.2.1/24', strict=False)
IPv4Network('192.0.2.0/24')

Även om strängformen ger betydligt större flexibilitet kan nätverk också definieras med heltal, precis som värdadresser. I det här fallet anses nätverket endast innehålla den enda adress som identifieras av heltalet, så nätverksprefixet innehåller hela nätverksadressen:

>>> ipaddress.ip_network(3221225984)
IPv4Network('192.0.2.0/32')
>>> ipaddress.ip_network(42540766411282592856903984951653826560)
IPv6Network('2001:db8::/128')

Precis som med adresser kan skapandet av en viss typ av nätverk tvingas fram genom att anropa klassens konstruktör direkt i stället för att använda fabriksfunktionen.

Host-gränssnitt

Som nämnts ovan räcker det inte med vare sig adressen eller nätverksklasserna om du behöver beskriva en adress i ett visst nätverk. Notation som 192.0.2.1/24 används ofta av nätverksingenjörer och de människor som skriver verktyg för brandväggar och routrar som kortform för ”värden 192.0.2.1 på nätverket 192.0.2.0/24”, Följaktligen tillhandahåller ipaddress en uppsättning hybridklasser som associerar en adress med ett visst nätverk. Gränssnittet för skapande är identiskt med det för definition av nätverksobjekt, förutom att adressdelen inte är begränsad till att vara en nätverksadress.

>>> ipaddress.ip_interface('192.0.2.1/24')
IPv4Interface('192.0.2.1/24')
>>> ipaddress.ip_interface('2001:db8::1/96')
IPv6Interface('2001:db8::1/96')

Inmatning av heltal accepteras (som med nätverk), och användning av en viss IP-version kan tvingas fram genom att anropa den relevanta konstruktören direkt.

Inspektera objekt för adress/nätverk/gränssnitt

Du har gjort dig besväret att skapa ett IPv(4|6)(Address|Network|Interface)-objekt, så du vill förmodligen få information om det. ipaddress försöker göra detta enkelt och intuitivt.

Extrahering av IP-versionen:

>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr6 = ipaddress.ip_address('2001:db8::1')
>>> addr6.version
6
>>> addr4.version
4

Hämta nätverket från ett gränssnitt:

>>> host4 = ipaddress.ip_interface('192.0.2.1/24')
>>> host4.network
IPv4Network('192.0.2.0/24')
>>> host6 = ipaddress.ip_interface('2001:db8::1/96')
>>> host6.network
IPv6Network('2001:db8::/96')

Ta reda på hur många individuella adresser det finns i ett nätverk:

>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.num_addresses
256
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.num_addresses
4294967296

Iterering genom de ”användbara” adresserna i ett nätverk:

>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> for x in net4.hosts():
...     print(x)
192.0.2.1
192.0.2.2
192.0.2.3
192.0.2.4
...
192.0.2.252
192.0.2.253
192.0.2.254

Skaffa nätmasken (dvs. ställa in bitar som motsvarar nätverksprefixet) eller värdmasken (alla bitar som inte ingår i nätmasken):

>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.netmask
IPv4Address('255.255.255.0')
>>> net4.hostmask
IPv4Address('0.0.0.255')
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.netmask
IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
>>> net6.hostmask
IPv6Address('::ffff:ffff')

Sprängning eller komprimering av adressen:

>>> addr6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0001'
>>> addr6.compressed
'2001:db8::1'
>>> net6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0000/96'
>>> net6.compressed
'2001:db8::/96'

Även om IPv4 inte stöder explosion eller komprimering tillhandahåller de associerade objekten fortfarande de relevanta egenskaperna så att versionsneutral kod enkelt kan säkerställa att den mest kortfattade eller mest utförliga formen används för IPv6-adresser samtidigt som IPv4-adresser hanteras korrekt.

Nätverk som adresslistor

Det är ibland användbart att behandla nätverk som listor. Detta innebär att det är möjligt att indexera dem så här:

>>> net4[1]
IPv4Address('192.0.2.1')
>>> net4[-1]
IPv4Address('192.0.2.255')
>>> net6[1]
IPv6Address('2001:db8::1')
>>> net6[-1]
IPv6Address('2001:db8::ffff:ffff')

Det innebär också att nätverksobjekt lämpar sig för att använda syntaxen för list membership test på följande sätt:

om adress i nätverk:
    # gör något

Containment-testning görs effektivt baserat på nätverksprefixet:

>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr4 in ipaddress.ip_network('192.0.2.0/24')
True
>>> addr4 in ipaddress.ip_network('192.0.3.0/24')
False

Jämförelser

ipaddress ger några enkla, förhoppningsvis intuitiva sätt att jämföra objekt, där det är meningsfullt:

>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
True

Ett TypeError-undantag uppstår om du försöker jämföra objekt av olika versioner eller olika typer.

Använda IP-adresser med andra moduler

Andra moduler som använder IP-adresser (t.ex. socket) accepterar vanligtvis inte objekt från den här modulen direkt. Istället måste de omvandlas till ett heltal eller en sträng som den andra modulen accepterar:

>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> str(addr4)
'192.0.2.1'
>>> int(addr4)
3221225985

Få mer information när skapandet av en instans misslyckas

När du skapar adress/nätverks/interface-objekt med hjälp av de versionsagnostiska fabriksfunktionerna kommer eventuella fel att rapporteras som ValueError med ett generiskt felmeddelande som helt enkelt säger att det inmatade värdet inte identifierades som ett objekt av den typen. Att det inte finns något specifikt fel beror på att det är nödvändigt att veta om värdet förutsätts vara IPv4 eller IPv6 för att kunna ge mer information om varför det har avvisats.

För att stödja användningsfall där det är användbart att ha tillgång till denna ytterligare detalj, ger de enskilda klasskonstruktörerna faktiskt upphov till underklasserna ValueError ipaddress.AddressValueError och ipaddress.NetmaskValueError för att ange exakt vilken del av definitionen som inte analyserades korrekt.

Felmeddelandena är betydligt mer detaljerade när man använder klasskonstruktörerna direkt. Till exempel:

>>> ipaddress.ip_address("192.168.0.256")
Traceback (most recent call last):
  ...
ValueError: '192.168.0.256' does not appear to be an IPv4 or IPv6 address
>>> ipaddress.IPv4Address("192.168.0.256")
Traceback (most recent call last):
  ...
ipaddress.AddressValueError: Octet 256 (> 255) not permitted in '192.168.0.256'

>>> ipaddress.ip_network("192.168.0.1/64")
Traceback (most recent call last):
  ...
ValueError: '192.168.0.1/64' does not appear to be an IPv4 or IPv6 network
>>> ipaddress.IPv4Network("192.168.0.1/64")
Traceback (most recent call last):
  ...
ipaddress.NetmaskValueError: '64' is not a valid netmask

Båda de modulspecifika undantagen har dock ValueError som sin överordnade klass, så om du inte är intresserad av den specifika typen av fel kan du fortfarande skriva kod som följande:

prova:
    network = ipaddress.IPv4Network(address)
except ValueError:
    print('adress/nätmask är ogiltig för IPv4:', adress)