/*
**************************************************************************
*
* Boot-ROM-Code to load an operating system across a TCP/IP network.
*
* Module: bootp.c
* Purpose: Get information for client with BOOTP protocol
* Entries: bootp
*
**************************************************************************
*
* Copyright (C) 1995-2003 Gero Kuhlmann <gero@gkminix.han.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: bootp.c,v 1.6 2003/03/09 00:24:27 gkminix Exp $
*
**************************************************************************
*
* We can print error messages within this module as it gets never called
* by a PXE function.
*/
#include <general.h>
#include <kernel/net.h>
#include <kernel/arpa.h>
#include <kernel/romlib.h>
#include <pxe/common.h>
#include "bootp.h"
/*
**************************************************************************
*
* Global variables:
*/
static unsigned long boot_xid; /* bootp transaction ID */
static unsigned long boot_start_time; /* when started to boot */
/*
**************************************************************************
*
* Send a BOOTP request via UDP.
*
*/
static int bootp_send()
{
register struct bootp *bp;
int i;
/* Create a new BOOTP output buffer */
if (!new_bootp_buf(BOOTP_DISCOVER))
return(PXENV_STATUS_FAILURE);
bp = bootp_bufs[BOOTP_DISCOVER];
/* Assemble the BOOTP request */
memset(bp, 0, bootp_sizes[BOOTP_DISCOVER]);
memcpy(bp->bp_chaddr, myhwaddr, ETH_ALEN);
bp->bp_op = BOOTP_OP_REQUEST;
bp->bp_hwtype = mytype;
bp->bp_hlen = ETH_ALEN;
bp->bp_xid = boot_xid;
bp->bp_secs = htons(((unsigned short)(get_ticks() - boot_start_time)) / 18);
bp->bp_ciaddr = n_IP_ANY;
bp->bp_siaddr = n_IP_BROADCAST;
*((unsigned long *)(bp->bp_vend)) = VM_RFC1048;
bp->bp_vend[VM_SIZE] = VEND_END;
/* Set the size of the BOOTP record */
set_bootp_size();
/* Send the bootp record as a broadcast message to all servers */
return(udp_write((unsigned char *)bp, getds(), bootp_sizes[BOOTP_DISCOVER]));
}
/*
**************************************************************************
*
* Validate received BOOTP reply
*/
static int validate()
{
register struct bootp *bp = bootp_bufs[cur_bootp_buf];
int retval = FALSE;
if (
/* Only accept BOOTP replies */
bp->bp_op == BOOTP_OP_REPLY &&
/* Only accept packets that match my transaction ID */
bp->bp_xid == boot_xid &&
/* Only accept packets that match my hardware address */
!memcmp(bp->bp_chaddr, myhwaddr, ETH_ALEN) &&
/* Only accept packets with no vendor field or correct magic number */
(bp->bp_vend[0] == '\0' ||
(*((unsigned long *)(bp->bp_vend)) == VM_RFC1048 &&
get_vend(VEND_END) != NULL)) &&
/* Do not accept loopback addresses */
bp->bp_yiaddr != n_IP_LOCALHOST)
retval = TRUE;
return(retval);
}
/*
**************************************************************************
*
* Get a BOOTP record from the server.
*
*/
static int bootp_get()
{
register struct bootp *bp;
unsigned int timeout;
unsigned int bufsize;
int retval, retries;
char errch;
/*
* Setup the IP interface information. Our own address has to be
* IP_ANY, so that the IP layer will not discard the incoming packets.
* Then open a UDP socket.
*/
myipaddr = n_IP_ANY;
mynetmask = n_IP_CLASS_A;
printf("\n\nBOOTP: ");
if ((retval = udp_open(n_IP_BROADCAST, n_IP_ANY,
htons(BOOTP_C_PORT),
htons(BOOTP_S_PORT))) != PXENV_STATUS_SUCCESS)
return(retval);
/*
* Now loop until receiving a reply from a server. The retry time is
* computed as suggested in RFC951.
*/
retries = 0;
timeout = BOOTP_TIMEOUT + random(0x001f);
printf("Sending request (press ESC to abort): ");
while (TRUE) {
/* Send new BOOTP request */
boot_xid = get_ticks() + random(0x7fff);
if ((retval = bootp_send()) != PXENV_STATUS_SUCCESS)
break;
/* Allocate buffer for BOOTP reply */
if (!new_bootp_buf(BOOTP_REPLY)) {
retval = PXENV_STATUS_FAILURE;
break;
}
bp = bootp_bufs[BOOTP_REPLY];
/* Wait for reply packet */
bufsize = bootp_sizes[BOOTP_REPLY];
retval = udp_read((unsigned char *)bp, getds(), &bufsize,
timeout, CHR_ESC);
/* Check for error and that we received a valid packet */
errch = '?';
if (retval != PXENV_STATUS_SUCCESS) {
/* Check if user pressed ESC key */
if (retval == PXENV_STATUS_ARP_CANCELED) {
retval = PXENV_STATUS_BOOTP_CANCELED;
break;
}
/* We have a timeout */
errch = '.';
if (retries++ > BOOTP_RETRIES) {
retval = PXENV_STATUS_BOOTP_TIMEOUT;
break;
}
} else if (bufsize >= BOOTP_MIN_SIZE && validate()) {
/* We received a valid BOOTP reply packet */
printf("ok\n");
retval = PXENV_STATUS_SUCCESS;
break;
}
/* Increase timeout time and print error character */
timeout = (timeout << 1) + random(0x001f);
while (timeout > 60) /* dont allow more than 60 seconds */
timeout -= 60;
printf("%c", errch);
}
/* Close UDP connection and return */
udp_close();
return(retval);
}
/*
**************************************************************************
*
* Handle BOOTP protocol.
*/
int bootp()
{
register unsigned char *cp;
int retval;
/* Set boot start time - required for gateway booting */
boot_start_time = get_ticks();
/* Get BOOTP record from server */
if ((retval = bootp_get()) != PXENV_STATUS_SUCCESS)
goto bootp_ret;
/* Setup the network interface with the values received from BOOTP server */
cur_bootp_buf = BOOTP_REPLY;
myipaddr = bootp_bufs[BOOTP_REPLY]->bp_yiaddr;
if ((cp = get_vend(VEND_SUBNET)) != NULL)
mynetmask = *((t_ipaddr *)(cp + 1));
else if (n_IN_CLASS_A(myipaddr))
mynetmask = n_IP_CLASS_A;
else if (n_IN_CLASS_B(myipaddr))
mynetmask = n_IP_CLASS_B;
else if (n_IN_CLASS_C(myipaddr))
mynetmask = n_IP_CLASS_C;
if ((cp = get_vend(VEND_ROUTER)) != NULL)
mygateway = *((t_ipaddr *)(cp + 1));
else
mygateway = n_IP_ANY;
#ifndef NOBPEXT
/* Next try to get the extensions file if there is any specified */
if ((cp = get_vend(VEND_EXTFILE)) != NULL)
get_ext(cp);
#endif
/*
* Finally set the size of the reply buffer. Also we have to set the
* pointer to the acknowledge packet (which doesn't exist with BOOTP).
*/
set_bootp_size();
bootp_bufs[BOOTP_ACK] = bootp_bufs[BOOTP_REPLY];
bootp_sizes[BOOTP_ACK] = bootp_sizes[BOOTP_REPLY];
retval = PXENV_STATUS_SUCCESS;
bootp_ret:
return(retval);
}
syntax highlighted by Code2HTML, v. 0.9.1