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 9 char LocalDomain[256]; 10 11 int over_load; 12 uint8_t uuid_type; 13 uint8_t uuid[16]; 14 15 static 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 22 static 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 29 static 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 49 static void local_domain(const void *data, int opt_len) 50 { 51 memcpy(LocalDomain, data, opt_len); 52 LocalDomain[opt_len] = 0; 53 } 54 55 static 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 61 static 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 68 static 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 83 static 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 96 static 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 102 static 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 113 static 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 120 static 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 127 static 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 137 struct dhcp_options { 138 int opt_num; 139 void (*fun)(const void *, int); 140 }; 141 142 static 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 */ 166 void 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 */ 228 void 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