mirror of
https://github.com/JoelBender/bacpypes
synced 2025-10-27 00:57:47 +08:00
bring the branch up-to-date and start adding network discovery tests
This commit is contained in:
@@ -2,6 +2,14 @@
|
||||
|
||||
"""
|
||||
Analysis - Decoding pcap files
|
||||
|
||||
Before analyzing files, install libpcap-dev:
|
||||
|
||||
$ sudo apt install libpcap-dev
|
||||
|
||||
then install pypcap:
|
||||
|
||||
https://github.com/pynetwork/pypcap
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -15,7 +23,7 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
from .debugging import ModuleLogger, DebugContents, bacpypes_debugging
|
||||
from .debugging import ModuleLogger, DebugContents, bacpypes_debugging, btox
|
||||
|
||||
from .pdu import PDU, Address
|
||||
from .bvll import BVLPDU, bvl_pdu_types, ForwardedNPDU, \
|
||||
@@ -33,13 +41,6 @@ _protocols={socket.IPPROTO_TCP:'tcp',
|
||||
socket.IPPROTO_UDP:'udp',
|
||||
socket.IPPROTO_ICMP:'icmp'}
|
||||
|
||||
#
|
||||
# _hexify
|
||||
#
|
||||
|
||||
def _hexify(s, sep='.'):
|
||||
return sep.join('%02X' % ord(c) for c in s)
|
||||
|
||||
#
|
||||
# strftimestamp
|
||||
#
|
||||
@@ -54,11 +55,11 @@ def strftimestamp(ts):
|
||||
|
||||
@bacpypes_debugging
|
||||
def decode_ethernet(s):
|
||||
if _debug: decode_ethernet._debug("decode_ethernet %s...", _hexify(s[:14]))
|
||||
if _debug: decode_ethernet._debug("decode_ethernet %s...", btox(s[:14]))
|
||||
|
||||
d={}
|
||||
d['destination_address'] = _hexify(s[0:6], ':')
|
||||
d['source_address'] = _hexify(s[6:12], ':')
|
||||
d['destination_address'] = btox(s[0:6], ':')
|
||||
d['source_address'] = btox(s[6:12], ':')
|
||||
d['type'] = struct.unpack('!H',s[12:14])[0]
|
||||
d['data'] = s[14:]
|
||||
|
||||
@@ -70,7 +71,7 @@ def decode_ethernet(s):
|
||||
|
||||
@bacpypes_debugging
|
||||
def decode_vlan(s):
|
||||
if _debug: decode_vlan._debug("decode_vlan %s...", _hexify(s[:4]))
|
||||
if _debug: decode_vlan._debug("decode_vlan %s...", btox(s[:4]))
|
||||
|
||||
d = {}
|
||||
x = struct.unpack('!H',s[0:2])[0]
|
||||
@@ -88,7 +89,7 @@ def decode_vlan(s):
|
||||
|
||||
@bacpypes_debugging
|
||||
def decode_ip(s):
|
||||
if _debug: decode_ip._debug("decode_ip %r", _hexify(s[:20]))
|
||||
if _debug: decode_ip._debug("decode_ip %r", btox(s[:20]))
|
||||
|
||||
d = {}
|
||||
d['version'] = (ord(s[0]) & 0xf0) >> 4
|
||||
@@ -117,7 +118,7 @@ def decode_ip(s):
|
||||
|
||||
@bacpypes_debugging
|
||||
def decode_udp(s):
|
||||
if _debug: decode_udp._debug("decode_udp %s...", _hexify(s[:8]))
|
||||
if _debug: decode_udp._debug("decode_udp %s...", btox(s[:8]))
|
||||
|
||||
d = {}
|
||||
d['source_port'] = struct.unpack('!H',s[0:2])[0]
|
||||
@@ -222,7 +223,7 @@ def decode_packet(data):
|
||||
|
||||
# check for version number
|
||||
if (pdu.pduData[0] != '\x01'):
|
||||
if _debug: decode_packet._debug(" - not a version 1 packet: %s...", _hexify(pdu.pduData[:30]))
|
||||
if _debug: decode_packet._debug(" - not a version 1 packet: %s...", btox(pdu.pduData[:30]))
|
||||
return None
|
||||
|
||||
# it's an NPDU
|
||||
@@ -355,30 +356,19 @@ def decode_file(fname):
|
||||
raise RuntimeError("failed to import pcap")
|
||||
|
||||
# create a pcap object
|
||||
p = pcap.pcapObject()
|
||||
p.open_offline(fname)
|
||||
p = pcap.pcap(fname)
|
||||
|
||||
i = 0
|
||||
while 1:
|
||||
# the object acts like an iterator
|
||||
pkt = p.next()
|
||||
if not pkt:
|
||||
break
|
||||
|
||||
# returns a tuple
|
||||
pktlen, data, timestamp = pkt
|
||||
for timestamp, data in p:
|
||||
pkt = decode_packet(data)
|
||||
if not pkt:
|
||||
continue
|
||||
|
||||
# save the index and timestamp in the packet
|
||||
pkt._index = i
|
||||
# pkt._index = i
|
||||
pkt._timestamp = timestamp
|
||||
|
||||
yield pkt
|
||||
|
||||
i += 1
|
||||
|
||||
#
|
||||
# Tracer
|
||||
#
|
||||
|
||||
@@ -349,6 +349,7 @@ class APCI(PCI, DebugContents):
|
||||
# APDU
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class APDU(APCI, PDUData):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -356,12 +357,13 @@ class APDU(APCI, PDUData):
|
||||
super(APDU, self).__init__(*args, **kwargs)
|
||||
|
||||
def encode(self, pdu):
|
||||
if _debug: APCI._debug("encode %s", str(pdu))
|
||||
if _debug: APDU._debug("encode %s", str(pdu))
|
||||
APCI.encode(self, pdu)
|
||||
pdu.put_data(self.pduData)
|
||||
|
||||
def decode(self, pdu):
|
||||
if _debug: APCI._debug("decode %s", str(pdu))
|
||||
if _debug: APDU._debug("decode %s", str(pdu))
|
||||
|
||||
APCI.decode(self, pdu)
|
||||
self.pduData = pdu.get_data(len(pdu.pduData))
|
||||
|
||||
@@ -393,17 +395,24 @@ class APDU(APCI, PDUData):
|
||||
# between PDU's. Otherwise the APCI content would be decoded twice.
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class _APDU(APDU):
|
||||
|
||||
def encode(self, pdu):
|
||||
if _debug: _APDU._debug("encode %r", pdu)
|
||||
|
||||
APCI.update(pdu, self)
|
||||
pdu.put_data(self.pduData)
|
||||
|
||||
def decode(self, pdu):
|
||||
if _debug: _APDU._debug("decode %r", pdu)
|
||||
|
||||
APCI.update(self, pdu)
|
||||
self.pduData = pdu.get_data(len(pdu.pduData))
|
||||
|
||||
def set_context(self, context):
|
||||
if _debug: _APDU._debug("set_context %r", context)
|
||||
|
||||
self.pduUserData = context.pduUserData
|
||||
self.pduDestination = context.pduSource
|
||||
self.pduExpectingReply = 0
|
||||
|
||||
@@ -107,8 +107,12 @@ class ObjectTypesSupported(BitString):
|
||||
, 'positiveIntegerValue':48
|
||||
, 'timePatternValue':49
|
||||
, 'timeValue':50
|
||||
, 'notificationForwarder':51
|
||||
, 'alertEnrollment':52
|
||||
, 'channel':53
|
||||
, 'lightingOutput':54
|
||||
}
|
||||
bitLen = 51
|
||||
bitLen = 55
|
||||
|
||||
class ResultFlags(BitString):
|
||||
bitNames = \
|
||||
|
||||
@@ -52,10 +52,10 @@ def stop(*args):
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
def dump_stack():
|
||||
if _debug: dump_stack._debug("dump_stack")
|
||||
def dump_stack(debug_handler):
|
||||
if _debug: dump_stack._debug("dump_stack %r", debug_handler)
|
||||
for filename, lineno, fn, _ in traceback.extract_stack()[:-1]:
|
||||
sys.stderr.write(" %-20s %s:%s\n" % (fn, filename.split('/')[-1], lineno))
|
||||
debug_handler(" %-20s %s:%s", fn, filename.split('/')[-1], lineno)
|
||||
|
||||
#
|
||||
# print_stack
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Network Service
|
||||
"""
|
||||
|
||||
from copy import copy as _copy
|
||||
from copy import deepcopy as _deepcopy
|
||||
|
||||
from .debugging import ModuleLogger, DebugContents, bacpypes_debugging
|
||||
from .errors import ConfigurationError
|
||||
@@ -467,9 +467,11 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - application layer message")
|
||||
|
||||
if processLocally and self.serverPeer:
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - processing APDU locally")
|
||||
|
||||
# decode as a generic APDU
|
||||
apdu = _APDU(user_data=npdu.pduUserData)
|
||||
apdu.decode(_copy(npdu))
|
||||
apdu.decode(_deepcopy(npdu))
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - apdu: %r", apdu)
|
||||
|
||||
# see if it needs to look routed
|
||||
@@ -507,6 +509,7 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
|
||||
# pass upstream to the application layer
|
||||
self.response(apdu)
|
||||
|
||||
else:
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - network layer message")
|
||||
|
||||
@@ -515,9 +518,11 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - unknown npdu type: %r", npdu.npduNetMessage)
|
||||
return
|
||||
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - processing NPDU locally")
|
||||
|
||||
# do a deeper decode of the NPDU
|
||||
xpdu = npdu_types[npdu.npduNetMessage](user_data=npdu.pduUserData)
|
||||
xpdu.decode(_copy(npdu))
|
||||
xpdu.decode(_deepcopy(npdu))
|
||||
|
||||
# pass to the service element
|
||||
self.sap_request(adapter, xpdu)
|
||||
@@ -538,7 +543,7 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
return
|
||||
|
||||
# build a new NPDU to send to other adapters
|
||||
newpdu = _copy(npdu)
|
||||
newpdu = _deepcopy(npdu)
|
||||
|
||||
# clear out the source and destination
|
||||
newpdu.pduSource = None
|
||||
@@ -560,8 +565,7 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
|
||||
for xadapter in self.adapters.values():
|
||||
if (xadapter is not adapter):
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - global broadcasting to: %r", xadapter)
|
||||
xadapter.process_npdu(newpdu)
|
||||
xadapter.process_npdu(_deepcopy(newpdu))
|
||||
return
|
||||
|
||||
if (npdu.npduDADR.addrType == Address.remoteBroadcastAddr) \
|
||||
@@ -587,10 +591,10 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
newpdu.npduDADR = None
|
||||
|
||||
# send the packet downstream
|
||||
xadapter.process_npdu(newpdu)
|
||||
xadapter.process_npdu(_deepcopy(newpdu))
|
||||
return
|
||||
|
||||
# see if there is routing information for this source network
|
||||
# see if there is routing information for this destination network
|
||||
router_info = self.router_info_cache.get_router_info(dnet)
|
||||
if router_info:
|
||||
router_net, router_address, router_status = router_info
|
||||
@@ -610,7 +614,7 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
newpdu.pduDestination = router_address
|
||||
|
||||
# send the packet downstream
|
||||
xadapter.process_npdu(newpdu)
|
||||
xadapter.process_npdu(_deepcopy(newpdu))
|
||||
return
|
||||
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - no router info found")
|
||||
@@ -629,6 +633,7 @@ class NetworkServiceAccessPoint(ServiceAccessPoint, Server, DebugContents):
|
||||
|
||||
# pass this along as if it came from the NSE
|
||||
self.sap_indication(xadapter, xnpdu)
|
||||
|
||||
return
|
||||
|
||||
if _debug: NetworkServiceAccessPoint._debug(" - bad DADR: %r", npdu.npduDADR)
|
||||
|
||||
@@ -1524,6 +1524,7 @@ class ObjectType(Enumerated):
|
||||
, 'accessUser':35
|
||||
, 'accessZone':36
|
||||
, 'accumulator':23
|
||||
, 'alertEnrollment':52
|
||||
, 'analogInput':0
|
||||
, 'analogOutput':1
|
||||
, 'analogValue':2
|
||||
@@ -1533,6 +1534,7 @@ class ObjectType(Enumerated):
|
||||
, 'binaryValue':5
|
||||
, 'bitstringValue':39
|
||||
, 'calendar':6
|
||||
, 'channel':53
|
||||
, 'characterstringValue':40
|
||||
, 'command':7
|
||||
, 'credentialDataInput':37
|
||||
@@ -1550,6 +1552,7 @@ class ObjectType(Enumerated):
|
||||
, 'largeAnalogValue':46
|
||||
, 'lifeSafetyPoint':21
|
||||
, 'lifeSafetyZone':22
|
||||
, 'lightingOutput':54
|
||||
, 'loadControl':28
|
||||
, 'loop':12
|
||||
, 'multiStateInput':13
|
||||
@@ -1557,6 +1560,7 @@ class ObjectType(Enumerated):
|
||||
, 'multiStateValue':19
|
||||
, 'networkSecurity':38
|
||||
, 'notificationClass':15
|
||||
, 'notificationForwarder':51
|
||||
, 'octetstringValue':47
|
||||
, 'positiveIntegerValue':48
|
||||
, 'program':16
|
||||
|
||||
Reference in New Issue
Block a user