[dhcp] Pass PXE boot menu item to PXE Boot Server
[people/oremanj/gpxe.git] / src / net / fakedhcp.c
1 /*
2  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <gpxe/settings.h>
25 #include <gpxe/netdevice.h>
26 #include <gpxe/dhcppkt.h>
27 #include <gpxe/fakedhcp.h>
28
29 /** @file
30  *
31  * Fake DHCP packets
32  *
33  */
34
35 /**
36  * Copy settings to DHCP packet
37  *
38  * @v dest              Destination DHCP packet
39  * @v source            Source settings block
40  * @v encapsulator      Encapsulating setting tag number, or zero
41  * @ret rc              Return status code
42  */
43 static int copy_encap_settings ( struct dhcp_packet *dest,
44                                  struct settings *source,
45                                  unsigned int encapsulator ) {
46         struct setting setting = { .name = "" };
47         unsigned int subtag;
48         unsigned int tag;
49         int len;
50         int check_len;
51         int rc;
52
53         for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
54                 tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
55                 switch ( tag ) {
56                 case DHCP_EB_ENCAP:
57                 case DHCP_VENDOR_ENCAP:
58                         /* Process encapsulated settings */
59                         if ( ( rc = copy_encap_settings ( dest, source,
60                                                           tag ) ) != 0 )
61                                 return rc;
62                         break;
63                 default:
64                         /* Copy setting, if present */
65                         setting.tag = tag;
66                         len = fetch_setting_len ( source, &setting );
67                         if ( len < 0 )
68                                 break;
69                         {
70                                 char buf[len];
71
72                                 check_len = fetch_setting ( source, &setting,
73                                                             buf, sizeof (buf));
74                                 assert ( check_len == len );
75                                 if ( ( rc = dhcppkt_store ( dest, tag, buf,
76                                                             sizeof(buf) )) !=0)
77                                         return rc;
78                         }
79                         break;
80                 }
81         }
82
83         return 0;
84 }
85
86 /**
87  * Copy settings to DHCP packet
88  *
89  * @v dest              Destination DHCP packet
90  * @v source            Source settings block
91  * @ret rc              Return status code
92  */
93 static int copy_settings ( struct dhcp_packet *dest,
94                            struct settings *source ) {
95         return copy_encap_settings ( dest, source, 0 );
96 }
97
98 /**
99  * Create fake DHCPDISCOVER packet
100  *
101  * @v netdev            Network device
102  * @v data              Buffer for DHCP packet
103  * @v max_len           Size of DHCP packet buffer
104  * @ret rc              Return status code
105  *
106  * Used by external code.
107  */
108 int create_fakedhcpdiscover ( struct net_device *netdev,
109                               void *data, size_t max_len ) {
110         struct dhcp_packet dhcppkt;
111         struct in_addr dummy_addr = { 0 };
112         int rc;
113
114         if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, DHCPDISCOVER,
115                                           dummy_addr, dummy_addr, dummy_addr,
116                                           NULL, data, max_len ) ) != 0 ) {
117                 DBG ( "Could not create DHCPDISCOVER: %s\n",
118                       strerror ( rc ) );
119                 return rc;
120         }
121
122         return 0;
123 }
124
125 /**
126  * Create fake DHCPACK packet
127  *
128  * @v netdev            Network device
129  * @v data              Buffer for DHCP packet
130  * @v max_len           Size of DHCP packet buffer
131  * @ret rc              Return status code
132  *
133  * Used by external code.
134  */
135 int create_fakedhcpack ( struct net_device *netdev,
136                          void *data, size_t max_len ) {
137         struct dhcp_packet dhcppkt;
138         int rc;
139
140         /* Create base DHCPACK packet */
141         if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
142                                          data, max_len ) ) != 0 ) {
143                 DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) );
144                 return rc;
145         }
146
147         /* Merge in globally-scoped settings, then netdev-specific
148          * settings.  Do it in this order so that netdev-specific
149          * settings take precedence regardless of stated priorities.
150          */
151         if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) {
152                 DBG ( "Could not set DHCPACK global settings: %s\n",
153                       strerror ( rc ) );
154                 return rc;
155         }
156         if ( ( rc = copy_settings ( &dhcppkt,
157                                     netdev_settings ( netdev ) ) ) != 0 ) {
158                 DBG ( "Could not set DHCPACK netdev settings: %s\n",
159                       strerror ( rc ) );
160                 return rc;
161         }
162
163         return 0;
164 }
165
166 /**
167  * Create ProxyDHCPACK packet
168  *
169  * @v netdev            Network device
170  * @v data              Buffer for DHCP packet
171  * @v max_len           Size of DHCP packet buffer
172  * @ret rc              Return status code
173  *
174  * Used by external code.
175  */
176 int create_fakeproxydhcpack ( struct net_device *netdev,
177                               void *data, size_t max_len ) {
178         struct dhcp_packet dhcppkt;
179         struct settings *settings;
180         struct settings *bs_settings;
181         int rc;
182
183         /* Identify ProxyDHCP settings */
184         settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
185
186         /* No ProxyDHCP settings => use normal DHCPACK */
187         if ( ! settings )
188                 return create_fakedhcpack ( netdev, data, max_len );
189
190         /* Create base DHCPACK packet */
191         if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
192                                          data, max_len ) ) != 0 ) {
193                 DBG ( "Could not create ProxyDHCPACK: %s\n",
194                       strerror ( rc ) );
195                 return rc;
196         }
197
198         /* Merge in ProxyDHCP options */
199         if ( ( rc = copy_settings ( &dhcppkt, settings ) ) != 0 ) {
200                 DBG ( "Could not set ProxyDHCPACK settings: %s\n",
201                       strerror ( rc ) );
202                 return rc;
203         }
204
205         /* Merge in BootServerDHCP options, if present */
206         bs_settings = find_settings ( BSDHCP_SETTINGS_NAME );
207         if ( bs_settings ) {
208                 if ( ( rc = copy_settings ( &dhcppkt, bs_settings ) ) != 0 ) {
209                         DBG ( "Could not set BootServerDHCPACK settings: "
210                               "%s\n", strerror ( rc ) );
211                         return rc;
212                 }
213         }
214
215         return 0;
216 }