Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2008 Michael Brown <mbrown (at) 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 FILE_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  */
     45 static 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  */
     95 static 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  */
    110 int 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  */
    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, 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  */
    177 int 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