summaryrefslogtreecommitdiff
path: root/tap-win32/dhcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'tap-win32/dhcp.c')
-rwxr-xr-xtap-win32/dhcp.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/tap-win32/dhcp.c b/tap-win32/dhcp.c
new file mode 100755
index 0000000..3891d42
--- /dev/null
+++ b/tap-win32/dhcp.c
@@ -0,0 +1,599 @@
+/*
+ * TAP-Win32/TAP-Win64 -- 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_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 != 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 */