Files
paho.mqtt-sn.embedded-c/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp
2021-05-05 15:47:19 +09:00

447 lines
11 KiB
C++

/**************************************************************************************
* Copyright (c) 2016, Tomoaki Yamaguchi
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Tomoaki Yamaguchi - initial API and implementation and/or initial documentation
**************************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <regex>
#include <string>
#include <stdlib.h>
#include "SensorNetwork.h"
#include "MQTTSNGWProcess.h"
using namespace std;
using namespace MQTTSNGW;
/*===========================================
Class SensorNetAddreess
These 4 methods are minimum requirements for the SensorNetAddress class.
isMatch(SensorNetAddress* )
operator =(SensorNetAddress& )
setAddress(string* )
sprint(char* )
UDPPort class requires these 3 methods.
getIpAddress(void)
getPortNo(void)
setAddress(uint32_t IpAddr, uint16_t port)
============================================*/
SensorNetAddress::SensorNetAddress()
{
_portNo = 0;
_IpAddr = 0;
}
SensorNetAddress::~SensorNetAddress()
{
}
uint32_t SensorNetAddress::getIpAddress(void)
{
return _IpAddr;
}
uint16_t SensorNetAddress::getPortNo(void)
{
return _portNo;
}
void SensorNetAddress::setAddress(uint32_t IpAddr, uint16_t port)
{
_IpAddr = IpAddr;
_portNo = port;
}
/**
* Set Address data to SensorNetAddress
*
* @param *ip_port is "IP_Address:PortNo" format string
* @return success = 0, Invalid format = -1
*
* This function is used in ClientList::authorize(const char* fileName)
* e.g.
* Authorized clients are defined by fileName = "clients.conf"
*
* Client02,172.16.1.7:12002
* Client03,172.16.1.8:13003
* Client01,172.16.1.6:12001
*
* This definition is necessary when using TLS connection.
* Gateway rejects clients not on the list for security reasons.
*
*/
int SensorNetAddress::setAddress(string* ip_port)
{
size_t pos = ip_port->find_first_of(":");
if ( pos == string::npos )
{
_portNo = 0;
_IpAddr = INADDR_NONE;
return -1;
}
string ip = ip_port->substr(0, pos);
string port = ip_port->substr(pos + 1);
int portNo = 0;
if ((portNo = atoi(port.c_str())) == 0 || (_IpAddr = inet_addr(ip.c_str())) == INADDR_NONE)
{
return -1;
}
_portNo = htons(portNo);
return 0;
}
bool SensorNetAddress::isMatch(SensorNetAddress* addr)
{
return ((this->_portNo == addr->_portNo) && (this->_IpAddr == addr->_IpAddr));
}
SensorNetAddress& SensorNetAddress::operator =(SensorNetAddress& addr)
{
this->_portNo = addr._portNo;
this->_IpAddr = addr._IpAddr;
return *this;
}
char* SensorNetAddress::sprint(char* buf)
{
struct in_addr inaddr = { _IpAddr };
char* ip = inet_ntoa(inaddr);
sprintf( buf, "%s:", ip);
sprintf( buf + strlen(buf), "%d", ntohs(_portNo));
return buf;
}
/*================================================================
Class SensorNetwork
In Gateway version 1.0
getDescpription( ) is used by Gateway::initialize( )
initialize( ) is used by ClientSendTask::initialize( )
getSenderAddress( ) is used by ClientRecvTask::run( )
broadcast( ) is used by MQTTSNPacket::broadcast( )
unicast( ) is used by MQTTSNPacket::unicast( )
read( ) is used by MQTTSNPacket::recv( )
================================================================*/
SensorNetwork::SensorNetwork()
{
}
SensorNetwork::~SensorNetwork()
{
}
int SensorNetwork::unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendToAddr)
{
return UDPPort::unicast(payload, payloadLength, sendToAddr);
}
int SensorNetwork::broadcast(const uint8_t* payload, uint16_t payloadLength)
{
return UDPPort::broadcast(payload, payloadLength);
}
int SensorNetwork::read(uint8_t* buf, uint16_t bufLen)
{
return UDPPort::recv(buf, bufLen, &_clientAddr);
}
/**
* Prepare UDP sockets and description of SensorNetwork like
* "UDP Multicast 225.1.1.1:1883 Gateway Port 10000".
* The description is for a start up prompt.
*/
void SensorNetwork::initialize(void)
{
char param[MQTTSNGW_PARAM_MAX];
uint16_t multicastPortNo = 0;
uint16_t unicastPortNo = 0;
string ip;
unsigned int ttl = 1;
/*
* theProcess->getParam( ) copies
* a text specified by "Key" into param[] from the Gateway.conf
*
* in Gateway.conf e.g.
*
* # UDP
* GatewayPortNo=10000
* MulticastIP=225.1.1.1
* MulticastPortNo=1883
*
*/
if (theProcess->getParam("MulticastIP", param) == 0)
{
ip = param;
_description = "UDP Multicast ";
_description += param;
}
if (theProcess->getParam("MulticastPortNo", param) == 0)
{
multicastPortNo = atoi(param);
_description += ":";
_description += param;
}
if (theProcess->getParam("GatewayPortNo", param) == 0)
{
unicastPortNo = atoi(param);
_description += " Gateway Port ";
_description += param;
}
if (theProcess->getParam("MulticastTTL", param) == 0)
{
ttl = atoi(param);
_description += " TTL: ";
_description += param;
}
/* Prepare UDP sockets */
errno = 0;
if ( UDPPort::open(ip.c_str(), multicastPortNo, unicastPortNo, ttl) < 0 )
{
throw EXCEPTION("Can't open a UDP", errno);
}
}
const char* SensorNetwork::getDescription(void)
{
return _description.c_str();
}
SensorNetAddress* SensorNetwork::getSenderAddress(void)
{
return &_clientAddr;
}
/*=========================================
Class udpStack
=========================================*/
UDPPort::UDPPort()
{
_disconReq = false;
_sockfdUnicast = -1;
_sockfdMulticast = -1;
_ttl = 0;
}
UDPPort::~UDPPort()
{
close();
}
void UDPPort::close(void)
{
if (_sockfdUnicast > 0)
{
::close(_sockfdUnicast);
_sockfdUnicast = -1;
}
if (_sockfdMulticast > 0)
{
::close(_sockfdMulticast);
_sockfdMulticast = -1;
}
}
int UDPPort::open(const char* ipAddress, uint16_t multiPortNo, uint16_t uniPortNo, unsigned int ttl)
{
char loopch = 0;
const int reuse = 1;
if (uniPortNo == 0 || multiPortNo == 0)
{
D_NWSTACK("error portNo undefined in UDPPort::open\n");
return -1;
}
uint32_t ip = inet_addr(ipAddress);
_grpAddr.setAddress(ip, htons(multiPortNo));
_clientAddr.setAddress(ip, htons(uniPortNo));
_ttl = ttl;
/*------ Create unicast socket --------*/
_sockfdUnicast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_sockfdUnicast < 0)
{
D_NWSTACK("error can't create unicast socket in UDPPort::open\n");
return -1;
}
setsockopt(_sockfdUnicast, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
sockaddr_in addru;
addru.sin_family = AF_INET;
addru.sin_port = htons(uniPortNo);
addru.sin_addr.s_addr = INADDR_ANY;
if (::bind(_sockfdUnicast, (sockaddr*) &addru, sizeof(addru)) < 0)
{
D_NWSTACK("error can't bind unicast socket in UDPPort::open\n");
return -1;
}
if (setsockopt(_sockfdUnicast, IPPROTO_IP, IP_MULTICAST_LOOP, (char*) &loopch, sizeof(loopch)) < 0)
{
D_NWSTACK("error IP_MULTICAST_LOOP in UDPPort::open\n");
close();
return -1;
}
/*------ Create Multicast socket --------*/
_sockfdMulticast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_sockfdMulticast < 0)
{
D_NWSTACK("error can't create multicast socket in UDPPort::open\n");
close();
return -1;
}
setsockopt(_sockfdMulticast, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
sockaddr_in addrm;
addrm.sin_family = AF_INET;
addrm.sin_port = _grpAddr.getPortNo();
addrm.sin_addr.s_addr = INADDR_ANY;
if (::bind(_sockfdMulticast, (sockaddr*) &addrm, sizeof(addrm)) < 0)
{
D_NWSTACK("error can't bind multicast socket in UDPPort::open\n");
return -1;
}
if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_MULTICAST_LOOP, (char*) &loopch, sizeof(loopch)) < 0)
{
D_NWSTACK("error IP_MULTICAST_LOOP in UDPPort::open\n");
close();
return -1;
}
ip_mreq mreq;
mreq.imr_interface.s_addr = INADDR_ANY;
mreq.imr_multiaddr.s_addr = _grpAddr.getIpAddress();
if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
D_NWSTACK("error Multicast IP_ADD_MEMBERSHIP in UDPPort::open\n");
close();
return -1;
}
if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,sizeof(ttl)) < 0)
{
D_NWSTACK("error Multicast IP_ADD_MEMBERSHIP in UDPPort::open\n");
close();
return -1;
}
if (setsockopt(_sockfdUnicast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
D_NWSTACK("error Unicast IP_ADD_MEMBERSHIP in UDPPort::open\n");
close();
return -1;
}
return 0;
}
int UDPPort::unicast(const uint8_t* buf, uint32_t length, SensorNetAddress* addr)
{
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = addr->getPortNo();
dest.sin_addr.s_addr = addr->getIpAddress();
int status = ::sendto(_sockfdUnicast, buf, length, 0, (const sockaddr*) &dest, sizeof(dest));
if (status < 0)
{
D_NWSTACK("errno == %d in UDPPort::sendto\n", errno);
}
D_NWSTACK("sendto %s:%u length = %d\n", inet_ntoa(dest.sin_addr), ntohs(dest.sin_port), status);
return status;
}
int UDPPort::broadcast(const uint8_t* buf, uint32_t length)
{
return unicast(buf, length, &_grpAddr);
}
int UDPPort::recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr)
{
struct timeval timeout;
fd_set recvfds;
int maxSock = 0;
timeout.tv_sec = 1;
timeout.tv_usec = 0; // 1 sec
FD_ZERO(&recvfds);
FD_SET(_sockfdUnicast, &recvfds);
FD_SET(_sockfdMulticast, &recvfds);
if (_sockfdMulticast > _sockfdUnicast)
{
maxSock = _sockfdMulticast;
}
else
{
maxSock = _sockfdUnicast;
}
int rc = 0;
if ( select(maxSock + 1, &recvfds, 0, 0, &timeout) > 0 )
{
if (FD_ISSET(_sockfdUnicast, &recvfds))
{
rc = recvfrom(_sockfdUnicast, buf, len, 0, addr);
}
else if (FD_ISSET(_sockfdMulticast, &recvfds))
{
rc = recvfrom(_sockfdMulticast, buf, len, 0, &_grpAddr);
}
}
return rc;
}
int UDPPort::recvfrom(int sockfd, uint8_t* buf, uint16_t len, uint8_t flags, SensorNetAddress* addr)
{
sockaddr_in sender;
socklen_t addrlen = sizeof(sender);
memset(&sender, 0, addrlen);
int status = ::recvfrom(sockfd, buf, len, flags, (sockaddr*) &sender, &addrlen);
if (status < 0 && errno != EAGAIN)
{
D_NWSTACK("errno == %d in UDPPort::recvfrom\n", errno);
return -1;
}
addr->setAddress(sender.sin_addr.s_addr, sender.sin_port);
D_NWSTACK("recved from %s:%d length = %d\n", inet_ntoa(sender.sin_addr),ntohs(sender.sin_port), status);
return status;
}