Home | History | Annotate | Download | only in pxe
      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