322 lines
9.2 KiB
C
322 lines
9.2 KiB
C
#include <stdint.h>
|
|
#include "bacdef.h"
|
|
#include "arcnet.h"
|
|
#include "net.h"
|
|
|
|
// my local device data - MAC address
|
|
uint8_t ARCNET_MAC_Address = 0;
|
|
// ARCNET file handle
|
|
static int ARCNET_Sock_FD = -1;
|
|
// ARCNET socket address (has the interface name)
|
|
static struct sockaddr ARCNET_Socket_Address;
|
|
// Broadcast address
|
|
#define ARCNET_BROADCAST 0
|
|
|
|
bool arcnet_valid(void)
|
|
{
|
|
return (ARCNET_Sock_FD >= 0);
|
|
}
|
|
|
|
void arcnet_cleanup(void)
|
|
{
|
|
if (arcnet_valid())
|
|
close(ARCNET_Sock_FD);
|
|
ARCNET_Sock_FD = -1;
|
|
|
|
return;
|
|
}
|
|
|
|
static int arcnet_bind(char *interface_name)
|
|
{
|
|
int sock_fd = -1; // return value
|
|
struct ifreq ifr;
|
|
int rv; // return value - error value from df or ioctl call
|
|
|
|
fprintf(stderr,"arcnet: opening \"%s\"\n",interface_name);
|
|
// note: on some systems you may have to add or enable in
|
|
// modules.conf (or in modutils/alias on Debian with update-modules)
|
|
// alias net-pf-17 af_packet
|
|
// Then follow it by: # modprobe af_packet
|
|
if ((sock_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0)
|
|
{
|
|
/* Error occured */
|
|
fprintf(stderr,
|
|
"arcnet: Error opening socket: %s\n",
|
|
strerror(errno));
|
|
fprintf(stderr,
|
|
"You might need to add the following to modules.conf\n"
|
|
"(or in /etc/modutils/alias on Debian with update-modules):\n"
|
|
"alias net-pf-17 af_packet\n"
|
|
"Also, add af_packet to /etc/modules.\n"
|
|
"Then follow it by:\n"
|
|
"# modprobe af_packet\n");
|
|
exit(-1);
|
|
}
|
|
|
|
if (ARCNET_Sock_FD >= 0)
|
|
{
|
|
/* Bind the socket to an interface name so we only get packets from it */
|
|
ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET;
|
|
//ARCNET_Socket_Address.sa_family = PF_INET;
|
|
/* Clear the memory before copying */
|
|
memset(ARCNET_Socket_Address.sa_data, '\0',
|
|
sizeof(ARCNET_Socket_Address.sa_data));
|
|
/* Strcpy the interface name into the address */
|
|
strncpy(ARCNET_Socket_Address.sa_data, interface_name,
|
|
sizeof(ARCNET_Socket_Address.sa_data)-1);
|
|
fprintf(stderr,"arcnet: binding \"%s\"\n",ARCNET_Socket_Address.sa_data);
|
|
if (bind(sock_fd, &ARCNET_Socket_Address,
|
|
sizeof(ARCNET_Socket_Address)) != 0)
|
|
{
|
|
/* Bind problem, close socket and return */
|
|
fprintf(stderr,
|
|
"arcnet: Unable to bind socket : %s\n",
|
|
strerror(errno));
|
|
fprintf(stderr,
|
|
"You might need to add the following to modules.conf\n"
|
|
"(or in /etc/modutils/alias on Debian with update-modules):\n"
|
|
"alias net-pf-17 af_packet\n"
|
|
"Also, add af_packet to /etc/modules.\n"
|
|
"Then follow it by:\n"
|
|
"# modprobe af_packet\n");
|
|
/* Close the socket */
|
|
close(sock_fd);
|
|
exit(-1);
|
|
}
|
|
}
|
|
strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
|
|
rv = ioctl(sock_fd, SIOCGIFHWADDR, &ifr);
|
|
if (rv != -1) /* worked okay */
|
|
ARCNET_MAC_Address = ifr.ifr_hwaddr.sa_data[0];
|
|
fprintf(stderr,"arcnet: MAC=%02Xh\n",ARCNET_MAC_Address);
|
|
|
|
atexit(arcnet_cleanup);
|
|
|
|
return sock_fd;
|
|
}
|
|
|
|
bool arcnet_init(char *interface_name)
|
|
{
|
|
ARCNET_Sock_FD = arcnet_bind(interface_name);
|
|
|
|
return arcnet_valid();
|
|
}
|
|
|
|
/* function to send a packet out the socket */
|
|
/* returns number of bytes sent on success, negative on failure */
|
|
int arcnet_send(
|
|
BACNET_ADDRESS *dest, // destination address
|
|
BACNET_ADDRESS *src, // source address
|
|
uint8_t *pdu, // any data to be sent - may be null
|
|
unsigned pdu_len) // number of bytes of data
|
|
{
|
|
int bytes = 0;
|
|
uint8_t mtu[512] = { 0 };
|
|
int mtu_len = 0;
|
|
struct archdr *pkt = (struct archdr *)mtu;
|
|
|
|
// don't waste time if the socket is not valid
|
|
if (ARCNET_Sock_FD < 0)
|
|
{
|
|
fprintf(stderr, "arcnet: socket is invalid!\n");
|
|
return -1;
|
|
}
|
|
/* load destination MAC address */
|
|
if (dest->mac_len == 1)
|
|
pkt->hard.dest = dest->mac[0];
|
|
else
|
|
{
|
|
fprintf(stderr, "arcnet: invalid destination MAC address!\n");
|
|
return -2;
|
|
}
|
|
if (src->mac_len == 1)
|
|
pkt->hard.source = src->mac[0];
|
|
else
|
|
{
|
|
fprintf(stderr, "arcnet: invalid source MAC address!\n");
|
|
return -3;
|
|
}
|
|
if ((ARC_HDR_SIZE + pdu_len) > 512)
|
|
{
|
|
fprintf(stderr, "arcnet: PDU is too big to send!\n");
|
|
return -4;
|
|
}
|
|
/* Logical PDU portion */
|
|
pkt->soft.raw[0] = 0xCD; /* SC for BACnet */
|
|
pkt->soft.raw[1] = 0x82; /* DSAP for BACnet */
|
|
pkt->soft.raw[2] = 0x82; /* SSAP for BACnet */
|
|
pkt->soft.raw[3] = 0x03; /* Control byte in header */
|
|
memcpy(&pkt->soft.raw[4], pdu, pdu_len);
|
|
/* packet length */
|
|
mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC*/ + pdu_len;
|
|
/* Send the packet */
|
|
bytes =
|
|
sendto(ARCNET_Sock_FD, &mtu, mtu_len, 0,
|
|
(struct sockaddr *) &ARCNET_Socket_Address,
|
|
sizeof(ARCNET_Socket_Address));
|
|
/* did it get sent? */
|
|
if (bytes < 0)
|
|
fprintf(stderr,"arcnet: Error sending packet: %s\n",
|
|
strerror(errno));
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/* function to send a PDU out the socket */
|
|
/* returns number of bytes sent on success, negative on failure */
|
|
int arcnet_send_pdu(
|
|
BACNET_ADDRESS *dest, // destination address
|
|
uint8_t *pdu, // any data to be sent - may be null
|
|
unsigned pdu_len) // number of bytes of data
|
|
{
|
|
BACNET_ADDRESS src = {0}; // source address
|
|
|
|
src.mac[0] = ARCNET_MAC_Address;
|
|
src.mac_len = 1;
|
|
return arcnet_send(dest, // destination address
|
|
&src, // source address
|
|
pdu, // any data to be sent - may be null
|
|
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
|
|
{
|
|
int received_bytes;
|
|
uint8_t buf[512] = {0}; // data
|
|
uint16_t pdu_len = 0; // return value
|
|
fd_set read_fds;
|
|
int max;
|
|
struct timeval select_timeout;
|
|
struct archdr *pkt = (struct archdr *)buf;
|
|
|
|
/* Make sure the socket is open */
|
|
if (ARCNET_Sock_FD <= 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(ARCNET_Sock_FD, &read_fds);
|
|
max = ARCNET_Sock_FD;
|
|
|
|
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
|
|
received_bytes = read(ARCNET_Sock_FD, &buf[0], sizeof(buf));
|
|
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;
|
|
|
|
printf("arcnet: received %u bytes from %02Xh (proto==%02Xh)\n",
|
|
received_bytes, pkt->hard.source, pkt->soft.raw[0]);
|
|
|
|
if (pkt->hard.source == ARCNET_MAC_Address) {
|
|
fprintf(stderr,"arcnet: self sent packet?\n");
|
|
return 0;
|
|
}
|
|
if (pkt->soft.raw[0] != 0xCD) {
|
|
fprintf(stderr,"arcnet: Non-BACnet packet.\n");
|
|
return 0;
|
|
}
|
|
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
|
|
(pkt->hard.dest != ARCNET_BROADCAST))
|
|
{
|
|
fprintf(stderr,"arcnet: This packet is not for us.\n");
|
|
return 0;
|
|
}
|
|
if ((pkt->soft.raw[1] != 0x82) || /* DSAP */
|
|
(pkt->soft.raw[2] != 0x82) || /* LSAP */
|
|
(pkt->soft.raw[3] != 0x03)) /* LLC Control */
|
|
{
|
|
fprintf(stderr,"arcnet: BACnet packet has invalid LLC.\n");
|
|
return 0;
|
|
}
|
|
/* It must be addressed to us or be a Broadcast */
|
|
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
|
|
(pkt->hard.dest != ARCNET_BROADCAST))
|
|
{
|
|
fprintf(stderr,"arcnet: This packet is not for us.\n");
|
|
return 0;
|
|
}
|
|
/* copy the source address */
|
|
src->mac_len = 1;
|
|
src->mac[0] = pkt->hard.source;
|
|
/* compute the PDU length */
|
|
pdu_len = received_bytes - ARC_HDR_SIZE;
|
|
pdu_len -= 4 /* SC, DSAP, SSAP, LLC Control */ ;
|
|
// copy the buffer into the PDU
|
|
if (pdu_len < max_pdu)
|
|
memmove(&pdu[0],&pkt->soft.raw[4],pdu_len);
|
|
// silently ignore packets that are too large
|
|
else
|
|
pdu_len = 0;
|
|
|
|
return pdu_len;
|
|
}
|
|
|
|
void arcnet_get_my_address(BACNET_ADDRESS *my_address)
|
|
{
|
|
int i = 0;
|
|
|
|
my_address->mac_len = 1;
|
|
my_address->mac[0] = ARCNET_MAC_Address;
|
|
my_address->net = 0; // local only, no routing
|
|
my_address->len = 0;
|
|
for (i = 0; i < MAX_MAC_LEN; i++)
|
|
{
|
|
my_address->adr[i] = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void arcnet_get_broadcast_address(
|
|
BACNET_ADDRESS *dest) // destination address
|
|
{
|
|
int i = 0; // counter
|
|
|
|
if (dest)
|
|
{
|
|
dest->mac[0] = ARCNET_BROADCAST;
|
|
dest->mac_len = 1;
|
|
dest->net = BACNET_BROADCAST_NETWORK;
|
|
dest->len = 0; // len=0 denotes broadcast address
|
|
for (i = 0; i < MAX_MAC_LEN; i++)
|
|
{
|
|
dest->adr[i] = 0;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|