different modes for UDP writer, allow to select network

This commit is contained in:
andreas 2024-11-07 19:47:27 +01:00
parent 490a5b9ba1
commit a5827e24d8
4 changed files with 250 additions and 24 deletions

View File

@ -17,4 +17,9 @@ class GwSocketHelper{
if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) != ESP_OK) return false;
return true;
}
static bool isMulticast(const String &addr){
in_addr iaddr;
if (inet_pton(AF_INET,addr.c_str(),&iaddr) != 1) return false;
return IN_MULTICAST(ntohl(iaddr.s_addr));
}
};

View File

@ -4,36 +4,175 @@
#include "GwBuffer.h"
#include "GwSocketConnection.h"
#include "GwSocketHelper.h"
#include "GWWifi.h"
GwUdpWriter::WriterSocket::WriterSocket(GwLog *l,int p,const String &src,const String &dst, SourceMode sm) :
sourceMode(sm), source(src), destination(dst), port(p),logger(l)
{
if (inet_pton(AF_INET, dst.c_str(), &dstA.sin_addr) != 1)
{
LOG_ERROR("UDPW: invalid destination ip address %s", dst.c_str());
return;
}
if (sourceMode != SourceMode::S_UNBOUND)
{
if (inet_pton(AF_INET, src.c_str(), &srcA) != 1)
{
LOG_ERROR("UDPW: invalid source ip address %s", src.c_str());
return;
}
}
dstA.sin_family=AF_INET;
dstA.sin_port=htons(port);
fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
if (fd < 0){
LOG_ERROR("UDPW: unable to create udp socket: %d",errno);
return;
}
int enable = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
switch (sourceMode)
{
case SourceMode::S_SRC:
{
sockaddr_in bindA;
bindA.sin_family = AF_INET;
bindA.sin_port = htons(0); // let system select
bindA.sin_addr = srcA;
if (bind(fd, (struct sockaddr *)&bindA, sizeof(bindA)) != 0)
{
LOG_ERROR("UDPW: bind failed for address %s: %d", source.c_str(), errno);
::close(fd);
fd = -1;
return;
}
}
break;
case SourceMode::S_MC:
{
if (setsockopt(fd,IPPROTO_IP,IP_MULTICAST_IF,&srcA,sizeof(srcA)) != 0){
LOG_ERROR("UDPW: unable to set MC source %s: %d",source.c_str(),errno);
::close(fd);
fd=-1;
return;
}
int loop=0;
setsockopt(fd,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));
}
break;
default:
//not bound
break;
}
}
bool GwUdpWriter::WriterSocket::changed(const String &newSrc, const String &newDst){
if (newDst != destination) return true;
if (sourceMode == SourceMode::S_UNBOUND) return false;
return newSrc != source;
}
size_t GwUdpWriter::WriterSocket::send(const char *buf,size_t len){
if (fd < 0) return 0;
ssize_t err = sendto(fd,buf,len,0,(struct sockaddr *)&dstA, sizeof(dstA));
if (err < 0){
LOG_DEBUG(GwLog::DEBUG,"UDPW %s error sending: %d",destination.c_str(), errno);
return 0;
}
return err;
}
GwUdpWriter::GwUdpWriter(const GwConfigHandler *config, GwLog *logger, int minId)
{
this->config = config;
this->logger = logger;
this->minId = minId;
port=config->getInt(GwConfigDefinitions::udpwPort);
}
void GwUdpWriter::checkStaSocket(){
String src;
String bc;
if (type == T_BCAP || type == T_MCAP || type == T_NORM || type == T_UNKNOWN ) return;
bool connected=false;
if (WiFi.isConnected()){
src=WiFi.localIP().toString();
bc=WiFi.broadcastIP().toString();
connected=true;
}
else{
if (staSocket == nullptr) return;
}
String dst;
WriterSocket::SourceMode sm=WriterSocket::SourceMode::S_SRC;
switch (type){
case T_BCALL:
case T_BCSTA:
sm=WriterSocket::SourceMode::S_SRC;
dst=bc;
break;
case T_MCALL:
case T_MCSTA:
dst=config->getString(GwConfigDefinitions::udpwMC);
sm=WriterSocket::SourceMode::S_MC;
break;
}
if (staSocket != nullptr)
{
if (!connected || staSocket->changed(src, dst))
{
staSocket->close();
delete staSocket;
staSocket = nullptr;
LOG_INFO("changing/stopping UDPW(sta) socket");
}
}
if (staSocket == nullptr && connected)
{
LOG_INFO("creating new UDP(sta) socket src=%s, dst=%s", src.c_str(), dst.c_str());
staSocket = new WriterSocket(logger, port, src, dst, WriterSocket::SourceMode::S_SRC);
}
}
void GwUdpWriter::begin()
{
fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
if (fd < 0){
LOG_ERROR("unable to create udp socket");
return;
if (type != T_UNKNOWN) return; //already started
type=(UType)(config->getInt(GwConfigDefinitions::udpwType));
LOG_INFO("UDPW begin, mode=%d",(int)type);
String src=WiFi.softAPIP().toString();
String dst;
WriterSocket::SourceMode sm=WriterSocket::SourceMode::S_UNBOUND;
bool createApSocket=false;
switch(type){
case T_BCALL:
case T_BCAP:
createApSocket=true;
dst=WiFi.softAPBroadcastIP().toString();
sm=WriterSocket::SourceMode::S_SRC;
break;
case T_MCALL:
case T_MCAP:
createApSocket=true;
dst=config->getString(GwConfigDefinitions::udpwMC);
sm=WriterSocket::SourceMode::S_SRC;
break;
case T_NORM:
createApSocket=true;
dst=config->getString(GwConfigDefinitions::udpwAddress);
sm=WriterSocket::SourceMode::S_UNBOUND;
}
int enable = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
port=config->getInt(GwConfigDefinitions::udpwPort);
//TODO: check port
address=config->getString(GwConfigDefinitions::udpwAddress);
LOG_INFO("UDP writer created, address=%s, port=%d",
address.c_str(),port);
inet_pton(AF_INET, address.c_str(), &destination.sin_addr);
destination.sin_family = AF_INET;
destination.sin_port = htons(port);
if (createApSocket){
LOG_INFO("creating new UDPW(ap) socket src=%s, dst=%s", src.c_str(), dst.c_str());
apSocket=new WriterSocket(logger,port,src,dst,sm);
}
checkStaSocket();
}
void GwUdpWriter::loop(bool handleRead, bool handleWrite)
{
if (handleWrite){
checkStaSocket();
}
}
@ -45,12 +184,17 @@ size_t GwUdpWriter::sendToClients(const char *buf, int source,bool partial)
{
if (source == minId) return 0;
size_t len=strlen(buf);
ssize_t err = sendto(fd,buf,len,0,(struct sockaddr *)&destination, sizeof(destination));
if (err < 0){
LOG_DEBUG(GwLog::DEBUG,"UDP writer error sending: %d",errno);
return 0;
bool hasSent=false;
size_t res=0;
if (apSocket != nullptr){
res=apSocket->send(buf,len);
if (res > 0) hasSent=true;
}
return err;
if (staSocket != nullptr){
res=staSocket->send(buf,len);
if (res > 0) hasSent=true;
}
return hasSent?len:0;
}

View File

@ -9,14 +9,59 @@
#include <arpa/inet.h>
class GwUdpWriter: public GwChannelInterface{
public:
using UType=enum{
T_BCALL=0,
T_BCAP=1,
T_BCSTA=2,
T_NORM=3,
T_MCALL=4,
T_MCAP=5,
T_MCSTA=6,
T_UNKNOWN=-1
};
private:
class WriterSocket{
public:
int fd=-1;
struct in_addr srcA;
struct sockaddr_in dstA;
String source;
String destination;
int port;
GwLog *logger;
using SourceMode=enum {
S_UNBOUND=0,
S_MC,
S_SRC
};
SourceMode sourceMode;
WriterSocket(GwLog *logger,int p,const String &src,const String &dst, SourceMode sm);
void close(){
if (fd > 0){
::close(fd);
}
fd=-1;
}
~WriterSocket(){
close();
}
bool changed(const String &newSrc, const String &newDst);
size_t send(const char *buf,size_t len);
};
const GwConfigHandler *config;
GwLog *logger;
int fd=-1;
/**
* we use fd/address to send to the AP network
* and fd2,address2 to send to the station network
* for type "normal" we only use fd
*/
WriterSocket *apSocket=nullptr; //also for T_NORM
WriterSocket *staSocket=nullptr;
int minId;
int port;
String address;
struct sockaddr_in destination;
UType type=T_UNKNOWN;
void checkStaSocket();
public:
GwUdpWriter(const GwConfigHandler *config,GwLog *logger,int minId);
~GwUdpWriter();

View File

@ -842,6 +842,23 @@
"description": "the UDP port we send to",
"category": "UDP writer"
},
{
"name": "udpwType",
"label": "remote address type",
"type": "list",
"default": "0",
"description": "to which networks/addresses do we send\nbc-all: send broadcast to AP and wifi client network\nbc-ap: send broadcast to access point only\nbc-cli: send broadcast to wifi client network\nnormal: normal target address\nmc-all: multicast to AP and wifi client network\nmc-ap:multicast to AP network\nmc-cli: muticast to wifi client network",
"list":[
{"l":"bc-all","v":"0"},
{"l":"bc-ap","v":"1"},
{"l":"bc-cli","v":"2"},
{"l":"normal","v":"3"},
{"l":"mc-all","v":"4"},
{"l":"mc-ap","v":"5"},
{"l":"mc-cli","v":"6"}
],
"category": "UDP writer"
},
{
"name": "udpwAddress",
"label": "remote address",
@ -849,7 +866,22 @@
"default": "",
"check": "checkIpAddress",
"description": "the IP address we connect to in the form 192.168.1.2",
"category": "UDP writer"
"category": "UDP writer",
"condition":{
"udpwType":["3"]
}
},
{
"name": "udpwMC",
"label": "multicast address",
"type": "string",
"default": "224.0.0.1",
"check": "checkMCAddress",
"description": "the multicast address we send to 224.0.0.0...239.255.255.255",
"category": "UDP writer",
"condition":{
"udpwType":["4","5","6"]
}
},
{
"name": "udpwWriteFilter",