/* 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 /* number of milliseconds to wait for a packet. we ommit it due to winpcap API. */ ) { struct pcap_pkthdr *header; int res; u_char *pkt_data; uint16_t pdu_len = 0; /* return value */ /* Make sure the socket is open */ if (!ethernet_valid()) { LogError("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); return 0; } /* Capture a packet */ res = pcap_next_ex(pcap_eth802_fp, &header, &pkt_data); if (res < 0) { char msgBuf[200]; sprintf(msgBuf, "ethernet.c: error in receiving packet: %s\n", pcap_geterr(pcap_eth802_fp)); return 0; } else if (res == 0) return 0; if (header->len == 0 || header->caplen == 0) return 0; /* the signature of an 802.2 BACnet packet */ if ((pkt_data[14] != 0x82) && (pkt_data[15] != 0x82)) { /*eth_log_error("ethernet.c: Non-BACnet packet\n"); */ return 0; } /* copy the source address */ src->mac_len = 6; memmove(src->mac, &pkt_data[6], 6); /* check destination address for when */ /* the Ethernet card is in promiscious mode */ if ((memcmp(&pkt_data[0], Ethernet_MAC_Address, 6) != 0) && (memcmp(&pkt_data[0], Ethernet_Broadcast, 6) != 0)) { /*eth_log_error( "ethernet.c: This packet isn't for us\n"); */ return 0; } (void) decode_unsigned16(&pkt_data[12], &pdu_len); pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; /* copy the buffer into the PDU */ if (pdu_len < max_pdu) memmove(&pdu[0], &pkt_data[17], pdu_len); /* ignore packets that are too large */ else pdu_len = 0; return pdu_len; }
uint16_t bvlc_for_non_bbmd(uint8_t * addr, uint16_t * port, uint8_t * npdu, uint16_t received_bytes) { uint16_t result_code = 0; /* aka, BVLC_RESULT_SUCCESSFUL_COMPLETION */ BVLC_Function_Code = npdu[1]; /* The BVLC function */ switch (BVLC_Function_Code) { case BVLC_RESULT: if (received_bytes >= 6) { /* This is the result of our foreign device registration */ (void) decode_unsigned16(&npdu[4], &result_code); BVLC_Result_Code = (BACNET_BVLC_RESULT) result_code; fprintf(stderr, "BVLC: Result Code=%d\n", BVLC_Result_Code); /* But don't send any response */ result_code = 0; } break; case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: result_code = BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK; break; case BVLC_READ_BROADCAST_DIST_TABLE: result_code = BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK; break; /* case BVLC_READ_BROADCAST_DIST_TABLE_ACK: */ case BVLC_REGISTER_FOREIGN_DEVICE: result_code = BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK; break; case BVLC_READ_FOREIGN_DEVICE_TABLE: result_code = BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK; break; /* case BVLC_READ_FOREIGN_DEVICE_TABLE_ACK: */ case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: result_code = BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK; break; case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: result_code = BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; break; /* case BVLC_FORWARDED_NPDU: */ /* case BVLC_ORIGINAL_UNICAST_NPDU: */ /* case BVLC_ORIGINAL_BROADCAST_NPDU: */ default: break; } if (result_code > 0) { bvlc_send_result(addr, port, result_code); fprintf(stderr, "BVLC: NAK code=%d\n", result_code); } return result_code; }
/* 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) { /* number of milliseconds to wait for a packet */ int received_bytes; uint8_t buf[MAX_MPDU] = { 0 }; /* data */ uint16_t pdu_len = 0; /* return value */ /* Make sure the socket is open */ if (!ethernet_valid()) return 0; received_bytes = R_Ether_Read(0, (void *) buf); if (received_bytes == 0) return 0; /* the signature of an 802.2 BACnet packet */ if ((buf[14] != 0x82) && (buf[15] != 0x82)) { return 0; } /* copy the source address */ src->mac_len = 6; memmove(src->mac, &buf[6], 6); /* check destination address for when */ /* the Ethernet card is in promiscious mode */ if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { return 0; } (void) decode_unsigned16(&buf[12], &pdu_len); pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; /* copy the buffer into the PDU */ if (pdu_len < max_pdu) memmove(&pdu[0], &buf[17], pdu_len); /* ignore packets that are too large */ else pdu_len = 0; return pdu_len; }
/** Handler to manage the Network Layer Control Messages received in a packet. * This handler is called if the NCPI bit 7 indicates that this packet is a * network layer message and there is no further DNET to pass it to. * The NCPI has already been decoded into the npdu_data structure. * @ingroup MISCHNDLR * * @param src [in] The routing source information, if any. * If src->net and src->len are 0, there is no * routing source information. * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. * Normally just one valid entry; terminated with a -1 value. * @param npdu_data [in] Contains a filled-out structure with information * decoded from the NCPI and other NPDU bytes. * @param npdu [in] Buffer containing the rest of the NPDU, following the * bytes that have already been decoded. * @param npdu_len [in] The length of the remaining NPDU message in npdu[]. */ static void network_control_handler( BACNET_ADDRESS * src, int *DNET_list, BACNET_NPDU_DATA * npdu_data, uint8_t * npdu, uint16_t npdu_len) { uint16_t npdu_offset = 0; uint16_t dnet = 0; uint16_t len = 0; switch (npdu_data->network_message_type) { case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: /* Send I-am-router-to-network with our one-network list if * our specific network is requested, or no specific * network is requested. Silently drop other DNET requests. */ if (npdu_len >= 2) { uint16_t network; len += decode_unsigned16(&npdu[len], &network); if (network == DNET_list[0]) { Send_I_Am_Router_To_Network(DNET_list); } } else { Send_I_Am_Router_To_Network(DNET_list); } break; case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: /* Per the standard, we are supposed to process this message and * add its DNETs to our routing table. * However, since we only have one upstream port that these * messages can come from and replies go to, it doesn't seem * to provide us any value to do this; when we need to send to * some remote device, we will start by pushing it out the * upstream port and let the attached router(s) take it from there. * Consequently, we'll do nothing interesting here. * -- Unless we act upon NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK * later for congestion control - then it could matter. */ debug_printf("%s for Networks: ", bactext_network_layer_msg_name (NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK)); while (npdu_len) { len = decode_unsigned16(&npdu[npdu_offset], &dnet); debug_printf("%hu", dnet); npdu_len -= len; npdu_offset += len; if (npdu_len) { debug_printf(", "); } } debug_printf("\n"); break; case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: /* Do nothing, same as previous case. */ break; case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: if (npdu_len >= 3) { decode_unsigned16(&npdu[1], &dnet); debug_printf("Received %s for Network: ", bactext_network_layer_msg_name (NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK)); debug_printf("%hu, Reason code: %d \n", dnet, npdu[0]); } break; case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: /* Do nothing - don't support upstream traffic congestion control */ break; case NETWORK_MESSAGE_INIT_RT_TABLE: /* If sent with Number of Ports == 0, we respond with * NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our * reachable networks. */ if (npdu_len > 0) { /* If Number of Ports is 0, broadcast our "full" table */ if (npdu[0] == 0) Send_Initialize_Routing_Table_Ack(NULL, DNET_list); else { /* If they sent us a list, just politely ACK it * with no routing list of our own. But we don't DO * anything with the info, either. */ int listTerminator = -1; Send_Initialize_Routing_Table_Ack(src, &listTerminator); } break; } /* Else, fall through to do nothing. */ case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: /* Do nothing with the routing table info, since don't support * upstream traffic congestion control */ break; case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: /* Do nothing - don't support PTP half-router control */ break; default: /* An unrecognized message is bad; send an error response. */ Send_Reject_Message_To_Network(src, NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, DNET_list[0]); /* Sending our DNET doesn't make a lot of sense, does it? */ break; } }
/** Decode the NPDU portion of a received message, particularly the NCPI byte. * The Network Layer Protocol Control Information byte is described * in section 6.2.2 of the BACnet standard. * @param npdu [in] Buffer holding the received NPDU header bytes (must be at least 2) * @param dest [out] Returned with routing destination information if the NPDU * has any and if this points to non-null storage for it. * If dest->net and dest->len are 0 on return, there is no * routing destination information. * @param src [out] Returned with routing source information if the NPDU * has any and if this points to non-null storage for it. * If src->net and src->len are 0 on return, there is no * routing source information. * This src describes the original source of the message when * it had to be routed to reach this BACnet Device. * @param npdu_data [out] Returns a filled-out structure with information * decoded from the NCPI and other NPDU bytes. * @return On success, returns the number of bytes which were decoded from the * NPDU section; if this is a network layer message, there may be more * bytes left in the NPDU; if not a network msg, the APDU follows. * If 0 or negative, there were problems with the data or arguments. */ int npdu_decode( uint8_t * npdu, BACNET_ADDRESS * dest, BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data) { int len = 0; /* return value - number of octets loaded in this function */ uint8_t i = 0; /* counter */ uint16_t src_net = 0; uint16_t dest_net = 0; uint8_t address_len = 0; uint8_t mac_octet = 0; if (npdu && npdu_data) { /* Protocol Version */ npdu_data->protocol_version = npdu[0]; /* control octet */ /* Bit 7: 1 indicates that the NSDU conveys a network layer message. */ /* Message Type field is present. */ /* 0 indicates that the NSDU contains a BACnet APDU. */ /* Message Type field is absent. */ npdu_data->network_layer_message = (npdu[1] & BIT7) ? true : false; /*Bit 6: Reserved. Shall be zero. */ /* Bit 4: Reserved. Shall be zero. */ /* Bit 2: The value of this bit corresponds to data expecting reply */ /* parameter in the N-UNITDATA primitives. */ /* 1 indicates that a BACnet-Confirmed-Request-PDU, */ /* a segment of a BACnet-ComplexACK-PDU, */ /* or a network layer message expecting a reply is present. */ /* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */ /* a segment of a BACnet-ComplexACK-PDU, */ /* or a network layer message expecting a reply is present. */ npdu_data->data_expecting_reply = (npdu[1] & BIT2) ? true : false; /* Bits 1,0: Network priority where: */ /* B'11' = Life Safety message */ /* B'10' = Critical Equipment message */ /* B'01' = Urgent message */ /* B'00' = Normal message */ npdu_data->priority = (BACNET_MESSAGE_PRIORITY) (npdu[1] & 0x03); /* set the offset to where the optional stuff starts */ len = 2; /*Bit 5: Destination specifier where: */ /* 0 = DNET, DLEN, DADR, and Hop Count absent */ /* 1 = DNET, DLEN, and Hop Count present */ /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ /* DLEN > 0 specifies length of DADR field */ if (npdu[1] & BIT5) { len += decode_unsigned16(&npdu[len], &dest_net); /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ /* DLEN > 0 specifies length of DADR field */ address_len = npdu[len++]; if (dest) { dest->net = dest_net; dest->len = address_len; } if (address_len) { if (address_len > MAX_MAC_LEN) { /* address is too large could be a malformed message */ return -1; } for (i = 0; i < address_len; i++) { mac_octet = npdu[len++]; if (dest) dest->adr[i] = mac_octet; } } } /* zero out the destination address */ else if (dest) { dest->net = 0; dest->len = 0; for (i = 0; i < MAX_MAC_LEN; i++) { dest->adr[i] = 0; } } /* Bit 3: Source specifier where: */ /* 0 = SNET, SLEN, and SADR absent */ /* 1 = SNET, SLEN, and SADR present */ /* SLEN = 0 Invalid */ /* SLEN > 0 specifies length of SADR field */ if (npdu[1] & BIT3) { len += decode_unsigned16(&npdu[len], &src_net); /* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */ /* SLEN > 0 specifies length of SADR field */ address_len = npdu[len++]; if (src) { src->net = src_net; src->len = address_len; } if (address_len) { if (address_len > MAX_MAC_LEN) { /* address is too large could be a malformed message */ return -1; } for (i = 0; i < address_len; i++) { mac_octet = npdu[len++]; if (src) src->adr[i] = mac_octet; } } } else if (src) { /* Clear the net number, with one exception: if the receive() * function set it to BACNET_BROADCAST_NETWORK, (eg, for * BVLC_ORIGINAL_BROADCAST_NPDU) then don't stomp on that. */ if (src->net != BACNET_BROADCAST_NETWORK) src->net = 0; src->len = 0; for (i = 0; i < MAX_MAC_LEN; i++) { src->adr[i] = 0; } } /* The Hop Count field shall be present only if the message is */ /* destined for a remote network, i.e., if DNET is present. */ /* This is a one-octet field that is initialized to a value of 0xff. */ if (dest_net) { npdu_data->hop_count = npdu[len++]; } else { npdu_data->hop_count = 0; } /* Indicates that the NSDU conveys a network layer message. */ /* Message Type field is present. */ if (npdu_data->network_layer_message) { npdu_data->network_message_type = (BACNET_NETWORK_MESSAGE_TYPE) npdu[len++]; /* Message Type field contains a value in the range 0x80 - 0xFF, */ /* then a Vendor ID field shall be present */ if (npdu_data->network_message_type >= 0x80) len += decode_unsigned16(&npdu[len], &npdu_data->vendor_id); } else { /* Since npdu_data->network_layer_message is false, * it doesn't much matter what we set here; this is safe: */ npdu_data->network_message_type = NETWORK_MESSAGE_INVALID; } } return len; }
/* 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) { /* number of milliseconds to wait for a packet */ int received_bytes; uint8_t buf[MAX_MPDU] = { 0 }; /* data */ uint16_t pdu_len = 0; /* return value */ fd_set read_fds; int max; struct timeval select_timeout; /* Make sure the socket is open */ if (Ethernet_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(Ethernet_Socket, &read_fds); max = Ethernet_Socket; if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) received_bytes = recv(Ethernet_Socket, (char *) &buf[0], MAX_MPDU, 0); else return 0; /* See if there is a problem */ if (received_bytes < 0) { /* EAGAIN Non-blocking I/O has been selected */ /* using O_NONBLOCK and no data */ /* was immediately available for reading. */ if (errno != EAGAIN) fprintf(stderr, "ethernet: Read error in receiving packet: %s\n", strerror(errno)); return 0; } if (received_bytes == 0) return 0; /* the signature of an 802.2 BACnet packet */ if ((buf[14] != 0x82) && (buf[15] != 0x82)) { /*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */ return 0; } /* copy the source address */ src->mac_len = 6; memmove(src->mac, &buf[6], 6); /* check destination address for when */ /* the Ethernet card is in promiscious mode */ if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { /*fprintf(stderr, "ethernet: This packet isn't for us\n"); */ return 0; } (void) decode_unsigned16(&buf[12], &pdu_len); pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; /* copy the buffer into the PDU */ if (pdu_len < max_pdu) memmove(&pdu[0], &buf[17], pdu_len); /* ignore packets that are too large */ /* client should check my max apdu first */ else pdu_len = 0; return pdu_len; }
static void My_Router_Handler( BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data, uint8_t * npdu, /* PDU data */ uint16_t npdu_len) { uint16_t npdu_offset = 0; uint16_t dnet = 0; uint16_t len = 0; uint16_t j = 0; unsigned port_mappings = 0; unsigned port_id = 0; unsigned port_info_len = 0; switch (npdu_data->network_message_type) { case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: break; case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: break; case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: break; case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: break; case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: break; case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: break; case NETWORK_MESSAGE_INIT_RT_TABLE: break; case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: printf("Initialize-Routing-Table-Ack from "); for (j = 0; j < MAX_MAC_LEN; j++) { if (j < src->mac_len) { printf("%02X", src->mac[j]); } } if (npdu_len >= 1) { port_mappings = npdu[npdu_offset]; printf("\nPort Mappings: %u\n", port_mappings); npdu_offset++; npdu_len--; } while (npdu_len) { if (npdu_len >= 2) { len = decode_unsigned16(&npdu[npdu_offset], &dnet); printf("DNET=%hu, ", dnet); npdu_offset += len; npdu_len -= len; } if (npdu_len >= 1) { port_id = npdu[npdu_offset]; printf("Port ID=%u, ", port_id); npdu_offset++; npdu_len--; } if (npdu_len >= 1) { port_info_len = npdu[npdu_offset]; printf("Port Info Length=%u, ", port_info_len); npdu_offset++; npdu_len--; } else { port_info_len = 0; } if (port_info_len) { printf("Port Info=\""); while (port_info_len) { if (npdu_len >= 1) { printf("%02X", npdu[npdu_offset]); npdu_offset++; npdu_len--; } } printf("\""); } if (npdu_len) { printf("\n"); } } printf("\n"); break; case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: break; case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: break; default: break; } }
int dl_ip_recv( IP_DATA * data, MSG_DATA ** msg_data, BACNET_ADDRESS * src, unsigned timeout) { int received_bytes = 0; uint16_t buff_len = 0; /* return value */ fd_set read_fds; struct timeval select_timeout; struct sockaddr_in sin = { 0 }; socklen_t sin_len = sizeof(sin); /* make sure the socket is open */ if (data->socket < 0) return 0; 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(data->socket, &read_fds); #ifdef TEST_PACKET received_bytes = sizeof(test_packet); memmove(data->buff, &test_packet, received_bytes); sin.sin_addr.s_addr = 0x7E1D40A; sin.sin_port = 0xC0BA; #else int ret = select(data->socket + 1, &read_fds, NULL, NULL, &select_timeout); /* see if there is a packet for us */ if (ret > 0) received_bytes = recvfrom(data->socket, (char *) &data->buff[0], data->max_buff, 0, (struct sockaddr *) &sin, &sin_len); else return 0; #endif PRINT(DEBUG, "received from %s\n", inet_ntoa(sin.sin_addr)); /* check for errors */ if (received_bytes <= 0) { return 0; } /* the signature of a BACnet/IP packet */ if (data->buff[0] != BVLL_TYPE_BACNET_IP) return 0; switch (data->buff[1]) { case BVLC_ORIGINAL_UNICAST_NPDU: case BVLC_ORIGINAL_BROADCAST_NPDU:{ if ((sin.sin_addr.s_addr == data->local_addr.s_addr) && (sin.sin_port == data->port)) { buff_len = 0; PRINT(DEBUG, "BIP: src is me. Discarded!\n"); } else { src->mac_len = 6; memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4); memcpy(&src->mac[4], &sin.sin_port, 2); (void) decode_unsigned16(&data->buff[2], &buff_len); /* subtract off the BVLC header */ buff_len -= 4; if (buff_len < data->max_buff) { /* allocate data message stucture */ (*msg_data) = (MSG_DATA *) malloc(sizeof(MSG_DATA)); (*msg_data)->pdu_len = buff_len; (*msg_data)->pdu = (uint8_t *) malloc((*msg_data)->pdu_len); /* fill up data message structure */ memmove(&(*msg_data)->pdu[0], &data->buff[4], (*msg_data)->pdu_len); memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS)); } /* ignore packets that are too large */ else { buff_len = 0; PRINT(ERROR, "BIP: PDU too large. Discarded!.\n"); } } } break; case BVLC_FORWARDED_NPDU:{ memcpy(&sin.sin_addr.s_addr, &data->buff[4], 4); memcpy(&sin.sin_port, &data->buff[8], 2); if ((sin.sin_addr.s_addr == data->local_addr.s_addr) && (sin.sin_port == data->port)) { buff_len = 0; } else { src->mac_len = 6; memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4); memcpy(&src->mac[4], &sin.sin_port, 2); (void) decode_unsigned16(&data->buff[2], &buff_len); /* subtract off the BVLC header */ buff_len -= 10; if (buff_len < data->max_buff) { /* allocate data message stucture */ (*msg_data) = (MSG_DATA *) malloc(sizeof(MSG_DATA)); (*msg_data)->pdu_len = buff_len; (*msg_data)->pdu = (uint8_t *) malloc((*msg_data)->pdu_len); /* fill up data message structure */ memmove(&(*msg_data)->pdu, &data->buff[4 + 6], (*msg_data)->pdu_len); memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS)); } else { /* ignore packets that are too large */ buff_len = 0; } } } break; default: PRINT(ERROR, "BIP: BVLC discarded!\n"); break; } return buff_len; }
/** 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; //return 0; }