bacpypes icon indicating copy to clipboard operation
bacpypes copied to clipboard

Readproperty with NATRouter in the chain

Open DB-CL opened this issue 2 years ago • 6 comments

Hello Joel, I hope you're doing great.

Here I am again with my NAT Related questions ;)

I'm using the NATRouter.py pretty much as the example in your github repository for a year now and it's doing a great job. I'm using C++ clients or Java clients to interract with my NATRouter and it's working exactly as expected. Recently I tried to create a Python client with bacpypes, and I'm having a lot of troubles to communicate with the NATRouter. Actually, I didn't manage to make it work.

Here is my use case : Untitled drawing

  • My clients (C++, Java and now python) are on the server side, with IP like 10.0.0.X
  • The packets are going this way 10.0.0.X <=> 172.16.0.2:37755 <=> 192.168.0.X:47808 (I have a NAT rule on the 4G router to map the packets originally destinated to 10.0.0.20:37755 to 172.16.0.2:37755 )
  • The NATRouter is launched this way : /usr/local/share/bacpypes/bin/python /usr/local/share/bacpypes/NATRouter.py 192.168.0.1/24 47808 1 172.16.0.2 37755 2 --route-aware
  • The read command used with the Python client is this one: read 1:192.168.0.51:47808 2:16 presentValue

With our C++ clients (with steve kargs library) or Java clients (BACnet4J) we can just use 10.0.0.20 as a BBMD on the port 37755, and we are able to read and write everything on the BACnet network directly from 10.0.0.X. No issues at all. Something is to be noted : the C++ client and Java client has no idea of the bacnet network "1" or "2". They use device's id to communicate (not IP) and it works.

But when I try the ReadPropertyForeign.py of the bacpypes library, I cannot achieve any read. I feel like I'm missing something but I don't know what exactly.

Additional information

BACPypes.ini

My BACPypes.ini on client side (assuming my IP is 10.0.0.1) is declared this way :

[BACpypes]
objectName: Betelgeuse
address: 10.0.0.1/24:47808
objectIdentifier: 600
maxApduLengthAccepted: 1024
segmentationSupported: segmentedBoth
vendorIdentifier: 15
foreignBBMD: 10.0.0.20:37755
foreignTTL: 30

The BACPypes.ini on the NATRouter

[BACpypes]
objectName: Rigel
address: 172.16.0.2/24
objectIdentifier: 599
maxApduLengthAccepted: 1024
segmentationSupported: segmentedBoth
vendorIdentifier: 15

The read command used with the

Wireshard oddities

One very strange behavior has been witnessed on wireshark. When I run the python client and right after the command I have something like this on the client :

10.0.0.1:47808 -> 10.0.0.20:37755 BVLC
10.0.0.20:37755 -> 10.0.0.1:47808 BVLC
---- here I launch the command read
10.0.0.1:47808 -> 10.0.0.20:37755 Who-is-router-to-network
10.0.0.20:37755 -> 10.0.0.1:47808 I-Am-router-to-network
10.0.0.1:47808 -> 10.0.0.20:37755 readProperty [ 1 ] analog-value,16 present value
10.0.0.1:47808 -> 10.0.0.20:37755 readProperty [ 1 ] analog-value,16 present value
10.0.0.1:47808 -> 10.0.0.20:37755 readProperty [ 1 ] analog-value,16 present value

And the response is never coming back.

When I tcpdump on the NATRouter I have this output :

23:57:21.270876 enp2s0 In  IP 172.16.0.31.47808 > 172.16.0.2.37755: UDP, length 6    <<< BVLC IN
23:57:21.272218 enp2s0 Out IP 172.16.0.2.37755 > 172.16.0.31.47808: UDP, length 6  <<< BVLC OUT
23:57:22.090965 enp2s0 In  IP 172.16.0.31.47808 > 172.16.0.2.37755: UDP, length 9  <<< Who is router 
23:57:22.093864 enp2s0 Out IP 172.16.0.2.37755 > 172.16.0.31.47808: UDP, length 9 <<< I am router
23:57:22.094023 enp2s0 Out IP 172.16.0.2.37755 > 172.16.0.31.48000: UDP, length 15 <<< ???
23:57:22.094168 enp2s0 Out IP 172.16.0.2.37755 > 172.16.0.31.1164: UDP, length 15 <<< ???
23:57:22.094310 enp2s0 Out IP 172.16.0.2.37755 > 172.16.0.31.1165: UDP, length 15 <<< ???
23:57:22.143543 enp2s0 In  IP 172.16.0.31.47808 > 172.16.0.2.37755: UDP, length 27 <<< readProperty
23:57:25.088839 enp2s0 In  IP 172.16.0.31.47808 > 172.16.0.2.37755: UDP, length 27 <<< readProperty

