1/* 2 * OpenVPN -- An application to securely tunnel IP networks 3 * over a single TCP/UDP port, with support for SSL/TLS-based 4 * session authentication and key exchange, 5 * packet encryption, packet authentication, and 6 * packet compression. 7 * 8 * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 12 * as published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program (see the file COPYING included with this 21 * distribution); if not, write to the Free Software Foundation, Inc., 22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25#ifdef HAVE_CONFIG_H 26#include "config.h" 27#elif defined(_MSC_VER) 28#include "config-msvc.h" 29#endif 30 31#include "syshead.h" 32 33#include "dhcp.h" 34#include "socket.h" 35#include "error.h" 36 37#include "memdbg.h" 38 39static int 40get_dhcp_message_type (const struct dhcp *dhcp, const int optlen) 41{ 42 const uint8_t *p = (uint8_t *) (dhcp + 1); 43 int i; 44 45 for (i = 0; i < optlen; ++i) 46 { 47 const uint8_t type = p[i]; 48 const int room = optlen - i; 49 if (type == DHCP_END) /* didn't find what we were looking for */ 50 return -1; 51 else if (type == DHCP_PAD) /* no-operation */ 52 ; 53 else if (type == DHCP_MSG_TYPE) /* what we are looking for */ 54 { 55 if (room >= 3) 56 { 57 if (p[i+1] == 1) /* option length should be 1 */ 58 return p[i+2]; /* return message type */ 59 } 60 return -1; 61 } 62 else /* some other option */ 63 { 64 if (room >= 2) 65 { 66 const int len = p[i+1]; /* get option length */ 67 i += (len + 1); /* advance to next option */ 68 } 69 } 70 } 71 return -1; 72} 73 74static in_addr_t 75do_extract (struct dhcp *dhcp, int optlen) 76{ 77 uint8_t *p = (uint8_t *) (dhcp + 1); 78 int i; 79 in_addr_t ret = 0; 80 81 for (i = 0; i < optlen; ) 82 { 83 const uint8_t type = p[i]; 84 const int room = optlen - i; 85 if (type == DHCP_END) 86 break; 87 else if (type == DHCP_PAD) 88 ++i; 89 else if (type == DHCP_ROUTER) 90 { 91 if (room >= 2) 92 { 93 const int len = p[i+1]; /* get option length */ 94 if (len <= (room-2)) 95 { 96 /* get router IP address */ 97 if (!ret && len >= 4 && (len & 3) == 0) 98 { 99 memcpy (&ret, p+i+2, 4); 100 ret = ntohl (ret); 101 } 102 { 103 /* delete the router option */ 104 uint8_t *dest = p + i; 105 const int owlen = len + 2; /* len of data to overwrite */ 106 uint8_t *src = dest + owlen; 107 uint8_t *end = p + optlen; 108 const int movlen = end - src; 109 if (movlen > 0) 110 memmove(dest, src, movlen); /* overwrite router option */ 111 memset(end - owlen, DHCP_PAD, owlen); /* pad tail */ 112 } 113 } 114 else 115 break; 116 } 117 else 118 break; 119 } 120 else /* some other option */ 121 { 122 if (room >= 2) 123 { 124 const int len = p[i+1]; /* get option length */ 125 i += (len + 2); /* advance to next option */ 126 } 127 else 128 break; 129 } 130 } 131 return ret; 132} 133 134static uint16_t 135udp_checksum (const uint8_t *buf, 136 const int len_udp, 137 const uint8_t *src_addr, 138 const uint8_t *dest_addr) 139{ 140 uint16_t word16; 141 uint32_t sum = 0; 142 int i; 143 144 /* make 16 bit words out of every two adjacent 8 bit words and */ 145 /* calculate the sum of all 16 bit words */ 146 for (i = 0; i < len_udp; i += 2){ 147 word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0); 148 sum += word16; 149 } 150 151 /* add the UDP pseudo header which contains the IP source and destination addresses */ 152 for (i = 0; i < 4; i += 2){ 153 word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF); 154 sum += word16; 155 } 156 for (i = 0; i < 4; i += 2){ 157 word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF); 158 sum += word16; 159 } 160 161 /* the protocol number and the length of the UDP packet */ 162 sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp; 163 164 /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */ 165 while (sum >> 16) 166 sum = (sum & 0xFFFF) + (sum >> 16); 167 168 /* Take the one's complement of sum */ 169 return ((uint16_t) ~sum); 170} 171 172in_addr_t 173dhcp_extract_router_msg (struct buffer *ipbuf) 174{ 175 struct dhcp_full *df = (struct dhcp_full *) BPTR (ipbuf); 176 const int optlen = BLEN (ipbuf) - (sizeof (struct openvpn_iphdr) + sizeof (struct openvpn_udphdr) + sizeof (struct dhcp)); 177 178 if (optlen >= 0 179 && df->ip.protocol == OPENVPN_IPPROTO_UDP 180 && df->udp.source == htons (BOOTPS_PORT) 181 && df->udp.dest == htons (BOOTPC_PORT) 182 && df->dhcp.op == BOOTREPLY) 183 { 184 const int message_type = get_dhcp_message_type (&df->dhcp, optlen); 185 if (message_type == DHCPACK || message_type == DHCPOFFER) 186 { 187 /* get the router IP address while padding out all DHCP router options */ 188 const in_addr_t ret = do_extract (&df->dhcp, optlen); 189 190 /* recompute the UDP checksum */ 191 df->udp.check = 0; 192 df->udp.check = htons (udp_checksum ((uint8_t *) &df->udp, 193 sizeof (struct openvpn_udphdr) + sizeof (struct dhcp) + optlen, 194 (uint8_t *)&df->ip.saddr, 195 (uint8_t *)&df->ip.daddr)); 196 197 /* only return the extracted Router address if DHCPACK */ 198 if (message_type == DHCPACK) 199 { 200 if (ret) 201 { 202 struct gc_arena gc = gc_new (); 203 msg (D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t (ret, 0, &gc)); 204 gc_free (&gc); 205 } 206 207 return ret; 208 } 209 } 210 } 211 return 0; 212} 213