Added Who-Is-Router process for Notification Class recipient unknown router addresses. (#1243)

* Added Who-Is-Router-To-Network process in basic Notification Class when recipient address is missing the router MAC address.

* Added buffer_length parameter to octet string buffer decode functions

* Fixed BACnet address handling for I-Am-Router address updating the recipient list address for the next hop router.

* Changed VMAC encoding and decoding to use octet string buffer API to reduce stack RAM.
This commit is contained in:
Steve Karg
2026-02-25 09:58:38 -06:00
committed by GitHub
parent e11cd319da
commit cf4f62f7e0
14 changed files with 225 additions and 69 deletions
+80 -30
View File
@@ -15,7 +15,6 @@
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/bacstr.h"
#include "bacnet/bactext.h"
#include "bacnet/bacaddr.h"
@@ -203,6 +202,33 @@ bool bacnet_address_init(
return true;
}
/**
* @brief Set the #BACNET_ADDRESS of the next-hop router to the MAC of the dest
* @param dest - #BACNET_ADDRESS to be configured
* @param router - #BACNET_ADDRESS MAC to be copied from
*/
void bacnet_address_router_set(
BACNET_ADDRESS *dest, const BACNET_ADDRESS *router)
{
uint8_t i = 0;
uint8_t mac_len = 0;
if (dest && router) {
mac_len = router->mac_len;
if (mac_len > MAX_MAC_LEN) {
mac_len = MAX_MAC_LEN;
}
dest->mac_len = mac_len;
for (i = 0; i < MAX_MAC_LEN; i++) {
if (i < mac_len) {
dest->mac[i] = router->mac[i];
} else {
dest->mac[i] = 0;
}
}
}
}
/**
* @brief Compare two #BACNET_MAC_ADDRESS values
* @param dest - #BACNET_MAC_ADDRESS to be compared
@@ -385,7 +411,8 @@ int bacnet_address_decode(
int apdu_len = 0;
uint8_t i = 0;
BACNET_UNSIGNED_INTEGER snet = 0;
BACNET_OCTET_STRING mac_addr = { 0 };
uint32_t mac_addr_len = 0;
uint8_t mac_addr[MAX_MAC_LEN] = { 0 };
if (!apdu) {
return BACNET_STATUS_ERROR;
@@ -406,33 +433,47 @@ int bacnet_address_decode(
}
apdu_len += len;
/* mac address as an octet-string */
len = bacnet_octet_string_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &mac_addr);
len = bacnet_octet_string_buffer_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &mac_addr[0], sizeof(mac_addr),
&mac_addr_len);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (mac_addr_len > MAX_MAC_LEN) {
return BACNET_STATUS_ERROR;
}
if (snet) {
if (value) {
if (mac_addr.length > sizeof(value->adr)) {
if (mac_addr_len > sizeof(value->adr)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->len = mac_addr.length;
value->len = (uint8_t)mac_addr_len;
/* copy address */
for (i = 0; i < value->len; i++) {
value->adr[i] = mac_addr.value[i];
value->adr[i] = mac_addr[i];
}
/* zero the router address */
value->mac_len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
value->mac[i] = 0;
}
}
} else {
if (value) {
if (mac_addr.length > sizeof(value->mac)) {
if (mac_addr_len > sizeof(value->mac)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->mac_len = mac_addr.length;
value->mac_len = (uint8_t)mac_addr_len;
/* copy address */
for (i = 0; i < value->mac_len; i++) {
value->mac[i] = mac_addr.value[i];
value->mac[i] = mac_addr[i];
}
/* zero the device behind a router address */
value->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
value->adr[i] = 0;
}
}
}
@@ -581,24 +622,21 @@ int decode_context_bacnet_address(
int bacnet_vmac_entry_data_encode(uint8_t *apdu, const BACNET_VMAC_ENTRY *value)
{
int apdu_len = 0, len;
BACNET_OCTET_STRING address = { 0 };
if (!value) {
return 0;
}
/* virtual-mac-address [0] OctetString */
octetstring_init(
&address, value->virtual_mac_address.adr,
len = encode_context_octet_string_buffer(
apdu, 0, value->virtual_mac_address.adr,
value->virtual_mac_address.len);
len = encode_context_octet_string(apdu, 0, &address);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* native-mac-address */
octetstring_init(
&address, value->native_mac_address, value->native_mac_address_len);
len = encode_context_octet_string(apdu, 1, &address);
len = encode_context_octet_string_buffer(
apdu, 1, value->native_mac_address, value->native_mac_address_len);
apdu_len += len;
return apdu_len;
@@ -647,44 +685,56 @@ int bacnet_vmac_entry_decode(
int len = 0;
int apdu_len = 0;
size_t i = 0;
BACNET_OCTET_STRING mac_addr = { 0 };
BACNET_VMAC_ENTRY entry = { 0 };
uint32_t mac_addr_len = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* virtual-mac-address [0] OctetString */
len = bacnet_octet_string_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &mac_addr);
len = bacnet_octet_string_buffer_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, entry.virtual_mac_address.adr,
sizeof(entry.virtual_mac_address.adr), &mac_addr_len);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (value) {
if (mac_addr.length > sizeof(value->virtual_mac_address.adr)) {
if (mac_addr_len > sizeof(value->virtual_mac_address.adr)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->virtual_mac_address.len = mac_addr.length;
value->virtual_mac_address.len = mac_addr_len;
/* copy address */
for (i = 0; i < mac_addr.length; i++) {
value->virtual_mac_address.adr[i] = mac_addr.value[i];
for (i = 0; i < sizeof(value->virtual_mac_address.adr); i++) {
if (i < mac_addr_len) {
value->virtual_mac_address.adr[i] =
entry.virtual_mac_address.adr[i];
} else {
value->virtual_mac_address.adr[i] = 0;
}
}
}
apdu_len += len;
/* native-mac-address [1] OctetString */
len = bacnet_octet_string_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &mac_addr);
len = bacnet_octet_string_buffer_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, entry.native_mac_address,
sizeof(entry.native_mac_address), &mac_addr_len);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (value) {
if (mac_addr.length > sizeof(value->native_mac_address)) {
if (mac_addr_len > sizeof(value->native_mac_address)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->native_mac_address_len = mac_addr.length;
value->native_mac_address_len = mac_addr_len;
/* copy address */
for (i = 0; i < mac_addr.length; i++) {
value->native_mac_address[i] = mac_addr.value[i];
for (i = 0; i < sizeof(value->native_mac_address); i++) {
if (i < mac_addr_len) {
value->native_mac_address[i] = entry.native_mac_address[i];
} else {
value->native_mac_address[i] = 0;
}
}
}
apdu_len += len;
+3
View File
@@ -58,6 +58,9 @@ bool bacnet_address_init(
const BACNET_MAC_ADDRESS *mac,
uint16_t dnet,
const BACNET_MAC_ADDRESS *adr);
BACNET_STACK_EXPORT
void bacnet_address_router_set(
BACNET_ADDRESS *dest, const BACNET_ADDRESS *router);
BACNET_STACK_EXPORT
bool bacnet_address_mac_same(
+12 -2
View File
@@ -2438,6 +2438,7 @@ int bacnet_octet_string_buffer_decode(
* or NULL for length only.
* @param buffer_size - number of bytes in the buffer where the
* decoded value is stored
* @param buffer_length - number of bytes decoded into the buffer
*
* @return number of bytes decoded, zero if tag mismatch,
* or #BACNET_STATUS_ERROR (-1) if malformed
@@ -2446,7 +2447,8 @@ int bacnet_octet_string_buffer_application_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t *buffer,
size_t buffer_size)
size_t buffer_size,
uint32_t *buffer_length)
{
int apdu_len = BACNET_STATUS_ERROR;
int len = 0;
@@ -2464,6 +2466,9 @@ int bacnet_octet_string_buffer_application_decode(
&apdu[len], apdu_size - apdu_len, tag.len_value_type, buffer,
buffer_size);
if (len >= 0) {
if (buffer_length) {
*buffer_length = tag.len_value_type;
}
apdu_len += len;
} else {
apdu_len = BACNET_STATUS_ERROR;
@@ -2488,6 +2493,7 @@ int bacnet_octet_string_buffer_application_decode(
* or NULL for length only.
* @param buffer_size - number of bytes in the buffer where the
* decoded value is stored
* @param buffer_length - number of bytes decoded into the buffer
*
* @return number of bytes decoded, or zero if tag mismatch, or
* #BACNET_STATUS_ERROR (-1) if malformed
@@ -2497,7 +2503,8 @@ int bacnet_octet_string_buffer_context_decode(
uint32_t apdu_size,
uint8_t tag_value,
uint8_t *buffer,
size_t buffer_size)
size_t buffer_size,
uint32_t *buffer_length)
{
int apdu_len = BACNET_STATUS_ERROR;
int len = 0;
@@ -2514,6 +2521,9 @@ int bacnet_octet_string_buffer_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, tag.len_value_type,
buffer, buffer_size);
if (len >= 0) {
if (buffer_length) {
*buffer_length = tag.len_value_type;
}
apdu_len += len;
} else {
apdu_len = BACNET_STATUS_ERROR;
+4 -2
View File
@@ -433,14 +433,16 @@ int bacnet_octet_string_buffer_application_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t *buffer,
size_t buffer_size);
size_t buffer_size,
uint32_t *buffer_length);
BACNET_STACK_EXPORT
int bacnet_octet_string_buffer_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_value,
uint8_t *buffer,
size_t buffer_size);
size_t buffer_size,
uint32_t *buffer_length);
BACNET_STACK_EXPORT
int encode_octet_string(uint8_t *apdu, const BACNET_OCTET_STRING *octet_string);
+22
View File
@@ -125,6 +125,28 @@ void bacnet_recipient_address_set(
}
}
/**
* @brief Inspect the BACnetRecipient data structure for valid router address
* @param recipient - BACnetRecipient structure
* @return true if BACnetRecipient is a valid address
*/
bool bacnet_recipient_address_router_unknown(const BACNET_RECIPIENT *recipient)
{
bool status = false;
if (recipient) {
if ((recipient->tag == BACNET_RECIPIENT_TAG_ADDRESS) &&
(recipient->type.address.net != 0) &&
(recipient->type.address.net != BACNET_BROADCAST_NETWORK) &&
(recipient->type.address.len != 0) &&
(recipient->type.address.mac_len == 0)) {
status = true;
}
}
return status;
}
/**
* @brief Copy the BACnetRecipient complex data from src to dest
* @param src - BACnetRecipient 1 structure
+2
View File
@@ -98,6 +98,8 @@ void bacnet_recipient_copy(BACNET_RECIPIENT *dest, const BACNET_RECIPIENT *src);
BACNET_STACK_EXPORT
bool bacnet_recipient_same(
const BACNET_RECIPIENT *r1, const BACNET_RECIPIENT *r2);
BACNET_STACK_EXPORT
bool bacnet_recipient_address_router_unknown(const BACNET_RECIPIENT *recipient);
BACNET_STACK_EXPORT
void bacnet_recipient_device_wildcard_set(BACNET_RECIPIENT *recipient);
+35 -4
View File
@@ -90,6 +90,33 @@ void Notification_Class_Writable_Property_List(
}
}
/**
* @brief Handle I-Am router to network for out of network recipients
* @param src - source address of the router
* @param network - network number of the router
*/
static void Notification_Class_I_Am_Router_To_Network_Handler(
BACNET_ADDRESS *src, uint16_t network)
{
NOTIFICATION_CLASS_INFO *notification;
BACNET_DESTINATION *destination;
BACNET_RECIPIENT *recipient;
unsigned i, j;
for (i = 0; i < MAX_NOTIFICATION_CLASSES; i++) {
notification = &NC_Info[i];
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
destination = &notification->Recipient_List[j];
recipient = &destination->Recipient;
/* update recipient addresses for this network */
if ((recipient->tag == BACNET_RECIPIENT_TAG_ADDRESS) &&
(recipient->type.address.net == network)) {
bacnet_address_router_set(&recipient->type.address, src);
}
}
}
}
void Notification_Class_Init(void)
{
uint8_t NotifyIdx = 0;
@@ -111,6 +138,8 @@ void Notification_Class_Init(void)
bacnet_destination_default_init(destination);
}
}
npdu_set_i_am_router_to_network_handler(
Notification_Class_I_Am_Router_To_Network_Handler);
return;
}
@@ -727,12 +756,11 @@ void Notification_Class_common_reporting_function(
"Notification Class[%u]: send notification to ADDR\n",
event_data->notificationClass);
/* send notification to the address indicated */
bacnet_address_copy(&dest, &pBacDest->Recipient.type.address);
if (pBacDest->ConfirmedNotify == true) {
if (address_get_device_id(&dest, &device_id)) {
Send_CEvent_Notify(device_id, event_data);
}
Send_CEvent_Notify_Address(
Event_Buffer, sizeof(Event_Buffer), event_data, &dest);
} else {
dest = pBacDest->Recipient.type.address;
Send_UEvent_Notify(Event_Buffer, event_data, &dest);
}
}
@@ -764,6 +792,9 @@ void Notification_Class_find_recipient(void)
address of device is unknown. */
Send_WhoIs(device_id, device_id);
}
} else if (bacnet_recipient_address_router_unknown(recipient)) {
Send_Who_Is_Router_To_Network(
NULL, recipient->type.address.net);
}
}
}
+7 -2
View File
@@ -683,6 +683,7 @@ int bvlc_broadcast_distribution_table_decode(
int len = 0, apdu_len = 0;
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry = NULL;
BACNET_HOST_N_PORT_MINIMAL host_n_port = { 0 };
uint32_t address_length = 0;
/* default reject code */
if (error_code) {
@@ -700,7 +701,6 @@ int bvlc_broadcast_distribution_table_decode(
apdu_len += len;
bdt_entry->dest_address.port = host_n_port.port;
if (host_n_port.tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) {
bdt_entry->valid = true;
bdt_entry->dest_address.address[0] =
host_n_port.host.ip_address.address[0];
bdt_entry->dest_address.address[1] =
@@ -719,8 +719,13 @@ int bvlc_broadcast_distribution_table_decode(
len = bacnet_octet_string_buffer_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1,
bdt_entry->broadcast_mask.address,
sizeof(bdt_entry->broadcast_mask.address));
sizeof(bdt_entry->broadcast_mask.address), &address_length);
if (len > 0) {
if (address_length == sizeof(bdt_entry->broadcast_mask.address)) {
bdt_entry->valid = true;
} else {
bdt_entry->valid = false;
}
apdu_len += len;
} else {
if (error_code) {