Files
bacnet_stack/src/bacnet/basic/npdu/h_npdu.c
T
Kari Argillander 369da70f2a Strip tabs and trailing white spaces, and fix end of files (#748)
* format: Strip trailing whitespaces

We want to get rid of trailing whitespaces completly as they make just git
noice. Much better to start using automated tools to get rid of them once and
not getting them back again. This way git history will be cleaner and review
easier.

Commit was generated with:

    pre-commit run --all-files trailing-whitespace

* format: Files should have exactly one new line end of them

It is good practice that every file has one new line. It is not now days so
mandatory but it also is not nice if file has lot of newlines end of it. We will
use pre-commit which takes automatically care about this so let's fix all.

Commit was generated with:

    pre-commit run --all-files end-of-file-fixer

* format: Convert tabs to spaces

Project mostly use spaces over tabs. When mixing tabs and spaces this usually
makes formatting issues and also when changing those in commits it will make lot
of git noise. We will force spaces most of the time and use pre-commit to fix.

Commit was generated with:

    pre-commit run --all-files remove-tabs

---------

Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
2024-08-25 14:13:57 -05:00

305 lines
11 KiB
C

/**
* @file
* @brief Handling for the basic BACnet NPDU layer of the BACnet stack
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2008
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdbool.h>
#include <stdint.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacaddr.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/npdu.h"
#include "bacnet/apdu.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/datalink/datalink.h"
#if PRINT_ENABLED
#include <stdio.h>
#endif
static uint16_t Local_Network_Number;
static uint8_t Local_Network_Number_Status = NETWORK_NUMBER_LEARNED;
/**
* @brief get the local network number
* @return local network number
*/
uint16_t npdu_network_number(void)
{
return Local_Network_Number;
}
/**
* @brief set the local network number
* @param net - local network number
*/
void npdu_network_number_set(uint16_t net)
{
Local_Network_Number = net;
}
/**
* @brief send the local network number is message
* @param dst - the destination address for the message
* @param net - local network number
* @param status - 0=learned, 1=assigned
* @return number of bytes sent
*/
int npdu_send_network_number_is(
BACNET_ADDRESS *dst, uint16_t net, uint8_t status)
{
uint16_t len = 0;
int pdu_len = 0;
int bytes_sent = 0;
bool data_expecting_reply = false;
BACNET_NPDU_DATA npdu_data = { 0 };
BACNET_ADDRESS my_address = { 0 };
uint8_t pdu[MAX_NPDU + 2 + 1] = { 0 };
/* Upon receipt of a What-Is-Network-Number message,
a device that knows the local network number shall
transmit a local broadcast Network-Number-Is message
back to the source device. */
datalink_get_my_address(&my_address);
npdu_encode_npdu_network(
&npdu_data, NETWORK_MESSAGE_NETWORK_NUMBER_IS, data_expecting_reply,
MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(pdu, dst, &my_address, &npdu_data);
if ((pdu_len > 0) && (pdu_len <= MAX_NPDU)) {
len = encode_unsigned16(&pdu[pdu_len], net);
pdu_len += len;
pdu[pdu_len] = status;
pdu_len++;
bytes_sent = datalink_send_pdu(dst, &npdu_data, pdu, pdu_len);
}
return bytes_sent;
}
/**
* @brief send the local network number is message
* @param dst - the destination address for the message
* @param net - local network number
* @return number of bytes sent
*/
int npdu_send_what_is_network_number(BACNET_ADDRESS *dst)
{
int pdu_len = 0;
bool data_expecting_reply = false;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS daddr = { 0 };
BACNET_ADDRESS saddr = { 0 };
uint8_t pdu[MAX_NPDU] = { 0 };
if (dst) {
bacnet_address_copy(&daddr, dst);
} else {
datalink_get_broadcast_address(&daddr);
}
datalink_get_my_address(&saddr);
npdu_encode_npdu_network(
&npdu_data, NETWORK_MESSAGE_WHAT_IS_NETWORK_NUMBER,
data_expecting_reply, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(pdu, &daddr, &saddr, &npdu_data);
/* Now send the message */
return datalink_send_pdu(dst, &npdu_data, pdu, pdu_len);
}
/** @file h_npdu.c Handles messages at the NPDU level of the BACnet stack. */
/** Handler to manage the Network Layer Control Messages received in a packet.
* This handler is called if the NCPI bit 7 indicates that this packet is a
* network layer message and there is no further DNET to pass it to.
* The NCPI has already been decoded into the npdu_data structure.
* @ingroup MISCHNDLR
*
* @param src [in] The routing source information, if any.
* If src->net and src->len are 0, there is no
* routing source information.
* @param npdu_data [in] Contains a filled-out structure with information
* decoded from the NCPI and other NPDU
* bytes.
* @param npdu [in] Buffer containing the rest of the NPDU, following the
* bytes that have already been decoded.
* @param npdu_len [in] The length of the remaining NPDU message in npdu[].
*/
static void network_control_handler(
BACNET_ADDRESS *src,
BACNET_NPDU_DATA *npdu_data,
uint8_t *npdu,
uint16_t npdu_len)
{
uint16_t dnet = 0;
uint8_t status = 0;
switch (npdu_data->network_message_type) {
case NETWORK_MESSAGE_WHAT_IS_NETWORK_NUMBER:
if (src->net == 0) {
if (Local_Network_Number) {
npdu_send_network_number_is(
src, Local_Network_Number, Local_Network_Number_Status);
} else {
/* Upon receipt of a What-Is-Network-Number message,
a device that does not know the local network number
shall discard the message. */
}
} else {
/* Devices shall ignore What-Is-Network-Number messages
that contain SNET/SADR or DNET/DADR information in
the NPCI. */
}
break;
case NETWORK_MESSAGE_NETWORK_NUMBER_IS:
if (src->net == 0) {
/* It shall be transmitted with a local broadcast address,
and shall never be routed. */
if (npdu_len >= 2) {
(void)decode_unsigned16(npdu, &dnet);
Local_Network_Number = dnet;
}
if (npdu_len >= 3) {
status = npdu[2];
/* Our net number is always learned, unless we
are a router. Ignore the learned/configured
status */
(void)status;
}
} else {
/* Devices shall ignore Network-Number-Is messages that
contain SNET/SADR or DNET/DADR information In the NPCI or
that are sent with a local unicast address. */
}
break;
default:
break;
}
if ((npdu_data->network_message_type >=
NETWORK_MESSAGE_ASHRAE_RESERVED_MIN) &&
(npdu_data->network_message_type <=
NETWORK_MESSAGE_ASHRAE_RESERVED_MAX)) {
npdu_send_reject_message_to_network(src, dnet);
}
}
/** Handler for the NPDU portion of a received packet.
* Aside from error-checking, if the NPDU doesn't contain routing info,
* this handler doesn't do much besides stepping over the NPDU header
* and passing the remaining bytes to the apdu_handler.
* @note The routing (except src) and NCPI information, including
* npdu_data->data_expecting_reply, are discarded.
* @see routing_npdu_handler
*
* @ingroup MISCHNDLR
*
* @param src [out] Returned with routing source information if the NPDU
* has any and if this points to non-null storage for it.
* If src->net and src->len are 0 on return, there is no
* routing source information.
* This src describes the original source of the message when
* it had to be routed to reach this BACnet Device, and this
* is passed down into the apdu_handler; however, I don't
* think this project's code has any use for the src info
* on return from this handler, since the response has
* already been sent via the apdu_handler.
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
*/
void npdu_handler(BACNET_ADDRESS *src, uint8_t *pdu, uint16_t pdu_len)
{
int apdu_offset = 0;
BACNET_ADDRESS dest = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
if (pdu_len < 1) {
return;
}
/* only handle the version that we know how to handle */
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
apdu_offset =
bacnet_npdu_decode(&pdu[0], pdu_len, &dest, src, &npdu_data);
if (npdu_data.network_layer_message) {
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
network_control_handler(
src, &npdu_data, &pdu[apdu_offset],
(uint16_t)(pdu_len - apdu_offset));
} else {
debug_printf("NPDU: message for router. Discarded!\n");
}
} else if ((apdu_offset > 0) && (apdu_offset < pdu_len)) {
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
/* only handle the version that we know how to handle */
/* and we are not a router, so ignore messages with
routing information cause they are not for us */
if ((dest.net == BACNET_BROADCAST_NETWORK) &&
((pdu[apdu_offset] & 0xF0) ==
PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
/* hack for 5.4.5.1 - IDLE */
/* ConfirmedBroadcastReceived */
/* then enter IDLE - ignore the PDU */
} else {
if (npdu_data.data_expecting_reply) {
apdu_network_priority_set(npdu_data.priority);
} else {
apdu_network_priority_set(MESSAGE_PRIORITY_NORMAL);
}
apdu_handler(
src, &pdu[apdu_offset],
(uint16_t)(pdu_len - apdu_offset));
}
} else {
#if PRINT_ENABLED
printf("NPDU: DNET=%u. Discarded!\n", (unsigned)dest.net);
#endif
}
}
} else {
#if PRINT_ENABLED
printf(
"NPDU: BACnet Protocol Version=%u. Discarded!\n",
(unsigned)pdu[0]);
#endif
}
return;
}
/**
* Send NPDU reject message to network
*
* @param dst - the destination address for the message
* @param net - local network number
* @return number of bytes sent
*/
int npdu_send_reject_message_to_network(BACNET_ADDRESS *dst, uint16_t net)
{
uint16_t len = 0;
int pdu_len = 0;
int bytes_sent = 0;
bool data_expecting_reply = false;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address = { 0 };
uint8_t pdu[MAX_NPDU + 2 + 1] = { 0 };
datalink_get_my_address(&my_address);
npdu_encode_npdu_network(
&npdu_data, NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK,
data_expecting_reply, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(pdu, dst, &my_address, &npdu_data);
pdu[pdu_len++] = NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE;
if ((pdu_len > 0) && (pdu_len <= MAX_NPDU)) {
len = encode_unsigned16(&pdu[pdu_len], net);
pdu_len += len;
bytes_sent = datalink_send_pdu(dst, &npdu_data, pdu, pdu_len);
}
return bytes_sent;
}