1/* vi: set sw=4 ts=4: */ 2/* clientpacket.c 3 * 4 * Packet generation and dispatching functions for the DHCP client. 5 * 6 * Russ Dill <Russ.Dill@asu.edu> July 2001 7 * 8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 9 */ 10 11#include <features.h> 12#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined \ 13 _NEWLIB_VERSION 14#include <netpacket/packet.h> 15#include <net/ethernet.h> 16#else 17#include <asm/types.h> 18#include <linux/if_packet.h> 19#include <linux/if_ether.h> 20#endif 21 22#include "common.h" 23#include "dhcpd.h" 24#include "dhcpc.h" 25#include "options.h" 26 27 28/* Create a random xid */ 29uint32_t random_xid(void) 30{ 31 static smallint initialized; 32 33 if (!initialized) { 34 srand(monotonic_us()); 35 initialized = 1; 36 } 37 return rand(); 38} 39 40 41/* initialize a packet with the proper defaults */ 42static void init_packet(struct dhcpMessage *packet, char type) 43{ 44 udhcp_init_header(packet, type); 45 memcpy(packet->chaddr, client_config.arp, 6); 46 if (client_config.clientid) 47 add_option_string(packet->options, client_config.clientid); 48 if (client_config.hostname) 49 add_option_string(packet->options, client_config.hostname); 50 if (client_config.fqdn) 51 add_option_string(packet->options, client_config.fqdn); 52 add_option_string(packet->options, client_config.vendorclass); 53} 54 55 56/* Add a parameter request list for stubborn DHCP servers. Pull the data 57 * from the struct in options.c. Don't do bounds checking here because it 58 * goes towards the head of the packet. */ 59static void add_requests(struct dhcpMessage *packet) 60{ 61 int end = end_option(packet->options); 62 int i, len = 0; 63 64 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; 65 for (i = 0; dhcp_options[i].code; i++) 66 if (dhcp_options[i].flags & OPTION_REQ) 67 packet->options[end + OPT_DATA + len++] = dhcp_options[i].code; 68 packet->options[end + OPT_LEN] = len; 69 packet->options[end + OPT_DATA + len] = DHCP_END; 70 71} 72 73 74/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ 75int send_discover(uint32_t xid, uint32_t requested) 76{ 77 struct dhcpMessage packet; 78 79 init_packet(&packet, DHCPDISCOVER); 80 packet.xid = xid; 81 if (requested) 82 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); 83 84 add_requests(&packet); 85 bb_info_msg("Sending discover..."); 86 return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 87 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 88} 89 90 91/* Broadcasts a DHCP request message */ 92int send_selecting(uint32_t xid, uint32_t server, uint32_t requested) 93{ 94 struct dhcpMessage packet; 95 struct in_addr addr; 96 97 init_packet(&packet, DHCPREQUEST); 98 packet.xid = xid; 99 100 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); 101 add_simple_option(packet.options, DHCP_SERVER_ID, server); 102 103 add_requests(&packet); 104 addr.s_addr = requested; 105 bb_info_msg("Sending select for %s...", inet_ntoa(addr)); 106 return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 107 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 108} 109 110 111/* Unicasts or broadcasts a DHCP renew message */ 112int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) 113{ 114 struct dhcpMessage packet; 115 116 init_packet(&packet, DHCPREQUEST); 117 packet.xid = xid; 118 packet.ciaddr = ciaddr; 119 120 add_requests(&packet); 121 bb_info_msg("Sending renew..."); 122 if (server) 123 return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); 124 125 return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 126 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); 127} 128 129 130/* Unicasts a DHCP release message */ 131int send_release(uint32_t server, uint32_t ciaddr) 132{ 133 struct dhcpMessage packet; 134 135 init_packet(&packet, DHCPRELEASE); 136 packet.xid = random_xid(); 137 packet.ciaddr = ciaddr; 138 139 add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); 140 add_simple_option(packet.options, DHCP_SERVER_ID, server); 141 142 bb_info_msg("Sending release..."); 143 return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); 144} 145 146 147/* return -1 on errors that are fatal for the socket, -2 for those that aren't */ 148int get_raw_packet(struct dhcpMessage *payload, int fd) 149{ 150 int bytes; 151 struct udp_dhcp_packet packet; 152 uint32_t source, dest; 153 uint16_t check; 154 155 memset(&packet, 0, sizeof(struct udp_dhcp_packet)); 156 bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); 157 if (bytes < 0) { 158 DEBUG("Cannot read on raw listening socket - ignoring"); 159 usleep(500000); /* possible down interface, looping condition */ 160 return -1; 161 } 162 163 if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { 164 DEBUG("Message too short, ignoring"); 165 return -2; 166 } 167 168 if (bytes < ntohs(packet.ip.tot_len)) { 169 DEBUG("Truncated packet"); 170 return -2; 171 } 172 173 /* ignore any extra garbage bytes */ 174 bytes = ntohs(packet.ip.tot_len); 175 176 /* Make sure its the right packet for us, and that it passes sanity checks */ 177 if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION 178 || packet.ip.ihl != sizeof(packet.ip) >> 2 179 || packet.udp.dest != htons(CLIENT_PORT) 180 || bytes > (int) sizeof(struct udp_dhcp_packet) 181 || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) 182 ) { 183 DEBUG("Unrelated/bogus packet"); 184 return -2; 185 } 186 187 /* check IP checksum */ 188 check = packet.ip.check; 189 packet.ip.check = 0; 190 if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) { 191 DEBUG("bad IP header checksum, ignoring"); 192 return -1; 193 } 194 195 /* verify the UDP checksum by replacing the header with a psuedo header */ 196 source = packet.ip.saddr; 197 dest = packet.ip.daddr; 198 check = packet.udp.check; 199 packet.udp.check = 0; 200 memset(&packet.ip, 0, sizeof(packet.ip)); 201 202 packet.ip.protocol = IPPROTO_UDP; 203 packet.ip.saddr = source; 204 packet.ip.daddr = dest; 205 packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ 206 if (check && check != udhcp_checksum(&packet, bytes)) { 207 bb_error_msg("packet with bad UDP checksum received, ignoring"); 208 return -2; 209 } 210 211 memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); 212 213 if (ntohl(payload->cookie) != DHCP_MAGIC) { 214 bb_error_msg("received bogus message (bad magic) - ignoring"); 215 return -2; 216 } 217 DEBUG("oooooh!!! got some!"); 218 return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); 219} 220