/* ************************************************************************** * * 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 * * 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 #include #include #include #include #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); }