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:
@@ -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))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user