// 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. // // // SOURCE CODE CONTRIBUTION // ------------------------ // // Your contribution to SoftEther VPN Project is much appreciated. // Please send patches to us through GitHub. // Read the SoftEther VPN Patch Acceptance Policy in advance: // http://www.softether.org/5-download/src/9.patch // // // 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/