aboutsummaryrefslogtreecommitdiff
path: root/project/dclib/dclib-network-linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'project/dclib/dclib-network-linux.c')
-rw-r--r--project/dclib/dclib-network-linux.c461
1 files changed, 461 insertions, 0 deletions
diff --git a/project/dclib/dclib-network-linux.c b/project/dclib/dclib-network-linux.c
new file mode 100644
index 0000000..900714e
--- /dev/null
+++ b/project/dclib/dclib-network-linux.c
@@ -0,0 +1,461 @@
+
+/***************************************************************************
+ * *
+ * _____ ____ *
+ * | __ \ / __ \ _ _ _____ *
+ * | | \ \ / / \_\ | | | | _ \ *
+ * | | \ \| | | | | | |_| | *
+ * | | | || | | | | | ___/ *
+ * | | / /| | __ | | | | _ \ *
+ * | |__/ / \ \__/ / | |___| | |_| | *
+ * |_____/ \____/ |_____|_|_____/ *
+ * *
+ * Wiimms source code library *
+ * *
+ ***************************************************************************
+ * *
+ * Copyright (c) 2012-2020 by Dirk Clemens <wiimm@wiimm.de> *
+ * *
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
+ * *
+ ***************************************************************************/
+
+#define _GNU_SOURCE 1
+
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#include <sys/ioctl.h>
+
+#ifdef SYSTEM_LINUX
+ #include <linux/route.h>
+#endif
+
+#include "dclib-network.h"
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// SendRawUDP() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SendRawUDP
+(
+ // Capability CAP_NET_RAW needed (or effective user ID of 0).
+
+ int sock, // RAW socket to use,
+ // if -1: open and close private socket
+
+ ccp send_addr, // NULL or sender address:port
+ u32 send_ip4, // fall back IP4, if 'send_addr' empty
+ u16 send_port, // default sender port
+
+ ccp recv_addr, // NULL or receiver address:port
+ u32 recv_ip4, // fall back IP4, if 'recv_addr' empty
+ u16 recv_port, // default receiver port
+
+ const void *data, // data to send
+ uint size, // size of 'data'
+ uint log_mode // 0:silent, >0:print errors,
+ // >=0x10: hexdump data, 'lmode' bytes max
+)
+{
+ DASSERT( data || !size );
+
+
+ //--- get addresses
+
+ NetworkHost_t send_host;
+ if ( send_addr && *send_addr )
+ {
+ if (!ResolveHost(&send_host,true,send_addr,send_port,false,false))
+ {
+ if (log_mode)
+ ERROR1(ERR_ERROR,"Can't resolve sender host: %s\n",send_addr);
+ return ERR_ERROR;
+ }
+ }
+ else
+ {
+ send_host.sa.sin_family = AF_INET;
+ send_host.sa.sin_addr.s_addr = htonl(send_ip4);
+ send_host.sa.sin_port = htons(send_port);
+ }
+
+
+ NetworkHost_t recv_host;
+ if ( recv_addr && *recv_addr )
+ {
+ if (!ResolveHost(&recv_host,true,recv_addr,recv_port,false,false))
+ {
+ if (log_mode)
+ ERROR1(ERR_ERROR,"Can't resolve sender host: %s\n",recv_addr);
+ return ERR_ERROR;
+ }
+ }
+ else
+ {
+ recv_host.sa.sin_family = AF_INET;
+ recv_host.sa.sin_addr.s_addr = htonl(recv_ip4);
+ recv_host.sa.sin_port = htons(recv_port);
+ }
+
+ return SendRawUDPsa(sock,&send_host.sa,&recv_host.sa,data,size,log_mode);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError SendRawUDPsa
+(
+ // Capability CAP_NET_RAW needed (or effective user ID of 0).
+
+ int sock, // RAW socket to use,
+ // if -1: open and close private socket
+
+ struct sockaddr_in *sa_send, // sockaddr of sender
+ struct sockaddr_in *sa_recv, // sockaddr of receiver
+
+ const void *data, // data to send
+ uint size, // size of 'data'
+ uint log_mode // 0:silent, >0:print errors,
+ // >=0x10: hexdump data, 'lmode' bytes max
+)
+{
+ DASSERT(sa_send);
+ DASSERT(sa_recv);
+ DASSERT( data || !size );
+ if ( size > MAX_UDP_PACKET_DATA )
+ size = MAX_UDP_PACKET_DATA;
+
+ TRACE("Send %zu+%u bytes from %s to %s\n",
+ sizeof(ip4_head_t) + sizeof(udp_head_t), size,
+ PrintIP4sa(0,0,sa_send),
+ PrintIP4sa(0,0,sa_recv) );
+
+
+ //--- open socket
+
+ enumError err = ERR_OK;
+
+ const bool own_sock = sock == -1;
+ if (own_sock)
+ {
+ sock = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
+ if ( sock == -1 )
+ {
+ if (log_mode)
+ ERROR1(ERR_CANT_CREATE,"Can't create RAW socket.\n");
+ err = ERR_CANT_CREATE;
+ goto abort;
+ }
+
+ int on = 1;
+ setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
+ }
+
+
+ //--- setup packet
+
+ udp_packet_t pkt;
+ const uint total_len = SetupRawUDPsa(&pkt,sa_send,sa_recv,data,size);
+ if ( log_mode >= 0x10 )
+ HexDump16( stdwrn, 0, 0, &pkt,
+ total_len < log_mode ? total_len : log_mode );
+
+
+ //--- send data
+
+ //HEXDUMP16(0,0,&pkt,total_len);
+ //HexDump16(stderr,0,0,&pkt,total_len);
+
+ int stat = sendto(sock,&pkt,total_len,0,(struct sockaddr*)sa_recv,sizeof(*sa_recv));
+ if ( stat == -1 )
+ {
+ ERROR1(ERR_ERROR,"Can't send to sock %d\n",sock);
+ if (log_mode)
+ ERROR1(ERR_WRITE_FAILED,"Can't send to socket %d\n",sock);
+ err = ERR_WRITE_FAILED;
+ goto abort;
+ }
+
+
+ //--- close socket and terminate
+
+ abort:
+ if ( own_sock && sock != -1 )
+ close(sock);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint SetupRawUDPsa
+(
+ // returns total packet len (mabe with limited 'size')
+
+ udp_packet_t *pkt, // paket header to setup (cleared and written)
+
+ struct sockaddr_in *sa_send, // sockaddr of sender
+ struct sockaddr_in *sa_recv, // sockaddr of receiver
+ const void *data, // data to send
+ uint size // size of 'data'
+)
+{
+ DASSERT(pkt);
+ DASSERT(sa_send);
+ DASSERT(sa_recv);
+ DASSERT( data || !size );
+ if ( size > MAX_UDP_PACKET_DATA )
+ size = MAX_UDP_PACKET_DATA;
+
+
+ //--- determine sender ip4
+
+ u32 sender_ip4 = sa_send->sin_addr.s_addr;
+ if (!sender_ip4)
+ {
+ RouteIP4_t rt;
+ if (FindGatewayIP4(&rt,sa_recv->sin_addr.s_addr))
+ sender_ip4 = GetIP4ByInterface(rt.iface);
+ }
+
+
+ //--- create data structure
+
+ static const u8 ip4_head[]
+ = { 0x45,0x00, 0x00,0x00, 0x12,0x34, 0x40,0x00, 0x40,0x11 };
+
+ const uint total_len = sizeof(ip4_head_t) + sizeof(udp_head_t) + size;
+
+ memset(pkt,0,sizeof(*pkt));
+ memcpy(&pkt->ip4,ip4_head,sizeof(ip4_head));
+ memcpy(pkt->data,data,size);
+
+ pkt->ip4.ip_src = sender_ip4;
+ pkt->ip4.ip_dest = sa_recv->sin_addr.s_addr;
+ pkt->ip4.total_len = htons(total_len);
+ pkt->ip4.checksum = CalcChecksumIP4(&pkt->ip4,sizeof(pkt->ip4));
+
+ pkt->udp.port_src = sa_send->sin_port;
+ pkt->udp.port_dest = sa_recv->sin_port;
+ pkt->udp.data_len = htons(size+8);
+ pkt->udp.checksum = CalcChecksumUDP(&pkt->ip4,&pkt->udp,data);
+
+
+ //--- terminate
+
+ //HEXDUMP16(0,0,&pkt,total_len);
+ //HexDump16(stderr,0,0,&pkt,total_len);
+
+ return total_len;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// Routing Support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool OpenRouteScanIP4
+(
+ // returns TRUE on success and FALSE if no element is found
+ RouteIP4_t *rt // valid data, will be initialized
+)
+{
+ DASSERT(rt);
+ memset(rt,0,sizeof(*rt));
+
+ rt->index = -1;
+ rt->col_iface = 0;
+ rt->col_dest = 1;
+ rt->col_mask = 7;
+ rt->col_gate = 2;
+ rt->col_flags = 3;
+
+ #ifdef __CYGWIN__
+ return false;
+ #else
+ rt->f = fopen("/proc/net/route","rb");
+ if (!rt->f)
+ return false;
+
+ char *ptr = fgets(rt->buf,sizeof(rt->buf),rt->f);
+ if (!ptr)
+ return false;
+
+ uint col;
+ for ( col = 0; *ptr; col++ )
+ {
+ while ( *ptr == ' ' || *ptr == '\t' )
+ ptr++;
+ if (!*ptr)
+ break;
+ char *beg = ptr;
+ while ( *ptr && *ptr != ' ' && *ptr != '\t' )
+ ptr++;
+ if (*ptr)
+ *ptr++ = 0;
+
+ if (!strcasecmp(beg,"Iface")) rt->col_iface = col;
+ else if (!strcasecmp(beg,"Destination"))rt->col_dest = col;
+ else if (!strcasecmp(beg,"Mask")) rt->col_mask = col;
+ else if (!strcasecmp(beg,"Gateway")) rt->col_gate = col;
+ else if (!strcasecmp(beg,"Flags")) rt->col_flags = col;
+ }
+
+ TRACE("f=%p, COLS: if=%d, dest=%d, mask=%d, gate=%d, flags=%d\n",
+ rt->f, rt->col_iface, rt->col_dest,
+ rt->col_mask, rt->col_gate, rt->col_flags );
+
+ return NextRouteScanIP4(rt);
+
+ #endif // !__CYGWIN__
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool NextRouteScanIP4
+(
+ // returns TRUE on success and FALSE if no more element is found
+ RouteIP4_t *rt // valid data
+)
+{
+ DASSERT(rt);
+
+ #ifdef __CYGWIN__
+ return false;
+ #else
+ char *ptr = fgets(rt->buf,sizeof(rt->buf),rt->f);
+ if (!ptr)
+ {
+ rt->index = -1;
+ return false;
+ }
+
+ uint col;
+ for ( col = 0; *ptr; col++ )
+ {
+ while ( *ptr == ' ' || *ptr == '\t' )
+ ptr++;
+ if (!*ptr)
+ break;
+ char *beg = ptr;
+ while ( *ptr && *ptr != ' ' && *ptr != '\t' )
+ ptr++;
+ if (*ptr)
+ *ptr++ = 0;
+
+ if ( col == rt->col_iface ) rt->iface = beg;
+ else if ( col==rt->col_dest ) rt->dest = strtoul(beg,0,16);
+ else if ( col==rt->col_mask ) rt->mask = strtoul(beg,0,16);
+ else if ( col==rt->col_gate ) rt->gate = strtoul(beg,0,16);
+ else if ( col==rt->col_flags ) rt->flags = strtoul(beg,0,16);
+ }
+
+ return true;
+ #endif // !__CYGWIN__
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CloseRouteScanIP4
+(
+ RouteIP4_t *rt // valid data
+)
+{
+ #ifndef __CYGWIN__
+ DASSERT(rt);
+ if (rt->f)
+ {
+ fclose(rt->f);
+ rt->f = 0;
+ }
+ #endif // !__CYGWIN__
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool FindGatewayIP4
+(
+ // returns TRUE if gateway found
+ RouteIP4_t *rt, // valid data, will be initialized
+ u32 ip4 // search gateway for this address (network byte order)
+)
+{
+ DASSERT(rt);
+
+ bool found;
+ for ( found = OpenRouteScanIP4(rt); found; found = NextRouteScanIP4(rt) )
+ {
+ #ifdef __CYGWIN__
+ if ( rt->dest == ( ip4 & rt->mask ))
+ break;
+ #else
+ if ( !(rt->flags & RTF_UP) )
+ continue;
+
+ if ( rt->flags & RTF_HOST )
+ {
+ if ( rt->dest == ip4 )
+ break;
+ }
+ else if ( rt->dest == ( ip4 & rt->mask ))
+ break;
+ #endif
+ }
+ CloseRouteScanIP4(rt);
+ return found;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+u32 GetIP4ByInterface
+( // return 0 or IP4 in network byte order
+ ccp iface // interface name. If NULL: Use default gateway
+)
+{
+ RouteIP4_t rt;
+ if ( !iface || !*iface )
+ {
+ if (!FindGatewayIP4(&rt,0))
+ return 0;
+ iface = rt.iface;
+ }
+
+ int sock = socket(AF_INET,SOCK_DGRAM,0);
+ if ( sock == -1 )
+ return 0;
+
+ struct ifreq ifr;
+ memset(&ifr,0,sizeof(ifr));
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name,iface,sizeof(ifr.ifr_name)-1);
+ int stat = ioctl(sock,SIOCGIFADDR,&ifr);
+ close(sock);
+ if ( stat == -1 )
+ return 0;
+
+ struct sockaddr_in *sa = (struct sockaddr_in*)&ifr.ifr_addr;
+ return sa->sin_addr.s_addr;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+