ChristianTremblay/BAC0

BAC0.lite.discover() not sending who-is to every host in the local network

tontonialberto opened this issue · 5 comments

I'm developing a Linux BACnet application which has to perform a discovery over an IP network to find out the available BACnet devices (and BBMDs/Routers).
More precisely, my setup is as follows:

  • My Raspberry (ie. the client) has eth0 address 192.168.1.198

  • On that same network there are 2 BACnet routers with addresses:

    1. 192.168.1.1
    2. 192.168.1.2
  • Each BACnet router also exposes a BACnet device

My problem: I supposed that calling the BAC0 discover() method would also perform a whois() to every host on the specified local network, but doesn't seem that to work that way. In my scenario I'm not able to detect the BACnet devices unless i iterate a who-is manually over every host.

Here are some attempts I've done.

01_discovery.py

Here I try to perform a discovery without specifying any BBMD/Router. I would expect to retrieve the device ids of the 2 routers, but instead I got 0 devices back.

The code:

import BAC0

network = BAC0.lite(ip="192.168.1.198", mask=24)
network.discover()
print("Discovered devices: ", network.discoveredDevices)
network.disconnect()

The output:

2023-09-07 15:34:20,826 - INFO    | Starting BAC0 version 22.9.21 (Lite)
2023-09-07 15:34:20,827 - INFO    | Use BAC0.log_level to adjust verbosity of the app.
2023-09-07 15:34:20,827 - INFO    | Ex. BAC0.log_level('silence') or BAC0.log_level('error')
2023-09-07 15:34:20,827 - INFO    | Starting TaskManager
2023-09-07 15:34:20,829 - INFO    | Using ip : 192.168.1.198
2023-09-07 15:34:20,946 - INFO    | Starting app...
2023-09-07 15:34:20,947 - INFO    | BAC0 started
2023-09-07 15:34:20,947 - INFO    | Registered as Simple BACnet/IP App
2023-09-07 15:34:20,954 - INFO    | Update Local COV Task started
2023-09-07 15:34:24,960 - INFO    | Found those networks : set()
2023-09-07 15:34:24,962 - INFO    | No BACnet network found, attempting a simple whois using provided device instances limits (0 - 4194303)
Discovered devices:  defaultdict(<class 'int'>, {})
2023-09-07 15:34:27,978 - INFO    | Stopping all tasks
2023-09-07 15:34:27,989 - INFO    | Stopping TaskManager
2023-09-07 15:34:27,999 - INFO    | Ok all tasks stopped
2023-09-07 15:34:28,002 - INFO    | BACnet stopped

02_whois.py

Here I try to perform a sort of handcrafted discovery by calling whois() on every IP address of my local network (in the example I just use the first 2 addresses because it takes many minutes to scan the entire network). As I expected, I was able to retrieve the device ids of the routers inside the network. The issue with this approach is that it's not "uniform" with respect to calling the discover() method. Moreover, like I mentioned before, sending whois() many times takes a very large amount of time.

The code:

import BAC0
import netaddr

network = BAC0.lite(ip="192.168.1.198", mask=24)
addresses = [netaddr.IPAddress("192.168.1.1"), netaddr.IPAddress("192.168.1.2")]
for address in addresses:
    whois = network.whois(str(address))
    print(f"whois {str(address)}: {whois}")
network.disconnect()

The output:

2023-09-07 15:34:51,478 - INFO    | Starting BAC0 version 22.9.21 (Lite)
2023-09-07 15:34:51,478 - INFO    | Use BAC0.log_level to adjust verbosity of the app.
2023-09-07 15:34:51,478 - INFO    | Ex. BAC0.log_level('silence') or BAC0.log_level('error')
2023-09-07 15:34:51,479 - INFO    | Starting TaskManager
2023-09-07 15:34:51,481 - INFO    | Using ip : 192.168.1.198
2023-09-07 15:34:51,599 - INFO    | Starting app...
2023-09-07 15:34:51,601 - INFO    | BAC0 started
2023-09-07 15:34:51,601 - INFO    | Registered as Simple BACnet/IP App
2023-09-07 15:34:51,608 - INFO    | Update Local COV Task started
whois 192.168.1.1: [('192.168.1.1', 1050626)]
whois 192.168.1.2: [('192.168.1.2', 1050625)]
2023-09-07 15:34:57,623 - INFO    | Stopping all tasks
2023-09-07 15:34:57,629 - INFO    | Stopping TaskManager
2023-09-07 15:34:57,634 - INFO    | Ok all tasks stopped
2023-09-07 15:34:57,636 - INFO    | BACnet stopped

