diff --git a/bacnet-stack/demo/whois/main.c b/bacnet-stack/demo/whois/main.c index 36f5b208..c658e183 100644 --- a/bacnet-stack/demo/whois/main.c +++ b/bacnet-stack/demo/whois/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include /* for time */ #include #include "bactext.h" @@ -49,16 +50,131 @@ #include "rs485.h" #endif #include "dlenv.h" +#include "net.h" /* buffer used for receive */ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; /* global variables used in this file */ -static int32_t Target_Object_Instance_Min = 0; -static int32_t Target_Object_Instance_Max = BACNET_MAX_INSTANCE; - +static int32_t Target_Object_Instance_Min = -1; +static int32_t Target_Object_Instance_Max = -1; +static int32_t Target_Network = BACNET_BROADCAST_NETWORK; +static char Target_Mac[7]; +static uint32_t Target_MacLen = 0; +static bool verbose = true; static bool Error_Detected = false; +#define BAC_ADDRESS_MULT 1 + +struct address_entry { + struct address_entry * next; + uint8_t Flags; + uint32_t device_id; + unsigned max_apdu; + BACNET_ADDRESS address; +}; + +static struct address_table { + struct address_entry * first; + struct address_entry * last; +} addr_tbl = {0}; + + +struct address_entry * alloc_address_entry(void) +{ + struct address_entry * rval; + rval = (struct address_entry *)calloc(1, sizeof(struct address_entry)); + if(addr_tbl.first == 0) + { + addr_tbl.first = addr_tbl.last = rval; + } + else + { + addr_tbl.last->next = rval; + addr_tbl.last = rval; + } + return rval; +} + + + +bool bacnet_address_matches(BACNET_ADDRESS * a1, BACNET_ADDRESS * a2) +{ + int i = 0; + if(a1->net != a2->net) return false; + if(a1->len != a2->len) return false; + for(;ilen;i++) + if(a1->adr[i]!=a2->adr[i]) + return false; + return true; +} + +void address_table_add( + uint32_t device_id, + unsigned max_apdu, + BACNET_ADDRESS * src) +{ + struct address_entry *pMatch; + uint8_t flags = 0; + + pMatch = addr_tbl.first; + while (pMatch) { + if (pMatch->device_id == device_id) + { + if (bacnet_address_matches(&pMatch->address, src)) + return; + flags |= BAC_ADDRESS_MULT; + pMatch->Flags |= BAC_ADDRESS_MULT; + } + pMatch = pMatch->next; + } + + pMatch = alloc_address_entry(); + + pMatch->Flags = flags; + pMatch->device_id = device_id; + pMatch->max_apdu = max_apdu; + pMatch->address = *src; + + return; +} + + + +void my_i_am_handler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); +#if PRINT_ENABLED + fprintf(stderr, "Received I-Am Request"); +#endif + if (len != -1) { +#if PRINT_ENABLED + fprintf(stderr, " from %lu, MAC = %d.%d.%d.%d.%d.%d\n", + (unsigned long) device_id, src->mac[0], src->mac[1], src->mac[2], + src->mac[3], src->mac[4], src->mac[5]); +#endif + address_table_add(device_id, max_apdu, src); + } else { +#if PRINT_ENABLED + fprintf(stderr, ", but unable to decode it.\n"); +#endif + } + + return; +} + void MyAbortHandler( BACNET_ADDRESS * src, uint8_t invoke_id, @@ -87,7 +203,7 @@ void MyRejectHandler( Error_Detected = true; } -static void Init_Service_Handlers( +static void init_service_handlers( void) { Device_Init(NULL); @@ -101,69 +217,189 @@ static void Init_Service_Handlers( apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property); /* handle the reply (request) coming back */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, my_i_am_handler); /* handle any errors coming back */ apdu_set_abort_handler(MyAbortHandler); apdu_set_reject_handler(MyRejectHandler); } -static void print_address_cache( - void) +void print_macaddr(uint8_t * addr, int len) { - int i, j; - BACNET_ADDRESS address; - uint32_t device_id = 0; - unsigned max_apdu = 0; - unsigned total_addresses = 0; - - printf(";%-7s %-20s %-5s %-20s %-4s\n", "Device", "MAC", "SNET", "SADR", - "APDU"); - printf(";------- -------------------- ----- -------------------- ----\n"); - for (i = 0; i < MAX_ADDRESS_CACHE; i++) { - if (address_get_by_index(i, &device_id, &max_apdu, &address)) { - total_addresses++; - printf(" %-7u ", device_id); - for (j = 0; j < MAX_MAC_LEN; j++) { - if (j < address.mac_len) { - if (j > 0) { - printf(":"); - } - printf("%02X", address.mac[j]); - } else { - if (j > 0) { - printf(" "); - } - printf(" "); - } - } - printf(" %-5hu ", address.net); - if (address.net) { - for (j = 0; j < MAX_MAC_LEN; j++) { - if (j < address.len) { - if (j > 0) { - printf(":"); - } - printf("%02X", address.adr[j]); - } else { - if (j > 0) { - printf(" "); - } - printf(" "); - } - } - } else { - printf("0 "); - for (j = 1; j < MAX_MAC_LEN; j++) { - printf(" "); - } - } - printf(" %-4hu ", max_apdu); - printf("\n"); - } - } - printf(";\n; Total Devices: %u\n", total_addresses); + int j = 0; + if(len == 0) + { + printf("%20s", ""); + return; + } + if(len == 1) + { + printf("%-6u%14s", addr[0], ""); + return; + } + while(j < len) + { + if (j != 0) { + printf(":"); + } + printf("%02X", addr[j++]); + } + while(j++ < MAX_MAC_LEN) + { + printf(" "); + } } +static void print_address_cache( + void) +{ + int j; + BACNET_ADDRESS address; + unsigned total_addresses = 0; + unsigned dup_addresses = 0; + struct address_entry *addr; + + printf(";%-7s %-20s %-5s %-20s %-4s\n", "Device", "MAC", "SNET", "SADR", + "APDU"); + printf(";-------- -------------------- ----- -------------------- ----\n"); + + + addr = addr_tbl.first; + while (addr) + { + address = addr->address; + + total_addresses++; + if(addr->Flags & BAC_ADDRESS_MULT) + { + dup_addresses++; + printf("*"); + } + else + { + printf(" "); + } + printf(" %-7u ", addr->device_id); + print_macaddr(address.mac, address.mac_len); + printf(" %-5hu ", address.net); + if (address.net) { + print_macaddr(address.adr, address.len); + } else { + printf("0 "); + for (j = 1; j < MAX_MAC_LEN; j++) { + printf(" "); + } + } + printf(" %-4hu ", addr->max_apdu); + printf("\n"); + + addr = addr->next; + } + printf(";\n; Total Devices: %u\n", total_addresses); + if (dup_addresses) + printf("; * Duplicate Devices: %u\n", dup_addresses); +} + +int print_usage(char* exe_name){ + printf( + "Usage:\n" + "\n" + "%s [[network]:[address]] [device-instance-min [device-instance-max]] [--help]\n" , + exe_name); + return 1; +} + + +int print_help(char* exe_name){ + printf( + "Usage:\n" + "\n" + "%s [[network]:[address]] [low_devid [high_devid]] [--help]\n" + "\n" + " Send BACnet WhoIs service request to a device or multiple devices, and wait\n" + " for responses. Displays any devices found an their network information.\n" + "\n" + "device-instance:\r\n" + " BACnet Device Object Instance number that you are trying to send a Who-Is\n" + " service request. The value should be in the range of 0 to 4194303. A range\n" + " of values can also be specified by using a minimum value and a maximum value.\n" + "\n" + "network:\n" + " BACnet network number for directed requests. Valid range is from 0 to 65535\n" + " where 0 is the local connection and 65535 is network broadcast.\n" + "\n" + "address:\n" + " BACnet mac address number. Valid ranges are from 0 to 255 or a IP connection \n" + " string including port number like 10.1.2.3:47808.\n" + "\n" + "Examples:\n\n" + "To send a WhoIs request to Network 123:\n" + "%s 123:\n\n" + "To send a WhoIs request to Network 123 Address 5:\n" + "%s 123:5\n\n" + "To send a WhoIs request to Device 123:\n" + "%s 123\n\n" + "To send a WhoIs request to Devices from 1000 to 9000:\n" + "%s 1000 9000\n\n" + "To send a WhoIs request to Devices from 1000 to 9000 on Network 123:\n" + "%s 123: 1000 9000\n\n" + "To send a WhoIs request to all devices:\n" + "%s\n\n", + exe_name,exe_name,exe_name,exe_name,exe_name,exe_name,exe_name,exe_name); + return 1; +} + + +/* Parse a string for a bacnet-address +** +** @return length of address parsed in bytes +*/ +int parse_bac_address( + BACNET_ADDRESS *dest, /* [out] BACNET Address*/ + char *src /* [in] nul terminated string to parse */ + ) +{ + int i = 0; + int z = 0; + uint16_t s; + int a[4],p; + int c = sscanf(src,"%u.%u.%u.%u:%u",&a[0],&a[1],&a[2],&a[3],&p); + + dest->len = 0; + + if (c==1) + { + if( a[0] < 256 ) /* mstp */ + { + dest->adr[0] = a[0]; + dest->len = 1; + } + else if ( a[0] < 0x0FFFF ) /* lon */ + { + s = htons((uint16_t)a[0]); + memcpy(&dest->adr[0], &s, 2); + dest->len = 2; + } + else + return 0; + } + else if (c==5) /* ip address */ + { + for(i=0;i<4;i++) + { + if(a[i] == 0 || a[i] > 255) + return 0; + + dest->adr[i] = a[i]; + } + s = htons((uint16_t)p); + memcpy(&dest->adr[i], &s, 2); + dest->len = 6; + } + return dest->len; +} + + + int main( int argc, char *argv[]) @@ -178,60 +414,70 @@ int main( time_t last_seconds = 0; time_t current_seconds = 0; time_t timeout_seconds = 0; + BACNET_ADDRESS dest; + int argi; - if (argc < 2) { - printf("Usage: %s device-instance | " - "device-instance-min device-instance-max\r\n", - filename_remove_path(argv[0])); - return 0; - } - if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { - printf("Send BACnet WhoIs service request to a device or\r\n" - "multiple devices, and wait for responses. Displays\r\n" - "any devices found and their network information.\r\n" - "\r\ndevice-instance:\r\n" - "BACnet Device Object Instance number that you are trying to\r\n" - "send a Who-Is service request. The value should be in\r\n" - "the range of 0 to 4194303. A range of values can also be\r\n" - "specified by using a minimum value and a maximum value.\r\n" - "\r\nExample:\r\n" "To send a WhoIs request to Device 123\r\n" - "use the following command:\r\n" "%s 123\r\n" - "To send a WhoIs request to Devices from 1000 to 9000:\r\n" - "%s 1000 9000\r\n" "To send a WhoIs request to all devices\r\n" - "use the following command:\r\n" "%s -1\r\n", - filename_remove_path(argv[0]), filename_remove_path(argv[0]), - filename_remove_path(argv[0])); - return 0; - } - /* decode the command line parameters */ - if (argc < 3) { - Target_Object_Instance_Min = strtol(argv[1], NULL, 0); - Target_Object_Instance_Max = Target_Object_Instance_Min; - if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { - fprintf(stderr, - "object-instance-min=%u - it must be less than %u\r\n", - Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); - return 1; - } + /* print help if requested */ + for (argi = 1; argi < argc; argi++) + { + if (strcmp(argv[argi], "--help") == 0) { + print_help(filename_remove_path(argv[0])); + return 0; + } + } + + datalink_get_broadcast_address(&dest); + + /* decode the command line parameters */ + if (argc >= 2) { + char *s; + long v = strtol(argv[1], &s, 0); + if(*s++ == ':') + { + if(argv[1][0] != ':') + dest.net = (uint16_t) v; + dest.mac_len = 0; + if(isdigit(*s)) + parse_bac_address(&dest,s); + } + else + { + Target_Object_Instance_Min = v; + } + } + + if (argc <= 2){ + /* empty */ + } else if (argc == 3){ + if(Target_Object_Instance_Min == -1) + Target_Object_Instance_Min = + Target_Object_Instance_Max = strtol(argv[2], NULL, 0); + else + Target_Object_Instance_Max = strtol(argv[2], NULL, 0); + } else if (argc == 4){ + Target_Object_Instance_Min = strtol(argv[2], NULL, 0); + Target_Object_Instance_Max = strtol(argv[3], NULL, 0); } else { - Target_Object_Instance_Min = strtol(argv[1], NULL, 0); - Target_Object_Instance_Max = strtol(argv[2], NULL, 0); - if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { - fprintf(stderr, - "object-instance-min=%u - it must be less than %u\r\n", - Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); - return 1; - } - if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) { - fprintf(stderr, - "object-instance-max=%u - it must be less than %u\r\n", - Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1); - return 1; - } + print_usage(filename_remove_path(argv[0])); + return 1; } - /* setup my info */ + + if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { + fprintf(stderr, + "object-instance-min=%u - it must be less than %u\r\n", + Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) { + fprintf(stderr, + "object-instance-max=%u - it must be less than %u\r\n", + Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1); + return 1; + } + + /* setup my info */ Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); - Init_Service_Handlers(); + init_service_handlers(); address_init(); dlenv_init(); atexit(datalink_cleanup); @@ -239,8 +485,8 @@ int main( last_seconds = time(NULL); timeout_seconds = apdu_timeout() / 1000; /* send the request */ - Send_WhoIs(Target_Object_Instance_Min, Target_Object_Instance_Max); - /* loop forever */ + Send_WhoIs_To_Network(&dest,Target_Object_Instance_Min, Target_Object_Instance_Max); + /* loop forever */ for (;;) { /* increment timer - exit if timed out */ current_seconds = time(NULL);