/* * TAP-Win32 -- A kernel driver to provide virtual tap device functionality * on Windows. Originally derived from the CIPE-Win32 * project by Damion K. Wilson, with extensive modifications by * James Yonan. * * All source code which derives from the CIPE-Win32 project is * Copyright (C) Damion K. Wilson, 2003, and is released under the * GPL version 2 (see below). * * All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC, * 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 InjectPacket (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 /* DEBUG */