From 3ee0cbd0dd24806871d6c1da18fdcd0d707cde24 Mon Sep 17 00:00:00 2001 From: Joel Bender Date: Thu, 3 May 2018 22:26:50 -0400 Subject: [PATCH] merge #181 with additional tests --- py25/bacpypes/bvllservice.py | 122 ++++++------ py25/bacpypes/vlan.py | 9 +- py27/bacpypes/bvllservice.py | 127 +++++++------ py27/bacpypes/vlan.py | 9 +- py34/bacpypes/bvllservice.py | 122 ++++++------ py34/bacpypes/vlan.py | 9 +- samples/NATRouter.py | 139 ++++++++++++++ tests/test_bvll/helpers.py | 294 ++++++++++++++++++++++++++--- tests/test_bvll/test_bbmd.py | 303 +++++++++++++++++++++++++++++- tests/test_bvll/test_foreign.py | 181 +++++++++--------- tests/test_bvll/test_simple.py | 27 ++- tests/test_network/helpers.py | 32 ++-- tests/test_network/test_net_1.py | 10 +- tests/test_network/test_net_2.py | 10 +- tests/test_network/test_net_3.py | 8 +- tests/test_vlan/test_ipnetwork.py | 24 +++ 16 files changed, 1094 insertions(+), 332 deletions(-) create mode 100644 samples/NATRouter.py diff --git a/py25/bacpypes/bvllservice.py b/py25/bacpypes/bvllservice.py index 65ae614..ecfb501 100755 --- a/py25/bacpypes/bvllservice.py +++ b/py25/bacpypes/bvllservice.py @@ -546,19 +546,31 @@ class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): return - elif isinstance(pdu, OriginalUnicastNPDU): + # check the BBMD registration status, we may not be registered + if self.registrationStatus != 0: + if _debug: BIPForeign._debug(" - packet dropped, unregistered") + return + + if isinstance(pdu, OriginalUnicastNPDU): # build a vanilla PDU xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) # send it upstream self.response(xpdu) - # check the BBMD registration status, we may not be registered - if self.registrationStatus != 0: - if _debug: BIPForeign._debug(" - packet dropped, unregistered") - return + elif isinstance(pdu, ForwardedNPDU): + # make sure the forwarded PDU from the bbmd + if pdu.pduSource != self.bbmdAddress: + if _debug: BIPForeign._debug(" - packet dropped, not from the BBMD") + return - if isinstance(pdu, ReadBroadcastDistributionTableAck): + # build a PDU with the source from the real source + xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) + + # send it upstream + self.response(xpdu) + + elif isinstance(pdu, ReadBroadcastDistributionTableAck): # send this to the service access point self.sap_response(pdu) @@ -566,13 +578,6 @@ class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): # send this to the service access point self.sap_response(pdu) - elif isinstance(pdu, ForwardedNPDU): - # build a PDU with the source from the real source - xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) - - # send it upstream - self.response(xpdu) - elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response xpdu = Result(code=0x0010, user_data=pdu.pduUserData) @@ -696,7 +701,7 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): # make an original unicast PDU xpdu = OriginalUnicastNPDU(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduDestination - if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) + if _debug: BIPBBMD._debug(" - original unicast xpdu: %r", xpdu) # send it downstream self.request(xpdu) @@ -719,13 +724,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: @@ -741,8 +746,9 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response - xpdu = Result(code=99, user_data=pdu.pduUserData) + xpdu = Result(code=0x0010, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduSource + if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) @@ -761,27 +767,38 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): self.sap_response(pdu) elif isinstance(pdu, ForwardedNPDU): - # build a PDU with the source from the real source - xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.bvlciAddress, pdu, destination=None, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) - # look for self as first entry in the BDT - if self.bbmdBDT and (self.bbmdBDT[0] == self.bbmdAddress): - xpdu.pduDestination = LocalBroadcast() - if _debug: BIPBBMD._debug(" - local broadcast") - self.request(xpdu) + # if this was unicast to us, do next hop + if pdu.pduDestination.addrType == Address.localStationAddr: + if _debug: BIPBBMD._debug(" - unicast message") + + # if this BBMD is listed in its BDT, send a local broadcast + if self.bbmdAddress in self.bbmdBDT: + xpdu.pduDestination = LocalBroadcast() + if _debug: BIPBBMD._debug(" - local broadcast") + self.request(xpdu) + + elif pdu.pduDestination.addrType == Address.localBroadcastAddr: + if _debug: BIPBBMD._debug(" - directed broadcast message") + + else: + BIPBBMD._warning("invalid destination address: %r", pdu.pduDestination) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, RegisterForeignDevice): @@ -820,12 +837,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): self.request(xpdu) elif isinstance(pdu, DistributeBroadcastToNetwork): - # build a PDU with a local broadcast address - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) @@ -835,35 +853,37 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte == self.bbmdAddress: xpdu.pduDestination = LocalBroadcast() - if _debug: BIPBBMD._debug(" - local broadcast") + if _debug: BIPBBMD._debug(" - local broadcast") self.request(xpdu) else: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the other registered foreign devices for fdte in self.bbmdFDT: if fdte.fdAddress != pdu.pduSource: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, OriginalUnicastNPDU): - # build a vanilla PDU - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) elif isinstance(pdu, OriginalBroadcastNPDU): - # build a PDU with a local broadcast address - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # make a forwarded PDU xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) @@ -873,13 +893,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: @@ -929,7 +949,7 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): del self.bbmdFDT[i] break else: - stat = 99 ### entry not found + stat = 0x0050 ### entry not found # return status return stat @@ -956,10 +976,6 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): else: raise TypeError("addr must be a string or an Address") - # if it's this BBMD, make it the first one - if self.bbmdBDT and (addr == self.bbmdAddress): - raise RuntimeError("add self to BDT as first address") - # see if it's already there for bdte in self.bbmdBDT: if addr == bdte: diff --git a/py25/bacpypes/vlan.py b/py25/bacpypes/vlan.py index 4d71696..e8e9fc2 100755 --- a/py25/bacpypes/vlan.py +++ b/py25/bacpypes/vlan.py @@ -161,9 +161,9 @@ class IPNetwork(Network): ('1.2.3.255', 5) and the other nodes must have the same tuple. """ - def __init__(self): + def __init__(self, name=''): if _debug: IPNetwork._debug("__init__") - Network.__init__(self) + Network.__init__(self, name=name) def add_node(self, node): if _debug: IPNetwork._debug("add_node %r", node) @@ -213,7 +213,7 @@ bacpypes_debugging(IPNode) class IPRouterNode(Client): - def __init__(self, router, addr, lan=None): + def __init__(self, router, addr, lan): if _debug: IPRouterNode._debug("__init__ %r %r lan=%r", router, addr, lan) # save the reference to the router @@ -238,6 +238,9 @@ class IPRouterNode(Client): # pass it downstream self.request(pdu) + def __repr__(self): + return "<%s for %s>" % (self.__class__.__name__, self.lan.name) + bacpypes_debugging(IPRouterNode) # diff --git a/py27/bacpypes/bvllservice.py b/py27/bacpypes/bvllservice.py index 4c2cc18..f3d4276 100755 --- a/py27/bacpypes/bvllservice.py +++ b/py27/bacpypes/bvllservice.py @@ -542,20 +542,31 @@ class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): return - elif isinstance(pdu, OriginalUnicastNPDU): - # build a vanilla PDU - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) - - # send it upstream - self.response(xpdu) - return - # check the BBMD registration status, we may not be registered if self.registrationStatus != 0: if _debug: BIPForeign._debug(" - packet dropped, unregistered") return - if isinstance(pdu, ReadBroadcastDistributionTableAck): + if isinstance(pdu, OriginalUnicastNPDU): + # build a vanilla PDU + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) + + # send it upstream + self.response(xpdu) + + elif isinstance(pdu, ForwardedNPDU): + # make sure the forwarded PDU from the bbmd + if pdu.pduSource != self.bbmdAddress: + if _debug: BIPForeign._debug(" - packet dropped, not from the BBMD") + return + + # build a PDU with the source from the real source + xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) + + # send it upstream + self.response(xpdu) + + elif isinstance(pdu, ReadBroadcastDistributionTableAck): # send this to the service access point self.sap_response(pdu) @@ -563,13 +574,6 @@ class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): # send this to the service access point self.sap_response(pdu) - elif isinstance(pdu, ForwardedNPDU): - # build a PDU with the source from the real source - xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) - - # send it upstream - self.response(xpdu) - elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response xpdu = Result(code=0x0010, user_data=pdu.pduUserData) @@ -692,7 +696,7 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): # make an original unicast PDU xpdu = OriginalUnicastNPDU(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduDestination - if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) + if _debug: BIPBBMD._debug(" - original unicast xpdu: %r", xpdu) # send it downstream self.request(xpdu) @@ -715,13 +719,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: @@ -737,8 +741,9 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response - xpdu = Result(code=99, user_data=pdu.pduUserData) + xpdu = Result(code=0x0010, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduSource + if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) @@ -757,27 +762,38 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): self.sap_response(pdu) elif isinstance(pdu, ForwardedNPDU): - # build a PDU with the source from the real source - xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.bvlciAddress, pdu, destination=None, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) - # look for self as first entry in the BDT - if self.bbmdBDT and (self.bbmdBDT[0] == self.bbmdAddress): - xpdu.pduDestination = LocalBroadcast() - if _debug: BIPBBMD._debug(" - local broadcast") - self.request(xpdu) + # if this was unicast to us, do next hop + if pdu.pduDestination.addrType == Address.localStationAddr: + if _debug: BIPBBMD._debug(" - unicast message") + + # if this BBMD is listed in its BDT, send a local broadcast + if self.bbmdAddress in self.bbmdBDT: + xpdu.pduDestination = LocalBroadcast() + if _debug: BIPBBMD._debug(" - local broadcast") + self.request(xpdu) + + elif pdu.pduDestination.addrType == Address.localBroadcastAddr: + if _debug: BIPBBMD._debug(" - directed broadcast message") + + else: + BIPBBMD._warning("invalid destination address: %r", pdu.pduDestination) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, RegisterForeignDevice): @@ -816,12 +832,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): self.request(xpdu) elif isinstance(pdu, DistributeBroadcastToNetwork): - # build a PDU with a local broadcast address - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) @@ -831,35 +848,37 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte == self.bbmdAddress: xpdu.pduDestination = LocalBroadcast() - if _debug: BIPBBMD._debug(" - local broadcast") + if _debug: BIPBBMD._debug(" - local broadcast") self.request(xpdu) else: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the other registered foreign devices for fdte in self.bbmdFDT: if fdte.fdAddress != pdu.pduSource: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, OriginalUnicastNPDU): - # build a vanilla PDU - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) elif isinstance(pdu, OriginalBroadcastNPDU): - # build a PDU with a local broadcast address - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # make a forwarded PDU xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) @@ -869,13 +888,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: @@ -925,7 +944,7 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): del self.bbmdFDT[i] break else: - stat = 99 ### entry not found + stat = 0x0050 ### entry not found # return status return stat @@ -952,10 +971,6 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): else: raise TypeError("addr must be a string or an Address") - # if it's this BBMD, make it the first one - if self.bbmdBDT and (addr == self.bbmdAddress): - raise RuntimeError("add self to BDT as first address") - # see if it's already there for bdte in self.bbmdBDT: if addr == bdte: diff --git a/py27/bacpypes/vlan.py b/py27/bacpypes/vlan.py index ead2d03..ea83923 100755 --- a/py27/bacpypes/vlan.py +++ b/py27/bacpypes/vlan.py @@ -161,9 +161,9 @@ class IPNetwork(Network): ('1.2.3.255', 5) and the other nodes must have the same tuple. """ - def __init__(self): + def __init__(self, name=''): if _debug: IPNetwork._debug("__init__") - Network.__init__(self) + Network.__init__(self, name=name) def add_node(self, node): if _debug: IPNetwork._debug("add_node %r", node) @@ -213,7 +213,7 @@ class IPNode(Node): @bacpypes_debugging class IPRouterNode(Client): - def __init__(self, router, addr, lan=None): + def __init__(self, router, addr, lan): if _debug: IPRouterNode._debug("__init__ %r %r lan=%r", router, addr, lan) # save the reference to the router @@ -238,6 +238,9 @@ class IPRouterNode(Client): # pass it downstream self.request(pdu) + def __repr__(self): + return "<%s for %s>" % (self.__class__.__name__, self.lan.name) + # # IPRouter # diff --git a/py34/bacpypes/bvllservice.py b/py34/bacpypes/bvllservice.py index dddb2d0..c382387 100755 --- a/py34/bacpypes/bvllservice.py +++ b/py34/bacpypes/bvllservice.py @@ -541,19 +541,31 @@ class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): return - elif isinstance(pdu, OriginalUnicastNPDU): + # check the BBMD registration status, we may not be registered + if self.registrationStatus != 0: + if _debug: BIPForeign._debug(" - packet dropped, unregistered") + return + + if isinstance(pdu, OriginalUnicastNPDU): # build a vanilla PDU xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) # send it upstream self.response(xpdu) - # check the BBMD registration status, we may not be registered - if self.registrationStatus != 0: - if _debug: BIPForeign._debug(" - packet dropped, unregistered") - return + elif isinstance(pdu, ForwardedNPDU): + # make sure the forwarded PDU from the bbmd + if pdu.pduSource != self.bbmdAddress: + if _debug: BIPForeign._debug(" - packet dropped, not from the BBMD") + return - if isinstance(pdu, ReadBroadcastDistributionTableAck): + # build a PDU with the source from the real source + xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) + + # send it upstream + self.response(xpdu) + + elif isinstance(pdu, ReadBroadcastDistributionTableAck): # send this to the service access point self.sap_response(pdu) @@ -561,13 +573,6 @@ class BIPForeign(BIPSAP, Client, Server, OneShotTask, DebugContents): # send this to the service access point self.sap_response(pdu) - elif isinstance(pdu, ForwardedNPDU): - # build a PDU with the source from the real source - xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) - - # send it upstream - self.response(xpdu) - elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response xpdu = Result(code=0x0010, user_data=pdu.pduUserData) @@ -690,7 +695,7 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): # make an original unicast PDU xpdu = OriginalUnicastNPDU(pdu, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduDestination - if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) + if _debug: BIPBBMD._debug(" - original unicast xpdu: %r", xpdu) # send it downstream self.request(xpdu) @@ -713,13 +718,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: @@ -735,8 +740,9 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): elif isinstance(pdu, WriteBroadcastDistributionTable): # build a response - xpdu = Result(code=99, user_data=pdu.pduUserData) + xpdu = Result(code=0x0010, user_data=pdu.pduUserData) xpdu.pduDestination = pdu.pduSource + if _debug: BIPBBMD._debug(" - xpdu: %r", xpdu) # send it downstream self.request(xpdu) @@ -755,27 +761,38 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): self.sap_response(pdu) elif isinstance(pdu, ForwardedNPDU): - # build a PDU with the source from the real source - xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.bvlciAddress, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.bvlciAddress, pdu, destination=None, user_data=pdu.pduUserData) if _debug: BIPBBMD._debug(" - forwarded xpdu: %r", xpdu) - # look for self as first entry in the BDT - if self.bbmdBDT and (self.bbmdBDT[0] == self.bbmdAddress): - xpdu.pduDestination = LocalBroadcast() - if _debug: BIPBBMD._debug(" - local broadcast") - self.request(xpdu) + # if this was unicast to us, do next hop + if pdu.pduDestination.addrType == Address.localStationAddr: + if _debug: BIPBBMD._debug(" - unicast message") + + # if this BBMD is listed in its BDT, send a local broadcast + if self.bbmdAddress in self.bbmdBDT: + xpdu.pduDestination = LocalBroadcast() + if _debug: BIPBBMD._debug(" - local broadcast") + self.request(xpdu) + + elif pdu.pduDestination.addrType == Address.localBroadcastAddr: + if _debug: BIPBBMD._debug(" - directed broadcast message") + + else: + BIPBBMD._warning("invalid destination address: %r", pdu.pduDestination) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, RegisterForeignDevice): @@ -814,12 +831,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): self.request(xpdu) elif isinstance(pdu, DistributeBroadcastToNetwork): - # build a PDU with a local broadcast address - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # build a forwarded NPDU to send out xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) @@ -829,35 +847,37 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte == self.bbmdAddress: xpdu.pduDestination = LocalBroadcast() - if _debug: BIPBBMD._debug(" - local broadcast") + if _debug: BIPBBMD._debug(" - local broadcast") self.request(xpdu) else: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the other registered foreign devices for fdte in self.bbmdFDT: if fdte.fdAddress != pdu.pduSource: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) elif isinstance(pdu, OriginalUnicastNPDU): - # build a vanilla PDU - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=pdu.pduDestination, user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) elif isinstance(pdu, OriginalBroadcastNPDU): - # build a PDU with a local broadcast address - xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) - if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) + # send it upstream if there is a network layer + if self.serverPeer: + # build a PDU with a local broadcast address + xpdu = PDU(pdu.pduData, source=pdu.pduSource, destination=LocalBroadcast(), user_data=pdu.pduUserData) + if _debug: BIPBBMD._debug(" - upstream xpdu: %r", xpdu) - # send it upstream - self.response(xpdu) + self.response(xpdu) # make a forwarded PDU xpdu = ForwardedNPDU(pdu.pduSource, pdu, user_data=pdu.pduUserData) @@ -867,13 +887,13 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): for bdte in self.bbmdBDT: if bdte != self.bbmdAddress: xpdu.pduDestination = Address( ((bdte.addrIP|~bdte.addrMask), bdte.addrPort) ) - if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to peer: %r", xpdu.pduDestination) self.request(xpdu) # send it to the registered foreign devices for fdte in self.bbmdFDT: xpdu.pduDestination = fdte.fdAddress - if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) + if _debug: BIPBBMD._debug(" - sending to foreign device: %r", xpdu.pduDestination) self.request(xpdu) else: @@ -923,7 +943,7 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): del self.bbmdFDT[i] break else: - stat = 99 ### entry not found + stat = 0x0050 ### entry not found # return status return stat @@ -950,10 +970,6 @@ class BIPBBMD(BIPSAP, Client, Server, RecurringTask, DebugContents): else: raise TypeError("addr must be a string or an Address") - # if it's this BBMD, make it the first one - if self.bbmdBDT and (addr == self.bbmdAddress): - raise RuntimeError("add self to BDT as first address") - # see if it's already there for bdte in self.bbmdBDT: if addr == bdte: diff --git a/py34/bacpypes/vlan.py b/py34/bacpypes/vlan.py index ead2d03..ea83923 100755 --- a/py34/bacpypes/vlan.py +++ b/py34/bacpypes/vlan.py @@ -161,9 +161,9 @@ class IPNetwork(Network): ('1.2.3.255', 5) and the other nodes must have the same tuple. """ - def __init__(self): + def __init__(self, name=''): if _debug: IPNetwork._debug("__init__") - Network.__init__(self) + Network.__init__(self, name=name) def add_node(self, node): if _debug: IPNetwork._debug("add_node %r", node) @@ -213,7 +213,7 @@ class IPNode(Node): @bacpypes_debugging class IPRouterNode(Client): - def __init__(self, router, addr, lan=None): + def __init__(self, router, addr, lan): if _debug: IPRouterNode._debug("__init__ %r %r lan=%r", router, addr, lan) # save the reference to the router @@ -238,6 +238,9 @@ class IPRouterNode(Client): # pass it downstream self.request(pdu) + def __repr__(self): + return "<%s for %s>" % (self.__class__.__name__, self.lan.name) + # # IPRouter # diff --git a/samples/NATRouter.py b/samples/NATRouter.py new file mode 100644 index 0000000..fa9b70c --- /dev/null +++ b/samples/NATRouter.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python + +""" +This sample application presents itself as a router between an "inside" network +that sits behind a NAT and a "global" network of other NAT router peers. + +$ python NATRouter.py addr1 port1 net1 addr2 port2 net2 + + addr1 - local address like 192.168.1.10/24 + port1 - local port + net1 - local network number + addr2 - global address like 201.1.1.1:47809 + port2 - local mapped port + net2 - global network number + +The sample addresses are like running BR1 from Figure J-8, Clause J.7.5. +""" + +from bacpypes.debugging import bacpypes_debugging, ModuleLogger +from bacpypes.consolelogging import ArgumentParser + +from bacpypes.core import run +from bacpypes.comm import bind + +from bacpypes.pdu import Address +from bacpypes.netservice import NetworkServiceAccessPoint, NetworkServiceElement +from bacpypes.bvllservice import BIPBBMD, BIPNAT, AnnexJCodec, UDPMultiplexer + +# some debugging +_debug = 0 +_log = ModuleLogger(globals()) + +# +# NATRouter +# + +@bacpypes_debugging +class NATRouter: + + def __init__(self, addr1, port1, net1, addr2, port2, net2): + if _debug: NATRouter._debug("__init__ %r %r %r %r %r %r", addr1, port1, net1, addr2, port2, net2) + + # a network service access point will be needed + self.nsap = NetworkServiceAccessPoint() + + # give the NSAP a generic network layer service element + self.nse = NetworkServiceElement() + bind(self.nse, self.nsap) + + #== First stack + + # local address + local_addr = Address("{}:{}".format(addr1, port1)) + + # create a BBMD stack + self.s1_bip = BIPBBMD(local_addr) + self.s1_annexj = AnnexJCodec() + self.s1_mux = UDPMultiplexer(local_addr) + + # bind the bottom layers + bind(self.s1_bip, self.s1_annexj, self.s1_mux.annexJ) + + # bind the BIP stack to the local network + self.nsap.bind(self.s1_bip, net1, addr1) + + #== Second stack + + # global address + global_addr = Address(addr2) + nat_addr = Address("{}:{}".format(addr1, port2)) + + # create a NAT stack + self.s2_bip = BIPNAT(global_addr) + self.s2_annexj = AnnexJCodec() + self.s2_mux = UDPMultiplexer(nat_addr) + + # bind the bottom layers + bind(self.s2_bip, self.s2_annexj, self.s2_mux.annexJ) + + # bind the BIP stack to the global network + self.nsap.bind(self.s2_bip, net2) + +# +# __main__ +# + +def main(): + # parse the command line arguments + parser = ArgumentParser(description=__doc__) + + # add an argument for local address + parser.add_argument('addr1', type=str, + help='address of first network', + ) + + # add an argument for local port + parser.add_argument('port1', type=int, + help='port number of local network', + ) + + # add an argument for interval + parser.add_argument('net1', type=int, + help='network number of local network', + ) + + # add an argument for interval + parser.add_argument('addr2', type=str, + help='address of global network (outside NAT)', + ) + + # add an argument for local port + parser.add_argument('port2', type=int, + help='port number of global forwarded port', + ) + + # add an argument for interval + parser.add_argument('net2', type=int, + help='network number of global network', + ) + + # now parse the arguments + args = parser.parse_args() + + if _debug: _log.debug("initialization") + if _debug: _log.debug(" - args: %r", args) + + # create the router + router = NATRouter(args.addr1, args.port1, args.net1, args.addr2, args.port2, args.net2) + if _debug: _log.debug(" - router: %r", router) + + _log.debug("running") + + run() + + _log.debug("fini") + + +if __name__ == "__main__": + main() diff --git a/tests/test_bvll/helpers.py b/tests/test_bvll/helpers.py index 5146319..c09ee50 100644 --- a/tests/test_bvll/helpers.py +++ b/tests/test_bvll/helpers.py @@ -6,10 +6,19 @@ B/IP VLAN Helper Classes from bacpypes.debugging import bacpypes_debugging, ModuleLogger -from bacpypes.comm import Client, Server, bind +from bacpypes.comm import Client, Server, ApplicationServiceElement, bind from bacpypes.pdu import Address, LocalBroadcast, PDU, unpack_ip_addr from bacpypes.vlan import IPNode +from bacpypes.app import DeviceInfoCache, Application +from bacpypes.appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint +from bacpypes.netservice import NetworkServiceAccessPoint, NetworkServiceElement + +from bacpypes.object import register_object_type +from bacpypes.local.device import LocalDeviceObject +from bacpypes.service.device import WhoIsIAmServices +from bacpypes.service.object import ReadWritePropertyServices + from ..state_machine import ClientStateMachine from bacpypes.bvllservice import BIPSimple, BIPForeign, BIPBBMD, AnnexJCodec @@ -26,6 +35,12 @@ _log = ModuleLogger(globals()) @bacpypes_debugging class FauxMultiplexer(Client, Server): + """This class is a placeholder for UDPMultiplexer without the code that + determines if the upstream packets are Annex-H or Annex-J packets, it + assumes they are all Annex-J. It creates and binds itself to an IPNode + which is added to an IPNetwork. + """ + def __init__(self, addr, network=None, cid=None, sid=None): if _debug: FauxMultiplexer._debug("__init__") @@ -81,37 +96,54 @@ class FauxMultiplexer(Client, Server): self.response(PDU(pdu, source=src, destination=dest)) # -# SnifferNode +# SnifferStateMachine # @bacpypes_debugging -class SnifferNode(ClientStateMachine): +class SnifferStateMachine(ClientStateMachine): + + """This class acts as a sniffer for BVLL messages. The client state + machine sits above an Annex-J codec so the send and receive PDUs are + BVLL PDUs. + """ def __init__(self, address, vlan): - if _debug: SnifferNode._debug("__init__ %r %r", address, vlan) + if _debug: SnifferStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address self.name = address self.address = Address(address) - # create a promiscuous node, added to the network - self.node = IPNode(self.address, vlan, promiscuous=True) - if _debug: SnifferNode._debug(" - node: %r", self.node) + # BACnet/IP interpreter + self.annexj = AnnexJCodec() - # bind this to the node - bind(self, self.node) + # fake multiplexer has a VLAN node in it + self.mux = FauxMultiplexer(self.address, vlan) + + # might receive all packets and allow spoofing + self.mux.node.promiscuous = True + self.mux.node.spoofing = True + + # bind the stack together + bind(self, self.annexj, self.mux) # -# CodecNode +# BIPStateMachine # @bacpypes_debugging -class CodecNode(ClientStateMachine): +class BIPStateMachine(ClientStateMachine): + + """This class is an application layer for BVLL messages that has no BVLL + processing like the 'simple', 'foreign', or 'bbmd' versions. The client + state machine sits above and Annex-J codec so the send and receive PDUs are + BVLL PDUs. + """ def __init__(self, address, vlan): - if _debug: CodecNode._debug("__init__ %r %r", address, vlan) + if _debug: BIPStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address @@ -129,14 +161,18 @@ class CodecNode(ClientStateMachine): # -# SimpleNode +# BIPSimpleStateMachine # @bacpypes_debugging -class SimpleNode(ClientStateMachine): +class BIPSimpleStateMachine(ClientStateMachine): + + """This class sits on a BIPSimple instance, the send() and receive() + parameters are NPDUs. + """ def __init__(self, address, vlan): - if _debug: SimpleNode._debug("__init__ %r %r", address, vlan) + if _debug: BIPSimpleStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address @@ -155,14 +191,18 @@ class SimpleNode(ClientStateMachine): # -# ForeignNode +# BIPForeignStateMachine # @bacpypes_debugging -class ForeignNode(ClientStateMachine): +class BIPForeignStateMachine(ClientStateMachine): + + """This class sits on a BIPForeign instance, the send() and receive() + parameters are NPDUs. + """ def __init__(self, address, vlan): - if _debug: ForeignNode._debug("__init__ %r %r", address, vlan) + if _debug: BIPForeignStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address @@ -180,14 +220,18 @@ class ForeignNode(ClientStateMachine): bind(self, self.bip, self.annexj, self.mux) # -# BBMDNode +# BIPBBMDStateMachine # @bacpypes_debugging -class BBMDNode(ClientStateMachine): +class BIPBBMDStateMachine(ClientStateMachine): + + """This class sits on a BIPBBMD instance, the send() and receive() + parameters are NPDUs. + """ def __init__(self, address, vlan): - if _debug: BBMDNode._debug("__init__ %r %r", address, vlan) + if _debug: BIPBBMDStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address @@ -200,7 +244,7 @@ class BBMDNode(ClientStateMachine): # build an address, full mask bdt_address = "%s/32:%d" % self.address.addrTuple - if _debug: BBMDNode._debug(" - bdt_address: %r", bdt_address) + if _debug: BIPBBMDStateMachine._debug(" - bdt_address: %r", bdt_address) # add itself as the first entry in the BDT self.bip.add_peer(Address(bdt_address)) @@ -211,3 +255,209 @@ class BBMDNode(ClientStateMachine): # bind the stack together bind(self, self.bip, self.annexj, self.mux) +# +# BIPSimpleNode +# + +@bacpypes_debugging +class BIPSimpleNode: + + """This class is a BIPSimple instance that is not bound to a state machine.""" + + def __init__(self, address, vlan): + if _debug: BIPSimpleNode._debug("__init__ %r %r", address, vlan) + + # save the name and address + self.name = address + self.address = Address(address) + + # BACnet/IP interpreter + self.bip = BIPSimple() + self.annexj = AnnexJCodec() + + # fake multiplexer has a VLAN node in it + self.mux = FauxMultiplexer(self.address, vlan) + + # bind the stack together + bind(self.bip, self.annexj, self.mux) + +# +# BIPBBMDNode +# + +@bacpypes_debugging +class BIPBBMDNode: + + """This class is a BIPBBMD instance that is not bound to a state machine.""" + + def __init__(self, address, vlan): + if _debug: BIPBBMDNode._debug("__init__ %r %r", address, vlan) + + # save the name and address + self.name = address + self.address = Address(address) + if _debug: BIPBBMDNode._debug(" - address: %r", self.address) + + # BACnet/IP interpreter + self.bip = BIPBBMD(self.address) + self.annexj = AnnexJCodec() + + # build an address, full mask + bdt_address = "%s/32:%d" % self.address.addrTuple + if _debug: BIPBBMDNode._debug(" - bdt_address: %r", bdt_address) + + # add itself as the first entry in the BDT + self.bip.add_peer(Address(bdt_address)) + + # fake multiplexer has a VLAN node in it + self.mux = FauxMultiplexer(self.address, vlan) + + # bind the stack together + bind(self.bip, self.annexj, self.mux) + + +# +# TestDeviceObject +# + +@register_object_type(vendor_id=999) +class TestDeviceObject(LocalDeviceObject): + + pass + +# +# BIPSimpleApplicationLayerStateMachine +# + +@bacpypes_debugging +class BIPSimpleApplicationLayerStateMachine(ApplicationServiceElement, ClientStateMachine): + + def __init__(self, address, vlan): + if _debug: BIPSimpleApplicationLayerStateMachine._debug("__init__ %r %r", address, vlan) + + # build a name, save the address + self.name = "app @ %s" % (address,) + self.address = Address(address) + + # build a local device object + local_device = TestDeviceObject( + objectName=self.name, + objectIdentifier=('device', 998), + vendorIdentifier=999, + ) + + # build an address and save it + self.address = Address(address) + if _debug: BIPSimpleApplicationLayerStateMachine._debug(" - address: %r", self.address) + + # continue with initialization + ApplicationServiceElement.__init__(self) + ClientStateMachine.__init__(self, name=local_device.objectName) + + # include a application decoder + self.asap = ApplicationServiceAccessPoint() + + # pass the device object to the state machine access point so it + # can know if it should support segmentation + self.smap = StateMachineAccessPoint(local_device) + + # the segmentation state machines need access to some device + # information cache, usually shared with the application + self.smap.deviceInfoCache = DeviceInfoCache() + + # a network service access point will be needed + self.nsap = NetworkServiceAccessPoint() + + # give the NSAP a generic network layer service element + self.nse = NetworkServiceElement() + bind(self.nse, self.nsap) + + # bind the top layers + bind(self, self.asap, self.smap, self.nsap) + + # BACnet/IP interpreter + self.bip = BIPSimple() + self.annexj = AnnexJCodec() + + # fake multiplexer has a VLAN node in it + self.mux = FauxMultiplexer(self.address, vlan) + + # bind the stack together + bind(self.bip, self.annexj, self.mux) + + # bind the stack to the local network + self.nsap.bind(self.bip) + + def indication(self, apdu): + if _debug: BIPSimpleApplicationLayerStateMachine._debug("indication %r", apdu) + self.receive(apdu) + + def confirmation(self, apdu): + if _debug: BIPSimpleApplicationLayerStateMachine._debug("confirmation %r %r", apdu) + self.receive(apdu) + +# +# BIPBBMDApplication +# + +class BIPBBMDApplication(Application, WhoIsIAmServices, ReadWritePropertyServices): + + def __init__(self, address, vlan): + if _debug: BIPBBMDApplication._debug("__init__ %r %r", address, vlan) + + # build a name, save the address + self.name = "app @ %s" % (address,) + self.address = Address(address) + if _debug: BIPBBMDApplication._debug(" - address: %r", self.address) + + # build a local device object + local_device = TestDeviceObject( + objectName=self.name, + objectIdentifier=('device', 999), + vendorIdentifier=999, + ) + + # continue with initialization + Application.__init__(self, local_device, self.address) + + # include a application decoder + self.asap = ApplicationServiceAccessPoint() + + # pass the device object to the state machine access point so it + # can know if it should support segmentation + self.smap = StateMachineAccessPoint(local_device) + + # the segmentation state machines need access to the same device + # information cache as the application + self.smap.deviceInfoCache = self.deviceInfoCache + + # a network service access point will be needed + self.nsap = NetworkServiceAccessPoint() + + # give the NSAP a generic network layer service element + self.nse = NetworkServiceElement() + bind(self.nse, self.nsap) + + # bind the top layers + bind(self, self.asap, self.smap, self.nsap) + + # BACnet/IP interpreter + self.bip = BIPBBMD(self.address) + self.annexj = AnnexJCodec() + + # build an address, full mask + bdt_address = "%s/32:%d" % self.address.addrTuple + if _debug: BIPBBMDNode._debug(" - bdt_address: %r", bdt_address) + + # add itself as the first entry in the BDT + self.bip.add_peer(Address(bdt_address)) + + # fake multiplexer has a VLAN node in it + self.mux = FauxMultiplexer(self.address, vlan) + + # bind the stack together + bind(self.bip, self.annexj, self.mux) + + # bind the stack to the local network + self.nsap.bind(self.bip) + diff --git a/tests/test_bvll/test_bbmd.py b/tests/test_bvll/test_bbmd.py index fdffa2a..d78798a 100644 --- a/tests/test_bvll/test_bbmd.py +++ b/tests/test_bvll/test_bbmd.py @@ -1 +1,302 @@ -# placeholder +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Test BBMD +--------- +""" + +import unittest + +from bacpypes.debugging import bacpypes_debugging, ModuleLogger, xtob + +from bacpypes.pdu import Address, PDU, LocalBroadcast +from bacpypes.vlan import IPNetwork, IPRouter +from bacpypes.bvll import ( + Result, + WriteBroadcastDistributionTable, + ReadBroadcastDistributionTable, ReadBroadcastDistributionTableAck, + ForwardedNPDU, + RegisterForeignDevice, + ReadForeignDeviceTable, ReadForeignDeviceTableAck, + DeleteForeignDeviceTableEntry, + DistributeBroadcastToNetwork, + OriginalUnicastNPDU, + OriginalBroadcastNPDU, + ) + +from bacpypes.apdu import ( + WhoIsRequest, IAmRequest, + ReadPropertyRequest, ReadPropertyACK, + AbortPDU, + ) + +from ..state_machine import StateMachineGroup, TrafficLog +from ..time_machine import reset_time_machine, run_time_machine + +from .helpers import ( + SnifferStateMachine, BIPStateMachine, BIPSimpleStateMachine, + BIPForeignStateMachine, BIPBBMDStateMachine, + BIPSimpleNode, BIPBBMDNode, + BIPSimpleApplicationLayerStateMachine, + BIPBBMDApplication, + ) + +# some debugging +_debug = 0 +_log = ModuleLogger(globals()) + + +# +# TNetwork +# + +@bacpypes_debugging +class TNetwork(StateMachineGroup): + + def __init__(self, count): + if _debug: TNetwork._debug("__init__ %r", count) + StateMachineGroup.__init__(self) + + # reset the time machine + reset_time_machine() + if _debug: TNetwork._debug(" - time machine reset") + + # create a traffic log + self.traffic_log = TrafficLog() + + # make a router + self.router = IPRouter() + + # make the networks + self.vlan = [] + for net in range(1, count + 1): + # make a network and set the traffic log + ip_network = IPNetwork("192.168.{}.0/24".format(net)) + ip_network.traffic_log = self.traffic_log + + # make a router + router_address = Address("192.168.{}.1/24".format(net)) + self.router.add_network(router_address, ip_network) + + self.vlan.append(ip_network) + + def run(self, time_limit=60.0): + if _debug: TNetwork._debug("run %r", time_limit) + + # run the group + super(TNetwork, self).run() + + # run it for some time + run_time_machine(time_limit) + if _debug: TNetwork._debug(" - time machine finished") + + # check for success + all_success, some_failed = super(TNetwork, self).check_for_success() + if _debug: + TNetwork._debug(" - all_success, some_failed: %r, %r", all_success, some_failed) + for state_machine in self.state_machines: + if state_machine.running: + TNetwork._debug(" %r (running)", state_machine) + elif not state_machine.current_state: + TNetwork._debug(" %r (not started)", state_machine) + else: + TNetwork._debug(" %r", state_machine) + for direction, pdu in state_machine.transaction_log: + TNetwork._debug(" %s %r", direction, pdu) + + # traffic log has what was processed on each vlan + self.traffic_log.dump(TNetwork._debug) + + assert all_success + + +@bacpypes_debugging +class TestNonBBMD(unittest.TestCase): + + def setup_method(self, method): + """This function is called before each test method is called and is + given a reference to the test method.""" + if _debug: TestNonBBMD._debug("setup_method %r", method) + + # create a network + self.tnet = TNetwork(1) + + # test device + self.td = BIPStateMachine("192.168.1.2/24", self.tnet.vlan[0]) + self.tnet.append(self.td) + + # implementation under test + self.iut = BIPSimpleNode("192.168.1.3/24", self.tnet.vlan[0]) + + def test_write_bdt_fail(self): + """Test writing a BDT.""" + if _debug: TestNonBBMD._debug("test_write_bdt_fail") + + # read the broadcast distribution table, get a nack + self.td.start_state.doc("1-1-0") \ + .send(WriteBroadcastDistributionTable(destination=self.iut.address)).doc("1-1-1") \ + .receive(Result, bvlciResultCode=0x0010).doc("1-1-2") \ + .success() + + # run the group + self.tnet.run() + + def test_read_bdt_fail(self): + """Test reading a BDT.""" + if _debug: TestNonBBMD._debug("test_read_bdt_fail") + + # read the broadcast distribution table, get a nack + self.td.start_state.doc("1-2-0") \ + .send(ReadBroadcastDistributionTable(destination=self.iut.address)).doc("1-2-1") \ + .receive(Result, bvlciResultCode=0x0020).doc("1-2-2") \ + .success() + + # run the group + self.tnet.run() + + def test_register_fail(self): + """Test registering as a foreign device to a non-BBMD.""" + if _debug: TestNonBBMD._debug("test_read_fdt_success") + + # read the broadcast distribution table, get a nack + self.td.start_state.doc("1-3-0") \ + .send(RegisterForeignDevice(10, destination=self.iut.address)).doc("1-3-1") \ + .receive(Result, bvlciResultCode=0x0030).doc("1-3-2") \ + .success() + + # run the group + self.tnet.run() + + def test_read_fdt_fail(self): + """Test reading an FDT from a non-BBMD.""" + if _debug: TestNonBBMD._debug("test_read_fdt_success") + + # read the broadcast distribution table, get a nack + self.td.start_state.doc("1-4-0") \ + .send(ReadForeignDeviceTable(destination=self.iut.address)).doc("1-4-1") \ + .receive(Result, bvlciResultCode=0x0040).doc("1-4-2") \ + .success() + + # run the group + self.tnet.run() + + def test_delete_fail(self): + """Test deleting an FDT entry from a non-BBMD.""" + if _debug: TestNonBBMD._debug("test_delete_fail") + + # read the broadcast distribution table, get a nack + self.td.start_state.doc("1-5-0") \ + .send(DeleteForeignDeviceTableEntry(Address("1.2.3.4"), destination=self.iut.address)).doc("1-5-1") \ + .receive(Result, bvlciResultCode=0x0050).doc("1-5-2") \ + .success() + + # run the group + self.tnet.run() + + def test_distribute_fail(self): + """Test asking a non-BBMD to distribute a broadcast.""" + if _debug: TestNonBBMD._debug("test_delete_fail") + + # read the broadcast distribution table, get a nack + self.td.start_state.doc("1-6-0") \ + .send(DistributeBroadcastToNetwork(xtob('deadbeef'), destination=self.iut.address)).doc("1-6-1") \ + .receive(Result, bvlciResultCode=0x0060).doc("1-6-2") \ + .success() + + # run the group + self.tnet.run() + + +@bacpypes_debugging +class TestBBMD(unittest.TestCase): + + def test_14_2_1_1(self): + """14.2.1.1 Execute Forwarded-NPDU (One-hop Distribution).""" + if _debug: TestBBMD._debug("test_14_2_1_1") + + # create a network + tnet = TNetwork(2) + + # implementation under test + iut = BIPBBMDApplication("192.168.1.2/24", tnet.vlan[0]) + if _debug: TestBBMD._debug(" - iut.bip: %r", iut.bip) + + # BBMD on net 2 + bbmd1 = BIPBBMDNode("192.168.2.2/24", tnet.vlan[1]) + + # add the IUT as a one-hop peer + bbmd1.bip.add_peer(Address("192.168.1.2/24")) + if _debug: TestBBMD._debug(" - bbmd1.bip: %r", bbmd1.bip) + + # test device + td = BIPSimpleApplicationLayerStateMachine("192.168.2.3/24", tnet.vlan[1]) + tnet.append(td) + + # listener looks for extra traffic + listener = BIPStateMachine("192.168.1.3/24", tnet.vlan[0]) + listener.mux.node.promiscuous = True + tnet.append(listener) + + # broadcast a forwarded NPDU + td.start_state.doc("2-1-0") \ + .send(WhoIsRequest(destination=LocalBroadcast())).doc("2-1-1") \ + .receive(IAmRequest).doc("2-1-2") \ + .success() + + # listen for the directed broadcast, then the original unicast, + # then fail if there's anything else + listener.start_state.doc("2-2-0") \ + .receive(ForwardedNPDU).doc("2-2-1") \ + .receive(OriginalUnicastNPDU).doc("2-2-2") \ + .timeout(3).doc("2-2-3") \ + .success() + + # run the group + tnet.run() + + def test_14_2_1_2(self): + """14.2.1.1 Execute Forwarded-NPDU (Two-hop Distribution).""" + if _debug: TestBBMD._debug("test_14_2_1_2") + + # create a network + tnet = TNetwork(2) + + # implementation under test + iut = BIPBBMDApplication("192.168.1.2/24", tnet.vlan[0]) + if _debug: TestBBMD._debug(" - iut.bip: %r", iut.bip) + + # BBMD on net 2 + bbmd1 = BIPBBMDNode("192.168.2.2/24", tnet.vlan[1]) + + # add the IUT as a two-hop peer + bbmd1.bip.add_peer(Address("192.168.1.2/32")) + if _debug: TestBBMD._debug(" - bbmd1.bip: %r", bbmd1.bip) + + # test device + td = BIPSimpleApplicationLayerStateMachine("192.168.2.3/24", tnet.vlan[1]) + tnet.append(td) + + # listener looks for extra traffic + listener = BIPStateMachine("192.168.1.3/24", tnet.vlan[0]) + listener.mux.node.promiscuous = True + tnet.append(listener) + + # broadcast a forwarded NPDU + td.start_state.doc("2-3-0") \ + .send(WhoIsRequest(destination=LocalBroadcast())).doc("2-3-1") \ + .receive(IAmRequest).doc("2-3-2") \ + .success() + + # listen for the forwarded NPDU, then the re-braodcast on the local LAN + # then the original unicast going back, then fail if there's anything else + listener.start_state.doc("2-4-0") \ + .receive(ForwardedNPDU).doc("2-4-1") \ + .receive(ForwardedNPDU).doc("2-4-2") \ + .receive(OriginalUnicastNPDU).doc("2-4-3") \ + .timeout(3).doc("2-4-4") \ + .success() + + # run the group + tnet.run() + diff --git a/tests/test_bvll/test_foreign.py b/tests/test_bvll/test_foreign.py index 521b527..0fa157b 100644 --- a/tests/test_bvll/test_foreign.py +++ b/tests/test_bvll/test_foreign.py @@ -12,12 +12,20 @@ from bacpypes.debugging import bacpypes_debugging, ModuleLogger, xtob from bacpypes.pdu import Address, PDU, LocalBroadcast from bacpypes.vlan import IPNetwork, IPRouter -from bacpypes.bvll import ReadForeignDeviceTable, ReadForeignDeviceTableAck +from bacpypes.bvll import ( + Result, RegisterForeignDevice, + ReadForeignDeviceTable, ReadForeignDeviceTableAck, + DistributeBroadcastToNetwork, ForwardedNPDU, + OriginalUnicastNPDU, OriginalBroadcastNPDU, + ) -from ..state_machine import StateMachineGroup +from ..state_machine import StateMachineGroup, TrafficLog from ..time_machine import reset_time_machine, run_time_machine -from .helpers import SnifferNode, CodecNode, SimpleNode, ForeignNode, BBMDNode +from .helpers import ( + SnifferStateMachine, BIPStateMachine, + BIPSimpleStateMachine, BIPForeignStateMachine, BIPBBMDStateMachine, + ) # some debugging _debug = 0 @@ -39,23 +47,28 @@ class TNetwork(StateMachineGroup): reset_time_machine() if _debug: TNetwork._debug(" - time machine reset") + # create a traffic log + self.traffic_log = TrafficLog() + # make a router self.router = IPRouter() # make a home LAN - self.home_vlan = IPNetwork() - self.router.add_network(Address("192.168.5.1/24"), self.home_vlan) + self.vlan_5 = IPNetwork("192.168.5.0/24") + self.vlan_5.traffic_log = self.traffic_log + self.router.add_network(Address("192.168.5.1/24"), self.vlan_5) # make a remote LAN - self.remote_vlan = IPNetwork() - self.router.add_network(Address("192.168.6.1/24"), self.remote_vlan) + self.vlan_6 = IPNetwork("192.168.6.0/24") + self.vlan_6.traffic_log = self.traffic_log + self.router.add_network(Address("192.168.6.1/24"), self.vlan_6) # the foreign device - self.fd = ForeignNode("192.168.6.2/24", self.remote_vlan) + self.fd = BIPForeignStateMachine("192.168.6.2/24", self.vlan_6) self.append(self.fd) # bbmd - self.bbmd = BBMDNode("192.168.5.3/24", self.home_vlan) + self.bbmd = BIPBBMDStateMachine("192.168.5.3/24", self.vlan_5) self.append(self.bbmd) def run(self, time_limit=60.0): @@ -70,6 +83,22 @@ class TNetwork(StateMachineGroup): # check for success all_success, some_failed = super(TNetwork, self).check_for_success() + + if _debug: + TNetwork._debug(" - all_success, some_failed: %r, %r", all_success, some_failed) + for state_machine in self.state_machines: + if state_machine.running: + TNetwork._debug(" %r (running)", state_machine) + elif not state_machine.current_state: + TNetwork._debug(" %r (not started)", state_machine) + else: + TNetwork._debug(" %r", state_machine) + for direction, pdu in state_machine.transaction_log: + TNetwork._debug(" %s %s", direction, str(pdu)) + + # traffic log has what was processed on each vlan + self.traffic_log.dump(TNetwork._debug) + assert all_success @@ -97,58 +126,46 @@ class TestForeign(unittest.TestCase): # create a network tnet = TNetwork() - # add an addition codec node to the home vlan - cnode = CodecNode("192.168.5.2/24", tnet.home_vlan) - tnet.append(cnode) - - # home sniffer node - home_sniffer = SnifferNode("192.168.5.254/24", tnet.home_vlan) - tnet.append(home_sniffer) - - # remote sniffer node - remote_sniffer = SnifferNode("192.168.6.254/24", tnet.remote_vlan) - tnet.append(remote_sniffer) - # tell the B/IP layer of the foreign device to register tnet.fd.start_state \ .call(tnet.fd.bip.register, tnet.bbmd.address, 30) \ .success() - # sniffer pieces - registration_request = xtob('81.05.0006' # bvlci - '001e' # time-to-live - ) - registration_ack = xtob('81.00.0006.0000') # simple ack + # remote sniffer node + remote_sniffer = SnifferStateMachine("192.168.6.254/24", tnet.vlan_6) + tnet.append(remote_sniffer) - # remote sniffer sees registration + # sniffer traffic remote_sniffer.start_state.doc("1-1-0") \ - .receive(PDU, pduData=registration_request).doc("1-1-1") \ - .receive(PDU, pduData=registration_ack).doc("1-1-2") \ + .receive(RegisterForeignDevice).doc("1-1-1") \ + .receive(Result).doc("1-1-2") \ .set_event('fd-registered').doc("1-1-3") \ .success() # the bbmd is idle tnet.bbmd.start_state.success() - # read the FDT - cnode.start_state.doc("1-2-0") \ + # home snooper node + home_snooper = BIPStateMachine("192.168.5.2/24", tnet.vlan_5) + tnet.append(home_snooper) + + # snooper will read the foreign device table + home_snooper.start_state.doc("1-2-0") \ .wait_event('fd-registered').doc("1-2-1") \ .send(ReadForeignDeviceTable(destination=tnet.bbmd.address)).doc("1-2-2") \ .receive(ReadForeignDeviceTableAck).doc("1-2-3") \ .success() - # the tnode reads the registration table - read_fdt_request = xtob('81.06.0004') # bvlci - read_fdt_ack = xtob('81.07.000e' # read-ack - 'c0.a8.06.02.ba.c0 001e 0023' # address, ttl, remaining - ) + # home sniffer node + home_sniffer = SnifferStateMachine("192.168.5.254/24", tnet.vlan_5) + tnet.append(home_sniffer) - # home sniffer sees registration + # sniffer traffic home_sniffer.start_state.doc("1-3-0") \ - .receive(PDU, pduData=registration_request).doc("1-3-1") \ - .receive(PDU, pduData=registration_ack).doc("1-3-2") \ - .receive(PDU, pduData=read_fdt_request).doc("1-3-3") \ - .receive(PDU, pduData=read_fdt_ack).doc("1-3-4") \ + .receive(RegisterForeignDevice).doc("1-3-1") \ + .receive(Result).doc("1-3-2") \ + .receive(ReadForeignDeviceTable).doc("1-3-3") \ + .receive(ReadForeignDeviceTableAck).doc("1-3-4") \ .success() # run the group @@ -170,21 +187,15 @@ class TestForeign(unittest.TestCase): tnet.bbmd.start_state.success() # remote sniffer node - remote_sniffer = SnifferNode("192.168.6.254/24", tnet.remote_vlan) + remote_sniffer = SnifferStateMachine("192.168.6.254/24", tnet.vlan_6) tnet.append(remote_sniffer) - # sniffer pieces - registration_request = xtob('81.05.0006' # bvlci - '000a' # time-to-live - ) - registration_ack = xtob('81.00.0006.0000') # simple ack - - # remote sniffer sees registration + # sniffer traffic remote_sniffer.start_state.doc("2-1-0") \ - .receive(PDU, pduData=registration_request).doc("2-1-1") \ - .receive(PDU, pduData=registration_ack).doc("2-1-2") \ - .receive(PDU, pduData=registration_request).doc("2-1-3") \ - .receive(PDU, pduData=registration_ack).doc("2-1-4") \ + .receive(RegisterForeignDevice).doc("2-1-1") \ + .receive(Result).doc("2-1-2") \ + .receive(RegisterForeignDevice).doc("2-1-3") \ + .receive(Result).doc("2-1-4") \ .success() # run the group @@ -205,7 +216,7 @@ class TestForeign(unittest.TestCase): # register, wait for ack, send some beef tnet.fd.start_state.doc("3-1-0") \ .call(tnet.fd.bip.register, tnet.bbmd.address, 60).doc("3-1-1") \ - .wait_event('fd-registered').doc("3-1-2") \ + .wait_event('3-registered').doc("3-1-2") \ .send(pdu).doc("3-1-3") \ .success() @@ -215,24 +226,15 @@ class TestForeign(unittest.TestCase): .success() # remote sniffer node - remote_sniffer = SnifferNode("192.168.6.254/24", tnet.remote_vlan) + remote_sniffer = SnifferStateMachine("192.168.6.254/24", tnet.vlan_6) tnet.append(remote_sniffer) - # sniffer pieces - registration_request = xtob('81.05.0006' # bvlci - '003c' # time-to-live (60) - ) - registration_ack = xtob('81.00.0006.0000') # simple ack - unicast_pdu = xtob('81.0a.0008' # original unicast bvlci - 'dead.beef' # PDU being unicast - ) - - # remote sniffer sees registration + # sniffer traffic remote_sniffer.start_state.doc("3-2-0") \ - .receive(PDU, pduData=registration_request).doc("3-2-1") \ - .receive(PDU, pduData=registration_ack).doc("3-2-2") \ - .set_event('fd-registered').doc("3-2-3") \ - .receive(PDU, pduData=unicast_pdu).doc("3-2-4") \ + .receive(RegisterForeignDevice).doc("3-2-1") \ + .receive(Result).doc("3-2-2") \ + .set_event('3-registered').doc("3-2-3") \ + .receive(OriginalUnicastNPDU).doc("3-2-4") \ .success() # run the group @@ -258,38 +260,29 @@ class TestForeign(unittest.TestCase): .success() # the bbmd is happy when it gets the pdu - tnet.bbmd.start_state \ - .receive(PDU, pduSource=tnet.fd.address, pduData=pdu_data) \ - .success() - - # home sniffer node - home_node = SimpleNode("192.168.5.254/24", tnet.home_vlan) - tnet.append(home_node) - - # home node happy when getting the pdu, broadcast by the bbmd - home_node.start_state.doc("4-2-0") \ + tnet.bbmd.start_state.doc("4-2-0") \ .receive(PDU, pduSource=tnet.fd.address, pduData=pdu_data).doc("4-2-1") \ .success() + # home simple node + home_node = BIPSimpleStateMachine("192.168.5.254/24", tnet.vlan_5) + tnet.append(home_node) + + # home node happy when getting the pdu, broadcast by the bbmd + home_node.start_state.doc("4-3-0") \ + .receive(PDU, pduSource=tnet.fd.address, pduData=pdu_data).doc("4-3-1") \ + .success() + # remote sniffer node - remote_sniffer = SnifferNode("192.168.6.254/24", tnet.remote_vlan) + remote_sniffer = SnifferStateMachine("192.168.6.254/24", tnet.vlan_6) tnet.append(remote_sniffer) - # sniffer pieces - registration_request = xtob('81.05.0006' # bvlci - '003c' # time-to-live (60) - ) - registration_ack = xtob('81.00.0006.0000') # simple ack - distribute_pdu = xtob('81.09.0008' # bvlci - 'deadbeef' # PDU to broadcast - ) - - # remote sniffer sees registration - remote_sniffer.start_state.doc("4-3-0") \ - .receive(PDU, pduData=registration_request).doc("4-3-1") \ - .receive(PDU, pduData=registration_ack).doc("4-3-2") \ + # remote traffic + remote_sniffer.start_state.doc("4-4-0") \ + .receive(RegisterForeignDevice).doc("4-4-1") \ + .receive(Result).doc("4-4-2") \ .set_event('4-registered') \ - .receive(PDU, pduData=distribute_pdu).doc("4-3-3") \ + .receive(DistributeBroadcastToNetwork).doc("4-4-3") \ .success() # run the group diff --git a/tests/test_bvll/test_simple.py b/tests/test_bvll/test_simple.py index fccf8cf..729b9d0 100644 --- a/tests/test_bvll/test_simple.py +++ b/tests/test_bvll/test_simple.py @@ -11,12 +11,15 @@ import unittest from bacpypes.debugging import bacpypes_debugging, ModuleLogger, xtob from bacpypes.pdu import PDU, LocalBroadcast +from bacpypes.bvll import OriginalUnicastNPDU, OriginalBroadcastNPDU from bacpypes.vlan import IPNetwork from ..state_machine import match_pdu, StateMachineGroup from ..time_machine import reset_time_machine, run_time_machine -from .helpers import SnifferNode, SimpleNode +from .helpers import ( + SnifferStateMachine, BIPSimpleStateMachine, + ) # some debugging _debug = 0 @@ -42,15 +45,15 @@ class TNetwork(StateMachineGroup): self.vlan = IPNetwork() # test device - self.td = SimpleNode("192.168.4.1/24", self.vlan) + self.td = BIPSimpleStateMachine("192.168.4.1/24", self.vlan) self.append(self.td) # implementation under test - self.iut = SimpleNode("192.168.4.2/24", self.vlan) + self.iut = BIPSimpleStateMachine("192.168.4.2/24", self.vlan) self.append(self.iut) # sniffer node - self.sniffer = SnifferNode("192.168.4.254/24", self.vlan) + self.sniffer = SnifferStateMachine("192.168.4.254/24", self.vlan) self.append(self.sniffer) @@ -109,12 +112,10 @@ class TestSimple(unittest.TestCase): tnet.iut.start_state.receive(PDU, pduSource=tnet.td.address).success() # sniffer sees message on the wire - tnet.sniffer.start_state.receive(PDU, + tnet.sniffer.start_state.receive(OriginalUnicastNPDU, pduSource=tnet.td.address.addrTuple, pduDestination=tnet.iut.address.addrTuple, - pduData=xtob('81.0a.0008' # original unicast bvlci - 'deadbeef' # PDU being unicast - ), + pduData=pdu_data, ).timeout(1.0).success() # run the group @@ -137,12 +138,10 @@ class TestSimple(unittest.TestCase): tnet.iut.start_state.receive(PDU, pduSource=tnet.td.address).success() # sniffer sees message on the wire - tnet.sniffer.start_state.receive(PDU, - pduSource=tnet.td.address.addrTuple, - pduDestination=('192.168.4.255', 47808), - pduData=xtob('81.0b.0008' # original broadcast bvlci - 'deadbeef' # PDU being unicast - ), + tnet.sniffer.start_state.receive(OriginalBroadcastNPDU, + pduSource=tnet.td.address.addrTuple, +# pduDestination=('192.168.4.255', 47808), + pduData=pdu_data, ).timeout(1.0).success() # run the group diff --git a/tests/test_network/helpers.py b/tests/test_network/helpers.py index 1c039e4..a409ffb 100644 --- a/tests/test_network/helpers.py +++ b/tests/test_network/helpers.py @@ -71,14 +71,14 @@ class NPDUCodec(Client, Server): # -# SnifferNode +# SnifferStateMachine # @bacpypes_debugging -class SnifferNode(ClientStateMachine): +class SnifferStateMachine(ClientStateMachine): def __init__(self, address, vlan): - if _debug: SnifferNode._debug("__init__ %r %r", address, vlan) + if _debug: SnifferStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address @@ -87,20 +87,20 @@ class SnifferNode(ClientStateMachine): # create a promiscuous node, added to the network self.node = Node(self.address, vlan, promiscuous=True) - if _debug: SnifferNode._debug(" - node: %r", self.node) + if _debug: SnifferStateMachine._debug(" - node: %r", self.node) # bind this to the node bind(self, self.node) # -# NetworkLayerNode +# NetworkLayerStateMachine # @bacpypes_debugging -class NetworkLayerNode(ClientStateMachine): +class NetworkLayerStateMachine(ClientStateMachine): def __init__(self, address, vlan): - if _debug: NetworkLayerNode._debug("__init__ %r %r", address, vlan) + if _debug: NetworkLayerStateMachine._debug("__init__ %r %r", address, vlan) ClientStateMachine.__init__(self) # save the name and address @@ -109,11 +109,11 @@ class NetworkLayerNode(ClientStateMachine): # create a network layer encoder/decoder self.codec = NPDUCodec() - if _debug: SnifferNode._debug(" - codec: %r", self.codec) + if _debug: SnifferStateMachine._debug(" - codec: %r", self.codec) # create a node, added to the network self.node = Node(self.address, vlan) - if _debug: SnifferNode._debug(" - node: %r", self.node) + if _debug: SnifferStateMachine._debug(" - node: %r", self.node) # bind this to the node bind(self, self.codec, self.node) @@ -158,14 +158,14 @@ class TestDeviceObject(LocalDeviceObject): pass # -# ApplicationLayerNode +# ApplicationLayerStateMachine # @bacpypes_debugging -class ApplicationLayerNode(ApplicationServiceElement, ClientStateMachine): +class ApplicationLayerStateMachine(ApplicationServiceElement, ClientStateMachine): def __init__(self, address, vlan): - if _debug: ApplicationLayerNode._debug("__init__ %r %r", address, vlan) + if _debug: ApplicationLayerStateMachine._debug("__init__ %r %r", address, vlan) # build a name, save the address self.name = "app @ %s" % (address,) @@ -180,7 +180,7 @@ class ApplicationLayerNode(ApplicationServiceElement, ClientStateMachine): # build an address and save it self.address = Address(address) - if _debug: ApplicationLayerNode._debug(" - address: %r", self.address) + if _debug: ApplicationLayerStateMachine._debug(" - address: %r", self.address) # continue with initialization ApplicationServiceElement.__init__(self) @@ -209,17 +209,17 @@ class ApplicationLayerNode(ApplicationServiceElement, ClientStateMachine): # create a node, added to the network self.node = Node(self.address, vlan) - if _debug: ApplicationLayerNode._debug(" - node: %r", self.node) + if _debug: ApplicationLayerStateMachine._debug(" - node: %r", self.node) # bind the stack to the local network self.nsap.bind(self.node) def indication(self, apdu): - if _debug: ApplicationLayerNode._debug("indication %r", apdu) + if _debug: ApplicationLayerStateMachine._debug("indication %r", apdu) self.receive(apdu) def confirmation(self, apdu): - if _debug: ApplicationLayerNode._debug("confirmation %r %r", apdu) + if _debug: ApplicationLayerStateMachine._debug("confirmation %r %r", apdu) self.receive(apdu) # diff --git a/tests/test_network/test_net_1.py b/tests/test_network/test_net_1.py index 3ed474d..f77d1d0 100644 --- a/tests/test_network/test_net_1.py +++ b/tests/test_network/test_net_1.py @@ -29,7 +29,7 @@ from bacpypes.npdu import ( from ..state_machine import match_pdu, StateMachineGroup from ..time_machine import reset_time_machine, run_time_machine -from .helpers import SnifferNode, NetworkLayerNode, RouterNode +from .helpers import SnifferStateMachine, NetworkLayerStateMachine, RouterNode # some debugging _debug = 0 @@ -58,11 +58,11 @@ class TNetwork(StateMachineGroup): self.vlan1 = Network(name="vlan1", broadcast_address=LocalBroadcast()) # test device - self.td = NetworkLayerNode("1", self.vlan1) + self.td = NetworkLayerStateMachine("1", self.vlan1) self.append(self.td) # sniffer node - self.sniffer1 = SnifferNode("2", self.vlan1) + self.sniffer1 = SnifferStateMachine("2", self.vlan1) self.append(self.sniffer1) # add the network @@ -72,7 +72,7 @@ class TNetwork(StateMachineGroup): self.vlan2 = Network(name="vlan2", broadcast_address=LocalBroadcast()) # sniffer node - self.sniffer2 = SnifferNode("4", self.vlan2) + self.sniffer2 = SnifferStateMachine("4", self.vlan2) self.append(self.sniffer2) # add the network @@ -82,7 +82,7 @@ class TNetwork(StateMachineGroup): self.vlan3 = Network(name="vlan3", broadcast_address=LocalBroadcast()) # sniffer node - self.sniffer3 = SnifferNode("6", self.vlan3) + self.sniffer3 = SnifferStateMachine("6", self.vlan3) self.append(self.sniffer3) # add the network diff --git a/tests/test_network/test_net_2.py b/tests/test_network/test_net_2.py index 10f554f..73d6539 100644 --- a/tests/test_network/test_net_2.py +++ b/tests/test_network/test_net_2.py @@ -30,7 +30,7 @@ from bacpypes.npdu import ( from ..state_machine import match_pdu, StateMachineGroup, TrafficLog from ..time_machine import reset_time_machine, run_time_machine -from .helpers import SnifferNode, NetworkLayerNode, RouterNode +from .helpers import SnifferStateMachine, NetworkLayerStateMachine, RouterNode # some debugging _debug = 0 @@ -64,11 +64,11 @@ class TNetwork(StateMachineGroup): self.vlan1.traffic_log = self.traffic_log # test device - self.td = NetworkLayerNode("1", self.vlan1) + self.td = NetworkLayerStateMachine("1", self.vlan1) self.append(self.td) # sniffer node - self.sniffer1 = SnifferNode("2", self.vlan1) + self.sniffer1 = SnifferStateMachine("2", self.vlan1) self.append(self.sniffer1) # connect vlan1 to iut1 @@ -79,7 +79,7 @@ class TNetwork(StateMachineGroup): self.vlan2.traffic_log = self.traffic_log # sniffer node - self.sniffer2 = SnifferNode("4", self.vlan2) + self.sniffer2 = SnifferStateMachine("4", self.vlan2) self.append(self.sniffer2) # connect vlan2 to both routers @@ -91,7 +91,7 @@ class TNetwork(StateMachineGroup): self.vlan3.traffic_log = self.traffic_log # sniffer node - self.sniffer3 = SnifferNode("7", self.vlan3) + self.sniffer3 = SnifferStateMachine("7", self.vlan3) self.append(self.sniffer3) # connect vlan3 to the second router diff --git a/tests/test_network/test_net_3.py b/tests/test_network/test_net_3.py index 1b8bfb7..0bf434c 100644 --- a/tests/test_network/test_net_3.py +++ b/tests/test_network/test_net_3.py @@ -38,7 +38,7 @@ from ..state_machine import match_pdu, StateMachineGroup from ..time_machine import reset_time_machine, run_time_machine from .helpers import ( - SnifferNode, NetworkLayerNode, RouterNode, ApplicationLayerNode, + SnifferStateMachine, NetworkLayerStateMachine, RouterNode, ApplicationLayerStateMachine, ApplicationNode, ) @@ -69,11 +69,11 @@ class TNetwork(StateMachineGroup): self.vlan1 = Network(name="vlan1", broadcast_address=LocalBroadcast()) # test device - self.td = ApplicationLayerNode("1", self.vlan1) + self.td = ApplicationLayerStateMachine("1", self.vlan1) self.append(self.td) # sniffer node - self.sniffer1 = SnifferNode("2", self.vlan1) + self.sniffer1 = SnifferStateMachine("2", self.vlan1) self.append(self.sniffer1) # add the network @@ -86,7 +86,7 @@ class TNetwork(StateMachineGroup): self.app2 = ApplicationNode("4", self.vlan2) # sniffer node - self.sniffer2 = SnifferNode("5", self.vlan2) + self.sniffer2 = SnifferStateMachine("5", self.vlan2) self.append(self.sniffer2) # add the network diff --git a/tests/test_vlan/test_ipnetwork.py b/tests/test_vlan/test_ipnetwork.py index a825f98..82f92eb 100644 --- a/tests/test_vlan/test_ipnetwork.py +++ b/tests/test_vlan/test_ipnetwork.py @@ -346,6 +346,30 @@ class TestRouter(unittest.TestCase): csm_10_3.start_state.timeout(1).success() csm_20_2.start_state.timeout(1).success() + def test_local_broadcast(self): + """Test that a node can send a message to all of the other nodes on + the same network. + """ + if _debug: TestRouter._debug("test_local_broadcast") + + # unpack the state machines + csm_10_2, csm_10_3, csm_20_2, csm_20_3 = self.smg.state_machines + + # make a broadcast PDU from network 10 node 1 + pdu = PDU(b'data', + source=('192.168.10.2', 47808), + destination=('192.168.10.255', 47808), + ) + if _debug: TestVLAN._debug(" - pdu: %r", pdu) + + # node 10-2 sends the pdu, node 10-3 gets pdu, nodes 20-2 and 20-3 dont + csm_10_2.start_state.send(pdu).success() + csm_10_3.start_state.receive(PDU, + pduSource=('192.168.10.2', 47808), + ).success() + csm_20_2.start_state.timeout(1).success() + csm_20_3.start_state.timeout(1).success() + def test_remote_broadcast(self): """Test that a node can send a message to all of the other nodes on a different network.