03_discovery_bbmd.py

Here I try to perform a discovery by specifying a BBMD address. As expected, I was able to retrieve not only the routers' ids but also the devices in other networks. The issue with this approach is that I cannot use it in a scenario in which I don't know the BBMD address.

The code:

import BAC0

network = network = BAC0.lite(ip="192.168.1.198", mask=24, bbmdAddress="192.168.1.1", bbmdTTL=30)
network.discover()
print("Discovered devices: ", network.discoveredDevices)
network.disconnect()

The output:

2023-09-07 15:35:16,914 - INFO    | Starting BAC0 version 22.9.21 (Lite)
2023-09-07 15:35:16,914 - INFO    | Use BAC0.log_level to adjust verbosity of the app.
2023-09-07 15:35:16,914 - INFO    | Ex. BAC0.log_level('silence') or BAC0.log_level('error')
2023-09-07 15:35:16,915 - INFO    | Starting TaskManager
2023-09-07 15:35:16,917 - INFO    | Using ip : 192.168.1.198
2023-09-07 15:35:17,033 - INFO    | Starting app...
2023-09-07 15:35:17,034 - INFO    | BAC0 started
2023-09-07 15:35:17,035 - INFO    | Registered as Foreign Device
2023-09-07 15:35:17,041 - INFO    | Update Local COV Task started
2023-09-07 15:35:19,053 - INFO    | 192.168.1.1 router to [2]
2023-09-07 15:35:19,181 - INFO    | 192.168.1.2 router to [1]
2023-09-07 15:35:21,047 - INFO    | Addr : 192.168.1.2
2023-09-07 15:35:21,079 - INFO    | 192.168.1.2 routing table
2023-09-07 15:35:21,080 - INFO    |     100 1 bytearray(b'')
2023-09-07 15:35:21,081 - INFO    |     10 1 bytearray(b'')
2023-09-07 15:35:21,081 - INFO    |     10 1 bytearray(b'')
2023-09-07 15:35:21,082 - INFO    |     2 1 bytearray(b'')
2023-09-07 15:35:21,082 - INFO    |     1 2 bytearray(b'')
2023-09-07 15:35:22,408 - INFO    | 192.168.1.1 router to [2]
2023-09-07 15:35:22,413 - INFO    | 192.168.1.2 router to [1]
2023-09-07 15:35:23,051 - INFO    | Found those networks : {1, 2}
2023-09-07 15:35:23,052 - INFO    | Discovering network 1
2023-09-07 15:35:25,711 - INFO    | 192.168.1.1 router to [2]
2023-09-07 15:35:25,716 - INFO    | 192.168.1.2 router to [1]
2023-09-07 15:35:26,062 - INFO    | Discovering network 2
Discovered devices:  defaultdict(<class 'int'>, {('1:0x011c', 2098203): 1, ('1:0x0115', 2098197): 1, ('1:0x0117', 2098199): 1, ('1:0x0118', 2098200): 1, ('1:0x0119', 2098201): 1, ('1:0x0116', 2098198): 1, ('1:0x0107', 2098183): 1, ('1:0x010e', 2098190): 1, ('1:0x0106', 2098182): 1, ('1:0x0104', 2098180): 1, ('1:0x010f', 2098191): 1, ('1:0x0112', 2098194): 1, ('1:0x0102', 2098178): 1, ('1:0x010b', 2098187): 1, ('1:0x0105', 2098181): 1, ('1:0x010a', 2098186): 1, ('1:0x010d', 2098189): 1, ('1:0x0114', 2098196): 1, ('1:0x0111', 2098193): 1, ('1:0x0113', 2098195): 1, ('1:0x0110', 2098192): 1, ('1:0x010c', 2098188): 1, ('1:0x0101', 2098177): 1, ('1:0x011a', 2098202): 1, ('1:0x0109', 2098185): 1, ('2:0x0118', 2099224): 1, ('2:0x0116', 2099222): 1, ('2:0x0117', 2099223): 1, ('2:0x010d', 2099213): 1, ('2:0x010b', 2099211): 1, ('2:0x0108', 2099208): 1, ('2:0x0113', 2099219): 1, ('2:0x0105', 2099205): 1, ('2:0x0112', 2099218): 1, ('2:0x010a', 2099210): 1, ('2:0x0102', 2099202): 1, ('2:0x0111', 2099217): 1, ('2:0x0104', 2099204): 1, ('2:0x0110', 2099216): 1, ('2:0x0101', 2099201): 1, ('2:0x0107', 2099207): 1, ('2:0x0119', 2099226): 1, ('2:0x010e', 2099214): 1, ('2:0x011b', 2099227): 1, ('2:0x010c', 2099212): 1, ('2:0x0109', 2099209): 1})
2023-09-07 15:35:29,076 - INFO    | Stopping all tasks
2023-09-07 15:35:29,081 - INFO    | Stopping TaskManager
2023-09-07 15:35:29,087 - INFO    | Ok all tasks stopped
2023-09-07 15:35:29,089 - INFO    | BACnet stopped

