mirror of
https://github.com/g4klx/ircDDBGateway.git
synced 2025-12-06 05:32:02 +01:00
394 lines
10 KiB
C++
394 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2011,2012,2013,2018 by Jonathan Naylor G4KLX
|
|
*
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "RepeaterHandler.h"
|
|
#include "DDHandler.h"
|
|
#include "Defs.h"
|
|
|
|
#include <wx/filename.h>
|
|
|
|
#if defined(__linux__)
|
|
#include <net/if.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/if_tun.h>
|
|
#include <linux/sockios.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
const unsigned int ETHERNET_ADDRESS_LENGTH = 6U;
|
|
const unsigned int ETHERNET_MTU = 1500U;
|
|
const unsigned int BUFFER_LENGTH = 2000U;
|
|
|
|
const unsigned int MIN_HEARD_TIME_SECS = 120U;
|
|
|
|
const int MINIMUM_DD_FRAME_LENGTH = 60;
|
|
|
|
const unsigned char ETHERNET_BROADCAST_ADDRESS[] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU};
|
|
// Multicast address '01:00:5E:00:00:01' - IP: '224.0.0.1' (send to all)
|
|
const unsigned char TOALL_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x01U};
|
|
// Multicast address '01:00:5E:00:00:23' - IP: '224.0.0.35' (DX-Cluster)
|
|
const unsigned char DX_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x23U};
|
|
|
|
CIRCDDB* CDDHandler::m_irc = NULL;
|
|
CHeaderLogger* CDDHandler::m_headerLogger = NULL;
|
|
int CDDHandler::m_fd = -1;
|
|
unsigned int CDDHandler::m_maxRoutes = 0U;
|
|
CEthernet** CDDHandler::m_list = NULL;
|
|
unsigned char* CDDHandler::m_buffer = NULL;
|
|
bool CDDHandler::m_logEnabled = false;
|
|
wxString CDDHandler::m_logDir = wxEmptyString;
|
|
wxString CDDHandler::m_name = wxEmptyString;
|
|
CTimer CDDHandler::m_timer = CTimer(1000U, MIN_HEARD_TIME_SECS);
|
|
|
|
CEthernet::CEthernet(const unsigned char* address, const wxString& callsign) :
|
|
m_address(NULL),
|
|
m_callsign(callsign)
|
|
{
|
|
wxASSERT(address != NULL);
|
|
wxASSERT(!callsign.IsEmpty());
|
|
|
|
m_address = new unsigned char[ETHERNET_ADDRESS_LENGTH];
|
|
::memcpy(m_address, address, ETHERNET_ADDRESS_LENGTH);
|
|
}
|
|
|
|
CEthernet::~CEthernet()
|
|
{
|
|
delete[] m_address;
|
|
}
|
|
|
|
unsigned char* CEthernet::getAddress() const
|
|
{
|
|
return m_address;
|
|
}
|
|
|
|
wxString CEthernet::getCallsign() const
|
|
{
|
|
return m_callsign;
|
|
}
|
|
|
|
void CDDHandler::initialise(unsigned int maxRoutes, const wxString& name)
|
|
{
|
|
wxASSERT(maxRoutes > 0U);
|
|
|
|
m_maxRoutes = maxRoutes;
|
|
m_name = name;
|
|
|
|
m_buffer = new unsigned char[BUFFER_LENGTH];
|
|
|
|
m_list = new CEthernet*[maxRoutes];
|
|
for (unsigned int i = 0U; i < maxRoutes; i++)
|
|
m_list[i] = NULL;
|
|
|
|
// Add a dummy entry for broadcasts
|
|
m_list[0] = new CEthernet(ETHERNET_BROADCAST_ADDRESS, wxT(" "));
|
|
// Add a dummy entry for "to all" multicast
|
|
m_list[1] = new CEthernet(TOALL_MULTICAST_ADDRESS, wxT("CQCQCQ "));
|
|
// Add a dummy entry for "DX-Cluster" multicast
|
|
m_list[2] = new CEthernet(DX_MULTICAST_ADDRESS, wxT("CQCQCQ "));
|
|
|
|
#if defined(__linux__)
|
|
m_fd = ::open("/dev/net/tun", O_RDWR);
|
|
if (m_fd < 0) {
|
|
wxLogError(wxT("Cannot open /dev/net/tun"));
|
|
return;
|
|
}
|
|
|
|
struct ifreq ifr1;
|
|
::memset(&ifr1, 0x00, sizeof(struct ifreq));
|
|
|
|
ifr1.ifr_flags = IFF_TAP | IFF_NO_PI;
|
|
::strcpy(ifr1.ifr_name, "tap%d");
|
|
|
|
if (::ioctl(m_fd, TUNSETIFF, (void *)&ifr1) < 0) {
|
|
wxLogError(wxT("TUNSETIFF ioctl failed, closing the tap device"));
|
|
::close(m_fd);
|
|
m_fd = -1;
|
|
return;
|
|
}
|
|
|
|
wxString device = wxString(ifr1.ifr_name, wxConvLocal);
|
|
wxLogMessage(wxT("DD mode Tap interface created on %s"), device.c_str());
|
|
|
|
int fd = ::socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (fd < 0) {
|
|
wxLogError(wxT("Unable to open the config socket, closing the tap device"));
|
|
::close(m_fd);
|
|
m_fd = -1;
|
|
return;
|
|
}
|
|
|
|
struct ifreq ifr2;
|
|
::memset(&ifr2, 0x00, sizeof(struct ifreq));
|
|
::strcpy(ifr2.ifr_name, ifr1.ifr_name);
|
|
|
|
ifr2.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST;
|
|
if (::ioctl(fd, SIOCSIFFLAGS, (void *)&ifr2) < 0) {
|
|
wxLogError(wxT("SIOCSIFFLAGS ioctl failed, closing the tap device"));
|
|
::close(m_fd);
|
|
m_fd = -1;
|
|
return;
|
|
}
|
|
|
|
::close(fd);
|
|
#endif
|
|
}
|
|
|
|
void CDDHandler::setLogging(bool enabled, const wxString& dir)
|
|
{
|
|
m_logDir = dir;
|
|
m_logEnabled = enabled;
|
|
}
|
|
|
|
void CDDHandler::setHeaderLogger(CHeaderLogger* logger)
|
|
{
|
|
m_headerLogger = logger;
|
|
}
|
|
|
|
void CDDHandler::setIRC(CIRCDDB* irc)
|
|
{
|
|
wxASSERT(irc != NULL);
|
|
|
|
m_irc = irc;
|
|
}
|
|
|
|
void CDDHandler::process(CDDData& data)
|
|
{
|
|
// If we're not initialised, return immediately
|
|
if (m_maxRoutes == 0U)
|
|
return;
|
|
|
|
unsigned char flag1 = data.getFlag1();
|
|
unsigned char flag2 = data.getFlag2();
|
|
unsigned char flag3 = data.getFlag3();
|
|
wxString myCall1 = data.getMyCall1();
|
|
wxString myCall2 = data.getMyCall2();
|
|
wxString yourCall = data.getYourCall();
|
|
wxString rptCall1 = data.getRptCall1();
|
|
wxString rptCall2 = data.getRptCall2();
|
|
|
|
if (!m_timer.isRunning() || m_timer.hasExpired()) {
|
|
// Write to Header.log if it's enabled
|
|
if (m_headerLogger != NULL)
|
|
m_headerLogger->write(wxT("Repeater"), data);
|
|
|
|
if (m_irc != NULL) {
|
|
m_irc->sendHeardWithTXMsg(myCall1, myCall2, yourCall, rptCall1, rptCall2, flag1, flag2, flag3, wxEmptyString, wxT("Digital Data "));
|
|
m_irc->sendHeardWithTXStats(myCall1, myCall2, yourCall, rptCall1, rptCall2, flag1, flag2, flag3, 1, 0, -1);
|
|
}
|
|
|
|
m_timer.start();
|
|
}
|
|
|
|
// Can we continue?
|
|
if (m_fd < 0)
|
|
return;
|
|
|
|
unsigned char* address = data.getSourceAddress();
|
|
|
|
bool found = false;
|
|
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
|
|
if (m_list[i] != NULL) {
|
|
unsigned char* addr = m_list[i]->getAddress();
|
|
|
|
if (::memcmp(addr, address, ETHERNET_ADDRESS_LENGTH) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
wxLogMessage(wxT("Adding DD user %s with ethernet address %02X:%02X:%02X:%02X:%02X:%02X"), myCall1.c_str(),
|
|
address[0], address[1], address[2], address[3], address[4], address[5]);
|
|
|
|
CEthernet* ethernet = new CEthernet(address, myCall1);
|
|
|
|
found = false;
|
|
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
|
|
if (m_list[i] == NULL) {
|
|
m_list[i] = ethernet;
|
|
found = true;
|
|
if (m_logEnabled)
|
|
writeStatus(*ethernet);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
wxLogError(wxT("No space to add new DD ethernet address"));
|
|
delete ethernet;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
unsigned int length = data.getEthernetFrame(m_buffer, BUFFER_LENGTH);
|
|
|
|
ssize_t len = ::write(m_fd, (char*)m_buffer, length);
|
|
if (len != ssize_t(length))
|
|
wxLogError(wxT("Error returned from write()"));
|
|
#endif
|
|
}
|
|
|
|
CDDData* CDDHandler::read()
|
|
{
|
|
// If we're not initialised, return immediately
|
|
if (m_maxRoutes == 0U)
|
|
return NULL;
|
|
|
|
#if defined(__WINDOWS__)
|
|
return NULL;
|
|
#else
|
|
// Check that the read() won't block
|
|
fd_set readFds;
|
|
FD_ZERO(&readFds);
|
|
#if defined(__WINDOWS__)
|
|
FD_SET((unsigned int)m_fd, &readFds);
|
|
#else
|
|
FD_SET(m_fd, &readFds);
|
|
#endif
|
|
|
|
// Return immediately
|
|
timeval tv;
|
|
tv.tv_sec = 0L;
|
|
tv.tv_usec = 0L;
|
|
|
|
int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv);
|
|
if (ret < 0) {
|
|
wxLogError(wxT("Error returned from select()"));
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(__WINDOWS__)
|
|
if (!FD_ISSET((unsigned int)m_fd, &readFds))
|
|
return NULL;
|
|
#else
|
|
if (!FD_ISSET(m_fd, &readFds))
|
|
return NULL;
|
|
#endif
|
|
|
|
// Ensure that the minimum length is padded with 0x00s
|
|
::memset(m_buffer, 0x00U, MINIMUM_DD_FRAME_LENGTH);
|
|
|
|
ssize_t len = ::read(m_fd, (char*)m_buffer, BUFFER_LENGTH);
|
|
if (len <= 0) {
|
|
wxLogError(wxT("Error returned from read()"));
|
|
return NULL;
|
|
}
|
|
|
|
// There seems to be a minimum size with DD mode, so pad with zeroes if it's not reached
|
|
if (len < MINIMUM_DD_FRAME_LENGTH)
|
|
len = MINIMUM_DD_FRAME_LENGTH;
|
|
|
|
// Point to the destination ethernet address
|
|
unsigned char* address = m_buffer + 0U;
|
|
|
|
// Do destination address to callsign lookup
|
|
CEthernet* ethernet = NULL;
|
|
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
|
|
if (m_list[i] != NULL) {
|
|
unsigned char* addr = m_list[i]->getAddress();
|
|
|
|
if (::memcmp(addr, address, ETHERNET_ADDRESS_LENGTH) == 0) {
|
|
ethernet = m_list[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ethernet == NULL) {
|
|
wxLogWarning(wxT("Cannot find the ethernet address of %02X:%02X:%02X:%02X:%02X:%02X in the ethernet list"), address[0], address[1], address[2], address[3], address[4], address[5]);
|
|
return NULL;
|
|
}
|
|
|
|
CRepeaterHandler* handler = CRepeaterHandler::findDDRepeater();
|
|
if (handler == NULL) {
|
|
wxLogWarning(wxT("Incoming DD data to unknown repeater"));
|
|
return NULL;
|
|
}
|
|
|
|
// wxLogMessage(wxT("Mapping ethernet address %02X:%02X:%02X:%02X:%02X:%02X to user %s"),
|
|
// address[0], address[1], address[2], address[3], address[4], address[5],
|
|
// ethernet->getCallsign().c_str());
|
|
|
|
CDDData* data = new CDDData;
|
|
data->setEthernetFrame(m_buffer, len);
|
|
data->setYourCall(ethernet->getCallsign());
|
|
|
|
handler->process(*data);
|
|
|
|
return data;
|
|
#endif
|
|
}
|
|
|
|
void CDDHandler::clock(unsigned int ms)
|
|
{
|
|
m_timer.clock(ms);
|
|
}
|
|
|
|
void CDDHandler::finalise()
|
|
{
|
|
#if !defined(WIN32)
|
|
if (m_fd >= 0) {
|
|
::close(m_fd);
|
|
m_fd = -1;
|
|
}
|
|
#endif
|
|
|
|
delete[] m_buffer;
|
|
|
|
for (unsigned int i = 0U; i < m_maxRoutes; i++)
|
|
delete m_list[i];
|
|
delete[] m_list;
|
|
}
|
|
|
|
void CDDHandler::writeStatus(const CEthernet& ethernet)
|
|
{
|
|
wxString fullName = DDMODE_BASE_NAME;
|
|
|
|
if (!m_name.IsEmpty()) {
|
|
fullName.Append(wxT("_"));
|
|
fullName.Append(m_name);
|
|
}
|
|
|
|
wxFileName fileName(m_logDir, fullName, wxT("log"));
|
|
|
|
wxFFile file;
|
|
bool ret = file.Open(fileName.GetFullPath(), wxT("at"));
|
|
if (!ret) {
|
|
wxLogError(wxT("Unable to open %s for writing"), fileName.GetFullPath().c_str());
|
|
return;
|
|
}
|
|
|
|
wxString callsign = ethernet.getCallsign();
|
|
unsigned char* address = ethernet.getAddress();
|
|
|
|
time_t timeNow = ::time(NULL);
|
|
struct tm* tm = ::gmtime(&timeNow);
|
|
|
|
wxString text;
|
|
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %02X:%02X:%02X:%02X:%02X:%02X %s\n"),
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
|
|
address[0], address[1], address[2], address[3], address[4], address[5], callsign.c_str());
|
|
|
|
file.Write(text);
|
|
|
|
file.Close();
|
|
}
|