/* 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 #include #include "CHeaders.h" #include "ipcode.h" #ifdef WIN32 #include #define read _read #define write _write #define close _close #include // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") #endif #include #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,¶m); 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,¶m); 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(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); Send_ETH(ÐARPREQMSG, 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(ÐARPREQMSG, 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(ÐARPREQMSG, 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(ÐARPREQMSG, 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(ÐARPREQMSG, 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 #include /* 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 #include 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); }