diff --git a/src/cg3protocol.cpp b/src/cg3protocol.cpp index ed4de34..053da9d 100644 --- a/src/cg3protocol.cpp +++ b/src/cg3protocol.cpp @@ -69,7 +69,7 @@ bool CG3Protocol::Init(void) std::cout << "Error opening socket on port UDP" << G3_CONFIG_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl; } - ok &= ((m_IcmpRawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0); + ok &= m_IcmpRawSocket.Open(IPPROTO_ICMP); if ( !ok ) { std::cout << "Error opening raw socket for ICMP" << std::endl; @@ -136,14 +136,10 @@ void CG3Protocol::ConfigThread(CG3Protocol *This) void CG3Protocol::IcmpThread(CG3Protocol *This) { - fcntl(This->m_IcmpRawSocket, F_SETFL, O_NONBLOCK); - while ( !This->m_bStopThread ) { This->IcmpTask(); } - - close(This->m_IcmpRawSocket); } @@ -354,49 +350,27 @@ void CG3Protocol::ConfigTask(void) void CG3Protocol::IcmpTask(void) { - unsigned char buffer[256]; + CBuffer Buffer; + CIp Ip; + int iIcmpType; - struct sockaddr_in sa; - unsigned int sasize = sizeof(sa); - - fd_set FdSet; - struct timeval tv; - - if (m_IcmpRawSocket != -1) + if ((iIcmpType = m_IcmpRawSocket.IcmpReceive(&Buffer, &Ip, 20)) != -1) { - FD_ZERO(&FdSet); - FD_SET(m_IcmpRawSocket, &FdSet); - tv.tv_sec = 0; - tv.tv_usec = 100000; - select(m_IcmpRawSocket + 1, &FdSet, 0, 0, &tv); - - int len = recvfrom(m_IcmpRawSocket, buffer, 255, 0, (struct sockaddr *)&sa, &sasize); - - if (len > 28) + if (iIcmpType == ICMP_DEST_UNREACH) { - struct ip *iph = (struct ip *)buffer; - int iphdrlen = iph->ip_hl * 4; - struct icmp *icmph = (struct icmp *)(buffer + iphdrlen); - - if (icmph->icmp_type == ICMP_DEST_UNREACH) - { - struct ip *remote_iph = (struct ip *)(buffer + iphdrlen + 8); - CClients *clients = g_Reflector.GetClients(); int index = -1; CClient *client = NULL; while ( (client = clients->FindNextClient(PROTOCOL_G3, &index)) != NULL ) { - CIp Ip = client->GetIp(); - if (Ip.GetAddr() == remote_iph->ip_dst.s_addr) + CIp ClientIp = client->GetIp(); + if (ClientIp.GetAddr() == Ip.GetAddr()) { clients->RemoveClient(client); } } g_Reflector.ReleaseClients(); - - } } } } @@ -431,7 +405,8 @@ void CG3Protocol::Task(void) { BaseIp = &ClIp; client->Alive(); - // supress host checks + // supress host checks - no ping needed to trigger potential ICMPs + // the regular data flow will do it m_LastKeepaliveTime.Now(); break; } @@ -466,7 +441,7 @@ void CG3Protocol::Task(void) else if ( (LastFrame = IsValidDvLastFramePacket(Buffer)) != NULL ) { //std::cout << "Terminal DV last frame" << std::endl; - + // handle it OnDvLastFramePacketIn(LastFrame, BaseIp); } @@ -484,20 +459,20 @@ void CG3Protocol::Task(void) // handle end of streaming timeout CheckStreamsTimeout(); - + // handle queue from reflector HandleQueue(); - - // keep alive + + // keep alive during idle if needed if ( m_LastKeepaliveTime.DurationSinceNow() > G3_KEEPALIVE_PERIOD ) { // handle keep alives HandleKeepalives(); - + // update time m_LastKeepaliveTime.Now(); - // reload option if needed + // reload option if needed - called once every G3_KEEPALIVE_PERIOD NeedReload(); } } @@ -807,22 +782,22 @@ void CG3Protocol::NeedReload(void) if (m_LastModTime != fileStat.st_mtime) { ReadOptions(); - } - } - // iterate on clients - CClients *clients = g_Reflector.GetClients(); - int index = -1; - CClient *client = NULL; - while ( (client = clients->FindNextClient(PROTOCOL_G3, &index)) != NULL ) - { - char module = client->GetReflectorModule(); - if (!strchr(m_Modules.c_str(), module) && !strchr(m_Modules.c_str(), '*')) - { - clients->RemoveClient(client); + // we have new options - iterate on clients for potential removal + CClients *clients = g_Reflector.GetClients(); + int index = -1; + CClient *client = NULL; + while ( (client = clients->FindNextClient(PROTOCOL_G3, &index)) != NULL ) + { + char module = client->GetReflectorModule(); + if (!strchr(m_Modules.c_str(), module) && !strchr(m_Modules.c_str(), '*')) + { + clients->RemoveClient(client); + } + } + g_Reflector.ReleaseClients(); } } - g_Reflector.ReleaseClients(); } void CG3Protocol::ReadOptions(void) diff --git a/src/cg3protocol.h b/src/cg3protocol.h index 7c0da03..5ffdb79 100644 --- a/src/cg3protocol.h +++ b/src/cg3protocol.h @@ -31,6 +31,8 @@ #include "cdvheaderpacket.h" #include "cdvframepacket.h" #include "cdvlastframepacket.h" +#include "crawsocket.h" +#include "cudpmsgsocket.h" //////////////////////////////////////////////////////////////////////////////////////// @@ -44,9 +46,11 @@ // 2 - Destination request on port UDP 12345 // - Calls to specific callsigns will be accepted for a default module // - Repeater calls will be accepted for local modules -// - All other calls are rehected +// - All other calls are rejected // -// 3 - Actual D-star flow like in Dextra to/from port 40000 (2 distint sockets) +// 3 - Actual D-star flow like in Dextra to/from port 40000 +// 2 distinct sockets where used in the initial protocol +// later firmwares implement a single bidirectional socket // // Alive monitoring is done via a "PING" to remote port 40000. We will get an // ICMP unreachable on terminal mode close or on station shut down if routing is done @@ -122,8 +126,8 @@ protected: // sockets CUdpSocket m_DvOutSocket; CUdpSocket m_PresenceSocket; - CUdpSocket m_ConfigSocket; - int m_IcmpRawSocket; + CUdpMsgSocket m_ConfigSocket; + CRawSocket m_IcmpRawSocket; // optional params uint32 m_GwAddress; diff --git a/src/crawsocket.cpp b/src/crawsocket.cpp new file mode 100644 index 0000000..a30908d --- /dev/null +++ b/src/crawsocket.cpp @@ -0,0 +1,156 @@ +// +// crawsocket.cpp +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 3 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "main.h" +#include +#include "creflector.h" +#include "crawsocket.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CRawSocket::CRawSocket() +{ + m_Socket = -1; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CRawSocket::~CRawSocket() +{ + if ( m_Socket != -1 ) + { + Close(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// open & close + +bool CRawSocket::Open(uint16 uiProto) +{ + bool open = false; + int on = 1; + + // create socket + m_Socket = socket(AF_INET,SOCK_RAW,uiProto); + if ( m_Socket != -1 ) + { + fcntl(m_Socket, F_SETFL, O_NONBLOCK); + open = true; + m_Proto = uiProto; + } + + // done + return open; +} + +void CRawSocket::Close(void) +{ + if ( m_Socket != -1 ) + { + close(m_Socket); + m_Socket = -1; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// read + +int CRawSocket::Receive(CBuffer *Buffer, CIp *Ip, int timeout) +{ + struct sockaddr_in Sin; + fd_set FdSet; + unsigned int uiFromLen = sizeof(struct sockaddr_in); + int iRecvLen = -1; + struct timeval tv; + + // socket valid ? + if ( m_Socket != -1 ) + { + // allocate buffer + Buffer->resize(UDP_BUFFER_LENMAX); + + // control socket + FD_ZERO(&FdSet); + FD_SET(m_Socket, &FdSet); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + select(m_Socket + 1, &FdSet, 0, 0, &tv); + + // read + iRecvLen = (int)recvfrom(m_Socket, + (void *)Buffer->data(), RAW_BUFFER_LENMAX, + 0, (struct sockaddr *)&Sin, &uiFromLen); + + // handle + if ( iRecvLen != -1 ) + { + // adjust buffer size + Buffer->resize(iRecvLen); + + // get IP + Ip->SetSockAddr(&Sin); + } + } + + // done + return iRecvLen; +} + +// protocol specific + +// ICMP + +int CRawSocket::IcmpReceive(CBuffer *Buffer, CIp *Ip, int timeout) +{ + int iIcmpType = -1; + int iRecv; + + if (m_Proto == IPPROTO_ICMP) + { + iRecv = Receive(Buffer, Ip, timeout); + + if (iRecv >= (int)(sizeof(struct ip) + sizeof(struct icmp))) + { + struct ip *iph = (struct ip *)Buffer->data(); + int iphdrlen = iph->ip_hl * 4; + struct icmp *icmph = (struct icmp *)((unsigned char *)iph + iphdrlen); + struct ip *remote_iph = (struct ip *)((unsigned char *)icmph + 8); + + iIcmpType = icmph->icmp_type; + + struct sockaddr_in Sin; + bzero(&Sin, sizeof(Sin)); + Sin.sin_family = AF_INET; + Sin.sin_addr.s_addr = remote_iph->ip_dst.s_addr; + + Ip->SetSockAddr(&Sin); + + } + } + return iIcmpType; +} diff --git a/src/crawsocket.h b/src/crawsocket.h new file mode 100644 index 0000000..1de0f8a --- /dev/null +++ b/src/crawsocket.h @@ -0,0 +1,99 @@ +// +// crawsocket.h +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 3 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +// Description: +// Raw socket access class with protocol specific + + +#ifndef crawsocket_h +#define crawsocket_h + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cip.h" +#include "cbuffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define RAW_BUFFER_LENMAX 65536 + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CRawSocket +{ +public: + // constructor + CRawSocket(); + + // destructor + ~CRawSocket(); + + // open & close + bool Open(uint16); + void Close(void); + int GetSocket(void) { return m_Socket; } + + // read + + // if ETH_P_ALL is used, the received data buffer will hold + // the ethernet header (struct ethhdr) followed by the IP header (struct iphdr), + // the protocol header (e.g tcp, udp, icmp) and the data. + // For specific protocols, the data content may vary depending on the protocol + // Returns the number of received bytes in buffer + + int Receive(CBuffer *, CIp *, int); + + // ICMP receive helper + // parameters: + // buffer - packet receive buffer (starting with ip header) + // ip - remote address (filled in on receive) + // timeout - receive timeout in msec + // return value: + // ICMP type, -1 if nothing was received + + int IcmpReceive(CBuffer *, CIp *, int); + + // write + // no write support - complexity makes it out of scope for now + // to be added if needed + +protected: + // data + int m_Socket; + int m_Proto; + struct sockaddr_in m_SocketAddr; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* crawsocket_h */ diff --git a/src/cudpmsgsocket.cpp b/src/cudpmsgsocket.cpp new file mode 100644 index 0000000..daadd17 --- /dev/null +++ b/src/cudpmsgsocket.cpp @@ -0,0 +1,102 @@ +// +// cudpmsgsocket.cpp +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 3 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "main.h" +#include +#include "cudpmsgsocket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// read + +int CUdpMsgSocket::Receive(CBuffer *Buffer, CIp *Ip, int timeout) +{ + struct sockaddr_in Sin; + fd_set FdSet; + unsigned int uiFromLen = sizeof(struct sockaddr_in); + int iRecvLen = -1; + struct timeval tv; + + struct msghdr Msg; + struct iovec Iov[1]; + + union { + struct cmsghdr cm; + unsigned char pktinfo_sizer[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)]; + } Control; + + // socket valid ? + if ( m_Socket != -1 ) + { + // allocate buffer + Buffer->resize(UDP_MSG_BUFFER_LENMAX); + + //prepare msghdr + bzero(&Msg, sizeof(Msg)); + Iov[0].iov_base = Buffer->data(); + Iov[0].iov_len = UDP_MSG_BUFFER_LENMAX; + + bzero(&Sin, sizeof(Sin)); + Msg.msg_name = &Sin; + Msg.msg_namelen = sizeof(Sin); + Msg.msg_iov = Iov; + Msg.msg_iovlen = 1; + Msg.msg_control = &Control; + Msg.msg_controllen = sizeof(Control); + + // control socket + FD_ZERO(&FdSet); + FD_SET(m_Socket, &FdSet); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + select(m_Socket + 1, &FdSet, 0, 0, &tv); + + // read + iRecvLen = (int)recvmsg(m_Socket, &Msg, 0); + + // handle + if ( iRecvLen != -1 ) + { + // adjust buffer size + Buffer->resize(iRecvLen); + + // get IP + Ip->SetSockAddr(&Sin); + + // get local IP + struct cmsghdr *Cmsg; + for (Cmsg = CMSG_FIRSTHDR(&Msg); Cmsg != NULL; Cmsg = CMSG_NXTHDR(&Msg, Cmsg)) + { + if (Cmsg->cmsg_level == IPPROTO_IP && Cmsg->cmsg_type == IP_PKTINFO) + { + struct in_pktinfo *PktInfo = (struct in_pktinfo *)CMSG_DATA(Cmsg); + m_LocalAddr.s_addr = PktInfo->ipi_spec_dst.s_addr; + } + } + } + } + + // done + return iRecvLen; +} + diff --git a/src/cudpmsgsocket.h b/src/cudpmsgsocket.h new file mode 100644 index 0000000..657ed21 --- /dev/null +++ b/src/cudpmsgsocket.h @@ -0,0 +1,53 @@ +// +// cudpmsgsocket.h +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd 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 3 of the License, or +// (at your option) any later version. +// +// xlxd 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 Foobar. If not, see . +// ---------------------------------------------------------------------------- + +// Description: +// Extension of the CUdpSocket class to allow retrieving +// the local target IP for a G3 Terminal mode config request + +#ifndef cudpmsgsocket_h +#define cudpmsgsocket_h + +#include "cudpsocket.h" + +#define UDP_MSG_BUFFER_LENMAX 1024 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUdpMsgSocket : public CUdpSocket +{ +public: + // read + int Receive(CBuffer *, CIp *, int); + + struct in_addr *GetLocalAddr(void) { return &m_LocalAddr; } + +protected: + // data + struct in_addr m_LocalAddr; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cudpmsgsocket_h */