60264756f47fec76f30520bc7e476d3ff26a6d79
[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 ciaddr = { 0 };
112         int rc;
113
114         if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, ciaddr, NULL, data,
115                                           max_len ) ) != 0 ) {
116                 DBG ( "Could not create DHCPDISCOVER: %s\n",
117                       strerror ( rc ) );
118                 return rc;
119         }
120
121         return 0;
122 }
123
124 /**
125  * Create fake DHCPACK packet
126  *
127  * @v netdev            Network device
128  * @v data              Buffer for DHCP packet
129  * @v max_len           Size of DHCP packet buffer
130  * @ret rc              Return status code
131  *
132  * Used by external code.
133  */
134 int create_fakedhcpack ( struct net_device *netdev,
135                          void *data, size_t max_len ) {
136         struct dhcp_packet dhcppkt;
137         int rc;
138
139         /* Create base DHCPACK packet */
140         if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
141                                          data, max_len ) ) != 0 ) {
142                 DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) );
143                 return rc;
144         }
145
146         /* Merge in globally-scoped settings, then netdev-specific
147          * settings.  Do it in this order so that netdev-specific
148          * settings take precedence regardless of stated priorities.
149          */
150         if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) {
151                 DBG ( "Could not set DHCPACK global settings: %s\n",
152                       strerror ( rc ) );
153                 return rc;
154         }
155         if ( ( rc = copy_settings ( &dhcppkt,
156                                     netdev_settings ( netdev ) ) ) != 0 ) {
157                 DBG ( "Could not set DHCPACK netdev settings: %s\n",
158                       strerror ( rc ) );
159                 return rc;
160         }
161
162         return 0;
163 }
164
165 /**
166  * Create ProxyDHCPACK packet
167  *
168  * @v netdev            Network device
169  * @v data              Buffer for DHCP packet
170  * @v max_len           Size of DHCP packet buffer
171  * @ret rc              Return status code
172  *
173  * Used by external code.
174  */
175 int create_fakeproxydhcpack ( struct net_device *netdev,
176                               void *data, size_t max_len ) {
177         struct dhcp_packet dhcppkt;
178         struct settings *settings;
179         int rc;
180
181         /* Identify ProxyDHCP settings */
182         settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
183
184         /* No ProxyDHCP settings => use normal DHCPACK */
185         if ( ! settings )
186                 return create_fakedhcpack ( netdev, data, max_len );
187
188         /* Create base DHCPACK packet */
189         if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
190                                          data, max_len ) ) != 0 ) {
191                 DBG ( "Could not create ProxyDHCPACK: %s\n",
192                       strerror ( rc ) );
193                 return rc;
194         }
195
196         /* Merge in ProxyDHCP options */
197         if ( ( rc = copy_settings ( &dhcppkt, settings ) ) != 0 ) {
198                 DBG ( "Could not set ProxyDHCPACK settings: %s\n",
199                       strerror ( rc ) );
200                 return rc;
201         }
202
203         return 0;
204 }