# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2016 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# A consolidated manage of neighbor table information management.
# Ultimately, this should use AF_NETLINK, but in the interest of time,
# use ip neigh for the moment

import confluent.util as util
import os
import eventlet.semaphore as semaphore
import eventlet.green.socket as socket
import struct

neightable = {}
neightime = 0

import re

neighlock = semaphore.Semaphore()

def _update_neigh():
    global neightable
    global neightime
    neightime = os.times()[4]
    s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
    s.bind((0, 0))
    # RTM_GETNEIGH
    nlhdr = b'\x1c\x00\x00\x00\x1e\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00'
    ndmsg=  b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    s.sendall(nlhdr + ndmsg)
    neightable = {}
    while True:
        pdata = s.recv(65536)
        v = memoryview(pdata)
        if struct.unpack('H', v[4:6])[0] == 3:
            break
        while len(v):
            length, typ = struct.unpack('IH', v[:6])
            if typ == 28:
                hlen = struct.calcsize('BIHBB')
                _, idx, state, flags, typ = struct.unpack('BIHBB', v[16:16+hlen])
                if typ == 1:  # only handle unicast entries
                    curraddr = None
                    currip = None
                    rta = v[16+hlen:length]
                    while len(rta):
                        rtalen, rtatyp = struct.unpack('HH', rta[:4])
                        if rtatyp == 2:  # hwaddr
                            curraddr = rta[4:rtalen].tobytes()
                            if len(curraddr) == 20:
                                curraddr = curraddr[12:]
                        elif rtatyp == 1:  # ip address
                            currip = rta[4:rtalen].tobytes()
                        rta = rta[rtalen:]
                        if not rtalen:
                            break
                    if curraddr and currip:
                        neightable[currip] = curraddr
            v = v[length:]


def get_hwaddr(ipaddr):
    hwaddr = None
    if os.name == 'nt':
        return hwaddr
    if ':' in ipaddr:
        ipaddr = socket.inet_pton(socket.AF_INET6, ipaddr)
    elif '.' in ipaddr:
        ipaddr = socket.inet_pton(socket.AF_INET, ipaddr)
    with neighlock:
        updated = False
        if os.times()[4] > (neightime + 30):
            _update_neigh()
            updated = True
        hwaddr = neightable.get(ipaddr, None)
        if not hwaddr and not updated:
            _update_neigh()
            hwaddr = neightable.get(ipaddr, None)
    if hwaddr:
        hwaddr = ':'.join(['{:02x}'.format(x) for x in bytearray(hwaddr)])
    return hwaddr


if __name__ == '__main__':
    import sys
    print(repr(get_hwaddr(sys.argv[1])))
@@ -678,109 +678,8 @@
 ent.
-%0A# Ultimately, this should use AF_NETLINK, but in the interest of time,%0A# use ip neigh for the moment
 %0A%0Aim
@@ -1291,16 +1291,29 @@
 le = %7B%7D%0A
+    try:%0A    
     whil
@@ -1320,16 +1320,20 @@
 e True:%0A
+    
         
@@ -1358,24 +1358,28 @@
 36)%0A        
+  
+  
 v = memoryvi
@@ -1388,16 +1388,20 @@
 (pdata)%0A
+    
         
@@ -1439,16 +1439,20 @@
 %5D == 3:%0A
+    
         
@@ -1461,32 +1461,36 @@
   break%0A        
+    
 while len(v):%0A  
@@ -1487,16 +1487,20 @@
 len(v):%0A
+    
         
@@ -1548,32 +1548,36 @@
 6%5D)%0A            
+ 
+   
 if typ == 28:%0A  
@@ -1574,16 +1574,20 @@
  == 28:%0A
+    
         
@@ -1642,16 +1642,20 @@
         
+   
+ 
 _, idx, 
@@ -1720,32 +1720,36 @@
                 
+    
 if typ == 1:  # 
@@ -1776,16 +1776,20 @@
 entries%0A
+    
         
@@ -1828,32 +1828,36 @@
                 
+    
 currip = None%0A  
@@ -1846,32 +1846,36 @@
   currip = None%0A
+    
                 
@@ -1914,32 +1914,36 @@
                 
+  
+  
 while len(rta):%0A
@@ -1942,16 +1942,20 @@
 n(rta):%0A
+    
         
@@ -2040,16 +2040,20 @@
         
+   
+ 
 if rtaty
@@ -2066,24 +2066,28 @@
 :  # hwaddr%0A
+    
             
@@ -2165,16 +2165,20 @@
         
+    
 if len(c
@@ -2217,32 +2217,36 @@
                 
+ 
+   
 curraddr = curra
@@ -2254,16 +2254,20 @@
 dr%5B12:%5D%0A
+    
         
@@ -2334,32 +2334,36 @@
                 
+  
+  
 currip = rta%5B4:r
@@ -2399,24 +2399,28 @@
             
+    
 rta = rta%5Brt
@@ -2426,16 +2426,20 @@
 talen:%5D%0A
+    
         
@@ -2489,38 +2489,46 @@
                 
+    
 break%0A
+    
                 
@@ -2575,24 +2575,28 @@
             
+ 
+   
 neightable%5Bc
@@ -2621,24 +2621,28 @@
             
+    
 v = v%5Blength
@@ -2644,16 +2644,47 @@
 ength:%5D%0A
+    finally:%0A        s.close()%0A
 %0A%0Adef ge

Make sure the netlink socket closes on the way out