/** * @file * @brief Handling for the basic BACnet NPDU layer of the BACnet stack * @author Steve Karg * @date 2008 * @copyright SPDX-License-Identifier: MIT */ #include #include /* 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 #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; }