Exemplo n.º 1
0
/* 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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
/* 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;
}
Exemplo n.º 4
0
/** 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;
    }
}
Exemplo n.º 5
0
/** 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;
}
Exemplo n.º 6
0
/* 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;
}
Exemplo n.º 7
0
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;
    }
}
Exemplo n.º 8
0
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;
}