1#include <stdio.h> 2#include <string.h> 3#include <core.h> 4#include <sys/cpu.h> 5#include <lwip/opt.h> /* DNS_MAX_SERVERS */ 6#include <dprintf.h> 7#include "pxe.h" 8 9char LocalDomain[256]; 10 11int over_load; 12uint8_t uuid_type; 13uint8_t uuid[16]; 14 15static void subnet_mask(const void *data, int opt_len) 16{ 17 if (opt_len != 4) 18 return; 19 IPInfo.netmask = *(const uint32_t *)data; 20} 21 22static void router(const void *data, int opt_len) 23{ 24 if (opt_len != 4) 25 return; 26 IPInfo.gateway = *(const uint32_t *)data; 27} 28 29static void dns_servers(const void *data, int opt_len) 30{ 31 const uint32_t *dp = data; 32 int num = 0; 33 34 while (num < DNS_MAX_SERVERS) { 35 uint32_t ip; 36 37 if (opt_len < 4) 38 break; 39 40 opt_len -= 4; 41 ip = *dp++; 42 if (ip_ok(ip)) 43 dns_server[num++] = ip; 44 } 45 while (num < DNS_MAX_SERVERS) 46 dns_server[num++] = 0; 47} 48 49static void local_domain(const void *data, int opt_len) 50{ 51 memcpy(LocalDomain, data, opt_len); 52 LocalDomain[opt_len] = 0; 53} 54 55static void vendor_encaps(const void *data, int opt_len) 56{ 57 /* Only recognize PXELINUX options */ 58 parse_dhcp_options(data, opt_len, 208); 59} 60 61static void option_overload(const void *data, int opt_len) 62{ 63 if (opt_len != 1) 64 return; 65 over_load = *(uint8_t *)data; 66} 67 68static void server(const void *data, int opt_len) 69{ 70 uint32_t ip; 71 72 if (opt_len != 4) 73 return; 74 75 if (IPInfo.serverip) 76 return; 77 78 ip = *(uint32_t *)data; 79 if (ip_ok(ip)) 80 IPInfo.serverip = ip; 81} 82 83static void client_identifier(const void *data, int opt_len) 84{ 85 if (opt_len > MAC_MAX || opt_len < 2 || 86 MAC_len != (opt_len >> 8) || 87 *(uint8_t *)data != MAC_type) 88 return; 89 90 opt_len --; 91 MAC_len = opt_len & 0xff; 92 memcpy(MAC, data+1, opt_len); 93 MAC[opt_len] = 0; 94} 95 96static void bootfile_name(const void *data, int opt_len) 97{ 98 memcpy(boot_file, data, opt_len); 99 boot_file[opt_len] = 0; 100} 101 102static void uuid_client_identifier(const void *data, int opt_len) 103{ 104 int type = *(const uint8_t *)data; 105 if (opt_len != 17 || type != 0 || have_uuid) 106 return; 107 108 have_uuid = true; 109 uuid_type = type; 110 memcpy(uuid, data+1, 16); 111} 112 113static void pxelinux_configfile(const void *data, int opt_len) 114{ 115 DHCPMagic |= 2; 116 memcpy(ConfigName, data, opt_len); 117 ConfigName[opt_len] = 0; 118} 119 120static void pxelinux_pathprefix(const void *data, int opt_len) 121{ 122 DHCPMagic |= 4; 123 memcpy(path_prefix, data, opt_len); 124 path_prefix[opt_len] = 0; 125} 126 127static void pxelinux_reboottime(const void *data, int opt_len) 128{ 129 if (opt_len != 4) 130 return; 131 132 RebootTime = ntohl(*(const uint32_t *)data); 133 DHCPMagic |= 8; /* Got reboot time */ 134} 135 136 137struct dhcp_options { 138 int opt_num; 139 void (*fun)(const void *, int); 140}; 141 142static const struct dhcp_options dhcp_opts[] = { 143 {1, subnet_mask}, 144 {3, router}, 145 {6, dns_servers}, 146 {15, local_domain}, 147 {43, vendor_encaps}, 148 {52, option_overload}, 149 {54, server}, 150 {61, client_identifier}, 151 {67, bootfile_name}, 152 {97, uuid_client_identifier}, 153 {209, pxelinux_configfile}, 154 {210, pxelinux_pathprefix}, 155 {211, pxelinux_reboottime} 156}; 157 158/* 159 * Parse a sequence of DHCP options, pointed to by _option_; 160 * -- some DHCP servers leave option fields unterminated 161 * in violation of the spec. 162 * 163 * filter contains the minimum value for the option to recognize 164 * -- this is used to restrict parsing to PXELINUX-specific options only. 165 */ 166void parse_dhcp_options(const void *option, int size, uint8_t opt_filter) 167{ 168 int opt_num; 169 int opt_len; 170 const int opt_entries = sizeof(dhcp_opts) / sizeof(dhcp_opts[0]); 171 int i = 0; 172 const uint8_t *p = option; 173 const struct dhcp_options *opt; 174 175 /* The only 1-byte options are 00 and FF, neither of which matter */ 176 while (size >= 2) { 177 opt_num = *p++; 178 size--; 179 180 if (opt_num == 0) 181 continue; 182 if (opt_num == 0xff) 183 break; 184 185 /* Anything else will have a length field */ 186 opt_len = *p++; /* c <- option lenght */ 187 size -= opt_len + 1; 188 if (size < 0) 189 break; 190 191 dprintf("DHCP: option %d, len %d\n", opt_num, opt_len); 192 193 if (opt_num >= opt_filter) { 194 opt = dhcp_opts; 195 for (i = 0; i < opt_entries; i++) { 196 if (opt_num == opt->opt_num) { 197 opt->fun(p, opt_len); 198 break; 199 } 200 opt++; 201 } 202 } 203 204 /* parse next */ 205 p += opt_len; 206 } 207} 208 209/* 210 * parse_dhcp 211 * 212 * Parse a DHCP packet. This includes dealing with "overloaded" 213 * option fields (see RFC 2132, section 9.3) 214 * 215 * This should fill in the following global variables, if the 216 * information is present: 217 * 218 * MyIP - client IP address 219 * server_ip - boot server IP address 220 * net_mask - network mask 221 * gate_way - default gateway router IP 222 * boot_file - boot file name 223 * DNSServers - DNS server IPs 224 * LocalDomain - Local domain name 225 * MAC_len, MAC - Client identifier, if MAC_len == 0 226 * 227 */ 228void parse_dhcp(const void *pkt, size_t pkt_len) 229{ 230 const struct bootp_t *dhcp = (const struct bootp_t *)pkt; 231 int opt_len; 232 233 IPInfo.ipver = 4; /* This is IPv4 only for now... */ 234 235 over_load = 0; 236 if (ip_ok(dhcp->yip)) 237 IPInfo.myip = dhcp->yip; 238 239 if (ip_ok(dhcp->sip)) 240 IPInfo.serverip = dhcp->sip; 241 242 opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options; 243 if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC)) 244 parse_dhcp_options(&dhcp->options, opt_len, 0); 245 246 if (over_load & 1) 247 parse_dhcp_options(&dhcp->bootfile, 128, 0); 248 else if (dhcp->bootfile[0]) 249 strcpy(boot_file, dhcp->bootfile); 250 251 if (over_load & 2) 252 parse_dhcp_options(dhcp->sname, 64, 0); 253} 254