linbpq/IPCode.c

5460 lines
114 KiB
C

/*
Copyright 2001-2022 John Wiseman G8BPQ
This file is part of LinBPQ/BPQ32.
LinBPQ/BPQ32 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.
LinBPQ/BPQ32 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 LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
*/
// Module to provide a basic Gateway between IP over AX.25 and the Internet.
// Uses WinPcap on Windows, TAP Driver on Linux
// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet.
// ARP frames are also reformatted, and monitored to build a simple routing table.
// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one),
// so the default route must be configured.
// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router.
// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router.
// The ax.25 applications will appear as single addresses on the Ethernet LAN
// The code can also switch packets between ax.25 interfaces
// First Version, July 2008
// Version 1.2.1 January 2009
// Add IP Address Mapping option
// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder
// so packets can be routed from RF to/from encapsulated 44 net subnets.
// Routes may also be learned from received RF packets, or added from config file
/*
TODo ?Multiple Adapters
*/
/*
Windows uses PCAP to send to both the local host (the machine running BPQ) and
to other machines on the same LAN. I may be able to add the 44/8 route but
dont at the moment.
On Linux, the local machine doesn't see packets sent via pcap, so it uses a TAP
device for the local host, and pcap for other addresses on the LAN. The TAP is
created dynamically - it doesn't have to be predefined. A route to 44/8 via the
TAP and an ARP entry for it are also added. The TAP runs unnumbered
44 addresses can be NAT'ed to the local LAN address, so hosts don't have to have
both an ISP and a 44 address. You can run your local LAN as 44, but I would expect
most uses to prefer to keep their LAN with its normal (usually 192.168) addresses.
If the PC address isn't the same as the IPGateway IPAddr a NAT entry is created
automaticaly.
In these cases the NAT line for jnos should have TAP appended to tell
LinBPQ it is reached over the TAP.
NAT 44.131.11.x 192.168.x.y TAP
*/
//int _winver = 0x0600;
#pragma data_seg("_BPQDATA")
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <time.h>
#include "CHeaders.h"
#include "ipcode.h"
#ifdef WIN32
#include <io.h>
#define read _read
#define write _write
#define close _close
#include <iphlpapi.h>
// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
#endif
#include <pcap.h>
#ifdef WIN32
int pcap_sendpacket(pcap_t *p, u_char *buf, int size);
#else
PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int);
#endif
#ifndef LINBPQ
#include "kernelresource.h"
LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#endif
//#define s_addr S_un.S_addr
extern BPQVECSTRUC * IPHOSTVECTORPTR;
BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port);
VOID SENDSABM(struct _LINKTABLE * LINK);
BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK);
BOOL ProcessConfig();
VOID RemoveARP(PARPDATA Arp);
VOID AddToRoutes(PARPDATA Arp, UINT IPAddr, char Type);
VOID ProcessTunnelMsg(PIPMSG IPptr);
VOID ProcessRIP44Message(PIPMSG IPptr);
PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found);
BOOL ProcessROUTELine(char * buf, BOOL Locked);
VOID DoRouteTimer();
PROUTEENTRY FindRoute(uint32_t IPADDR);
VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap);
USHORT Generate_CHECKSUM(VOID * ptr1, int Len);
VOID RecalcTCPChecksum(PIPMSG IPptr);
VOID RecalcUDPChecksum(PIPMSG IPptr);
BOOL Send_ETH(VOID * Block, DWORD len, BOOL SendToTAP);
VOID ProcessEthARPMsg(PETHARP arpptr, BOOL FromTAP);
VOID WriteIPRLine(PROUTEENTRY RouteRecord, FILE * file);
int CountBits(uint32_t in);
VOID SendARPMsg(PARPDATA ARPptr, BOOL ToTAP);;
BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls);
int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF);
#define ARPTIMEOUT 3600
// ARP REQUEST (AX.25)
AXARP AXARPREQMSG = {0};
// ARP REQUEST/REPLY (Eth)
ETHARP ETHARPREQMSG = {0};
ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed
int NumberofARPEntries = 0;
ROUTEENTRY ** RouteRecords = NULL;
int NumberofRoutes = 0;
time_t LastRIP44Msg = 0;
//HANDLE hBPQNET = INVALID_HANDLE_VALUE;
uint32_t OurIPAddr = 0;
char IPAddrText[20]; // Text form of Our Address
uint32_t EncapAddr = INADDR_NONE; // Virtual Host for IPIP PCAP Mode.
char EncapAddrText[20]; // Text form of Our Address
UCHAR RouterMac[6] = {0}; // Mac Address of our Internet Gateway.
//uint32_t HostIPAddr = INADDR_NONE; // Makes more sense to use same addr for host
uint32_t HostNATAddr = INADDR_NONE; // LAN address (not 44 net) of our host
char HostNATAddrText[20];
uint32_t OurNetMask = 0xffffffff;
BOOL WantTAP = FALSE;
BOOL WantEncap = 0; // Run RIP44 and Net44 Encap
BOOL NoDefaultRoute = FALSE; // Don't add route to 44/8
int WantUDPTunnel = 0;
SOCKET EncapSock = 0;
SOCKET UDPSendSock = 0;
BOOL UDPEncap = FALSE;
BOOL IPv6 = FALSE;
int UDPPort = 4473; // RX Port, Send on +1
BOOL BPQSNMP = FALSE; // If set process SNMP in BPQ, else pass to host
int IPTTL = 128;
int tap_fd = 0;
int FramesForwarded = 0;
int FramesDropped = 0;
int ARPTimeouts = 0;
int SecTimer = 10;
int baseline=0;
unsigned char hostaddr[64];
static int nat_table_len = 0;
static struct nat_table_entry nat_table[MAX_ENTRIES];
uint32_t UCSD44 = 0; // RIP SOurce - Normally 44.0.0.1
// Following two fields used by stats to get round shared memmory problem
ARPDATA Arp={0};
int ARPFlag = -1;
// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer,
// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25
// Enet Header ia 14 bytes, AX.25 UI is 16
// Also used to reassemble NOS Fragmented ax.25 packets
static UCHAR Buffer[4096] = {0};
#define EthOffset 30 // Should be plenty
DWORD IPLen = 0;
UCHAR QST[7]={'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; //QST IN AX25
#ifdef WIN32
UCHAR ourMACAddr[6] = {02,'B','P','Q',2,2};
#else
UCHAR ourMACAddr[6] = {02,'B','P','Q',1,1};
#endif
UCHAR RealMacAddress[6];
uint64_t IPPortMask = 0;
IPSTATS IPStats = {0};
UCHAR BPQDirectory[260];
char ARPFN[MAX_PATH];
char IPRFN[MAX_PATH];
HANDLE handle;
//#ifdef WIN32
pcap_t *adhandle = 0;
pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *);
int pcap_reopen_delay;
//#endif
static char Adapter[256];
int Promiscuous = 1; // Default to Promiscuous
#ifdef WIN32
HINSTANCE PcapDriver=0;
typedef void * (FAR *FARPROCX)();
typedef int (FAR *FARPROCI)(); // Routines that return int
int (FAR * pcap_sendpacketx)();
FARPROCI pcap_findalldevsx;
FARPROCX pcap_compilex;
FARPROCX pcap_setfilterx;
FARPROCI pcap_datalinkx;
FARPROCI pcap_next_exx;
FARPROCX pcap_geterrx;
FARPROCX pcap_closex;
FARPROCX pcap_setnonblockx;
char Dllname[6]="wpcap";
FARPROCX GetAddress(char * Proc);
FARPROCI GetAddressI(char * Proc);
#else
#define pcap_findalldevsx pcap_findalldevs
#define pcap_compilex pcap_compile
#define pcap_open_livex pcap_open_live
#define pcap_setfilterx pcap_setfilter
#define pcap_datalinkx pcap_datalink
#define pcap_next_exx pcap_next_ex
#define pcap_geterrx pcap_geterr
#define pcap_sendpacketx pcap_sendpacket
#define pcap_closex pcap_close
#define pcap_setnonblockx pcap_setnonblock
#endif
VOID __cdecl Debugprintf(const char * format, ...);
// Out Address Space (now only have lower 3/4 of 44 net)
DWORD Lower44; // 44.0 to 44.127
DWORD Upper44 ; // 44.128 to 44.191
DWORD Lower44Mask;
DWORD Upper44Mask;
#ifdef WIN32
// Routine to check if a route to 44.0.0.0/9 exists and points to us
BOOL Check44Route(int Interface)
{
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
PMIB_IPFORWARDROW Row;
int Size = 0;
DWORD n;
int gotLower = 0;
int gotUpper = 0;
// First call gets the required size
n = GetIpForwardTable(pIpForwardTable, &Size, FALSE);
pIpForwardTable = malloc(Size);
n = GetIpForwardTable(pIpForwardTable, &Size, FALSE);
if (n)
return FALSE; // Couldnt read table
Row = pIpForwardTable->table;
for (n = 0; n < pIpForwardTable->dwNumEntries; n++)
{
if (Row->dwForwardIfIndex == Interface)
{
if (Row->dwForwardDest == Lower44 && Row->dwForwardMask == Lower44Mask)
gotLower = 1;
if (Row->dwForwardDest == Upper44 && Row->dwForwardMask == Upper44Mask)
gotUpper = 1;
}
Row++;
}
free(pIpForwardTable);
if (gotLower & gotUpper)
return TRUE;
return FALSE;
}
BOOL Setup44Route(int Interface, char * Gateway)
{
// Better just to call route.exe, so we can set -p flag and use runas
char Params[256];
// delete old 44/8 route
sprintf(Params, " -delete 44.0.0.0/8");
ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL);
sprintf(Params, " -p add 44.0.0.0 mask 255.128.0.0 %s if %d", Gateway, Interface);
ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL);
sprintf(Params, " -p add 44.128.0.0 mask 255.192.0.0 %s if %d", Gateway, Interface);
ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL);
return 1;
}
#endif
char FormatIPWork[20];
char * FormatIP(uint32_t Addr)
{
unsigned char work[4];
memcpy(work, &Addr, 4);
sprintf(FormatIPWork, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
return FormatIPWork;
}
int CompareRoutes (const VOID * a, const VOID * b)
{
PROUTEENTRY x;
PROUTEENTRY y;
unsigned long r1, r2;
x = * (PROUTEENTRY const *) a;
y = * (PROUTEENTRY const *) b;
r1 = x->NETWORK;
r2 = y->NETWORK;
r1 = htonl(r1);
r2 = htonl(r2);
if (r1 < r2 ) return -1;
if (r1 == r2 ) return 0;
return 1;
}
int CompareMasks (const VOID * a, const VOID * b)
{
PROUTEENTRY x;
PROUTEENTRY y;
uint32_t m1, m2;
uint32_t r1, r2;
x = * (PROUTEENTRY const *) a;
y = * (PROUTEENTRY const *) b;
r1 = x->NETWORK;
r2 = y->NETWORK;
m1 = x->SUBNET;
m2 = y->SUBNET;
m1 = htonl(m1);
m2 = htonl(m2);
r1 = htonl(r1);
r2 = htonl(r2);
if (m1 > m2) return -1;
if (m1 == m2)
{
if (r1 < r2) return -1;
if (r1 == r2 ) return 0;
}
return 1;
}
void OpenTAP();
BOOL GetPCAP()
{
#ifdef WIN32
PcapDriver=LoadLibrary(Dllname);
if (PcapDriver == NULL) return(FALSE);
if ((pcap_findalldevsx=GetAddressI("pcap_findalldevs")) == 0 ) return FALSE;
if ((pcap_sendpacketx = GetAddressI("pcap_sendpacket")) == 0 ) return FALSE;
if ((pcap_datalinkx=GetAddressI("pcap_datalink")) == 0 ) return FALSE;
if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE;
if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE;
pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live");
if (pcap_open_livex == NULL) return FALSE;
if ((pcap_geterrx = GetAddress("pcap_geterr")) == 0 ) return FALSE;
if ((pcap_next_exx = GetAddressI("pcap_next_ex")) == 0 ) return FALSE;
if ((pcap_closex = GetAddress("pcap_close")) == 0 ) return FALSE;
if ((pcap_setnonblockx = GetAddress("pcap_setnonblock")) == 0 ) return FALSE;
#endif
return TRUE;
}
Dll BOOL APIENTRY Init_IP()
{
int ret;
if (BPQDirectory[0] == 0)
{
strcpy(ARPFN,"BPQARP.dat");
}
else
{
strcpy(ARPFN,BPQDirectory);
strcat(ARPFN,"/");
strcat(ARPFN,"BPQARP.dat");
}
if (BPQDirectory[0] == 0)
{
strcpy(IPRFN,"BPQIPR.dat");
}
else
{
strcpy(IPRFN,BPQDirectory);
strcat(IPRFN,"/");
strcat(IPRFN,"BPQIPR.dat");
}
Lower44 = inet_addr("44.0.0.0");
Upper44 = inet_addr("44.128.0.0"); // Loer Half
Lower44Mask = inet_addr("255.128.0.0"); // 44.128 to 44.192
Upper44Mask = inet_addr("255.192.0.0");
// Clear fields in case of restart
ARPRecords = NULL; // ARP Table - malloc'ed as needed
NumberofARPEntries=0;
RouteRecords = NULL;
NumberofRoutes = 0;
nat_table_len = 0;
ReadConfigFile();
ourMACAddr[5] = (UCHAR)(OurIPAddr >> 24) & 255;
// Clear old packets
IPHOSTVECTORPTR->HOSTAPPLFLAGS = 0x80; // Request IP frames from Node
// Set up static fields in ARP messages
AXARPREQMSG.HWTYPE=0x0300; // AX25
memcpy(AXARPREQMSG.MSGHDDR.DEST, QST, 7);
memcpy(AXARPREQMSG.MSGHDDR.ORIGIN, MYCALL, 7);
AXARPREQMSG.MSGHDDR.ORIGIN[6] |= 1; // Set End of Call
AXARPREQMSG.MSGHDDR.PID = 0xcd; // ARP
AXARPREQMSG.MSGHDDR.CTL = 03; // UI
AXARPREQMSG.PID=0xcc00; // TYPE
AXARPREQMSG.HWTYPE=0x0300;
AXARPREQMSG.HWADDRLEN = 7;
AXARPREQMSG.IPADDRLEN = 4;
memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7);
AXARPREQMSG.SENDIPADDR = OurIPAddr;
memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6);
memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6);
ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP
ETHARPREQMSG.HWTYPE=0x0100; // Eth
ETHARPREQMSG.PID=0x0008;
ETHARPREQMSG.HWADDRLEN = 6;
ETHARPREQMSG.IPADDRLEN = 4;
//#ifdef WIN32
if (Adapter[0] == 0)
return FALSE;
if (GetPCAP() == FALSE)
return FALSE;
// on Windows create a NAT entry for IPADDR.
// on linux enable the TAP device (on Linux you can't use pcap to talk to
// the local host, whereas on Windows you can.
#ifndef MACBPQ
#ifndef FREEBSD
{
pcap_if_t * ifs = NULL, * saveifs;
char Line[80];
char ErrBuf[256];
// Find IP Addr of Adapter Interface
pcap_findalldevsx(&ifs, ErrBuf);
saveifs = ifs; // Save for release
while (ifs)
{
if (strcmp(ifs->name, Adapter) == 0)
break;
ifs = ifs->next;
}
if (ifs)
{
struct pcap_addr *address;
address = ifs->addresses;
while (address)
{
if (address->addr->sa_family == 2)
break;
address = address->next;
}
if (address)
{
memcpy(&HostNATAddr, &address->addr->sa_data[2], 4);
sprintf(HostNATAddrText, "%d.%d.%d.%d", (UCHAR)address->addr->sa_data[2],
(UCHAR)address->addr->sa_data[3],
(UCHAR)address->addr->sa_data[4],
(UCHAR)address->addr->sa_data[5]);
}
// We need to create a NAT entry.
// For now do for both Windows and Linux
sprintf(Line, "NAT %s %s", IPAddrText, HostNATAddrText);
Debugprintf("Generated NAT %s\n", Line);
ProcessLine(Line);
#ifdef WIN32
#else
// Linux, need TAP
WantTAP = TRUE;
#endif
}
}
#endif
#endif
//
// Open PCAP Driver
if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch
{
char buf[80];
if (OpenPCAP())
sprintf(buf,"IP Using %s\n", Adapter);
else
sprintf(buf," IP Unable to open %s\n", Adapter);
WritetoConsoleLocal(buf);
if (adhandle == NULL)
{
WritetoConsoleLocal("Failed to open pcap device - IP Support Disabled\n");
return FALSE;
}
}
//#else
// Linux - if TAP requested, open it
#ifndef WIN32
#ifndef MACBPQ
#ifndef FREEBSD
if (WantTAP)
OpenTAP();
#endif
#endif
#endif
ReadARP();
ReadIPRoutes();
// if we are running as Net44 encap, open a socket for IPIP
if (WantUDPTunnel)
{
struct sockaddr_in sinx;
u_long param = 1;
UDPSendSock = socket(AF_INET,SOCK_DGRAM,0);
ioctl (UDPSendSock,FIONBIO,&param);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = htons(WantUDPTunnel);
ret = bind(UDPSendSock, (struct sockaddr *) &sinx, sizeof(sinx));
}
if (WantEncap || WantUDPTunnel)
{
union
{
struct sockaddr_in sinx;
struct sockaddr_in6 sinx6;
} sinx = {0};
u_long param = 1;
int err, ret;
char Msg[80];
if (UCSD44 == 0)
UCSD44 = inet_addr("44.0.0.1");
#ifdef WIN32
// Find Interface number for PCAP Device
{
UINT ulOutBufLen;
PIP_ADAPTER_INFO pAdapterInfo;
PIP_ADAPTER_INFO pAdapter = NULL;
DWORD dwRetVal = 0;
int Interface = 0;
// Make an initial call to GetAdaptersInfo to get
// the necessary size into the ulOutBufLen variable
GetAdaptersInfo(NULL, &ulOutBufLen);
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
{
pAdapter = pAdapterInfo;
while (pAdapter)
{
if (strstr(Adapter, pAdapter->AdapterName))
{
Interface = pAdapter->Index;
break;
}
pAdapter = pAdapter->Next;
}
free(pAdapterInfo);
}
if (NoDefaultRoute == FALSE)
{
// Check Route to 44 and if not there add
if (Check44Route(Interface))
WritetoConsoleLocal("Route to 44/9 and 44.128/10 found\n");
else
{
#ifndef _winver
#define _winver 0x0600
#pragma warning(push)
#pragma warning(disable : 4996)
if (_winver >= 0x0600)
#pragma warning(pop)
Setup44Route(Interface, "0.0.0.0");
else
Setup44Route(Interface, EncapAddrText);
#else
Setup44Route(Interface, "0.0.0.0");
#endif
Sleep(2000);
if (Check44Route(Interface))
WritetoConsoleLocal("Route to44/9 and 44.128/10 added\n");
else
WritetoConsoleLocal("Adding route to 44/9 and 44.128/10 Failed\n");
}
}
}
#endif
if (WantEncap)
{
if (EncapAddr != INADDR_NONE)
{
// Using Virtual Host on PCAP Adapter (Windows)
WritetoConsoleLocal("Net44 Tunnel opened on PCAP device\n");
WritetoConsoleLocal("IP Support Enabled\n");
return TRUE;
}
if (UDPEncap)
{
// Open UDP Socket
if (IPv6)
EncapSock = socket(AF_INET6,SOCK_DGRAM,0);
else
EncapSock = socket(AF_INET,SOCK_DGRAM,0);
sinx.sinx.sin_port = htons(UDPPort);
}
else
{
// Open Raw Socket
EncapSock = socket(AF_INET, SOCK_RAW, 4);
sinx.sinx.sin_port = 0;
}
if (EncapSock == INVALID_SOCKET)
{
err = WSAGetLastError();
sprintf(Msg, "Failed to create socket for IPIP Encap - error code = %d\n", err);
WritetoConsoleLocal(Msg);
}
else
{
ioctl (EncapSock,FIONBIO,&param);
if (IPv6)
{
sinx.sinx.sin_family = AF_INET6;
memset (&sinx.sinx6.sin6_addr, 0, 16);
ret = bind(EncapSock, (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx6));
}
else
{
sinx.sinx.sin_family = AF_INET;
sinx.sinx.sin_addr.s_addr = INADDR_ANY;
ret = bind(EncapSock, (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx));
}
if (ret)
{
// Bind Failed
err = WSAGetLastError();
sprintf(Msg, "Bind Failed for IPIP Encap socket - error code = %d\n", err);
WritetoConsoleLocal(Msg);
}
else
{
WritetoConsoleLocal("Net44 Tunnel opened\n");
}
}
}
}
WritetoConsoleLocal("IP Support Enabled\n");
return TRUE;
}
VOID IPClose()
{
SaveIPRoutes();
if (adhandle)
pcap_closex(adhandle);
#ifdef LINBPQ
if (WantTAP && tap_fd)
close(tap_fd);
#endif
if (EncapSock)
closesocket(EncapSock);
}
union
{
struct sockaddr_in rxaddr;
struct sockaddr_in6 rxaddr6;
} RXaddr;
Dll BOOL APIENTRY Poll_IP()
{
int res;
struct pcap_pkthdr *header;
const u_char *pkt_data;
// Entered every 100 mS
// if ARPFlag set, copy requested ARP record (For BPQStatus GUI)
if (ARPFlag != -1)
{
memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA));
ARPFlag = -1;
}
SecTimer--;
if (SecTimer == 0)
{
SecTimer = 10;
DoARPTimer();
DoRouteTimer();
}
Pollloop:
//#ifdef WIN32
if (adhandle)
{
res = (int)pcap_next_exx(adhandle, &header, &pkt_data);
if (res > 0)
{
PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset];
if (header->len > 1514)
{
// Debugprintf("Ether Packet Len = %d", header->len);
goto Pollloop;
}
memcpy(&Buffer[EthOffset],pkt_data, header->len);
if (ethptr->ETYPE == 0x0008)
{
ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]);
// PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14];
// ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255);
goto Pollloop;
}
if (ethptr->ETYPE == 0x0608)
{
ProcessEthARPMsg((PETHARP)ethptr, FALSE);
goto Pollloop;
}
// Ignore anything else
goto Pollloop;
}
else
{
if (res < 0)
{
char * error = (char *)pcap_geterrx(adhandle) ;
Debugprintf(error);
if (OpenPCAP() == FALSE)
pcap_reopen_delay = 300;
}
}
}
else
{
// No handle.
if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch
{
// Try reopening periodically
pcap_reopen_delay --;
if (pcap_reopen_delay < 0)
if (OpenPCAP() == FALSE)
pcap_reopen_delay = 300; // Retry every 30 seconds
}
}
//#endif
#ifdef LINBPQ
PollTAPloop:
if (WantTAP && tap_fd)
{
int nread;
nread = read(tap_fd, &Buffer[EthOffset], 1600);
if (nread > 0)
{
PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset];
if (ethptr->ETYPE == 0x0008)
{
ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]);
goto PollTAPloop;
}
if (ethptr->ETYPE == 0x0608)
{
ProcessEthARPMsg((PETHARP)ethptr, TRUE);
goto PollTAPloop;
}
// if 08FF pass to BPQETHER Driver
/*
if (ethptr->ETYPE == 0xFF08)
{
PBUFFHEADER axmsg;
PBUFFHEADER savemsg;
int len;
// BPQEther Encap
len = Buffer[EthOffset + 15]*256 + Buffer[EthOffset + 14];
axmsg = (PBUFFHEADER)&Buffer[EthOffset + 9];
axmsg->LENGTH = len;
axmsg->PORT = 99; // Dummy for IP Gate
printf("BPQ Eth Len %d PID %d\n", len, axmsg->PID);
if ((len < 16) || (len > 320))
goto PollTAPloop; // Probably RLI Mode Frame
//len-=3;
//memcpy(&buff[7],&pkt_data[16],len);
// len+=5;
savemsg=axmsg;
// Packet from AX.25
if (CompareCalls(axmsg->ORIGIN, MYCALL))
return 0; // Echoed packet
switch (axmsg->PID)
{
case 0xcc:
// IP Message
{
PIPMSG ipptr = (PIPMSG)++axmsg;
axmsg--;
ProcessIPMsg(ipptr, axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT);
break;
}
case 0xcd:
// ARP Message
ProcessAXARPMsg((PAXARP)axmsg);
SaveARP();
break;
// case 0x08:
}
goto PollTAPloop;
}
*/
// Ignore anything else
// printf("TAP ype %X\n", ntohs(ethptr->ETYPE));
goto PollTAPloop;
}
}
#endif
PollEncaploop:
if (EncapSock)
{
int nread;
int addrlen = sizeof(struct sockaddr_in6);
nread = recvfrom(EncapSock, &Buffer[EthOffset], 1600, 0, (struct sockaddr *)&RXaddr.rxaddr,&addrlen);
if (nread > 0)
{
PIPMSG IPptr = (PIPMSG)&Buffer[EthOffset];
if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet
{
ProcessTunnelMsg(IPptr);
}
goto PollEncaploop;
}
else
res = GetLastError();
}
if (UDPSendSock)
{
int nread;
int addrlen = sizeof(struct sockaddr_in6);
nread = recvfrom(UDPSendSock, &Buffer[EthOffset], 1600, 0, (struct sockaddr *)&RXaddr.rxaddr,&addrlen);
if (nread > 0)
{
PIPMSG IPptr = (PIPMSG)&Buffer[EthOffset];
// RouteIPMsg(IPptr);
ProcessIPMsg(IPptr, Buffer, 'E', 255);
goto PollEncaploop;
}
else
res = GetLastError();
}
if (IPHOSTVECTORPTR->HOSTTRACEQ != 0)
{
PBUFFHEADER axmsg;
PBUFFHEADER savemsg;
axmsg = (PBUFFHEADER)IPHOSTVECTORPTR->HOSTTRACEQ;
IPHOSTVECTORPTR->HOSTTRACEQ = axmsg->CHAIN;
savemsg=axmsg;
// Packet from AX.25
if (axmsg->DEST[0] == 0xCF) // Netrom
{
// Could we use the More bit for fragmentation??
// Seems a good idea, but would'nt be compatible with NOS
// Have to move message down buffer
UCHAR TEMP[7];
memmove(TEMP, &axmsg->DEST[1], 7);
memmove(axmsg->DEST, &axmsg->ORIGIN[1], 7);
memmove(axmsg->ORIGIN, TEMP, 7);
axmsg->PID = 0xCC;
memmove(&axmsg->PID + 1, &axmsg->PID + 6, axmsg->LENGTH);
axmsg->PORT = 0;
}
if (CompareCalls(axmsg->ORIGIN, MYCALL))
{
ReleaseBuffer(axmsg);
return 0; // Echoed packet
}
switch (axmsg->PID)
{
case 0xcc:
// IP Message,
{
PIPMSG ipptr = (PIPMSG)++axmsg;
axmsg--;
ProcessIPMsg(ipptr, axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT);
break;
}
case 0xcd:
// ARP Message
ProcessAXARPMsg((PAXARP)axmsg);
SaveARP();
break;
case 0x08:
// Fragmented message
// The L2 code ensures that the last fragment is present before passing the
// message up to us. It is just possible that bits are missing
{
UCHAR * ptr = &axmsg->PID;
UCHAR * nextfrag;
int frags;
int len;
ptr++;
if (!(*ptr & 0x80))
break; // Not first fragment???
frags=*ptr++ & 0x7f;
len = axmsg->LENGTH;
len-= sizeof(BUFFHEADER);
len--; // Remove Frag Control Byte
memcpy(&Buffer[EthOffset], ptr, len);
nextfrag = &Buffer[EthOffset]+len;
// Release Buffer
fragloop:
ReleaseBuffer(savemsg);
if (IPHOSTVECTORPTR->HOSTTRACEQ == 0) goto Pollloop; // Shouldn't happen
axmsg = (PBUFFHEADER)IPHOSTVECTORPTR->HOSTTRACEQ;
IPHOSTVECTORPTR->HOSTTRACEQ = axmsg->CHAIN;
savemsg=axmsg;
ptr = &axmsg->PID;
ptr++;
if (--frags != (*ptr++ & 0x7f))
break; // Out of sequence
len = axmsg->LENGTH;
len-= sizeof(BUFFHEADER);
len--; // Remove Frag Control Byte
memcpy(nextfrag, ptr, len);
nextfrag+=len;
if (frags != 0) goto fragloop;
ProcessIPMsg((PIPMSG)&Buffer[EthOffset+1],axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT);
break;
}
}
// Release the buffer
ReleaseBuffer(savemsg);
goto Pollloop;
}
return TRUE;
}
BOOL Send_ETH(VOID * Block, DWORD len, BOOL SendtoTAP)
{
// On Windows we don't use TAP so everything goes to pcap
if (len < 60) len = 60;
#ifndef WIN32
if (SendtoTAP)
{
if (tap_fd)
write(tap_fd, Block, len);
return TRUE;
}
#endif
// On Windows we don't use TAP so everything goes to pcap
if (adhandle)
{
// Send down the packet
pcap_sendpacketx(adhandle, // Adapter
Block, // buffer with the packet
len); // size
}
return TRUE;
}
#define AX25_P_SEGMENT 0x08
#define SEG_REM 0x7F
#define SEG_FIRST 0x80
static VOID Send_AX_Datagram(PMESSAGE Block, DWORD Len, UCHAR Port, UCHAR * HWADDR)
{
// Can't use API SENDRAW, as that tries to get the semaphore, which we already have
// Block includes the Msg Header (7 bytes), Len Does not!
memcpy(Block->DEST, HWADDR, 7);
memcpy(Block->ORIGIN, MYCALL, 7);
Block->DEST[6] &= 0x7e; // Clear End of Call
Block->DEST[6] |= 0x80; // set Command Bit
Block->ORIGIN[6] |= 1; // Set End of Call
Block->CTL = 3; //UI
#ifdef LINBPQ
if (Port == 99) // BPQETHER over BPQTUN Port
{
// Add BPQETHER Header
int txlen = Block->LENGTH;
UCHAR txbuff[1600];
if (txlen < 1 || txlen > 400)
return;
// Length field is little-endian
// BPQEther Header is 14 bytes before the Length
txbuff[14]=(txlen & 0xff);
txbuff[15]=(txlen >> 8);
memcpy(&txbuff[16],&Block[7],txlen);
//memcpy(&txbuff[0],&EthDest[0],6);
//memcpy(&txbuff[6],&EthSource[0],6);
//memcpy(&txbuff[12],&EtherType,2);
write(tap_fd, Block, Len);
return;
}
#endif
Send_AX(Block, Len, Port);
return;
}
VOID Send_AX_Connected(VOID * Block, DWORD Len, UCHAR Port, UCHAR * HWADDR)
{
DWORD PACLEN = 256;
int first = 1, fragno, txlen;
UCHAR * p;
// Len includes the 16 byte ax header (Addr CTL PID)
// if Len - 16 is greater than PACLEN, then fragment (only possible on Virtual Circuits,
// as fragmentation relies upon reliable delivery
if ((Len - 16) <= PACLEN) // No need to fragment
{
SendNetFrame(HWADDR, MYCALL, Block, Len, Port);
return;
}
Len = Len-16; // Back to real length
PACLEN-=2; // Allow for fragment control info)
fragno = Len / PACLEN;
if (Len % PACLEN == 0) fragno--;
p = Block;
p += 20;
while (Len > 0)
{
*p++ = AX25_P_SEGMENT;
*p = fragno--;
if (first)
*p |= SEG_FIRST;
txlen = (PACLEN > Len) ? Len : PACLEN;
Debugprintf("Send IP to VC Fragment, Len = %d", txlen);
// Sobsequent fragments only add one byte (the PID is left in place)
if (first)
SendNetFrame(HWADDR, MYCALL, p-23, txlen+18, Port);
else
{
SendNetFrame(HWADDR, MYCALL, p-23, txlen+17, Port); // only one frag byte
p--;
}
first = 0;
p += (txlen);
Len -= txlen;
}
return;
}
static VOID SendNetFrame(UCHAR * ToCall, UCHAR * FromCall, UCHAR * Block, DWORD Len, UCHAR Port)
{
// ATTACH FRAME TO OUTBOUND L3 QUEUES (ONLY USED FOR IP ROUTER)
struct DATAMESSAGE * buffptr;
struct _LINKTABLE * LINK;
struct PORTCONTROL * PORT;
if (Len > MAXDATA)
return;
if (QCOUNT < 100)
return;
memcpy(&Block[7],ToCall, 7);
memcpy(&Block[14],FromCall, 7);
buffptr = GetBuff();
if (buffptr == 0)
return; // No buffers
Len -= 15; // We added 16 before (for UI Header) but L2 send includes the PID, so is one more than datalength
buffptr->LENGTH = (USHORT)Len + MSGHDDRLEN;
// SEE IF L2 OR L3
if (Port == 0) // L3
{
struct DEST_LIST * DEST;
L3MESSAGE * L3Header;
if (FindDestination(ToCall, &DEST) == 0)
{
ReleaseBuffer(buffptr);
return;
}
// We have to build the Netrom Header
L3Header = (L3MESSAGE *)&buffptr->L2DATA[0];
buffptr->PID = 0xCF; // NETROM
memcpy(L3Header->L3SRCE, FromCall, 7);
memcpy(L3Header->L3DEST, ToCall, 7);
L3Header->L3TTL = L3LIVES;
L3Header->L4FLAGS = 0; // Opcode
L3Header->L4ID = 0x0C; // IP
L3Header->L4INDEX = 0x0C;
memcpy(L3Header->L4DATA, &Block[23], Len); // Dond Send PID - implied by OCOC above
buffptr->LENGTH += 20; // Netrom Header
C_Q_ADD(&DEST->DEST_Q, buffptr);
return;
}
// SEND FRAME TO L2 DEST, CREATING A LINK ENTRY IF NECESSARY
memcpy(&buffptr->PID, &Block[22], Len);
if (FindLink(ToCall, FromCall, Port, &LINK))
{
// Have a link
C_Q_ADD(&LINK->TX_Q, buffptr);
return;
}
if (LINK == NULL) // No spare space
{
ReleaseBuffer(buffptr);
return;
}
LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(Port);
if (PORT == NULL)
return; // maybe port has been deleted
LINK->L2TIME = PORT->PORTT1; // SET TIMER VALUE
if (ToCall[7])
LINK->L2TIME += PORT->PORTT1; // Extend Timer for Digis
LINK->LINKWINDOW = PORT->PORTWINDOW;
LINK->L2STATE = 2;
memcpy(LINK->LINKCALL, ToCall, 7);
memcpy(LINK->OURCALL, FromCall, 7);
memcpy(LINK->DIGIS, &ToCall[7], 56);
LINK->LINKTYPE = 2; // Dopwnlink
SENDSABM(LINK);
C_Q_ADD(&LINK->TX_Q, buffptr);
return;
}
VOID ProcessEthIPMsg(PETHMSG Buffer)
{
PIPMSG ipptr = (PIPMSG)&Buffer[1];
struct nat_table_entry * NAT = NULL;
int index;
if (memcmp(Buffer, ourMACAddr,6 ) != 0)
return; // Not for us
if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0)
return; // Discard our sends
// See if from a NAT'ed address
for (index=0; index < nat_table_len; index++)
{
NAT = &nat_table[index];
if (NAT->mappedipaddr == ipptr->IPSOURCE.addr)
{
ipptr->IPSOURCE.addr = NAT->origipaddr;
ipptr->IPCHECKSUM = 0; // to force cksum recalc below
break;
}
}
// if Checkum offload is active we get the packet before the NIC sees it (from PCAP)
if (ipptr->IPCHECKSUM == 0) // Windows seems to do this
{
// Generate IP and TCP/UDP checksums
int Len = ntohs(ipptr->IPLENGTH);
if (Len == 0)
{
return;
}
Len-=20;
ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20);
if (ipptr->IPPROTOCOL == 6) // TCP
{
PTCPMSG TCP = (PTCPMSG)&ipptr->Data;
PHEADER PH = {0};
PH.IPPROTOCOL = 6;
PH.LENGTH = htons(Len);
memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4);
memcpy(&PH.IPDEST, &ipptr->IPDEST, 4);
TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12);
TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len);
}
}
if (ipptr->IPDEST.addr == EncapAddr)
{
if (ipptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet
{
memcpy(RouterMac, Buffer->SOURCE, 6);
ProcessTunnelMsg(ipptr);
}
return; // Ignore Others
}
ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255);
}
VOID ProcessEthARPMsg(PETHARP arpptr, BOOL FromTAP)
{
int i=0;
uint64_t Mask=IPPortMask;
PARPDATA Arp;
PROUTEENTRY Route;
BOOL Found;
if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 )
return; // Discard our sends
switch (arpptr->ARPOPCODE)
{
case 0x0100:
// Is it for our ENCAP (not on our 44 LAN)
// printf("ARP Request for %08x Tell %08x\n",
// arpptr->TARGETIPADDR, arpptr->SENDIPADDR);
// Process anything for 44 from TAP
// (or should the be from either..???)
// if (FromTAP && (arpptr->TARGETIPADDR & 0xff) == 44)
if ((arpptr->TARGETIPADDR & 0xff) == 44)
goto ARPOk;
if (arpptr->TARGETIPADDR != EncapAddr)
{
// We should only accept requests from our subnet - we might have more than one net on iterface
if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask))
{
// Discard Unless it is from a NAT'ed Host
struct nat_table_entry * NAT = NULL;
int index;
for (index=0; index < nat_table_len; index++)
{
NAT = &nat_table[index];
if (NAT->mappedipaddr == arpptr->SENDIPADDR)
break;
}
if (index >= nat_table_len)
return;
// Also check it is for a 44. address or we send all LAN ARPS to
// RF
if ((arpptr->TARGETIPADDR & 0xff) != 44)
return;
}
if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0
return;
}
// Add to our table, as we will almost certainly want to send back to it
ARPOk:
// Debugprintf("ARP Request for %08x Tell %08x\n",
// arpptr->TARGETIPADDR, arpptr->SENDIPADDR);
Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found);
if (Found)
goto AlreadyThere; // Already there
if (Arp == NULL) return; // No point if table full
Arp->IPADDR = arpptr->SENDIPADDR;
Arp->ARPTYPE = 'E';
Arp->ARPINTERFACE = 255;
memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6);
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = ARPTIMEOUT;
// Also add to routes
AddToRoutes(Arp,arpptr->SENDIPADDR, 'E');
SaveARP();
AlreadyThere:
if (arpptr->TARGETIPADDR == OurIPAddr || arpptr->TARGETIPADDR == EncapAddr)
{
uint32_t Save = arpptr->TARGETIPADDR;
arpptr->ARPOPCODE = 0x0200;
memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6);
memcpy(arpptr->SENDHWADDR, ourMACAddr ,6);
arpptr->TARGETIPADDR = arpptr->SENDIPADDR;
arpptr->SENDIPADDR = Save;
memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6);
memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6);
// Debugprintf("Forus ARP Reply for %08x Targ %08x HNAT %08x\n",
// arpptr->SENDIPADDR, arpptr->TARGETIPADDR, HostNATAddr);
Send_ETH(arpptr,60, FromTAP);
return;
}
// If for our Ethernet Subnet, Ignore or we send loads of unnecessary msgs to ax.25
// Actually our subnet could be subnetted further
// So respond for NAT'ed addresses
// Why not just see if we have a route first??
Route = FindRoute(arpptr->TARGETIPADDR);
if (Route)
{
if (Route->TYPE == 'T' || Route->TYPE == 'U')
goto ProxyARPReply; // Assume we can always reach via tunnel
Arp = LookupARP(Route->GATEWAY, FALSE, &Found);
if (Arp)
{
if(Arp->ARPVALID && (Arp->ARPTYPE == 'E'))
return; // On LAN, so should reply direct
goto ProxyARPReply;
}
}
if ((arpptr->TARGETIPADDR & OurNetMask) == (OurIPAddr & OurNetMask))
{
// Unless for a NAT'ed address, in which case we reply with our virtual MAC
struct nat_table_entry * NAT = NULL;
int index;
for (index=0; index < nat_table_len; index++)
{
NAT = &nat_table[index];
if (NAT->origipaddr == arpptr->TARGETIPADDR)
break;
}
if (index >= nat_table_len)
return;
goto ProxyARPReply;
}
// Should't we just reply if we know it ?? (Proxy ARP)
// Maybe, but that may mean dowstream nodes dont learnit
// Sould we look in routes table, as we may have a gateway to it.
Route = FindRoute(arpptr->TARGETIPADDR);
if (Route)
{
if (Route->TYPE == 'T')
goto ProxyARPReply; // Assume we can always reach via tunnel
Arp = LookupARP(Route->GATEWAY, FALSE, &Found);
if (Arp)
{
if(Arp->ARPVALID && (Arp->ARPTYPE == 'E'))
return; // On LAN, so should reply direct
ProxyARPReply:
ETHARPREQMSG.TARGETIPADDR = arpptr->SENDIPADDR;
ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply
ETHARPREQMSG.SENDIPADDR = arpptr->TARGETIPADDR;
memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6);
memcpy(ETHARPREQMSG.MSGHDDR.DEST, arpptr->SENDHWADDR, 6);
// Debugprintf("Proxy ARP Reply for %08x Targ %08x HNAT %08x\n",
// ETHARPREQMSG.SENDIPADDR, ETHARPREQMSG.TARGETIPADDR, HostNATAddr);
// We send to TAP if request from TAP
// Send_ETH(&ETHARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr);
Send_ETH(&ETHARPREQMSG, 42, FromTAP);
return;
}
}
// Not in our cache, so send to all other ports enabled for IP, reformatting as necessary
AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
memset(AXARPREQMSG.TARGETHWADDR, 0, 7);
AXARPREQMSG.ARPOPCODE = 0x0100;
for (i = 1; i <= MaxBPQPortNo; i++)
{
if (Mask & 1)
Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, i, QST);
Mask >>= 1;
}
break;
case 0x0200:
if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 )
return; // Not for us
// Update ARP Cache
Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found);
if (Found)
goto Update;
if (Arp == NULL)
goto SendBack;
// Also add to routes
AddToRoutes(Arp, arpptr->SENDIPADDR, 'E');
Update:
Arp->IPADDR = arpptr->SENDIPADDR;
memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6);
Arp->ARPTYPE = 'E';
Arp->ARPINTERFACE = 255;
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = ARPTIMEOUT;
SaveARP();
SendBack:
// Send Back to Originator of ARP Request
if (Arp && arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request?
{
struct DATAMESSAGE * buffptr;
PIPMSG IPptr;
while (Arp->ARP_Q)
{
buffptr = Q_REM_NP(&Arp->ARP_Q);
IPptr = (PIPMSG)&buffptr->L2DATA[30];
RouteIPMsg(IPptr);
free(buffptr);
}
break;
}
Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found);
if (Found)
{
if (Arp->ARPINTERFACE == 255)
{
ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply
ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6);
memcpy(ETHARPREQMSG.MSGHDDR.DEST, Arp->HWADDR, 6);
Send_ETH(&ETHARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr);
return;
}
else
{
AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
AXARPREQMSG.ARPOPCODE = 0x0200; // Reply
AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7);
memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7);
Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, Arp->HWADDR);
return;
}
}
break;
default:
break;
}
return;
}
VOID ProcessAXARPMsg(PAXARP arpptr)
{
int i=0;
uint64_t Mask=IPPortMask;
PARPDATA Arp;
PROUTEENTRY Route;
BOOL Found;
arpptr->MSGHDDR.ORIGIN[6] &= 0x7e; // Clear end of Call
if (memcmp(arpptr->MSGHDDR.ORIGIN, MYCALL, 7) == 0) // ?Echoed packet?
return;
switch (arpptr->ARPOPCODE)
{
case 0x0100:
{
// Add to our table, as we will almost certainly want to send back to it
if (arpptr->TARGETIPADDR == 0)
return; // Ignore 0.0.0.0
Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found);
if (Found)
goto AlreadyThere; // Already there
if (Arp != NULL)
{
// ENTRY NOT FOUND - IF ANY SPARE ENTRIES, USE ONE
Arp->IPADDR = arpptr->SENDIPADDR;
memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,7);
Arp->ARPTYPE = 'D';
Arp->ARPINTERFACE = arpptr->MSGHDDR.PORT;
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = ARPTIMEOUT;
// Also add to routes
AddToRoutes(Arp, arpptr->SENDIPADDR, 'D');
}
AlreadyThere:
if (arpptr->TARGETIPADDR == OurIPAddr)
{
arpptr->ARPOPCODE = 0x0200;
memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR, 7);
memcpy(arpptr->SENDHWADDR, MYCALL, 7);
arpptr->TARGETIPADDR = arpptr->SENDIPADDR;
arpptr->SENDIPADDR = OurIPAddr;
Send_AX_Datagram((PMESSAGE)arpptr, 46, arpptr->MSGHDDR.PORT, arpptr->MSGHDDR.ORIGIN);
return;
}
// Should't we just reply if we know it ?? (Proxy ARP)
// Maybe, but that may mean dowstream nodes dont learnit
// Sould we look in routes table, as we may have a gateway to it.
Route = FindRoute(arpptr->TARGETIPADDR);
if (Route)
{
if (Route->TYPE == 'T')
goto AXProxyARPReply; // Assume we can always reach via tunnel
Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found);
if (Found)
{
// if Trarget is the station we got the request from, there is a loop
// KIll the ARP entry, and ignore
if (memcmp(Arp->HWADDR, arpptr->SENDHWADDR, 7) == 0)
{
RemoveARP(Arp);
return;
}
AXProxyARPReply:
AXARPREQMSG.ARPOPCODE = 0x0200; // Reply
AXARPREQMSG.TARGETIPADDR = arpptr->SENDIPADDR;
AXARPREQMSG.SENDIPADDR = arpptr->TARGETIPADDR;
memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7);
memcpy(AXARPREQMSG.TARGETHWADDR, arpptr->SENDHWADDR, 7);
Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, arpptr->MSGHDDR.PORT, arpptr->SENDHWADDR);
return;
}
}
// Not in our cache, so send to all other ports enabled for IP, reformatting as necessary
AXARPREQMSG.ARPOPCODE = 0x0100;
AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
for (i=1; i<=MaxBPQPortNo; i++)
{
if (i != arpptr->MSGHDDR.PORT)
if (Mask & 1)
Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, i, QST);
Mask >>= 1;
}
memset(ETHARPREQMSG.MSGHDDR.DEST, 0xff, 6);
memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6);
ETHARPREQMSG.ARPOPCODE = 0x0100;
ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
memcpy(ETHARPREQMSG.SENDHWADDR, ourMACAddr, 6);
Send_ETH(&ETHARPREQMSG, 42, FALSE);
break;
}
case 0x0200:
// Update ARP Cache
Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found);
if (Found)
goto Update;
if (Arp == NULL)
goto SendBack;
// Also add to routes
AddToRoutes(Arp, arpptr->SENDIPADDR, 'D');
Update:
Arp->IPADDR = arpptr->SENDIPADDR;
memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,7);
Arp->ARPTYPE = 'D';
Arp->ARPINTERFACE = arpptr->MSGHDDR.PORT;
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = ARPTIMEOUT;
SendBack:
// Send Back to Originator of ARP Request
if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request?
break;
Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found);
if (Found)
{
if (Arp->ARPINTERFACE == 255)
{
ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply
ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr,6);
memcpy(ETHARPREQMSG.TARGETHWADDR, Arp->HWADDR, 6);
memcpy(ETHARPREQMSG.MSGHDDR.DEST, Arp->HWADDR, 6);
Send_ETH(&ETHARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr);
return;
}
else
{
AXARPREQMSG.ARPOPCODE = 0x0200; // Reply
AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR;
AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR;
memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7);
memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7);
Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, Arp->HWADDR);
return;
}
}
break;
default:
return;
}
}
VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port)
{
uint32_t Dest;
PARPDATA Arp;
PROUTEENTRY Route;
BOOL Found;
PUDPMSG UDPptr;
if (IPptr->VERLEN != 0x45)
{
Dest = IPptr->IPDEST.addr;
Debugprintf("IP Pkt not 45 for %s\n", FormatIP(Dest));
return; // Only support Type = 4, Len = 20
}
if (!CheckIPChecksum(IPptr))
{
Dest = IPptr->IPDEST.addr;
Debugprintf("IP Pkt Bad CKSUM for %s\n", FormatIP(Dest));
return;
}
// Make sure origin ia routable. If not, add to ARP
Route = FindRoute(IPptr->IPSOURCE.addr);
if (Route == NULL)
{
Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Arp != NULL)
{
Arp->IPADDR = IPptr->IPSOURCE.addr;
if (Type == 'E')
{
memcpy(Arp->HWADDR, MACADDR, 6);
}
else
{
memcpy(Arp->HWADDR, MACADDR, 7);
Arp->HWADDR[6] &= 0x7e;
}
Arp->ARPTYPE = Type;
Arp->ARPINTERFACE = Port;
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = ARPTIMEOUT;
// Also add to routes
AddToRoutes(Arp, IPptr->IPSOURCE.addr, Type);
SaveARP();
}
}
}
if (Route && Route->ARP)
Route->ARP->ARPTIMER = ARPTIMEOUT; // Refresh
// See if for us - if not pass to router
Dest = IPptr->IPDEST.addr;
// Debugprintf("IP Pkt for %s\n", FormatIP(Dest));
if (Dest == OurIPAddr)
goto ForUs;
RouteIPMsg(IPptr);
return;
ForUs:
// We now pass stuff addressed to us to the host, unless it is a reponse
// to our ping request
// Not sure what to do with snmp (maybe option)
// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet
// {
// ProcessTunnelMsg(IPptr);
// return;
// }
if (IPptr->IPPROTOCOL == 1) // ICMP
{
int Len;
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
Len = ntohs(IPptr->IPLENGTH);
Len-=20;
Debugprintf("FORUS ICMP Type %d", ICMPptr->ICMPTYPE);
if (Len == 28 && ICMPptr->ICMPTYPE == 0 && memcmp(ICMPptr->ICMPData, "*BPQ", 4) == 0)
{
// Probably our response
ProcessICMPMsg(IPptr);
return;
}
}
// Support UDP for SNMP
if (BPQSNMP && IPptr->IPPROTOCOL == 17) // UDP
{
UDPptr = (PUDPMSG)&IPptr->Data;
if (UDPptr->DESTPORT == htons(161))
{
ProcessSNMPMessage(IPptr);
return;
}
}
// Pass rest to host
RouteIPMsg(IPptr);
return;
}
unsigned short cksum(unsigned short *ip, int len)
{
uint32_t sum = 0; /* assume 32 bit long, 16 bit short */
unsigned short copy [2048];
// if not word aligned copy
if (len > 1024 || len < 0 || ip == 0)
Debugprintf("%d", len);
// if (&ip & 1)
{
memcpy(copy, ip, len);
ip = copy;
}
while(len > 1)
{
sum += *(ip++);
if(sum & 0x80000000) /* if high order bit set, fold */
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if(len) /* take care of left over byte */
sum += (unsigned short) *(unsigned char *)ip;
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return (unsigned short)sum;
}
BOOL CheckIPChecksum(PIPMSG IPptr)
{
USHORT checksum=0;
if (IPptr->IPCHECKSUM == 0)
return TRUE; //Not used
checksum = cksum((unsigned short *)IPptr, 20);
if (checksum == 0xffff)
return TRUE;
else
return FALSE;
}
BOOL Check_Checksum(VOID * ptr1, int Len)
{
USHORT checksum;
checksum = cksum((unsigned short *)ptr1, Len);
if (checksum == 0xffff)
return TRUE;
else
return FALSE;
}
USHORT Generate_CHECKSUM(VOID * ptr1, int Len)
{
USHORT checksum=0;
checksum = cksum((unsigned short *)ptr1, Len);
return ~checksum ;
}
VOID ProcessTunnelMsg(PIPMSG IPptr)
{
UCHAR * ptr;
PIPMSG Outer = IPptr; // Save tunnel header
int Origlen;
// int InnerLen;
// Check header length - for now drop any message with options
if (IPptr->VERLEN != 0x45)
return;
Origlen = htons(Outer->IPLENGTH);
ptr = (UCHAR *)IPptr;
ptr += 20; // Skip IPIP Header
IPptr = (PIPMSG) ptr;
// If we are relaying it from a DMZ host there will be another header
if (IPptr->IPPROTOCOL == 4) // IPIP
{
Outer = IPptr;
ptr = (UCHAR *)IPptr;
ptr += 20; // Skip IPIP Header
IPptr = (PIPMSG) ptr;
Origlen -= 20;
}
// First check for RIP44 Messages
if (IPptr->IPPROTOCOL == 17) // UDP
{
PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data;
if (IPptr->IPSOURCE.addr == UCSD44 && UDPptr->DESTPORT == htons(520))
{
ProcessRIP44Message(IPptr);
return;
}
}
// for now drop anything not from a 44 address.
// now also need to drop 44.192/10 as it has been sold to Amazon
if (IPptr->IPSOURCE.S_un_b.s_b1 != 44)
return;
// Drop 44.192 - 44.255
if (IPptr->IPSOURCE.S_un_b.s_b2 >= 192)
return;
// See if for us
// Handle Pings, our Ping responses and SNMP in BPQ
// pass rest to host
if (IPptr->IPDEST.addr == OurIPAddr)
{
if (IPptr->IPPROTOCOL == 1) // ICMP
{
int Len;
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
Len = ntohs(IPptr->IPLENGTH);
Len-=20;
if (Len == 28 && ICMPptr->ICMPTYPE == 0 && memcmp(ICMPptr->ICMPData, "*BPQ", 4) == 0)
{
// Probably ours
ProcessICMPMsg(IPptr);
return;
}
}
if (IPptr->IPPROTOCOL == 1) // ICMP
{
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
if (ICMPptr->ICMPTYPE == 8)
{
ProcessICMPMsg(IPptr);
return;
}
}
// Support UDP for SNMP
if (IPptr->IPPROTOCOL == 17) // UDP
{
PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data;
if (UDPptr->DESTPORT == htons(161))
{
ProcessSNMPMessage(IPptr);
return;
}
}
// Others should be passed to our host
// I think we can just drop through to RouteIPMsg
}
// I think anything else is just passed to the router
RouteIPMsg(IPptr);
}
VOID ProcessRIP44Message(PIPMSG IPptr)
{
int Len;
PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data;
PRIP2HDDR HDDR = (PRIP2HDDR)&UDPptr->UDPData;
PRIP2ENTRY RIP2;
Len = ntohs(IPptr->IPLENGTH);
Len -= 20;
if (UDPptr->CHECKSUM)
if (Check_Checksum(UDPptr, Len) == FALSE)
return;
if (HDDR->Command != 2 || HDDR->Version != 2)
return;
RIP2 = (PRIP2ENTRY) ++HDDR;
Len -= 12; // UDP and RIP Headers
if (RIP2->AddrFamily == 0xFFFF)
{
// Authentication Entry
Len -= 20;
RIP2++;
}
while (Len >= 20) // Entry Length
{
// See if already in table
PROUTEENTRY Route;
BOOL Found;
// if for our subnet, ignore
// Actually don't need to, as we won't overwrite the preconfigured
// interface route
Route = LookupRoute(RIP2->IPAddress, RIP2->Mask, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Route != NULL && RIP2->Metric < 16)
{
Route->NETWORK = RIP2->IPAddress;
Route->SUBNET = RIP2->Mask;
Route->METRIC = RIP2->Metric;
Route->Encap = RIP2->NextHop;
Route->TYPE = 'T';
Route->RIPTIMOUT = 3600; // 1 hour for now
}
}
else
{
// Already in table
// Should we replace an RF route with an ENCAP route??
// For now, no. May make an option later
// Should never replace our interface routes
if (Route->TYPE == 'T')
{
// See if same Encap, and if not, is this better metric?
// Is this possible with RIP44??
if (Route->Encap != RIP2->NextHop)
{
if (Route->METRIC >= RIP2->Metric)
{
// Should also change if equal, as dynamic address could have changed
Route->METRIC = RIP2->Metric;
Route->Encap = RIP2->NextHop;
}
}
Route->METRIC = RIP2->Metric;
if (RIP2->Metric >= 15)
{
// HE IS TELLING US ITS UNREACHABLE - START DELETE TIMER
Route->RIPTIMOUT = 0;
if (Route->GARTIMOUT == 0)
Route ->GARTIMOUT = 4;
}
else
{
Route->RIPTIMOUT = 3600; // 1 hour for now
Route->GARTIMOUT = 0; // In case started to delete
}
}
}
Len -= 20;
RIP2++;
}
SaveIPRoutes();
qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks);
LastRIP44Msg = time(NULL);
}
VOID ProcessICMPMsg(PIPMSG IPptr)
{
int Len;
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
Len = ntohs(IPptr->IPLENGTH);
Len-=20;
Check_Checksum(ICMPptr, Len);
if (ICMPptr->ICMPTYPE == 8)
{
// ICMP_ECHO
ICMPptr->ICMPTYPE = 0; // Convert to Reply
// CHECKSUM IT
ICMPptr->ICMPCHECKSUM = 0;
ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len);
// Swap Dest to Origin
IPptr->IPDEST = IPptr->IPSOURCE;
IPptr->IPSOURCE.addr = OurIPAddr;
IPptr->IPTTL = IPTTL;
// RouteIPMsg redoes checksum
RouteIPMsg(IPptr); // Send Back
return;
}
if (ICMPptr->ICMPTYPE == 0)
{
// ICMP_REPLY:
// It could be a reply to our request
// or from a our host pc
UCHAR * BUFFER = GetBuff();
UCHAR * ptr1;
struct _MESSAGE * Msg;
TRANSPORTENTRY * Session = L4TABLE;
char IP[20];
unsigned char work[4];
// Internal Pings have Length 28 and Circuit Index as ID
if (Len > 28 || ICMPptr->ICMPID >= MAXCIRCUITS)
{
// For Host
ReleaseBuffer(BUFFER);
RouteIPMsg(IPptr);
return;
}
Session += ICMPptr->ICMPID;
if (BUFFER == NULL)
return;
ptr1 = &BUFFER[7];
memcpy(work, &IPptr->IPSOURCE, 4);
sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
*ptr1++ = 0xf0; // PID
ptr1 += sprintf(ptr1, "Ping Response from %s", IP);
*ptr1++ = 0x0d; // CR
Len = (int)(ptr1 - BUFFER);
Msg = (struct _MESSAGE *)BUFFER;
Msg->LENGTH = Len;
Msg->CHAIN = NULL;
C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER);
PostDataAvailable(Session);
return;
}
}
VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2)
{
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
UCHAR * ptr;
if (OurIPAddr == 0)
return; // Can't do much without one
if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11)
return; // Don't send Time Exceeded for TimeExceded
// Copy the Original IP Header and first 8 bytes of packet down the buffer
ptr = (UCHAR *) ICMPptr;
memmove(ptr + 8, IPptr, 28); // IP header plus 8 data
// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header
IPptr->IPDEST = IPptr->IPSOURCE;
IPptr->IPSOURCE.addr = OurIPAddr;
IPptr->IPPROTOCOL = ICMP;
IPptr->IPTTL = IPTTL;
IPptr->FRAGWORD = 0;
IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data
memset (ICMPptr, 0, 8);
ICMPptr->ICMPTYPE = Type;
ICMPptr->ICMPCODE = Code;
ICMPptr->ICMPSEQUENCE = htons(P2);
ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36);
RouteIPMsg(IPptr);
}
VOID SendICMPTimeExceeded(PIPMSG IPptr)
{
SendICMPMessage(IPptr, 11, 0, 0);
return;
}
VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap)
{
union
{
struct sockaddr_in txaddr;
struct sockaddr_in6 txaddr6;
} TXaddr = {0};
int sent;
int addrlen = sizeof(struct sockaddr_in6);
int Origlen;
TXaddr.txaddr.sin_family = AF_INET;
Origlen = htons(IPptr->IPLENGTH);
// If we are using PCAP interface we have to add IPIP and MAC Headers.
if (EncapAddr != INADDR_NONE)
{
UCHAR IPCopy[2048]; // Need Space to add headers
PETHMSG Ethptr = (PETHMSG)IPCopy;
PIPMSG Outer = (PIPMSG)&IPCopy[14];
memset(IPCopy, 0, 34); // Eth + IP Headers
Outer->VERLEN = 0x45;
Outer->IPDEST.addr = Encap;
Outer->IPSOURCE.addr = EncapAddr;
Outer->IPPROTOCOL = 4;
Outer->IPTTL = IPTTL;
Outer->IPID = IPptr->IPID;
Outer->IPLENGTH = htons(Origlen + 20);
memcpy(&Outer->Data, IPptr, Origlen);
Outer->IPCHECKSUM = 0;
Outer->IPCHECKSUM = Generate_CHECKSUM(Outer, 20);
memcpy(Ethptr->DEST, RouterMac, 6);
memcpy(Ethptr->SOURCE, ourMACAddr, 6);
Ethptr->ETYPE= 0x0008;
Send_ETH(Ethptr, Origlen + 34, FALSE);
return;
}
if (UDPEncap)
{
UCHAR * ptr;
memcpy(&TXaddr, &RXaddr, sizeof(struct sockaddr_in6));
TXaddr.txaddr.sin_port = htons(UDPPort);
// UDP Processor Needs the Encap Address, but we don't need the IPIP hearer
// as that is added by the raw send later. Just stick it on the end.
ptr = (UCHAR *)IPptr;
memcpy(ptr + Origlen, &Encap, 4);
Origlen += 4;
}
else
memcpy(&TXaddr.txaddr.sin_addr, &Encap, 4);
sent = sendto(EncapSock, (char *)IPptr, Origlen, 0, (struct sockaddr *)&TXaddr, addrlen);
sent = GetLastError();
}
BOOL RouteIPMsg(PIPMSG IPptr)
{
PARPDATA Arp, ARPptr;
PROUTEENTRY Route;
BOOL Found;
struct nat_table_entry * NAT = NULL;
int index;
BOOL SendtoTAP = FALSE; // used on LinBPQ for NAT to This Host
// Decremnent TTL and Recalculate header checksum
IPptr->IPTTL--;
if (IPptr->IPTTL == 0)
{
SendICMPTimeExceeded(IPptr);
return FALSE;
}
// See if for a NATed Address
// Debugprintf("RouteIPFrame IP %s\n", FormatIP(IPptr->IPDEST.addr));
for (index=0; index < nat_table_len; index++)
{
NAT = &nat_table[index];
// for the moment only map all ports
if (NAT->origipaddr == IPptr->IPDEST.addr)
{
char Msg[80];
int ptr;
IPLen = htons(IPptr->IPLENGTH);
ptr = sprintf(Msg, "NAT %s to ", FormatIP(IPptr->IPDEST.addr));
sprintf(&Msg[ptr], "%s\n", FormatIP(NAT->mappedipaddr));
// Debugprintf("%s", Msg);
IPptr->IPDEST.addr = NAT->mappedipaddr;
if (IPptr->IPPROTOCOL == 6)
RecalcTCPChecksum(IPptr);
else if (IPptr->IPPROTOCOL == 17)
RecalcUDPChecksum(IPptr);
else if (IPptr->IPPROTOCOL == 1)
{
// ICMP. If it has an inner packet (Time Exceeded or Need to Fragment)
// we also need to un-NAT the inner packet.
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
if (ICMPptr->ICMPTYPE == 3 || ICMPptr->ICMPTYPE == 11)
{
PIPMSG IPptr = (PIPMSG)ICMPptr->ICMPData;
Debugprintf("NAT ICMP Unreachable or Time Exceeded %d", ICMPptr->ICMPTYPE);
IPptr->IPSOURCE.addr = NAT->mappedipaddr;
// Dest could also need to be de-natted
for (index=0; index < nat_table_len; index++)
{
NAT = &nat_table[index];
if (NAT->mappedipaddr == IPptr->IPDEST.addr)
{
IPptr->IPDEST.addr = NAT->origipaddr;
break;
}
}
IPptr->IPCHECKSUM = 0;
IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20);
ICMPptr->ICMPCHECKSUM = 0;
ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, IPLen - 20);
}
}
SendtoTAP = NAT->ThisHost;
break;
}
}
if (IPptr->IPDEST.addr == HostNATAddr)
SendtoTAP = TRUE;
IPptr->IPCHECKSUM = 0;
IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20);
// Everything is in the routes table, even arp-derived routes. so just look there
Route = FindRoute(IPptr->IPDEST.addr);
if (Route == NULL)
return FALSE; // ?? Dest unreachable ??
Route->FRAMECOUNT++;
Arp = Route->ARP;
if (Arp == NULL)
{
if (Route->TYPE == 'T')
{
SendIPtoEncap(IPptr, Route->Encap);
return TRUE;
}
else if (Route->TYPE == 'U')
{
int sent;
int addrlen = sizeof(struct sockaddr_in6);
int Origlen;
Origlen = htons(IPptr->IPLENGTH);
sent = sendto(UDPSendSock, (char *)IPptr, Origlen, 0, (struct sockaddr *)&Route->UDPADDR, addrlen);
return TRUE;
}
// Look up target address in ARP
if (Route->GATEWAY)
Arp = LookupARP(Route->GATEWAY, FALSE, &Found);
else
Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found);
if (!Found)
{
if (Route->GATEWAY == 0) // Interace Route
{
ARPptr = AllocARPEntry();
if (ARPptr != NULL)
{
struct DATAMESSAGE * buffptr;
Route->ARP = ARPptr;
ARPptr->ARPROUTE = Route;
ARPptr->ARPINTERFACE = 255;
ARPptr->ARPTIMER = 5;
ARPptr->ARPTYPE = 'E';
ARPptr->IPADDR = IPptr->IPDEST.addr;
// Save a copy to send on if ARP reply received
buffptr = malloc(2048);
if (buffptr)
{
if (ntohs(IPptr->IPLENGTH) > 1600)
{
Debugprintf("Overlength IP Packet %d" , ntohs(IPptr->IPLENGTH));
return TRUE;
}
memcpy(&buffptr->L2DATA[30], IPptr, ntohs(IPptr->IPLENGTH));
C_Q_ADD_NP(&ARPptr->ARP_Q, buffptr);
}
SendARPMsg(ARPptr, SendtoTAP);
return TRUE;
}
}
return FALSE;
}
}
if (Arp->ARPVALID)
{
if (Arp->ARPTYPE == 'T')
SendIPtoEncap(IPptr, Route->Encap);
else
if (Arp->ARPTYPE == 'E')
SendIPtoEther(IPptr, Arp->HWADDR, SendtoTAP);
else
SendIPtoAX25(IPptr, Arp->HWADDR, Arp->ARPINTERFACE, Arp->ARPTYPE);
return TRUE;
}
return FALSE;
}
VOID SendIPtoEther(PIPMSG IPptr, UCHAR * HWADDR, BOOL SendtoTAP)
{
// AX.25 headers are bigger, so there will always be room in buffer for enet header
PETHMSG Ethptr = (PETHMSG)IPptr;
int Len;
(UCHAR *)Ethptr--;
Len = ntohs(IPptr->IPLENGTH);
Len+=14; // Add eth Header
memcpy(Ethptr->DEST, HWADDR, 6);
memcpy(Ethptr->SOURCE, ourMACAddr, 6);
Ethptr->ETYPE= 0x0008;
Send_ETH(Ethptr,Len, SendtoTAP);
return;
}
VOID SendIPtoAX25(PIPMSG IPptr, UCHAR * HWADDR, int Port, char Mode)
{
PBUFFHEADER Msgptr = (PBUFFHEADER)IPptr;
int Len;
USHORT FRAGWORD = ntohs(IPptr->FRAGWORD);
USHORT OrigFragWord = FRAGWORD;
int PACLEN = 256;
(UCHAR *)Msgptr--;
Msgptr->PID = 0xcc; //IP
if (Port == 0) // NETROM
PACLEN = 235;
Len = ntohs(IPptr->IPLENGTH);
// Don't fragment VC stuff
if (Mode == 'V') // Virtual Circuit
{
Send_AX_Connected((PMESSAGE)Msgptr, Len + 16, Port, HWADDR);
return;
}
while (Len > PACLEN)
{
// Need to Frgament
USHORT Fraglen; // Max Fragment Size (PACLEN rounded down to 8 boundary))
USHORT Datalen; // Data Content))
UCHAR * ptr1 = &IPptr->Data;
//Bit 0: reserved, must be zero
//Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
//Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
//Fragment Offset: 13 bits
// FRAGWORD &= 0x3fff; // Clear Dont Fragment bit
if (FRAGWORD & (1 << 14))
{
SendICMPMessage(IPptr, 3, 4, PACLEN); // type 3 (dest unreachable), code 4 (frag needed but don't-fragment bit set))
return;
}
FRAGWORD |= (1 << 13); // Set More Fragments bit
IPptr->FRAGWORD = htons(FRAGWORD);
Datalen = (PACLEN - 20) & 0xFFF8; // Must be multiple of 8 bytes
Fraglen = Datalen + 20;
IPptr->IPLENGTH = htons(Fraglen);
IPptr->IPCHECKSUM = 0;
IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20);
// Send First Fragment
if (Mode == 'D') // Datagram
Send_AX_Datagram((PMESSAGE)Msgptr, Fraglen + 16, Port, HWADDR);
else
Send_AX_Connected((PMESSAGE)Msgptr, Fraglen + 16, Port, HWADDR);
// Update Header
FRAGWORD += Datalen / 8;
// Move Data Down the buffer
Len -= Datalen;
memmove(ptr1, ptr1 + (Datalen), Len);
}
// Reset Header in case we've messed with it
IPptr->IPLENGTH = htons(Len);
// if this started out as a fragment before we split it more,
// we need to leave the MF bit set
if ((OrigFragWord & 0x2000) == 0)
FRAGWORD &= 0x5fff; // Clear More Fragments bit
IPptr->FRAGWORD = htons(FRAGWORD);
IPptr->IPCHECKSUM = 0;
IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20);
Len+=16;
if (Mode == 'D') // Datagram
{
Send_AX_Datagram((PMESSAGE)Msgptr, Len, Port, HWADDR);
return;
}
Send_AX_Connected((PMESSAGE)Msgptr, Len, Port, HWADDR);
}
PROUTEENTRY AllocRouteEntry()
{
PROUTEENTRY Routeptr;
if (NumberofRoutes == 0)
RouteRecords = malloc(sizeof(void *));
else
RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *));
Routeptr = zalloc(sizeof(ROUTEENTRY));
if (Routeptr == NULL) return NULL;
RouteRecords[NumberofRoutes++] = Routeptr;
return Routeptr;
}
PARPDATA AllocARPEntry()
{
ARPDATA * ARPptr;
if (NumberofARPEntries == 0)
ARPRecords = malloc(sizeof(void *));
else
ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)* sizeof(void *));
ARPptr = malloc(sizeof(ARPDATA));
if (ARPptr == NULL) return NULL;
memset(ARPptr, 0, sizeof(ARPDATA));
ARPRecords[NumberofARPEntries++] = ARPptr;
return ARPptr;
}
VOID SendARPMsg(PARPDATA Arp, BOOL ToTAP)
{
// Send ARP. Initially used only to find default gateway
Arp->ARPTIMER = 5; // Retry periodically
if (Arp->ARPINTERFACE == 255)
{
ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST
ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR;
memset(ETHARPREQMSG.TARGETHWADDR, 0, 6);
ETHARPREQMSG.SENDIPADDR = OurIPAddr;
memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6);
memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6);
memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6);
Send_ETH(&ETHARPREQMSG, 42, ToTAP);
return;
}
else
{
AXARPREQMSG.TARGETIPADDR = Arp->IPADDR;
memset(AXARPREQMSG.TARGETHWADDR, 0, 7);
AXARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST
AXARPREQMSG.SENDIPADDR = OurIPAddr;
memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7);
memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7);
Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, QST);
return;
}
}
PROUTEENTRY FindRoute(uint32_t IPADDR)
{
PROUTEENTRY Route = NULL;
int i;
for (i = 0; i < NumberofRoutes; i++)
{
Route = RouteRecords[i];
if ((IPADDR & Route->SUBNET) == Route->NETWORK)
return Route;
}
return NULL;
}
PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found)
{
PROUTEENTRY Route = NULL;
int i;
for (i = 0; i < NumberofRoutes; i++)
{
Route = RouteRecords[i];
if (Route->NETWORK == IPADDR && Route->SUBNET == Mask)
{
*Found = TRUE;
return Route;
}
}
// Not Found
*Found = FALSE;
if (Add)
{
Route = AllocRouteEntry();
return Route;
}
else
return NULL;
}
PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found)
{
PARPDATA Arp = NULL;
int i;
for (i=0; i < NumberofARPEntries; i++)
{
Arp = ARPRecords[i];
if (Arp->IPADDR == IPADDR)
{
*Found = TRUE;
return Arp;
}
}
// Not Found
*Found = FALSE;
if (Add)
{
Arp = AllocARPEntry();
return Arp;
}
else
return NULL;
}
VOID RemoveARP(PARPDATA Arp);
VOID RemoveRoute(PROUTEENTRY Route)
{
int i;
for (i=0; i < NumberofRoutes; i++)
{
if (Route == RouteRecords[i])
{
while (i < NumberofRoutes)
{
RouteRecords[i] = RouteRecords[i+1];
i++;
}
if (Route->ARP)
{
PARPDATA Arp = Route->ARP;
Route->ARP->ARPROUTE = NULL; // Avoid recursion
if (Arp->LOCKED == 0)
RemoveARP(Arp);
}
free(Route);
NumberofRoutes--;
return;
}
}
}
VOID RemoveARP(PARPDATA Arp)
{
int i;
while (Arp->ARP_Q)
free(Q_REM_NP((void *)&Arp->ARP_Q));
for (i=0; i < NumberofARPEntries; i++)
{
if (Arp == ARPRecords[i])
{
while (i < NumberofARPEntries)
{
ARPRecords[i] = ARPRecords[i+1];
i++;
}
// Remove linked route
if (Arp->ARPROUTE)
{
PROUTEENTRY Route = Arp->ARPROUTE;
Arp->ARPROUTE->ARP = NULL; // Avoid recursion
if (Route->LOCKED == 0)
RemoveRoute(Route);
}
free(Arp);
NumberofARPEntries--;
return;
}
}
}
Dll int APIENTRY GetIPInfo(VOID * ARPRecs, VOID * IPStatsParam, int index)
{
IPStats.ARPEntries = NumberofARPEntries;
ARPFlag = index;
#ifndef LINBPQ
#ifndef _WIN64
_asm {
mov esi, ARPRecs
mov DWORD PTR[ESI], offset Arp
mov esi, IPStatsParam
mov DWORD PTR[ESI], offset IPStats
}
#endif
#endif
return ARPFlag;
}
static BOOL ReadConfigFile()
{
// IPAddr 192.168.0.129
// IPBroadcast 192.168.0.255
// IPGateway 192.168.0.1
// IPPorts 1,4
// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000
char * Config;
char * ptr1, * ptr2;
PROUTEENTRY Route;
BOOL Found;
char buf[256],errbuf[256];
Config = PortConfig[IPConfigSlot]; // Config fnom bpq32.cfg
if (Config)
{
// Using config from bpq32.cfg
ptr1 = Config;
ptr2 = strchr(ptr1, 13);
while(ptr2)
{
memcpy(buf, ptr1, ptr2 - ptr1);
buf[ptr2 - ptr1] = 0;
ptr1 = ptr2 + 2;
ptr2 = strchr(ptr1, 13);
strlop(buf, ';');
strlop(buf, '#');
strcpy(errbuf,buf); // save in case of error
if (!ProcessLine(buf))
{
WritetoConsoleLocal("IP Gateway bad config record ");
strcat(errbuf, "\n");
WritetoConsoleLocal(errbuf);
}
}
// Add an Interface Route to our LAN
Route = LookupRoute(OurIPAddr & OurNetMask, OurNetMask, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Route != NULL)
{
Route->NETWORK = OurIPAddr & OurNetMask;
Route->SUBNET = OurNetMask;
Route->GATEWAY = 0;
Route->LOCKED = 1;
Route->TYPE = 'E';
}
}
}
return TRUE;
}
static int ProcessLine(char * buf)
{
char * ptr, * p_value, * p_origport, * p_host;
int port, mappedport, ipad, mappedipad;
BOOL NATTAP = FALSE;
int i;
ptr = strtok(buf, " \t\n\r");
p_value = strtok(NULL, " \t\n\r");
if(ptr == NULL) return (TRUE);
if(*ptr =='#') return (TRUE); // comment
if(*ptr ==';') return (TRUE); // comment
if(_stricmp(ptr,"ADAPTER") == 0)
{
//#ifndef WIN32
// WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n");
// return TRUE;
//#endif
strcpy(Adapter,p_value);
return (TRUE);
}
if(_stricmp(ptr,"promiscuous") == 0)
{
Promiscuous = atoi(p_value);
return (TRUE);
}
if(_stricmp(ptr,"UDPTunnel") == 0)
{
WantUDPTunnel = atoi(p_value);
return (TRUE);
}
if (_stricmp(ptr,"44Encap") == 0) // Enable Net44 IPIP Tunnel
{
WantEncap = TRUE;
if (p_value == NULL)
return TRUE;
EncapAddr = inet_addr(p_value);
strcpy(EncapAddrText, p_value);
if (EncapAddr != INADDR_NONE)
{
int a,b,c,d,e,f,num;
// See if MAC Specified
p_value = strtok(NULL, " \t\n\r");
if (p_value == NULL)
return TRUE;
num=sscanf(p_value,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f);
if (num == 6)
{
RouterMac[0]=a;
RouterMac[1]=b;
RouterMac[2]=c;
RouterMac[3]=d;
RouterMac[4]=e;
RouterMac[5]=f;
p_value = strtok(NULL, " \t\n\r");
if (p_value == NULL)
return TRUE;
}
// see if source addr specified
if (_stricmp(p_value, "RIPSOURCE") == 0)
{
// Set Source addr of RIP44 packets
p_value = strtok(NULL, " =\t\n\r");
UCSD44 = inet_addr(p_value);
return TRUE;
}
return FALSE;
}
if (_stricmp(p_value, "UDP") == 0)
{
UDPEncap = TRUE;
// look for options PORT and/or IPv6
p_value = strtok(NULL, " \t\n\r");
while (p_value)
{
if (_stricmp(p_value, "IPv6") == 0)
IPv6 = TRUE;
else
if (_memicmp(p_value, "PORT=", 5) == 0)
UDPPort = atoi(&p_value[5]);
p_value = strtok(NULL, " \t\n\r");
}
}
return (TRUE);
}
if (_stricmp(ptr,"IPAddr") == 0)
{
// accept /xx as a netmask
char * p_mask = strlop(p_value, '/');
if (p_mask)
{
uint32_t IPMask;
int Bits = atoi(p_mask);
if (Bits > 32)
Bits = 32;
if (Bits == 0)
IPMask = 0;
else
IPMask = (0xFFFFFFFF) << (32 - Bits);
OurNetMask = htonl(IPMask); // Needs to be Network order
}
OurIPAddr = inet_addr(p_value);
if (OurIPAddr == INADDR_NONE) return (FALSE);
strcpy(IPAddrText, p_value);
return (TRUE);
}
// if (_stricmp(ptr,"HostIPAddr") == 0)
// {
// HostIPAddr = inet_addr(p_value);
// if (HostIPAddr == INADDR_NONE) return (FALSE);
// strcpy(HostIPAddrText, p_value);
// return (TRUE);
// }
if (_stricmp(ptr,"IPNetMask") == 0)
{
OurNetMask = inet_addr(p_value);
if (strcmp(p_value, "255.255.255.255") == 0)
return TRUE;
if (OurNetMask == INADDR_NONE) return (FALSE);
return (TRUE);
}
if (_stricmp(ptr,"IPPorts") == 0)
{
struct _EXTPORTDATA * PORTVEC;
while (p_value != NULL)
{
i=atoi(p_value);
if (i == 0) return FALSE;
PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromPortNum(i);
if (PORTVEC == NULL)
return FALSE;
// if not KISS, make sure it can send UI frames
if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL
if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR
if (PORTVEC->PORTCONTROL.UICAPABLE == 0)
return FALSE;
IPPortMask |= (uint64_t)1 << (i-1);
p_value = strlop(p_value, ',');
}
return (TRUE);
}
if (_stricmp(ptr,"NoDefaultRoute") == 0)
{
NoDefaultRoute = TRUE;
return TRUE;
}
// ARP 44.131.4.18 GM8BPQ-7 1
if (_stricmp(ptr,"ARP") == 0)
{
p_value[strlen(p_value)] = ' '; // put back together
return ProcessARPLine(p_value, TRUE);
}
if (_stricmp(ptr,"ROUTE") == 0)
{
p_value[strlen(p_value)] = ' '; // put back together
ProcessROUTELine(p_value, TRUE);
return TRUE;
}
if (_stricmp(ptr,"NAT") == 0)
{
PROUTEENTRY Route;
BOOL Found;
if (OurIPAddr == 0)
{
WritetoConsoleLocal("NAT lines should follow IPAddr\n");
return FALSE;
}
if (!p_value) return FALSE;
ipad = inet_addr(p_value);
if (ipad == INADDR_NONE)
return FALSE;
p_host = strtok(NULL, " ,\t\n\r");
if (!p_host) return FALSE;
mappedipad = inet_addr(p_host);
if (mappedipad == INADDR_NONE)
return FALSE;
p_origport = strtok(NULL, " ,\t\n\r");
if (p_origport && strcmp(p_origport, "TAP") == 0)
NATTAP = TRUE;
//
// // Default is all ports
//
// if (p_origport)
// {
// p_port = strtok(NULL, " ,\t\n\r");
// if (!p_port) return FALSE;
//
// port = atoi(p_origport);
// mappedport=atoi(p_port);
// }
// else
port = mappedport = 0;
#ifndef WIN32
// on Linux, we send stuff for our host to TAP
if (ipad == OurIPAddr || NATTAP)
nat_table[nat_table_len].ThisHost = TRUE;
#endif
nat_table[nat_table_len].origipaddr = ipad;
nat_table[nat_table_len].origport = ntohs(port);
nat_table[nat_table_len].mappedipaddr = mappedipad;
nat_table[nat_table_len++].mappedport = ntohs(mappedport);
// Add a Host Route
Route = LookupRoute(mappedipad, 0xffffffff, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Route != NULL)
{
Route->NETWORK = mappedipad ;
Route->SUBNET = 0xffffffff;
Route->GATEWAY = 0;
Route->LOCKED = 1;
Route->TYPE = 'E';
}
}
return TRUE;
}
if (_stricmp(ptr,"ENABLESNMP") == 0)
{
BPQSNMP = TRUE;
return TRUE;
}
//
// Bad line
//
return FALSE;
}
VOID DoARPTimer()
{
PARPDATA Arp = NULL;
int i;
for (i=0; i < NumberofARPEntries; i++)
{
Arp = ARPRecords[i];
if (!Arp->ARPVALID)
{
Arp->ARPTIMER--;
if (Arp->ARPTIMER == 0)
{
// Retry Request
// SendARPMsg(Arp);
RemoveARP(Arp);
}
continue;
}
// Time out active entries
if (Arp->LOCKED == 0)
{
Arp->ARPTIMER--;
if (Arp->ARPTIMER == 0)
{
// Remove Entry
RemoveARP(Arp);
SaveARP();
}
}
}
}
VOID DoRouteTimer()
{
int i;
PROUTEENTRY Route;
time_t NOW = time(NULL);
for (i=0; i < NumberofRoutes; i++)
{
Route = RouteRecords[i];
if (Route->RIPTIMOUT)
Route->RIPTIMOUT--;
if (Route->TYPE == 'T' && Route->RIPTIMOUT == 0 && Route->LOCKED == FALSE)
{
// Only remove Encap routes if we are still getting RIP44 messages,
// so we can keep going if UCSD stops sending updates, but can time
// out entries that are removed
if ((NOW - LastRIP44Msg) < 3600)
{
RemoveRoute(Route);
return; // Will remove all eventually
}
}
}
}
// PCAP Support Code
#ifdef WIN32
FARPROCX GetAddress(char * Proc)
{
FARPROCX ProcAddr;
int err=0;
char buf[256];
int n;
ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc);
if (ProcAddr == 0)
{
err=GetLastError();
n=sprintf(buf,"Error finding %s - %d", Proc,err);
WritetoConsoleLocal(buf);
return(0);
}
return ProcAddr;
}
FARPROCI GetAddressI(char * Proc)
{
FARPROCI ProcAddr;
int err = 0;
char buf[256];
int n;
ProcAddr = (FARPROCI)GetProcAddress(PcapDriver, Proc);
if (ProcAddr == 0)
{
err = GetLastError();
n = sprintf(buf, "Error finding %s - %d", Proc, err);
WritetoConsoleLocal(buf);
return(0);
}
return ProcAddr;
}
#endif
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
int OpenPCAP()
{
u_long param=1;
BOOL bcopt=TRUE;
int i=0;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
char packet_filter[256];
struct bpf_program fcode;
char buf[256];
int n;
#ifndef MACBPQ
/* Open the adapter */
adhandle= pcap_open_livex(Adapter, // name of the device
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
Promiscuous, // promiscuous mode (nonzero means promiscuous)
1, // read timeout
errbuf // error buffer
);
if (adhandle == NULL)
return FALSE;
pcap_setnonblockx(adhandle, 1, errbuf);
/* Check the link layer. We support only Ethernet for simplicity. */
if((int)pcap_datalinkx(adhandle) != DLT_EN10MB)
{
n=sprintf(buf,"\nThis program works only on Ethernet networks.\n");
WritetoConsoleLocal(buf);
adhandle = 0;
return FALSE;
}
netmask=0xffffff;
// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806");
n = sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x",
ourMACAddr[0], ourMACAddr[1], ourMACAddr[2],
ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]);
//compile the filter
if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 )
{
n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n");
WritetoConsoleLocal(buf);
adhandle = 0;
return FALSE;
}
//set the filter
if (pcap_setfilterx(adhandle, &fcode)<0)
{
n=sprintf(buf,"\nError setting the filter.\n");
WritetoConsoleLocal(buf);
adhandle = 0;
return FALSE;
}
#endif
return TRUE;
}
VOID ReadARP()
{
FILE *file;
char buf[256],errbuf[256];
if ((file = fopen(ARPFN,"r")) == NULL) return;
while(fgets(buf, 255, file) != NULL)
{
strcpy(errbuf,buf); // save in case of error
if (!ProcessARPLine(buf, FALSE))
{
WritetoConsoleLocal("IP Gateway bad ARP record ");
WritetoConsoleLocal(errbuf);
}
}
fclose(file);
return;
}
BOOL ProcessARPLine(char * buf, BOOL Locked)
{
char * p_ip, * p_mac, * p_port, * p_type;
int Port;
char Mac[7];
char AXCall[64];
BOOL Stay, Spy;
uint32_t IPAddr;
int a,b,c,d,e,f,num;
struct PORTCONTROL * PORT;
PARPDATA Arp;
BOOL Found;
_strupr(buf); // calls should be upper case
// 192.168.0.131 GM8BPQ-13 1 D
p_ip = strtok(buf, " \t\n\r");
p_mac = strtok(NULL, " \t\n\r");
p_port = strtok(NULL, " \t\n\r");
p_type = strtok(NULL, " \t\n\r");
if(p_ip == NULL) return (TRUE);
if(*p_ip =='#') return (TRUE); // comment
if(*p_ip ==';') return (TRUE); // comment
if (p_mac == NULL) return FALSE;
if (p_port == NULL) return FALSE;
if (p_type == NULL) return FALSE;
IPAddr = inet_addr(p_ip);
if (IPAddr == INADDR_NONE) return FALSE;
_strupr(p_type);
// Don't restore Eth addresses from the save file
if (*p_type == 'E' && Locked == FALSE)
return TRUE;
if (!((*p_type == 'D') || (*p_type == 'E') || (*p_type =='V'))) return FALSE;
Port=atoi(p_port);
if (p_mac == NULL) return (FALSE);
if (*p_type == 'E')
{
num=sscanf(p_mac,"%x:%x:%x:%x:%x:%x",&a,&b,&c,&d,&e,&f);
if (num != 6) return FALSE;
Mac[0]=a;
Mac[1]=b;
Mac[2]=c;
Mac[3]=d;
Mac[4]=e;
Mac[5]=f;
if (Port != 255) return FALSE;
}
else
{
if (DecodeCallString(p_mac, &Stay, &Spy, &AXCall[0]) == 0)
return FALSE;
if (Port == 0 && *p_type !='V') // Port 0 for NETROM
return FALSE;
if (Port)
{
PORT = GetPortTableEntryFromPortNum(Port);
if (PORT == NULL)
return FALSE;
}
}
Arp = LookupARP(IPAddr, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Arp != NULL)
{
Arp->IPADDR = IPAddr;
if (*p_type == 'E')
{
memcpy(Arp->HWADDR, Mac, 6);
}
else
{
memcpy(Arp->HWADDR, AXCall, 64);
Arp->HWADDR[6] &= 0x7e;
}
Arp->ARPTYPE = *p_type;
Arp->ARPINTERFACE = Port;
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = (Arp->ARPTYPE == 'E')? 300 : ARPTIMEOUT;
Arp->LOCKED = Locked;
// Also add to Routes
AddToRoutes(Arp, IPAddr, *p_type);
Arp->ARPROUTE->LOCKED = Locked;
}
}
return TRUE;
}
VOID AddToRoutes(PARPDATA Arp, UINT IPAddr, char Type)
{
PROUTEENTRY Route;
BOOL Found;
UINT IPMask = 0xffffffff; // All ARP rerived routes are Host Routes
Route = LookupRoute(Arp->IPADDR, IPMask, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Route != NULL)
{
Route->NETWORK = IPAddr;
Route->SUBNET = IPMask;
Route->GATEWAY = IPAddr; // Host Route
Route->TYPE = Type;
}
}
Arp->ARPROUTE = Route;
Route->ARP = Arp; // Crosslink Arp<>Routee
// Sort into reverse mask order
qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks);
}
// ROUTE 44.131.4.18/32 D GM8BPQ-7 1// Datagram?netrom/VC via Call !!!! No - this sohuld be an ARP entry
// ROUTE 44.131.4.18/32 T n.n.n.n // Vis Tunnel Endpoint n.n.n.n
// ROUTE 44.131.4.18/32 E n.n.n.n // Via IP address over Ethernet
int CountDots(char * Addr)
{
int Dots = 0;
while(*Addr)
if (*(Addr++) == '.')
Dots++;
return Dots;
}
BOOL ProcessROUTELine(char * buf, BOOL Locked)
{
char * p_ip, * p_type, * p_mask, * p_gateway, * p_port;
uint32_t IPAddr, IPMask, IPGateway;
char Type = ' ';
int n, Port;
PROUTEENTRY Route;
BOOL Found;
// ROUTE 44.131.4.18/31 44.131.4.1 // Normal Route
// ROUTE 44.131.4.18/31 1.2.3.4 T // Tunnel via 1.2.3.4
p_ip = strtok(buf, " \t\n\r");
p_gateway = strtok(NULL, " \t\n\r");
p_type = strtok(NULL, " \t\n\r");
if (_stricmp(p_ip, "addprivate") == 0)
{
// From Encap.txt file
p_ip = p_gateway;
p_gateway = strtok(NULL, " \t\n\r");
Type = 'T';
p_type = NULL;
}
if(p_ip == NULL) return (TRUE);
if(*p_ip =='#') return (TRUE); // comment
if(*p_ip ==';') return (TRUE); // comment
if (p_gateway == NULL) return FALSE;
if (p_type)
{
if (_stricmp(p_type, "UDPT") == 0) // Tunnelling over UDP
{
p_port = strtok(NULL, " \t\n\r");
Type = 'U';
if (p_port)
Port = atoi(p_port);
}
else
{
Type = *p_type;
if (Type == 't') Type = 'T';
}
}
p_mask = strchr(p_ip, '/');
if (p_mask)
{
int Bits = atoi(p_mask + 1);
if (Bits > 32)
Bits = 32;
if (Bits == 0)
IPMask = 0;
else
IPMask = (0xFFFFFFFF) << (32 - Bits);
*p_mask = 0;
}
else
IPMask = 32; // No mask means Host route
IPMask = htonl(IPMask); // Needs to be Network order
IPGateway = inet_addr(p_gateway);
if (IPGateway == INADDR_NONE) return FALSE;
// The encap.txt format omits trailing zeros.
n = CountDots(p_ip);
if (n == 2)
strcat(p_ip, ".0");
else if (n == 1)
strcat(p_ip, ".0.0");
else if (n == 0)
strcat(p_ip, ".0.0.0");
IPAddr = inet_addr(p_ip);
if (IPAddr == INADDR_NONE) return FALSE;
Route = LookupRoute(IPAddr, IPMask, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Route != NULL)
{
Route->NETWORK = IPAddr;
Route->SUBNET = IPMask;
if (Type == 'U')
{
Route->UDPADDR.sin_port = htons(Port);
Route->UDPADDR.sin_addr.s_addr = inet_addr(p_gateway);
Route->UDPADDR.sin_family = AF_INET;
}
else if (Type == 'T')
Route->Encap = IPGateway;
else
Route->GATEWAY = IPGateway;
Route->TYPE = Type;
Route->LOCKED = Locked;
// Link to ARP
Route->ARP = LookupARP(Route->GATEWAY, FALSE, &Found);
}
}
return TRUE;
}
VOID SaveARP ()
{
PARPDATA Arp;
int i;
FILE * file;
if ((file = fopen(ARPFN, "w")) == NULL)
return;
for (i=0; i < NumberofARPEntries; i++)
{
Arp = ARPRecords[i];
if (Arp->ARPVALID && !Arp->LOCKED)
WriteARPLine(Arp, file);
}
fclose(file);
return ;
}
VOID WriteARPLine(PARPDATA ARPRecord, FILE * file)
{
int SSID, Len, j;
char Mac[20];
char Call[7];
char IP[20];
char Line[100];
unsigned char work[4];
memcpy(work, &ARPRecord->IPADDR, 4);
sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
if(ARPRecord->ARPINTERFACE == 255) // Ethernet
{
sprintf(Mac," %02x:%02x:%02x:%02x:%02x:%02x",
ARPRecord->HWADDR[0],
ARPRecord->HWADDR[1],
ARPRecord->HWADDR[2],
ARPRecord->HWADDR[3],
ARPRecord->HWADDR[4],
ARPRecord->HWADDR[5]);
}
else
{
for (j=0; j< 6; j++)
{
Call[j] = ARPRecord->HWADDR[j]/2;
if (Call[j] == 32) Call[j] = 0;
}
Call[6] = 0;
SSID = (ARPRecord->HWADDR[6] & 31)/2;
sprintf(Mac,"%s-%d", Call, SSID);
}
Len = sprintf(Line,"%s %s %d %c\n",
IP, Mac, ARPRecord->ARPINTERFACE, ARPRecord->ARPTYPE);
fputs(Line, file);
return;
}
VOID ReadIPRoutes()
{
PROUTEENTRY Route;
FILE * file;
char * Net;
char * Nexthop;
char * Encap;
char * Context;
char * Type;
char * p_mask;
char Line[256];
uint32_t IPAddr, IPMask = 0xffffffff, IPGateway;
BOOL Found;
if ((file = fopen(IPRFN, "r")) == NULL)
return;
// 44.0.0.1/32 0.0.0.0 T 1 8 encap 169.228.66.251
while(fgets(Line, 255, file) != NULL)
{
Net = strtok_s(Line, " \n", &Context);
if (Net == 0) continue;
if (strcmp(Net, "ENCAPMAC") == 0)
{
int a,b,c,d,e,f,num;
num=sscanf(Context,"%x:%x:%x:%x:%x:%x",&a,&b,&c,&d,&e,&f);
if (num == 6)
{
RouterMac[0]=a;
RouterMac[1]=b;
RouterMac[2]=c;
RouterMac[3]=d;
RouterMac[4]=e;
RouterMac[5]=f;
}
continue;;
}
Nexthop = strtok_s(NULL, " \n", &Context);
if (Nexthop == 0) continue;
Type = strtok_s(NULL, " \n", &Context);
if (Type == 0) continue;
p_mask = strlop(Net, '/');
if (p_mask)
{
int Bits = atoi(p_mask);
if (Bits > 32)
Bits = 32;
if (Bits == 0)
IPMask = 0;
else
IPMask = (0xFFFFFFFF) << (32 - Bits);
IPMask = htonl(IPMask); // Needs to be Network order
}
IPAddr = inet_addr(Net);
if (IPAddr == INADDR_NONE) continue;
IPGateway = inet_addr(Nexthop);
if (IPGateway == INADDR_NONE) continue;
if (Type[0] == 'T')
{
// Skip Metric, Time and "encap", get encap addr
Encap = strtok_s(NULL, " \n", &Context);
Encap = strtok_s(NULL, " \n", &Context);
Encap = strtok_s(NULL, " \n", &Context);
Encap = strtok_s(NULL, " \n", &Context);
if (Encap == 0) continue;
IPGateway = inet_addr(Encap);
if (IPGateway == INADDR_NONE) continue;
}
Route = LookupRoute(IPAddr, IPMask, TRUE, &Found);
if (!Found)
{
// Add if possible
if (Route != NULL)
{
Route->NETWORK = IPAddr;
Route->SUBNET = IPMask;
if (Type[0] == 'T')
Route->Encap = IPGateway;
else
Route->GATEWAY = IPGateway;
Route->TYPE = Type[0];
Route->RIPTIMOUT = 900;
// Link to ARP
if (Type[0] != 'T')
Route->ARP = LookupARP(Route->GATEWAY, FALSE, &Found);
}
}
}
fclose(file);
return;
}
VOID SaveIPRoutes ()
{
PROUTEENTRY Route;
int i;
FILE * file;
char Line[128];
if ((file = fopen(IPRFN, "w")) == NULL)
return;
// Save Gateway MAC
sprintf(Line,"ENCAPMAC %02x:%02x:%02x:%02x:%02x:%02x\n",
RouterMac[0],
RouterMac[1],
RouterMac[2],
RouterMac[3],
RouterMac[4],
RouterMac[5]);
fputs(Line, file);
for (i=0; i < NumberofRoutes; i++)
{
Route = RouteRecords[i];
if (!Route->LOCKED)
WriteIPRLine(Route, file);
}
fclose(file);
return ;
}
VOID WriteIPRLine(PROUTEENTRY RouteRecord, FILE * file)
{
int Len;
char Net[20];
char Nexthop[20];
char Encap[20];
char Line[100];
unsigned char work[4];
memcpy(work, &RouteRecord->NETWORK, 4);
sprintf(Net, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
memcpy(work, &RouteRecord->GATEWAY, 4);
sprintf(Nexthop, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
memcpy(work, &RouteRecord->Encap, 4);
sprintf(Encap, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
if (RouteRecord->TYPE == 'T')
Len = sprintf(Line, "%s/%d %s %c %d %d encap %s\n",
Net, CountBits(RouteRecord->SUBNET),
Nexthop, RouteRecord->TYPE,
RouteRecord->METRIC, RouteRecord->RIPTIMOUT, Encap);
else
Len = sprintf(Line, "%s/%d %s %c %d %d\n",
Net, CountBits(RouteRecord->SUBNET),
Nexthop, RouteRecord->TYPE,
RouteRecord->METRIC, RouteRecord->RIPTIMOUT);
fputs(Line, file);
return;
}
int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len)
{
struct _IPMSG PH = {0};
IPptr->IPCHECKSUM = 0;
PH.IPPROTOCOL = 6;
PH.IPLENGTH = htons(Len);
memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4);
memcpy(&PH.IPDEST, &IPptr->IPDEST, 4);
TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20);
TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len);
// No need to do IP checksum as RouteIPMessage doesit
// CHECKSUM IT
// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20);
RouteIPMsg(IPptr);
return 0;
}
int CheckSumAndSendUDP(PIPMSG IPptr, PUDPMSG UDPmsg, USHORT Len)
{
struct _IPMSG PH = {0};
IPptr->IPCHECKSUM = 0;
PH.IPPROTOCOL = 17;
PH.IPLENGTH = htons(Len);
memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4);
memcpy(&PH.IPDEST, &IPptr->IPDEST, 4);
UDPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20);
UDPmsg->CHECKSUM = Generate_CHECKSUM(UDPmsg, Len);
// No need to do IP checksum as ROuteIPMessage doesit
// CHECKSUM IT
// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20);
RouteIPMsg(IPptr);
return 0;
}
VOID RecalcTCPChecksum(PIPMSG IPptr)
{
PTCPMSG TCPptr = (PTCPMSG)&IPptr->Data;
PHEADER PH = {0};
USHORT Len = ntohs(IPptr->IPLENGTH);
Len-=20;
PH.IPPROTOCOL = 6;
PH.LENGTH = htons(Len);
memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4);
memcpy(&PH.IPDEST, &IPptr->IPDEST, 4);
TCPptr->CHECKSUM = ~Generate_CHECKSUM(&PH, 12);
TCPptr->CHECKSUM = Generate_CHECKSUM(TCPptr, Len);
}
VOID RecalcUDPChecksum(PIPMSG IPptr)
{
PUDPMSG UDPmsg = (PUDPMSG)&IPptr->Data;
PHEADER PH = {0};
USHORT Len = ntohs(IPptr->IPLENGTH);
Len-=20;
PH.IPPROTOCOL = 17;
PH.LENGTH = htons(Len);
memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4);
memcpy(&PH.IPDEST, &IPptr->IPDEST, 4);
UDPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 12);
UDPmsg->CHECKSUM = Generate_CHECKSUM(UDPmsg, Len);
}
#ifndef WIN32
#ifndef MACBPQ
#ifndef FREEBSD
#include <net/if.h>
#include <linux/if_tun.h>
/* buffer for reading from tun/tap interface, must be >= 1500 */
#define BUFSIZE 2000
/**************************************************************************
* tun_alloc: allocates or reconnects to a tun/tap device. The caller *
* must reserve enough space in *dev. *
**************************************************************************/
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
if( (fd = open(clonedev , O_RDWR)) < 0 ) {
perror("Opening /dev/net/tun");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
perror("ioctl(TUNSETIFF)");
close(fd);
return err;
}
strcpy(dev, ifr.ifr_name);
return fd;
}
#include <net/if_arp.h>
#include <net/route.h>
void OpenTAP()
{
int flags = IFF_TAP;
char if_name[IFNAMSIZ] = "LinBPQTAP";
struct arpreq arpreq;
int s;
struct ifreq ifr;
int sockfd;
struct rtentry rm;
int err;
int n;
uint nread, nwrite, plength;
char buffer[BUFSIZE];
int optval = 1;
struct nat_table_entry * NAT = NULL;
int index;
/* initialize tun/tap interface */
if ((tap_fd = tun_alloc(if_name, flags | IFF_NO_PI)) < 0 )
{
printf("Error connecting to tun/tap interface %s!\n", if_name);
tap_fd = 0;
return;
}
printf("Successfully connected to TAP interface %s\n", if_name);
ioctl(tap_fd, FIONBIO, &optval);
// Bring it up
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror ("Socket");
return;
}
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
ifr.ifr_flags |= IFF_UP;
if ((err = ioctl(sockfd, SIOCSIFFLAGS, &ifr)) < 0)
{
perror("SIOCSIFFLAGS");
printf("SIOCSIFFLAGS failed , ret->%d\n",err);
return;
}
// Fix from github user isavitsky
/*
* After some research I found that on most of my
* systems, including Raspberry Pi IV, a slight delay
* is needed before considering the TAP device
* up and running. Otherwise the interface structures
* do not initialise properly and later in the code
* around the line 4700 when we initialise our ARP
* structure:
*
* memcpy(Arp->HWADDR, xbuffer.ifr_hwaddr.sa_data, 6);
*
* the MAC address is getting filled in with random
* value which makes the communication via our TAP
* device using the configured IPADDR virtually
* impossible.
*
*/
Debugprintf("Waiting for the TAP to become UP and RUNNING");
for (int i=10; i>0; i--)
{
Sleep(10);
if ((err = ioctl(sockfd, SIOCGIFFLAGS, &ifr)) < 0)
{
perror("SIOCGIFFLAGS");
return;
}
if((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING))
{
Debugprintf("TAP is UP and RUNNING");
break;
}
else if (i == 1)
{
Debugprintf("TAP is still not UP and RUNNING");
return;
}
}
printf("TAP brought up\n");
// Set MTU to 256
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
ifr.ifr_mtu = 256;
if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0)
perror("Set MTU");
else
printf("TAP MTU set to 256\n");
if (NoDefaultRoute == FALSE)
{
// Add a Route for 44/9 and 44.128/10 via TAP
memset(&rm, 0, sizeof(rm));
(( struct sockaddr_in*)&rm.rt_dst)->sin_family = AF_INET;
(( struct sockaddr_in*)&rm.rt_dst)->sin_addr.s_addr = inet_addr("44.0.0.0");
(( struct sockaddr_in*)&rm.rt_dst)->sin_port = 0;
(( struct sockaddr_in*)&rm.rt_genmask)->sin_family = AF_INET;
(( struct sockaddr_in*)&rm.rt_genmask)->sin_addr.s_addr = inet_addr("255.128.0.0");
(( struct sockaddr_in*)&rm.rt_genmask)->sin_port = 0;
(( struct sockaddr_in*)&rm.rt_gateway)->sin_family = AF_INET;
(( struct sockaddr_in*)&rm.rt_gateway)->sin_addr.s_addr = 0;
(( struct sockaddr_in*)&rm.rt_gateway)->sin_port = 0;
rm.rt_dev = if_name;
rm.rt_flags = RTF_UP; // | RTF_GATEWAY;
if ((err = ioctl(sockfd, SIOCADDRT, &rm)) < 0)
{
perror("SIOCADDRT");
printf("SIOCADDRT failed , ret->%d\n",err);
return;
}
printf("Route to 44/9 added via LinBPQTAP\n");
memset(&rm, 0, sizeof(rm));
(( struct sockaddr_in*)&rm.rt_dst)->sin_family = AF_INET;
(( struct sockaddr_in*)&rm.rt_dst)->sin_addr.s_addr = inet_addr("44.128.0.0");
(( struct sockaddr_in*)&rm.rt_dst)->sin_port = 0;
(( struct sockaddr_in*)&rm.rt_genmask)->sin_family = AF_INET;
(( struct sockaddr_in*)&rm.rt_genmask)->sin_addr.s_addr = inet_addr("255.192.0.0");
(( struct sockaddr_in*)&rm.rt_genmask)->sin_port = 0;
(( struct sockaddr_in*)&rm.rt_gateway)->sin_family = AF_INET;
(( struct sockaddr_in*)&rm.rt_gateway)->sin_addr.s_addr = 0;
(( struct sockaddr_in*)&rm.rt_gateway)->sin_port = 0;
rm.rt_dev = if_name;
rm.rt_flags = RTF_UP; // | RTF_GATEWAY;
if ((err = ioctl(sockfd, SIOCADDRT, &rm)) < 0)
{
perror("SIOCADDRT");
printf("SIOCADDRT failed , ret->%d\n",err);
return;
}
printf("Route to 44.128/10 added via LinBPQTAP\n");
}
// Set up ARP entries for any virtual hosts (eg jnos)
bzero((caddr_t)&arpreq, sizeof(arpreq));
for (index=0; index < nat_table_len; index++)
{
struct sockaddr_in *psin;
psin = (struct sockaddr_in *)&arpreq.arp_pa;
NAT = &nat_table[index];
if (NAT->ThisHost && OurIPAddr != NAT->origipaddr)
{
printf("Adding ARP for %s\n", FormatIP(NAT->mappedipaddr));
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = NAT->mappedipaddr;
arpreq.arp_flags = ATF_PERM | ATF_COM | ATF_PUBL;
strcpy(arpreq.arp_dev, "LinBPQTAP");
if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0)
perror("ARP IOCTL");
}
}
// Create LinBPQ ARP entry for real IP Address
// Get Address
struct ifreq xbuffer;
memset(&xbuffer, 0x00, sizeof(xbuffer));
strcpy(xbuffer.ifr_name, "LinBPQTAP");
ioctl(sockfd, SIOCGIFHWADDR, &xbuffer);
PARPDATA Arp;
PROUTEENTRY Route;
BOOL Found;
Arp = LookupARP(HostNATAddr, TRUE, &Found);
if (Arp != NULL)
{
Arp->IPADDR = HostNATAddr;
memcpy(Arp->HWADDR, xbuffer.ifr_hwaddr.sa_data, 6);
Arp->ARPTYPE = 'E';
Arp->ARPINTERFACE = 255;
Arp->ARPVALID = TRUE;
Arp->ARPTIMER = 0;
Arp->LOCKED = TRUE;
// Also add to Routes
AddToRoutes(Arp, HostNATAddr, 'E');
Arp->ARPROUTE->LOCKED = TRUE;
}
close(sockfd);
}
#endif
#endif
#endif
extern struct DATAMESSAGE * REPLYBUFFER;
char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...);
VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
// Send ICMP Echo Request
uint32_t PingAddr;
UCHAR Msg[120] = "";
PIPMSG IPptr = (PIPMSG)&Msg[40]; // Space for frame header (not used)
PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data;
time_t NOW = time(NULL);
Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
if (IPRequired == FALSE)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
PingAddr = inet_addr(CmdTail);
if (PingAddr == INADDR_NONE)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Address\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
// We keep the message pretty short in case running over RF
// Send "*BPQPINGID*, then Timestamp (Secs), then padding to 20 bytes
// So we can use same address for host we examine ping responses for
// the pattern, and intercept ours
IPptr->VERLEN = 0x45;
IPptr->IPDEST.addr = PingAddr;
IPptr->IPSOURCE.addr = OurIPAddr;
IPptr->IPPROTOCOL = ICMP;
IPptr->IPTTL = IPTTL;
IPptr->FRAGWORD = 0;
IPptr->IPLENGTH = htons(48); // IP Header ICMP Header 20 Data
ICMPptr->ICMPTYPE = 8;
ICMPptr->ICMPID = Session->CIRCUITINDEX;
strcpy(ICMPptr->ICMPData, "*BPQPINGID*"); // 12 including null
memcpy(&ICMPptr->ICMPData[12], &NOW, 4);
ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 28);
if (RouteIPMsg(IPptr))
Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r");
else
Bufferptr = Cmdprintf(Session, Bufferptr, "No Route to Host\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
// DISPLAY IP Gateway ARP status or Clear
int i;
PARPDATA ARPRecord, Arp;
int SSID, j, n;
char Mac[128];
char Call[7];
char IP[20];
unsigned char work[4];
Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
if (IPRequired == FALSE)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
if (memcmp(CmdTail, "CLEAR ", 6) == 0)
{
int n = NumberofARPEntries;
int rec = 0;
for (i=0; i < n; i++)
{
Arp = ARPRecords[rec];
if (Arp->LOCKED)
rec++;
else
RemoveARP(Arp);
}
Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
SaveARP();
return;
}
for (i=0; i < NumberofARPEntries; i++)
{
ARPRecord = ARPRecords[i];
// if (ARPRecord->ARPVALID)
{
memcpy(work, &ARPRecord->IPADDR, 4);
sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
if(ARPRecord->ARPINTERFACE == 255) // Ethernet
{
sprintf(Mac," %02x:%02x:%02x:%02x:%02x:%02x",
ARPRecord->HWADDR[0],
ARPRecord->HWADDR[1],
ARPRecord->HWADDR[2],
ARPRecord->HWADDR[3],
ARPRecord->HWADDR[4],
ARPRecord->HWADDR[5]);
}
else
{
UCHAR * AXCall = &ARPRecord->HWADDR[0];
n = 0;
while (AXCall[0])
{
for (j=0; j< 6; j++)
{
Call[j] = AXCall[j]/2;
if (Call[j] == 32) Call[j] = 0;
}
Call[j] = 0;
SSID = (AXCall[6] & 31)/2;
if (SSID)
n += sprintf(&Mac[n], " %s-%d", Call, SSID);
else
n += sprintf(&Mac[n], " %s", Call);
AXCall += 7;
}
}
Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s %d %c %d %s\r",
IP, Mac, ARPRecord->ARPINTERFACE, ARPRecord->ARPTYPE,
(int)ARPRecord->ARPTIMER, ARPRecord->LOCKED?"Locked":"");
}
}
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}
VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
// DISPLAY IP Gateway ARP status or Clear
struct nat_table_entry * NAT = NULL;
int index;
char From[20];
char To[20];
Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
if (IPRequired == FALSE)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
for (index=0; index < nat_table_len; index++)
{
NAT = &nat_table[index];
strcpy(From, FormatIP(NAT->origipaddr));
strcpy(To, FormatIP(NAT->mappedipaddr));
#ifdef LINBPQ
Bufferptr = Cmdprintf(Session, Bufferptr, "%s to %s %s\r", From, To,
NAT->ThisHost?"via TAP":"");
#else
Bufferptr = Cmdprintf(Session, Bufferptr, "%s to %s\r", From, To);
#endif
}
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}
int CountBits64(uint64_t in)
{
int n = 0;
while (in)
{
if (in & 1) n ++;
in >>=1;
}
return n;
}
int CountBits(uint32_t in)
{
int n = 0;
while (in)
{
if (in & 1) n ++;
in >>=1;
}
return n;
}
VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
// DISPLAY IP Gateway ARP status or Clear
int i;
PROUTEENTRY RouteRecord;
char Net[20];
char Nexthop[20];
char Encap[20];
char *Context;
char Reply[128];
char UCReply[128];
unsigned char work[4];
if (IPRequired == FALSE)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "\rIP Gateway is not enabled\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
Bufferptr = Cmdprintf(Session, Bufferptr, "%d Entries\r", NumberofRoutes);
if (NumberofRoutes)
qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareRoutes);
for (i=0; i < NumberofRoutes; i++)
{
RouteRecord = RouteRecords[i];
// if (RouteRecord->ARPVALID)
{
memcpy(work, &RouteRecord->NETWORK, 4);
sprintf(Net, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
memcpy(work, &RouteRecord->GATEWAY, 4);
sprintf(Nexthop, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
memcpy(work, &RouteRecord->Encap, 4);
sprintf(Encap, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);
if (RouteRecord->TYPE == 'T')
sprintf(Reply, "%s/%d %d %c %d %d encap %s\r",
Net, CountBits(RouteRecord->SUBNET),
RouteRecord->FRAMECOUNT, RouteRecord->TYPE,
RouteRecord->METRIC, RouteRecord->RIPTIMOUT, Encap);
else
sprintf(Reply, "%s/%d %d %s %c %d %d %s\r",
Net, CountBits(RouteRecord->SUBNET),
RouteRecord->FRAMECOUNT, Nexthop, RouteRecord->TYPE,
RouteRecord->METRIC, RouteRecord->RIPTIMOUT,
RouteRecord->LOCKED?"Locked":"");
}
// Treat any parameter as a "Find Filter"
CmdTail = strtok_s(CmdTail, " ", &Context);
strcpy(UCReply, Reply);
_strupr(UCReply);
if (CmdTail && CmdTail[0] && strstr(UCReply, CmdTail) == 0)
continue;
Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Reply);
}
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
if (NumberofRoutes)
qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); // Back to Maks order
}
// SNMP Support Code. Pretty limited - basically just for MRTG
/*
Primitive ASN.1 Types Identifier in hex
INTEGER 02
BIT STRING 03
OCTET STRING 04
NULL 05
OBJECT IDENTIFIER 06
Constructed ASN.1 type Identifier in hex
SEQUENCE 30
Primitive SNMP application types Identifier in hex
IpAddress 40
Opaque 44
NsapAddress 45
Counter64 (available only in SNMPv2) 46
Uinteger32 (available only in SNMPv2) 47
Context-specific types within an SNMP Message Identifier in hex
GetRequest-PDU A0
GetNextRequestPUD A1
GetResponse-PDU (Response-PDU in SNMPv 2) A2
SetRequest-PDU A3
Trap-PDU (obsolete in SNMPv 2) A4
GetBulkRequest-PDU (added in SNMPv 2) A5
InformRequest-PDU (added in SNMPv 2) A6
SNMPv2-Trap-PDU (added in SNMPv 2) A7
*/
#define Counter32 0x41
#define Gauge32 0x42
#define TimeTicks 0x43
UCHAR ifInOctets[] = {'+',6,1,2,1,2,2,1,10};
UCHAR ifOutOctets[] = {'+',6,1,2,1,2,2,1,16};
int ifInOctetsLen = 9; // Not Inc Port
int ifOutOctetsLen = 9; // Not Inc Port
UCHAR sysUpTime[] = {'+', 6,1,2,1,1,3,0};
int sysUpTimeLen = 8;
UCHAR sysName[] = {'+', 6,1,2,1,1,5,0};
int sysNameLen = 8;
extern time_t TimeLoaded;
int InOctets[64] = {0};
int OutOctets[64] = {0};
// ASN PDUs have to be constructed backwards, as each header included a length
// This code assumes we have enough space in front of the buffer.
int ASNGetInt(UCHAR * Msg, int Len)
{
int Val = 0;
while(Len)
{
Val = (Val << 8) + *(Msg++);
Len --;
}
return Val;
}
int ASNPutInt(UCHAR * Buffer, int Offset, unsigned int Val, int Type)
{
int Len = 0;
// Encode in minimum space. But servers seem to sign-extend top byte, so if top bit set add another zero;
// I think zero is sent as a zero length field
if (Val == 0)
{
// return zero as 01 00, not zero length string
Buffer[--Offset] = 0; // Val
Buffer[--Offset] = 1; // Len
Buffer[--Offset] = Type;
return 3;
}
while(Val)
{
Buffer[--Offset] = Val & 255; // Value
Val = Val >> 8;
Len++;
}
if (Len < 4 && (Buffer[Offset] & 0x80)) // Negative
{
Buffer[--Offset] = 0;
Len ++;
}
Buffer[--Offset] = Len; // Len
Buffer[--Offset] = Type;
return Len + 2;
}
int AddHeader(UCHAR * Buffer, int Offset, UCHAR Type, int Length)
{
Buffer[Offset - 2] = Type;
Buffer[Offset - 1] = Length;
return 2;
}
int BuildReply(UCHAR * Buffer, int Offset, UCHAR * OID, int OIDLen, UCHAR * Value, int ReqID)
{
int IDLen;
int ValLen = Value[1] + 2;
// Value is pre-encoded = type, len, data
// Contruct the Varbindings. Sequence OID Value
Offset -= ValLen;
memcpy(&Buffer[Offset], Value, ValLen);
Offset -= OIDLen;
memcpy(&Buffer[Offset], OID, OIDLen);
Buffer[--Offset] = OIDLen;
Buffer[--Offset] = 6; // OID Type
Buffer[--Offset] = OIDLen + ValLen + 2;
Buffer[--Offset] = 48; // Sequence
Buffer[--Offset] = OIDLen + ValLen + 4;
Buffer[--Offset] = 48; // Sequence
// Add the error fields (two zero ints
Buffer[--Offset] = 0; // Value
Buffer[--Offset] = 1; // Len
Buffer[--Offset] = 2; // Int
Buffer[--Offset] = 0; // Value
Buffer[--Offset] = 1; // Len
Buffer[--Offset] = 2; // Int
// ID
IDLen = ASNPutInt(Buffer, Offset, ReqID, 2);
Offset -= IDLen;
// PDU Type
Buffer[--Offset] = OIDLen + ValLen + 12 + IDLen;
Buffer[--Offset] = 0xA2; // Len
return OIDLen + ValLen + 14 + IDLen;
}
// snmpget -v1 -c jnos [ve4klm.ampr.org | www.langelaar.net] 1.3.6.1.2.1.2.2.1.16.5
int ProcessSNMPPayload(UCHAR * Msg, int Len, UCHAR * Reply, int * OffPtr)
{
char Community[256];
UCHAR OID[256];
int OIDLen;
int Type;
int Length, ComLen;
int IntVal;
int ReqID;
int RequestType;
// ASN 1 Encoding - Type, Len, Data
while (Len > 0)
{
Type = *(Msg++);
Length = *(Msg++);
// First should be a Sequence
if (Type != 0x30)
return 0;
Len -= 2;
Type = *(Msg++);
Length = *(Msg++);
IntVal = *(Msg++);
// Should be Integer - SNMP Version - We support V1, identified by zero
if (Type != 2 || Length != 1 || IntVal != 0)
return 0;
Len -= 3;
Type = *(Msg++);
ComLen = *(Msg++);
// Should Be String (community)
if (Type != 4)
return 0;
memcpy(Community, Msg, ComLen);
Community[ComLen] = 0;
Len -=2; // Header
Len -= ComLen;
Msg += (ComLen);
// A Complex Data Types - GetRequest PDU etc
RequestType = *(Msg);
*(Msg++) = 0xA2;
Length = *(Msg++);
Len -= 2;
// A 2 byte value requestid
// Next is integer requestid
Type = *(Msg++);
Length = *(Msg++);
if (Type != 2)
return 0;
ReqID = ASNGetInt(Msg, Length);
Len -= (2 + Length);
Msg += Length;
// Two more Integers - error status, error index
Type = *(Msg++);
Length = *(Msg++);
if (Type != 2)
return 0;
ASNGetInt(Msg, Length);
Len -= (2 + Length);
Msg += Length;
Type = *(Msg++);
Length = *(Msg++);
if (Type != 2)
return 0;
ASNGetInt(Msg, Length);
Len -= (2 + Length);
Msg += Length;
// Two Variable-bindings structs - another Sequence
Type = *(Msg++);
Length = *(Msg++);
Len -= 2;
if (Type != 0x30)
return 0;
Type = *(Msg++);
Length = *(Msg++);
Len -= 2;
if (Type != 0x30)
return 0;
// Next is OID
Type = *(Msg++);
Length = *(Msg++);
if (Type != 6) // Object ID
return 0;
memcpy(OID, Msg, Length);
OID[Length] = 0;
OIDLen = Length;
Len -=2; // Header
Len -= Length;
Msg += Length;
// Should Just have a null value left
Type = *(Msg++);
Length = *(Msg++);
if (Type != 5 || Length != 0)
return 0;
Len -=2; // Header
// Should be nothing left
}
if (RequestType == 160)
{
int Offset = 255;
int PDULen = 0;
char Value[256];
int ValLen;
// Only Support Get
if (memcmp(OID, sysName, sysNameLen) == 0)
{
ValLen = (int)strlen(MYNODECALL);;
Value[0] = 4; // String
Value[1] = ValLen;
memcpy(&Value[2], MYNODECALL, ValLen);
PDULen = BuildReply(Reply, Offset, sysName, sysNameLen, Value, ReqID);
}
else if (memcmp(OID, sysUpTime, sysUpTimeLen) == 0)
{
int ValOffset = 10;
ValLen = ASNPutInt(Value, ValOffset, (int)((time(NULL) - TimeLoaded) * 100), TimeTicks);
ValOffset -= ValLen;
PDULen = BuildReply(Reply, Offset, sysUpTime, sysUpTimeLen, &Value[ValOffset], ReqID);
}
else if (memcmp(OID, ifOutOctets, ifOutOctetsLen) == 0)
{
int Port = OID[9];
int ValOffset = 10;
ValLen = ASNPutInt(Value, ValOffset, OutOctets[Port], Counter32);
ValOffset -= ValLen;
PDULen = BuildReply(Reply, Offset, OID, OIDLen, &Value[ValOffset], ReqID);
}
else if (memcmp(OID, ifInOctets, ifInOctetsLen) == 0)
{
int Port = OID[9];
int ValOffset = 10;
ValLen = ASNPutInt(Value, ValOffset, InOctets[Port], Counter32);
ValOffset -= ValLen;
PDULen = BuildReply(Reply, Offset, OID, OIDLen, &Value[ValOffset], ReqID);
}
else
return 0;
Offset -= PDULen;
Offset -= ComLen;
memcpy(&Reply[Offset], Community, ComLen);
Reply[--Offset] = ComLen;
Reply[--Offset] = 4;
// Version
Reply[--Offset] = 0;
Reply[--Offset] = 1;
Reply[--Offset] = 2;
Reply[--Offset] = PDULen + ComLen + 5;
Reply[--Offset] = 48;
*OffPtr = Offset;
return PDULen + ComLen + 7;
}
return 0;
}
VOID ProcessSNMPMessage(PIPMSG IPptr)
{
int Len;
PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data;
UCHAR * Msg;
int Type;
int Length, ComLen;
int IntVal;
UCHAR Reply[256];
int PDULen, SendLen;
int Offset = 0;
Len = ntohs(IPptr->IPLENGTH);
Len-=20;
Check_Checksum(UDPptr, Len);
// 4 bytes version
// Null Terminated Community
Msg = (char *) UDPptr;
Msg += 8; // Over UDP Header
Len -= 8;
SendLen = ProcessSNMPPayload(Msg, Len, Reply, &Offset);
if (SendLen == 0)
return;
memcpy(UDPptr->UDPData, &Reply[Offset], SendLen);
// Swap Dest to Origin
IPptr->IPDEST = IPptr->IPSOURCE;
IPptr->IPSOURCE.addr = OurIPAddr;
UDPptr->DESTPORT = UDPptr->SOURCEPORT;
UDPptr->SOURCEPORT = htons(161);
SendLen += 8; // UDP Header
UDPptr->LENGTH = htons(SendLen);
IPptr->IPLENGTH = htons(SendLen + 20);
CheckSumAndSendUDP(IPptr, UDPptr, SendLen);
}