Issue 2 move folders and use deep path include file names to prevent collisions (#4)
* moving folders and files and adjust server demo build * Fix Makefile for apps/server on Linux * fix unit test source file folders * fix datetime convert UTC functions. Add Code::Blocks project for datetime testing * added some ignore extensions * disable parallel make option * fix build for abort, dcc, and epics apps * fix build for dcc, epics, error, and getevent apps. * Fixed building of all apps * fix the ipv4 to ipv6 router app build * Change indent style from Google to Webkit * make pretty to re-format style * removed common Makefile since we already had one and two was too many * remove scripts from root folder that are no longer maintained or used * remove mercurial EOL and ignore files for git repo * remove .vscodeconfig files from repo * tweak clang-format style * clang-format src and apps with tweaked style * added clang-tidy to fix readability if braces in src * result of make tidy for src and apps * fix clang-tidy mangling * Added code::blocks project for BACnet server simulation * added code::blocks linux project for WhoIs app * update text files for EOL * fix EOL in some files * fixed make win32 apps for older gcc * Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler. * created codeblocks project for apps/epics for Windows * fixing ports/xplained to work with new data structure. * fix ports/xplained example for Atmel Studio compile * fix ports/stm32f10x example for gcc Makefile compile * fix ports/stm32f10x example for IAR EWARM compile * fix ports/xplained timer callback * fix ports/bdk_atxx_mspt build with subdirs * fix ports/bdk_atxx_mspt build with subdirs * updated git ignore for IAR build artifacts * updated gitignore for non-tracked files and folders * fixed bdk-atxx4-mstp port for Rowley Crossworks project file * fixed bdk-atxx4-mstp port for GCC AVR Makefile * fixed atmega168 port for IAR AVR and GCC AVR Makefile * fixed at91sam7s port for IAR ARM and GCC ARM Makefile * removed unmaintainable DOS, RTOS32, and atmega8 ports. Updated rx62n (untested). * changed arm7 to uip port
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef ARCNET_H
|
||||
#define ARCNET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
/* specific defines for ARCNET */
|
||||
#define MAX_HEADER (1+1+2+2+1+1+1+1)
|
||||
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
bool arcnet_valid(
|
||||
void);
|
||||
void arcnet_cleanup(
|
||||
void);
|
||||
bool arcnet_init(
|
||||
char *interface_name);
|
||||
|
||||
/* function to send a packet out the 802.2 socket */
|
||||
/* returns zero on success, non-zero on failure */
|
||||
int arcnet_send_pdu(
|
||||
BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len); /* number of bytes of data */
|
||||
|
||||
/* receives an framed packet */
|
||||
/* returns the number of octets in the PDU, or zero on failure */
|
||||
uint16_t arcnet_receive(
|
||||
BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
void arcnet_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
void arcnet_get_broadcast_address(
|
||||
BACNET_ADDRESS * dest); /* destination address */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,800 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacsec.h"
|
||||
|
||||
BACNET_KEY_IDENTIFIER_ALGORITHM key_algorithm(uint16_t id)
|
||||
{
|
||||
return (BACNET_KEY_IDENTIFIER_ALGORITHM)((id >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
BACNET_KEY_IDENTIFIER_KEY_NUMBER key_number(uint16_t id)
|
||||
{
|
||||
return (BACNET_KEY_IDENTIFIER_KEY_NUMBER)(id & 0xFF);
|
||||
}
|
||||
|
||||
int encode_security_wrapper(
|
||||
int bytes_before, uint8_t *apdu, BACNET_SECURITY_WRAPPER *wrapper)
|
||||
{
|
||||
int curr = 0;
|
||||
int enc_begin = 0;
|
||||
BACNET_KEY_ENTRY key;
|
||||
BACNET_SECURITY_RESPONSE_CODE res = SEC_RESP_SUCCESS;
|
||||
|
||||
apdu[curr] = 0;
|
||||
/* control byte */
|
||||
if (wrapper->payload_net_or_bvll_flag) {
|
||||
apdu[curr] |= 1 << 7;
|
||||
}
|
||||
/* encryption flag will be set after signature calculation */
|
||||
/* bit 5 is reserved and shall be 0 */
|
||||
if (wrapper->authentication_flag) {
|
||||
apdu[curr] |= 1 << 4;
|
||||
}
|
||||
if (wrapper->do_not_unwrap_flag) {
|
||||
apdu[curr] |= 1 << 3;
|
||||
}
|
||||
if (wrapper->do_not_decrypt_flag) {
|
||||
apdu[curr] |= 1 << 2;
|
||||
}
|
||||
if (wrapper->non_trusted_source_flag) {
|
||||
apdu[curr] |= 1 << 1;
|
||||
}
|
||||
if (wrapper->secured_by_router_flag) {
|
||||
apdu[curr] |= 1;
|
||||
}
|
||||
curr++;
|
||||
/* basic integrity checks */
|
||||
if (wrapper->do_not_decrypt_flag && !wrapper->do_not_unwrap_flag) {
|
||||
return -SEC_RESP_MALFORMED_MESSAGE;
|
||||
}
|
||||
if (!wrapper->encrypted_flag && wrapper->do_not_decrypt_flag) {
|
||||
return -SEC_RESP_MALFORMED_MESSAGE;
|
||||
}
|
||||
/* key */
|
||||
apdu[curr++] = wrapper->key_revision;
|
||||
curr += encode_unsigned16(&apdu[curr], wrapper->key_identifier);
|
||||
/* find appropriate key */
|
||||
key.key_identifier = wrapper->key_identifier;
|
||||
res = bacnet_find_key(wrapper->key_revision, &key);
|
||||
if (res != SEC_RESP_SUCCESS) {
|
||||
return -res;
|
||||
}
|
||||
/* source device instance */
|
||||
curr += encode_unsigned24(&apdu[curr], wrapper->source_device_instance);
|
||||
/* message id */
|
||||
curr += encode_unsigned32(&apdu[curr], wrapper->message_id);
|
||||
/* timestamp */
|
||||
curr += encode_unsigned32(&apdu[curr], wrapper->timestamp);
|
||||
/* begin encryption starting from destination device instance */
|
||||
enc_begin = curr;
|
||||
/* destination device instance */
|
||||
curr +=
|
||||
encode_unsigned24(&apdu[curr], wrapper->destination_device_instance);
|
||||
/* dst address */
|
||||
curr += encode_unsigned16(&apdu[curr], wrapper->dnet);
|
||||
apdu[curr++] = wrapper->dlen;
|
||||
memcpy(&apdu[curr], wrapper->dadr, wrapper->dlen);
|
||||
curr += wrapper->dlen;
|
||||
/* src address */
|
||||
curr += encode_unsigned16(&apdu[curr], wrapper->snet);
|
||||
apdu[curr++] = wrapper->slen;
|
||||
memcpy(&apdu[curr], wrapper->sadr, wrapper->slen);
|
||||
curr += wrapper->slen;
|
||||
/* authentication */
|
||||
if (wrapper->authentication_flag) {
|
||||
apdu[curr++] = wrapper->authentication_mechanism;
|
||||
/* authentication data */
|
||||
curr += encode_unsigned16(&apdu[curr], wrapper->user_id);
|
||||
apdu[curr++] = wrapper->user_role;
|
||||
if ((wrapper->authentication_mechanism >= 1) &&
|
||||
(wrapper->authentication_mechanism <= 199)) {
|
||||
curr += encode_unsigned16(
|
||||
&apdu[curr], wrapper->authentication_data_length + 5);
|
||||
memcpy(&apdu[curr], wrapper->authentication_data,
|
||||
wrapper->authentication_data_length);
|
||||
curr += wrapper->authentication_data_length;
|
||||
} else if (wrapper->authentication_mechanism >= 200) {
|
||||
curr += encode_unsigned16(
|
||||
&apdu[curr], wrapper->authentication_data_length + 7);
|
||||
curr += encode_unsigned16(&apdu[curr], wrapper->vendor_id);
|
||||
memcpy(&apdu[curr], wrapper->authentication_data,
|
||||
wrapper->authentication_data_length);
|
||||
curr += wrapper->authentication_data_length;
|
||||
}
|
||||
}
|
||||
memcpy(&apdu[curr], wrapper->service_data, wrapper->service_data_len);
|
||||
curr += wrapper->service_data_len;
|
||||
/* signature calculation */
|
||||
key_sign_msg(&key, &apdu[-bytes_before], (uint32_t)(bytes_before + curr),
|
||||
wrapper->signature);
|
||||
/* padding and encryption */
|
||||
if (wrapper->encrypted_flag) {
|
||||
/* set encryption flag, signing is done */
|
||||
apdu[0] |= 1 << 6;
|
||||
/* handle padding */
|
||||
key_set_padding(
|
||||
&key, curr - enc_begin, &wrapper->padding_len, wrapper->padding);
|
||||
if (wrapper->padding_len > 2) {
|
||||
memcpy(&apdu[curr], wrapper->padding, wrapper->padding_len - 2);
|
||||
curr += wrapper->padding_len - 2;
|
||||
}
|
||||
curr += encode_unsigned16(&apdu[curr], wrapper->padding_len);
|
||||
/* encryption */
|
||||
key_encrypt_msg(&key, &apdu[enc_begin], (uint32_t)(curr - enc_begin),
|
||||
wrapper->signature);
|
||||
}
|
||||
memcpy(&apdu[curr], wrapper->signature, SIGNATURE_LEN);
|
||||
curr += SIGNATURE_LEN;
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int encode_challenge_request(uint8_t *apdu, BACNET_CHALLENGE_REQUEST *bc_req)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
apdu[curr++] = bc_req->message_challenge;
|
||||
curr += encode_unsigned32(&apdu[curr], bc_req->orig_message_id);
|
||||
curr += encode_unsigned32(&apdu[curr], bc_req->orig_timestamp);
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int encode_security_payload(uint8_t *apdu, BACNET_SECURITY_PAYLOAD *payload)
|
||||
{
|
||||
encode_unsigned16(&apdu[0], payload->payload_length);
|
||||
memcpy(&apdu[2], payload->payload, payload->payload_length);
|
||||
|
||||
return (int)(2 + payload->payload_length);
|
||||
}
|
||||
|
||||
int encode_security_response(uint8_t *apdu, BACNET_SECURITY_RESPONSE *resp)
|
||||
{
|
||||
int curr = 0;
|
||||
int i;
|
||||
|
||||
apdu[curr++] = resp->response_code;
|
||||
curr += encode_unsigned32(&apdu[curr], resp->orig_message_id);
|
||||
curr += encode_unsigned32(&apdu[curr], resp->orig_timestamp);
|
||||
switch ((BACNET_SECURITY_RESPONSE_CODE)resp->response_code) {
|
||||
case SEC_RESP_BAD_TIMESTAMP:
|
||||
curr += encode_unsigned32(
|
||||
&apdu[curr], resp->response.bad_timestamp.expected_timestamp);
|
||||
break;
|
||||
case SEC_RESP_CANNOT_USE_KEY:
|
||||
curr += encode_unsigned16(
|
||||
&apdu[curr], resp->response.cannot_use_key.key);
|
||||
break;
|
||||
case SEC_RESP_INCORRECT_KEY:
|
||||
apdu[curr++] = resp->response.incorrect_key.number_of_keys;
|
||||
for (i = 0; i < (int)resp->response.incorrect_key.number_of_keys;
|
||||
i++) {
|
||||
curr += encode_unsigned16(
|
||||
&apdu[curr], resp->response.incorrect_key.keys[i]);
|
||||
}
|
||||
break;
|
||||
case SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE:
|
||||
apdu[curr++] = resp->response.unknown_authentication_type
|
||||
.original_authentication_type;
|
||||
curr += encode_unsigned16(&apdu[curr],
|
||||
resp->response.unknown_authentication_type.vendor_id);
|
||||
break;
|
||||
case SEC_RESP_UNKNOWN_KEY:
|
||||
curr += encode_unsigned16(
|
||||
&apdu[curr], resp->response.unknown_key.original_key);
|
||||
break;
|
||||
case SEC_RESP_UNKNOWN_KEY_REVISION:
|
||||
apdu[curr++] =
|
||||
resp->response.unknown_key_revision.original_key_revision;
|
||||
break;
|
||||
case SEC_RESP_TOO_MANY_KEYS:
|
||||
apdu[curr++] = resp->response.too_many_keys.max_num_of_keys;
|
||||
break;
|
||||
case SEC_RESP_INVALID_KEY_DATA:
|
||||
curr += encode_unsigned16(
|
||||
&apdu[curr], resp->response.invalid_key_data.key);
|
||||
break;
|
||||
case SEC_RESP_SUCCESS:
|
||||
case SEC_RESP_ACCESS_DENIED:
|
||||
case SEC_RESP_BAD_DESTINATION_ADDRESS:
|
||||
case SEC_RESP_BAD_DESTINATION_DEVICE_ID:
|
||||
case SEC_RESP_BAD_SIGNATURE:
|
||||
case SEC_RESP_BAD_SOURCE_ADDRESS:
|
||||
case SEC_RESP_CANNOT_VERIFY_MESSAGE_ID:
|
||||
case SEC_RESP_CORRECT_KEY_REVISION:
|
||||
case SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED:
|
||||
case SEC_RESP_DUPLICATE_MESSAGE:
|
||||
case SEC_RESP_ENCRYPTION_NOT_CONFIGURED:
|
||||
case SEC_RESP_ENCRYPTION_REQUIRED:
|
||||
case SEC_RESP_KEY_UPDATE_IN_PROGRESS:
|
||||
case SEC_RESP_MALFORMED_MESSAGE:
|
||||
case SEC_RESP_NOT_KEY_SERVER:
|
||||
case SEC_RESP_SECURITY_NOT_CONFIGURED:
|
||||
case SEC_RESP_SOURCE_SECURITY_REQUIRED:
|
||||
case SEC_RESP_UNKNOWN_SOURCE_MESSAGE:
|
||||
break;
|
||||
default:
|
||||
return -1; /* unknown message type */
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int encode_request_key_update(uint8_t *apdu, BACNET_REQUEST_KEY_UPDATE *req)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
apdu[curr++] = req->set_1_key_revision;
|
||||
curr += encode_unsigned32(&apdu[curr], req->set_1_activation_time);
|
||||
curr += encode_unsigned32(&apdu[curr], req->set_1_expiration_time);
|
||||
apdu[curr++] = req->set_2_key_revision;
|
||||
curr += encode_unsigned32(&apdu[curr], req->set_2_activation_time);
|
||||
curr += encode_unsigned32(&apdu[curr], req->set_2_expiration_time);
|
||||
apdu[curr++] = req->distribution_key_revision;
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int encode_key_entry(uint8_t *apdu, BACNET_KEY_ENTRY *entry)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
curr += encode_unsigned16(&apdu[curr], entry->key_identifier);
|
||||
apdu[curr++] = entry->key_len;
|
||||
memcpy(&apdu[curr], entry->key, entry->key_len);
|
||||
curr += entry->key_len;
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int encode_update_key_set(uint8_t *apdu, BACNET_UPDATE_KEY_SET *key_set)
|
||||
{
|
||||
int curr = 0;
|
||||
int i, res;
|
||||
apdu[curr] = 0;
|
||||
|
||||
if (key_set->remove) {
|
||||
apdu[curr] |= 1;
|
||||
}
|
||||
if (key_set->more) {
|
||||
apdu[curr] |= 1 << 1;
|
||||
}
|
||||
if (key_set->set_clr[1]) {
|
||||
apdu[curr] |= 1 << 2;
|
||||
}
|
||||
if (key_set->set_ck[1]) {
|
||||
apdu[curr] |= 1 << 3;
|
||||
}
|
||||
if (key_set->set_rae[1]) {
|
||||
apdu[curr] |= 1 << 4;
|
||||
}
|
||||
if (key_set->set_clr[0]) {
|
||||
apdu[curr] |= 1 << 5;
|
||||
}
|
||||
if (key_set->set_ck[0]) {
|
||||
apdu[curr] |= 1 << 6;
|
||||
}
|
||||
if (key_set->set_rae[0]) {
|
||||
apdu[curr] |= 1 << 7;
|
||||
}
|
||||
curr++;
|
||||
if (key_set->set_rae[0]) {
|
||||
apdu[curr++] = key_set->set_key_revision[0];
|
||||
curr += encode_unsigned32(&apdu[curr], key_set->set_activation_time[0]);
|
||||
curr += encode_unsigned32(&apdu[curr], key_set->set_expiration_time[0]);
|
||||
}
|
||||
if (key_set->set_ck[0]) {
|
||||
apdu[curr++] = key_set->set_key_count[0];
|
||||
if (key_set->set_key_count[0] > MAX_UPDATE_KEY_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < (int)key_set->set_key_count[0]; i++) {
|
||||
res = encode_key_entry(&apdu[curr], &key_set->set_keys[0][i]);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
curr += res;
|
||||
}
|
||||
}
|
||||
if (key_set->set_rae[1]) {
|
||||
apdu[curr++] = key_set->set_key_revision[1];
|
||||
curr += encode_unsigned32(&apdu[curr], key_set->set_activation_time[1]);
|
||||
curr += encode_unsigned32(&apdu[curr], key_set->set_expiration_time[1]);
|
||||
}
|
||||
if (key_set->set_ck[1]) {
|
||||
apdu[curr++] = key_set->set_key_count[1];
|
||||
if (key_set->set_key_count[1] > MAX_UPDATE_KEY_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < (int)key_set->set_key_count[1]; i++) {
|
||||
res = encode_key_entry(&apdu[curr], &key_set->set_keys[1][i]);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
curr += res;
|
||||
}
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
int encode_update_distribution_key(
|
||||
uint8_t *apdu, BACNET_UPDATE_DISTRIBUTION_KEY *dist_key)
|
||||
{
|
||||
int curr = 0;
|
||||
int res;
|
||||
|
||||
apdu[curr++] = dist_key->key_revision;
|
||||
res = encode_key_entry(&apdu[curr], &dist_key->key);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return curr + res;
|
||||
}
|
||||
|
||||
int encode_request_master_key(
|
||||
uint8_t *apdu, BACNET_REQUEST_MASTER_KEY *req_master_key)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
apdu[curr++] = req_master_key->no_supported_algorithms;
|
||||
memcpy(&apdu[curr], req_master_key->es_algorithms,
|
||||
req_master_key->no_supported_algorithms);
|
||||
|
||||
return (int)(curr + req_master_key->no_supported_algorithms);
|
||||
}
|
||||
|
||||
int encode_set_master_key(uint8_t *apdu, BACNET_SET_MASTER_KEY *set_master_key)
|
||||
{
|
||||
return encode_key_entry(apdu, &set_master_key->key);
|
||||
}
|
||||
|
||||
int decode_security_wrapper_safe(int bytes_before,
|
||||
uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SECURITY_WRAPPER *wrapper)
|
||||
{
|
||||
int curr = 0;
|
||||
int enc_begin = 0;
|
||||
int real_len = (int)(apdu_len_remaining - SIGNATURE_LEN);
|
||||
BACNET_KEY_ENTRY key;
|
||||
BACNET_SECURITY_RESPONSE_CODE res = SEC_RESP_SUCCESS;
|
||||
|
||||
if (apdu_len_remaining < 40) {
|
||||
return -SEC_RESP_MALFORMED_MESSAGE;
|
||||
}
|
||||
wrapper->payload_net_or_bvll_flag = ((apdu[curr] & (1 << 7)) != 0);
|
||||
wrapper->encrypted_flag = ((apdu[curr] & (1 << 6)) != 0);
|
||||
wrapper->authentication_flag = ((apdu[curr] & (1 << 4)) != 0);
|
||||
wrapper->do_not_unwrap_flag = ((apdu[curr] & (1 << 3)) != 0);
|
||||
wrapper->do_not_decrypt_flag = ((apdu[curr] & (1 << 2)) != 0);
|
||||
wrapper->non_trusted_source_flag = ((apdu[curr] & (1 << 1)) != 0);
|
||||
wrapper->secured_by_router_flag = ((apdu[curr] & 1) != 0);
|
||||
/* basic integrity checks */
|
||||
if (wrapper->do_not_decrypt_flag && !wrapper->do_not_unwrap_flag) {
|
||||
return -SEC_RESP_MALFORMED_MESSAGE;
|
||||
}
|
||||
if (!wrapper->encrypted_flag && wrapper->do_not_decrypt_flag) {
|
||||
return -SEC_RESP_MALFORMED_MESSAGE;
|
||||
}
|
||||
/* remove encryption flag for signature validation */
|
||||
apdu[curr] &= ~((uint8_t)(1 << 6));
|
||||
curr++;
|
||||
/* key */
|
||||
wrapper->key_revision = apdu[curr++];
|
||||
curr += decode_unsigned16(&apdu[curr], &wrapper->key_identifier);
|
||||
/* find appropriate key */
|
||||
key.key_identifier = wrapper->key_identifier;
|
||||
res = bacnet_find_key(wrapper->key_revision, &key);
|
||||
if (res != SEC_RESP_SUCCESS) {
|
||||
return -res;
|
||||
}
|
||||
/* source device instance */
|
||||
curr += decode_unsigned24(&apdu[curr], &wrapper->source_device_instance);
|
||||
/* message id */
|
||||
curr += decode_unsigned32(&apdu[curr], &wrapper->message_id);
|
||||
/* timestamp */
|
||||
curr += decode_unsigned32(&apdu[curr], &wrapper->timestamp);
|
||||
/* begin decryption starting from destination device instance */
|
||||
enc_begin = curr;
|
||||
/* read signature */
|
||||
memcpy(wrapper->signature, &apdu[real_len], SIGNATURE_LEN);
|
||||
if (wrapper->encrypted_flag) {
|
||||
if (!key_decrypt_msg(&key, &apdu[enc_begin],
|
||||
(uint32_t)(real_len - enc_begin), wrapper->signature)) {
|
||||
return -SEC_RESP_MALFORMED_MESSAGE;
|
||||
}
|
||||
curr += decode_unsigned16(&apdu[real_len - 2], &wrapper->padding_len);
|
||||
real_len -= wrapper->padding_len;
|
||||
memcpy(wrapper->padding, &apdu[wrapper->padding_len],
|
||||
wrapper->padding_len - 2);
|
||||
}
|
||||
/* destination device instance */
|
||||
curr +=
|
||||
decode_unsigned24(&apdu[curr], &wrapper->destination_device_instance);
|
||||
/* dst address */
|
||||
curr += decode_unsigned16(&apdu[curr], &wrapper->dnet);
|
||||
wrapper->dlen = apdu[curr++];
|
||||
memcpy(wrapper->dadr, &apdu[curr], wrapper->dlen);
|
||||
curr += wrapper->dlen;
|
||||
/* src address */
|
||||
curr += decode_unsigned16(&apdu[curr], &wrapper->snet);
|
||||
wrapper->slen = apdu[curr++];
|
||||
memcpy(wrapper->sadr, &apdu[curr], wrapper->slen);
|
||||
curr += wrapper->slen;
|
||||
/* authentication */
|
||||
if (wrapper->authentication_flag) {
|
||||
wrapper->authentication_mechanism = apdu[curr++];
|
||||
/* authentication data */
|
||||
curr += decode_unsigned16(&apdu[curr], &wrapper->user_id);
|
||||
wrapper->user_role = apdu[curr++];
|
||||
if ((wrapper->authentication_mechanism >= 1) &&
|
||||
(wrapper->authentication_mechanism <= 199)) {
|
||||
curr += decode_unsigned16(
|
||||
&apdu[curr], &wrapper->authentication_data_length);
|
||||
wrapper->authentication_data_length -= 5;
|
||||
memcpy(wrapper->authentication_data, &apdu[curr],
|
||||
wrapper->authentication_data_length);
|
||||
curr += wrapper->authentication_data_length;
|
||||
} else if (wrapper->authentication_mechanism >= 200) {
|
||||
curr += decode_unsigned16(
|
||||
&apdu[curr], &wrapper->authentication_data_length);
|
||||
wrapper->authentication_data_length -= 7;
|
||||
curr += decode_unsigned16(&apdu[curr], &wrapper->vendor_id);
|
||||
memcpy(wrapper->authentication_data, &apdu[curr],
|
||||
wrapper->authentication_data_length);
|
||||
curr += wrapper->authentication_data_length;
|
||||
}
|
||||
}
|
||||
wrapper->service_data_len = (uint16_t)(real_len - curr);
|
||||
memcpy(wrapper->service_data, &apdu[curr], wrapper->service_data_len);
|
||||
curr += wrapper->service_data_len;
|
||||
if (!key_verify_sign_msg(&key, &apdu[-bytes_before],
|
||||
(uint32_t)(bytes_before + real_len), wrapper->signature)) {
|
||||
return -SEC_RESP_BAD_SIGNATURE;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int decode_challenge_request_safe(uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_CHALLENGE_REQUEST *bc_req)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
if (apdu_len_remaining < 9) {
|
||||
return -1;
|
||||
}
|
||||
bc_req->message_challenge = apdu[curr++];
|
||||
curr += decode_unsigned32(&apdu[curr], &bc_req->orig_message_id);
|
||||
curr += decode_unsigned32(&apdu[curr], &bc_req->orig_timestamp);
|
||||
|
||||
return curr; /* always 9! */
|
||||
}
|
||||
|
||||
int decode_security_payload_safe(uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SECURITY_PAYLOAD *payload)
|
||||
{
|
||||
if (apdu_len_remaining < 2) {
|
||||
return -1;
|
||||
}
|
||||
decode_unsigned16(&apdu[0], &payload->payload_length);
|
||||
if (apdu_len_remaining - 2 < payload->payload_length) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(payload->payload, &apdu[2], payload->payload_length);
|
||||
return (int)(2 + payload->payload_length);
|
||||
}
|
||||
|
||||
int decode_security_response_safe(
|
||||
uint8_t *apdu, uint32_t apdu_len_remaining, BACNET_SECURITY_RESPONSE *resp)
|
||||
{
|
||||
int curr = 0;
|
||||
int i;
|
||||
|
||||
if (apdu_len_remaining < 9) {
|
||||
return -1;
|
||||
}
|
||||
resp->response_code = apdu[curr++];
|
||||
curr += decode_unsigned32(&apdu[curr], &resp->orig_message_id);
|
||||
curr += decode_unsigned32(&apdu[curr], &resp->orig_timestamp);
|
||||
switch ((BACNET_SECURITY_RESPONSE_CODE)resp->response_code) {
|
||||
case SEC_RESP_BAD_TIMESTAMP:
|
||||
if (apdu_len_remaining < 13) {
|
||||
return -1;
|
||||
}
|
||||
curr += decode_unsigned32(
|
||||
&apdu[curr], &resp->response.bad_timestamp.expected_timestamp);
|
||||
break;
|
||||
case SEC_RESP_CANNOT_USE_KEY:
|
||||
if (apdu_len_remaining < 11) {
|
||||
return -1;
|
||||
}
|
||||
curr += decode_unsigned16(
|
||||
&apdu[curr], &resp->response.cannot_use_key.key);
|
||||
break;
|
||||
case SEC_RESP_INCORRECT_KEY:
|
||||
if (apdu_len_remaining < 10) {
|
||||
return -1;
|
||||
}
|
||||
resp->response.incorrect_key.number_of_keys = apdu[curr++];
|
||||
if (apdu_len_remaining - 10 <
|
||||
resp->response.incorrect_key.number_of_keys * 2) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < (int)resp->response.incorrect_key.number_of_keys;
|
||||
i++) {
|
||||
curr += decode_unsigned16(
|
||||
&apdu[curr], &resp->response.incorrect_key.keys[i]);
|
||||
}
|
||||
break;
|
||||
case SEC_RESP_UNKNOWN_AUTHENTICATION_TYPE:
|
||||
if (apdu_len_remaining < 12) {
|
||||
return -1;
|
||||
}
|
||||
resp->response.unknown_authentication_type
|
||||
.original_authentication_type = apdu[curr++];
|
||||
curr += decode_unsigned16(&apdu[curr],
|
||||
&resp->response.unknown_authentication_type.vendor_id);
|
||||
if (resp->response.unknown_authentication_type
|
||||
.original_authentication_type < 200 &&
|
||||
resp->response.unknown_authentication_type.vendor_id != 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SEC_RESP_UNKNOWN_KEY:
|
||||
if (apdu_len_remaining < 11) {
|
||||
return -1;
|
||||
}
|
||||
curr += decode_unsigned16(
|
||||
&apdu[curr], &resp->response.unknown_key.original_key);
|
||||
break;
|
||||
case SEC_RESP_UNKNOWN_KEY_REVISION:
|
||||
if (apdu_len_remaining < 10) {
|
||||
return -1;
|
||||
}
|
||||
resp->response.unknown_key_revision.original_key_revision =
|
||||
apdu[curr++];
|
||||
break;
|
||||
case SEC_RESP_TOO_MANY_KEYS:
|
||||
if (apdu_len_remaining < 10) {
|
||||
return -1;
|
||||
}
|
||||
resp->response.too_many_keys.max_num_of_keys = apdu[curr++];
|
||||
break;
|
||||
case SEC_RESP_INVALID_KEY_DATA:
|
||||
if (apdu_len_remaining < 11) {
|
||||
return -1;
|
||||
}
|
||||
curr += decode_unsigned16(
|
||||
&apdu[curr], &resp->response.invalid_key_data.key);
|
||||
break;
|
||||
case SEC_RESP_SUCCESS:
|
||||
case SEC_RESP_ACCESS_DENIED:
|
||||
case SEC_RESP_BAD_DESTINATION_ADDRESS:
|
||||
case SEC_RESP_BAD_DESTINATION_DEVICE_ID:
|
||||
case SEC_RESP_BAD_SIGNATURE:
|
||||
case SEC_RESP_BAD_SOURCE_ADDRESS:
|
||||
case SEC_RESP_CANNOT_VERIFY_MESSAGE_ID:
|
||||
case SEC_RESP_CORRECT_KEY_REVISION:
|
||||
case SEC_RESP_DESTINATION_DEVICE_ID_REQUIRED:
|
||||
case SEC_RESP_DUPLICATE_MESSAGE:
|
||||
case SEC_RESP_ENCRYPTION_NOT_CONFIGURED:
|
||||
case SEC_RESP_ENCRYPTION_REQUIRED:
|
||||
case SEC_RESP_KEY_UPDATE_IN_PROGRESS:
|
||||
case SEC_RESP_MALFORMED_MESSAGE:
|
||||
case SEC_RESP_NOT_KEY_SERVER:
|
||||
case SEC_RESP_SECURITY_NOT_CONFIGURED:
|
||||
case SEC_RESP_SOURCE_SECURITY_REQUIRED:
|
||||
case SEC_RESP_UNKNOWN_SOURCE_MESSAGE:
|
||||
break;
|
||||
default:
|
||||
return -1; /* unknown message type */
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
int decode_request_key_update_safe(
|
||||
uint8_t *apdu, uint32_t apdu_len_remaining, BACNET_REQUEST_KEY_UPDATE *req)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
if (apdu_len_remaining < 19) {
|
||||
return -1;
|
||||
}
|
||||
req->set_1_key_revision = apdu[curr++];
|
||||
curr += decode_unsigned32(&apdu[curr], &req->set_1_activation_time);
|
||||
curr += decode_unsigned32(&apdu[curr], &req->set_1_expiration_time);
|
||||
req->set_2_key_revision = apdu[curr++];
|
||||
curr += decode_unsigned32(&apdu[curr], &req->set_2_activation_time);
|
||||
curr += decode_unsigned32(&apdu[curr], &req->set_2_expiration_time);
|
||||
req->distribution_key_revision = apdu[curr++];
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int decode_key_entry_safe(
|
||||
uint8_t *apdu, uint32_t apdu_len_remaining, BACNET_KEY_ENTRY *entry)
|
||||
{
|
||||
int curr = 0;
|
||||
|
||||
if (apdu_len_remaining < 3) {
|
||||
return -1;
|
||||
}
|
||||
curr += decode_unsigned16(&apdu[curr], &entry->key_identifier);
|
||||
entry->key_len = apdu[curr++];
|
||||
if (apdu_len_remaining - 3 < entry->key_len ||
|
||||
entry->key_len > MAX_KEY_LEN) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(entry->key, &apdu[curr], entry->key_len);
|
||||
curr += entry->key_len;
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int decode_update_key_set_safe(
|
||||
uint8_t *apdu, uint32_t apdu_len_remaining, BACNET_UPDATE_KEY_SET *key_set)
|
||||
{
|
||||
int curr = 0;
|
||||
int i, res;
|
||||
|
||||
if (apdu_len_remaining < 1) {
|
||||
return -1;
|
||||
}
|
||||
if (apdu[curr] & 1) {
|
||||
key_set->remove = true;
|
||||
}
|
||||
if ((apdu[curr] >> 1) & 1) {
|
||||
key_set->more = true;
|
||||
}
|
||||
if ((apdu[curr] >> 2) & 1) {
|
||||
key_set->set_clr[1] = true;
|
||||
}
|
||||
if ((apdu[curr] >> 3) & 1) {
|
||||
key_set->set_ck[1] = true;
|
||||
}
|
||||
if ((apdu[curr] >> 4) & 1) {
|
||||
key_set->set_rae[1] = true;
|
||||
}
|
||||
if ((apdu[curr] >> 5) & 1) {
|
||||
key_set->set_clr[0] = true;
|
||||
}
|
||||
if ((apdu[curr] >> 6) & 1) {
|
||||
key_set->set_ck[0] = true;
|
||||
}
|
||||
if ((apdu[curr] >> 7) & 1) {
|
||||
key_set->set_rae[0] = true;
|
||||
}
|
||||
curr++;
|
||||
if (key_set->set_rae[0]) {
|
||||
if (apdu_len_remaining - curr < 9) {
|
||||
return -1;
|
||||
}
|
||||
key_set->set_key_revision[0] = apdu[curr++];
|
||||
curr +=
|
||||
decode_unsigned32(&apdu[curr], &key_set->set_activation_time[0]);
|
||||
curr +=
|
||||
decode_unsigned32(&apdu[curr], &key_set->set_expiration_time[0]);
|
||||
}
|
||||
if (key_set->set_ck[0]) {
|
||||
if (apdu_len_remaining - curr < 1) {
|
||||
return -1;
|
||||
}
|
||||
key_set->set_key_count[0] = apdu[curr++];
|
||||
if (key_set->set_key_count[0] > MAX_UPDATE_KEY_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < (int)key_set->set_key_count[0]; i++) {
|
||||
res = decode_key_entry_safe(apdu + curr, apdu_len_remaining - curr,
|
||||
&key_set->set_keys[0][i]);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
curr += res;
|
||||
}
|
||||
}
|
||||
if (key_set->set_rae[1]) {
|
||||
if (apdu_len_remaining - curr < 9) {
|
||||
return -1;
|
||||
}
|
||||
key_set->set_key_revision[1] = apdu[curr++];
|
||||
curr +=
|
||||
decode_unsigned32(&apdu[curr], &key_set->set_activation_time[1]);
|
||||
curr +=
|
||||
decode_unsigned32(&apdu[curr], &key_set->set_expiration_time[1]);
|
||||
}
|
||||
if (key_set->set_ck[1]) {
|
||||
if (apdu_len_remaining - curr < 1) {
|
||||
return -1;
|
||||
}
|
||||
key_set->set_key_count[1] = apdu[curr++];
|
||||
if (key_set->set_key_count[1] > MAX_UPDATE_KEY_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < (int)key_set->set_key_count[1]; i++) {
|
||||
res = decode_key_entry_safe(apdu + curr, apdu_len_remaining - curr,
|
||||
&key_set->set_keys[1][i]);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
curr += res;
|
||||
}
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
int decode_update_distribution_key_safe(uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_UPDATE_DISTRIBUTION_KEY *dist_key)
|
||||
{
|
||||
int curr = 0;
|
||||
int res;
|
||||
if (apdu_len_remaining < 1) {
|
||||
return -1;
|
||||
}
|
||||
dist_key->key_revision = apdu[curr++];
|
||||
res = decode_key_entry_safe(
|
||||
&apdu[curr], apdu_len_remaining - curr, &dist_key->key);
|
||||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return curr + res;
|
||||
}
|
||||
|
||||
int decode_request_master_key_safe(uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_REQUEST_MASTER_KEY *req_master_key)
|
||||
{
|
||||
uint32_t curr = 0;
|
||||
|
||||
if (apdu_len_remaining < 1) {
|
||||
return -1;
|
||||
}
|
||||
req_master_key->no_supported_algorithms = apdu[curr++];
|
||||
if (apdu_len_remaining < curr + req_master_key->no_supported_algorithms) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(req_master_key->es_algorithms, &apdu[curr],
|
||||
req_master_key->no_supported_algorithms);
|
||||
|
||||
return (int)(curr + req_master_key->no_supported_algorithms);
|
||||
}
|
||||
|
||||
int decode_set_master_key_safe(uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SET_MASTER_KEY *set_master_key)
|
||||
{
|
||||
return decode_key_entry_safe(
|
||||
apdu, apdu_len_remaining, &set_master_key->key);
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BACNET_SECURITY_H
|
||||
#define BACNET_SECURITY_H
|
||||
|
||||
#define MAX_AUTH_DATA_LEN 16
|
||||
#define MD5_KEY_SIZE 16
|
||||
#define AES_KEY_SIZE 16
|
||||
#define SHA256_KEY_SIZE 32
|
||||
#define MAX_KEY_LEN 48
|
||||
#define MAX_UPDATE_KEY_COUNT 32
|
||||
#define MAX_INCORRECT_KEYS 255
|
||||
#define MAX_SUPPORTED_ALGORITHMS 255
|
||||
#define MAX_PAD_LEN 16
|
||||
#define SIGNATURE_LEN 16
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
typedef struct BACnet_Security_Wrapper {
|
||||
bool payload_net_or_bvll_flag; /* true if NPDU or BVLL */
|
||||
bool encrypted_flag;
|
||||
bool authentication_flag; /* always false for responses */
|
||||
bool do_not_unwrap_flag; /* always true if do-not-encrypt is true */
|
||||
bool do_not_decrypt_flag; /* when encrypted flag is false, it also false */
|
||||
bool non_trusted_source_flag;
|
||||
bool secured_by_router_flag;
|
||||
uint8_t key_revision; /* 0 for Device-Master key */
|
||||
uint16_t key_identifier;
|
||||
uint32_t source_device_instance;
|
||||
uint32_t message_id; /* monotonically increased value */
|
||||
uint32_t timestamp; /* seconds from UTC 1970-1-1 00:00:00 */
|
||||
uint32_t destination_device_instance;
|
||||
uint16_t dnet;
|
||||
uint8_t dlen;
|
||||
uint8_t dadr[MAX_MAC_LEN];
|
||||
uint16_t snet;
|
||||
uint8_t slen;
|
||||
uint8_t sadr[MAX_MAC_LEN];
|
||||
uint8_t authentication_mechanism; /* present when User-Authenticated or
|
||||
* Application-Specific keys are used with: */
|
||||
/* APDU: Confrmed-Request, Unconfirmed-Request */
|
||||
/* NPDU: Initialize-Routing-Table, Establish-Connection-To-Network,
|
||||
* Disconnect-Connection-To-Network */
|
||||
/* BVLL: Write-Broadcast-Distribution-Table, Read-Broadcast-Distribution-Table,
|
||||
* Register-Foreign-Device, Read-Foreign-Device-Table,
|
||||
* Delete-Foreign-Device-Table-Entry */
|
||||
/* 0 is only legitimate value for now. 200-255 are vendor-specific */
|
||||
uint16_t user_id; /* 0 for unknown */
|
||||
uint8_t user_role; /* 0 and 1 are "system users": 0 for device-to-device non-human,
|
||||
* 1 for device-to-device by unknown human */
|
||||
uint16_t authentication_data_length; /* authentication mechanism 1-255 */
|
||||
uint16_t vendor_id; /* authentication mechanism 200-255 */
|
||||
uint8_t authentication_data[MAX_AUTH_DATA_LEN]; /* other than id, role, length and
|
||||
* vendor-id */
|
||||
uint16_t service_data_len; /* case-to-case */
|
||||
uint8_t *service_data;
|
||||
uint8_t service_type; /* first octet of service_data */
|
||||
uint16_t padding_len; /* included in padding */
|
||||
uint8_t padding[MAX_PAD_LEN];
|
||||
uint8_t signature[SIGNATURE_LEN]; /* hmac-md5 or hmac-sha256, first 16 bytes */
|
||||
} BACNET_SECURITY_WRAPPER;
|
||||
|
||||
typedef struct Challenge_Request {
|
||||
uint8_t message_challenge; /* 1 as a response, everything else for other */
|
||||
uint32_t orig_message_id;
|
||||
uint32_t orig_timestamp;
|
||||
} BACNET_CHALLENGE_REQUEST;
|
||||
|
||||
typedef struct Security_Payload {
|
||||
uint16_t payload_length;
|
||||
uint8_t *payload;
|
||||
} BACNET_SECURITY_PAYLOAD;
|
||||
|
||||
struct Bad_Timestamp {
|
||||
uint32_t expected_timestamp;
|
||||
};
|
||||
|
||||
struct Cannot_Use_Key {
|
||||
uint16_t key;
|
||||
};
|
||||
|
||||
struct Incorrect_Key {
|
||||
uint8_t number_of_keys;
|
||||
uint16_t keys[MAX_INCORRECT_KEYS];
|
||||
};
|
||||
|
||||
struct Unknown_Authentication_Type {
|
||||
uint8_t original_authentication_type;
|
||||
uint16_t vendor_id;
|
||||
};
|
||||
|
||||
struct Unknown_Key {
|
||||
uint16_t original_key;
|
||||
};
|
||||
|
||||
struct Unknown_Key_Revision {
|
||||
uint8_t original_key_revision;
|
||||
};
|
||||
|
||||
struct Too_Many_Keys {
|
||||
uint8_t max_num_of_keys;
|
||||
};
|
||||
|
||||
struct Invalid_Key_Data {
|
||||
uint16_t key;
|
||||
};
|
||||
|
||||
typedef struct Security_Response {
|
||||
uint16_t response_code;
|
||||
uint32_t orig_message_id;
|
||||
uint32_t orig_timestamp;
|
||||
union {
|
||||
struct Bad_Timestamp bad_timestamp;
|
||||
struct Cannot_Use_Key cannot_use_key;
|
||||
struct Incorrect_Key incorrect_key;
|
||||
struct Unknown_Authentication_Type unknown_authentication_type;
|
||||
struct Unknown_Key unknown_key;
|
||||
struct Unknown_Key_Revision unknown_key_revision;
|
||||
struct Too_Many_Keys too_many_keys;
|
||||
struct Invalid_Key_Data invalid_key_data;
|
||||
} response;
|
||||
} BACNET_SECURITY_RESPONSE;
|
||||
|
||||
typedef struct Request_Key_Update {
|
||||
uint8_t set_1_key_revision;
|
||||
uint32_t set_1_activation_time;
|
||||
uint32_t set_1_expiration_time;
|
||||
uint8_t set_2_key_revision;
|
||||
uint32_t set_2_activation_time;
|
||||
uint32_t set_2_expiration_time;
|
||||
uint8_t distribution_key_revision;
|
||||
} BACNET_REQUEST_KEY_UPDATE;
|
||||
|
||||
typedef struct Key_Entry {
|
||||
uint16_t key_identifier;
|
||||
uint8_t key_len;
|
||||
uint8_t key[MAX_KEY_LEN];
|
||||
} BACNET_KEY_ENTRY;
|
||||
|
||||
typedef struct Update_Key_Set {
|
||||
bool set_rae[2], set_ck[2], set_clr[2];
|
||||
bool more;
|
||||
bool remove; /* false for add, true for remove */
|
||||
uint8_t set_key_revision[2];
|
||||
uint32_t set_activation_time[2];
|
||||
uint32_t set_expiration_time[2];
|
||||
uint8_t set_key_count[2];
|
||||
BACNET_KEY_ENTRY set_keys[2][MAX_UPDATE_KEY_COUNT];
|
||||
} BACNET_UPDATE_KEY_SET;
|
||||
|
||||
typedef struct Update_Distribution_Key {
|
||||
uint8_t key_revision;
|
||||
BACNET_KEY_ENTRY key;
|
||||
} BACNET_UPDATE_DISTRIBUTION_KEY;
|
||||
|
||||
typedef struct Request_Master_Key {
|
||||
uint8_t no_supported_algorithms;
|
||||
uint8_t es_algorithms[MAX_SUPPORTED_ALGORITHMS];
|
||||
} BACNET_REQUEST_MASTER_KEY;
|
||||
|
||||
typedef struct Set_Master_Key {
|
||||
BACNET_KEY_ENTRY key;
|
||||
} BACNET_SET_MASTER_KEY;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* helper functions */
|
||||
BACNET_KEY_IDENTIFIER_ALGORITHM key_algorithm(uint16_t id);
|
||||
BACNET_KEY_IDENTIFIER_KEY_NUMBER key_number(uint16_t id);
|
||||
|
||||
/* key manipulation functions - port specific! */
|
||||
BACNET_SECURITY_RESPONSE_CODE bacnet_master_key_set(BACNET_SET_MASTER_KEY *
|
||||
key);
|
||||
BACNET_SECURITY_RESPONSE_CODE
|
||||
bacnet_distribution_key_update(BACNET_UPDATE_DISTRIBUTION_KEY * key);
|
||||
BACNET_SECURITY_RESPONSE_CODE bacnet_key_set_update(BACNET_UPDATE_KEY_SET *
|
||||
update_key_sets);
|
||||
BACNET_SECURITY_RESPONSE_CODE bacnet_find_key(uint8_t revision,
|
||||
BACNET_KEY_ENTRY * key);
|
||||
|
||||
/* signing/verification and encryption/decryption - port specific */
|
||||
int key_sign_msg(BACNET_KEY_ENTRY * key,
|
||||
uint8_t * msg,
|
||||
uint32_t msg_len,
|
||||
uint8_t * signature);
|
||||
bool key_verify_sign_msg(BACNET_KEY_ENTRY * key,
|
||||
uint8_t * msg,
|
||||
uint32_t msg_len,
|
||||
uint8_t * signature);
|
||||
int key_encrypt_msg(BACNET_KEY_ENTRY * key,
|
||||
uint8_t * msg,
|
||||
uint32_t msg_len,
|
||||
uint8_t * signature);
|
||||
bool key_decrypt_msg(BACNET_KEY_ENTRY * key,
|
||||
uint8_t * msg,
|
||||
uint32_t msg_len,
|
||||
uint8_t * signature);
|
||||
void key_set_padding(BACNET_KEY_ENTRY * key,
|
||||
int enc_len,
|
||||
uint16_t * padding_len,
|
||||
uint8_t * padding);
|
||||
|
||||
/* encoders */
|
||||
int encode_security_wrapper(int bytes_before,
|
||||
uint8_t * apdu,
|
||||
BACNET_SECURITY_WRAPPER * wrapper);
|
||||
int encode_challenge_request(uint8_t * apdu,
|
||||
BACNET_CHALLENGE_REQUEST * bc_req);
|
||||
int encode_security_payload(uint8_t * apdu,
|
||||
BACNET_SECURITY_PAYLOAD * payload);
|
||||
int encode_security_response(uint8_t * apdu,
|
||||
BACNET_SECURITY_RESPONSE * resp);
|
||||
int encode_request_key_update(uint8_t * apdu,
|
||||
BACNET_REQUEST_KEY_UPDATE * req);
|
||||
int encode_key_entry(uint8_t * apdu,
|
||||
BACNET_KEY_ENTRY * entry);
|
||||
int encode_update_key_set(uint8_t * apdu,
|
||||
BACNET_UPDATE_KEY_SET * key_set);
|
||||
int encode_update_distribution_key(uint8_t * apdu,
|
||||
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key);
|
||||
int encode_request_master_key(uint8_t * apdu,
|
||||
BACNET_REQUEST_MASTER_KEY * req_master_key);
|
||||
int encode_set_master_key(uint8_t * apdu,
|
||||
BACNET_SET_MASTER_KEY * set_master_key);
|
||||
|
||||
/* safe decoders */
|
||||
int decode_security_wrapper_safe(int bytes_before,
|
||||
uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SECURITY_WRAPPER * wrapper);
|
||||
int decode_challenge_request_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_CHALLENGE_REQUEST * bc_req);
|
||||
int decode_security_payload_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SECURITY_PAYLOAD * payload);
|
||||
int decode_security_response_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SECURITY_RESPONSE * resp);
|
||||
int decode_request_key_update_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_REQUEST_KEY_UPDATE * req);
|
||||
int decode_key_entry_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_KEY_ENTRY * entry);
|
||||
int decode_update_key_set_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_UPDATE_KEY_SET * key_set);
|
||||
int decode_update_distribution_key_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_UPDATE_DISTRIBUTION_KEY * dist_key);
|
||||
int decode_request_master_key_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_REQUEST_MASTER_KEY * req_master_key);
|
||||
int decode_set_master_key_safe(uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
BACNET_SET_MASTER_KEY * set_master_key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,386 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <stdint.h> /* for standard integer types uint8_t etc. */
|
||||
#include <stdbool.h> /* for the standard bool type. */
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/datalink/bip.h"
|
||||
#include "bacnet/datalink/bvlc.h"
|
||||
#include "bacport.h" /* custom per port */
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h> /* for standard i/o, like printing */
|
||||
#endif
|
||||
|
||||
/** @file bip.c Configuration and Operations for BACnet/IP */
|
||||
|
||||
static int BIP_Socket = -1;
|
||||
/* port to use - stored in network byte order */
|
||||
static uint16_t BIP_Port = 0; /* this will force initialization in demos */
|
||||
/* IP Address - stored in network byte order */
|
||||
static struct in_addr BIP_Address;
|
||||
/* Broadcast Address - stored in network byte order */
|
||||
static struct in_addr BIP_Broadcast_Address;
|
||||
|
||||
/** Setter for the BACnet/IP socket handle.
|
||||
*
|
||||
* @param sock_fd [in] Handle for the BACnet/IP socket.
|
||||
*/
|
||||
void bip_set_socket(int sock_fd)
|
||||
{
|
||||
BIP_Socket = sock_fd;
|
||||
}
|
||||
|
||||
/** Getter for the BACnet/IP socket handle.
|
||||
*
|
||||
* @return The handle to the BACnet/IP socket.
|
||||
*/
|
||||
int bip_socket(void)
|
||||
{
|
||||
return BIP_Socket;
|
||||
}
|
||||
|
||||
bool bip_valid(void)
|
||||
{
|
||||
return (BIP_Socket != -1);
|
||||
}
|
||||
|
||||
void bip_set_addr(uint32_t net_address)
|
||||
{ /* in network byte order */
|
||||
BIP_Address.s_addr = net_address;
|
||||
}
|
||||
|
||||
/* returns network byte order */
|
||||
uint32_t bip_get_addr(void)
|
||||
{
|
||||
return BIP_Address.s_addr;
|
||||
}
|
||||
|
||||
void bip_set_broadcast_addr(uint32_t net_address)
|
||||
{ /* in network byte order */
|
||||
BIP_Broadcast_Address.s_addr = net_address;
|
||||
}
|
||||
|
||||
/* returns network byte order */
|
||||
uint32_t bip_get_broadcast_addr(void)
|
||||
{
|
||||
return BIP_Broadcast_Address.s_addr;
|
||||
}
|
||||
|
||||
void bip_set_port(uint16_t port)
|
||||
{ /* in network byte order */
|
||||
BIP_Port = port;
|
||||
}
|
||||
|
||||
/* returns network byte order */
|
||||
uint16_t bip_get_port(void)
|
||||
{
|
||||
return BIP_Port;
|
||||
}
|
||||
|
||||
static int bip_decode_bip_address(BACNET_ADDRESS *bac_addr,
|
||||
struct in_addr *address, /* in network format */
|
||||
uint16_t *port)
|
||||
{ /* in network format */
|
||||
int len = 0;
|
||||
|
||||
if (bac_addr) {
|
||||
memcpy(&address->s_addr, &bac_addr->mac[0], 4);
|
||||
memcpy(port, &bac_addr->mac[4], 2);
|
||||
len = 6;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/** Function to send a packet out the BACnet/IP socket (Annex J).
|
||||
* @ingroup DLBIP
|
||||
*
|
||||
* @param dest [in] Destination address (may encode an IP address and port #).
|
||||
* @param npdu_data [in] The NPDU header (Network) information (not used).
|
||||
* @param pdu [in] Buffer of data to be sent - may be null (why?).
|
||||
* @param pdu_len [in] Number of bytes in the pdu buffer.
|
||||
* @return Number of bytes sent on success, negative number on failure.
|
||||
*/
|
||||
int bip_send_pdu(BACNET_ADDRESS *dest, /* destination address */
|
||||
BACNET_NPDU_DATA *npdu_data, /* network information */
|
||||
uint8_t *pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len)
|
||||
{ /* number of bytes of data */
|
||||
struct sockaddr_in bip_dest;
|
||||
uint8_t mtu[MAX_MPDU] = { 0 };
|
||||
int mtu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
/* addr and port in host format */
|
||||
struct in_addr address;
|
||||
uint16_t port = 0;
|
||||
|
||||
(void)npdu_data;
|
||||
/* assumes that the driver has already been initialized */
|
||||
if (BIP_Socket < 0) {
|
||||
return BIP_Socket;
|
||||
}
|
||||
|
||||
mtu[0] = BVLL_TYPE_BACNET_IP;
|
||||
bip_dest.sin_family = AF_INET;
|
||||
if ((dest->net == BACNET_BROADCAST_NETWORK) || (dest->mac_len == 0)) {
|
||||
/* broadcast */
|
||||
address.s_addr = BIP_Broadcast_Address.s_addr;
|
||||
port = BIP_Port;
|
||||
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
|
||||
} else if ((dest->net > 0) && (dest->len == 0)) {
|
||||
/* network specific broadcast */
|
||||
if (dest->mac_len == 6) {
|
||||
bip_decode_bip_address(dest, &address, &port);
|
||||
} else {
|
||||
address.s_addr = BIP_Broadcast_Address.s_addr;
|
||||
port = BIP_Port;
|
||||
}
|
||||
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
|
||||
} else if (dest->mac_len == 6) {
|
||||
bip_decode_bip_address(dest, &address, &port);
|
||||
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
|
||||
} else {
|
||||
/* invalid address */
|
||||
return -1;
|
||||
}
|
||||
bip_dest.sin_addr.s_addr = address.s_addr;
|
||||
bip_dest.sin_port = port;
|
||||
memset(&(bip_dest.sin_zero), '\0', 8);
|
||||
mtu_len = 2;
|
||||
mtu_len += encode_unsigned16(
|
||||
&mtu[mtu_len], (uint16_t)(pdu_len + 4 /*inclusive */));
|
||||
memcpy(&mtu[mtu_len], pdu, pdu_len);
|
||||
mtu_len += pdu_len;
|
||||
|
||||
/* Send the packet */
|
||||
bytes_sent = sendto(BIP_Socket, (char *)mtu, mtu_len, 0,
|
||||
(struct sockaddr *)&bip_dest, sizeof(struct sockaddr));
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
/** Implementation of the receive() function for BACnet/IP; receives one
|
||||
* packet, verifies its BVLC header, and removes the BVLC header from
|
||||
* the PDU data before returning.
|
||||
*
|
||||
* @param src [out] Source of the packet - who should receive any response.
|
||||
* @param pdu [out] A buffer to hold the PDU portion of the received packet,
|
||||
* after the BVLC portion has been stripped
|
||||
* off.
|
||||
* @param max_pdu [in] Size of the pdu[] buffer.
|
||||
* @param timeout [in] The number of milliseconds to wait for a packet.
|
||||
* @return The number of octets (remaining) in the PDU, or zero on failure.
|
||||
*/
|
||||
uint16_t bip_receive(BACNET_ADDRESS *src, /* source address */
|
||||
uint8_t *pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout)
|
||||
{
|
||||
int received_bytes = 0;
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
fd_set read_fds;
|
||||
int max = 0;
|
||||
struct timeval select_timeout;
|
||||
struct sockaddr_in sin = { 0 };
|
||||
socklen_t sin_len = sizeof(sin);
|
||||
uint16_t i = 0;
|
||||
int function = 0;
|
||||
|
||||
/* Make sure the socket is open */
|
||||
if (BIP_Socket < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we could just use a non-blocking socket, but that consumes all
|
||||
the CPU time. We can use a timeout; it is only supported as
|
||||
a select. */
|
||||
if (timeout >= 1000) {
|
||||
select_timeout.tv_sec = timeout / 1000;
|
||||
select_timeout.tv_usec =
|
||||
1000 * (timeout - select_timeout.tv_sec * 1000);
|
||||
} else {
|
||||
select_timeout.tv_sec = 0;
|
||||
select_timeout.tv_usec = 1000 * timeout;
|
||||
}
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(BIP_Socket, &read_fds);
|
||||
max = BIP_Socket;
|
||||
/* see if there is a packet for us */
|
||||
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) {
|
||||
received_bytes = recvfrom(BIP_Socket, (char *)&pdu[0], max_pdu, 0,
|
||||
(struct sockaddr *)&sin, &sin_len);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See if there is a problem */
|
||||
if (received_bytes < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no problem, just no bytes */
|
||||
if (received_bytes == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the signature of a BACnet/IP packet */
|
||||
if (pdu[0] != BVLL_TYPE_BACNET_IP) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bvlc_for_non_bbmd(&sin, pdu, received_bytes) > 0) {
|
||||
/* Handled, usually with a NACK. */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "BIP: BVLC discarded!\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
function = bvlc_get_function_code(); /* aka, pdu[1] */
|
||||
if ((function == BVLC_ORIGINAL_UNICAST_NPDU) ||
|
||||
(function == BVLC_ORIGINAL_BROADCAST_NPDU)) {
|
||||
/* ignore messages from me */
|
||||
if ((sin.sin_addr.s_addr == BIP_Address.s_addr) &&
|
||||
(sin.sin_port == BIP_Port)) {
|
||||
pdu_len = 0;
|
||||
#if 0
|
||||
fprintf(stderr, "BIP: src is me. Discarded!\n");
|
||||
#endif
|
||||
} else {
|
||||
/* data in src->mac[] is in network format */
|
||||
src->mac_len = 6;
|
||||
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
|
||||
memcpy(&src->mac[4], &sin.sin_port, 2);
|
||||
/* FIXME: check destination address */
|
||||
/* see if it is broadcast or for us */
|
||||
/* decode the length of the PDU - length is inclusive of BVLC */
|
||||
(void)decode_unsigned16(&pdu[2], &pdu_len);
|
||||
/* subtract off the BVLC header */
|
||||
pdu_len -= 4;
|
||||
if (pdu_len < max_pdu) {
|
||||
#if 0
|
||||
fprintf(stderr, "BIP: NPDU[%hu]:", pdu_len);
|
||||
#endif
|
||||
/* shift the buffer to return a valid PDU */
|
||||
for (i = 0; i < pdu_len; i++) {
|
||||
pdu[i] = pdu[4 + i];
|
||||
#if 0
|
||||
fprintf(stderr, "%02X ", pdu[i]);
|
||||
#endif
|
||||
}
|
||||
#if 0
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
}
|
||||
/* ignore packets that are too large */
|
||||
/* clients should check my max-apdu first */
|
||||
else {
|
||||
pdu_len = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "BIP: PDU too large. Discarded!.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if (function == BVLC_FORWARDED_NPDU) {
|
||||
memcpy(&sin.sin_addr.s_addr, &pdu[4], 4);
|
||||
memcpy(&sin.sin_port, &pdu[8], 2);
|
||||
if ((sin.sin_addr.s_addr == BIP_Address.s_addr) &&
|
||||
(sin.sin_port == BIP_Port)) {
|
||||
/* ignore messages from me */
|
||||
pdu_len = 0;
|
||||
} else {
|
||||
/* data in src->mac[] is in network format */
|
||||
src->mac_len = 6;
|
||||
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
|
||||
memcpy(&src->mac[4], &sin.sin_port, 2);
|
||||
/* FIXME: check destination address */
|
||||
/* see if it is broadcast or for us */
|
||||
/* decode the length of the PDU - length is inclusive of BVLC */
|
||||
(void)decode_unsigned16(&pdu[2], &pdu_len);
|
||||
/* subtract off the BVLC header */
|
||||
pdu_len -= 10;
|
||||
if (pdu_len < max_pdu) {
|
||||
/* shift the buffer to return a valid PDU */
|
||||
for (i = 0; i < pdu_len; i++) {
|
||||
pdu[i] = pdu[4 + 6 + i];
|
||||
}
|
||||
} else {
|
||||
/* ignore packets that are too large */
|
||||
/* clients should check my max-apdu first */
|
||||
pdu_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
void bip_get_my_address(BACNET_ADDRESS *my_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (my_address) {
|
||||
my_address->mac_len = 6;
|
||||
memcpy(&my_address->mac[0], &BIP_Address.s_addr, 4);
|
||||
memcpy(&my_address->mac[4], &BIP_Port, 2);
|
||||
my_address->net = 0; /* local only, no routing */
|
||||
my_address->len = 0; /* no SLEN */
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
/* no SADR */
|
||||
my_address->adr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void bip_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||
{ /* destination address */
|
||||
int i = 0; /* counter */
|
||||
|
||||
if (dest) {
|
||||
dest->mac_len = 6;
|
||||
memcpy(&dest->mac[0], &BIP_Broadcast_Address.s_addr, 4);
|
||||
memcpy(&dest->mac[4], &BIP_Port, 2);
|
||||
dest->net = BACNET_BROADCAST_NETWORK;
|
||||
dest->len = 0; /* no SLEN */
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
/* no SADR */
|
||||
dest->adr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BIP_H
|
||||
#define BIP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacport.h"
|
||||
|
||||
/* specific defines for BACnet/IP over Ethernet */
|
||||
#define MAX_HEADER (1 + 1 + 2)
|
||||
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
|
||||
|
||||
#define BVLL_TYPE_BACNET_IP (0x81)
|
||||
|
||||
extern bool BIP_Debug;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* note: define init, set_interface, and cleanup in your port */
|
||||
/* on Linux, ifname is eth0, ath0, arc0, and others.
|
||||
on Windows, ifname is the dotted ip address of the interface */
|
||||
bool bip_init(
|
||||
char *ifname);
|
||||
void bip_set_interface(
|
||||
char *ifname);
|
||||
void bip_cleanup(
|
||||
void);
|
||||
|
||||
/* common BACnet/IP functions */
|
||||
void bip_set_socket(
|
||||
int sock_fd);
|
||||
int bip_socket(
|
||||
void);
|
||||
bool bip_valid(
|
||||
void);
|
||||
void bip_get_broadcast_address(
|
||||
BACNET_ADDRESS * dest); /* destination address */
|
||||
void bip_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
|
||||
/* function to send a packet out the BACnet/IP socket */
|
||||
/* returns zero on success, non-zero on failure */
|
||||
int bip_send_pdu(
|
||||
BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len); /* number of bytes of data */
|
||||
|
||||
/* receives a BACnet/IP packet */
|
||||
/* returns the number of octets in the PDU, or zero on failure */
|
||||
uint16_t bip_receive(
|
||||
BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
/* use network byte order for setting */
|
||||
void bip_set_port(
|
||||
uint16_t port);
|
||||
bool bip_port_changed(void);
|
||||
/* returns network byte order */
|
||||
uint16_t bip_get_port(
|
||||
void);
|
||||
|
||||
/* use network byte order for setting */
|
||||
void bip_set_addr(
|
||||
uint32_t net_address);
|
||||
/* returns network byte order */
|
||||
uint32_t bip_get_addr(
|
||||
void);
|
||||
|
||||
/* use network byte order for setting */
|
||||
void bip_set_broadcast_addr(
|
||||
uint32_t net_address);
|
||||
/* returns network byte order */
|
||||
uint32_t bip_get_broadcast_addr(
|
||||
void);
|
||||
|
||||
/* gets an IP address by name, where name can be a
|
||||
string that is an IP address in dotted form, or
|
||||
a name that is a domain name
|
||||
returns 0 if not found, or
|
||||
an IP address in network byte order */
|
||||
long bip_getaddrbyname(
|
||||
const char *host_name);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
/** @defgroup DLBIP BACnet/IP DataLink Network Layer
|
||||
* @ingroup DataLink
|
||||
* Implementation of the Network Layer using BACnet/IP as the transport, as
|
||||
* described in Annex J.
|
||||
* The functions described here fulfill the roles defined generically at the
|
||||
* DataLink level by serving as the implementation of the function templates.
|
||||
*
|
||||
*/
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date 2015
|
||||
* @defgroup DLBIP6 BACnet/IPv6 DataLink Network Layer
|
||||
* @ingroup DataLink
|
||||
*
|
||||
* Implementation of the Network Layer using BACnet/IPv6 as the transport, as
|
||||
* described in Annex J.
|
||||
* The functions described here fulfill the roles defined generically at the
|
||||
* DataLink level by serving as the implementation of the function templates.
|
||||
*/
|
||||
#ifndef BIP6_H
|
||||
#define BIP6_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/datalink/bvlc6.h"
|
||||
|
||||
/* specific defines for BACnet/IP over Ethernet */
|
||||
#define BIP6_HEADER_MAX (1 + 1 + 2)
|
||||
#define BIP6_MPDU_MAX (BIP6_HEADER_MAX+MAX_PDU)
|
||||
/* for legacy demo applications */
|
||||
#define MAX_MPDU BIP6_MPDU_MAX
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* 6 datalink functions used by demo handlers and applications:
|
||||
init, send, receive, cleanup, unicast/broadcast address.
|
||||
Note: the addresses used here are VMAC addresses. */
|
||||
bool bip6_init(
|
||||
char *ifname);
|
||||
void bip6_cleanup(
|
||||
void);
|
||||
void bip6_get_broadcast_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
void bip6_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
int bip6_send_pdu(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t * pdu,
|
||||
unsigned pdu_len);
|
||||
uint16_t bip6_receive(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * pdu,
|
||||
uint16_t max_pdu,
|
||||
unsigned timeout);
|
||||
|
||||
/* functions that are custom per port */
|
||||
void bip6_set_interface(
|
||||
char *ifname);
|
||||
|
||||
bool bip6_address_match_self(
|
||||
BACNET_IP6_ADDRESS *addr);
|
||||
|
||||
bool bip6_set_addr(
|
||||
BACNET_IP6_ADDRESS *addr);
|
||||
bool bip6_get_addr(
|
||||
BACNET_IP6_ADDRESS *addr);
|
||||
|
||||
void bip6_set_port(
|
||||
uint16_t port);
|
||||
uint16_t bip6_get_port(
|
||||
void);
|
||||
|
||||
bool bip6_set_broadcast_addr(
|
||||
BACNET_IP6_ADDRESS *addr);
|
||||
/* returns network byte order */
|
||||
bool bip6_get_broadcast_addr(
|
||||
BACNET_IP6_ADDRESS *addr);
|
||||
|
||||
int bip6_send_mpdu(
|
||||
BACNET_IP6_ADDRESS *addr,
|
||||
uint8_t * mtu,
|
||||
uint16_t mtu_len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BVLC_H
|
||||
#define BVLC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/datalink/bip.h"
|
||||
|
||||
struct sockaddr_in; /* Defined elsewhere, needed here. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if defined(BBMD_ENABLED) && BBMD_ENABLED
|
||||
void bvlc_maintenance_timer(
|
||||
time_t seconds);
|
||||
#else
|
||||
#define bvlc_maintenance_timer(x)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
/* true if valid entry - false if not */
|
||||
bool valid;
|
||||
/* BACnet/IP address */
|
||||
struct in_addr dest_address; /* in network format */
|
||||
/* BACnet/IP port number - not always 47808=BAC0h */
|
||||
uint16_t dest_port; /* in network format */
|
||||
/* Broadcast Distribution Mask */
|
||||
struct in_addr broadcast_mask; /* in tework format */
|
||||
} BBMD_TABLE_ENTRY;
|
||||
|
||||
uint16_t bvlc_receive(
|
||||
BACNET_ADDRESS * src, /* returns the source address */
|
||||
uint8_t * npdu, /* returns the NPDU */
|
||||
uint16_t max_npdu, /* amount of space available in the NPDU */
|
||||
unsigned timeout); /* number of milliseconds to wait for a packet */
|
||||
|
||||
int bvlc_send_pdu(
|
||||
BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len);
|
||||
|
||||
int bvlc_send_mpdu(
|
||||
struct sockaddr_in *dest,
|
||||
uint8_t * mtu,
|
||||
uint16_t mtu_len);
|
||||
|
||||
#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED
|
||||
int bvlc_encode_write_bdt_init(
|
||||
uint8_t * pdu,
|
||||
unsigned entries);
|
||||
int bvlc_encode_read_fdt(
|
||||
uint8_t * pdu);
|
||||
int bvlc_encode_delete_fdt_entry(
|
||||
uint8_t * pdu,
|
||||
uint32_t address, /* in network byte order */
|
||||
uint16_t port); /* in network byte order */
|
||||
int bvlc_encode_original_unicast_npdu(
|
||||
uint8_t * pdu,
|
||||
uint8_t * npdu,
|
||||
unsigned npdu_length);
|
||||
int bvlc_encode_original_broadcast_npdu(
|
||||
uint8_t * pdu,
|
||||
uint8_t * npdu,
|
||||
unsigned npdu_length);
|
||||
#endif
|
||||
int bvlc_encode_read_bdt(
|
||||
uint8_t * pdu);
|
||||
int bvlc_bbmd_read_bdt(
|
||||
uint32_t bbmd_address,
|
||||
uint16_t bbmd_port);
|
||||
|
||||
/* registers with a bbmd as a foreign device */
|
||||
int bvlc_register_with_bbmd(
|
||||
uint32_t bbmd_address, /* in network byte order */
|
||||
uint16_t bbmd_port, /* in network byte order */
|
||||
uint16_t time_to_live_seconds);
|
||||
|
||||
/* Note any BVLC_RESULT code, or NAK the BVLL message in the unsupported cases. */
|
||||
int bvlc_for_non_bbmd(
|
||||
struct sockaddr_in *sout,
|
||||
uint8_t * npdu,
|
||||
uint16_t received_bytes);
|
||||
|
||||
/* Returns the last BVLL Result we received, either as the result of a BBMD
|
||||
* request we sent, or (if not a BBMD or Client), from trying to register
|
||||
* as a foreign device. */
|
||||
BACNET_BVLC_RESULT bvlc_get_last_result(
|
||||
void);
|
||||
|
||||
/* Returns the current BVLL Function Code we are processing.
|
||||
* We have to store this higher layer code for when the lower layers
|
||||
* need to know what it is, especially to differentiate between
|
||||
* BVLC_ORIGINAL_UNICAST_NPDU and BVLC_ORIGINAL_BROADCAST_NPDU. */
|
||||
BACNET_BVLC_FUNCTION bvlc_get_function_code(
|
||||
void);
|
||||
|
||||
|
||||
/* Local interface to manage BBMD.
|
||||
* The interface user needs to handle mutual exclusion if needed i.e.
|
||||
* BACnet packet is not being handled when the BBMD table is modified.
|
||||
*/
|
||||
|
||||
/* Get handle to broadcast distribution table. Returns the number of
|
||||
* valid entries in the table. */
|
||||
int bvlc_get_bdt_local(
|
||||
const BBMD_TABLE_ENTRY** table);
|
||||
|
||||
/* Invalidate all entries in the broadcast distribution table */
|
||||
void bvlc_clear_bdt_local(void);
|
||||
|
||||
/* Add new entry to broadcast distribution table. Returns true if the new
|
||||
* entry was added successfully */
|
||||
bool bvlc_add_bdt_entry_local(
|
||||
BBMD_TABLE_ENTRY* entry);
|
||||
|
||||
/* Backup broadcast distribution table to a file.
|
||||
* Filename is the BBMD_BACKUP_FILE constant
|
||||
*/
|
||||
void bvlc_bdt_backup_local(
|
||||
void);
|
||||
|
||||
/* Restore broadcast distribution from a file.
|
||||
* Filename is the BBMD_BACKUP_FILE constant
|
||||
*/
|
||||
void bvlc_bdt_restore_local(
|
||||
void);
|
||||
|
||||
/* NAT handling
|
||||
* If the communication between BBMDs goes through a NAT enabled internet
|
||||
* router, special considerations are needed as stated in Annex J.7.8.
|
||||
*
|
||||
* In short, the local IP address of the BBMD is different than the global
|
||||
* address which is visible to the other BBMDs or foreign devices. This is
|
||||
* why the source address in forwarded messages needs to be changed to the
|
||||
* global IP address.
|
||||
*
|
||||
* For other considerations/limitations see Annex J.7.8.
|
||||
*/
|
||||
|
||||
/* Set global IP address of a NAT enabled router which is used in forwarded
|
||||
* messages. Enables NAT handling.
|
||||
*/
|
||||
void bvlc_set_global_address_for_nat(const struct in_addr* addr);
|
||||
|
||||
/* Disable NAT handling of BBMD.
|
||||
*/
|
||||
void bvlc_disable_nat(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date 2015
|
||||
*
|
||||
* Implementation of the BACnet Virtual Link Layer using IPv6,
|
||||
* as described in Annex J.
|
||||
*/
|
||||
#ifndef BVLC6_H
|
||||
#define BVLC6_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
/**
|
||||
* BVLL for BACnet/IPv6
|
||||
* @{
|
||||
*/
|
||||
#define BVLL_TYPE_BACNET_IP6 (0x82)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* B/IPv6 BVLL Messages
|
||||
* @{
|
||||
*/
|
||||
#define BVLC6_RESULT 0x00
|
||||
#define BVLC6_ORIGINAL_UNICAST_NPDU 0x01
|
||||
#define BVLC6_ORIGINAL_BROADCAST_NPDU 0x02
|
||||
#define BVLC6_ADDRESS_RESOLUTION 0x03
|
||||
#define BVLC6_FORWARDED_ADDRESS_RESOLUTION 0x04
|
||||
#define BVLC6_ADDRESS_RESOLUTION_ACK 0x05
|
||||
#define BVLC6_VIRTUAL_ADDRESS_RESOLUTION 0x06
|
||||
#define BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK 0x07
|
||||
#define BVLC6_FORWARDED_NPDU 0x08
|
||||
#define BVLC6_REGISTER_FOREIGN_DEVICE 0x09
|
||||
#define BVLC6_DELETE_FOREIGN_DEVICE 0x0A
|
||||
#define BVLC6_SECURE_BVLL 0x0B
|
||||
#define BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK 0x0C
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* BVLC Result Code
|
||||
* @{
|
||||
*/
|
||||
#define BVLC6_RESULT_SUCCESSFUL_COMPLETION 0x0000
|
||||
#define BVLC6_RESULT_ADDRESS_RESOLUTION_NAK 0x0030
|
||||
#define BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK 0x0060
|
||||
#define BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK 0x0090
|
||||
#define BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK 0x00A0
|
||||
#define BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK 0x00C0
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* BACnet IPv6 Multicast Group ID
|
||||
* BACnet broadcast messages shall be delivered by IPv6 multicasts
|
||||
* as opposed to using IP broadcasting. Broadcasting in
|
||||
* IPv6 is subsumed by multicasting to the all-nodes link
|
||||
* group FF02::1; however, the use of the all-nodes group is not
|
||||
* recommended, and BACnet/IPv6 uses an IANA permanently assigned
|
||||
* multicast group identifier to avoid disturbing
|
||||
* every interface in the network.
|
||||
*
|
||||
* The IANA assigned BACnet/IPv6 variable scope multicast address
|
||||
* is FF0X:0:0:0:0:0:0:BAC0 (FF0X::BAC0) which indicates the multicast
|
||||
* group identifier X'BAC0'. The following multicast scopes are
|
||||
* defined for B/IPv6.
|
||||
* @{
|
||||
*/
|
||||
#define BIP6_MULTICAST_GROUP_ID 0xBAC0
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* IANA prefixes
|
||||
* @{
|
||||
*/
|
||||
#define BIP6_MULTICAST_reserved_0 0xFF00
|
||||
#define BIP6_MULTICAST_NODE_LOCAL 0xFF01
|
||||
#define BIP6_MULTICAST_LINK_LOCAL 0xFF02
|
||||
#define BIP6_MULTICAST_reserved_3 0xFF03
|
||||
#define BIP6_MULTICAST_ADMIN_LOCAL 0xFF04
|
||||
#define BIP6_MULTICAST_SITE_LOCAL 0xFF05
|
||||
#define BIP6_MULTICAST_ORG_LOCAL 0xFF08
|
||||
#define BIP6_MULTICAST_GLOBAL 0xFF0E
|
||||
/** @} */
|
||||
|
||||
/* number of bytes in the IPv6 address */
|
||||
#define IP6_ADDRESS_MAX 16
|
||||
/* number of bytes in the B/IPv6 address */
|
||||
#define BIP6_ADDRESS_MAX 18
|
||||
|
||||
/**
|
||||
* BACnet IPv6 Address
|
||||
*
|
||||
* Data link layer addressing between B/IPv6 nodes consists of a 128-bit
|
||||
* IPv6 address followed by a two-octet UDP port number (both of which
|
||||
* shall be transmitted with the most significant octet first).
|
||||
* This address shall be referred to as a B/IPv6 address.
|
||||
* @{
|
||||
*/
|
||||
typedef struct BACnet_IP6_Address {
|
||||
uint8_t address[IP6_ADDRESS_MAX];
|
||||
uint16_t port;
|
||||
} BACNET_IP6_ADDRESS;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* BACnet /IPv6 Broadcast Distribution Table Format
|
||||
*
|
||||
* The BDT shall consist of either the eighteen-octet B/IPv6 address
|
||||
* of the peer BBMD or the combination of the fully qualified
|
||||
* domain name service (DNS) entry and UDP port that resolves to
|
||||
* the B/IPv6 address of the peer BBMD. The Broadcast
|
||||
* Distribution Table shall not contain an entry for the BBMD in
|
||||
* which the BDT resides.
|
||||
* @{
|
||||
*/
|
||||
struct BACnet_IP6_Broadcast_Distribution_Table_Entry;
|
||||
typedef struct BACnet_IP6_Broadcast_Distribution_Table_Entry {
|
||||
/* true if valid entry - false if not */
|
||||
bool valid;
|
||||
/* BACnet/IPv6 address */
|
||||
BACNET_IP6_ADDRESS bip6_address;
|
||||
struct BACnet_IP6_Broadcast_Distribution_Table_Entry *next;
|
||||
} BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Foreign Device Table (FDT)
|
||||
*
|
||||
* Each entry shall contain the B/IPv6 address and the TTL of the
|
||||
* registered foreign device.
|
||||
*
|
||||
* Each entry shall consist of the eighteen-octet B/IPv6 address of the
|
||||
* registrant; the 2-octet Time-to-Live value supplied at the time of
|
||||
* registration; and a 2-octet value representing the number of seconds
|
||||
* remaining before the BBMD will purge the registrant's FDT entry if no
|
||||
* re-registration occurs. The number of seconds remaining shall be
|
||||
* initialized to the 2-octet Time-to-Live value supplied at the time
|
||||
* of registration plus 30 seconds (see U.4.5.2), with a maximum of 65535.
|
||||
* @{
|
||||
*/
|
||||
struct BACnet_IP6_Foreign_Device_Table_Entry;
|
||||
typedef struct BACnet_IP6_Foreign_Device_Table_Entry {
|
||||
/* true if valid entry - false if not */
|
||||
bool valid;
|
||||
/* BACnet/IPv6 address */
|
||||
BACNET_IP6_ADDRESS bip6_address;
|
||||
/* requested time-to-live value */
|
||||
uint16_t ttl_seconds;
|
||||
/* number of seconds remaining */
|
||||
uint16_t ttl_seconds_remaining;
|
||||
struct BACnet_IP6_Foreign_Device_Table_Entry *next;
|
||||
} BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY;
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#endif /* __cplusplus */
|
||||
int bvlc6_encode_address(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
BACNET_IP6_ADDRESS * ip6_address);
|
||||
int bvlc6_decode_address(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
BACNET_IP6_ADDRESS * ip6_address);
|
||||
bool bvlc6_address_copy(
|
||||
BACNET_IP6_ADDRESS * dst,
|
||||
BACNET_IP6_ADDRESS * src);
|
||||
bool bvlc6_address_different(
|
||||
BACNET_IP6_ADDRESS * dst,
|
||||
BACNET_IP6_ADDRESS * src);
|
||||
|
||||
bool bvlc6_address_set(
|
||||
BACNET_IP6_ADDRESS * addr,
|
||||
uint16_t addr0,
|
||||
uint16_t addr1,
|
||||
uint16_t addr2,
|
||||
uint16_t addr3,
|
||||
uint16_t addr4,
|
||||
uint16_t addr5,
|
||||
uint16_t addr6,
|
||||
uint16_t addr7);
|
||||
bool bvlc6_address_get(
|
||||
BACNET_IP6_ADDRESS * addr,
|
||||
uint16_t *addr0,
|
||||
uint16_t *addr1,
|
||||
uint16_t *addr2,
|
||||
uint16_t *addr3,
|
||||
uint16_t *addr4,
|
||||
uint16_t *addr5,
|
||||
uint16_t *addr6,
|
||||
uint16_t *addr7);
|
||||
|
||||
bool bvlc6_vmac_address_set(
|
||||
BACNET_ADDRESS * addr,
|
||||
uint32_t device_id);
|
||||
bool bvlc6_vmac_address_get(
|
||||
BACNET_ADDRESS * addr,
|
||||
uint32_t *device_id);
|
||||
|
||||
int bvlc6_encode_header(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint8_t message_type,
|
||||
uint16_t length);
|
||||
int bvlc6_decode_header(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint8_t * message_type,
|
||||
uint16_t * length);
|
||||
|
||||
int bvlc6_encode_result(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac,
|
||||
uint16_t result_code);
|
||||
int bvlc6_decode_result(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac,
|
||||
uint16_t * result_code);
|
||||
|
||||
int bvlc6_encode_original_unicast(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
uint32_t vmac_dst,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len);
|
||||
int bvlc6_decode_original_unicast(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
uint32_t * vmac_dst,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_size,
|
||||
uint16_t * npdu_len);
|
||||
|
||||
int bvlc6_encode_original_broadcast(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len);
|
||||
int bvlc6_decode_original_broadcast(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_size,
|
||||
uint16_t * npdu_len);
|
||||
|
||||
int bvlc6_encode_address_resolution(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
uint32_t vmac_target);
|
||||
int bvlc6_decode_address_resolution(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
uint32_t * vmac_target);
|
||||
|
||||
int bvlc6_encode_forwarded_address_resolution(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
uint32_t vmac_target,
|
||||
BACNET_IP6_ADDRESS * bip6_address);
|
||||
int bvlc6_decode_forwarded_address_resolution(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
uint32_t * vmac_target,
|
||||
BACNET_IP6_ADDRESS * bip6_address);
|
||||
|
||||
int bvlc6_encode_address_resolution_ack(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
uint32_t vmac_dst);
|
||||
int bvlc6_decode_address_resolution_ack(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
uint32_t * vmac_dst);
|
||||
|
||||
int bvlc6_encode_virtual_address_resolution(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src);
|
||||
int bvlc6_decode_virtual_address_resolution(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src);
|
||||
|
||||
int bvlc6_encode_virtual_address_resolution_ack(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
uint32_t vmac_dst);
|
||||
int bvlc6_decode_virtual_address_resolution_ack(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
uint32_t * vmac_dst);
|
||||
|
||||
int bvlc6_encode_forwarded_npdu(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
BACNET_IP6_ADDRESS * address,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len);
|
||||
int bvlc6_decode_forwarded_npdu(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
BACNET_IP6_ADDRESS * address,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_size,
|
||||
uint16_t * npdu_len);
|
||||
|
||||
int bvlc6_encode_register_foreign_device(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
uint16_t ttl_seconds);
|
||||
int bvlc6_decode_register_foreign_device(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
uint16_t * ttl_seconds);
|
||||
|
||||
int bvlc6_encode_delete_foreign_device(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac_src,
|
||||
BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry);
|
||||
int bvlc6_decode_delete_foreign_device(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac_src,
|
||||
BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry);
|
||||
|
||||
int bvlc6_encode_secure_bvll(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint8_t * sbuf,
|
||||
uint16_t sbuf_len);
|
||||
int bvlc6_decode_secure_bvll(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint8_t * sbuf,
|
||||
uint16_t sbuf_size,
|
||||
uint16_t * sbuf_len);
|
||||
|
||||
int bvlc6_encode_distribute_broadcast_to_network(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_size,
|
||||
uint32_t vmac,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len);
|
||||
int bvlc6_decode_distribute_broadcast_to_network(
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len,
|
||||
uint32_t * vmac,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_size,
|
||||
uint16_t * npdu_len);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void test_BVLC6(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* */
|
||||
@@ -0,0 +1,269 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2004 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307
|
||||
USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "crc.h"
|
||||
|
||||
/** @file crc.c Calculate CRCs */
|
||||
|
||||
#if defined(CRC_USE_TABLE)
|
||||
/* note: table is created using unit test below */
|
||||
static const uint8_t HeaderCRC[256] = { 0x00, 0xfe, 0xff, 0x01, 0xfd, 0x03,
|
||||
0x02, 0xfc, 0xf9, 0x07, 0x06, 0xf8, 0x04, 0xfa, 0xfb, 0x05, 0xf1, 0x0f,
|
||||
0x0e, 0xf0, 0x0c, 0xf2, 0xf3, 0x0d, 0x08, 0xf6, 0xf7, 0x09, 0xf5, 0x0b,
|
||||
0x0a, 0xf4, 0xe1, 0x1f, 0x1e, 0xe0, 0x1c, 0xe2, 0xe3, 0x1d, 0x18, 0xe6,
|
||||
0xe7, 0x19, 0xe5, 0x1b, 0x1a, 0xe4, 0x10, 0xee, 0xef, 0x11, 0xed, 0x13,
|
||||
0x12, 0xec, 0xe9, 0x17, 0x16, 0xe8, 0x14, 0xea, 0xeb, 0x15, 0xc1, 0x3f,
|
||||
0x3e, 0xc0, 0x3c, 0xc2, 0xc3, 0x3d, 0x38, 0xc6, 0xc7, 0x39, 0xc5, 0x3b,
|
||||
0x3a, 0xc4, 0x30, 0xce, 0xcf, 0x31, 0xcd, 0x33, 0x32, 0xcc, 0xc9, 0x37,
|
||||
0x36, 0xc8, 0x34, 0xca, 0xcb, 0x35, 0x20, 0xde, 0xdf, 0x21, 0xdd, 0x23,
|
||||
0x22, 0xdc, 0xd9, 0x27, 0x26, 0xd8, 0x24, 0xda, 0xdb, 0x25, 0xd1, 0x2f,
|
||||
0x2e, 0xd0, 0x2c, 0xd2, 0xd3, 0x2d, 0x28, 0xd6, 0xd7, 0x29, 0xd5, 0x2b,
|
||||
0x2a, 0xd4, 0x81, 0x7f, 0x7e, 0x80, 0x7c, 0x82, 0x83, 0x7d, 0x78, 0x86,
|
||||
0x87, 0x79, 0x85, 0x7b, 0x7a, 0x84, 0x70, 0x8e, 0x8f, 0x71, 0x8d, 0x73,
|
||||
0x72, 0x8c, 0x89, 0x77, 0x76, 0x88, 0x74, 0x8a, 0x8b, 0x75, 0x60, 0x9e,
|
||||
0x9f, 0x61, 0x9d, 0x63, 0x62, 0x9c, 0x99, 0x67, 0x66, 0x98, 0x64, 0x9a,
|
||||
0x9b, 0x65, 0x91, 0x6f, 0x6e, 0x90, 0x6c, 0x92, 0x93, 0x6d, 0x68, 0x96,
|
||||
0x97, 0x69, 0x95, 0x6b, 0x6a, 0x94, 0x40, 0xbe, 0xbf, 0x41, 0xbd, 0x43,
|
||||
0x42, 0xbc, 0xb9, 0x47, 0x46, 0xb8, 0x44, 0xba, 0xbb, 0x45, 0xb1, 0x4f,
|
||||
0x4e, 0xb0, 0x4c, 0xb2, 0xb3, 0x4d, 0x48, 0xb6, 0xb7, 0x49, 0xb5, 0x4b,
|
||||
0x4a, 0xb4, 0xa1, 0x5f, 0x5e, 0xa0, 0x5c, 0xa2, 0xa3, 0x5d, 0x58, 0xa6,
|
||||
0xa7, 0x59, 0xa5, 0x5b, 0x5a, 0xa4, 0x50, 0xae, 0xaf, 0x51, 0xad, 0x53,
|
||||
0x52, 0xac, 0xa9, 0x57, 0x56, 0xa8, 0x54, 0xaa, 0xab, 0x55 };
|
||||
|
||||
uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue)
|
||||
{
|
||||
return HeaderCRC[crcValue ^ dataValue];
|
||||
}
|
||||
|
||||
/* note: table is created using unit test below */
|
||||
static const uint16_t DataCRC[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624,
|
||||
0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5,
|
||||
0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7,
|
||||
0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||||
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a,
|
||||
0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a,
|
||||
0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9,
|
||||
0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f,
|
||||
0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868,
|
||||
0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528,
|
||||
0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb,
|
||||
0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||||
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387,
|
||||
0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46,
|
||||
0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a,
|
||||
0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb,
|
||||
0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad,
|
||||
0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c,
|
||||
0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c,
|
||||
0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||||
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3,
|
||||
0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785,
|
||||
0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956,
|
||||
0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416,
|
||||
0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1,
|
||||
0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3,
|
||||
0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70,
|
||||
0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||||
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 };
|
||||
|
||||
uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue)
|
||||
{
|
||||
return ((crcValue >> 8) ^ DataCRC[(crcValue & 0x00FF) ^ dataValue]);
|
||||
}
|
||||
#else
|
||||
/* Accumulate "dataValue" into the CRC in crcValue. */
|
||||
/* Return value is updated CRC */
|
||||
/* */
|
||||
/* The ^ operator means exclusive OR. */
|
||||
/* Note: This function is copied directly from the BACnet standard. */
|
||||
uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue)
|
||||
{
|
||||
uint16_t crc;
|
||||
|
||||
crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
|
||||
|
||||
/* Exclusive OR the terms in the table (top down) */
|
||||
crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3) ^ (crc << 4) ^ (crc << 5) ^
|
||||
(crc << 6) ^ (crc << 7);
|
||||
|
||||
/* Combine bits shifted out left hand end */
|
||||
return (crc & 0xfe) ^ ((crc >> 8) & 1);
|
||||
}
|
||||
|
||||
/* Accumulate "dataValue" into the CRC in crcValue. */
|
||||
/* Return value is updated CRC */
|
||||
/* */
|
||||
/* The ^ operator means exclusive OR. */
|
||||
/* Note: This function is copied directly from the BACnet standard. */
|
||||
uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue)
|
||||
{
|
||||
uint16_t crcLow;
|
||||
|
||||
crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */
|
||||
|
||||
/* Exclusive OR the terms in the table (top down) */
|
||||
return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3) ^ (crcLow << 12) ^
|
||||
(crcLow >> 4) ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
#include "bacnet/bytes.h"
|
||||
|
||||
/* test from Annex G 1.0 of BACnet Standard */
|
||||
void testCRC8(Test *pTest)
|
||||
{
|
||||
uint8_t crc = 0xff; /* accumulates the crc value */
|
||||
uint8_t frame_crc; /* appended to the end of the frame */
|
||||
|
||||
crc = CRC_Calc_Header(0x00, crc);
|
||||
ct_test(pTest, crc == 0x55);
|
||||
crc = CRC_Calc_Header(0x10, crc);
|
||||
ct_test(pTest, crc == 0xC2);
|
||||
crc = CRC_Calc_Header(0x05, crc);
|
||||
ct_test(pTest, crc == 0xBC);
|
||||
crc = CRC_Calc_Header(0x00, crc);
|
||||
ct_test(pTest, crc == 0x95);
|
||||
crc = CRC_Calc_Header(0x00, crc);
|
||||
ct_test(pTest, crc == 0x73);
|
||||
/* send the ones complement of the CRC in place of */
|
||||
/* the CRC, and the resulting CRC will always equal 0x55. */
|
||||
frame_crc = ~crc;
|
||||
ct_test(pTest, frame_crc == 0x8C);
|
||||
/* use the ones complement value and the next to last CRC value */
|
||||
crc = CRC_Calc_Header(frame_crc, crc);
|
||||
ct_test(pTest, crc == 0x55);
|
||||
}
|
||||
|
||||
/* test from Annex G 2.0 of BACnet Standard */
|
||||
void testCRC16(Test *pTest)
|
||||
{
|
||||
uint16_t crc = 0xffff;
|
||||
uint16_t data_crc;
|
||||
|
||||
crc = CRC_Calc_Data(0x01, crc);
|
||||
ct_test(pTest, crc == 0x1E0E);
|
||||
crc = CRC_Calc_Data(0x22, crc);
|
||||
ct_test(pTest, crc == 0xEB70);
|
||||
crc = CRC_Calc_Data(0x30, crc);
|
||||
ct_test(pTest, crc == 0x42EF);
|
||||
/* send the ones complement of the CRC in place of */
|
||||
/* the CRC, and the resulting CRC will always equal 0xF0B8. */
|
||||
data_crc = ~crc;
|
||||
ct_test(pTest, data_crc == 0xBD10);
|
||||
crc = CRC_Calc_Data(LO_BYTE(data_crc), crc);
|
||||
ct_test(pTest, crc == 0x0F3A);
|
||||
crc = CRC_Calc_Data(HI_BYTE(data_crc), crc);
|
||||
ct_test(pTest, crc == 0xF0B8);
|
||||
}
|
||||
|
||||
void testCRC8CreateTable(Test *pTest)
|
||||
{
|
||||
uint8_t crc = 0xff; /* accumulates the crc value */
|
||||
int i;
|
||||
|
||||
(void)pTest;
|
||||
printf("static const uint8_t HeaderCRC[256] =\n");
|
||||
printf("{\n");
|
||||
printf(" ");
|
||||
for (i = 0; i < 256; i++) {
|
||||
crc = CRC_Calc_Header(i, 0);
|
||||
printf("0x%02x, ", crc);
|
||||
if (!((i + 1) % 8)) {
|
||||
printf("\n");
|
||||
if (i != 255) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
void testCRC16CreateTable(Test *pTest)
|
||||
{
|
||||
uint16_t crc;
|
||||
int i;
|
||||
|
||||
(void)pTest;
|
||||
printf("static const uint16_t DataCRC[256] =\n");
|
||||
printf("{\n");
|
||||
printf(" ");
|
||||
for (i = 0; i < 256; i++) {
|
||||
crc = CRC_Calc_Data(i, 0);
|
||||
printf("0x%04x, ", crc);
|
||||
if (!((i + 1) % 8)) {
|
||||
printf("\n");
|
||||
if (i != 255) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TEST_CRC
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("crc", NULL);
|
||||
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testCRC8);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testCRC16);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testCRC8CreateTable);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testCRC16CreateTable);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef CRC_H
|
||||
#define CRC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
uint8_t CRC_Calc_Header(
|
||||
uint8_t dataValue,
|
||||
uint8_t crcValue);
|
||||
uint16_t CRC_Calc_Data(
|
||||
uint8_t dataValue,
|
||||
uint16_t crcValue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,176 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2007 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
/** @file datalink.c Optional run-time assignment of datalink transport */
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
|
||||
#if defined(BACDL_ALL) || defined FOR_DOXYGEN
|
||||
#include "bacnet/datalink/ethernet.h"
|
||||
#include "bacnet/datalink/bip.h"
|
||||
#include "bacnet/datalink/bip6.h"
|
||||
#include "bacnet/datalink/bvlc.h"
|
||||
#include "bacnet/datalink/arcnet.h"
|
||||
#include "bacnet/datalink/dlmstp.h"
|
||||
#include <string.h>
|
||||
/* Function pointers - point to your datalink */
|
||||
|
||||
/** Function template to Initialize the DataLink services at the given
|
||||
interface.
|
||||
* @ingroup DLTemplates
|
||||
*
|
||||
* @note For Linux, ifname is eth0, ath0, arc0, ttyS0, and others.
|
||||
For Windows, ifname is the COM port or dotted ip address of the
|
||||
interface.
|
||||
|
||||
* @param ifname [in] The named interface to use for the network layer.
|
||||
* @return True if the interface is successfully initialized,
|
||||
* else False if the initialization fails.
|
||||
*/
|
||||
bool (*datalink_init)(char *ifname);
|
||||
|
||||
/** Function template to send a packet via the DataLink.
|
||||
* @ingroup DLTemplates
|
||||
*
|
||||
* @param dest [in] Destination address.
|
||||
* @param npdu_data [in] The NPDU header (Network) information.
|
||||
* @param pdu [in] Buffer of data to be sent - may be null.
|
||||
* @param pdu_len [in] Number of bytes in the pdu buffer.
|
||||
* @return Number of bytes sent on success, negative number on failure.
|
||||
*/
|
||||
int (*datalink_send_pdu)(BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *pdu,
|
||||
unsigned pdu_len);
|
||||
|
||||
uint16_t (*datalink_receive)(
|
||||
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout);
|
||||
|
||||
/** Function template to close the DataLink services and perform any cleanup.
|
||||
* @ingroup DLTemplates
|
||||
*/
|
||||
void (*datalink_cleanup)(void);
|
||||
|
||||
void (*datalink_get_broadcast_address)(BACNET_ADDRESS *dest);
|
||||
|
||||
void (*datalink_get_my_address)(BACNET_ADDRESS *my_address);
|
||||
|
||||
void datalink_set(char *datalink_string)
|
||||
{
|
||||
if (strcasecmp("bip", datalink_string) == 0) {
|
||||
datalink_init = bip_init;
|
||||
datalink_send_pdu = bip_send_pdu;
|
||||
datalink_receive = bip_receive;
|
||||
datalink_cleanup = bip_cleanup;
|
||||
datalink_get_broadcast_address = bip_get_broadcast_address;
|
||||
datalink_get_my_address = bip_get_my_address;
|
||||
} else if (strcasecmp("bvlc", datalink_string) == 0) {
|
||||
datalink_init = bip_init;
|
||||
datalink_send_pdu = bvlc_send_pdu;
|
||||
datalink_receive = bvlc_receive;
|
||||
datalink_cleanup = bip_cleanup;
|
||||
datalink_get_broadcast_address = bip_get_broadcast_address;
|
||||
datalink_get_my_address = bip_get_my_address;
|
||||
} else if (strcasecmp("bip6", datalink_string) == 0) {
|
||||
datalink_init = bip6_init;
|
||||
datalink_send_pdu = bip6_send_pdu;
|
||||
datalink_receive = bip6_receive;
|
||||
datalink_cleanup = bip6_cleanup;
|
||||
datalink_get_broadcast_address = bip6_get_broadcast_address;
|
||||
datalink_get_my_address = bip6_get_my_address;
|
||||
} else if (strcasecmp("bvlc6", datalink_string) == 0) {
|
||||
datalink_init = bip6_init;
|
||||
datalink_send_pdu = bvlc6_send_pdu;
|
||||
datalink_receive = bvlc6_receive;
|
||||
datalink_cleanup = bip6_cleanup;
|
||||
datalink_get_broadcast_address = bip6_get_broadcast_address;
|
||||
datalink_get_my_address = bip6_get_my_address;
|
||||
} else if (strcasecmp("ethernet", datalink_string) == 0) {
|
||||
datalink_init = ethernet_init;
|
||||
datalink_send_pdu = ethernet_send_pdu;
|
||||
datalink_receive = ethernet_receive;
|
||||
datalink_cleanup = ethernet_cleanup;
|
||||
datalink_get_broadcast_address = ethernet_get_broadcast_address;
|
||||
datalink_get_my_address = ethernet_get_my_address;
|
||||
} else if (strcasecmp("arcnet", datalink_string) == 0) {
|
||||
datalink_init = arcnet_init;
|
||||
datalink_send_pdu = arcnet_send_pdu;
|
||||
datalink_receive = arcnet_receive;
|
||||
datalink_cleanup = arcnet_cleanup;
|
||||
datalink_get_broadcast_address = arcnet_get_broadcast_address;
|
||||
datalink_get_my_address = arcnet_get_my_address;
|
||||
} else if (strcasecmp("mstp", datalink_string) == 0) {
|
||||
datalink_init = dlmstp_init;
|
||||
datalink_send_pdu = dlmstp_send_pdu;
|
||||
datalink_receive = dlmstp_receive;
|
||||
datalink_cleanup = dlmstp_cleanup;
|
||||
datalink_get_broadcast_address = dlmstp_get_broadcast_address;
|
||||
datalink_get_my_address = dlmstp_get_my_address;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACDL_NONE)
|
||||
int datalink_send_pdu(BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *pdu,
|
||||
unsigned pdu_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t datalink_receive(
|
||||
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void datalink_cleanup(void)
|
||||
{
|
||||
}
|
||||
|
||||
void datalink_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||
{
|
||||
}
|
||||
|
||||
void datalink_get_my_address(BACNET_ADDRESS *my_address)
|
||||
{
|
||||
}
|
||||
|
||||
void datalink_set_interface(char *ifname)
|
||||
{
|
||||
}
|
||||
|
||||
void datalink_set(char *datalink_string)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,154 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef DATALINK_H
|
||||
#define DATALINK_H
|
||||
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
#if defined(BACDL_ETHERNET)
|
||||
#include "bacnet/datalink/ethernet.h"
|
||||
|
||||
#define datalink_init ethernet_init
|
||||
#define datalink_send_pdu ethernet_send_pdu
|
||||
#define datalink_receive ethernet_receive
|
||||
#define datalink_cleanup ethernet_cleanup
|
||||
#define datalink_get_broadcast_address ethernet_get_broadcast_address
|
||||
#define datalink_get_my_address ethernet_get_my_address
|
||||
|
||||
#elif defined(BACDL_ARCNET)
|
||||
#include "bacnet/datalink/arcnet.h"
|
||||
|
||||
#define datalink_init arcnet_init
|
||||
#define datalink_send_pdu arcnet_send_pdu
|
||||
#define datalink_receive arcnet_receive
|
||||
#define datalink_cleanup arcnet_cleanup
|
||||
#define datalink_get_broadcast_address arcnet_get_broadcast_address
|
||||
#define datalink_get_my_address arcnet_get_my_address
|
||||
|
||||
#elif defined(BACDL_MSTP)
|
||||
#include "bacnet/datalink/dlmstp.h"
|
||||
|
||||
#define datalink_init dlmstp_init
|
||||
#define datalink_send_pdu dlmstp_send_pdu
|
||||
#define datalink_receive dlmstp_receive
|
||||
#define datalink_cleanup dlmstp_cleanup
|
||||
#define datalink_get_broadcast_address dlmstp_get_broadcast_address
|
||||
#define datalink_get_my_address dlmstp_get_my_address
|
||||
|
||||
#elif defined(BACDL_BIP)
|
||||
#include "bacnet/datalink/bip.h"
|
||||
#include "bacnet/datalink/bvlc.h"
|
||||
|
||||
#define datalink_init bip_init
|
||||
#if defined(BBMD_ENABLED) && BBMD_ENABLED
|
||||
#define datalink_send_pdu bvlc_send_pdu
|
||||
#define datalink_receive bvlc_receive
|
||||
#else
|
||||
#define datalink_send_pdu bip_send_pdu
|
||||
#define datalink_receive bip_receive
|
||||
#endif
|
||||
#define datalink_cleanup bip_cleanup
|
||||
#define datalink_get_broadcast_address bip_get_broadcast_address
|
||||
#ifdef BAC_ROUTING
|
||||
extern void routed_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
#define datalink_get_my_address routed_get_my_address
|
||||
#else
|
||||
#define datalink_get_my_address bip_get_my_address
|
||||
#endif
|
||||
|
||||
#elif defined(BACDL_BIP6)
|
||||
#include "bacnet/datalink/bip6.h"
|
||||
#include "bacnet/datalink/bvlc6.h"
|
||||
#define datalink_init bip6_init
|
||||
#define datalink_send_pdu bip6_send_pdu
|
||||
#define datalink_receive bip6_receive
|
||||
#define datalink_cleanup bip6_cleanup
|
||||
#define datalink_get_broadcast_address bip6_get_broadcast_address
|
||||
#define datalink_get_my_address bip6_get_my_address
|
||||
|
||||
#elif defined(BACDL_ALL) || defined(BACDL_NONE)
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
#define MAX_HEADER (8)
|
||||
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int datalink_send_pdu(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t * pdu,
|
||||
unsigned pdu_len);
|
||||
extern uint16_t datalink_receive(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * pdu,
|
||||
uint16_t max_pdu,
|
||||
unsigned timeout);
|
||||
extern void datalink_cleanup(
|
||||
void);
|
||||
extern void datalink_get_broadcast_address(
|
||||
BACNET_ADDRESS * dest);
|
||||
extern void datalink_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
extern void datalink_set_interface(
|
||||
char *ifname);
|
||||
extern void datalink_set(
|
||||
char *datalink_string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
/** @defgroup DataLink The BACnet Network (DataLink) Layer
|
||||
* <b>6 THE NETWORK LAYER </b><br>
|
||||
* The purpose of the BACnet network layer is to provide the means by which
|
||||
* messages can be relayed from one BACnet network to another, regardless of
|
||||
* the BACnet data link technology in use on that network. Whereas the data
|
||||
* link layer provides the capability to address messages to a single device
|
||||
* or broadcast them to all devices on the local network, the network layer
|
||||
* allows messages to be directed to a single remote device, broadcast on a
|
||||
* remote network, or broadcast globally to all devices on all networks.
|
||||
* A BACnet Device is uniquely located by a network number and a MAC address.
|
||||
*
|
||||
* Each client or server application must define exactly one of these
|
||||
* DataLink settings, which will control which parts of the code will be built:
|
||||
* - BACDL_ETHERNET -- for Clause 7 ISO 8802-3 ("Ethernet") LAN
|
||||
* - BACDL_ARCNET -- for Clause 8 ARCNET LAN
|
||||
* - BACDL_MSTP -- for Clause 9 MASTER-SLAVE/TOKEN PASSING (MS/TP) LAN
|
||||
* - BACDL_BIP -- for ANNEX J - BACnet/IP
|
||||
* - BACDL_ALL -- Unspecified for the build, so the transport can be
|
||||
* chosen at runtime from among these choices.
|
||||
* - Clause 10 POINT-TO-POINT (PTP) and Clause 11 EIA/CEA-709.1 ("LonTalk") LAN
|
||||
* are not currently supported by this project.
|
||||
*//** @defgroup DLTemplates DataLink Template Functions
|
||||
* @ingroup DataLink
|
||||
* Most of the functions in this group are function templates which are assigned
|
||||
* to a specific DataLink network layer implementation either at compile time or
|
||||
* at runtime.
|
||||
*/
|
||||
#endif
|
||||
@@ -0,0 +1,506 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* environment variables used for the command line tools */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
#include "bacnet/basic/object/netport.h"
|
||||
#endif
|
||||
|
||||
/** @file dlenv.c Initialize the DataLink configuration. */
|
||||
|
||||
#if defined(BACDL_BIP)
|
||||
/* timer used to renew Foreign Device Registration */
|
||||
static uint16_t BBMD_Timer_Seconds;
|
||||
/* BBMD variables */
|
||||
static long bbmd_timetolive_seconds = 60000;
|
||||
static long bbmd_port = 0xBAC0;
|
||||
static long bbmd_address = 0;
|
||||
static long bbmd_mask = 0xFFFFFFFF;
|
||||
static int bbmd_result = 0;
|
||||
static BBMD_TABLE_ENTRY BBMD_Table_Entry;
|
||||
|
||||
/* Simple setters for BBMD registration variables. */
|
||||
|
||||
/** Sets the IPv4 address for BBMD registration.
|
||||
* If not set here or provided by Environment variables,
|
||||
* no BBMD registration will occur.
|
||||
* @param address - IPv4 address (long) of BBMD to register with,
|
||||
* in network byte order.
|
||||
*/
|
||||
void dlenv_bbmd_address_set(long address)
|
||||
{
|
||||
bbmd_address = address;
|
||||
}
|
||||
|
||||
/** Set the port for BBMD registration.
|
||||
* Default if not set is 0xBAC0.
|
||||
* @param port - The port number (provided in network byte order).
|
||||
*/
|
||||
void dlenv_bbmd_port_set(int port)
|
||||
{
|
||||
bbmd_port = port;
|
||||
}
|
||||
|
||||
/** Set the Lease Time (Time-to-Live) for BBMD registration.
|
||||
* Default if not set is 60000 (1000 minutes).
|
||||
* @param ttl_secs - The Lease Time, in seconds.
|
||||
*/
|
||||
void dlenv_bbmd_ttl_set(int ttl_secs)
|
||||
{
|
||||
bbmd_timetolive_seconds = ttl_secs;
|
||||
}
|
||||
|
||||
/** Get the result of the last attempt to register with the indicated BBMD.
|
||||
* If we sent a foreign registration request, then see if we've received
|
||||
* a NAK in our BVLC handler.
|
||||
*
|
||||
* @return Positive number (of bytes sent) if registration was successful,
|
||||
* 0 if no registration request was made, or
|
||||
* -1 if registration attempt failed.
|
||||
*/
|
||||
int dlenv_bbmd_result(void)
|
||||
{
|
||||
if ((bbmd_result > 0) &&
|
||||
(bvlc_get_last_result() == BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK)) {
|
||||
return -1;
|
||||
}
|
||||
/* Else, show our send: */
|
||||
return bbmd_result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Register as a Foreign Device with the designated BBMD.
|
||||
* @ingroup DataLink
|
||||
* The BBMD's address, port, and lease time must be provided by
|
||||
* internal variables or Environment variables.
|
||||
* If no address for the BBMD is provided, no BBMD registration will occur.
|
||||
*
|
||||
* The Environment Variables depend on define of BACDL_BIP:
|
||||
* - BACNET_BBMD_PORT - 0..65534, defaults to 47808
|
||||
* - BACNET_BBMD_TIMETOLIVE - 0..65535 seconds, defaults to 60000
|
||||
* - BACNET_BBMD_ADDRESS - dotted IPv4 address
|
||||
* @return Positive number (of bytes sent) on success,
|
||||
* 0 if no registration request is sent, or
|
||||
* -1 if registration fails.
|
||||
*/
|
||||
int dlenv_register_as_foreign_device(void)
|
||||
{
|
||||
int retval = 0;
|
||||
#if defined(BACDL_BIP)
|
||||
char *pEnv = NULL;
|
||||
unsigned a[4] = { 0 };
|
||||
char bbmd_env[32] = "";
|
||||
unsigned entry_number = 0;
|
||||
int c;
|
||||
|
||||
pEnv = getenv("BACNET_BBMD_PORT");
|
||||
if (pEnv) {
|
||||
bbmd_port = strtol(pEnv, NULL, 0);
|
||||
if (bbmd_port > 0xFFFF) {
|
||||
bbmd_port = 0xBAC0;
|
||||
}
|
||||
}
|
||||
pEnv = getenv("BACNET_BBMD_TIMETOLIVE");
|
||||
if (pEnv) {
|
||||
bbmd_timetolive_seconds = strtol(pEnv, NULL, 0);
|
||||
if (bbmd_timetolive_seconds > 0xFFFF) {
|
||||
bbmd_timetolive_seconds = 0xFFFF;
|
||||
}
|
||||
}
|
||||
pEnv = getenv("BACNET_BBMD_ADDRESS");
|
||||
if (pEnv) {
|
||||
bbmd_address = bip_getaddrbyname(pEnv);
|
||||
}
|
||||
if (bbmd_address) {
|
||||
struct in_addr addr;
|
||||
addr.s_addr = bbmd_address;
|
||||
fprintf(stderr, "Registering with BBMD at %s:%ld for %ld seconds\n",
|
||||
inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds);
|
||||
retval = bvlc_register_with_bbmd(bbmd_address,
|
||||
htons((uint16_t)bbmd_port), (uint16_t)bbmd_timetolive_seconds);
|
||||
if (retval < 0) {
|
||||
fprintf(stderr, "FAILED to Register with BBMD at %s \n",
|
||||
inet_ntoa(addr));
|
||||
}
|
||||
BBMD_Timer_Seconds = (uint16_t)bbmd_timetolive_seconds;
|
||||
} else {
|
||||
for (entry_number = 1; entry_number <= 128; entry_number++) {
|
||||
sprintf(bbmd_env, "BACNET_BDT_ADDR_%u", entry_number);
|
||||
pEnv = getenv(bbmd_env);
|
||||
if (pEnv) {
|
||||
bbmd_address = bip_getaddrbyname(pEnv);
|
||||
} else if (entry_number == 1) {
|
||||
/* BDT 1 is self (note: can be overridden) */
|
||||
bbmd_address = bip_get_addr();
|
||||
}
|
||||
if (bbmd_address) {
|
||||
bbmd_port = 0xBAC0;
|
||||
sprintf(bbmd_env, "BACNET_BDT_PORT_%u", entry_number);
|
||||
pEnv = getenv(bbmd_env);
|
||||
if (pEnv) {
|
||||
bbmd_port = strtol(pEnv, NULL, 0);
|
||||
if (bbmd_port > 0xFFFF) {
|
||||
bbmd_port = 0xBAC0;
|
||||
}
|
||||
} else if (entry_number == 1) {
|
||||
/* BDT 1 is self (note: can be overridden) */
|
||||
bbmd_port = bip_get_port();
|
||||
}
|
||||
bbmd_mask = 0xFFFFFFFF;
|
||||
sprintf(bbmd_env, "BACNET_BDT_MASK_%u", entry_number);
|
||||
pEnv = getenv(bbmd_env);
|
||||
if (pEnv) {
|
||||
c = sscanf(
|
||||
pEnv, "%3u.%3u.%3u.%3u", &a[0], &a[1], &a[2], &a[3]);
|
||||
if (c == 4) {
|
||||
bbmd_mask = ((a[0] & 0xFF) << 24) |
|
||||
((a[1] & 0xFF) << 16) | ((a[2] & 0xFF) << 8) |
|
||||
(a[3] & 0xFF);
|
||||
}
|
||||
}
|
||||
BBMD_Table_Entry.valid = true;
|
||||
BBMD_Table_Entry.dest_address.s_addr = bbmd_address;
|
||||
BBMD_Table_Entry.dest_port = bbmd_port;
|
||||
BBMD_Table_Entry.broadcast_mask.s_addr = bbmd_mask;
|
||||
bvlc_add_bdt_entry_local(&BBMD_Table_Entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
bbmd_result = retval;
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
#if defined(BACDL_BIP)
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
uint32_t instance = 1;
|
||||
uint32_t address = 0;
|
||||
uint32_t broadcast = 0;
|
||||
uint32_t test_broadcast = 0;
|
||||
uint32_t mask = 0;
|
||||
uint16_t port = 0;
|
||||
uint8_t mac[4 + 2] = { 0 };
|
||||
uint8_t prefix = 0;
|
||||
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "BACnet/IP Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_BIP);
|
||||
port = bip_get_port();
|
||||
Network_Port_BIP_Port_Set(instance, port);
|
||||
address = bip_get_addr();
|
||||
memcpy(&mac[0], &address, 4);
|
||||
memcpy(&mac[4], &port, 2);
|
||||
Network_Port_MAC_Address_Set(instance, &mac[0], 6);
|
||||
broadcast = bip_get_broadcast_addr();
|
||||
for (prefix = 0; prefix < 32; prefix++) {
|
||||
mask = htonl((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF);
|
||||
test_broadcast = (address & mask) | (~mask);
|
||||
if (test_broadcast == broadcast) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Network_Port_IP_Subnet_Prefix_Set(instance, prefix);
|
||||
Network_Port_Link_Speed_Set(instance, 0.0);
|
||||
/* common NP data */
|
||||
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||
Network_Port_Out_Of_Service_Set(instance, false);
|
||||
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||
Network_Port_Network_Number_Set(instance, 0);
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#elif defined(BACDL_MSTP)
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
uint32_t instance = 1;
|
||||
uint8_t mac[1] = { 0 };
|
||||
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "MS/TP Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_MSTP);
|
||||
Network_Port_MSTP_Max_Master_Set(instance, dlmstp_max_master());
|
||||
Network_Port_MSTP_Max_Info_Frames_Set(instance, dlmstp_max_info_frames());
|
||||
Network_Port_Link_Speed_Set(instance, dlmstp_baud_rate());
|
||||
mac[0] = dlmstp_mac_address();
|
||||
Network_Port_MAC_Address_Set(instance, &mac[0], 1);
|
||||
/* common NP data */
|
||||
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||
Network_Port_Out_Of_Service_Set(instance, false);
|
||||
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||
Network_Port_Network_Number_Set(instance, 0);
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#elif defined(BACDL_BIP6)
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
uint32_t instance = 1;
|
||||
uint8_t prefix = 0;
|
||||
BACNET_ADDRESS addr = { 0 };
|
||||
BACNET_IP6_ADDRESS addr6 = { 0 };
|
||||
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "BACnet/IPv6 Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_BIP6);
|
||||
Network_Port_BIP6_Port_Set(instance, bip6_get_port());
|
||||
bip6_get_my_address(&addr);
|
||||
Network_Port_MAC_Address_Set(instance, &addr.mac[0], addr.mac_len);
|
||||
bip6_get_addr(&addr6);
|
||||
Network_Port_IPv6_Address_Set(instance, &addr6.address[0]);
|
||||
bip6_get_broadcast_addr(&addr6);
|
||||
Network_Port_IPv6_Multicast_Address_Set(instance, &addr6.address[0]);
|
||||
Network_Port_IPv6_Subnet_Prefix_Set(instance, prefix);
|
||||
|
||||
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||
Network_Port_Link_Speed_Set(instance, 0.0);
|
||||
Network_Port_Out_Of_Service_Set(instance, false);
|
||||
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||
Network_Port_Network_Number_Set(instance, 0);
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Datalink maintenance timer
|
||||
* @ingroup DataLink
|
||||
*
|
||||
* Call this function to renew our Foreign Device Registration
|
||||
* @param elapsed_seconds Number of seconds that have elapsed since last called.
|
||||
*/
|
||||
void dlenv_maintenance_timer(uint16_t elapsed_seconds)
|
||||
{
|
||||
#if defined(BACDL_BIP)
|
||||
if (BBMD_Timer_Seconds) {
|
||||
if (BBMD_Timer_Seconds <= elapsed_seconds) {
|
||||
BBMD_Timer_Seconds = 0;
|
||||
} else {
|
||||
BBMD_Timer_Seconds -= elapsed_seconds;
|
||||
}
|
||||
if (BBMD_Timer_Seconds == 0) {
|
||||
(void)dlenv_register_as_foreign_device();
|
||||
/* If that failed (negative), maybe just a network issue.
|
||||
* If nothing happened (0), may be un/misconfigured.
|
||||
* Set up to try again later in all cases. */
|
||||
BBMD_Timer_Seconds = (uint16_t)bbmd_timetolive_seconds;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Initialize the DataLink configuration from Environment variables,
|
||||
* or else to defaults.
|
||||
* @ingroup DataLink
|
||||
* The items configured depend on which BACDL_ the code is built for,
|
||||
* eg, BACDL_BIP.
|
||||
*
|
||||
* For most items, checks first for an environment variable, and, if
|
||||
* found, uses that to set the item's value. Otherwise, will set
|
||||
* to a default value.
|
||||
*
|
||||
* The Environment Variables, by BACDL_ type, are:
|
||||
* - BACDL_ALL: (the general-purpose solution)
|
||||
* - BACNET_DATALINK to set which BACDL_ type we are using.
|
||||
* - (Any):
|
||||
* - BACNET_APDU_TIMEOUT - set this value in milliseconds to change
|
||||
* the APDU timeout. APDU Timeout is how much time a client
|
||||
* waits for a response from a BACnet device.
|
||||
* - BACNET_APDU_RETRIES - indicate the maximum number of times that
|
||||
* an APDU shall be retransmitted.
|
||||
* - BACNET_IFACE - set this value to dotted IP address (Windows) of
|
||||
* the interface (see ipconfig command on Windows) for which you
|
||||
* want to bind. On Linux, set this to the /dev interface
|
||||
* (i.e. eth0, arc0). Default is eth0 on Linux, and the default
|
||||
* interface on Windows. Hence, if there is only a single network
|
||||
* interface on Windows, the applications will choose it, and this
|
||||
* setting will not be needed.
|
||||
* - BACDL_BIP: (BACnet/IP)
|
||||
* - BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
|
||||
* communications. Default is 47808 (0xBAC0).
|
||||
* - BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign
|
||||
* Device Registration. Defaults to 47808 (0xBAC0).
|
||||
* - BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
|
||||
* Registration (0..65535). Defaults to 60000 seconds.
|
||||
* - BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign
|
||||
* Device Registrar.
|
||||
* - BACNET_BDT_ADDR_1 - dotted IPv4 address of the BBMD table entry 1..128
|
||||
* - BACNET_BDT_PORT_1 - UDP port of the BBMD table entry 1..128 (optional)
|
||||
* - BACNET_BDT_MASK_1 - dotted IPv4 mask of the BBMD table
|
||||
* entry 1..128 (optional)
|
||||
* - BACNET_IP_NAT_ADDR - dotted IPv4 address of the public facing router
|
||||
* - BACDL_MSTP: (BACnet MS/TP)
|
||||
* - BACNET_MAX_INFO_FRAMES
|
||||
* - BACNET_MAX_MASTER
|
||||
* - BACNET_MSTP_BAUD
|
||||
* - BACNET_MSTP_MAC
|
||||
* - BACDL_BIP6: (BACnet/IPv6)
|
||||
* - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6
|
||||
* communications. Default is 47808 (0xBAC0).
|
||||
* - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ...
|
||||
*/
|
||||
void dlenv_init(void)
|
||||
{
|
||||
char *pEnv = NULL;
|
||||
|
||||
#if defined(BACDL_ALL)
|
||||
pEnv = getenv("BACNET_DATALINK");
|
||||
if (pEnv) {
|
||||
datalink_set(pEnv);
|
||||
} else {
|
||||
datalink_set(NULL);
|
||||
}
|
||||
#endif
|
||||
#if defined(BACDL_BIP6)
|
||||
BACNET_IP6_ADDRESS addr;
|
||||
pEnv = getenv("BACNET_BIP6_BROADCAST");
|
||||
if (pEnv) {
|
||||
bvlc6_address_set(&addr, (uint16_t)strtol(pEnv, NULL, 0), 0, 0, 0, 0, 0,
|
||||
0, BIP6_MULTICAST_GROUP_ID);
|
||||
bip6_set_broadcast_addr(&addr);
|
||||
} else {
|
||||
bvlc6_address_set(&addr, BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0,
|
||||
BIP6_MULTICAST_GROUP_ID);
|
||||
bip6_set_broadcast_addr(&addr);
|
||||
}
|
||||
pEnv = getenv("BACNET_BIP6_PORT");
|
||||
if (pEnv) {
|
||||
bip6_set_port((uint16_t)strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
bip6_set_port(0xBAC0);
|
||||
}
|
||||
#endif
|
||||
#if defined(BACDL_BIP)
|
||||
#if defined(BIP_DEBUG)
|
||||
BIP_Debug = true;
|
||||
#endif
|
||||
pEnv = getenv("BACNET_IP_PORT");
|
||||
if (pEnv) {
|
||||
bip_set_port(htons((uint16_t)strtol(pEnv, NULL, 0)));
|
||||
} else {
|
||||
/* BIP_Port is statically initialized to 0xBAC0,
|
||||
* so if it is different, then it was programmatically altered,
|
||||
* and we shouldn't just stomp on it here.
|
||||
* Unless it is set below 1024, since:
|
||||
* "The range for well-known ports managed by the IANA is 0-1023."
|
||||
*/
|
||||
if (ntohs(bip_get_port()) < 1024) {
|
||||
bip_set_port(htons(0xBAC0));
|
||||
}
|
||||
}
|
||||
pEnv = getenv("BACNET_IP_NAT_ADDR");
|
||||
if (pEnv) {
|
||||
struct in_addr nat_addr;
|
||||
nat_addr.s_addr = bip_getaddrbyname(pEnv);
|
||||
if (nat_addr.s_addr) {
|
||||
bvlc_set_global_address_for_nat(&nat_addr);
|
||||
}
|
||||
}
|
||||
#elif defined(BACDL_MSTP)
|
||||
pEnv = getenv("BACNET_MAX_INFO_FRAMES");
|
||||
if (pEnv) {
|
||||
dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_max_info_frames(1);
|
||||
}
|
||||
pEnv = getenv("BACNET_MAX_MASTER");
|
||||
if (pEnv) {
|
||||
dlmstp_set_max_master(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_max_master(127);
|
||||
}
|
||||
pEnv = getenv("BACNET_MSTP_BAUD");
|
||||
if (pEnv) {
|
||||
dlmstp_set_baud_rate(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_baud_rate(38400);
|
||||
}
|
||||
pEnv = getenv("BACNET_MSTP_MAC");
|
||||
if (pEnv) {
|
||||
dlmstp_set_mac_address(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_mac_address(127);
|
||||
}
|
||||
#endif
|
||||
pEnv = getenv("BACNET_APDU_TIMEOUT");
|
||||
if (pEnv) {
|
||||
apdu_timeout_set((uint16_t)strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
#if defined(BACDL_MSTP)
|
||||
apdu_timeout_set(60000);
|
||||
#endif
|
||||
}
|
||||
pEnv = getenv("BACNET_APDU_RETRIES");
|
||||
if (pEnv) {
|
||||
apdu_retries_set((uint8_t)strtol(pEnv, NULL, 0));
|
||||
}
|
||||
/* === Initialize the Datalink Here === */
|
||||
if (!datalink_init(getenv("BACNET_IFACE"))) {
|
||||
exit(1);
|
||||
}
|
||||
#if (MAX_TSM_TRANSACTIONS)
|
||||
pEnv = getenv("BACNET_INVOKE_ID");
|
||||
if (pEnv) {
|
||||
tsm_invokeID_set((uint8_t)strtol(pEnv, NULL, 0));
|
||||
}
|
||||
#endif
|
||||
dlenv_network_port_init();
|
||||
dlenv_register_as_foreign_device();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef DLENV_H
|
||||
#define DLENV_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void dlenv_init(
|
||||
void);
|
||||
int dlenv_register_as_foreign_device(
|
||||
void);
|
||||
void dlenv_maintenance_timer(
|
||||
uint16_t elapsed_seconds);
|
||||
|
||||
/* Simple setters and getter. */
|
||||
void dlenv_bbmd_address_set(
|
||||
long address);
|
||||
void dlenv_bbmd_port_set(
|
||||
int port);
|
||||
void dlenv_bbmd_ttl_set(
|
||||
int ttl_secs);
|
||||
int dlenv_bbmd_result(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef DLMSTP_H
|
||||
#define DLMSTP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
/* defines specific to MS/TP */
|
||||
/* preamble+type+dest+src+len+crc8+crc16 */
|
||||
#define MAX_HEADER (2+1+1+1+2+1+2)
|
||||
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
|
||||
|
||||
typedef struct dlmstp_packet {
|
||||
bool ready; /* true if ready to be sent or received */
|
||||
BACNET_ADDRESS address; /* source address */
|
||||
uint8_t frame_type; /* type of message */
|
||||
uint16_t pdu_len; /* packet length */
|
||||
uint8_t pdu[MAX_MPDU]; /* packet */
|
||||
} DLMSTP_PACKET;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
bool dlmstp_init(
|
||||
char *ifname);
|
||||
void dlmstp_reset(
|
||||
void);
|
||||
void dlmstp_cleanup(
|
||||
void);
|
||||
|
||||
/* returns number of bytes sent on success, negative on failure */
|
||||
int dlmstp_send_pdu(
|
||||
BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len); /* number of bytes of data */
|
||||
|
||||
/* returns the number of octets in the PDU, or zero on failure */
|
||||
uint16_t dlmstp_receive(
|
||||
BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
/* This parameter represents the value of the Max_Info_Frames property of */
|
||||
/* the node's Device object. The value of Max_Info_Frames specifies the */
|
||||
/* maximum number of information frames the node may send before it must */
|
||||
/* pass the token. Max_Info_Frames may have different values on different */
|
||||
/* nodes. This may be used to allocate more or less of the available link */
|
||||
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
|
||||
/* node, its value shall be 1. */
|
||||
void dlmstp_set_max_info_frames(
|
||||
uint8_t max_info_frames);
|
||||
uint8_t dlmstp_max_info_frames(
|
||||
void);
|
||||
|
||||
/* This parameter represents the value of the Max_Master property of the */
|
||||
/* node's Device object. The value of Max_Master specifies the highest */
|
||||
/* allowable address for master nodes. The value of Max_Master shall be */
|
||||
/* less than or equal to 127. If Max_Master is not writable in a node, */
|
||||
/* its value shall be 127. */
|
||||
void dlmstp_set_max_master(
|
||||
uint8_t max_master);
|
||||
uint8_t dlmstp_max_master(
|
||||
void);
|
||||
|
||||
/* MAC address 0-127 */
|
||||
void dlmstp_set_mac_address(
|
||||
uint8_t my_address);
|
||||
uint8_t dlmstp_mac_address(
|
||||
void);
|
||||
|
||||
void dlmstp_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
void dlmstp_get_broadcast_address(
|
||||
BACNET_ADDRESS * dest); /* destination address */
|
||||
|
||||
/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */
|
||||
void dlmstp_set_baud_rate(
|
||||
uint32_t baud);
|
||||
uint32_t dlmstp_baud_rate(
|
||||
void);
|
||||
|
||||
void dlmstp_fill_bacnet_address(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t mstp_address);
|
||||
|
||||
bool dlmstp_sole_master(
|
||||
void);
|
||||
bool dlmstp_send_pdu_queue_empty(void);
|
||||
bool dlmstp_send_pdu_queue_full(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef ETHERNET_H
|
||||
#define ETHERNET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
/* specific defines for Ethernet */
|
||||
#define MAX_HEADER (6+6+2+1+1+1)
|
||||
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
bool ethernet_valid(
|
||||
void);
|
||||
void ethernet_cleanup(
|
||||
void);
|
||||
bool ethernet_init(
|
||||
char *interface_name);
|
||||
|
||||
/* function to send a packet out the 802.2 socket */
|
||||
/* returns number of bytes sent on success, negative on failure */
|
||||
int ethernet_send_pdu(
|
||||
BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len); /* number of bytes of data */
|
||||
|
||||
/* receives an 802.2 framed packet */
|
||||
/* returns the number of octets in the PDU, or zero on failure */
|
||||
uint16_t ethernet_receive(
|
||||
BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
void ethernet_set_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
void ethernet_get_my_address(
|
||||
BACNET_ADDRESS * my_address);
|
||||
void ethernet_get_broadcast_address(
|
||||
BACNET_ADDRESS * dest); /* destination address */
|
||||
|
||||
/* some functions from Linux driver */
|
||||
void ethernet_debug_address(
|
||||
const char *info,
|
||||
BACNET_ADDRESS * dest);
|
||||
int ethernet_send(
|
||||
uint8_t * mtu,
|
||||
int mtu_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,232 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2004 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef MSTP_H
|
||||
#define MSTP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/datalink/mstpdef.h"
|
||||
|
||||
struct mstp_port_struct_t {
|
||||
MSTP_RECEIVE_STATE receive_state;
|
||||
/* When a master node is powered up or reset, */
|
||||
/* it shall unconditionally enter the INITIALIZE state. */
|
||||
MSTP_MASTER_STATE master_state;
|
||||
/* A Boolean flag set to TRUE by the Receive State Machine */
|
||||
/* if an error is detected during the reception of a frame. */
|
||||
/* Set to FALSE by the Master or Slave Node state machine. */
|
||||
unsigned ReceiveError:1;
|
||||
/* There is data in the buffer */
|
||||
unsigned DataAvailable:1;
|
||||
unsigned ReceivedInvalidFrame:1;
|
||||
/* A Boolean flag set to TRUE by the Receive State Machine */
|
||||
/* if a valid frame is received. */
|
||||
/* Set to FALSE by the Master or Slave Node state machine. */
|
||||
unsigned ReceivedValidFrame:1;
|
||||
/* A Boolean flag set to TRUE by the Receive State Machine */
|
||||
/* if a valid frame is received but it is not addressed to us. */
|
||||
/* Set to FALSE by the Master or Slave Node state machine. */
|
||||
unsigned ReceivedValidFrameNotForUs:1;
|
||||
/* A Boolean flag set to TRUE by the master machine if this node is the */
|
||||
/* only known master node. */
|
||||
unsigned SoleMaster:1;
|
||||
/* stores the latest received data */
|
||||
uint8_t DataRegister;
|
||||
/* Used to accumulate the CRC on the data field of a frame. */
|
||||
uint16_t DataCRC;
|
||||
/* Used to store the actual CRC from the data field. */
|
||||
uint8_t DataCRCActualMSB;
|
||||
uint8_t DataCRCActualLSB;
|
||||
/* Used to store the data length of a received frame. */
|
||||
uint16_t DataLength;
|
||||
/* Used to store the destination address of a received frame. */
|
||||
uint8_t DestinationAddress;
|
||||
/* Used to count the number of received octets or errors. */
|
||||
/* This is used in the detection of link activity. */
|
||||
/* Compared to Nmin_octets */
|
||||
uint8_t EventCount;
|
||||
/* Used to store the frame type of a received frame. */
|
||||
uint8_t FrameType;
|
||||
/* The number of frames sent by this node during a single token hold. */
|
||||
/* When this counter reaches the value Nmax_info_frames, the node must */
|
||||
/* pass the token. */
|
||||
uint8_t FrameCount;
|
||||
/* Used to accumulate the CRC on the header of a frame. */
|
||||
uint8_t HeaderCRC;
|
||||
/* Used to store the actual CRC from the header. */
|
||||
uint8_t HeaderCRCActual;
|
||||
/* Used as an index by the Receive State Machine, up to a maximum value of */
|
||||
/* InputBufferSize. */
|
||||
uint32_t Index;
|
||||
/* An array of octets, used to store octets as they are received. */
|
||||
/* InputBuffer is indexed from 0 to InputBufferSize-1. */
|
||||
/* The maximum size of a frame is 501 octets. */
|
||||
/* FIXME: assign this to an actual array of bytes! */
|
||||
/* Note: the buffer is designed as a pointer since some compilers
|
||||
and microcontroller architectures have limits as to places to
|
||||
hold contiguous memory. */
|
||||
uint8_t *InputBuffer;
|
||||
uint16_t InputBufferSize;
|
||||
/* "Next Station," the MAC address of the node to which This Station passes */
|
||||
/* the token. If the Next_Station is unknown, Next_Station shall be equal to */
|
||||
/* This_Station. */
|
||||
uint8_t Next_Station;
|
||||
/* "Poll Station," the MAC address of the node to which This Station last */
|
||||
/* sent a Poll For Master. This is used during token maintenance. */
|
||||
uint8_t Poll_Station;
|
||||
/* A counter of transmission retries used for Token and Poll For Master */
|
||||
/* transmission. */
|
||||
unsigned RetryCount;
|
||||
/* A timer with nominal 5 millisecond resolution used to measure and */
|
||||
/* generate silence on the medium between octets. It is incremented by a */
|
||||
/* timer process and is cleared by the Receive State Machine when activity */
|
||||
/* is detected and by the SendFrame procedure as each octet is transmitted. */
|
||||
/* Since the timer resolution is limited and the timer is not necessarily */
|
||||
/* synchronized to other machine events, a timer value of N will actually */
|
||||
/* denote intervals between N-1 and N */
|
||||
/* Note: done here as functions - put into timer task or ISR
|
||||
so that you can be atomic on 8 bit microcontrollers */
|
||||
uint32_t(
|
||||
*SilenceTimer) (
|
||||
void *pArg);
|
||||
void (
|
||||
*SilenceTimerReset) (
|
||||
void *pArg);
|
||||
|
||||
/* A timer used to measure and generate Reply Postponed frames. It is */
|
||||
/* incremented by a timer process and is cleared by the Master Node State */
|
||||
/* Machine when a Data Expecting Reply Answer activity is completed. */
|
||||
/* note: we always send a reply postponed since a message other than
|
||||
the reply may be in the transmit queue */
|
||||
/* uint16_t ReplyPostponedTimer; */
|
||||
|
||||
/* Used to store the Source Address of a received frame. */
|
||||
uint8_t SourceAddress;
|
||||
|
||||
/* The number of tokens received by this node. When this counter reaches the */
|
||||
/* value Npoll, the node polls the address range between TS and NS for */
|
||||
/* additional master nodes. TokenCount is set to zero at the end of the */
|
||||
/* polling process. */
|
||||
unsigned TokenCount;
|
||||
|
||||
/* "This Station," the MAC address of this node. TS is generally read from a */
|
||||
/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */
|
||||
/* 0 to 254. The value 255 is used to denote broadcast when used as a */
|
||||
/* destination address but is not allowed as a value for TS. */
|
||||
uint8_t This_Station;
|
||||
|
||||
/* This parameter represents the value of the Max_Info_Frames property of */
|
||||
/* the node's Device object. The value of Max_Info_Frames specifies the */
|
||||
/* maximum number of information frames the node may send before it must */
|
||||
/* pass the token. Max_Info_Frames may have different values on different */
|
||||
/* nodes. This may be used to allocate more or less of the available link */
|
||||
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
|
||||
/* node, its value shall be 1. */
|
||||
uint8_t Nmax_info_frames;
|
||||
|
||||
/* This parameter represents the value of the Max_Master property of the */
|
||||
/* node's Device object. The value of Max_Master specifies the highest */
|
||||
/* allowable address for master nodes. The value of Max_Master shall be */
|
||||
/* less than or equal to 127. If Max_Master is not writable in a node, */
|
||||
/* its value shall be 127. */
|
||||
uint8_t Nmax_master;
|
||||
|
||||
/* An array of octets, used to store octets for transmitting */
|
||||
/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */
|
||||
/* The maximum size of a frame is 501 octets. */
|
||||
/* FIXME: assign this to an actual array of bytes! */
|
||||
/* Note: the buffer is designed as a pointer since some compilers
|
||||
and microcontroller architectures have limits as to places to
|
||||
hold contiguous memory. */
|
||||
uint8_t *OutputBuffer;
|
||||
uint16_t OutputBufferSize;
|
||||
|
||||
/*Platform-specific port data */
|
||||
void *UserData;
|
||||
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void MSTP_Init(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
void MSTP_Receive_Frame_FSM(
|
||||
volatile struct mstp_port_struct_t
|
||||
*mstp_port);
|
||||
bool MSTP_Master_Node_FSM(
|
||||
volatile struct mstp_port_struct_t
|
||||
*mstp_port);
|
||||
void MSTP_Slave_Node_FSM(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
/* returns true if line is active */
|
||||
bool MSTP_Line_Active(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
uint16_t MSTP_Create_Frame(
|
||||
uint8_t * buffer, /* where frame is loaded */
|
||||
uint16_t buffer_len, /* amount of space available */
|
||||
uint8_t frame_type, /* type of frame to send - see defines */
|
||||
uint8_t destination, /* destination address */
|
||||
uint8_t source, /* source address */
|
||||
uint8_t * data, /* any data to be sent - may be null */
|
||||
uint16_t data_len); /* number of bytes of data (up to 501) */
|
||||
|
||||
void MSTP_Create_And_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port to send from */
|
||||
uint8_t frame_type, /* type of frame to send - see defines */
|
||||
uint8_t destination, /* destination address */
|
||||
uint8_t source, /* source address */
|
||||
uint8_t * data, /* any data to be sent - may be null */
|
||||
uint16_t data_len);
|
||||
|
||||
void MSTP_Fill_BACnet_Address(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t mstp_address);
|
||||
|
||||
/* functions used by the MS/TP state machine to put or get data */
|
||||
/* FIXME: developer must implement these in their DLMSTP module */
|
||||
uint16_t MSTP_Put_Receive(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
/* for the MS/TP state machine to use for getting the reply for
|
||||
Data-Expecting-Reply Frame */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef MSTPDEF_H
|
||||
#define MSTPDEF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
/* The value 255 is used to denote broadcast when used as a */
|
||||
/* destination address but is not allowed as a value for a station. */
|
||||
/* Station addresses for master nodes can be 0-127. */
|
||||
/* Station addresses for slave nodes can be 127-254. */
|
||||
#define MSTP_BROADCAST_ADDRESS 255
|
||||
|
||||
/* MS/TP Frame Type */
|
||||
/* Frame Types 8 through 127 are reserved by ASHRAE. */
|
||||
#define FRAME_TYPE_TOKEN 0
|
||||
#define FRAME_TYPE_POLL_FOR_MASTER 1
|
||||
#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2
|
||||
#define FRAME_TYPE_TEST_REQUEST 3
|
||||
#define FRAME_TYPE_TEST_RESPONSE 4
|
||||
#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5
|
||||
#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6
|
||||
#define FRAME_TYPE_REPLY_POSTPONED 7
|
||||
/* Frame Types 128 through 255: Proprietary Frames */
|
||||
/* These frames are available to vendors as proprietary (non-BACnet) frames. */
|
||||
/* The first two octets of the Data field shall specify the unique vendor */
|
||||
/* identification code, most significant octet first, for the type of */
|
||||
/* vendor-proprietary frame to be conveyed. The length of the data portion */
|
||||
/* of a Proprietary frame shall be in the range of 2 to 501 octets. */
|
||||
#define FRAME_TYPE_PROPRIETARY_MIN 128
|
||||
#define FRAME_TYPE_PROPRIETARY_MAX 255
|
||||
/* The initial CRC16 checksum value */
|
||||
#define CRC16_INITIAL_VALUE (0xFFFF)
|
||||
|
||||
/* receive FSM states */
|
||||
typedef enum {
|
||||
MSTP_RECEIVE_STATE_IDLE = 0,
|
||||
MSTP_RECEIVE_STATE_PREAMBLE = 1,
|
||||
MSTP_RECEIVE_STATE_HEADER = 2,
|
||||
MSTP_RECEIVE_STATE_DATA = 3,
|
||||
MSTP_RECEIVE_STATE_SKIP_DATA = 4
|
||||
} MSTP_RECEIVE_STATE;
|
||||
|
||||
/* master node FSM states */
|
||||
typedef enum {
|
||||
MSTP_MASTER_STATE_INITIALIZE = 0,
|
||||
MSTP_MASTER_STATE_IDLE = 1,
|
||||
MSTP_MASTER_STATE_USE_TOKEN = 2,
|
||||
MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3,
|
||||
MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4,
|
||||
MSTP_MASTER_STATE_PASS_TOKEN = 5,
|
||||
MSTP_MASTER_STATE_NO_TOKEN = 6,
|
||||
MSTP_MASTER_STATE_POLL_FOR_MASTER = 7,
|
||||
MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8
|
||||
} MSTP_MASTER_STATE;
|
||||
|
||||
/* The time without a DataAvailable or ReceiveError event before declaration */
|
||||
/* of loss of token: 500 milliseconds. */
|
||||
#define Tno_token 500
|
||||
|
||||
/* The minimum time after the end of the stop bit of the final octet of a */
|
||||
/* received frame before a node may enable its EIA-485 driver: 40 bit times. */
|
||||
/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */
|
||||
/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */
|
||||
/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */
|
||||
/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */
|
||||
/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */
|
||||
/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */
|
||||
/* 40 bits is 4 octets including a start and stop bit with each octet */
|
||||
#define Tturnaround (40UL)
|
||||
/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */
|
||||
|
||||
/* The number of tokens received or used before a Poll For Master cycle */
|
||||
/* is executed: 50. */
|
||||
#define Npoll 50
|
||||
|
||||
/* The number of retries on sending Token: 1. */
|
||||
#define Nretry_token 1
|
||||
|
||||
/* The maximum idle time a sending node may allow to elapse between octets */
|
||||
/* of a frame the node is transmitting: 20 bit times. */
|
||||
#define Tframe_gap 20
|
||||
|
||||
/* The maximum time after the end of the stop bit of the final */
|
||||
/* octet of a transmitted frame before a node must disable its */
|
||||
/* EIA-485 driver: 15 bit times. */
|
||||
#define Tpostdrive 15
|
||||
|
||||
/* The width of the time slot within which a node may generate a token: */
|
||||
/* 10 milliseconds. */
|
||||
#define Tslot 10
|
||||
|
||||
/* The maximum time a node may wait after reception of the token or */
|
||||
/* a Poll For Master frame before sending the first octet of a frame: */
|
||||
/* 15 milliseconds. */
|
||||
#define Tusage_delay 15
|
||||
|
||||
#define DEFAULT_MAX_INFO_FRAMES 1
|
||||
#define DEFAULT_MAX_MASTER 127
|
||||
#define DEFAULT_MAC_ADDRESS 127
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2007 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdio.h>
|
||||
#include "bacnet/datalink/mstp.h"
|
||||
#include "bacnet/indtext.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/datalink/mstptext.h"
|
||||
|
||||
/** @file mstptext.c Text mapping functions for BACnet MS/TP */
|
||||
|
||||
static INDTEXT_DATA mstp_receive_state_text[] = { { MSTP_RECEIVE_STATE_IDLE,
|
||||
"IDLE" },
|
||||
{ MSTP_RECEIVE_STATE_PREAMBLE, "PREAMBLE" },
|
||||
{ MSTP_RECEIVE_STATE_HEADER, "HEADER" },
|
||||
{ MSTP_RECEIVE_STATE_DATA, "DATA" }, { 0, NULL } };
|
||||
|
||||
const char *mstptext_receive_state(unsigned index)
|
||||
{
|
||||
return indtext_by_index_default(mstp_receive_state_text, index, "unknown");
|
||||
}
|
||||
|
||||
static INDTEXT_DATA mstp_master_state_text[] = { { MSTP_MASTER_STATE_INITIALIZE,
|
||||
"INITIALIZE" },
|
||||
{ MSTP_MASTER_STATE_IDLE, "IDLE" },
|
||||
{ MSTP_MASTER_STATE_USE_TOKEN, "USE_TOKEN" },
|
||||
{ MSTP_MASTER_STATE_WAIT_FOR_REPLY, "WAIT_FOR_REPLY" },
|
||||
{ MSTP_MASTER_STATE_DONE_WITH_TOKEN, "DONE_WITH_TOKEN" },
|
||||
{ MSTP_MASTER_STATE_PASS_TOKEN, "PASS_TOKEN" },
|
||||
{ MSTP_MASTER_STATE_NO_TOKEN, "NO_TOKEN" },
|
||||
{ MSTP_MASTER_STATE_POLL_FOR_MASTER, "POLL_FOR_MASTER" },
|
||||
{ MSTP_MASTER_STATE_ANSWER_DATA_REQUEST, "ANSWER_DATA_REQUEST" },
|
||||
{ 0, NULL } };
|
||||
|
||||
const char *mstptext_master_state(unsigned index)
|
||||
{
|
||||
return indtext_by_index_default(mstp_master_state_text, index, "unknown");
|
||||
}
|
||||
|
||||
static INDTEXT_DATA mstp_frame_type_text[] = { { FRAME_TYPE_TOKEN, "TOKEN" },
|
||||
{ FRAME_TYPE_POLL_FOR_MASTER, "POLL_FOR_MASTER" },
|
||||
{ FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, "REPLY_TO_POLL_FOR_MASTER" },
|
||||
{ FRAME_TYPE_TEST_REQUEST, "TEST_REQUEST" },
|
||||
{ FRAME_TYPE_TEST_RESPONSE, "TEST_RESPONSE" },
|
||||
{ FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY, "BACNET_DATA_EXPECTING_REPLY" },
|
||||
{ FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
|
||||
"BACNET_DATA_NOT_EXPECTING_REPLY" },
|
||||
{ FRAME_TYPE_REPLY_POSTPONED, "REPLY_POSTPONED" }, { 0, NULL } };
|
||||
|
||||
const char *mstptext_frame_type(unsigned index)
|
||||
{
|
||||
return indtext_by_index_split_default(mstp_frame_type_text, index,
|
||||
FRAME_TYPE_PROPRIETARY_MIN, "UNKNOWN", "PROPRIETARY");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef MSTPTEXT_H
|
||||
#define MSTPTEXT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
const char *mstptext_receive_state(
|
||||
unsigned index);
|
||||
const char *mstptext_master_state(
|
||||
unsigned index);
|
||||
const char *mstptext_frame_type(
|
||||
unsigned index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
Reference in New Issue
Block a user