ฉันจะตรวจสอบได้อย่างไรว่า ip อยู่ในเครือข่ายใน Python


101

ระบุที่อยู่ IP (พูด 192.168.0.1) ฉันจะตรวจสอบได้อย่างไรว่าอยู่ในเครือข่าย (พูด 192.168.0.0/24) ใน Python

มีเครื่องมือทั่วไปใน Python สำหรับการจัดการที่อยู่ IP หรือไม่? สิ่งต่างๆเช่นการค้นหาโฮสต์, ที่อยู่ IP ไปยัง int, ที่อยู่เครือข่ายที่มี netmask ถึง int และอื่น ๆ ? หวังว่าในไลบรารี Python มาตรฐานสำหรับ 2.5


คำถามนี้ดูเหมือนจะเป็น Canonical ที่ใช้ได้สำหรับคำตอบ 2.x ที่เก่ามาก แต่ล้าสมัยสำหรับ 3.x ดูวิธีจัดระเบียบและกำหนด Canonical สำหรับ“ Python / pandas เปรียบเทียบที่อยู่ IP / CIDR”
smci

@smci ฉันไม่เห็นว่าทำไม; คำตอบของ phihag ที่stackoverflow.com/a/1004527/1709587เป็นคำตอบที่ดีอย่างสมบูรณ์สำหรับ Python 3 และอยู่ที่นี่มาตั้งแต่ปี 2014 เราได้ย้อนกลับการแก้ไขของคุณที่ทำให้คำตอบนั้นไม่ถูกต้อง
Mark Amery

@Staale - คุณควรปรับปรุงคำตอบของคุณที่นี่หนึ่งที่ไม่ได้มีข้อผิดพลาดที่สำคัญ คำตอบอื่น ๆ ใช้ไลบรารีในตัวเพื่อทำสิ่งเดียวกันให้สำเร็จในโค้ด 1/10 โดยไม่มีข้อบกพร่องใด ๆ
แอดดิสัน

คำตอบ:


28

บทความนี้แสดงให้เห็นว่าคุณสามารถทำได้ด้วยโมดูลsocketและstructโดยไม่ต้องออกแรงมากเกินไป ฉันได้เพิ่มบทความเล็กน้อยดังนี้:

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)

ผลลัพธ์นี้:

False
True

หากคุณต้องการเพียงฟังก์ชันเดียวที่รับสตริงจะมีลักษณะดังนี้:

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

8
นอกจากนี้ struct.unpack ('L', socket.inet_aton (ip)) [0] จะล้มเหลวในสถาปัตยกรรมที่ 'L' คลายแพ็กเป็นสิ่งที่แตกต่างจาก 4 ไบต์โดยไม่คำนึงถึงความอดทน
Rafał Dowgird

5
ดำเนินการต่อในความคิดเห็นของ Rafal เพื่อให้สิ่งนี้ทำงานกับตัวแปล Python 64 บิตให้แทนที่บรรทัดที่เป็นปัญหาด้วย:return struct.unpack('<L',socket.inet_aton(ip))[0]
nitwit

12
ฉันคิดว่าโซลูชันของคุณมีข้อบกพร่องร้ายแรง: addressInNetwork('172.7.1.1', '172.3.0.0/16') -> True(ฉันแปลง 'L' เป็น '<L' ในระบบปฏิบัติการ 64 บิตของฉัน)
Taha Jahangir

21
ข้อควรระวัง: โซลูชันนี้มีข้อบกพร่องร้ายแรง:addressInNetwork('172.7.1.1', '172.3.0.0/16') -> True
Taha Jahangir

6
คำตอบนี้มีข้อผิดพลาด ดูคำตอบ: stackoverflow.com/questions/819355/…
Debanshu Kundu

158

ฉันชอบใช้netaddrสำหรับสิ่งนั้น:

from netaddr import CIDR, IP

if IP("192.168.0.1") in CIDR("192.168.0.0/24"):
    print "Yay!"

ดังที่ arno_v ชี้ให้เห็นในความคิดเห็น netaddr เวอร์ชันใหม่จะเป็นเช่นนี้:

from netaddr import IPNetwork, IPAddress
if IPAddress("192.168.0.1") in IPNetwork("192.168.0.0/24"):
    print "Yay!"

>>> netaddr.all_matching_cidrs ("192.168.0.1", ["192.168.0.0/24","212.11.64.0/19"]) [IPNetwork ('192.168.0.0/24')]

22
หรือในเวอร์ชันใหม่: จาก netaddr import IPNetwork, IPAddress IPAddress ("192.168.0.1") ใน IPNetwork ("192.168.0.0/24")
arno_v

