1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-09-19 10:10:40 +03:00
SoftEtherVPN/src/Cedar/Layer3.c
2021-04-05 04:48:25 +02:00

2114 lines
40 KiB
C

// SoftEther VPN Source Code - Developer Edition Master Branch
// Cedar Communication Module
// Layer3.c
// Layer-3 switch module
#include "Layer3.h"
#include "Connection.h"
#include "Hub.h"
#include "Server.h"
#include "Session.h"
#include "Virtual.h"
#include "Mayaqua/FileIO.h"
#include "Mayaqua/Kernel.h"
#include "Mayaqua/Memory.h"
#include "Mayaqua/Object.h"
#include "Mayaqua/Str.h"
#include "Mayaqua/TcpIp.h"
#include "Mayaqua/Tick64.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->FlagsAndFragmentOffset[0] = ip->FlagsAndFragmentOffset[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 at destination
// 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;
}
if (!((f->IpAddress & f->SubnetMask) == (ip & f->SubnetMask)))
{
// Outside the subnet
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);
Sha0(hash, b->Buf, b->Size);
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 = CalcChecksumForIPv4(f->IpAddress, dest_ip, 0x11, udp, sizeof(UDP_HEADER) + sizeof(beacon_str), 0);
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;
}
}