You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
710 lines
16 KiB
710 lines
16 KiB
/* |
|
* 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-2014 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 |
|
*/ |
|
|
|
#include "tap.h" |
|
|
|
//========================= |
|
// Code to set DHCP options |
|
//========================= |
|
|
|
VOID |
|
SetDHCPOpt( |
|
__in DHCPMsg *m, |
|
__in void *data, |
|
__in 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( |
|
__in DHCPMsg *msg, |
|
__in int type |
|
) |
|
{ |
|
DHCPOPT0 opt; |
|
opt.type = (UCHAR) type; |
|
SetDHCPOpt (msg, &opt, sizeof (opt)); |
|
} |
|
|
|
VOID |
|
SetDHCPOpt8( |
|
__in DHCPMsg *msg, |
|
__in int type, |
|
__in ULONG data |
|
) |
|
{ |
|
DHCPOPT8 opt; |
|
opt.type = (UCHAR) type; |
|
opt.len = sizeof (opt.data); |
|
opt.data = (UCHAR) data; |
|
SetDHCPOpt (msg, &opt, sizeof (opt)); |
|
} |
|
|
|
VOID |
|
SetDHCPOpt32( |
|
__in DHCPMsg *msg, |
|
__in int type, |
|
__in 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( |
|
__in const UCHAR *buf, |
|
__in 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 ( |
|
__in const UCHAR *buf, |
|
__in const int len_udp, |
|
__in const UCHAR *src_addr, |
|
__in 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( |
|
__in 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( |
|
__in const DHCP *dhcp, |
|
__in 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 ( |
|
__in const PTAP_ADAPTER_CONTEXT Adapter, |
|
__in const ETH_HEADER *eth, |
|
__in const IPHDR *ip, |
|
__in const UDPHDR *udp, |
|
__in const DHCP *dhcp |
|
) |
|
{ |
|
// Must be UDPv4 protocol |
|
if (!(eth->proto == htons (NDIS_ETH_TYPE_IPV4) && ip->protocol == IPPROTO_UDP)) |
|
{ |
|
return FALSE; |
|
} |
|
|
|
// Source MAC must be our adapter |
|
if (!MAC_EQUAL (eth->src, Adapter->CurrentAddress)) |
|
{ |
|
return FALSE; |
|
} |
|
|
|
// Dest MAC must be either broadcast or our virtual DHCP server |
|
if (!(ETH_IS_BROADCAST(eth->dest) |
|
|| MAC_EQUAL (eth->dest, 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 ( |
|
__in const PTAP_ADAPTER_CONTEXT Adapter, |
|
__inout DHCPPre *p, |
|
__in const ETH_HEADER *eth, |
|
__in const IPHDR *ip, |
|
__in const UDPHDR *udp, |
|
__in const DHCP *dhcp, |
|
__in const int optlen, |
|
__in const int type) |
|
{ |
|
// Should we broadcast or direct to a specific MAC / IP address? |
|
const BOOLEAN broadcast = (type == DHCPNAK |
|
|| ETH_IS_BROADCAST(eth->dest)); |
|
|
|
// |
|
// Build ethernet header |
|
// |
|
ETH_COPY_NETWORK_ADDRESS (p->eth.src, Adapter->m_dhcp_server_mac); |
|
|
|
if (broadcast) |
|
{ |
|
memset(p->eth.dest,0xFF,ETH_LENGTH_OF_ADDRESS); |
|
} |
|
else |
|
{ |
|
ETH_COPY_NETWORK_ADDRESS (p->eth.dest, eth->src); |
|
} |
|
|
|
p->eth.proto = htons (NDIS_ETH_TYPE_IPV4); |
|
|
|
// |
|
// 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 = Adapter->m_dhcp_server_ip; |
|
|
|
if (broadcast) |
|
{ |
|
p->ip.daddr = ~0; |
|
} |
|
else |
|
{ |
|
p->ip.daddr = Adapter->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 = Adapter->m_dhcp_addr; |
|
} |
|
|
|
p->dhcp.siaddr = Adapter->m_dhcp_server_ip; |
|
p->dhcp.giaddr = 0; |
|
ETH_COPY_NETWORK_ADDRESS (p->dhcp.chaddr, eth->src); |
|
p->dhcp.magic = htonl (0x63825363); |
|
} |
|
|
|
//============================= |
|
// Build specific DHCP messages |
|
//============================= |
|
|
|
VOID |
|
SendDHCPMsg( |
|
__in PTAP_ADAPTER_CONTEXT Adapter, |
|
__in const int type, |
|
__in const ETH_HEADER *eth, |
|
__in const IPHDR *ip, |
|
__in const UDPHDR *udp, |
|
__in 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, Adapter->m_dhcp_server_ip); |
|
|
|
if (type == DHCPOFFER || type == DHCPACK) |
|
{ |
|
// Lease Time |
|
SetDHCPOpt32 (pkt, DHCP_LEASE_TIME, htonl (Adapter->m_dhcp_lease_time)); |
|
|
|
// Netmask |
|
SetDHCPOpt32 (pkt, DHCP_NETMASK, Adapter->m_dhcp_netmask); |
|
|
|
// Other user-defined options |
|
SetDHCPOpt ( |
|
pkt, |
|
Adapter->m_dhcp_user_supplied_options_buffer, |
|
Adapter->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 ( |
|
Adapter, |
|
&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 |
|
IndicateReceivePacket( |
|
Adapter, |
|
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( |
|
__in PTAP_ADAPTER_CONTEXT Adapter, |
|
__in const ETH_HEADER *eth, |
|
__in const IPHDR *ip, |
|
__in const UDPHDR *udp, |
|
__in const DHCP *dhcp, |
|
__in 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 (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 != Adapter->m_dhcp_addr) |
|
|| !Adapter->m_dhcp_received_discover |
|
|| Adapter->m_dhcp_bad_requests >= BAD_DHCPREQUEST_NAK_THRESHOLD)) |
|
{ |
|
SendDHCPMsg( |
|
Adapter, |
|
DHCPNAK, |
|
eth, ip, udp, dhcp |
|
); |
|
} |
|
else |
|
{ |
|
SendDHCPMsg( |
|
Adapter, |
|
(msg_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK), |
|
eth, ip, udp, dhcp |
|
); |
|
} |
|
|
|
// Remember if we received a DHCPDISCOVER |
|
if (msg_type == DHCPDISCOVER) |
|
{ |
|
Adapter->m_dhcp_received_discover = TRUE; |
|
} |
|
|
|
// Is this a bad DHCPREQUEST? |
|
if (msg_type == DHCPREQUEST && dhcp->ciaddr && dhcp->ciaddr != Adapter->m_dhcp_addr) |
|
{ |
|
++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 */
|
|
|