147

ใช้IPADDRESS ( ใน STDLIB ตั้งแต่ 3.3 , ที่ PyPi 2.6 / 2.7 ):

>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1') in ipaddress.ip_network('192.168.0.0/24')
True

หากคุณต้องการประเมินไฟล์ ที่อยู่ IP จำนวนมากด้วยวิธีนี้คุณอาจต้องการคำนวณ netmask ล่วงหน้าเช่น

n = ipaddress.ip_network('192.0.0.0/16')
netw = int(n.network_address)
mask = int(n.netmask)

จากนั้นสำหรับที่อยู่แต่ละรายการให้คำนวณการแทนค่าฐานสองด้วยหนึ่งใน

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

สุดท้ายคุณสามารถตรวจสอบ:

in_network = (a & mask) == netw

2
ระวัง python-ipaddr ทำงานค่อนข้างช้าสำหรับเราดังนั้นจึงอาจไม่เหมาะสำหรับบางกรณีที่ต้องมีการเปรียบเทียบบ่อยครั้ง YMMV ดังนั้นจงเปรียบเทียบตัวเอง
drdaeman

ในบางเวอร์ชันคุณอาจต้องจัดหาสตริง Unicode แทนประเภท Python str เช่นipaddress.ip_address(u'192.168.0.1') in ipaddress.ip_network(u'192.168.0.0/24').
Moondoggy

1
ฉันกังวลว่าวิธีนี้กำลังทำซ้ำรายการที่อยู่ในเครือข่าย แต่โมดูล ipaddress จะแทนที่__contains__วิธีการเพื่อทำอย่างมีประสิทธิภาพโดยการเปรียบเทียบการแสดงจำนวนเต็มของเครือข่ายและที่อยู่ที่ออกอากาศดังนั้นโปรดมั่นใจว่าคุณกังวลหรือไม่ .
avatarofhope2

29

สำหรับ 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')

เอาท์พุต:

False
True

1
lib มาตรฐานใช้การตรวจสอบระดับบิตอย่างชาญฉลาดดังนั้นนี่จึงเป็นทางออกที่ดีที่สุดที่นี่ github.com/python/cpython/blob/3.8/Lib/ipaddress.py#L690
rocketspacer

10

รหัสนี้ใช้งานได้สำหรับฉันบน Linux x86 ฉันไม่ได้ให้ความคิดใด ๆ เกี่ยวกับปัญหา endianess แต่ฉันได้ทดสอบกับโมดูล "ipaddr" โดยใช้ที่อยู่ IP กว่า 200K ที่ทดสอบกับสายเครือข่าย 8 สายที่แตกต่างกันและผลลัพธ์ของ ipaddr ก็เหมือนกับรหัสนี้

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)

ตัวอย่าง:

>>> print addressInNetwork('10.9.8.7', '10.9.1.0/16')
True
>>> print addressInNetwork('10.9.8.7', '10.9.1.0/24')
False

ดีและรวดเร็ว ไม่จำเป็นต้องมีไลบรารีสำหรับการดำเนินการตรรกะง่ายๆ
Chris Koston

7

ฉันไม่ใช่แฟนของการใช้โมดูลเมื่อไม่จำเป็น งานนี้ต้องใช้คณิตศาสตร์ง่ายๆเท่านั้นดังนั้นนี่คือฟังก์ชันง่ายๆของฉันในการทำงาน:

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

จากนั้นจะใช้มัน:

>>> 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) 

เพียงเท่านี้ก็เร็วกว่าโซลูชันด้านบนพร้อมโมดูลที่ให้มา


{TypeError}'map' object is not subscriptable. คุณต้องมีo = list(o)หลังo = map(int, ip.split('.'))
gies0r

7

ใช้ Python3 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}")))

คำอธิบาย

คุณสามารถคิดว่าที่อยู่ IPเป็นเครือข่ายที่มี netmask ที่ใหญ่ที่สุดเท่าที่จะเป็นไปได้ ( /32สำหรับ IPv4/128สำหรับ IPv6)

การตรวจสอบว่า192.168.0.1อยู่ใน192.168.0.0/16นั้นเหมือนกับการตรวจสอบว่า192.168.0.1/32เป็นเครือข่ายย่อยของหรือไม่192.168.0.0/16


... ไม่แน่ใจว่าทำไมคำตอบนี้ถึงไม่อยู่บนสุด (เลย)
Filippo Vitale

6

ฉันลองใช้วิธีแก้ปัญหาของ Dave Webb แต่พบปัญหาบางอย่าง:

