diff options
Diffstat (limited to 'src/Cedar/Layer3.c')
-rw-r--r-- | src/Cedar/Layer3.c | 2173 |
1 files changed, 2173 insertions, 0 deletions
diff --git a/src/Cedar/Layer3.c b/src/Cedar/Layer3.c new file mode 100644 index 00000000..3bb8ecbe --- /dev/null +++ b/src/Cedar/Layer3.c @@ -0,0 +1,2173 @@ +// SoftEther VPN Source Code
+// Cedar Communication Module
+//
+// 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.
+//
+// All Rights Reserved.
+//
+// http://www.softether.org/
+//
+// Author: Daiyuu Nobori
+// Comments: Tetsuo Sugiyama, Ph.D.
+//
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// version 2 as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License version 2
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
+// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
+//
+//
+// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
+// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
+// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
+// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
+// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
+// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
+// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
+// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
+// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
+// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
+// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
+// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
+// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
+// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
+//
+// USE ONLY IN JAPAN. DO NOT USE IT IN OTHER COUNTRIES. IMPORTING THIS
+// SOFTWARE INTO OTHER COUNTRIES IS AT YOUR OWN RISK. SOME COUNTRIES
+// PROHIBIT ENCRYPTED COMMUNICATIONS. USING THIS SOFTWARE IN OTHER
+// COUNTRIES MIGHT BE RESTRICTED.
+//
+//
+// DEAR SECURITY EXPERTS
+// ---------------------
+//
+// If you find a bug or a security vulnerability please kindly inform us
+// about the problem immediately so that we can fix the security problem
+// to protect a lot of users around the world as soon as possible.
+//
+// Our e-mail address for security reports is:
+// softether-vpn-security [at] softether.org
+//
+// Please note that the above e-mail address is not a technical support
+// inquiry address. If you need technical assistance, please visit
+// http://www.softether.org/ and ask your question on the users forum.
+//
+// Thank you for your cooperation.
+
+
+// Layer3.c
+// Layer-3 switch module
+
+#include "CedarPch.h"
+
+static UCHAR broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+// Process the IP queue
+void L3PollingIpQueue(L3IF *f)
+{
+ L3PACKET *p;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ // Process the packet came from another session
+ while (p = GetNext(f->IpPacketQueue))
+ {
+ PKT *pkt = p->Packet;
+
+ // Send as an IP packet
+ L3SendIp(f, p);
+ }
+}
+
+// Process IP packets
+void L3RecvIp(L3IF *f, PKT *p, bool self)
+{
+ IPV4_HEADER *ip;
+ UINT header_size;
+ UINT next_hop = 0;
+ L3IF *dst;
+ L3PACKET *packet;
+ UINT new_ttl = 0;
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+
+ ip = p->L3.IPv4Header;
+ header_size = IPV4_GET_HEADER_LEN(p->L3.IPv4Header) * 4;
+
+ // Calculate the checksum
+ if (IpCheckChecksum(ip) == false)
+ {
+ // The checksum does not match
+ goto FREE_PACKET;
+ }
+
+ // Register in the ARP table
+ L3KnownArp(f, ip->SrcIP, p->MacAddressSrc);
+
+ if (p->BroadcastPacket)
+ {
+ // Not to route in the case of broadcast packet
+ goto FREE_PACKET;
+ }
+
+ // Calculate the TTL
+ if (ip->TimeToLive >= 1)
+ {
+ new_ttl = ip->TimeToLive - 1;
+ }
+ else
+ {
+ new_ttl = 0;
+ }
+
+ if (new_ttl == 0)
+ {
+ if (ip->DstIP != f->IpAddress)
+ {
+ UINT src_packet_size = p->PacketSize - sizeof(MAC_HEADER);
+ UINT icmp_packet_total_size = sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4 + header_size + 8;
+ UCHAR *buf;
+ IPV4_HEADER *ipv4;
+ ICMP_HEADER *icmpv4;
+ UCHAR *data;
+ PKT *pkt;
+ UINT data_size = MIN(p->PacketSize - header_size, header_size + 8);
+
+ // Generate an ICMP message that means that the TTL has expired
+ buf = ZeroMalloc(icmp_packet_total_size);
+ ipv4 = (IPV4_HEADER *)(buf + sizeof(MAC_HEADER));
+ icmpv4 = (ICMP_HEADER *)(buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER));
+ data = buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4;
+
+ IPV4_SET_VERSION(ipv4, 4);
+ IPV4_SET_HEADER_LEN(ipv4, sizeof(IPV4_HEADER) / 4);
+ ipv4->TotalLength = Endian16((USHORT)(icmp_packet_total_size - sizeof(MAC_HEADER)));
+ ipv4->TimeToLive = 0xff;
+ ipv4->Protocol = IP_PROTO_ICMPV4;
+ ipv4->SrcIP = f->IpAddress;
+ ipv4->DstIP = ip->SrcIP;
+ ipv4->Checksum = IpChecksum(ipv4, sizeof(IPV4_HEADER));
+
+ icmpv4->Type = 11;
+ Copy(data, ip, data_size);
+ icmpv4->Checksum = IpChecksum(icmpv4, sizeof(ICMP_HEADER) + data_size + 4);
+
+ buf[12] = 0x08;
+ buf[13] = 0x00;
+
+ pkt = ParsePacket(buf, icmp_packet_total_size);
+ if (pkt == NULL)
+ {
+ Free(buf);
+ }
+ else
+ {
+ L3RecvIp(f, pkt, true);
+ }
+
+ // Discard the packet body whose the TTL has expired
+ goto FREE_PACKET;
+ }
+ }
+
+ // Rewrite the TTL
+ p->L3.IPv4Header->TimeToLive = (UCHAR)new_ttl;
+
+ // Get the interface corresponding to the destination IP address
+ dst = L3GetNextIf(f->Switch, ip->DstIP, &next_hop);
+
+ if (dst == NULL && self == false)
+ {
+ UINT src_packet_size = p->PacketSize - sizeof(MAC_HEADER);
+ UINT icmp_packet_total_size = sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4 + header_size + 8;
+ UCHAR *buf;
+ IPV4_HEADER *ipv4;
+ ICMP_HEADER *icmpv4;
+ UCHAR *data;
+ PKT *pkt;
+ UINT data_size = MIN(p->PacketSize - header_size, header_size + 8);
+
+ // Respond with ICMP that indicates that no route can be found
+ buf = ZeroMalloc(icmp_packet_total_size);
+ ipv4 = (IPV4_HEADER *)(buf + sizeof(MAC_HEADER));
+ icmpv4 = (ICMP_HEADER *)(buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER));
+ data = buf + sizeof(MAC_HEADER) + sizeof(IPV4_HEADER) + sizeof(ICMP_HEADER) + 4;
+
+ IPV4_SET_VERSION(ipv4, 4);
+ IPV4_SET_HEADER_LEN(ipv4, sizeof(IPV4_HEADER) / 4);
+ ipv4->TotalLength = Endian16((USHORT)(icmp_packet_total_size - sizeof(MAC_HEADER)));
+ ipv4->TimeToLive = 0xff;
+ ipv4->Protocol = IP_PROTO_ICMPV4;
+ ipv4->SrcIP = f->IpAddress;
+ ipv4->DstIP = ip->SrcIP;
+ ipv4->Checksum = IpChecksum(ipv4, sizeof(IPV4_HEADER));
+
+ icmpv4->Type = 3;
+ Copy(data, ip, data_size);
+ icmpv4->Checksum = IpChecksum(icmpv4, sizeof(ICMP_HEADER) + data_size + 4);
+
+ buf[12] = 0x08;
+ buf[13] = 0x00;
+
+ pkt = ParsePacket(buf, icmp_packet_total_size);
+ if (pkt == NULL)
+ {
+ Free(buf);
+ }
+ else
+ {
+ L3RecvIp(f, pkt, true);
+ }
+
+ // Discard the packet body whose route can not be found
+ goto FREE_PACKET;
+ }
+
+ if (dst != NULL && ip->DstIP == dst->IpAddress)
+ {
+ bool free_packet = true;
+ // IP packet addressed to myself has arrived
+ if (p->TypeL4 == L4_ICMPV4)
+ {
+ ICMP_HEADER *icmp = p->L4.ICMPHeader;
+ if (icmp->Type == ICMP_TYPE_ECHO_REQUEST)
+ {
+ // Reply by rewriting the source and destination of the IP packet
+ UINT src_ip, dst_ip;
+ src_ip = p->L3.IPv4Header->DstIP;
+ dst_ip = p->L3.IPv4Header->SrcIP;
+
+ p->L3.IPv4Header->DstIP = dst_ip;
+ p->L3.IPv4Header->SrcIP = src_ip;
+
+ ip->TimeToLive = 0xff;
+
+ // Recalculates the checksum
+ ip->FlagsAndFlagmentOffset[0] = ip->FlagsAndFlagmentOffset[1] = 0;
+ icmp->Checksum = 0;
+ icmp->Type = ICMP_TYPE_ECHO_RESPONSE;
+ icmp->Checksum = IpChecksum(icmp, p->PacketSize - sizeof(MAC_HEADER) - header_size);
+
+ dst = L3GetNextIf(f->Switch, ip->DstIP, &next_hop);
+
+ free_packet = false;
+ }
+ }
+
+ if (free_packet)
+ {
+ goto FREE_PACKET;
+ }
+ }
+
+ if (dst == NULL)
+ {
+ // The destination does not exist
+ goto FREE_PACKET;
+ }
+
+ // Recalculate the IP checksum
+ ip->Checksum = 0;
+ ip->Checksum = IpChecksum(ip, header_size);
+
+ // Treat as a Layer-3 packet
+ packet = ZeroMalloc(sizeof(L3PACKET));
+ packet->Expire = Tick64() + IP_WAIT_FOR_ARP_TIMEOUT;
+ packet->NextHopIp = next_hop;
+ packet->Packet = p;
+
+ // Store to the destination session
+ L3StoreIpPacketToIf(f, dst, packet);
+
+ return;
+
+FREE_PACKET:
+ // Release the packet
+ Free(p->PacketData);
+ FreePacket(p);
+ return;
+}
+
+// Process the Layer 2 packet
+void L3RecvL2(L3IF *f, PKT *p)
+{
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+
+ // Ignore any packets except a unicast packet which is destinated other
+ // or a packet which I sent
+ if (Cmp(p->MacAddressSrc, f->MacAddress, 6) == 0 ||
+ (p->BroadcastPacket == false && Cmp(p->MacAddressDest, f->MacAddress, 6) != 0))
+ {
+ // Release the packet
+ Free(p->PacketData);
+ FreePacket(p);
+ return;
+ }
+
+ if (p->TypeL3 == L3_ARPV4)
+ {
+ // Received an ARP packet
+ L3RecvArp(f, p);
+
+ // Release the packet
+ Free(p->PacketData);
+ FreePacket(p);
+ }
+ else if (p->TypeL3 == L3_IPV4)
+ {
+ // Received an IP packet
+ L3RecvIp(f, p, false);
+ }
+ else
+ {
+ // Release the packet
+ Free(p->PacketData);
+ FreePacket(p);
+ }
+}
+
+// Store the IP packet to a different interface
+void L3StoreIpPacketToIf(L3IF *src_if, L3IF *dst_if, L3PACKET *p)
+{
+ // Validate arguments
+ if (src_if == NULL || p == NULL || dst_if == NULL)
+ {
+ return;
+ }
+
+ // Add to the queue of store-destination session
+ InsertQueue(dst_if->IpPacketQueue, p);
+
+ // Hit the Cancel object of the store-destination session
+ AddCancelList(src_if->CancelList, dst_if->Session->Cancel1);
+}
+
+// Write the packet (Process because the packet was received)
+void L3PutPacket(L3IF *f, void *data, UINT size)
+{
+ PKT *p;
+ L3SW *s;
+ if (f == NULL)
+ {
+ return;
+ }
+
+ s = f->Switch;
+
+ if (data != NULL)
+ {
+ // Handle the next packet
+ if (f->CancelList == NULL)
+ {
+ f->CancelList = NewCancelList();
+ }
+
+ // Packet analysis
+ p = ParsePacket(data, size);
+
+ if (p == NULL)
+ {
+ // Packet analysis failure
+ Free(data);
+ }
+ else
+ {
+ // Packet analysis success
+ Lock(s->lock);
+ {
+ L3RecvL2(f, p);
+ }
+ Unlock(s->lock);
+ }
+ }
+ else
+ {
+ // Cancel for the cancellation list after all packet processing has been finished
+ if (f->CancelList != NULL)
+ {
+ CancelList(f->CancelList);
+ ReleaseCancelList(f->CancelList);
+ f->CancelList = NULL;
+ }
+ }
+}
+
+// Send the waiting IP packets whose destination MAC address has been resolved
+void L3SendWaitingIp(L3IF *f, UCHAR *mac, UINT ip, L3ARPENTRY *a)
+{
+ UINT i;
+ LIST *o = NULL;
+ // Validate arguments
+ if (f == NULL || mac == NULL || a == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(f->IpWaitList);i++)
+ {
+ L3PACKET *p = LIST_DATA(f->IpWaitList, i);
+
+ if (p->NextHopIp == ip)
+ {
+ if (o == NULL)
+ {
+ o = NewListFast(NULL);
+ }
+ Add(o, p);
+ }
+ }
+
+ if (o != NULL)
+ {
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ L3PACKET *p = LIST_DATA(o, i);
+
+ // Transmission
+ L3SendIpNow(f, a, p);
+
+ Delete(f->IpWaitList, p);
+ Free(p->Packet->PacketData);
+ FreePacket(p->Packet);
+ Free(p);
+ }
+
+ ReleaseList(o);
+ }
+}
+
+// Register in the ARP table
+void L3InsertArpTable(L3IF *f, UINT ip, UCHAR *mac)
+{
+ L3ARPENTRY *a, t;
+ // Validate arguments
+ if (f == NULL || ip == 0 || ip == 0xffffffff || mac == NULL)
+ {
+ return;
+ }
+
+ Zero(&t, sizeof(t));
+ t.IpAddress = ip;
+
+ a = Search(f->ArpTable, &t);
+
+ if (a == NULL)
+ {
+ // Since this is not registered, register this
+ a = ZeroMalloc(sizeof(L3ARPENTRY));
+ a->IpAddress = ip;
+ Copy(a->MacAddress, mac, 6);
+ Insert(f->ArpTable, a);
+ }
+
+ // Extend the expiration date
+ a->Expire = Tick64() + ARP_ENTRY_EXPIRES;
+
+ // Send waiting IP packets
+ L3SendWaitingIp(f, mac, ip, a);
+}
+
+// Function to be called when the ARP resolved
+void L3KnownArp(L3IF *f, UINT ip, UCHAR *mac)
+{
+ L3ARPWAIT t, *w;
+ // Validate arguments
+ if (f == NULL || ip == 0 || ip == 0xffffffff || mac == NULL)
+ {
+ return;
+ }
+
+ // Delete an ARP query entry to this IP address
+ Zero(&t, sizeof(t));
+ t.IpAddress = ip;
+ w = Search(f->IpWaitList, &t);
+ if (w != NULL)
+ {
+ Delete(f->IpWaitList, w);
+ Free(w);
+ }
+
+ // Register in the ARP table
+ L3InsertArpTable(f, ip, mac);
+}
+
+// Issue an ARP query
+void L3SendArp(L3IF *f, UINT ip)
+{
+ L3ARPWAIT t, *w;
+ // Validate arguments
+ if (f == NULL || ip == 0 || ip == 0xffffffff)
+ {
+ return;
+ }
+
+ // Examine whether it has not already registered
+ Zero(&t, sizeof(t));
+ t.IpAddress = ip;
+ w = Search(f->ArpWaitTable, &t);
+
+ if (w != NULL)
+ {
+ // Do not do anything because it is already registered in the waiting list
+ return;
+ }
+ else
+ {
+ // Register in the waiting list newly
+ w = ZeroMalloc(sizeof(L3ARPWAIT));
+ w->Expire = Tick64() + ARP_REQUEST_GIVEUP;
+ w->IpAddress = ip;
+ Insert(f->ArpWaitTable, w);
+ }
+}
+
+// Received an ARP request
+void L3RecvArpRequest(L3IF *f, PKT *p)
+{
+ ARPV4_HEADER *a;
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+
+ a = p->L3.ARPv4Header;
+
+ L3KnownArp(f, a->SrcIP, a->SrcAddress);
+
+ if (a->TargetIP == f->IpAddress)
+ {
+ // Respond only if the ARP packet addressed to myself
+ L3SendArpResponseNow(f, a->SrcAddress, a->SrcIP, f->IpAddress);
+ }
+}
+
+// Received an ARP response
+void L3RecvArpResponse(L3IF *f, PKT *p)
+{
+ ARPV4_HEADER *a;
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+
+ a = p->L3.ARPv4Header;
+
+ L3KnownArp(f, a->SrcIP, a->SrcAddress);
+}
+
+// Received an ARP packet
+void L3RecvArp(L3IF *f, PKT *p)
+{
+ ARPV4_HEADER *a;
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+
+ a = p->L3.ARPv4Header;
+
+ if (Endian16(a->HardwareType) != ARP_HARDWARE_TYPE_ETHERNET ||
+ Endian16(a->ProtocolType) != MAC_PROTO_IPV4 ||
+ a->HardwareSize != 6 || a->ProtocolSize != 4)
+ {
+ return;
+ }
+ if (Cmp(a->SrcAddress, p->MacAddressSrc, 6) != 0)
+ {
+ return;
+ }
+
+ switch (Endian16(a->Operation))
+ {
+ case ARP_OPERATION_REQUEST:
+ // ARP request arrives
+ L3RecvArpRequest(f, p);
+ break;
+
+ case ARP_OPERATION_RESPONSE:
+ // ARP response arrives
+ L3RecvArpResponse(f, p);
+ break;
+ }
+}
+
+// Send an IP packet
+void L3SendIp(L3IF *f, L3PACKET *p)
+{
+ L3ARPENTRY *a = NULL;
+ bool broadcast = false;
+ IPV4_HEADER *ip;
+ bool for_me = false;
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+ if (p->Packet->TypeL3 != L3_IPV4)
+ {
+ return;
+ }
+
+ ip = p->Packet->L3.IPv4Header;
+
+ // Determining whether it's a broadcast
+ if (p->NextHopIp == 0xffffffff ||
+ ((p->NextHopIp & f->SubnetMask) == (f->IpAddress & f->SubnetMask)) &&
+ ((p->NextHopIp & (~f->SubnetMask)) == (~f->SubnetMask)))
+ {
+ broadcast = true;
+ }
+
+ if (broadcast == false && ip->DstIP == f->IpAddress)
+ {
+ // me?
+ }
+ else if (broadcast == false)
+ {
+ // Examine whether the ARP entry contains this in the case of unicast
+ a = L3SearchArpTable(f, p->NextHopIp);
+
+ if (a == NULL)
+ {
+ // Since It is not in the ARP table,
+ // insert it into the IP waiting list without sending immediately
+ p->Expire = Tick64() + IP_WAIT_FOR_ARP_TIMEOUT;
+
+ Insert(f->IpWaitList, p);
+
+ // Issue an ARP query
+ L3SendArp(f, p->NextHopIp);
+ return;
+ }
+ }
+
+ if (for_me == false)
+ {
+ // Send the IP packet
+ L3SendIpNow(f, a, p);
+ }
+
+ // Release the packet
+ Free(p->Packet->PacketData);
+ FreePacket(p->Packet);
+ Free(p);
+}
+
+// Send the IP packet immediately
+void L3SendIpNow(L3IF *f, L3ARPENTRY *a, L3PACKET *p)
+{
+ // Validate arguments
+ if (f == NULL || p == NULL)
+ {
+ return;
+ }
+
+ L3SendL2Now(f, a != NULL ? a->MacAddress : broadcast, f->MacAddress, Endian16(p->Packet->MacHeader->Protocol),
+ p->Packet->L3.PointerL3, p->Packet->PacketSize - sizeof(MAC_HEADER));
+}
+
+// Search in the ARP table
+L3ARPENTRY *L3SearchArpTable(L3IF *f, UINT ip)
+{
+ L3ARPENTRY *e, t;
+ // Validate arguments
+ if (f == NULL || ip == 0 || ip == 0xffffffff)
+ {
+ return NULL;
+ }
+
+ Zero(&t, sizeof(t));
+ t.IpAddress = ip;
+
+ e = Search(f->ArpTable, &t);
+
+ return e;
+}
+
+// Send an ARP request packet
+void L3SendArpRequestNow(L3IF *f, UINT dest_ip)
+{
+ ARPV4_HEADER arp;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ // Build an ARP header
+ arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+ arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+ arp.HardwareSize = 6;
+ arp.ProtocolSize = 4;
+ arp.Operation = Endian16(ARP_OPERATION_REQUEST);
+ Copy(arp.SrcAddress, f->MacAddress, 6);
+ arp.SrcIP = f->IpAddress;
+ Zero(&arp.TargetAddress, 6);
+ arp.TargetIP = dest_ip;
+
+ // Transmission
+ L3SendL2Now(f, broadcast, f->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+}
+
+// Send an ARP response packet
+void L3SendArpResponseNow(L3IF *f, UCHAR *dest_mac, UINT dest_ip, UINT src_ip)
+{
+ ARPV4_HEADER arp;
+ // Validate arguments
+ if (f == NULL || dest_mac == NULL)
+ {
+ return;
+ }
+
+ // Build a header
+ arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+ arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+ arp.HardwareSize = 6;
+ arp.ProtocolSize = 4;
+ arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
+ Copy(arp.SrcAddress, f->MacAddress, 6);
+ Copy(arp.TargetAddress, dest_mac, 6);
+ arp.SrcIP = src_ip;
+ arp.TargetIP = dest_ip;
+
+ // Transmission
+ L3SendL2Now(f, dest_mac, f->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+}
+
+// Generate a MAC address of the interface
+void L3GenerateMacAddress(L3IF *f)
+{
+ BUF *b;
+ UCHAR hash[SHA1_SIZE];
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ b = NewBuf();
+ WriteBuf(b, f->Switch->Name, StrLen(f->Switch->Name));
+ WriteBuf(b, f->HubName, StrLen(f->HubName));
+ WriteBuf(b, &f->IpAddress, sizeof(f->IpAddress));
+
+ GenMacAddress(f->MacAddress);
+ Hash(hash, b->Buf, b->Size, true);
+ Copy(f->MacAddress + 2, hash, 4);
+ f->MacAddress[1] = 0xA3;
+ FreeBuf(b);
+}
+
+// Send an L2 packet immediately
+void L3SendL2Now(L3IF *f, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size)
+{
+ UCHAR *buf;
+ MAC_HEADER *mac_header;
+ PKT *p;
+ // Validate arguments
+ if (f == NULL || dest_mac == NULL || src_mac == NULL || data == NULL)
+ {
+ return;
+ }
+
+ // Buffer creation
+ buf = Malloc(MAC_HEADER_SIZE + size);
+
+ // MAC header
+ mac_header = (MAC_HEADER *)&buf[0];
+ Copy(mac_header->DestAddress, dest_mac, 6);
+ Copy(mac_header->SrcAddress, src_mac, 6);
+ mac_header->Protocol = Endian16(protocol);
+
+ // Copy data
+ Copy(&buf[sizeof(MAC_HEADER)], data, size);
+
+ // Size
+ size += sizeof(MAC_HEADER);
+
+ // Packet generation
+ p = ZeroMalloc(sizeof(PKT));
+ p->PacketData = buf;
+ p->PacketSize = size;
+
+ // Add to the queue
+ InsertQueue(f->SendQueue, p);
+}
+
+// Polling for the ARP resolution waiting list
+void L3PollingArpWaitTable(L3IF *f)
+{
+ UINT i;
+ LIST *o = NULL;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(f->ArpWaitTable);i++)
+ {
+ L3ARPWAIT *w = LIST_DATA(f->ArpWaitTable, i);
+
+ if (w->Expire <= Tick64())
+ {
+ // The ARP request entry is expired
+ if (o == NULL)
+ {
+ o = NewListFast(NULL);
+ }
+
+ Insert(o, w);
+ }
+ else if ((w->LastSentTime + ARP_REQUEST_TIMEOUT) <= Tick64())
+ {
+ // Send a next ARP request packet
+ w->LastSentTime = Tick64();
+
+ L3SendArpRequestNow(f, w->IpAddress);
+ }
+ }
+
+ if (o != NULL)
+ {
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ L3ARPWAIT *w = LIST_DATA(o, i);
+
+ Delete(f->ArpWaitTable, w);
+ Free(w);
+ }
+
+ ReleaseList(o);
+ }
+}
+
+// Clear old ARP table entries
+void L3DeleteOldArpTable(L3IF *f)
+{
+ UINT i;
+ LIST *o = NULL;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ if ((f->LastDeleteOldArpTable + ARP_ENTRY_POLLING_TIME) > Tick64())
+ {
+ return;
+ }
+ f->LastDeleteOldArpTable = Tick64();
+
+ for (i = 0;i < LIST_NUM(f->ArpTable);i++)
+ {
+ L3ARPENTRY *a = LIST_DATA(f->ArpTable, i);
+
+ if (a->Expire <= Tick64())
+ {
+ // Expired
+ if (o == NULL)
+ {
+ o = NewListFast(NULL);
+ }
+
+ Insert(o, a);
+ }
+ }
+
+ if (o != NULL)
+ {
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ L3ARPENTRY *a = LIST_DATA(o, i);
+
+ Delete(f->ArpTable, a);
+ Free(a);
+ }
+
+ ReleaseList(o);
+ }
+}
+
+// Clear the IP waiting list
+void L3DeleteOldIpWaitList(L3IF *f)
+{
+ UINT i;
+ LIST *o = NULL;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(f->IpWaitList);i++)
+ {
+ L3PACKET *p = LIST_DATA(f->IpWaitList, i);
+
+ if (p->Expire <= Tick64())
+ {
+ if (o == NULL)
+ {
+ o = NewListFast(NULL);
+ }
+
+ Insert(o, p);
+ }
+ }
+
+ if (o != NULL)
+ {
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ L3PACKET *p = LIST_DATA(o, i);
+
+ Delete(f->IpWaitList, p);
+
+ Free(p->Packet->PacketData);
+ FreePacket(p->Packet);
+ Free(p);
+ }
+
+ ReleaseList(o);
+ }
+}
+
+// Beacon transmission
+void L3PollingBeacon(L3IF *f)
+{
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ if (f->LastBeaconSent == 0 ||
+ (f->LastBeaconSent + BEACON_SEND_INTERVAL) <= Tick64())
+ {
+ UINT dest_ip;
+ UCHAR *udp_buf;
+ UINT udp_buf_size;
+ ARPV4_HEADER arp;
+ IPV4_HEADER *ip;
+ UDP_HEADER *udp;
+ static char beacon_str[] =
+ "PacketiX VPN Virtual Layer-3 Switch Beacon";
+
+ // Send an UDP
+ dest_ip = (f->IpAddress & f->SubnetMask) | (~f->SubnetMask);
+ udp_buf_size = sizeof(IPV4_HEADER) + sizeof(UDP_HEADER) + sizeof(beacon_str);
+ udp_buf = ZeroMalloc(udp_buf_size);
+
+ ip = (IPV4_HEADER *)udp_buf;
+ udp = (UDP_HEADER *)(udp_buf + sizeof(IPV4_HEADER));
+ udp->DstPort = Endian16(7);
+ udp->SrcPort = Endian16(7);
+ udp->PacketLength = Endian16(sizeof(UDP_HEADER) + sizeof(beacon_str));
+
+ Copy(udp_buf + sizeof(IPV4_HEADER) + sizeof(UDP_HEADER), beacon_str, sizeof(beacon_str));
+
+ udp->Checksum = IpChecksum(udp, sizeof(UDP_HEADER) + sizeof(beacon_str));
+
+ ip->DstIP = dest_ip;
+ IPV4_SET_VERSION(ip, 4);
+ IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4));
+ ip->TypeOfService = DEFAULT_IP_TOS;
+ ip->TotalLength = Endian16((USHORT)(udp_buf_size));
+ ip->TimeToLive = DEFAULT_IP_TTL;
+ ip->Protocol = IP_PROTO_UDP;
+ ip->SrcIP = f->IpAddress;
+ ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE);
+
+ L3SendL2Now(f, broadcast, f->MacAddress, MAC_PROTO_IPV4, udp_buf, udp_buf_size);
+
+ Free(udp_buf);
+
+ // Build the ARP header
+ arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);
+ arp.ProtocolType = Endian16(MAC_PROTO_IPV4);
+ arp.HardwareSize = 6;
+ arp.ProtocolSize = 4;
+ arp.Operation = Endian16(ARP_OPERATION_RESPONSE);
+ Copy(arp.SrcAddress, f->MacAddress, 6);
+ arp.SrcIP = f->IpAddress;
+ arp.TargetAddress[0] =
+ arp.TargetAddress[1] =
+ arp.TargetAddress[2] =
+ arp.TargetAddress[3] =
+ arp.TargetAddress[4] =
+ arp.TargetAddress[5] = 0xff;
+ arp.TargetIP = dest_ip;
+
+ // Transmission
+ L3SendL2Now(f, broadcast, f->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));
+
+ f->LastBeaconSent = Tick64();
+ }
+}
+
+// Polling process
+void L3Polling(L3IF *f)
+{
+ L3SW *s;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ s = f->Switch;
+
+ // Lock the entire switch in the middle of the polling process
+ Lock(s->lock);
+ {
+ // Beacon transmission
+ L3PollingBeacon(f);
+
+ // Process the IP queue
+ L3PollingIpQueue(f);
+
+ // Clear old ARP table entries
+ L3DeleteOldArpTable(f);
+
+ // Polling ARP resolution waiting list
+ L3PollingArpWaitTable(f);
+
+ // Clear the IP waiting list
+ L3DeleteOldIpWaitList(f);
+ }
+ Unlock(s->lock);
+}
+
+// Get the next packet
+UINT L3GetNextPacket(L3IF *f, void **data)
+{
+ UINT ret = 0;
+ // Validate arguments
+ if (f == NULL || data == NULL)
+ {
+ return 0;
+ }
+
+START:
+ // Examine the send queue
+ LockQueue(f->SendQueue);
+ {
+ PKT *p = GetNext(f->SendQueue);
+
+ if (p != NULL)
+ {
+ // There is a packet
+ ret = p->PacketSize;
+ *data = p->PacketData;
+ // Packet structure may be discarded
+ Free(p);
+ }
+ }
+ UnlockQueue(f->SendQueue);
+
+ if (ret == 0)
+ {
+ // Polling process
+ L3Polling(f);
+
+ // Examine whether a new packet is queued for results of the polling process
+ if (f->SendQueue->num_item != 0)
+ {
+ // Get the packet immediately if it's in the queue
+ goto START;
+ }
+ }
+
+ return ret;
+}
+
+// Determine the packet destined for the specified IP address should be sent to which interface
+L3IF *L3GetNextIf(L3SW *s, UINT ip, UINT *next_hop)
+{
+ UINT i;
+ L3IF *f;
+ UINT next_hop_ip = 0;
+ // Validate arguments
+ if (s == NULL || ip == 0 || ip == 0xffffffff)
+ {
+ return NULL;
+ }
+
+ f = NULL;
+
+ // Examine whether the specified IP address is contained
+ // in the networks which each interfaces belong to
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *ff = LIST_DATA(s->IfList, i);
+
+ if ((ff->IpAddress & ff->SubnetMask) == (ip & ff->SubnetMask))
+ {
+ f = ff;
+ next_hop_ip = ip;
+ break;
+ }
+ }
+
+ if (f == NULL)
+ {
+ // Find the routing table if it's not found
+ L3TABLE *t = L3GetBestRoute(s, ip);
+
+ if (t == NULL)
+ {
+ // Still not found
+ return NULL;
+ }
+ else
+ {
+ // Find the interface with the IP address of the router of
+ // NextHop of the found route
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *ff = LIST_DATA(s->IfList, i);
+
+ if ((ff->IpAddress & ff->SubnetMask) == (t->GatewayAddress & ff->SubnetMask))
+ {
+ f = ff;
+ next_hop_ip = t->GatewayAddress;
+ break;
+ }
+ }
+ }
+ }
+
+ if (f == NULL)
+ {
+ // Destination interface was unknown after all
+ return NULL;
+ }
+
+ if (next_hop != NULL)
+ {
+ *next_hop = next_hop_ip;
+ }
+
+ return f;
+}
+
+// Get the best routing table entry for the specified IP address
+L3TABLE *L3GetBestRoute(L3SW *s, UINT ip)
+{
+ UINT i;
+ UINT max_mask = 0;
+ UINT min_metric = INFINITE;
+ L3TABLE *ret = NULL;
+ // Validate arguments
+ if (s == NULL || ip == 0)
+ {
+ return NULL;
+ }
+
+ // 1st condition: Choose the one which have the largest subnet mask
+ // 2nd condition: Choose the one which have the smallest metric
+ for (i = 0;i < LIST_NUM(s->TableList);i++)
+ {
+ L3TABLE *t = LIST_DATA(s->TableList, i);
+
+ if ((t->NetworkAddress & t->SubnetMask) == (ip & t->SubnetMask))
+ {
+ if (t->SubnetMask >= max_mask)
+ {
+ max_mask = t->SubnetMask;
+ if (min_metric >= t->Metric)
+ {
+ min_metric = t->Metric;
+ ret = t;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+// Initialize the Layer-3 interface
+void L3InitInterface(L3IF *f)
+{
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ // MAC address generation
+ L3GenerateMacAddress(f);
+
+ // List generation
+ f->ArpTable = NewList(CmpL3ArpEntry);
+ f->ArpWaitTable = NewList(CmpL3ArpWaitTable);
+ f->IpPacketQueue = NewQueue();
+ f->IpWaitList = NewList(NULL);
+ f->SendQueue = NewQueue();
+}
+
+// Release the Layer-3 interface
+void L3FreeInterface(L3IF *f)
+{
+ UINT i;
+ L3PACKET *p;
+ PKT *pkt;
+ // Validate arguments
+ if (f == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(f->ArpTable);i++)
+ {
+ L3ARPENTRY *a = LIST_DATA(f->ArpTable, i);
+ Free(a);
+ }
+ ReleaseList(f->ArpTable);
+ f->ArpTable = NULL;
+
+ for (i = 0;i < LIST_NUM(f->ArpWaitTable);i++)
+ {
+ L3ARPWAIT *w = LIST_DATA(f->ArpWaitTable, i);
+ Free(w);
+ }
+ ReleaseList(f->ArpWaitTable);
+ f->ArpWaitTable = NULL;
+
+ while (p = GetNext(f->IpPacketQueue))
+ {
+ Free(p->Packet->PacketData);
+ FreePacket(p->Packet);
+ Free(p);
+ }
+ ReleaseQueue(f->IpPacketQueue);
+ f->IpPacketQueue = NULL;
+
+ for (i = 0;i < LIST_NUM(f->IpWaitList);i++)
+ {
+ L3PACKET *p = LIST_DATA(f->IpWaitList, i);
+ Free(p->Packet->PacketData);
+ FreePacket(p->Packet);
+ Free(p);
+ }
+ ReleaseList(f->IpWaitList);
+ f->IpWaitList = NULL;
+
+ while (pkt = GetNext(f->SendQueue))
+ {
+ Free(pkt->PacketData);
+ FreePacket(pkt);
+ }
+ ReleaseQueue(f->SendQueue);
+ f->SendQueue = NULL;
+}
+
+// Layer-3 interface thread
+void L3IfThread(THREAD *t, void *param)
+{
+ L3IF *f;
+ CONNECTION *c;
+ SESSION *s;
+ POLICY *policy;
+ char tmp[MAX_SIZE];
+ char name[MAX_SIZE];
+ char username[MAX_SIZE];
+ // Validate arguments
+ if (t == NULL || param == NULL)
+ {
+ return;
+ }
+
+ f = (L3IF *)param;
+
+ StrCpy(username, sizeof(username), L3_USERNAME);
+ if (f->Switch != NULL)
+ {
+ StrCat(username, sizeof(username), f->Switch->Name);
+ }
+
+ // Create a connection
+ c = NewServerConnection(f->Switch->Cedar, NULL, t);
+ c->Protocol = CONNECTION_HUB_LAYER3;
+
+ // Create a Session
+ policy = ClonePolicy(GetDefaultPolicy());
+ // Not to limit the number of broadcast by policy
+ policy->NoBroadcastLimiter = true;
+ s = NewServerSession(f->Switch->Cedar, c, f->Hub, username, policy);
+ c->Session = s;
+
+ ReleaseConnection(c);
+
+ // Determine the name of the session
+ GetMachineHostName(tmp, sizeof(tmp));
+ if (f->Switch->Cedar->Server->ServerType == SERVER_TYPE_STANDALONE)
+ {
+ Format(name, sizeof(name), "SID-L3-%s-%u", f->Switch->Name, Inc(f->Hub->SessionCounter));
+ }
+ else
+ {
+ Format(name, sizeof(name), "SID-L3-%s-%s-%u", tmp, f->Switch->Name, Inc(f->Hub->SessionCounter));
+ }
+ ConvertSafeFileName(name, sizeof(name), name);
+ StrUpper(name);
+
+ Free(s->Name);
+ s->Name = CopyStr(name);
+
+ s->L3SwitchMode = true;
+ s->L3If = f;
+
+ if (s->Username != NULL)
+ {
+ Free(s->Username);
+ }
+ s->Username = CopyStr(username);
+
+ StrCpy(s->UserNameReal, sizeof(s->UserNameReal), username);
+
+ f->Session = s;
+ AddRef(s->ref);
+
+ // Notify the initialization completion
+ NoticeThreadInit(t);
+
+ // Session main process
+ SessionMain(s);
+
+ // Release the session
+ ReleaseSession(s);
+}
+
+// Initialize all Layer-3 interfaces
+void L3InitAllInterfaces(L3SW *s)
+{
+ UINT i;
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *f = LIST_DATA(s->IfList, i);
+ THREAD *t;
+
+ L3InitInterface(f);
+
+ f->Hub = GetHub(s->Cedar, f->HubName);
+ t = NewThread(L3IfThread, f);
+ WaitThreadInit(t);
+ ReleaseThread(t);
+ }
+}
+
+// Release all Layer-3 interfaces
+void L3FreeAllInterfaces(L3SW *s)
+{
+ UINT i;
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *f = LIST_DATA(s->IfList, i);
+
+ ReleaseHub(f->Hub);
+ f->Hub = NULL;
+ ReleaseSession(f->Session);
+ f->Session = NULL;
+
+ L3FreeInterface(f);
+ }
+}
+
+// Layer-3 test
+void L3Test(SERVER *s)
+{
+ L3SW *ss = L3AddSw(s->Cedar, "TEST");
+ L3AddIf(ss, "DEFAULT", 0x0101a8c0, 0x00ffffff);
+ L3AddIf(ss, "DEFAULT2", 0x0102a8c0, 0x00ffffff);
+ L3SwStart(ss);
+ ReleaseL3Sw(ss);
+}
+
+// Layer-3 switch thread
+void L3SwThread(THREAD *t, void *param)
+{
+ L3SW *s;
+ bool shutdown_now = false;
+ // Validate arguments
+ if (t == NULL || param == NULL)
+ {
+ return;
+ }
+
+ s = (L3SW *)param;
+
+ s->Active = true;
+
+ NoticeThreadInit(t);
+
+ // Operation start
+ SLog(s->Cedar, "L3_SWITCH_START", s->Name);
+
+ while (s->Halt == false)
+ {
+ if (s->Online == false)
+ {
+ // Because the L3 switch is off-line now,
+ // attempt to make it on-line periodically
+ LockList(s->Cedar->HubList);
+ {
+ Lock(s->lock);
+ {
+ UINT i;
+ UINT n = 0;
+ bool all_exists = true;
+ if (LIST_NUM(s->IfList) == 0)
+ {
+ // Don't operate if there is no interface
+ all_exists = false;
+ }
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *f = LIST_DATA(s->IfList, i);
+ HUB *h = GetHub(s->Cedar, f->HubName);
+
+ if (h != NULL)
+ {
+ if (h->Offline || h->Type == HUB_TYPE_FARM_DYNAMIC)
+ {
+ all_exists = false;
+ }
+ else
+ {
+ n++;
+ }
+ ReleaseHub(h);
+ }
+ else
+ {
+ all_exists = false;
+ }
+ }
+
+ if (all_exists && n >= 1)
+ {
+ // Start the operation because all Virtual HUBs for
+ // interfaces are enabled
+ SLog(s->Cedar, "L3_SWITCH_ONLINE", s->Name);
+ L3InitAllInterfaces(s);
+ s->Online = true;
+ }
+ }
+ Unlock(s->lock);
+ }
+ UnlockList(s->Cedar->HubList);
+ }
+ else
+ {
+ // Examine periodically whether all sessions terminated
+ UINT i;
+ bool any_halted = false;
+ LIST *o = NULL;
+
+SHUTDOWN:
+
+ Lock(s->lock);
+ {
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *f = LIST_DATA(s->IfList, i);
+ if (f->Session->Halt || f->Hub->Offline != false)
+ {
+ any_halted = true;
+ break;
+ }
+ }
+
+ if (shutdown_now)
+ {
+ any_halted = true;
+ }
+
+ if (any_halted)
+ {
+ SLog(s->Cedar, "L3_SWITCH_OFFLINE", s->Name);
+ o = NewListFast(NULL);
+ // If there is any terminated session, terminate all sessions
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *f = LIST_DATA(s->IfList, i);
+ Insert(o, f->Session);
+ }
+
+ // Restore to the offline
+ s->Online = false;
+ }
+ }
+ Unlock(s->lock);
+
+ if (o != NULL)
+ {
+ UINT i;
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ SESSION *s = LIST_DATA(o, i);
+ StopSession(s);
+ }
+ L3FreeAllInterfaces(s);
+ ReleaseList(o);
+ o = NULL;
+ }
+ }
+
+ SleepThread(50);
+ }
+
+ if (s->Online != false)
+ {
+ shutdown_now = true;
+ goto SHUTDOWN;
+ }
+
+ // Stop the operation
+ SLog(s->Cedar, "L3_SWITCH_STOP", s->Name);
+}
+
+// Start a Layer-3 switch
+void L3SwStart(L3SW *s)
+{
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ Lock(s->lock);
+ {
+ if (s->Active == false)
+ {
+ // Start if there is registered interface
+ if (LIST_NUM(s->IfList) >= 1)
+ {
+ s->Halt = false;
+
+ // Create a thread
+ s->Thread = NewThread(L3SwThread, s);
+ WaitThreadInit(s->Thread);
+ }
+ }
+ }
+ Unlock(s->lock);
+}
+
+// Stop the Layer-3 switch
+void L3SwStop(L3SW *s)
+{
+ THREAD *t = NULL;
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ Lock(s->lock);
+ {
+ if (s->Active == false)
+ {
+ Unlock(s->lock);
+ return;
+ }
+
+ s->Halt = true;
+
+ t = s->Thread;
+
+ s->Active = false;
+ }
+ Unlock(s->lock);
+
+ WaitThread(t, INFINITE);
+ ReleaseThread(t);
+}
+
+// Add a Layer-3 switch
+L3SW *L3AddSw(CEDAR *c, char *name)
+{
+ L3SW *s = NULL;
+ // Validate arguments
+ if (c == NULL || name == NULL)
+ {
+ return NULL;
+ }
+
+ LockList(c->L3SwList);
+ {
+ s = L3GetSw(c, name);
+
+ if (s == NULL)
+ {
+ s = NewL3Sw(c, name);
+
+ Insert(c->L3SwList, s);
+
+ AddRef(s->ref);
+ }
+ else
+ {
+ ReleaseL3Sw(s);
+ s = NULL;
+ }
+ }
+ UnlockList(c->L3SwList);
+
+ return s;
+}
+
+// Delete the Layer-3 switch
+bool L3DelSw(CEDAR *c, char *name)
+{
+ L3SW *s;
+ bool ret = false;
+ // Validate arguments
+ if (c == NULL || name == NULL)
+ {
+ return false;
+ }
+
+ LockList(c->L3SwList);
+ {
+ s = L3GetSw(c, name);
+
+ if (s != NULL)
+ {
+ // Stop and delete
+ L3SwStop(s);
+ Delete(c->L3SwList, s);
+ ReleaseL3Sw(s);
+ ReleaseL3Sw(s);
+
+ ret = true;
+ }
+ }
+ UnlockList(c->L3SwList);
+
+ return ret;
+}
+
+
+// Delete the routing table
+bool L3DelTable(L3SW *s, L3TABLE *tbl)
+{
+ bool ret = false;
+ // Validate arguments
+ if (s == NULL || tbl == NULL)
+ {
+ return false;
+ }
+
+ Lock(s->lock);
+ {
+ if (s->Active == false)
+ {
+ L3TABLE *t = Search(s->TableList, tbl);
+
+ if (t != NULL)
+ {
+ Delete(s->TableList, t);
+ Free(t);
+
+ ret = true;
+ }
+ }
+ }
+ Unlock(s->lock);
+
+ return ret;
+}
+
+// Add to the routing table
+bool L3AddTable(L3SW *s, L3TABLE *tbl)
+{
+ bool ret = false;
+ // Validate arguments
+ if (s == NULL || tbl == NULL)
+ {
+ return false;
+ }
+
+ if (tbl->Metric == 0 || tbl->GatewayAddress == 0 || tbl->GatewayAddress == 0xffffffff)
+ {
+ return false;
+ }
+
+ Lock(s->lock);
+ {
+ if (LIST_NUM(s->TableList) >= GetServerCapsInt(s->Cedar->Server, "i_max_l3_table"))
+ {
+ // Too many
+ }
+ else
+ {
+ // Create
+ if (s->Active == false)
+ {
+ if (Search(s->TableList, tbl) == NULL)
+ {
+ L3TABLE *t = ZeroMalloc(sizeof(L3TABLE));
+
+ Copy(t, tbl, sizeof(L3TABLE));
+
+ Insert(s->TableList, t);
+
+ ret = true;
+ }
+ }
+ }
+ }
+ Unlock(s->lock);
+
+ return ret;
+}
+
+// Get the L3 switch
+L3SW *L3GetSw(CEDAR *c, char *name)
+{
+ L3SW t, *s;
+ // Validate arguments
+ if (c == NULL || name == NULL)
+ {
+ return NULL;
+ }
+
+ Zero(&t, sizeof(t));
+ StrCpy(t.Name, sizeof(t.Name), name);
+
+ LockList(c->L3SwList);
+ {
+ s = Search(c->L3SwList, &t);
+ }
+ UnlockList(c->L3SwList);
+
+ if (s != NULL)
+ {
+ AddRef(s->ref);
+ }
+
+ return s;
+}
+
+// Get the interface that is connected to the specified Virtual HUB from the L3 switch
+L3IF *L3SearchIf(L3SW *s, char *hubname)
+{
+ L3IF t, *f;
+ // Validate arguments
+ if (s == NULL || hubname == NULL)
+ {
+ return NULL;
+ }
+
+ Zero(&t, sizeof(t));
+ StrCpy(t.HubName, sizeof(t.HubName), hubname);
+
+ f = Search(s->IfList, &t);
+
+ return f;
+}
+
+// Delete the interface
+bool L3DelIf(L3SW *s, char *hubname)
+{
+ L3IF *f;
+ bool ret = false;
+ // Validate arguments
+ if (s == NULL || hubname == NULL)
+ {
+ return false;
+ }
+
+ Lock(s->lock);
+ {
+ if (s->Active == false)
+ {
+ f = L3SearchIf(s, hubname);
+
+ if (f != NULL)
+ {
+ // Remove
+ Delete(s->IfList, f);
+ Free(f);
+
+ ret = true;
+ }
+ }
+ }
+ Unlock(s->lock);
+
+ return ret;
+}
+
+// Add an interface
+bool L3AddIf(L3SW *s, char *hubname, UINT ip, UINT subnet)
+{
+ L3IF *f;
+ bool ret = false;
+ // Validate arguments
+ if (s == NULL || hubname == NULL || IsSafeStr(hubname) == false ||
+ ip == 0 || ip == 0xffffffff)
+ {
+ return false;
+ }
+
+ Lock(s->lock);
+ {
+ if (LIST_NUM(s->TableList) >= GetServerCapsInt(s->Cedar->Server, "i_max_l3_if"))
+ {
+ // Too many
+ }
+ else
+ {
+ if (s->Active == false)
+ {
+ // Examine whether the interface is already in the same Virtual HUB
+ if (L3SearchIf(s, hubname) == NULL)
+ {
+ // Add
+ f = ZeroMalloc(sizeof(L3IF));
+
+ f->Switch = s;
+ StrCpy(f->HubName, sizeof(f->HubName), hubname);
+ f->IpAddress = ip;
+ f->SubnetMask = subnet;
+
+ Insert(s->IfList, f);
+
+ ret = true;
+ }
+ }
+ }
+ }
+ Unlock(s->lock);
+
+ return ret;
+}
+
+// Clean-up the L3 switch
+void CleanupL3Sw(L3SW *s)
+{
+ UINT i;
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(s->IfList);i++)
+ {
+ L3IF *f = LIST_DATA(s->IfList, i);
+ Free(f);
+ }
+ ReleaseList(s->IfList);
+
+ for (i = 0;i < LIST_NUM(s->TableList);i++)
+ {
+ L3TABLE *t = LIST_DATA(s->TableList, i);
+ Free(t);
+ }
+ ReleaseList(s->TableList);
+
+ DeleteLock(s->lock);
+ Free(s);
+}
+
+// Release the L3 switch
+void ReleaseL3Sw(L3SW *s)
+{
+ // Validate arguments
+ if (s == NULL)
+ {
+ return;
+ }
+
+ if (Release(s->ref) == 0)
+ {
+ CleanupL3Sw(s);
+ }
+}
+
+// Create a new L3 switch
+L3SW *NewL3Sw(CEDAR *c, char *name)
+{
+ L3SW *o;
+ // Validate arguments
+ if (c == NULL || name == NULL)
+ {
+ return NULL;
+ }
+
+ o = ZeroMalloc(sizeof(L3SW));
+
+ StrCpy(o->Name, sizeof(o->Name), name);
+
+ o->lock = NewLock();
+ o->ref = NewRef();
+ o->Cedar = c;
+ o->Active = false;
+
+ o->IfList = NewList(CmpL3If);
+ o->TableList = NewList(CmpL3Table);
+
+ return o;
+}
+
+// Stop all L3 switches in the Cedar
+void L3FreeAllSw(CEDAR *c)
+{
+ LIST *o;
+ UINT i;
+ // Validate arguments
+ if (c == NULL)
+ {
+ return;
+ }
+
+ o = NewListFast(NULL);
+
+ LockList(c->L3SwList);
+ {
+ for (i = 0;i < LIST_NUM(c->L3SwList);i++)
+ {
+ L3SW *s = LIST_DATA(c->L3SwList, i);
+ Insert(o, CopyStr(s->Name));
+ }
+
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ char *name = LIST_DATA(o, i);
+
+ L3DelSw(c, name);
+
+ Free(name);
+ }
+
+ ReleaseList(o);
+ }
+ UnlockList(c->L3SwList);
+}
+
+// Stop the L3 switch function of the Cedar
+void FreeCedarLayer3(CEDAR *c)
+{
+ // Validate arguments
+ if (c == NULL)
+ {
+ return;
+ }
+
+ ReleaseList(c->L3SwList);
+ c->L3SwList = NULL;
+}
+
+// Start the L3 switch function of the Cedar
+void InitCedarLayer3(CEDAR *c)
+{
+ // Validate arguments
+ if (c == NULL)
+ {
+ return;
+ }
+
+ c->L3SwList = NewList(CmpL3Sw);
+}
+
+// Interface comparison function
+int CmpL3If(void *p1, void *p2)
+{
+ L3IF *f1, *f2;
+ if (p1 == NULL || p2 == NULL)
+ {
+ return 0;
+ }
+ f1 = *(L3IF **)p1;
+ f2 = *(L3IF **)p2;
+ if (f1 == NULL || f2 == NULL)
+ {
+ return 0;
+ }
+
+ return StrCmpi(f1->HubName, f2->HubName);
+}
+
+// Routing table entry comparison function
+int CmpL3Table(void *p1, void *p2)
+{
+ L3TABLE *t1, *t2;
+ if (p1 == NULL || p2 == NULL)
+ {
+ return 0;
+ }
+ t1 = *(L3TABLE **)p1;
+ t2 = *(L3TABLE **)p2;
+ if (t1 == NULL || t2 == NULL)
+ {
+ return 0;
+ }
+
+ if (t1->NetworkAddress > t2->NetworkAddress)
+ {
+ return 1;
+ }
+ else if (t1->NetworkAddress < t2->NetworkAddress)
+ {
+ return -1;
+ }
+ else if (t1->SubnetMask > t2->SubnetMask)
+ {
+ return 1;
+ }
+ else if (t1->SubnetMask < t2->SubnetMask)
+ {
+ return -1;
+ }
+ else if (t1->GatewayAddress > t2->GatewayAddress)
+ {
+ return 1;
+ }
+ else if (t1->GatewayAddress < t2->GatewayAddress)
+ {
+ return -1;
+ }
+ else if (t1->Metric > t2->Metric)
+ {
+ return 1;
+ }
+ else if (t1->Metric < t2->Metric)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// L3SW comparison function
+int CmpL3Sw(void *p1, void *p2)
+{
+ L3SW *s1, *s2;
+ if (p1 == NULL || p2 == NULL)
+ {
+ return 0;
+ }
+ s1 = *(L3SW **)p1;
+ s2 = *(L3SW **)p2;
+ if (s1 == NULL || s2 == NULL)
+ {
+ return 0;
+ }
+
+ return StrCmpi(s1->Name, s2->Name);
+}
+
+// ARP waiting entry comparison function
+int CmpL3ArpWaitTable(void *p1, void *p2)
+{
+ L3ARPWAIT *w1, *w2;
+ if (p1 == NULL || p2 == NULL)
+ {
+ return 0;
+ }
+ w1 = *(L3ARPWAIT **)p1;
+ w2 = *(L3ARPWAIT **)p2;
+ if (w1 == NULL || w2 == NULL)
+ {
+ return 0;
+ }
+ if (w1->IpAddress > w2->IpAddress)
+ {
+ return 1;
+ }
+ else if (w1->IpAddress < w2->IpAddress)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// ARP entries comparison function
+int CmpL3ArpEntry(void *p1, void *p2)
+{
+ L3ARPENTRY *e1, *e2;
+ if (p1 == NULL || p2 == NULL)
+ {
+ return 0;
+ }
+ e1 = *(L3ARPENTRY **)p1;
+ e2 = *(L3ARPENTRY **)p2;
+ if (e1 == NULL || e2 == NULL)
+ {
+ return 0;
+ }
+ if (e1->IpAddress > e2->IpAddress)
+ {
+ return 1;
+ }
+ else if (e1->IpAddress < e2->IpAddress)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+// Developed by SoftEther VPN Project at University of Tsukuba in Japan.
+// Department of Computer Science has dozens of overly-enthusiastic geeks.
+// Join us: http://www.tsukuba.ac.jp/english/admission/
|