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