And nothing is transmitted to the bacnet network (nothing at all, although the tcpdump is on udp, any interface)

BUT I noticed something really odd. Sometimes, the BBMD on the BACnet network just send "Unconfirmed REQ Who-is" (I don't know why) and if these request are sent between the BVLC and the read command, then the ReadPropertyForeign.py don't do the "Who-is-router-to-network" and use the wrong IP to answer (172.16.0.2), like that :

10.0.0.1:47808 -> 10.0.0.20:37755 BVLC
10.0.0.20:37755 -> 10.0.0.1:47808 BVLC
10.0.0.20:37755 -> 10.0.0.1:47808 Unconfirmed-REQ who-Is 40629-40629
10.0.0.20:37755 -> 10.0.0.1:47808 Unconfirmed-REQ who-Is 30705-30705
10.0.0.20:37755 -> 10.0.0.1:47808 Unconfirmed-REQ who-Is 30711-30711
---- here I launch the command read
10.0.0.1:47808 -> 172.16.0.2:37755 readProperty [ 1 ] analog-value,16 present value
10.0.0.1:47808 -> 172.16.0.2:37755 readProperty [ 1 ] analog-value,16 present value
10.0.0.1:47808 -> 172.16.0.2:37755 readProperty [ 1 ] analog-value,16 present value

I have no idea why it does that.

On server side, it looks almost the same, with all the Unconfirmed-REQ in addition and the readProperty does not arrive since the IP is wrong on client side.

I don't think this is the root cause of my issues, because even when the IP is the right one, the NATRouter does not emit any packet on the BACnet network (192.168...), so I don't have any answer.

Any idea on what may be going on here?

Thanks

DB-CL avatar Mar 04 '22 23:03 DB-CL

Okay, I have simplified my scenario and it's kinda working. I managed to get Internet on my NATRouter client, so I installed a VPN client and now I have this scenario :

Untitled drawing (1)

I have also modified a little bit my setup :

  • /usr/local/share/bacpypes/bin/python /usr/local/share/bacpypes/NATRouter.py 192.168.0.1/24 47808 1 10.0.0.20 37755 2 --route-aware
  • On the BACPipes.ini of the NATRouter I've modified the address line to this : address: 192.168.0.1/24 but as far as I can see, this address doesn't seem to matter much because when I put address: 10.0.0.20/24 it works as well

Now it's working, but I still have the bug with the Unconfirmed-REQ. It does not look NAT Related so I created another issue : https://github.com/JoelBender/bacpypes/issues/450

Maybe two networks was too much to go through, but I really wonder why Java and C++ implementations have no issues to deal with that and why bacpypes is having such a hard time. Especially when it's bacpypes saving my ass with the NATRouter when the other implentation are totally dry on NAT scenarios. I would love to read your thoughts on this, because I feel like I still have a lot to learn on this matter.

DB-CL avatar Mar 05 '22 17:03 DB-CL

There should be only one BBMD per subnet. If both can ping themselves, your bacpypes app can be one. In its table you fill the bbmd in the device subnet. And vice-versa.

For what I see, bbmd2,3 can cause troubles.

ChristianTremblay avatar Mar 06 '22 01:03 ChristianTremblay

Hi Christian,

Thank you for your response.

As far as I can understand, beeing a BBMD in the BACnet network looks like a mandatory condition for the NATRouter to work.

So if my understanding is ok, that would be impossible to work with a BACnet network as a foreign device from the outside of the network (another subnet), where there is already another BBMD (not NAT friendly) in the BACnet network ? Am I right with this assertion ?

Thanks

DB-CL avatar Mar 06 '22 11:03 DB-CL

I'm not sure. But I think you could have a bacpypes app used as a foreign device registered on another bbmd, on another subnet.

I see FD as "exceptions"... like this one must be treated differently.

ChristianTremblay avatar Mar 06 '22 13:03 ChristianTremblay

First, your second configuration is closer to what is expected. Here are some thoughts:

  1. The NATRouter.py application does not use a BACpypes configuration file, all of the parameters it needs are on the command line.
  2. The application happens to be a BBMD on the local network (192.168.0.0/24) but doesn't have to be in the general sense, it is an IP-to-IP router. I set up this sample to match Figure J-8.
  3. The fact that there are multiple BBMD's on your local network can be irritating and confusing because it can result in additional useless traffic at best, or device-address-binding problems and mucked up topology information (weird copies of I-Am-Router-To-Network appearing where they shouldn't) which is a nightmare. I'm going to assume that the BBMD1 and it's peers have no broadcast distribution table and have foreign device registration turned off, which makes them "normal" B/IPv4 nodes.
  4. If you are using a VPN tunnel (10.0.0.0/xx) then there is no NAT going on that the NATRouter can see. The source address from the server is the same as the destination address to send the response, you'll get simpler behavior by using the IP2IPRouter.py application.
  5. In your first example where the "Router 4G" was providing the NAT service, you have three BACnet networks, one is on the Internet side (10.0.0.0/xx), one is on the local network (172.16.0.0/24), and another one is also a local network (192.168.0.0/24) so packets being received on a particular socket assume the BACnet network number, which might not be what the other clients are expecting. You would need a slightly different version of NATRouter to handle this situation.
  6. The use of 172.16.0.0/24 as a network is a warning flag for me about running in a container, which is a NAT-like environment but isn't as friendly to UDP (connectionless) as it is for TCP (connection oriented).
  7. Your assertion is not correct, you can be a foreign device on the Internet side and send your foreign device registration request to the outside address of the NAT box, which will forward it to the BBMD on the inside. But there is a "gotcha" -- the BBMD cannot be on the local network, it has to also be configured as BACnet router to the local network. All of the traffic (unicast and request to broadcast a PDU) from the foreign device will be sent to the BBMD/router to be forwarded to other devices because the response cannot be directly sent back to the foreign device, it can only be sent there by the BBMD-router. The NATRouter is your friend in this case.
  8. Wireshark (and especially tshark) can hide some important details inside the layers of a packet, like "was this unicast directly to me" or "forwarded to me" in the BVLL.

