dhcp_prot.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,494 行 · 第 1/4 页
C
1,494 行
/*==========================================================================
//
// dhcp_prot.c
//
// DHCP protocol implementation for DHCP client
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Andrew Lunn
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): hmt
// Contributors: gthomas, andrew.lunn@ascom.ch
// Date: 2000-07-01
// Purpose: DHCP support
// Description:
//
//####DESCRIPTIONEND####
//
//========================================================================*/
#include <pkgconf/system.h>
#include <pkgconf/net.h>
#ifdef CYGPKG_NET_DHCP
#ifdef CYGPKG_NET_SNTP
#include <pkgconf/net_sntp.h>
#endif /* CYGPKG_NET_SNTP */
#if 0
#define perror( txt ) // nothing
#endif
#include <network.h>
#include <dhcp.h>
#include <errno.h>
#include <cyg/infra/cyg_ass.h>
#ifdef INET6
#include <net/if_var.h>
#include <netinet6/in6_var.h>
#endif
extern int cyg_arc4random(void);
#ifdef CYGOPT_NET_DHCP_OPTION_HOST_NAME
static char dhcp_hostname[CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN+1];
// Set the hostname used by the DHCP TAG_HOST_NAME option. We
// copy the callers name into a private buffer, since we don't
// know the context in which the callers hostname was allocated.
void dhcp_set_hostname(char *hostname)
{
CYG_ASSERT( (strlen(hostname)<=CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN), "dhcp hostname too long" );
strncpy(dhcp_hostname, hostname, CYGNUM_NET_DHCP_OPTION_HOST_NAME_LEN);
}
#endif
/* Forward reference prototypes. */
static int unset_tag( struct bootp *ppkt, unsigned char tag );
// ------------------------------------------------------------------------
// Returns a pointer to the end of dhcp message (or NULL if invalid)
// meaning the address of the byte *after* the TAG_END token in the vendor
// data.
static unsigned char *
scan_dhcp_size( struct bootp *ppkt )
{
unsigned char *op;
op = &ppkt->bp_vend[0];
// First check for the cookie!
if ( op[0] != 99 ||
op[1] != 130 ||
op[2] != 83 ||
op[3] != 99 ) {
CYG_FAIL( "Bad DHCP cookie" );
return NULL;
}
op += 4;
// This will only scan the options field.
while (*op != TAG_END) {
if ( *op == TAG_PAD ) {
op++;
} else {
op += *(op+1)+2;
}
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in dhcp_size" );
return NULL;
}
}
// Check op has not gone wild
CYG_ASSERT( op > (unsigned char *)(&ppkt[0]), "op pointer underflow!" );
// Compare op with non-existent "next" struct bootp in the array.
CYG_ASSERT( op < (unsigned char *)(&ppkt[1]), "op pointer overflow!" );
return op + 1; // Address of first invalid byte
}
// ------------------------------------------------------------------------
// Get the actual packet size of an initialized buffer
static int
dhcp_size( struct bootp *ppkt )
{
unsigned char *op;
op = scan_dhcp_size( ppkt );
if ( !op ) return 0;
return (op - (unsigned char *)ppkt);
}
// ------------------------------------------------------------------------
// Get the actual packet size of an initialized buffer
// This will also pad the packet with 0 if length is less
// than BP_STD_TX_MINPKTSZ.
static int
dhcp_size_for_send( struct bootp *ppkt )
{
unsigned char *op;
op = scan_dhcp_size( ppkt );
if ( !op ) return 0; // Better not scribble!
// Zero extra bytes until the packet is large enough.
for ( ; op < (((unsigned char *)ppkt) + BP_STD_TX_MINPKTSZ); op++ )
*op = 0;
return (op - (unsigned char *)ppkt);
}
// ------------------------------------------------------------------------
// Insert/set an option value in an initialized buffer
static int
set_fixed_tag( struct bootp *ppkt,
unsigned char tag,
cyg_uint32 value,
int len)
{
unsigned char *op;
// Initially this will only scan the options field.
op = &ppkt->bp_vend[4];
while (*op != TAG_END) {
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag" );
return false;
}
if (*op == tag) // Found it...
break;
if ( *op == TAG_PAD ) {
op++;
} else {
op += *(op+1)+2;
}
}
if (*op == tag) { // Found it...
/* There are three possibilities:
* 1) *(op+1) == len
* 2) *(op+1) > len
* 3) *(op+1) < len
* For 1, just overwrite the existing option data.
* For 2, overwrite the existing option data and pullup the
* remaining option data (if any).
* For 3, pullup any remaining option data to remove the option
* and then add the option to the end.
* For simplicity, for case 2 and 3, we just call unset_tag()
* and re-add the option to the end.
*/
if ( *(op+1) != len ) {
/* Remove existing option entry. */
unset_tag(ppkt, tag);
/* Adjust the op pointer to re-add at the end. */
op = scan_dhcp_size(ppkt);
CYG_ASSERT(op!=NULL, "Invalid options size in set_fixed_tag" );
op--;
CYG_ASSERT(*op==TAG_END, "Missing TAG_END in set_fixed_tag");
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag replace" );
return false;
}
*op = tag;
*(op+1) = len;
*(op + len + 2) = TAG_END;
}
}
else { // overwrite the end tag and install a new one
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in set_fixed_tag append" );
return false;
}
*op = tag;
*(op+1) = len;
*(op + len + 2) = TAG_END;
}
// and insert the value. Net order is BE.
op += len + 2 - 1; // point to end of value
while ( len-- > 0 ) {
*op-- = (unsigned char)(value & 255);
value >>= 8;
}
return true;
}
static int
set_variable_tag( struct bootp *ppkt,
unsigned char tag,
cyg_uint8 *pvalue,
int len)
{
unsigned char *op;
// Initially this will only scan the options field.
op = &ppkt->bp_vend[4];
while (*op != TAG_END) {
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in set_variable_tag" );
return false;
}
if (*op == tag) // Found it...
break;
if ( *op == TAG_PAD ) {
op++;
} else {
op += *(op+1)+2;
}
}
if (*op == tag) { // Found it...
/* There are three possibilities:
* 1) *(op+1) == len
* 2) *(op+1) > len
* 3) *(op+1) < len
* For 1, just overwrite the existing option data.
* For 2, overwrite the existing option data and pullup the
* remaining option data (if any).
* For 3, pullup any remaining option data to remove the option
* and then add the option to the end.
* For simplicity, for case 2 and 3, we just call unset_tag()
* and re-add the option to the end.
*/
if ( *(op+1) != len ) {
/* Remove existing option entry. */
unset_tag(ppkt, tag);
/* Adjust the op pointer to re-add at the end. */
op = scan_dhcp_size(ppkt);
CYG_ASSERT(op!=NULL, "Invalid options size in set_variable_tag" );
op--;
CYG_ASSERT(*op==TAG_END, "Missing TAG_END in set_variable_tag");
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in set_variable_tag replace" );
return false;
}
*op = tag;
*(op+1) = len;
*(op + len + 2) = TAG_END;
}
}
else { // overwrite the end tag and install a new one
if ( op + len + 2 > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in set_variable_tag append" );
return false;
}
*op = tag;
*(op+1) = len;
*(op + len + 2) = TAG_END;
}
// and insert the value. No order is implied.
op += 2; // point to start of value
while ( len-- > 0 ) {
*op++ = *pvalue++;
}
return true;
}
static int
unset_tag( struct bootp *ppkt,
unsigned char tag )
{
unsigned char *op, *nextp = 0, *killp = 0;
// Initially this will only scan the options field.
op = &ppkt->bp_vend[4];
while (*op != TAG_END) {
if ( op > &ppkt->bp_vend[BP_VEND_LEN-1] ) {
CYG_FAIL( "Oversize DHCP packet in unset_tag" );
return false;
}
if (*op == tag) { // Found it...
killp = op; // item to kill
nextp = op + *(op+1)+2; // next item address
}
if ( *op == TAG_PAD ) {
op++;
} else {
op += *(op+1)+2;
}
}
if ( !killp )
return false;
// Obliterate the found op by copying down: *op is the end.
while( nextp <= op ) // <= to copy the TAG_END too.
*killp++ = *nextp++;
return true;
}
// ------------------------------------------------------------------------
// Bring up an interface enough to broadcast, before we know who we are
static int
bring_half_up(const char *intf, struct ifreq *ifrp )
{
int s = -1;
int one = 1;
struct sockaddr_in *addrp;
struct ecos_rtentry route;
int retcode = false;
// Ensure clean slate
cyg_route_reinit(); // Force any existing routes to be forgotten
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
goto out;
}
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
perror("setsockopt");
goto out;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?