2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-29 00:31:09 +00:00

Refactor neigh table handling and expand SSDP

SSDP support snoop is implemented.
This commit is contained in:
Jarrod Johnson
2016-11-15 15:57:55 -05:00
parent 786bf00521
commit 3edba0f8bb
4 changed files with 132 additions and 61 deletions
@@ -0,0 +1,51 @@
# 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 eventlet.green.subprocess as subprocess
import os
neightable = {}
neightime = 0
def update_neigh():
global neightable
global neightime
neightable = {}
if os.name == 'nt':
return
ipn = subprocess.Popen(['ip', 'neigh'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(neighdata, err) = ipn.communicate()
for entry in neighdata.split('\n'):
entry = entry.split(' ')
if len(entry) < 5 or not entry[4]:
continue
neightable[entry[0]] = entry[4]
neightime = os.times()[4]
def refresh_neigh():
global neightime
if os.name == 'nt':
return
if os.times()[4] > (neightime + 30):
update_neigh()
@@ -74,6 +74,8 @@ def find_info_in_options(rq, optidx):
def snoop(handler):
#TODO(jjohnson2): ipv6 socket and multicast for DHCPv6, should that be
#prominent
#TODO(jjohnson2): IP_PKTINFO, recvmsg to get the destination ip, per
#proxydhcp.c from xCAT
net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
net4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
net4.bind(('', 67))
+16 -50
View File
@@ -14,13 +14,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import confluent.neighutil as neighutil
import confluent.util as util
import os
import random
import eventlet.green.select as select
import eventlet.green.socket as socket
import struct
import subprocess
import confluent.util as util
# SLP has a lot of ambition that was unfulfilled in practice.
@@ -32,42 +33,6 @@ srvreqfooter = b'\x00\x07DEFAULT\x00\x00\x00\x00'
attrlistext = b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00'
neightable = {}
mactable = {}
neightime = 0
def update_neigh():
global neightable
neightable = {}
mactable = {}
if os.name == 'nt':
return
ipn = subprocess.Popen(['ip', 'neigh'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(neighdata, err) = ipn.communicate()
for entry in neighdata.split('\n'):
entry = entry.split(' ')
if len(entry) < 5 or not entry[4]:
continue
neightable[entry[0]] = entry[4]
if entry[4] in mactable:
mactable[entry[4]].append(entry[0])
else:
mactable[entry[4]] = [entry[0]]
neightime = os.times()[4]
def _refresh_neigh():
global neightime
if os.name == 'nt':
return
if os.times()[4] > (neightime + 30):
update_neigh()
def _parse_slp_header(packet):
packet = bytearray(packet)
if len(packet) < 16 or packet[0] != 2:
@@ -122,8 +87,8 @@ def _parse_slp_packet(packet, peer, rsps, xidmap):
if '%' in addr:
addr = addr[:addr.index('%')]
mac = None
if addr in neightable:
identifier = neightable[addr]
if addr in neighutil.neightable:
identifier = neighutil.neightable[addr]
mac = identifier
else:
identifier = addr
@@ -251,7 +216,7 @@ def _grab_rsps(socks, rsps, interval, xidmap):
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
_refresh_neigh()
neighutil.refresh_neigh()
_parse_slp_packet(rsp, peer, rsps, xidmap)
r, _, _ = select.select(socks, (), (), interval)
@@ -379,8 +344,7 @@ def query_srvtypes(target):
def snoop(handler):
"""Watch for SLP activity
handler will be called with mac address, a list of sockaddrs, and
a list of relevant service types as the three arguments
handler will be called with a dictionary of relevant attributes
:param handler:
:return:
@@ -407,20 +371,18 @@ def snoop(handler):
while True:
newmacs = set([])
r, _, _ = select.select((net, net4), (), (), 60)
update_neigh()
neighutil.update_neigh()
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
ip = peer[0].partition('%')[0]
if ip not in neightable:
if ip not in neighutil.neightable:
continue
if peer in known_peers:
continue
known_peers.add(peer)
mac = neightable[ip]
mac = neighutil.neightable[ip]
if mac in peerbymacaddress:
newmacs.add(mac)
peerbymacaddress[mac]['peers'].append(peer)
else:
q = query_srvtypes(peer)
@@ -429,15 +391,19 @@ def snoop(handler):
# ignore for now
known_peers.discard(peer)
continue
newmacs.add(mac)
peerbymacaddress[mac] = {
'services': q,
'peers': [peer],
}
newmacs.add(mac)
r, _, _ = select.select((net, net4), (), (), 0.1)
for mac in newmacs:
handler(mac, peerbymacaddress[mac]['peers'],
peerbymacaddress[mac]['services'])
peerinfo = {
'hwaddr': mac,
'peers': peerbymacaddress[mac]['peers'],
'services': peerbymacaddress[mac]['services'],
}
handler(peerinfo)
def find_targets(srvtypes, addresses=None):
+63 -11
View File
@@ -28,6 +28,7 @@
# NTS: ssdp:alive
import confluent.neighutil as neighutil
import confluent.util as util
import eventlet.green.select as select
import eventlet.green.socket as socket
@@ -66,6 +67,7 @@ def snoop(handler, byehandler=None):
# Normally, I like using v6/v4 agnostic socket. However, since we are
# dabbling in multicast wizardry here, such sockets can cause big problems,
# so we will have two distinct sockets
known_peers = set([])
net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
for ifidx in util.list_interface_indexes():
@@ -77,16 +79,52 @@ def snoop(handler, byehandler=None):
net4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
net4.bind(('', 1900))
net6.bind(('', 1900))
peerbymacaddress = {}
while True:
newmacs = set([])
machandlers = {}
r, _, _ = select.select((net4, net6), (), (), 60)
neighutil.update_neigh()
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
ip = peer[0].partition('%')[0]
rsp = rsp.split('\r\n')
print(repr(ip))
print(repr(rsp))
method, _, _ = rsp[0].split(' ', 2)
if method == 'NOTIFY':
ip = peer[0].partition('%')[0]
if ip not in neighutil.neightable:
continue
if peer in known_peers:
continue
mac = neighutil.neightable[ip]
known_peers.add(peer)
newmacs.add(mac)
if mac in peerbymacaddress:
peerbymacaddress[mac]['peers'].append(peer)
else:
peerbymacaddress[mac] = {
'hwaddr': mac,
'peers': [peer],
}
peerdata = peerbymacaddress[mac]
for headline in rsp[1:]:
if not headline:
continue
header, _, value = headline.partition(':')
header = header.strip()
value = value.strip()
if header == 'NT':
peerdata['service'] = value
elif header == 'NTS':
if value == 'ssdp:byebye':
machandlers[mac] = byehandler
elif value == 'ssdp:alive':
machandlers[mac] = handler
r, _, _ = select.select((net4, net6), (), (), 0.1)
for mac in newmacs:
thehandler = machandlers[mac]
if thehandler:
thehandler(peerbymacaddress[mac])
def _find_service(service, target):
@@ -127,14 +165,28 @@ def _find_service(service, target):
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
print(repr(rsp))
print(repr(peer))
headlines = rsp.split('\r\n')
_, code, _ = headlines[0].split(' ', 2)
if code == '200':
peerdata = {
'peers': [peer],
}
for headline in headlines[1:]:
if not headline:
continue
header, _, value = headline.partition(':')
header = header.strip()
value = value.strip()
if header == 'AL' or header == 'LOCATION':
peerdata['url'] = value
print(repr(peerdata))
r, _, _ = select.select((net4, net6), (), (), 1)
if __name__ == '__main__':
# find_targets(['urn:dmtf-org:service:redfish-rest:1'])
def fun(a, b, c):
print(repr(a))
print(repr(b))
print(repr(c))
snoop(fun)
find_targets(['urn:dmtf-org:service:redfish-rest:1'])
def fun(a):
print(repr(a))
def byefun(a):
print('bye' + repr(a))
snoop(fun, byefun)