diff --git a/bacnet-stack/ports/lwip/bip.c b/bacnet-stack/ports/lwip/bip.c new file mode 100644 index 00000000..14df1f55 --- /dev/null +++ b/bacnet-stack/ports/lwip/bip.c @@ -0,0 +1,343 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2012 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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bip.h" +#include "bvlc.h" +#include "handlers.h" +#include "net.h" /* custom per port */ + +/** @file bip.c Configuration and Operations for BACnet/IP */ + +/* port to use - stored in network byte order */ +static uint16_t BIP_Port = 0xBAC0; +/* 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; +/* lwIP socket, of sorts */ +static struct udp_pcb *Server_upcb; + +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 void bip_mac_to_addr( + struct ip_addr *address, + uint8_t *mac) +{ + if (mac && address) { + address->addr = ((u32_t) ((((uint32_t) mac[0]) << 24) & 0xff000000)); + address->addr |= ((u32_t) ((((uint32_t) mac[1]) << 16) & 0x00ff0000)); + address->addr |= ((u32_t) ((((uint32_t) mac[2]) << 8) & 0x0000ff00)); + address->addr |= ((u32_t) (((uint32_t) mac[3]) & 0x000000ff)); + } +} + +static void bip_addr_to_mac( + uint8_t *mac, + struct ip_addr *address) +{ + if (mac && address) { + mac[0] = (uint8_t)(address->addr >> 24); + mac[1] = (uint8_t)(address->addr >> 16); + mac[2] = (uint8_t)(address->addr >> 8); + mac[3] = (uint8_t)(address->addr); + } +} + +static int bip_decode_bip_address( + BACNET_ADDRESS * bac_addr, + struct ip_addr *address, /* in network format */ + uint16_t * port) +{ /* in network format */ + int len = 0; + + if (bac_addr) { + bip_mac_to_addr(address, &bac_addr->mac[0]); + 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) +{ + struct pbuf *pkt = NULL; + uint8_t *mtu = NULL; + int mtu_len = 0; + /* addr and port in host format */ + struct ip_addr dst_ip; + uint16_t port = 0; + uint16_t length = pdu_len + 4; + err_t status = ERR_OK; + + (void) npdu_data; + pkt = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_POOL); + if (pkt == NULL) { + return 0; + } + mtu = (uint8_t *)pkt->payload; + mtu[0] = BVLL_TYPE_BACNET_IP; + if (dest->net == BACNET_BROADCAST_NETWORK) { + /* broadcast */ + dst_ip.addr = BIP_Broadcast_Address.s_addr; + port = BIP_Port; + mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; + } else if (dest->mac_len == 6) { + /* unicast */ + bip_decode_bip_address(dest, &dst_ip, &port); + mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU; + } else { + /* invalid address */ + return -1; + } + 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; + pkt->len = mtu_len; + /* Send the packet */ + status = udp_sendto(Server_upcb, pkt, &dst_ip, port); + /* free the buffer pbuf */ + pbuf_free(pkt); + if (status != ERR_OK) { + return 0; + } + + return mtu_len; +} + +void bip_server_callback( + void *arg, + struct udp_pcb *upcb, + struct pbuf *pkt, + struct ip_addr *addr, + u16_t port) +{ + uint8_t function = 0; + uint16_t pdu_len = 0; + uint16_t pdu_offset = 0; + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + struct ip_addr sin_addr; + uint16_t sin_port = 0; + uint8_t *pdu = (uint8_t *)pkt->payload; + + /* the signature of a BACnet/IP packet */ + if (pdu[0] != BVLL_TYPE_BACNET_IP) { + return; + } + function = pdu[1]; + if ((function == BVLC_ORIGINAL_UNICAST_NPDU) || + (function == BVLC_ORIGINAL_BROADCAST_NPDU)) { + /* ignore messages from me */ + if ((addr->addr == BIP_Address.s_addr) && + (port == BIP_Port)) { + pdu_len = 0; + } else { + /* data in src->mac[] is in network format */ + src.mac_len = 6; + bip_addr_to_mac(&src.mac[0], addr); + memcpy(&src.mac[4], &port, 2); + /* 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; + pdu_offset = 4; + } + } else if (function == BVLC_FORWARDED_NPDU) { + bip_mac_to_addr(&sin_addr, &pdu[4]); + memcpy(&sin_port, &pdu[8], 2); + if ((sin_addr.addr == BIP_Address.s_addr) && + (sin_port == BIP_Port)) { + /* ignore forwarded messages from me */ + pdu_len = 0; + } else { + /* data in src->mac[] is in network format */ + src.mac_len = 6; + bip_addr_to_mac(&src.mac[0], &sin_addr); + memcpy(&src.mac[4], &sin_port, 2); + /* 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; + pdu_offset = 10; + } + } + if (pdu_len) { + npdu_handler(&src, &pdu[pdu_offset], pdu_len); + } +#if 0 + /* prepare for next packet */ + udp_disconnect(upcb); + udp_bind(upcb, IP_ADDR_ANY, BIP_Port); + /* Set a receive callback for the upcb */ + udp_recv(upcb, bip_server_callback, NULL); +#endif + /* free our packet */ + pbuf_free(pkt); +} + +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; +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send broadcasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IP (by default, 0xBAC0 = 47808). + * + * @note For Windows, ifname is the dotted ip address of the interface. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "eth0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip_init( + char *ifname) +{ + (void)ifname; + /* Create a new UDP control block */ + Server_upcb = udp_new(); + if (Server_upcb == NULL) { + /* increase MEMP_NUM_UDP_PCB in lwipopts.h */ + while(1) {}; + } + /* Bind the upcb to the UDP_PORT port */ + /* Using IP_ADDR_ANY allow the upcb to be used by any local interface */ + udp_bind(Server_upcb, IP_ADDR_ANY, BIP_Port); + /* Set a receive callback for the upcb */ + udp_recv(Server_upcb, bip_server_callback, NULL); + + return true; +} diff --git a/bacnet-stack/ports/lwip/net.h b/bacnet-stack/ports/lwip/net.h new file mode 100644 index 00000000..7edb4f0e --- /dev/null +++ b/bacnet-stack/ports/lwip/net.h @@ -0,0 +1,55 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* 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 NET_H +#define NET_H + +#include +#include +#include +#include "lwip/def.h" +#include "lwip/udp.h" +#include "lwip/memp.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "ethernetif.h" +#include "lwip/inet.h" + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; + char sa_data[14]; +}; + +#endif