My final doubt: I think that discover() should perform a broadcast who-is like I did in scenario 02. Otherwise it's not that useful.
Any advice on how to accomplish my needs?

Thanks in advance,

Alberto

There is a global_broadcast switch that you can pass to the function.

I removed "default" broadcast who_is a long time ago because it is not a good idea to make that de facto on networks. It will generate à I-Am storm and if your network is gigantic... some people will not be super happy as the network may fail...

Use with care

Thanks for the fast response.

I've also made many attemps of scenario 01 setting also global_broadcast=True but same result unfortunately.

I forgot to emphasize the fact that a single who-is request (like I did in experiment 02) takes 4-5 seconds to complete. Is there a whois() timeout parameter that can be specified somewhere?

My first reading was a little diagonal... everything is on the same subnet... you shouldn't have trouble and you shouldn't need the broadcast

Here is what I get on Windows

In [1]: import BAC0

In [2]: bacnet = BAC0.lite(ip='192.168.211.24', mask=24)
2023-09-09 17:35:44,266 - INFO    | Starting BAC0 version 22.9.21 (Lite)
2023-09-09 17:35:44,267 - INFO    | Use BAC0.log_level to adjust verbosity of the app.
2023-09-09 17:35:44,267 - INFO    | Ex. BAC0.log_level('silence') or BAC0.log_level('error')
2023-09-09 17:35:44,267 - INFO    | Starting TaskManager
2023-09-09 17:35:44,268 - INFO    | Using ip : 192.168.211.24
2023-09-09 17:35:48,210 - INFO    | Starting app...
2023-09-09 17:35:48,210 - INFO    | BAC0 started
2023-09-09 17:35:48,210 - INFO    | Registered as Simple BACnet/IP App
2023-09-09 17:35:48,227 - INFO    | Update Local COV Task started

In [3]: bacnet.discover()
2023-09-09 17:35:52,281 - WARNING | 192.168.211.4 Rejected message to network (reason : It is an unknown network layer message)
2023-09-09 17:35:52,297 - WARNING | 192.168.211.30 Rejected message to network (reason : It is an unknown network layer message)
2023-09-09 17:35:52,299 - INFO    | 192.168.210.253 network number is 1
2023-09-09 17:35:53,609 - INFO    | 192.168.211.4 router to [304, 211]
2023-09-09 17:35:53,616 - INFO    | 192.168.211.30 router to [303]
2023-09-09 17:35:53,628 - INFO    | 192.168.210.253 router to [2]
2023-09-09 17:35:55,601 - INFO    | Addr : 192.168.210.253
2023-09-09 17:35:55,663 - INFO    | 192.168.210.253 routing table
2023-09-09 17:35:55,663 - INFO    |     2 1 bytearray(b'')
2023-09-09 17:35:55,663 - INFO    |     1 2 bytearray(b'')
2023-09-09 17:35:57,607 - INFO    | Found those networks : {1, 2, 303, 304, 211}
2023-09-09 17:35:57,607 - INFO    | Discovering network 304
2023-09-09 17:36:00,624 - INFO    | Discovering network 1
2023-09-09 17:36:03,751 - INFO    | Discovering network 2
2023-09-09 17:36:07,695 - INFO    | Discovering network 211
2023-09-09 17:36:11,062 - INFO    | Discovering network 303
Out[3]:
[('304:4', 5404),
 ('304:5', 5405),
 ('192.168.211.63', 500),
 ('192.168.211.201', 3056681),
 ('192.168.211.4', 2114),
 ('192.168.211.30', 81113),
 ('192.168.210.253', 210253),
 ('2:5', 5),
 ('303:7', 5007),
 ('303:8', 72008),
 ('303:9', 5209),
 ('303:10', 60010),
 ('303:4', 5204)]

Maybe your Pi has some kind of firewall configured ? Something that would block some messages... ?

I eventually managed to call discover() successfully. As you mentioned, there was an issue with ufw on my Raspberry which blocked incoming traffic to port 47808. The discovery process took 10 seconds in total to complete.

Thanks!