1/* clientpacket.c 2 * 3 * Packet generation and dispatching functions for the DHCP client. 4 * 5 * Russ Dill <Russ.Dill@asu.edu> July 2001 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22#include <string.h> 23#include <sys/socket.h> 24#include <features.h> 25#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 26#include <netpacket/packet.h> 27#include <net/ethernet.h> 28#else 29#include <asm/types.h> 30#include <linux/if_packet.h> 31#include <linux/if_ether.h> 32#endif 33#include <stdlib.h> 34#include <time.h> 35#include <unistd.h> 36#include <netinet/in.h> 37#include <arpa/inet.h> 38#include <errno.h> 39#include <sys/types.h> 40#include <sys/stat.h> 41#include <fcntl.h> 42 43 44#include "dhcpd.h" 45#include "packet.h" 46#include "options.h" 47#include "dhcpc.h" 48#include "debug.h" 49 50 51/* Create a random xid */ 52unsigned long random_xid(void) 53{ 54 static int initialized; 55 if (!initialized) { 56 int fd; 57 unsigned long seed; 58 59 fd = open("/dev/urandom", 0); 60 if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) { 61 LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %s", 62 strerror(errno)); 63 seed = time(0); 64 } 65 if (fd >= 0) close(fd); 66 srand(seed); 67 initialized++; 68 } 69 return rand(); 70} 71 72 73/* initialize a packet with the proper defaults */ 74static void init_packet(struct dhcpMessage *packet, char type) 75{ 76 struct vendor { 77 char vendor, length; 78 char str[sizeof("udhcp "VERSION)]; 79 } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION}; 80 81 init_header(packet, type); 82 memcpy(packet->chaddr, client_config.arp, 6); 83 add_option_string(packet->options, client_config.clientid); 84 /* foxconn wklin modified start, 08/10/2007 */ 85 if (type != DHCPDECLINE) { 86 if (client_config.hostname) add_option_string(packet->options, client_config.hostname); 87 add_option_string(packet->options, (unsigned char *) &vendor_id); 88 } 89 /* foxconn wklin modified end, 08/10/2007 */ 90} 91 92 93/* Add a paramater request list for stubborn DHCP servers. Pull the data 94 * from the struct in options.c. Don't do bounds checking here because it 95 * goes towards the head of the packet. */ 96static void add_requests(struct dhcpMessage *packet) 97{ 98 int end = end_option(packet->options); 99 int i, len = 0; 100 101 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; 102 for (i = 0; options[i].code; i++) 103 if (options[i].flags & OPTION_REQ) 104 packet->options[end + OPT_DATA + len++] = options[i].code; 105 packet->options[end + OPT_LEN] = len; 106 packet->options[end + OPT_DATA + len] = DHCP_END; 107 108} 109 110 111/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ 112int send_discover(unsigned long xid, unsigned long requested) 113{ 114 struct dhcpMessage packet; 115 116 init_packet(&packet, DHCPDISCOVER); 117 packet.xid = xid; 118 if (requested) 119 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); 120 121 add_requests(&packet); 122 return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 123 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 124} 125 126 127/* Broadcasts a DHCP request message */ 128int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) 129{ 130 struct dhcpMessage packet; 131 struct in_addr addr; 132 133 init_packet(&packet, DHCPREQUEST); 134 packet.xid = xid; 135 136 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); 137 add_simple_option(packet.options, DHCP_SERVER_ID, server); 138 139 add_requests(&packet); 140 addr.s_addr = requested; 141 LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); 142 return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 143 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 144} 145 146 147/* Unicasts or broadcasts a DHCP renew message */ 148int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) 149{ 150 struct dhcpMessage packet; 151 int ret = 0; 152 153 init_packet(&packet, DHCPREQUEST); 154 packet.xid = xid; 155 packet.ciaddr = ciaddr; 156 157 add_requests(&packet); 158 LOG(LOG_DEBUG, "Sending renew..."); 159 if (server) 160 ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); 161 else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 162 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 163 return ret; 164} 165 166 167/* Unicasts a DHCP release message */ 168int send_release(unsigned long server, unsigned long ciaddr) 169{ 170 struct dhcpMessage packet; 171 172 init_packet(&packet, DHCPRELEASE); 173 packet.xid = random_xid(); 174 packet.ciaddr = ciaddr; 175 176 add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); 177 add_simple_option(packet.options, DHCP_SERVER_ID, server); 178 179 LOG(LOG_DEBUG, "Sending release..."); 180 return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); 181} 182 183/* foxconn wklin added start, 08/07/2007 */ 184/* Unicasts a DHCP decline message */ 185int send_decline(unsigned long xid, unsigned long server, unsigned long ciaddr) 186{ 187 struct dhcpMessage packet; 188 int ret = 0; 189 190 init_packet(&packet, DHCPDECLINE); 191 packet.xid = xid; 192 packet.ciaddr = 0; /* 0 per RFC2131 p.37 */ 193 194 add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); 195 add_simple_option(packet.options, DHCP_SERVER_ID, server); 196 197 LOG(LOG_DEBUG, "Sending decline..."); 198 ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 199 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 200 return ret; 201} 202/* foxconn wklin added end, 08/07/2007 */ 203 204/* return -1 on errors that are fatal for the socket, -2 for those that aren't */ 205int get_raw_packet(struct dhcpMessage *payload, int fd) 206{ 207 int bytes; 208 struct udp_dhcp_packet_rcv packet;/*foxconn wklin modified, 10/03/2007*/ 209 u_int32_t source, dest; 210 u_int16_t check; 211 212 /* foxconn wklin modified start, 10/03/2007 */ 213 memset(&packet, 0, sizeof(struct udp_dhcp_packet_rcv)); 214 bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet_rcv)); 215 /* foxconn wklin modified end, 10/03/2007 */ 216 if (bytes < 0) { 217 DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); 218 usleep(500000); /* possible down interface, looping condition */ 219 return -1; 220 } 221 222 if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { 223 DEBUG(LOG_INFO, "message too short, ignoring"); 224 return -2; 225 } 226 227 if (bytes < ntohs(packet.ip.tot_len)) { 228 DEBUG(LOG_INFO, "Truncated packet"); 229 return -2; 230 } 231 232 /* ignore any extra garbage bytes */ 233 bytes = ntohs(packet.ip.tot_len); 234 235 /* foxconn wklin modified start, 10/03/2007 */ 236 /* use udp_dhcp_packet_rcv data structure */ 237 /* Make sure its the right packet for us, and that it passes sanity checks */ 238 if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || 239 packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) || 240 bytes > (int) sizeof(struct udp_dhcp_packet_rcv) || 241 ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) { 242 DEBUG(LOG_INFO, "unrelated/bogus packet"); 243 return -2; 244 } 245 /* foxconn wklin modified end, 10/03/2007 */ 246 247 /* check IP checksum */ 248 check = packet.ip.check; 249 packet.ip.check = 0; 250 if (check != checksum(&(packet.ip), sizeof(packet.ip))) { 251 DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); 252 return -1; 253 } 254 255 /* verify the UDP checksum by replacing the header with a psuedo header */ 256 source = packet.ip.saddr; 257 dest = packet.ip.daddr; 258 check = packet.udp.check; 259 packet.udp.check = 0; 260 memset(&packet.ip, 0, sizeof(packet.ip)); 261 262 packet.ip.protocol = IPPROTO_UDP; 263 packet.ip.saddr = source; 264 packet.ip.daddr = dest; 265 packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ 266 if (check && check != checksum(&packet, bytes)) { 267 DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); 268 return -2; 269 } 270 271 memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); 272 273 if (ntohl(payload->cookie) != DHCP_MAGIC) { 274 LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); 275 return -2; 276 } 277 DEBUG(LOG_INFO, "oooooh!!! got some!"); 278 return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); 279 280} 281 282