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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <ipxe/settings.h>
32 #include <ipxe/netdevice.h>
33 #include <ipxe/dhcppkt.h>
34 #include <ipxe/fakedhcp.h>
35
36 /** @file
37 *
38 * Fake DHCP packets
39 *
40 */
41
42 /**
43 * Copy settings to DHCP packet
44 *
45 * @v dest Destination DHCP packet
46 * @v source Source settings block
47 * @v encapsulator Encapsulating setting tag number, or zero
48 * @ret rc Return status code
49 */
copy_encap_settings(struct dhcp_packet * dest,struct settings * source,unsigned int encapsulator)50 static int copy_encap_settings ( struct dhcp_packet *dest,
51 struct settings *source,
52 unsigned int encapsulator ) {
53 struct setting setting = { .name = "" };
54 unsigned int subtag;
55 unsigned int tag;
56 void *data;
57 int len;
58 int rc;
59
60 for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
61 tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
62 switch ( tag ) {
63 case DHCP_EB_ENCAP:
64 case DHCP_VENDOR_ENCAP:
65 /* Process encapsulated settings */
66 if ( ( rc = copy_encap_settings ( dest, source,
67 tag ) ) != 0 )
68 return rc;
69 break;
70 default:
71 /* Copy setting, if present */
72 setting.tag = tag;
73 len = fetch_raw_setting_copy ( source, &setting, &data);
74 if ( len >= 0 ) {
75 rc = dhcppkt_store ( dest, tag, data, len );
76 free ( data );
77 if ( rc != 0 )
78 return rc;
79 }
80 break;
81 }
82 }
83
84 return 0;
85 }
86
87 /**
88 * Copy settings to DHCP packet
89 *
90 * @v dest Destination DHCP packet
91 * @v source Source settings block
92 * @ret rc Return status code
93 */
copy_settings(struct dhcp_packet * dest,struct settings * source)94 static int copy_settings ( struct dhcp_packet *dest,
95 struct settings *source ) {
96 return copy_encap_settings ( dest, source, 0 );
97 }
98
99 /**
100 * Create fake DHCPDISCOVER packet
101 *
102 * @v netdev Network device
103 * @v data Buffer for DHCP packet
104 * @v max_len Size of DHCP packet buffer
105 * @ret rc Return status code
106 *
107 * Used by external code.
108 */
create_fakedhcpdiscover(struct net_device * netdev,void * data,size_t max_len)109 int create_fakedhcpdiscover ( struct net_device *netdev,
110 void *data, size_t max_len ) {
111 struct dhcp_packet dhcppkt;
112 struct in_addr ciaddr = { 0 };
113 int rc;
114
115 if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, DHCPDISCOVER,
116 dhcp_last_xid, ciaddr, data,
117 max_len ) ) != 0 ) {
118 DBG ( "Could not create DHCPDISCOVER: %s\n",
119 strerror ( rc ) );
120 return rc;
121 }
122
123 return 0;
124 }
125
126 /**
127 * Create fake DHCPACK packet
128 *
129 * @v netdev Network device
130 * @v data Buffer for DHCP packet
131 * @v max_len Size of DHCP packet buffer
132 * @ret rc Return status code
133 *
134 * Used by external code.
135 */
create_fakedhcpack(struct net_device * netdev,void * data,size_t max_len)136 int create_fakedhcpack ( struct net_device *netdev,
137 void *data, size_t max_len ) {
138 struct dhcp_packet dhcppkt;
139 int rc;
140
141 /* Create base DHCPACK packet */
142 if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK,
143 dhcp_last_xid, NULL, 0,
144 data, max_len ) ) != 0 ) {
145 DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) );
146 return rc;
147 }
148
149 /* Merge in globally-scoped settings, then netdev-specific
150 * settings. Do it in this order so that netdev-specific
151 * settings take precedence regardless of stated priorities.
152 */
153 if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) {
154 DBG ( "Could not set DHCPACK global settings: %s\n",
155 strerror ( rc ) );
156 return rc;
157 }
158 if ( ( rc = copy_settings ( &dhcppkt,
159 netdev_settings ( netdev ) ) ) != 0 ) {
160 DBG ( "Could not set DHCPACK netdev settings: %s\n",
161 strerror ( rc ) );
162 return rc;
163 }
164
165 return 0;
166 }
167
168 /**
169 * Create fake PXE Boot Server ACK packet
170 *
171 * @v netdev Network device
172 * @v data Buffer for DHCP packet
173 * @v max_len Size of DHCP packet buffer
174 * @ret rc Return status code
175 *
176 * Used by external code.
177 */
create_fakepxebsack(struct net_device * netdev,void * data,size_t max_len)178 int create_fakepxebsack ( struct net_device *netdev,
179 void *data, size_t max_len ) {
180 struct dhcp_packet dhcppkt;
181 struct settings *proxy_settings;
182 struct settings *pxebs_settings;
183 int rc;
184
185 /* Identify available settings */
186 proxy_settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
187 pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME );
188 if ( ( ! proxy_settings ) && ( ! pxebs_settings ) ) {
189 /* No PXE boot server; return the regular DHCPACK */
190 return create_fakedhcpack ( netdev, data, max_len );
191 }
192
193 /* Create base DHCPACK packet */
194 if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK,
195 dhcp_last_xid, NULL, 0,
196 data, max_len ) ) != 0 ) {
197 DBG ( "Could not create PXE BS ACK: %s\n",
198 strerror ( rc ) );
199 return rc;
200 }
201
202 /* Populate ciaddr */
203 fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
204 &dhcppkt.dhcphdr->ciaddr );
205
206 /* Merge in ProxyDHCP options */
207 if ( proxy_settings &&
208 ( ( rc = copy_settings ( &dhcppkt, proxy_settings ) ) != 0 ) ) {
209 DBG ( "Could not copy ProxyDHCP settings: %s\n",
210 strerror ( rc ) );
211 return rc;
212 }
213
214 /* Merge in BootServerDHCP options, if present */
215 if ( pxebs_settings &&
216 ( ( rc = copy_settings ( &dhcppkt, pxebs_settings ) ) != 0 ) ) {
217 DBG ( "Could not copy PXE BS settings: %s\n",
218 strerror ( rc ) );
219 return rc;
220 }
221
222 return 0;
223 }
224