โดยพื้นฐานแล้ว - ควรตรวจสอบการจับคู่โดย AND ที่อยู่ IP กับมาสก์จากนั้นตรวจสอบผลลัพธ์ที่ตรงกับที่อยู่เครือข่ายทุกประการ ไม่ได้ AND ที่อยู่ IP ด้วยที่อยู่เครือข่ายเหมือนที่เคยทำ

ฉันยังสังเกตเห็นว่าการเพิกเฉยต่อพฤติกรรมของ Endian โดยสมมติว่าความสอดคล้องนั้นจะช่วยให้คุณประหยัดได้ก็ต่อเมื่อมาสก์บนขอบเขตออคเต็ต (/ 24, / 16) เพื่อให้มาสก์อื่น ๆ (/ 23, / 21) ทำงานได้อย่างถูกต้องฉันได้เพิ่มคำสั่ง "มากกว่า" ในโครงสร้างและเปลี่ยนรหัสสำหรับการสร้างรูปแบบไบนารีให้เริ่มต้นด้วย "1" ทั้งหมดและเลื่อนไปทางซ้ายด้วย (32-mask ).

สุดท้ายฉันได้เพิ่มการตรวจสอบง่ายๆว่าที่อยู่เครือข่ายนั้นถูกต้องสำหรับมาสก์และพิมพ์คำเตือนหากไม่เป็นเช่นนั้น

นี่คือผลลัพธ์:

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

ดูเหมือนว่าจะทำงานได้อย่างน่าเชื่อถือบน 64 บิต ('L' ล้มเหลวเนื่องจากค่าเป็น 32 บิต) และส่งคืนค่าตามลำดับที่เหมาะสม (ipaddr จะเป็น 0xC0A80001 สำหรับ 192.168.0.1) นอกจากนี้ยังจัดการกับ "192.168.0.1/24" เป็น netmask สำหรับ "192.168.0.1" (ไม่ใช่มาตรฐาน แต่เป็นไปได้และแก้ไขได้ง่าย)
IBBoard

ทำงานได้อย่างสมบูรณ์บน Python 2.4
xlash

5

ไม่ได้อยู่ในไลบรารีมาตรฐานสำหรับ 2.5 แต่ ipaddr ทำให้สิ่งนี้ง่ายมาก ฉันเชื่อว่ามันอยู่ใน 3.3 ภายใต้ชื่อ 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)

นี่เป็นสิ่งที่ฉันชอบที่สุดจากตัวเลือกมากมายที่นี่ (ณ เวลาที่แสดงความคิดเห็น 2017) ขอบคุณ!
rsaw

5

คำตอบที่ยอมรับใช้ไม่ได้ ... ซึ่งกำลังทำให้ฉันโกรธ Mask อยู่ข้างหลังและไม่ทำงานกับบิตใด ๆ ที่ไม่ใช่บล็อก 8 บิตธรรมดา (เช่น / 24) ฉันปรับคำตอบแล้วมันก็ใช้ได้ดี

    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

นี่คือฟังก์ชั่นที่ส่งกลับสตริงไบนารีประเพื่อช่วยเหลือเห็นภาพกำบังที่ .. ชนิดเช่นipcalcเอาท์พุท

    def bb(i):
     def s = '{:032b}'.format(i)
     def return s[0:8]+"."+s[8:16]+"."+s[16:24]+"."+s[24:32]

เช่น:

ภาพหน้าจอของหลาม


4

รหัสของ Marc เกือบถูกต้อง รหัสฉบับสมบูรณ์คือ -

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))

เห็นได้ชัดว่ามาจากแหล่งเดียวกันกับด้านบน ...

ข้อสังเกตที่สำคัญมากคือรหัสแรกมีข้อผิดพลาดเล็กน้อย - ที่อยู่ IP 255.255.255.255 ยังแสดงเป็น IP ที่ถูกต้องสำหรับซับเน็ต ฉันมีเวลาพอสมควรในการทำให้รหัสนี้ใช้งานได้และขอบคุณ Marc สำหรับคำตอบที่ถูกต้อง


พยายามและทดสอบ จากตัวอย่างซ็อกเก็ต / โครงสร้างทั้งหมดในหน้านี้นี่คืออันเดียวที่ถูกต้อง
Zabuzzman

4

การใช้โมดูล "struct" อาจทำให้เกิดปัญหากับ endian-ness และขนาดประเภทและไม่จำเป็น หรือคือ socket.inet_aton () Python ทำงานได้ดีกับที่อยู่ IP แบบจุดสี่เหลี่ยม:

def ip_to_u32(ip):
  return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)

