mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-22 14:22:23 +00:00
600 lines
14 KiB
C
600 lines
14 KiB
C
/*
|
|
* TAP-Windows -- A kernel driver to provide virtual tap
|
|
* device functionality on Windows.
|
|
*
|
|
* This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
|
|
*
|
|
* This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
|
|
* and is released under the GPL version 2 (see below).
|
|
*
|
|
* 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
|
|
* along with this program (see the file COPYING included with this
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
//=========================
|
|
// Code to set DHCP options
|
|
//=========================
|
|
|
|
VOID
|
|
SetDHCPOpt (DHCPMsg *m, void *data, unsigned int len)
|
|
{
|
|
if (!m->overflow)
|
|
{
|
|
if (m->optlen + len <= DHCP_OPTIONS_BUFFER_SIZE)
|
|
{
|
|
if (len)
|
|
{
|
|
NdisMoveMemory (m->msg.options + m->optlen, data, len);
|
|
m->optlen += len;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m->overflow = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SetDHCPOpt0 (DHCPMsg *msg, int type)
|
|
{
|
|
DHCPOPT0 opt;
|
|
opt.type = (UCHAR) type;
|
|
SetDHCPOpt (msg, &opt, sizeof (opt));
|
|
}
|
|
|
|
VOID
|
|
SetDHCPOpt8 (DHCPMsg *msg, int type, ULONG data)
|
|
{
|
|
DHCPOPT8 opt;
|
|
opt.type = (UCHAR) type;
|
|
opt.len = sizeof (opt.data);
|
|
opt.data = (UCHAR) data;
|
|
SetDHCPOpt (msg, &opt, sizeof (opt));
|
|
}
|
|
|
|
VOID
|
|
SetDHCPOpt32 (DHCPMsg *msg, int type, ULONG data)
|
|
{
|
|
DHCPOPT32 opt;
|
|
opt.type = (UCHAR) type;
|
|
opt.len = sizeof (opt.data);
|
|
opt.data = data;
|
|
SetDHCPOpt (msg, &opt, sizeof (opt));
|
|
}
|
|
|
|
//==============
|
|
// Checksum code
|
|
//==============
|
|
|
|
USHORT
|
|
ip_checksum (const UCHAR *buf, const int len_ip_header)
|
|
{
|
|
USHORT word16;
|
|
ULONG sum = 0;
|
|
int i;
|
|
|
|
// make 16 bit words out of every two adjacent 8 bit words in the packet
|
|
// and add them up
|
|
for (i = 0; i < len_ip_header - 1; i += 2) {
|
|
word16 = ((buf[i] << 8) & 0xFF00) + (buf[i+1] & 0xFF);
|
|
sum += (ULONG) word16;
|
|
}
|
|
|
|
// take only 16 bits out of the 32 bit sum and add up the carries
|
|
while (sum >> 16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
// one's complement the result
|
|
return ((USHORT) ~sum);
|
|
}
|
|
|
|
USHORT
|
|
udp_checksum (const UCHAR *buf,
|
|
const int len_udp,
|
|
const UCHAR *src_addr,
|
|
const UCHAR *dest_addr)
|
|
{
|
|
USHORT word16;
|
|
ULONG sum = 0;
|
|
int i;
|
|
|
|
// make 16 bit words out of every two adjacent 8 bit words and
|
|
// calculate the sum of all 16 bit words
|
|
for (i = 0; i < len_udp; i += 2){
|
|
word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
|
|
sum += word16;
|
|
}
|
|
|
|
// add the UDP pseudo header which contains the IP source and destination addresses
|
|
for (i = 0; i < 4; i += 2){
|
|
word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
|
|
sum += word16;
|
|
}
|
|
for (i = 0; i < 4; i += 2){
|
|
word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
|
|
sum += word16;
|
|
}
|
|
|
|
// the protocol number and the length of the UDP packet
|
|
sum += (USHORT) IPPROTO_UDP + (USHORT) len_udp;
|
|
|
|
// keep only the last 16 bits of the 32 bit calculated sum and add the carries
|
|
while (sum >> 16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
// Take the one's complement of sum
|
|
return ((USHORT) ~sum);
|
|
}
|
|
|
|
//================================
|
|
// Set IP and UDP packet checksums
|
|
//================================
|
|
|
|
VOID
|
|
SetChecksumDHCPMsg (DHCPMsg *m)
|
|
{
|
|
// Set IP checksum
|
|
m->msg.pre.ip.check = htons (ip_checksum ((UCHAR *) &m->msg.pre.ip, sizeof (IPHDR)));
|
|
|
|
// Set UDP Checksum
|
|
m->msg.pre.udp.check = htons (udp_checksum ((UCHAR *) &m->msg.pre.udp,
|
|
sizeof (UDPHDR) + sizeof (DHCP) + m->optlen,
|
|
(UCHAR *)&m->msg.pre.ip.saddr,
|
|
(UCHAR *)&m->msg.pre.ip.daddr));
|
|
}
|
|
|
|
//===================
|
|
// DHCP message tests
|
|
//===================
|
|
|
|
int
|
|
GetDHCPMessageType (const DHCP *dhcp, const int optlen)
|
|
{
|
|
const UCHAR *p = (UCHAR *) (dhcp + 1);
|
|
int i;
|
|
|
|
for (i = 0; i < optlen; ++i)
|
|
{
|
|
const UCHAR type = p[i];
|
|
const int room = optlen - i - 1;
|
|
if (type == DHCP_END) // didn't find what we were looking for
|
|
return -1;
|
|
else if (type == DHCP_PAD) // no-operation
|
|
;
|
|
else if (type == DHCP_MSG_TYPE) // what we are looking for
|
|
{
|
|
if (room >= 2)
|
|
{
|
|
if (p[i+1] == 1) // message length should be 1
|
|
return p[i+2]; // return message type
|
|
}
|
|
return -1;
|
|
}
|
|
else // some other message
|
|
{
|
|
if (room >= 1)
|
|
{
|
|
const int len = p[i+1]; // get message length
|
|
i += (len + 1); // advance to next message
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
BOOLEAN
|
|
DHCPMessageOurs (const TapAdapterPointer p_Adapter,
|
|
const ETH_HEADER *eth,
|
|
const IPHDR *ip,
|
|
const UDPHDR *udp,
|
|
const DHCP *dhcp)
|
|
{
|
|
// Must be UDPv4 protocol
|
|
if (!(eth->proto == htons (ETH_P_IP) && ip->protocol == IPPROTO_UDP))
|
|
return FALSE;
|
|
|
|
// Source MAC must be our adapter
|
|
if (!MAC_EQUAL (eth->src, p_Adapter->m_MAC))
|
|
return FALSE;
|
|
|
|
// Dest MAC must be either broadcast or our virtual DHCP server
|
|
if (!(MAC_EQUAL (eth->dest, p_Adapter->m_MAC_Broadcast)
|
|
|| MAC_EQUAL (eth->dest, p_Adapter->m_dhcp_server_mac)))
|
|
return FALSE;
|
|
|
|
// Port numbers must be correct
|
|
if (!(udp->dest == htons (BOOTPS_PORT)
|
|
&& udp->source == htons (BOOTPC_PORT)))
|
|
return FALSE;
|
|
|
|
// Hardware address must be MAC addr sized
|
|
if (!(dhcp->hlen == sizeof (MACADDR)))
|
|
return FALSE;
|
|
|
|
// Hardware address must match our adapter
|
|
if (!MAC_EQUAL (eth->src, dhcp->chaddr))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//=====================================================
|
|
// Build all of DHCP packet except for DHCP options.
|
|
// Assume that *p has been zeroed before we are called.
|
|
//=====================================================
|
|
|
|
VOID
|
|
BuildDHCPPre (const TapAdapterPointer a,
|
|
DHCPPre *p,
|
|
const ETH_HEADER *eth,
|
|
const IPHDR *ip,
|
|
const UDPHDR *udp,
|
|
const DHCP *dhcp,
|
|
const int optlen,
|
|
const int type)
|
|
{
|
|
// Should we broadcast or direct to a specific MAC / IP address?
|
|
const BOOLEAN broadcast = (type == DHCPNAK
|
|
|| MAC_EQUAL (eth->dest, a->m_MAC_Broadcast));
|
|
// Build ethernet header
|
|
|
|
COPY_MAC (p->eth.src, a->m_dhcp_server_mac);
|
|
|
|
if (broadcast)
|
|
COPY_MAC (p->eth.dest, a->m_MAC_Broadcast);
|
|
else
|
|
COPY_MAC (p->eth.dest, eth->src);
|
|
|
|
p->eth.proto = htons (ETH_P_IP);
|
|
|
|
// Build IP header
|
|
|
|
p->ip.version_len = (4 << 4) | (sizeof (IPHDR) >> 2);
|
|
p->ip.tos = 0;
|
|
p->ip.tot_len = htons (sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen);
|
|
p->ip.id = 0;
|
|
p->ip.frag_off = 0;
|
|
p->ip.ttl = 16;
|
|
p->ip.protocol = IPPROTO_UDP;
|
|
p->ip.check = 0;
|
|
p->ip.saddr = a->m_dhcp_server_ip;
|
|
|
|
if (broadcast)
|
|
p->ip.daddr = ~0;
|
|
else
|
|
p->ip.daddr = a->m_dhcp_addr;
|
|
|
|
// Build UDP header
|
|
|
|
p->udp.source = htons (BOOTPS_PORT);
|
|
p->udp.dest = htons (BOOTPC_PORT);
|
|
p->udp.len = htons (sizeof (UDPHDR) + sizeof (DHCP) + optlen);
|
|
p->udp.check = 0;
|
|
|
|
// Build DHCP response
|
|
|
|
p->dhcp.op = BOOTREPLY;
|
|
p->dhcp.htype = 1;
|
|
p->dhcp.hlen = sizeof (MACADDR);
|
|
p->dhcp.hops = 0;
|
|
p->dhcp.xid = dhcp->xid;
|
|
p->dhcp.secs = 0;
|
|
p->dhcp.flags = 0;
|
|
p->dhcp.ciaddr = 0;
|
|
|
|
if (type == DHCPNAK)
|
|
p->dhcp.yiaddr = 0;
|
|
else
|
|
p->dhcp.yiaddr = a->m_dhcp_addr;
|
|
|
|
p->dhcp.siaddr = a->m_dhcp_server_ip;
|
|
p->dhcp.giaddr = 0;
|
|
COPY_MAC (p->dhcp.chaddr, eth->src);
|
|
p->dhcp.magic = htonl (0x63825363);
|
|
}
|
|
//=============================
|
|
// Build specific DHCP messages
|
|
//=============================
|
|
|
|
VOID
|
|
SendDHCPMsg (const TapAdapterPointer a,
|
|
const int type,
|
|
const ETH_HEADER *eth,
|
|
const IPHDR *ip,
|
|
const UDPHDR *udp,
|
|
const DHCP *dhcp)
|
|
{
|
|
DHCPMsg *pkt;
|
|
|
|
if (!(type == DHCPOFFER || type == DHCPACK || type == DHCPNAK))
|
|
{
|
|
DEBUGP (("[TAP] SendDHCPMsg: Bad DHCP type: %d\n", type));
|
|
return;
|
|
}
|
|
|
|
pkt = (DHCPMsg *) MemAlloc (sizeof (DHCPMsg), TRUE);
|
|
|
|
if (pkt)
|
|
{
|
|
//-----------------------
|
|
// Build DHCP options
|
|
//-----------------------
|
|
|
|
// Message Type
|
|
SetDHCPOpt8 (pkt, DHCP_MSG_TYPE, type);
|
|
|
|
// Server ID
|
|
SetDHCPOpt32 (pkt, DHCP_SERVER_ID, a->m_dhcp_server_ip);
|
|
|
|
if (type == DHCPOFFER || type == DHCPACK)
|
|
{
|
|
// Lease Time
|
|
SetDHCPOpt32 (pkt, DHCP_LEASE_TIME, htonl (a->m_dhcp_lease_time));
|
|
|
|
// Netmask
|
|
SetDHCPOpt32 (pkt, DHCP_NETMASK, a->m_dhcp_netmask);
|
|
|
|
// Other user-defined options
|
|
SetDHCPOpt (pkt,
|
|
a->m_dhcp_user_supplied_options_buffer,
|
|
a->m_dhcp_user_supplied_options_buffer_len);
|
|
}
|
|
|
|
// End
|
|
SetDHCPOpt0 (pkt, DHCP_END);
|
|
|
|
if (!DHCPMSG_OVERFLOW (pkt))
|
|
{
|
|
// The initial part of the DHCP message (not including options) gets built here
|
|
BuildDHCPPre (a,
|
|
&pkt->msg.pre,
|
|
eth,
|
|
ip,
|
|
udp,
|
|
dhcp,
|
|
DHCPMSG_LEN_OPT (pkt),
|
|
type);
|
|
|
|
SetChecksumDHCPMsg (pkt);
|
|
|
|
DUMP_PACKET ("DHCPMsg",
|
|
DHCPMSG_BUF (pkt),
|
|
DHCPMSG_LEN_FULL (pkt));
|
|
|
|
// Return DHCP response to kernel
|
|
InjectPacketDeferred (a,
|
|
DHCPMSG_BUF (pkt),
|
|
DHCPMSG_LEN_FULL (pkt));
|
|
}
|
|
else
|
|
{
|
|
DEBUGP (("[TAP] SendDHCPMsg: DHCP buffer overflow\n"));
|
|
}
|
|
|
|
MemFree (pkt, sizeof (DHCPMsg));
|
|
}
|
|
}
|
|
|
|
//===================================================================
|
|
// Handle a BOOTPS packet produced by the local system to
|
|
// resolve the address/netmask of this adapter.
|
|
// If we are in TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode, reply
|
|
// to the message. Return TRUE if we processed the passed
|
|
// message, so that downstream stages can ignore it.
|
|
//===================================================================
|
|
|
|
BOOLEAN
|
|
ProcessDHCP (TapAdapterPointer p_Adapter,
|
|
const ETH_HEADER *eth,
|
|
const IPHDR *ip,
|
|
const UDPHDR *udp,
|
|
const DHCP *dhcp,
|
|
int optlen)
|
|
{
|
|
int msg_type;
|
|
|
|
// Sanity check IP header
|
|
if (!(ntohs (ip->tot_len) == sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen
|
|
&& (ntohs (ip->frag_off) & IP_OFFMASK) == 0))
|
|
return TRUE;
|
|
|
|
// Does this message belong to us?
|
|
if (!DHCPMessageOurs (p_Adapter, eth, ip, udp, dhcp))
|
|
return FALSE;
|
|
|
|
msg_type = GetDHCPMessageType (dhcp, optlen);
|
|
|
|
// Drop non-BOOTREQUEST messages
|
|
if (dhcp->op != BOOTREQUEST)
|
|
return TRUE;
|
|
|
|
// Drop any messages except DHCPDISCOVER or DHCPREQUEST
|
|
if (!(msg_type == DHCPDISCOVER || msg_type == DHCPREQUEST))
|
|
return TRUE;
|
|
|
|
// Should we reply with DHCPOFFER, DHCPACK, or DHCPNAK?
|
|
if (msg_type == DHCPREQUEST
|
|
&& ((dhcp->ciaddr && dhcp->ciaddr != p_Adapter->m_dhcp_addr)
|
|
|| !p_Adapter->m_dhcp_received_discover
|
|
|| p_Adapter->m_dhcp_bad_requests >= BAD_DHCPREQUEST_NAK_THRESHOLD))
|
|
SendDHCPMsg (p_Adapter,
|
|
DHCPNAK,
|
|
eth, ip, udp, dhcp);
|
|
else
|
|
SendDHCPMsg (p_Adapter,
|
|
(msg_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK),
|
|
eth, ip, udp, dhcp);
|
|
|
|
// Remember if we received a DHCPDISCOVER
|
|
if (msg_type == DHCPDISCOVER)
|
|
p_Adapter->m_dhcp_received_discover = TRUE;
|
|
|
|
// Is this a bad DHCPREQUEST?
|
|
if (msg_type == DHCPREQUEST && dhcp->ciaddr && dhcp->ciaddr != p_Adapter->m_dhcp_addr)
|
|
++p_Adapter->m_dhcp_bad_requests;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
const char *
|
|
message_op_text (int op)
|
|
{
|
|
switch (op)
|
|
{
|
|
case BOOTREQUEST:
|
|
return "BOOTREQUEST";
|
|
case BOOTREPLY:
|
|
return "BOOTREPLY";
|
|
default:
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
const char *
|
|
message_type_text (int type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case DHCPDISCOVER:
|
|
return "DHCPDISCOVER";
|
|
case DHCPOFFER:
|
|
return "DHCPOFFER";
|
|
case DHCPREQUEST:
|
|
return "DHCPREQUEST";
|
|
case DHCPDECLINE:
|
|
return "DHCPDECLINE";
|
|
case DHCPACK:
|
|
return "DHCPACK";
|
|
case DHCPNAK:
|
|
return "DHCPNAK";
|
|
case DHCPRELEASE:
|
|
return "DHCPRELEASE";
|
|
case DHCPINFORM:
|
|
return "DHCPINFORM";
|
|
default:
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
const char *
|
|
port_name (int port)
|
|
{
|
|
switch (port)
|
|
{
|
|
case BOOTPS_PORT:
|
|
return "BOOTPS";
|
|
case BOOTPC_PORT:
|
|
return "BOOTPC";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DumpDHCP (const ETH_HEADER *eth,
|
|
const IPHDR *ip,
|
|
const UDPHDR *udp,
|
|
const DHCP *dhcp,
|
|
const int optlen)
|
|
{
|
|
DEBUGP ((" %s", message_op_text (dhcp->op)));
|
|
DEBUGP ((" %s ", message_type_text (GetDHCPMessageType (dhcp, optlen))));
|
|
PrIP (ip->saddr);
|
|
DEBUGP ((":%s[", port_name (ntohs (udp->source))));
|
|
PrMac (eth->src);
|
|
DEBUGP (("] -> "));
|
|
PrIP (ip->daddr);
|
|
DEBUGP ((":%s[", port_name (ntohs (udp->dest))));
|
|
PrMac (eth->dest);
|
|
DEBUGP (("]"));
|
|
if (dhcp->ciaddr)
|
|
{
|
|
DEBUGP ((" ci="));
|
|
PrIP (dhcp->ciaddr);
|
|
}
|
|
if (dhcp->yiaddr)
|
|
{
|
|
DEBUGP ((" yi="));
|
|
PrIP (dhcp->yiaddr);
|
|
}
|
|
if (dhcp->siaddr)
|
|
{
|
|
DEBUGP ((" si="));
|
|
PrIP (dhcp->siaddr);
|
|
}
|
|
if (dhcp->hlen == sizeof (MACADDR))
|
|
{
|
|
DEBUGP ((" ch="));
|
|
PrMac (dhcp->chaddr);
|
|
}
|
|
|
|
DEBUGP ((" xid=0x%08x", ntohl (dhcp->xid)));
|
|
|
|
if (ntohl (dhcp->magic) != 0x63825363)
|
|
DEBUGP ((" ma=0x%08x", ntohl (dhcp->magic)));
|
|
if (dhcp->htype != 1)
|
|
DEBUGP ((" htype=%d", dhcp->htype));
|
|
if (dhcp->hops)
|
|
DEBUGP ((" hops=%d", dhcp->hops));
|
|
if (ntohs (dhcp->secs))
|
|
DEBUGP ((" secs=%d", ntohs (dhcp->secs)));
|
|
if (ntohs (dhcp->flags))
|
|
DEBUGP ((" flags=0x%04x", ntohs (dhcp->flags)));
|
|
|
|
// extra stuff
|
|
|
|
if (ip->version_len != 0x45)
|
|
DEBUGP ((" vl=0x%02x", ip->version_len));
|
|
if (ntohs (ip->tot_len) != sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen)
|
|
DEBUGP ((" tl=%d", ntohs (ip->tot_len)));
|
|
if (ntohs (udp->len) != sizeof (UDPHDR) + sizeof (DHCP) + optlen)
|
|
DEBUGP ((" ul=%d", ntohs (udp->len)));
|
|
|
|
if (ip->tos)
|
|
DEBUGP ((" tos=0x%02x", ip->tos));
|
|
if (ntohs (ip->id))
|
|
DEBUGP ((" id=0x%04x", ntohs (ip->id)));
|
|
if (ntohs (ip->frag_off))
|
|
DEBUGP ((" frag_off=0x%04x", ntohs (ip->frag_off)));
|
|
|
|
DEBUGP ((" ttl=%d", ip->ttl));
|
|
DEBUGP ((" ic=0x%04x [0x%04x]", ntohs (ip->check),
|
|
ip_checksum ((UCHAR*)ip, sizeof (IPHDR))));
|
|
DEBUGP ((" uc=0x%04x [0x%04x/%d]", ntohs (udp->check),
|
|
udp_checksum ((UCHAR *) udp,
|
|
sizeof (UDPHDR) + sizeof (DHCP) + optlen,
|
|
(UCHAR *) &ip->saddr,
|
|
(UCHAR *) &ip->daddr),
|
|
optlen));
|
|
|
|
// Options
|
|
{
|
|
const UCHAR *opt = (UCHAR *) (dhcp + 1);
|
|
int i;
|
|
|
|
DEBUGP ((" OPT"));
|
|
for (i = 0; i < optlen; ++i)
|
|
{
|
|
const UCHAR data = opt[i];
|
|
DEBUGP ((".%d", data));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* DBG */
|