Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/SoftEtherVPN/SoftEtherVPN_Stable.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Cedar/BridgeUnix.c')
-rw-r--r--src/Cedar/BridgeUnix.c995
1 files changed, 983 insertions, 12 deletions
diff --git a/src/Cedar/BridgeUnix.c b/src/Cedar/BridgeUnix.c
index 321bb0aa..74d36d9a 100644
--- a/src/Cedar/BridgeUnix.c
+++ b/src/Cedar/BridgeUnix.c
@@ -3,9 +3,9 @@
//
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
//
-// Copyright (c) 2012-2014 Daiyuu Nobori.
-// Copyright (c) 2012-2014 SoftEther VPN Project, University of Tsukuba, Japan.
-// Copyright (c) 2012-2014 SoftEther Corporation.
+// Copyright (c) 2012-2016 Daiyuu Nobori.
+// Copyright (c) 2012-2016 SoftEther VPN Project, University of Tsukuba, Japan.
+// Copyright (c) 2012-2016 SoftEther Corporation.
//
// All Rights Reserved.
//
@@ -143,14 +143,38 @@
#include <ifaddrs.h>
#endif // BRIDGE_BPF
+#ifdef UNIX_LINUX
+struct my_tpacket_auxdata
+{
+ UINT tp_status;
+ UINT tp_len;
+ UINT tp_snaplen;
+ USHORT tp_mac;
+ USHORT tp_net;
+ USHORT tp_vlan_tci;
+ USHORT tp_vlan_tpid;
+};
+#define MY_TP_STATUS_VLAN_VALID (1 << 4)
+#define MY_TP_STATUS_VLAN_TPID_VALID (1 << 6)
+#define MY_PACKET_AUXDATA 8
+#endif // UNIX_LINUX
+
+static LIST *eth_offload_list = NULL;
+
// Initialize
void InitEth()
{
+ eth_offload_list = NewList(NULL);
}
// Free
void FreeEth()
{
+ if (eth_offload_list != NULL)
+ {
+ FreeStrList(eth_offload_list);
+ eth_offload_list = NULL;
+ }
}
// Check whether interface description string of Ethernet device can be retrieved in this system
@@ -350,7 +374,7 @@ TOKEN_LIST *GetEthListSolaris()
#ifdef UNIX_LINUX
// Get Ethernet device list on Linux
-TOKEN_LIST *GetEthListLinux()
+TOKEN_LIST *GetEthListLinux(bool enum_normal, bool enum_rawip)
{
struct ifreq ifr;
TOKEN_LIST *t;
@@ -359,6 +383,11 @@ TOKEN_LIST *GetEthListLinux()
LIST *o;
char name[MAX_SIZE];
+ if (enum_normal == false && enum_rawip)
+ {
+ return ParseToken(BRIDGE_SPECIAL_IPRAW_NAME, NULL);
+ }
+
o = NewListFast(CompareStr);
s = UnixEthOpenRawSocket();
@@ -407,7 +436,7 @@ TOKEN_LIST *GetEthListLinux()
Sort(o);
t = ZeroMalloc(sizeof(TOKEN_LIST));
- t->NumTokens = LIST_NUM(o);
+ t->NumTokens = LIST_NUM(o) + (enum_rawip ? 1 : 0);
t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
for (i = 0;i < LIST_NUM(o);i++)
@@ -416,6 +445,11 @@ TOKEN_LIST *GetEthListLinux()
t->Token[i] = name;
}
+ if (enum_rawip)
+ {
+ t->Token[t->NumTokens - 1] = CopyStr(BRIDGE_SPECIAL_IPRAW_NAME);
+ }
+
ReleaseList(o);
return t;
@@ -519,10 +553,14 @@ TOKEN_LIST *GetEthListBpf()
// Enumerate Ethernet devices
TOKEN_LIST *GetEthList()
{
+ return GetEthListEx(NULL, true, false);
+}
+TOKEN_LIST *GetEthListEx(UINT *total_num_including_hidden, bool enum_normal, bool enum_rawip)
+{
TOKEN_LIST *t = NULL;
#if defined(UNIX_LINUX)
- t = GetEthListLinux();
+ t = GetEthListLinux(enum_normal, enum_rawip);
#elif defined(UNIX_SOLARIS)
t = GetEthListSolaris();
#elif defined(BRIDGE_PCAP)
@@ -543,6 +581,7 @@ ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr)
struct sockaddr_ll addr;
int s;
int index;
+ bool aux_ok = false;
CANCEL *c;
// Validate arguments
if (name == NULL)
@@ -550,6 +589,11 @@ ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr)
return NULL;
}
+ if (StrCmpi(name, BRIDGE_SPECIAL_IPRAW_NAME) == 0)
+ {
+ return OpenEthLinuxIpRaw();
+ }
+
if (tapmode)
{
#ifndef NO_VLAN
@@ -624,12 +668,30 @@ ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr)
}
}
+ if (true)
+ {
+ int val = 1;
+ int ss_ret = setsockopt(s, SOL_PACKET, MY_PACKET_AUXDATA, &val, sizeof(val));
+
+ if (ss_ret < 0)
+ {
+ Debug("eth(%s): setsockopt: PACKET_AUXDATA failed.\n", name);
+ }
+ else
+ {
+ Debug("eth(%s): setsockopt: PACKET_AUXDATA ok.\n", name);
+ aux_ok = true;
+ }
+ }
+
e = ZeroMalloc(sizeof(ETH));
e->Name = CopyStr(name);
e->Title = CopyStr(name);
e->IfIndex = index;
e->Socket = s;
+ e->Linux_IsAuxDataSupported = aux_ok;
+
c = NewCancel();
UnixDeletePipe(c->pipe_read, c->pipe_write);
c->pipe_read = c->pipe_write = -1;
@@ -646,8 +708,27 @@ ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr)
if (tapmode == false)
{
- // Disable hardware offloading
- UnixDisableInterfaceOffload(name);
+ if (GetGlobalServerFlag(GSF_LOCALBRIDGE_NO_DISABLE_OFFLOAD) == false)
+ {
+ bool b = false;
+
+ LockList(eth_offload_list);
+ {
+ if (IsInListStr(eth_offload_list, name) == false)
+ {
+ b = true;
+
+ Add(eth_offload_list, CopyStr(name));
+ }
+ }
+ UnlockList(eth_offload_list);
+
+ if (b)
+ {
+ // Disable hardware offloading
+ UnixDisableInterfaceOffload(name);
+ }
+ }
}
return e;
@@ -670,6 +751,10 @@ UINT EthGetMtu(ETH *e)
{
return 0;
}
+ if (e->IsRawIpMode)
+ {
+ return 0;
+ }
if (e->CurrentMtu != 0)
{
@@ -740,6 +825,10 @@ bool EthSetMtu(ETH *e, UINT mtu)
{
return false;
}
+ if (e->IsRawIpMode)
+ {
+ return false;
+ }
if (mtu == 0)
{
@@ -803,6 +892,11 @@ bool EthIsChangeMtuSupported(ETH *e)
return false;
}
+ if (e->IsRawIpMode)
+ {
+ return false;
+ }
+
return true;
#else // defined(UNIX_LINUX) || defined(UNIX_BSD) || defined(UNIX_SOLARIS)
return false;
@@ -1464,6 +1558,13 @@ void CloseEth(ETH *e)
return;
}
+ if (e->IsRawIpMode)
+ {
+ CloseEthLinuxIpRaw(e);
+
+ return;
+ }
+
if (e->Tap != NULL)
{
#ifndef NO_VLAN
@@ -1566,16 +1667,30 @@ UINT EthGetPacket(ETH *e, void **data)
}
#ifdef UNIX_LINUX
+
UINT EthGetPacketLinux(ETH *e, void **data)
{
int s, ret;
UCHAR tmp[UNIX_ETH_TMP_BUFFER_SIZE];
+ struct iovec msg_iov;
+ struct msghdr msg_header;
+ struct cmsghdr *cmsg;
+ union
+ {
+ struct cmsghdr cmsg;
+ char buf[CMSG_SPACE(sizeof(struct my_tpacket_auxdata))];
+ } cmsg_buf;
// Validate arguments
if (e == NULL || data == NULL)
{
return INFINITE;
}
+ if (e->IsRawIpMode)
+ {
+ return EthGetPacketLinuxIpRaw(e, data);
+ }
+
if (e->Tap != NULL)
{
#ifndef NO_VLAN
@@ -1603,7 +1718,28 @@ UINT EthGetPacketLinux(ETH *e, void **data)
}
// Read
- ret = read(s, tmp, sizeof(tmp));
+ msg_iov.iov_base = tmp;
+ msg_iov.iov_len = sizeof(tmp);
+
+ msg_header.msg_name = NULL;
+ msg_header.msg_namelen = 0;
+ msg_header.msg_iov = &msg_iov;
+ msg_header.msg_iovlen = 1;
+ if (e->Linux_IsAuxDataSupported)
+ {
+ memset(&cmsg_buf, 0, sizeof(cmsg_buf));
+
+ msg_header.msg_control = &cmsg_buf;
+ msg_header.msg_controllen = sizeof(cmsg_buf);
+ }
+ else
+ {
+ msg_header.msg_control = NULL;
+ msg_header.msg_controllen = 0;
+ }
+ msg_header.msg_flags = 0;
+
+ ret = recvmsg(s, &msg_header, 0);
if (ret == 0 || (ret == -1 && errno == EAGAIN))
{
// No packet
@@ -1619,9 +1755,75 @@ UINT EthGetPacketLinux(ETH *e, void **data)
}
else
{
- // Success to read a packet
- *data = MallocFast(ret);
- Copy(*data, tmp, ret);
+ bool flag = false;
+ USHORT api_vlan_id = 0;
+ USHORT api_vlan_tpid = 0;
+
+ if (e->Linux_IsAuxDataSupported)
+ {
+ for (cmsg = CMSG_FIRSTHDR(&msg_header); cmsg; cmsg = CMSG_NXTHDR(&msg_header, cmsg))
+ {
+ struct my_tpacket_auxdata *aux;
+ UINT len;
+ USHORT vlan_tpid = 0x8100;
+ USHORT vlan_id = 0;
+
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct my_tpacket_auxdata)) ||
+ cmsg->cmsg_level != SOL_PACKET ||
+ cmsg->cmsg_type != MY_PACKET_AUXDATA)
+ {
+ continue;
+ }
+
+ aux = (struct my_tpacket_auxdata *)CMSG_DATA(cmsg);
+
+ if (aux != NULL)
+ {
+ if (aux->tp_vlan_tci != 0)
+ {
+ vlan_id = aux->tp_vlan_tci;
+ }
+ }
+
+ if (vlan_id != 0)
+ {
+ api_vlan_id = vlan_id;
+ api_vlan_tpid = vlan_tpid;
+ break;
+ }
+ }
+
+ if (api_vlan_id != 0 && api_vlan_tpid != 0)
+ {
+ // VLAN ID has been received with PACKET_AUXDATA.
+ // Insert the tag.
+ USHORT vlan_id_ne = Endian16(api_vlan_id);
+ USHORT vlan_tpid_ne = Endian16(api_vlan_tpid);
+
+ if (ret >= 14)
+ {
+ if (*((USHORT *)(tmp + 12)) != vlan_tpid_ne)
+ {
+ *data = MallocFast(ret + 4);
+ Copy(*data, tmp, 12);
+ Copy(((UCHAR *)*data) + 12, &vlan_tpid_ne, 2);
+ Copy(((UCHAR *)*data) + 14, &vlan_id_ne, 2);
+ Copy(((UCHAR *)*data) + 16, tmp + 12, ret - 12);
+
+ flag = true;
+
+ ret += 4;
+ }
+ }
+ }
+ }
+
+ // Success to read a packet (No VLAN)
+ if (flag == false)
+ {
+ *data = MallocFast(ret);
+ Copy(*data, tmp, ret);
+ }
return ret;
}
@@ -1791,6 +1993,11 @@ void EthPutPacket(ETH *e, void *data, UINT size)
{
return;
}
+ if (e->IsRawIpMode)
+ {
+ EthPutPacketLinuxIpRaw(e, data, size);
+ return;
+ }
if (size < 14 || size > MAX_PACKET_SIZE)
{
Free(data);
@@ -1824,16 +2031,780 @@ void EthPutPacket(ETH *e, void *data, UINT size)
Debug("EthPutPacket: ret:%d size:%d\n", ret, size);
}
#else // BRIDGE_PCAP
+#ifndef UNIX_LINUX
ret = write(s, data, size);
if (ret<0)
{
Debug("EthPutPacket: ret:%d errno:%d size:%d\n", ret, errno, size);
}
+#else // UNIX_LINUX
+ {
+ struct iovec msg_iov;
+ struct msghdr msg_header;
+
+ msg_iov.iov_base = data;
+ msg_iov.iov_len = size;
+
+ msg_header.msg_name = NULL;
+ msg_header.msg_namelen = 0;
+ msg_header.msg_iov = &msg_iov;
+ msg_header.msg_iovlen = 1;
+ msg_header.msg_control = NULL;
+ msg_header.msg_controllen = 0;
+ msg_header.msg_flags = 0;
+
+ ret = sendmsg(s, &msg_header, 0);
+
+ if (ret<0)
+ {
+ Debug("EthPutPacket: ret:%d errno:%d size:%d\n", ret, errno, size);
+ }
+ }
+#endif // UNIX_LINUX
#endif //BRIDGE_PCAP
Free(data);
}
+
+
+
+// Open ETH by using IP raw packets
+ETH *OpenEthLinuxIpRaw()
+{
+ ETH *e;
+
+ if (IsRawIpBridgeSupported() == false)
+ {
+ return NULL;
+ }
+
+ e = ZeroMalloc(sizeof(ETH));
+
+ e->IsRawIpMode = true;
+
+ e->RawTcp = NewUDP4(MAKE_SPECIAL_PORT(IPPROTO_TCP), NULL);
+ e->RawUdp = NewUDP4(MAKE_SPECIAL_PORT(IPPROTO_UDP), NULL);
+ e->RawIcmp = NewUDP4(MAKE_SPECIAL_PORT(IPPROTO_ICMP), NULL);
+
+ if (e->RawTcp == NULL || e->RawUdp == NULL || e->RawIcmp == NULL)
+ {
+ ReleaseSock(e->RawTcp);
+ ReleaseSock(e->RawUdp);
+ ReleaseSock(e->RawIcmp);
+
+ Free(e);
+ return NULL;
+ }
+
+ ClearSockDfBit(e->RawTcp);
+ ClearSockDfBit(e->RawUdp);
+ ClearSockDfBit(e->RawIcmp);
+
+ SetRawSockHeaderIncludeOption(e->RawTcp, true);
+ SetRawSockHeaderIncludeOption(e->RawUdp, true);
+ SetRawSockHeaderIncludeOption(e->RawIcmp, true);
+
+ e->Name = CopyStr(BRIDGE_SPECIAL_IPRAW_NAME);
+ e->Title = CopyStr(BRIDGE_SPECIAL_IPRAW_NAME);
+ e->Cancel = NewCancel();
+
+ UnixDeletePipe(e->Cancel->pipe_read, e->Cancel->pipe_write);
+ e->Cancel->pipe_read = e->Cancel->pipe_write = -1;
+
+ UnixSetSocketNonBlockingMode(e->RawTcp->socket, true);
+ UnixSetSocketNonBlockingMode(e->RawUdp->socket, true);
+ UnixSetSocketNonBlockingMode(e->RawIcmp->socket, true);
+
+ e->Cancel->SpecialFlag = true;
+ e->Cancel->pipe_read = e->RawTcp->socket;
+ e->Cancel->pipe_special_read2 = e->RawUdp->socket;
+ e->Cancel->pipe_special_read3 = e->RawIcmp->socket;
+
+ e->RawIpMyMacAddr[2] = 0x01;
+ e->RawIpMyMacAddr[5] = 0x01;
+
+ SetIP(&e->MyIP, 10, 171, 7, 253);
+ SetIP(&e->YourIP, 10, 171, 7, 254);
+
+ e->RawIpSendQueue = NewQueueFast();
+
+ e->RawIP_TmpBufferSize = 67000;
+ e->RawIP_TmpBuffer = Malloc(e->RawIP_TmpBufferSize);
+
+ return e;
+}
+
+// Close ETH by using IP raw packets
+void CloseEthLinuxIpRaw(ETH *e)
+{
+ if (e == NULL)
+ {
+ return;
+ }
+
+ while (true)
+ {
+ BUF *buf = GetNext(e->RawIpSendQueue);
+ if (buf == NULL)
+ {
+ break;
+ }
+
+ FreeBuf(buf);
+ }
+ ReleaseQueue(e->RawIpSendQueue);
+
+ Free(e->Name);
+ Free(e->Title);
+
+ ReleaseSock(e->RawTcp);
+ ReleaseSock(e->RawUdp);
+ ReleaseSock(e->RawIcmp);
+
+ ReleaseCancel(e->Cancel);
+
+ Free(e->RawIP_TmpBuffer);
+
+ Free(e);
+}
+
+// Receive an IP raw packet
+UINT EthGetPacketLinuxIpRaw(ETH *e, void **data)
+{
+ UINT r;
+ BUF *b;
+ // Validate arguments
+ if (e == NULL || data == NULL)
+ {
+ return INFINITE;
+ }
+ if (e->RawIp_HasError)
+ {
+ return INFINITE;
+ }
+
+ b = GetNext(e->RawIpSendQueue);
+ if (b != NULL)
+ {
+ UINT size;
+
+ *data = b->Buf;
+ size = b->Size;
+
+ Free(b);
+
+ return size;
+ }
+
+ r = EthGetPacketLinuxIpRawForSock(e, data, e->RawTcp, IP_PROTO_TCP);
+ if (r == 0)
+ {
+ r = EthGetPacketLinuxIpRawForSock(e, data, e->RawUdp, IP_PROTO_UDP);
+ if (r == 0)
+ {
+ r = EthGetPacketLinuxIpRawForSock(e, data, e->RawIcmp, IP_PROTO_ICMPV4);
+ }
+ }
+
+ if (r == INFINITE)
+ {
+ e->RawIp_HasError = true;
+ }
+
+ return r;
+}
+
+// Receive an IP raw packet for the specified socket
+UINT EthGetPacketLinuxIpRawForSock(ETH *e, void **data, SOCK *s, UINT proto)
+{
+ UCHAR *tmp;
+ UINT r;
+ IP src_addr;
+ UINT src_port;
+ UINT ret = INFINITE;
+ UCHAR *retbuf;
+ PKT *p;
+ bool ok = false;
+ // Validate arguments
+ if (e == NULL || data == NULL)
+ {
+ return INFINITE;
+ }
+
+ tmp = e->RawIP_TmpBuffer;
+
+LABEL_RETRY:
+ *data = NULL;
+
+ r = RecvFrom(s, &src_addr, &src_port, tmp, e->RawIP_TmpBufferSize);
+ if (r == SOCK_LATER)
+ {
+ return 0;
+ }
+
+ if (r == 0)
+ {
+ if (s->IgnoreRecvErr)
+ {
+ return 0;
+ }
+ else
+ {
+ return INFINITE;
+ }
+ }
+
+ ret = 14 + r;
+ retbuf = Malloc(ret);
+ *data = retbuf;
+
+ Copy(retbuf, e->RawIpYourMacAddr, 6);
+ Copy(retbuf + 6, e->RawIpMyMacAddr, 6);
+ retbuf[12] = 0x08;
+ retbuf[13] = 0x00;
+ Copy(retbuf + 14, tmp, r);
+
+ // Mangle packet
+ p = ParsePacket(retbuf, ret);
+ if (p != NULL)
+ {
+ if (p->TypeL3 == L3_IPV4)
+ {
+ IPV4_HEADER *ip;
+ IP original_dest_ip;
+
+ ip = p->L3.IPv4Header;
+
+ UINTToIP(&original_dest_ip, ip->DstIP);
+
+ if (IsZeroIP(&e->MyPhysicalIPForce) == false && CmpIpAddr(&e->MyPhysicalIPForce, &original_dest_ip) == 0 ||
+ (IsIPMyHost(&original_dest_ip) && IsLocalHostIP(&original_dest_ip) == false && IsHostIPAddress4(&original_dest_ip)))
+ {
+ if (IsZeroIP(&e->MyPhysicalIPForce) && CmpIpAddr(&e->MyPhysicalIP, &original_dest_ip) != 0)
+ {
+ // Update MyPhysicalIP
+ Copy(&e->MyPhysicalIP, &original_dest_ip, sizeof(IP));
+// Debug("e->MyPhysicalIP = %r\n", &e->MyPhysicalIP);
+ }
+
+ if (IsZeroIP(&e->MyPhysicalIPForce) == false)
+ {
+ Copy(&e->MyPhysicalIP, &e->MyPhysicalIPForce, sizeof(IP));
+ }
+
+ ip->DstIP = IPToUINT(&e->YourIP);
+ ip->Checksum = 0;
+ ip->Checksum = IpChecksum(ip, IPV4_GET_HEADER_LEN(ip) * 5);
+
+ if (p->TypeL4 == L4_TCP)
+ {
+ TCP_HEADER *tcp = p->L4.TCPHeader;
+/*
+ if (Endian16(tcp->SrcPort) == 80)
+ {
+ IP a, b;
+ UINTToIP(&a, ip->SrcIP);
+ UINTToIP(&b, ip->DstIP);
+ Debug("%r %r %u %u\n", &a, &b, Endian16(tcp->SrcPort), Endian16(tcp->DstPort));
+ }*/
+
+ ok = true;
+ }
+ else if (p->TypeL4 == L4_UDP)
+ {
+ UDP_HEADER *udp = p->L4.UDPHeader;
+
+ udp->Checksum = 0;
+
+ ok = true;
+ }
+ else if (p->TypeL4 == L4_ICMPV4)
+ {
+ ICMP_HEADER *icmp = p->L4.ICMPHeader;
+
+ if (icmp->Type == ICMP_TYPE_DESTINATION_UNREACHABLE || icmp->Type == ICMP_TYPE_TIME_EXCEEDED)
+ {
+ // Rewrite the Src IP of the IPv4 header of the ICMP response packet
+ UINT size = p->PacketSize - ((UCHAR *)icmp - (UCHAR *)p->PacketData);
+ UCHAR *data = (UCHAR *)icmp;
+ IPV4_HEADER *orig_ipv4 = (IPV4_HEADER *)(((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
+ UINT orig_ipv4_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
+
+ UINT orig_ipv4_header_size = GetIpHeaderSize((UCHAR *)orig_ipv4, orig_ipv4_size);
+
+ if (orig_ipv4_header_size >= sizeof(IPV4_HEADER) && orig_ipv4_size >= orig_ipv4_header_size)
+ {
+ if (orig_ipv4->Protocol == IP_PROTO_ICMPV4)
+ {
+ // Search the inner ICMP header
+ UINT inner_icmp_size = orig_ipv4_size - orig_ipv4_header_size;
+
+ if (inner_icmp_size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
+ {
+ ICMP_HEADER *inner_icmp = (ICMP_HEADER *)(((UCHAR *)data) +
+ sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO) + orig_ipv4_header_size);
+
+ if (inner_icmp->Type == ICMP_TYPE_ECHO_REQUEST)
+ {
+ ICMP_ECHO *inner_echo = (ICMP_ECHO *)(((UCHAR *)inner_icmp) + sizeof(ICMP_HEADER));
+
+ inner_icmp->Checksum = 0;
+ orig_ipv4->SrcIP = IPToUINT(&e->YourIP);
+ orig_ipv4->Checksum = 0;
+ orig_ipv4->Checksum = IpChecksum(orig_ipv4, orig_ipv4_header_size);
+
+ // Rewrite the outer ICMP header
+ if (true)
+ {
+ UCHAR *payload;
+ UINT payload_size;
+ ICMP_ECHO *echo;
+
+ // Echo Response
+ echo = (ICMP_ECHO *)(((UCHAR *)data) + sizeof(ICMP_HEADER));
+
+ if (size >= (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO)))
+ {
+ payload = ((UCHAR *)data) + sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO);
+ payload_size = size - (sizeof(ICMP_HEADER) + sizeof(ICMP_ECHO));
+
+ // Rewrite the header
+ icmp->Checksum = 0;
+ icmp->Checksum = IpChecksum(icmp, size);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ icmp->Checksum = 0;
+ icmp->Checksum = IpChecksum(icmp, p->PayloadSize);
+
+ ok = true;
+ }
+ else if (p->TypeL4 == L4_FRAGMENT)
+ {
+ ok = true;
+ }
+ }
+ }
+
+ FreePacket(p);
+ }
+
+ if (ok == false)
+ {
+ Free(*data);
+ *data = NULL;
+
+ goto LABEL_RETRY;
+ }
+
+ return ret;
+}
+
+// Send internal IP packet (insert into the send queue)
+void EthSendIpPacketInnerIpRaw(ETH *e, void *data, UINT size, USHORT protocol)
+{
+ BUF *b;
+ if (e == NULL || data == NULL || size == 0)
+ {
+ return;
+ }
+
+ if (e->RawIpSendQueue->num_item >= 1024)
+ {
+ return;
+ }
+
+ b = NewBuf();
+ WriteBuf(b, e->RawIpYourMacAddr, 6);
+ WriteBuf(b, e->RawIpMyMacAddr, 6);
+ WriteBufShort(b, protocol);
+ WriteBuf(b, data, size);
+ SeekBufToBegin(b);
+
+ InsertQueue(e->RawIpSendQueue, b);
+}
+
+// Process the packet internal if necessary
+bool EthProcessIpPacketInnerIpRaw(ETH *e, PKT *p)
+{
+ bool ret = false;
+ if (e == NULL || p == NULL)
+ {
+ return false;
+ }
+
+ if (p->TypeL3 == L3_ARPV4)
+ {
+ // ARP processing
+ ARPV4_HEADER *arp = p->L3.ARPv4Header;
+
+ if (Endian16(arp->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&
+ Endian16(arp->ProtocolType) == MAC_PROTO_IPV4 &&
+ arp->HardwareSize == 6 && arp->ProtocolType == 4)
+ {
+ if (IPToUINT(&e->MyIP) == arp->TargetIP)
+ {
+ if (Endian16(arp->Operation) == ARP_OPERATION_REQUEST)
+ {
+ ARPV4_HEADER r;
+
+ Zero(&r, sizeof(r));
+ r.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+ r.ProtocolType = Endian16(MAC_PROTO_IPV4);
+ r.HardwareSize = 6;
+ r.ProtocolSize = 4;
+ r.Operation = Endian16(ARP_OPERATION_RESPONSE);
+ Copy(r.SrcAddress, e->RawIpMyMacAddr, 6);
+ Copy(r.TargetAddress, arp->SrcAddress, 6);
+ r.SrcIP = IPToUINT(&e->MyIP);
+ r.TargetIP = arp->SrcIP;
+
+ EthSendIpPacketInnerIpRaw(e, &r, sizeof(ARPV4_HEADER), MAC_PROTO_ARPV4);
+ }
+ }
+ }
+ }
+ else if (p->TypeL3 == L3_IPV4 && p->TypeL4 == L4_UDP && p->TypeL7 == L7_DHCPV4)
+ {
+ // DHCP processing
+ DHCPV4_HEADER *dhcp;
+ UCHAR *data;
+ UINT size;
+ UINT dhcp_header_size;
+ UINT dhcp_data_offset;
+ UINT tran_id;
+ UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE);
+ bool ok;
+ DHCP_OPTION_LIST *opt;
+
+ dhcp = p->L7.DHCPv4Header;
+ tran_id = Endian32(dhcp->TransactionId);
+
+ // Get the DHCP data and size
+ dhcp_header_size = sizeof(DHCPV4_HEADER);
+ dhcp_data_offset = (UINT)(((UCHAR *)p->L7.DHCPv4Header) - ((UCHAR *)p->MacHeader) + dhcp_header_size);
+ data = ((UCHAR *)dhcp) + dhcp_header_size;
+ size = p->PacketSize - dhcp_data_offset;
+ if (dhcp_header_size < 5)
+ {
+ // Data size is invalid
+ return false;
+ }
+
+ // Search for Magic Cookie
+ ok = false;
+ while (size >= 5)
+ {
+ if (Cmp(data, &magic_cookie, sizeof(magic_cookie)) == 0)
+ {
+ // Found
+ data += 4;
+ size -= 4;
+ ok = true;
+ break;
+ }
+ data++;
+ size--;
+ }
+
+ if (ok == false)
+ {
+ // The packet is invalid
+ return false;
+ }
+
+ // Parse DHCP options list
+ opt = ParseDhcpOptionList(data, size);
+ if (opt == NULL)
+ {
+ // The packet is invalid
+ return false;
+ }
+
+ if (dhcp->OpCode == 1 && (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST || opt->Opcode == DHCP_INFORM))
+ {
+ // Operate as the server
+ UINT ip = IPToUINT(&e->YourIP);
+ if (ip != 0 || opt->Opcode == DHCP_INFORM)
+ {
+ // Respond if there is providable IP address
+ DHCP_OPTION_LIST ret;
+ LIST *o;
+ UINT hw_type;
+ UINT hw_addr_size;
+ UINT new_ip = ip;
+ IP default_dns;
+
+ Zero(&default_dns, sizeof(default_dns));
+
+ Zero(&ret, sizeof(ret));
+
+ ret.Opcode = (opt->Opcode == DHCP_DISCOVER ? DHCP_OFFER : DHCP_ACK);
+ ret.ServerAddress = IPToUINT(&e->MyIP);
+ ret.LeaseTime = 3600;
+ if (opt->Opcode == DHCP_INFORM)
+ {
+ ret.LeaseTime = 0;
+ }
+
+ ret.SubnetMask = SetIP32(255, 255, 255, 252);
+
+ if (UnixGetDefaultDns(&default_dns) && IsZeroIp(&default_dns) == false)
+ {
+ ret.DnsServer = IPToUINT(&default_dns);
+ ret.DnsServer2 = SetIP32(8, 8, 8, 8);
+ }
+ else
+ {
+ ret.DnsServer = SetIP32(8, 8, 8, 8);
+ ret.DnsServer2 = SetIP32(8, 8, 4, 4);
+ }
+
+ ret.Gateway = IPToUINT(&e->MyIP);
+
+ if (opt->Opcode != DHCP_INFORM)
+ {
+ char client_mac[MAX_SIZE];
+ char client_ip[64];
+ IP ips;
+ BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6);
+ UINTToIP(&ips, ip);
+ IPToStr(client_ip, sizeof(client_ip), &ips);
+ Debug("IP_RAW: DHCP %s : %s given %s\n",
+ ret.Opcode == DHCP_OFFER ? "DHCP_OFFER" : "DHCP_ACK",
+ client_mac, client_ip);
+ }
+
+ // Build a DHCP option
+ o = BuildDhcpOption(&ret);
+ if (o != NULL)
+ {
+ BUF *b = BuildDhcpOptionsBuf(o);
+ if (b != NULL)
+ {
+ UINT dest_ip = p->L3.IPv4Header->SrcIP;
+ UINT blank_size = 128 + 64;
+ UINT dhcp_packet_size;
+ UINT magic = Endian32(DHCP_MAGIC_COOKIE);
+ DHCPV4_HEADER *dhcp;
+ void *magic_cookie_addr;
+ void *buffer_addr;
+
+ if (dest_ip == 0)
+ {
+ dest_ip = 0xffffffff;
+ }
+
+ // Calculate the DHCP packet size
+ dhcp_packet_size = blank_size + sizeof(DHCPV4_HEADER) + sizeof(magic) + b->Size;
+
+ if (dhcp_packet_size < DHCP_MIN_SIZE)
+ {
+ // Padding
+ dhcp_packet_size = DHCP_MIN_SIZE;
+ }
+
+ // Create a header
+ dhcp = ZeroMalloc(dhcp_packet_size);
+
+ dhcp->OpCode = 2;
+ dhcp->HardwareType = hw_type;
+ dhcp->HardwareAddressSize = hw_addr_size;
+ dhcp->Hops = 0;
+ dhcp->TransactionId = Endian32(tran_id);
+ dhcp->Seconds = 0;
+ dhcp->Flags = 0;
+ dhcp->YourIP = new_ip;
+ dhcp->ServerIP = IPToUINT(&e->MyIP);
+ Copy(dhcp->ClientMacAddress, p->MacAddressSrc, 6);
+
+ // Calculate the address
+ magic_cookie_addr = (((UCHAR *)dhcp) + sizeof(DHCPV4_HEADER) + blank_size);
+ buffer_addr = ((UCHAR *)magic_cookie_addr) + sizeof(magic);
+
+ // Magic Cookie
+ Copy(magic_cookie_addr, &magic, sizeof(magic));
+
+ // Buffer
+ Copy(buffer_addr, b->Buf, b->Size);
+
+ if (true)
+ {
+ UCHAR *data = ZeroMalloc(sizeof(IPV4_HEADER) + sizeof(UDP_HEADER) + dhcp_packet_size);
+ IPV4_HEADER *ipv4 = (IPV4_HEADER *)(data);
+ UDP_HEADER *udp = (UDP_HEADER *)(data + sizeof(IPV4_HEADER));
+
+ Copy(data + sizeof(IPV4_HEADER) + sizeof(UDP_HEADER), dhcp, dhcp_packet_size);
+
+ IPV4_SET_VERSION(ipv4, 4);
+ IPV4_SET_HEADER_LEN(ipv4, 5);
+ ipv4->TotalLength = Endian16(sizeof(IPV4_HEADER) + sizeof(UDP_HEADER) + dhcp_packet_size);
+ ipv4->TimeToLive = 63;
+ ipv4->Protocol = IP_PROTO_UDP;
+ ipv4->SrcIP = IPToUINT(&e->MyIP);
+ ipv4->DstIP = dest_ip;
+ ipv4->Checksum = IpChecksum(ipv4, sizeof(IPV4_HEADER));
+
+ udp->SrcPort = Endian16(NAT_DHCP_SERVER_PORT);
+ udp->DstPort = Endian16(NAT_DHCP_CLIENT_PORT);
+ udp->PacketLength = Endian16(sizeof(UDP_HEADER) + dhcp_packet_size);
+ udp->Checksum = CalcChecksumForIPv4(ipv4->SrcIP, ipv4->DstIP, IP_PROTO_UDP,
+ dhcp, dhcp_packet_size, 0);
+ if (udp->Checksum == 0)
+ {
+ udp->Checksum = 0xffff;
+ }
+
+ EthSendIpPacketInnerIpRaw(e, data, sizeof(IPV4_HEADER) + sizeof(UDP_HEADER) + dhcp_packet_size, MAC_PROTO_IPV4);
+
+ Free(data);
+ }
+
+ // Release the memory
+ Free(dhcp);
+ FreeBuf(b);
+ }
+ FreeDhcpOptions(o);
+ }
+ }
+ }
+
+ Free(opt);
+ }
+
+ return ret;
+}
+
+// Send an IP raw packet
+void EthPutPacketLinuxIpRaw(ETH *e, void *data, UINT size)
+{
+ PKT *p;
+ // Validate arguments
+ if (e == NULL || data == NULL)
+ {
+ return;
+ }
+ if (size < 14 || size > MAX_PACKET_SIZE || e->RawIp_HasError)
+ {
+ Free(data);
+ return;
+ }
+
+ p = ParsePacket(data, size);
+
+ if (p->BroadcastPacket || Cmp(p->MacAddressDest, e->RawIpMyMacAddr, 6) == 0)
+ {
+ if (IsValidUnicastMacAddress(p->MacAddressSrc))
+ {
+ Copy(e->RawIpYourMacAddr, p->MacAddressSrc, 6);
+ }
+ }
+
+ if (IsZero(e->RawIpYourMacAddr, 6) || IsValidUnicastMacAddress(p->MacAddressSrc) == false ||
+ (p->BroadcastPacket == false && Cmp(p->MacAddressDest, e->RawIpMyMacAddr, 6) != 0))
+ {
+ Free(data);
+ FreePacket(p);
+ return;
+ }
+
+ if (p != NULL)
+ {
+ SOCK *s = NULL;
+
+ if (p->TypeL3 == L3_IPV4)
+ {
+ if (p->TypeL4 == L4_TCP)
+ {
+ if (IsZeroIP(&e->MyPhysicalIP) == false)
+ {
+ s = e->RawTcp;
+ }
+ }
+ else if (p->TypeL4 == L4_UDP)
+ {
+ if (EthProcessIpPacketInnerIpRaw(e, p) == false)
+ {
+ s = e->RawUdp;
+ }
+ }
+ else if (p->TypeL4 == L4_ICMPV4)
+ {
+ if (IsZeroIP(&e->MyPhysicalIP) == false)
+ {
+ s = e->RawIcmp;
+ }
+ }
+ else if (p->TypeL4 == L4_FRAGMENT)
+ {
+ if (IsZeroIP(&e->MyPhysicalIP) == false)
+ {
+ s = e->RawIcmp;
+ }
+ }
+ }
+ else if (p->TypeL3 == L3_ARPV4)
+ {
+ EthProcessIpPacketInnerIpRaw(e, p);
+ }
+
+ if (s != NULL && p->L3.IPv4Header->DstIP != 0xffffffff && p->BroadcastPacket == false &&
+ p->L3.IPv4Header->SrcIP == IPToUINT(&e->YourIP))
+ {
+ UCHAR *send_data = p->IPv4PayloadData;
+ UCHAR *head = p->PacketData;
+ UINT remove_header_size = (UINT)(send_data - head);
+
+ if (p->PacketSize > remove_header_size)
+ {
+ IP dest;
+ UINT send_data_size = p->PacketSize - remove_header_size;
+
+ // checksum
+ if (p->TypeL4 == L4_UDP)
+ {
+ p->L4.UDPHeader->Checksum = 0;
+ }
+ else if (p->TypeL4 == L4_TCP)
+ {
+ p->L4.TCPHeader->Checksum = 0;
+ p->L4.TCPHeader->Checksum = CalcChecksumForIPv4(IPToUINT(&e->MyPhysicalIP),
+ p->L3.IPv4Header->DstIP, IP_PROTO_TCP,
+ p->L4.TCPHeader, p->IPv4PayloadSize, 0);
+ }
+
+ UINTToIP(&dest, p->L3.IPv4Header->DstIP);
+
+ if (s->RawIP_HeaderIncludeFlag == false)
+ {
+ SendTo(s, &dest, 0, send_data, send_data_size);
+ }
+ else
+ {
+ IPV4_HEADER *ip = p->L3.IPv4Header;
+
+ ip->SrcIP = IPToUINT(&e->MyPhysicalIP);
+ ip->Checksum = 0;
+ ip->Checksum = IpChecksum(ip, IPV4_GET_HEADER_LEN(ip) * 4);
+
+ SendTo(s, &dest, 0, ip, ((UCHAR *)p->PacketData - (UCHAR *)ip) + p->PacketSize);
+ }
+ }
+ }
+
+ FreePacket(p);
+ }
+
+ Free(data);
+}
+
+
#endif // BRIDGE_C