ฉันจำเป็นต้องทำการจับคู่ IP ในแต่ละซ็อกเก็ตการโทรยอมรับ () กับเครือข่ายต้นทางที่อนุญาตทั้งหมดดังนั้นฉันจึงคำนวณมาสก์และเครือข่ายล่วงหน้าเป็นจำนวนเต็ม:

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

จากนั้นฉันสามารถดูได้อย่างรวดเร็วว่า IP ที่ระบุอยู่ในเครือข่ายใดเครือข่ายหนึ่ง:

ip = ip_to_u32(ipstr)
for mask, net in cached_masks:
  if ip & mask == net:
    # matched!
    break
else:
  raise BadClientIP(ipstr)

ไม่จำเป็นต้องมีการนำเข้าโมดูลและโค้ดนั้นเร็วมากในการจับคู่


cached_masks นี้อ้างถึงอะไร ??
Ajin

2

จาก netaddr นำเข้า 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')]

นี่คือการใช้งานสำหรับวิธีนี้:

>>> 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.

โดยทั่วไปคุณระบุที่อยู่ IP เป็นอาร์กิวเมนต์แรกและรายการของ cidrs เป็นอาร์กิวเมนต์ที่สอง รายการ Hit จะถูกส่งกลับ


2
# นี่ทำงานได้อย่างถูกต้องโดยไม่มีการจัดการไบต์ไบต์แปลก ๆ
ที่อยู่ defInNetwork (ip สุทธิ):
    '' 'เป็นที่อยู่ในเครือข่าย' ''
    # แปลงที่อยู่เป็นคำสั่งของโฮสต์ดังนั้นการเปลี่ยนแปลงจึงสมเหตุสมผล
    ip = struct.unpack ('> L', socket.inet_aton (ไอพี)) [0]
    netaddr บิต = net.split ('/')
    netaddr = struct.unpack ('> L', socket.inet_aton (netaddr)) [0]
    # ต้องเลื่อนซ้ายทุกค่า / 32 = กะศูนย์, / 0 = 32 เลื่อนไปทางซ้าย
    netmask = (0xffffffff << (32-int (บิต))) & 0xffffffff
    # ไม่จำเป็นต้องปิดบังที่อยู่เครือข่ายตราบใดที่ที่อยู่เครือข่ายที่ถูกต้อง
    ผลตอบแทน (ip & netmask) == netaddr 

รหัสทำงานไม่ถูกต้องบนระบบปฏิบัติการ 64 บิตเนื่องจากnetmaskค่าไม่ถูกต้อง ฉันมีเสรีภาพในการแก้ไขปัญหานั้น
drdaeman

2

โซลูชันก่อนหน้านี้มีจุดบกพร่องใน ip & net == net การค้นหา ip ที่ถูกต้องคือ ip & netmask = net

รหัสแก้ไขข้อบกพร่อง:

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");

2

คำตอบที่เลือกมีข้อบกพร่อง

ต่อไปนี้เป็นรหัสที่ถูกต้อง:

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

หมายเหตุ: ipaddr & netmask == netaddr & netmaskแทนipaddr & netmask == netmask.

ฉันก็แทนที่((2L<<int(bits)-1) - 1)ด้วย((1L << int(bits)) - 1)เพราะคนหลังดูเข้าใจมากขึ้น


ฉันคิดว่าการแปลงหน้ากาก((2L<<int(bits)-1) - 1)ถูกต้อง เช่นถ้าหน้ากากเป็น 16 ควรเป็น "255.255.0.0" หรือ 65535L แต่((1L << int(bits)) - 1)จะได้ 32767L ซึ่งไม่ถูกต้อง
Chris.Q

@ Chris.Q ((1L << int(bits)) - 1)ให้ 65535L ในระบบของฉันbitsตั้ง 16 !!
Debanshu Kundu

นอกจากนี้สำหรับbitsชุด0, ((2L<<int(bits)-1) - 1)คือการเพิ่มข้อผิดพลาด
Debanshu Kundu

ใช่จริงๆแล้วไม่มีค่าอื่นนอกเหนือจาก / 0, / 8, / 16, / 32 ทำงานอย่างถูกต้อง
Debanshu Kundu

2

นี่คือคลาสที่ฉันเขียนสำหรับการจับคู่คำนำหน้าที่ยาวที่สุด:

#!/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

และนี่คือโปรแกรมทดสอบ:

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

1