JoelBender avatar Mar 06 '22 21:03 JoelBender

Thank you both for your inputs !

  1. OK, make sense now ^^ (Indeed, I can see that the INI file is not used in the code ... should have checked before, sry)
  2. OK, I will dig in that direction
  3. I've already seen weird copies of "I-am-router" ... I think we are in this configuration 4 and 5. I understand what you say and I agree with the "no nat" of my second scenario, but how should have I done the first one to match the NATRouter expected use case ?
  4. Not sure I understand your point. No containers involved, the IPs here are totally made up, my real world scenario have much complicated CIDR (way less readable, so I took 3 totally different subnets)
  5. Yes I got this "gotcha" a year ago ^^ and that's why we were using NATRouter. But now I'm realizing there is a gotcha in the gotcha : the NATRouter must be the only one BBMD in the network, and it is not the case. I have no word to say on what should be on the network or what shouldn't be. I'm only here to "talk" with BACnet from the outside of the network. I'm allowed to put one device in the network, that's it.
  6. Noted

I have an additional question. What should be my best option to talk to a BACnet network from the outside (not beeing able to do modifications on this network). Until today, I was trying to be as BACnetish as possible by using NATRouter and foreign device registration. But I'm really starting to think that if from the very beginning I had wrapped a local BACnet client in an HTTP API (or MQTT), and mapped every BACnet command I use ... Today I would have a way better solution because no matter the quantity of firewalls or networks to cross, I would always find a way to do it. What would you do in my position ?

Thanks again !

DB-CL avatar Mar 07 '22 08:03 DB-CL