ขอบคุณสำหรับสคริปต์ของคุณ!
ฉันทำงานค่อนข้างนานเพื่อให้ทุกอย่างทำงานได้ ... ดังนั้นฉันจึงแบ่งปันที่นี่

  • การใช้ netaddr Class จะช้ากว่าการใช้การแปลงไบนารี 10 เท่าดังนั้นหากคุณต้องการใช้กับ IP จำนวนมากคุณควรพิจารณาไม่ใช้คลาส netaddr
  • ฟังก์ชัน makeMask ไม่ทำงาน! ทำงานเฉพาะสำหรับ / 8, / 16, / 24
    เช่น:

    บิต = "21"; socket.inet_ntoa (struct.pack ('= L', (2L << int (bits) -1) - 1))
    '255.255.31.0' ในขณะที่ควรเป็น 255.255.248.0

    ดังนั้นฉันจึงใช้ฟังก์ชันอื่น calcDottedNetmask (มาสก์) จาก http://code.activestate.com/recipes/576483-convert-subnetmask-from-cidr-notation-to-dotdecima/
    เช่น:


#!/usr/bin/python
>>> calcDottedNetmask(21)
>>> '255.255.248.0'
  • อีกปัญหาหนึ่งคือกระบวนการจับคู่หาก IP เป็นของเครือข่าย! การใช้งานพื้นฐานควรเปรียบเทียบ (ipaddr & netmask) และ (network & netmask)
    เช่นในขณะนี้ฟังก์ชันไม่ถูกต้อง

#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
>>>True which is completely WRONG!!

ดังนั้นฟังก์ชัน addressInNetwork ใหม่ของฉันจึงดูเหมือน:


#!/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))


ตอนนี้ตอบถูก !!


#!/usr/bin/python
>>> addressInNetwork('188.104.8.64','172.16.0.0/12')
False

ฉันหวังว่ามันจะช่วยคนอื่น ๆ และประหยัดเวลาสำหรับพวกเขา!


1
เวอร์ชันปัจจุบันของโค้ดด้านบนจะให้การย้อนกลับในบรรทัดสุดท้ายซึ่งคุณสามารถ "| =" int และ tuple
Sean Reifschneider


1
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')

$ หลาม check-subnet.py
เท็จ
True
เท็จ


คุณช่วยอธิบายได้ไหมว่าคุณกำลังเพิ่มอะไรในคำตอบที่ให้มาแล้ว?
David Guyon

คำตอบที่ระบุมีปัญหาบางอย่างซึ่งไม่สามารถจัดการ CIDR ได้ ฉันเพิ่งเปลี่ยนลำดับไบต์ของที่อยู่ IP เพียงเท่านี้:>>> struct.unpack('!L',socket.inet_aton('10.10.10.110'))[0] 168430190 >>> socket.inet_ntoa(struct.pack('!L', 168430190)) '10.10.10.110'
Johnson

1
ขอบคุณสำหรับคำตอบ. ฉันเดาว่าเป็นสิ่งที่คุณควรเพิ่มในคำตอบของคุณเพื่อชี้แจง อยู่ในจิตวิญญาณของ StackOverflow ที่จะอธิบายว่า "อะไร" "ทำไม" และสุดท้ายคือ "อย่างไร" คำตอบของคุณมีเพียง "how" :( ฉันให้คุณตอบคำตอบของคุณโดยแก้ไข;)
David Guyon


0

จากแหล่งข้อมูลต่างๆข้างต้นและจากการค้นคว้าของฉันเองนี่คือวิธีที่ฉันทำให้ซับเน็ตและการคำนวณที่อยู่ทำงานได้ ชิ้นส่วนเหล่านี้เพียงพอที่จะไขข้อข้องใจและคำถามอื่น ๆ ที่เกี่ยวข้อง

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)


0

นี่คือรหัสของฉัน

# -*- 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')

0

หากคุณไม่ต้องการนำเข้าโมดูลอื่น ๆ คุณสามารถใช้:

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]

0

ฉันลองใช้ชุดย่อยของโซลูชันที่เสนอในคำตอบเหล่านี้ .. แต่ไม่ประสบความสำเร็จในที่สุดฉันก็ปรับและแก้ไขโค้ดที่เสนอและเขียนฟังก์ชันคงที่ของฉัน

ฉันทดสอบและใช้งานได้อย่างน้อยกับสถาปัตยกรรม endian เล็ก ๆ - เช่น x86 - ถ้าใครชอบลองสถาปัตยกรรม endian ขนาดใหญ่โปรดให้ข้อเสนอแนะ

IP2Intรหัสมาจากโพสต์นี้ส่วนวิธีอื่นคือการแก้ไขข้อเสนอก่อนหน้านี้ในคำถามนี้อย่างสมบูรณ์ (สำหรับกรณีทดสอบของฉัน)

รหัส:

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

หวังว่าจะมีประโยชน์


0

นี่คือวิธีแก้ปัญหาโดยใช้แพ็คเกจ netaddr

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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.