mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-21 02:50:14 +01:00
1180 lines
37 KiB
C
1180 lines
37 KiB
C
|
|
/*
|
||
|
|
* dhcpcd - DHCP client daemon -
|
||
|
|
* Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi@fore.com>
|
||
|
|
* Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com>
|
||
|
|
*
|
||
|
|
* dhcpcd is an RFC2131 and RFC1541 compliant DHCP client daemon.
|
||
|
|
*
|
||
|
|
* This 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
|
||
|
|
* (at your option) 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.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <sys/types.h>
|
||
|
|
#include <sys/socket.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <sys/ioctl.h>
|
||
|
|
#include <sys/time.h>
|
||
|
|
#include <sys/utsname.h>
|
||
|
|
#include <net/if.h>
|
||
|
|
#include <net/if_arp.h>
|
||
|
|
#include <net/if_packet.h>
|
||
|
|
#include <net/route.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <syslog.h>
|
||
|
|
#include <errno.h>
|
||
|
|
#include <setjmp.h>
|
||
|
|
#include <time.h>
|
||
|
|
#include "client.h"
|
||
|
|
#include "buildmsg.h"
|
||
|
|
#include "udpipgen.h"
|
||
|
|
#include "arp.h"
|
||
|
|
|
||
|
|
int DebugFlag = 1;
|
||
|
|
#define DEBUG
|
||
|
|
|
||
|
|
void debug_dump_dhcp_options (udpipMessage *udp_msg, dhcpOptions *options);
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
int parseDhcpMsgRecv(udpipMessage *udp_msg, dhcpOptions *options) /* this routine parses dhcp message received */
|
||
|
|
{
|
||
|
|
const struct ip *ip_msg = (struct ip *)((struct udpiphdr *)udp_msg->udpipmsg)->ip;
|
||
|
|
dhcpMessage *dhcp_msg = (dhcpMessage *)&(udp_msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
register u_char *p = dhcp_msg->options+4;
|
||
|
|
unsigned char *end = dhcp_msg->options + sizeof(dhcp_msg->options);
|
||
|
|
|
||
|
|
/* Force T1 and T2 to 0: either new values will be in message, or they
|
||
|
|
will need to be recalculated from lease time */
|
||
|
|
if ( options->val[dhcpT1value] && options->len[dhcpT1value] > 0 )
|
||
|
|
memset (options->val[dhcpT1value], 0, options->len[dhcpT1value]);
|
||
|
|
if ( options->val[dhcpT2value] && options->len[dhcpT2value] > 0 )
|
||
|
|
memset (options->val[dhcpT2value], 0, options->len[dhcpT2value]);
|
||
|
|
|
||
|
|
while ( p < end )
|
||
|
|
{
|
||
|
|
switch ( *p )
|
||
|
|
{
|
||
|
|
case endOption:
|
||
|
|
goto swend;
|
||
|
|
case padOption:
|
||
|
|
p++;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
if ( p[1] )
|
||
|
|
{
|
||
|
|
if ( options->len[*p] == p[1] )
|
||
|
|
memcpy (options->val[*p], p+2, p[1]);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
options->len[*p] = p[1];
|
||
|
|
if ( options->val[*p] )
|
||
|
|
free (options->val[*p]);
|
||
|
|
else
|
||
|
|
options->num++;
|
||
|
|
options->val[*p] = malloc (p[1]+1);
|
||
|
|
memset (options->val[*p], 0, p[1]+1);
|
||
|
|
memcpy (options->val[*p], p+2, p[1]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
p+=p[1]+2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
swend:
|
||
|
|
#ifdef DEBUG
|
||
|
|
debug_dump_dhcp_options (udp_msg, options);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
if ( ! dhcp_msg->yiaddr )
|
||
|
|
dhcp_msg->yiaddr = DhcpMsgSend->ciaddr;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if ( ! options->val[dhcpServerIdentifier] ) /* did not get dhcpServerIdentifier */
|
||
|
|
{
|
||
|
|
/* make it the same as IP address of the sender */
|
||
|
|
options->val[dhcpServerIdentifier] = malloc(4);
|
||
|
|
memcpy (options->val[dhcpServerIdentifier], &ip_msg->ip_src.s_addr, 4);
|
||
|
|
options->len[dhcpServerIdentifier] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "dhcpServerIdentifier option is missing in DHCP server response. Assuming %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)options->val[dhcpServerIdentifier])[0],
|
||
|
|
((unsigned char *)options->val[dhcpServerIdentifier])[1],
|
||
|
|
((unsigned char *)options->val[dhcpServerIdentifier])[2],
|
||
|
|
((unsigned char *)options->val[dhcpServerIdentifier])[3]);
|
||
|
|
}
|
||
|
|
if ( ! options->val[dns] ) /* did not get DNS */
|
||
|
|
{
|
||
|
|
/* make it the same as dhcpServerIdentifier */
|
||
|
|
options->val[dns] = malloc(4);
|
||
|
|
memcpy (options->val[dns], options->val[dhcpServerIdentifier], 4);
|
||
|
|
options->len[dns] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "dns option is missing in DHCP server response. Assuming %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)options->val[dns])[0],
|
||
|
|
((unsigned char *)options->val[dns])[1],
|
||
|
|
((unsigned char *)options->val[dns])[2],
|
||
|
|
((unsigned char *)options->val[dns])[3]);
|
||
|
|
}
|
||
|
|
if ( ! options->val[subnetMask] ) /* did not get subnetMask */
|
||
|
|
{
|
||
|
|
options->val[subnetMask] = malloc(4);
|
||
|
|
((unsigned char *)options->val[subnetMask])[0] = 255;
|
||
|
|
if ( IN_CLASSA(ntohl(dhcp_msg->yiaddr)) )
|
||
|
|
{
|
||
|
|
((unsigned char *)options->val[subnetMask])[1] = 0; /* class A */
|
||
|
|
((unsigned char *)options->val[subnetMask])[2] = 0;
|
||
|
|
((unsigned char *)options->val[subnetMask])[3] = 0;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
((unsigned char *)options->val[subnetMask])[1] = 255;
|
||
|
|
if ( IN_CLASSB(ntohl(dhcp_msg->yiaddr)) )
|
||
|
|
{
|
||
|
|
((unsigned char *)(options->val[subnetMask]))[2] = 0;/* class B */
|
||
|
|
((unsigned char *)(options->val[subnetMask]))[3] = 0;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
((unsigned char *)options->val[subnetMask])[2] = 255;
|
||
|
|
if ( IN_CLASSC(ntohl(dhcp_msg->yiaddr)) )
|
||
|
|
((unsigned char *)options->val[subnetMask])[3] = 0; /* class C */
|
||
|
|
else
|
||
|
|
((unsigned char *)options->val[subnetMask])[3] = 255;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
options->len[subnetMask] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "subnetMask option is missing in DHCP server response. Assuming %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)options->val[subnetMask])[0],
|
||
|
|
((unsigned char *)options->val[subnetMask])[1],
|
||
|
|
((unsigned char *)options->val[subnetMask])[2],
|
||
|
|
((unsigned char *)options->val[subnetMask])[3]);
|
||
|
|
}
|
||
|
|
if ( ! options->val[broadcastAddr] ) /* did not get broadcastAddr */
|
||
|
|
{
|
||
|
|
int br = dhcp_msg->yiaddr | ~*((int *)options->val[subnetMask]);
|
||
|
|
options->val[broadcastAddr] = malloc(4);
|
||
|
|
memcpy (options->val[broadcastAddr], &br, 4);
|
||
|
|
options->len[broadcastAddr] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "broadcastAddr option is missing in DHCP server response. Assuming %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)options->val[broadcastAddr])[0],
|
||
|
|
((unsigned char *)options->val[broadcastAddr])[1],
|
||
|
|
((unsigned char *)options->val[broadcastAddr])[2],
|
||
|
|
((unsigned char *)options->val[broadcastAddr])[3]);
|
||
|
|
}
|
||
|
|
#if 0
|
||
|
|
if ( ! options->val[routersOnSubnet] )
|
||
|
|
{
|
||
|
|
options->val[routersOnSubnet] = malloc(4);
|
||
|
|
if ( dhcp_msg->giaddr )
|
||
|
|
memcpy(options->val[routersOnSubnet],&dhcp_msg->giaddr,4);
|
||
|
|
else
|
||
|
|
memcpy(options->val[routersOnSubnet],options->val[dhcpServerIdentifier],4);
|
||
|
|
options->len[routersOnSubnet] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "routersOnSubnet option is missing in DHCP server response. Assuming %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)options->val[routersOnSubnet])[0],
|
||
|
|
((unsigned char *)options->val[routersOnSubnet])[1],
|
||
|
|
((unsigned char *)options->val[routersOnSubnet])[2],
|
||
|
|
((unsigned char *)options->val[routersOnSubnet])[3]);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
if ( options->val[dhcpIPaddrLeaseTime] && options->len[dhcpIPaddrLeaseTime] == 4 )
|
||
|
|
{
|
||
|
|
if ( *(unsigned int *)options->val[dhcpIPaddrLeaseTime] == 0 )
|
||
|
|
{
|
||
|
|
unsigned int lease_time = htonl (DHCP_DEFAULT_LEASETIME);
|
||
|
|
memcpy (options->val[dhcpIPaddrLeaseTime], &lease_time, 4);
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpIPaddrLeaseTime=0 in DHCP server response. Assuming %u sec\n", lease_time);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpIPaddrLeaseTime = %u in DHCP server response.\n",
|
||
|
|
ntohl(*(unsigned int *)options->val[dhcpIPaddrLeaseTime]));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else /* did not get dhcpIPaddrLeaseTime */
|
||
|
|
{
|
||
|
|
unsigned int lease_time = htonl (DHCP_DEFAULT_LEASETIME);
|
||
|
|
options->val[dhcpIPaddrLeaseTime] = malloc(4);
|
||
|
|
memcpy (options->val[dhcpIPaddrLeaseTime], &lease_time, 4);
|
||
|
|
options->len[dhcpIPaddrLeaseTime] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpIPaddrLeaseTime option is missing in DHCP server response. Assuming %u sec\n", lease_time);
|
||
|
|
}
|
||
|
|
if ( options->val[dhcpT1value] && options->len[dhcpT1value] == 4 )
|
||
|
|
{
|
||
|
|
if ( *(unsigned int *)options->val[dhcpT1value] == 0 )
|
||
|
|
{
|
||
|
|
unsigned t2 = 0.5 * ntohl(*(unsigned int *)options->val[dhcpIPaddrLeaseTime]);
|
||
|
|
int t1 = htonl(t2);
|
||
|
|
memcpy (options->val[dhcpT1value],&t1,4);
|
||
|
|
options->len[dhcpT1value] = 4;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpT1value is missing in DHCP server response. Assuming %u sec\n",t2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else /* did not get T1 */
|
||
|
|
{
|
||
|
|
unsigned t2 = 0.5*ntohl(*(unsigned int *)options->val[dhcpIPaddrLeaseTime]);
|
||
|
|
int t1 = htonl(t2);
|
||
|
|
options->val[dhcpT1value] = malloc(4);
|
||
|
|
memcpy (options->val[dhcpT1value],&t1,4);
|
||
|
|
options->len[dhcpT1value] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpT1value is missing in DHCP server response. Assuming %u sec\n",t2);
|
||
|
|
}
|
||
|
|
if ( options->val[dhcpT2value] && options->len[dhcpT2value] == 4 )
|
||
|
|
{
|
||
|
|
if ( *(unsigned int *)options->val[dhcpT2value] == 0 )
|
||
|
|
{
|
||
|
|
unsigned t2 = 0.875*ntohl(*(unsigned int *)options->val[dhcpIPaddrLeaseTime]);
|
||
|
|
int t1 = htonl(t2);
|
||
|
|
memcpy(options->val[dhcpT2value],&t1,4);
|
||
|
|
options->len[dhcpT2value] = 4;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpT2value is missing in DHCP server response. Assuming %u sec\n",t2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else /* did not get T2 */
|
||
|
|
{
|
||
|
|
unsigned t2 = 0.875*ntohl(*(unsigned int *)options->val[dhcpIPaddrLeaseTime]);
|
||
|
|
int t1 = htonl(t2);
|
||
|
|
options->val[dhcpT2value] = malloc(4);
|
||
|
|
memcpy(options->val[dhcpT2value],&t1,4);
|
||
|
|
options->len[dhcpT2value] = 4;
|
||
|
|
options->num++;
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG,"dhcpT2value is missing in DHCP server response. Assuming %u sec\n",t2);
|
||
|
|
}
|
||
|
|
if ( options->val[dhcpMessageType] )
|
||
|
|
return *(unsigned char *)options->val[dhcpMessageType];
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
void classIDsetup(dhcp_interface *iface, const char *g_cls_id)
|
||
|
|
{
|
||
|
|
unsigned int g_cls_id_len = 0;
|
||
|
|
|
||
|
|
if (!iface) return;
|
||
|
|
|
||
|
|
if (g_cls_id)
|
||
|
|
g_cls_id_len = strlen (g_cls_id);
|
||
|
|
|
||
|
|
if (g_cls_id_len)
|
||
|
|
{
|
||
|
|
memcpy (iface->cls_id, g_cls_id, g_cls_id_len);
|
||
|
|
iface->cls_id_len = g_cls_id_len;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
struct utsname sname;
|
||
|
|
if ( uname(&sname) )
|
||
|
|
syslog(LOG_ERR,"classIDsetup: uname: %m\n");
|
||
|
|
snprintf (iface->cls_id, DHCP_CLASS_ID_MAX_LEN, "%s %s %s",
|
||
|
|
sname.sysname, sname.release, sname.machine);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
void clientIDsetup(dhcp_interface *iface, const char *g_cli_id)
|
||
|
|
{
|
||
|
|
unsigned int g_cli_id_len = 0;
|
||
|
|
unsigned char *c = iface->cli_id;
|
||
|
|
|
||
|
|
if (!iface) return;
|
||
|
|
|
||
|
|
if (g_cli_id)
|
||
|
|
g_cli_id_len = strlen (g_cli_id);
|
||
|
|
|
||
|
|
*c++ = dhcpClientIdentifier;
|
||
|
|
if ( g_cli_id_len )
|
||
|
|
{
|
||
|
|
*c++ = g_cli_id_len + 1; /* 1 for the field below */
|
||
|
|
*c++ = 0; /* type: string */
|
||
|
|
memcpy (c, g_cli_id, g_cli_id_len);
|
||
|
|
iface->cli_id_len = g_cli_id_len + 3;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
*c++ = ETH_ALEN + 1; /* length: 6 (MAC Addr) + 1 (# field) */
|
||
|
|
*c++ = (iface->bTokenRing) ? ARPHRD_IEEE802_TR : ARPHRD_ETHER; /* type: Ethernet address */
|
||
|
|
memcpy (c, iface->chaddr, ETH_ALEN);
|
||
|
|
iface->cli_id_len = ETH_ALEN + 3;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
void releaseDhcpOptions (dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
register int i;
|
||
|
|
for ( i = 1; i < 256; i++ )
|
||
|
|
{
|
||
|
|
if ( iface->dhcp_options.val[i] )
|
||
|
|
free(iface->dhcp_options.val[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&(iface->dhcp_options), 0, sizeof(dhcpOptions));
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
#ifdef DEBUG
|
||
|
|
static void dumpframe(const char *title, struct packed_ether_header *frame)
|
||
|
|
{
|
||
|
|
int i;
|
||
|
|
unsigned char *dp;
|
||
|
|
|
||
|
|
printf("%s:", title);
|
||
|
|
dp = (unsigned char *)frame;
|
||
|
|
for (i = 0; i < 32; i++)
|
||
|
|
{
|
||
|
|
if ((i % 16) == 0)
|
||
|
|
printf("\n");
|
||
|
|
printf("0x%02X ", *dp++);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif /* DEBUG */
|
||
|
|
/*****************************************************************************/
|
||
|
|
/***** convert ethernet and token-ring frames *****/
|
||
|
|
int eth2tr(struct packed_ether_header *frame, int datalen)
|
||
|
|
{
|
||
|
|
struct trh_hdr *phdr;
|
||
|
|
struct trllc *pllc;
|
||
|
|
char trheader[sizeof(struct trh_hdr) - sizeof(phdr->rseg) + sizeof(struct trllc)];
|
||
|
|
int len;
|
||
|
|
|
||
|
|
#ifdef DEBUG
|
||
|
|
dumpframe("eth2tr: Incoming eth frame", frame);
|
||
|
|
#endif
|
||
|
|
memset(trheader, 0, sizeof(trheader));
|
||
|
|
phdr = (struct trh_hdr *)trheader;
|
||
|
|
phdr->ac = AC;
|
||
|
|
phdr->fc = LLC_FRAME;
|
||
|
|
memcpy(phdr->daddr, frame->ether_dhost, TR_ALEN);
|
||
|
|
memcpy(phdr->saddr, frame->ether_shost, TR_ALEN);
|
||
|
|
if (phdr->daddr[0] & 0x80)
|
||
|
|
{ /* Destination is a broadcast */
|
||
|
|
phdr->rcf = sizeof(phdr->rcf) | htons(TR_RCF_BROADCAST | 0x70); /* Unlimited frame length */
|
||
|
|
pllc = (struct trllc *)&phdr->rseg[0];
|
||
|
|
phdr->saddr[0] |= TR_RII; /* Set source-route indicator */
|
||
|
|
len = sizeof(trheader);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
pllc = (struct trllc *)&phdr->rcf;
|
||
|
|
len = sizeof(trheader) - sizeof(phdr->rcf);
|
||
|
|
}
|
||
|
|
pllc->dsap = EXTENDED_SAP;
|
||
|
|
pllc->ssap = EXTENDED_SAP;
|
||
|
|
pllc->llc = UI_CMD;
|
||
|
|
pllc->protid[0] = pllc->protid[1] = pllc->protid[2] = 0;
|
||
|
|
pllc->ethertype = frame->ether_type;
|
||
|
|
/* Make room for larger TR header */
|
||
|
|
memmove((char *)(frame + 1) + (len - sizeof(struct packed_ether_header)), frame + 1, datalen);
|
||
|
|
memcpy(frame, trheader, len); /* Install TR header */
|
||
|
|
#ifdef DEBUG
|
||
|
|
dumpframe("eth2tr: Outgoing tr frame", frame);
|
||
|
|
#endif
|
||
|
|
return len + datalen;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int tr2eth(struct packed_ether_header *frame)
|
||
|
|
{
|
||
|
|
struct trh_hdr hdr;
|
||
|
|
struct trllc *pllc;
|
||
|
|
int hlen = 0;
|
||
|
|
|
||
|
|
#ifdef DEBUG
|
||
|
|
dumpframe("tr2eth: Incoming tr frame", frame);
|
||
|
|
#endif
|
||
|
|
hdr = *((struct trh_hdr *)frame);
|
||
|
|
if (hdr.saddr[0] & TR_RII)
|
||
|
|
{
|
||
|
|
fake_rif:
|
||
|
|
hlen = hdr.rcf & ntohs(TR_RCF_LEN_MASK);
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("rcf = 0x%X SR len %d\n", hdr.rcf, hlen);
|
||
|
|
#endif
|
||
|
|
if (hlen < sizeof(hdr.rcf) || (hlen & 1))
|
||
|
|
return 1;
|
||
|
|
hdr.saddr[0] &= ~TR_RII;
|
||
|
|
}
|
||
|
|
pllc = (struct trllc *)(((__u8 *)frame) + sizeof(struct trh_hdr) - TR_MAXRIFLEN + hlen);
|
||
|
|
if (pllc->dsap != EXTENDED_SAP || pllc->llc != UI_CMD)
|
||
|
|
{
|
||
|
|
if (hlen == 0)
|
||
|
|
goto fake_rif; /* Bug in 2.2.3 kernel */
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("corrupted TR-IP packet of ui=0x%x and dsap 0x%x discarded\n", pllc->llc, pllc->dsap);
|
||
|
|
#endif
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
memcpy(frame->ether_dhost, hdr.daddr, ETH_ALEN);
|
||
|
|
memcpy(frame->ether_shost, hdr.saddr, ETH_ALEN);
|
||
|
|
frame->ether_type = pllc->ethertype;
|
||
|
|
memmove(frame + 1, pllc + 1, IPPACKET_SIZE); /* Move data portion: Overlapping buffer */
|
||
|
|
#ifdef DEBUG
|
||
|
|
dumpframe("tr2eth: Outgoing eth frame", frame);
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
/* Subtract the `struct timeval' values X and Y,
|
||
|
|
storing the result in RESULT.
|
||
|
|
Return 1 if the difference is negative, otherwise 0. */
|
||
|
|
static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
|
||
|
|
{
|
||
|
|
/* Perform the carry for the later subtraction by updating Y. */
|
||
|
|
if (x->tv_usec < y->tv_usec)
|
||
|
|
{
|
||
|
|
int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
|
||
|
|
y->tv_usec -= 1000000 * nsec;
|
||
|
|
y->tv_sec += nsec;
|
||
|
|
}
|
||
|
|
if (x->tv_usec - y->tv_usec > 1000000)
|
||
|
|
{
|
||
|
|
int nsec = (x->tv_usec - y->tv_usec) / 1000000;
|
||
|
|
y->tv_usec += 1000000 * nsec;
|
||
|
|
y->tv_sec -= nsec;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Compute the time remaining to wait.
|
||
|
|
`tv_usec' is certainly positive. */
|
||
|
|
result->tv_sec = x->tv_sec - y->tv_sec;
|
||
|
|
result->tv_usec = x->tv_usec - y->tv_usec;
|
||
|
|
|
||
|
|
/* Return 1 if result is negative. */
|
||
|
|
return x->tv_sec < y->tv_sec;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int peekfd (dhcp_interface *iface, time_t tv_usec)
|
||
|
|
{
|
||
|
|
fd_set fs;
|
||
|
|
struct timeval begin;
|
||
|
|
time_t i = tv_usec;
|
||
|
|
|
||
|
|
FD_ZERO (&fs);
|
||
|
|
FD_SET (iface->sk, &fs);
|
||
|
|
gettimeofday (&begin, NULL);
|
||
|
|
|
||
|
|
/* Wake up each second to check whether or not we've been told
|
||
|
|
* to stop with iface->cease
|
||
|
|
*/
|
||
|
|
while (i > 0)
|
||
|
|
{
|
||
|
|
struct timeval now;
|
||
|
|
struct timeval diff;
|
||
|
|
struct timeval wait = {1, 0};
|
||
|
|
|
||
|
|
if ( select (iface->sk+1, &fs, NULL, NULL, &wait) == -1 )
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
if ( FD_ISSET(iface->sk, &fs) )
|
||
|
|
return RET_DHCP_SUCCESS;
|
||
|
|
gettimeofday (&now, NULL);
|
||
|
|
timeval_subtract (&diff, &now, &begin);
|
||
|
|
i = tv_usec - ((diff.tv_sec * 1000000) + diff.tv_usec);
|
||
|
|
if (iface->cease)
|
||
|
|
return RET_DHCP_CEASED;
|
||
|
|
}
|
||
|
|
return RET_DHCP_TIMEOUT;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcpSendAndRecv (dhcp_interface *iface, unsigned int expected_reply_type,
|
||
|
|
dhcp_msg_build_proc buildUdpIpMsg, udpipMessage **return_msg)
|
||
|
|
{
|
||
|
|
udpipMessage *udp_msg_recv = NULL;
|
||
|
|
struct sockaddr addr;
|
||
|
|
int len, err, local_timeout = 0;
|
||
|
|
int j = DHCP_INITIAL_RTO / 2;
|
||
|
|
struct timeval local_begin, current, diff;
|
||
|
|
struct timeval overall_end;
|
||
|
|
|
||
|
|
*return_msg = NULL;
|
||
|
|
|
||
|
|
gettimeofday (&overall_end, NULL);
|
||
|
|
overall_end.tv_sec += iface->client_options->base_timeout;
|
||
|
|
|
||
|
|
do
|
||
|
|
{
|
||
|
|
udpipMessage *udp_msg_send = buildUdpIpMsg (iface);
|
||
|
|
|
||
|
|
if (!udp_msg_send)
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
j += j;
|
||
|
|
if (j > DHCP_MAX_RTO)
|
||
|
|
j = DHCP_MAX_RTO;
|
||
|
|
|
||
|
|
/* Make sure waiting j seconds isn't greater than our overall time left
|
||
|
|
* on this operation, and clamp j to the overall time left if it is.
|
||
|
|
*/
|
||
|
|
gettimeofday (¤t, NULL);
|
||
|
|
if (timeval_subtract (&diff, &overall_end, ¤t))
|
||
|
|
{
|
||
|
|
free (udp_msg_send);
|
||
|
|
return RET_DHCP_TIMEOUT;
|
||
|
|
}
|
||
|
|
if (j > ((diff.tv_sec * 1000000) + diff.tv_usec))
|
||
|
|
j = (diff.tv_sec * 1000000) + diff.tv_usec;
|
||
|
|
|
||
|
|
if (iface->bTokenRing) /* Here we convert a Eth frame into an TR frame */
|
||
|
|
len = eth2tr (&(udp_msg_send->ethhdr), sizeof(udpiphdr) + sizeof(dhcpMessage));
|
||
|
|
else
|
||
|
|
len = sizeof (struct packed_ether_header) + sizeof(udpiphdr) + sizeof(dhcpMessage);
|
||
|
|
|
||
|
|
memset (&addr, 0, sizeof(struct sockaddr));
|
||
|
|
memcpy (addr.sa_data, iface->iface, strlen (iface->iface));
|
||
|
|
if (sendto (iface->sk, udp_msg_send, len, 0, &addr, sizeof(struct sockaddr)) == -1)
|
||
|
|
{
|
||
|
|
syslog (LOG_ERR,"sendto: %m\n");
|
||
|
|
free (udp_msg_send);
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
}
|
||
|
|
free (udp_msg_send);
|
||
|
|
gettimeofday (&local_begin, NULL);
|
||
|
|
err = peekfd (iface, (j + random () % 200000));
|
||
|
|
if (iface->cease || (err == RET_DHCP_CEASED))
|
||
|
|
return RET_DHCP_CEASED;
|
||
|
|
} while ( err == RET_DHCP_TIMEOUT );
|
||
|
|
|
||
|
|
do
|
||
|
|
{
|
||
|
|
struct ip ipRecv_local;
|
||
|
|
char *tmp_ip;
|
||
|
|
const struct udphdr *udp_msg_recv_hdr;
|
||
|
|
dhcpMessage *dhcp_msg_recv;
|
||
|
|
int reply_type = -1;
|
||
|
|
char foobuf[512];
|
||
|
|
int i, o;
|
||
|
|
|
||
|
|
udp_msg_recv = calloc (1, sizeof (udpipMessage));
|
||
|
|
o = sizeof (struct sockaddr);
|
||
|
|
len = recvfrom (iface->sk, udp_msg_recv, sizeof(udpipMessage), 0, (struct sockaddr *)&addr, &o);
|
||
|
|
if (len == -1)
|
||
|
|
{
|
||
|
|
syslog(LOG_ERR,"recvfrom: %m\n");
|
||
|
|
free (udp_msg_recv);
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (iface->bTokenRing)
|
||
|
|
{ /* Here we convert a TR frame into an Eth frame */
|
||
|
|
if (tr2eth (&(udp_msg_recv->ethhdr)))
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
gettimeofday (¤t, NULL);
|
||
|
|
timeval_subtract (&diff, ¤t, &local_begin);
|
||
|
|
local_timeout = j - diff.tv_sec * 1000000 - diff.tv_usec + random() % 200000;
|
||
|
|
|
||
|
|
/* Make sure waiting local_timeout seconds isn't greater than our overall time left
|
||
|
|
* on this operation, and clamp local_timeout to the overall time left if it is.
|
||
|
|
*/
|
||
|
|
if (timeval_subtract (&diff, &overall_end, ¤t))
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
return RET_DHCP_TIMEOUT;
|
||
|
|
}
|
||
|
|
if ((local_timeout*1000000) > ((diff.tv_sec * 1000000) + diff.tv_usec))
|
||
|
|
local_timeout = (diff.tv_sec * 1000000) + diff.tv_usec;
|
||
|
|
|
||
|
|
/* Ignore non-IP packets */
|
||
|
|
if ( udp_msg_recv->ethhdr.ether_type != htons(ETHERTYPE_IP) )
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
tmp_ip = udp_msg_recv->udpipmsg;
|
||
|
|
for (i = 0; i < sizeof (struct ip) - 2; i += 2)
|
||
|
|
{
|
||
|
|
if ( ( udp_msg_recv->udpipmsg[i] == 0x45 ) && ( udp_msg_recv->udpipmsg[i+1] == 0x00 ) )
|
||
|
|
{
|
||
|
|
tmp_ip = &(udp_msg_recv->udpipmsg[i]);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Use local copy because ipRecv is not aligned. */
|
||
|
|
memcpy (&ipRecv_local, ((struct udpiphdr *)tmp_ip)->ip, sizeof(struct ip));
|
||
|
|
udp_msg_recv_hdr = (struct udphdr *)((char*)(((struct udpiphdr*)tmp_ip)->ip)+sizeof(struct ip));
|
||
|
|
if ( ipRecv_local.ip_p != IPPROTO_UDP )
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( iface->bTokenRing && (udp_msg_recv_hdr->uh_dport != htons(DHCP_CLIENT_PORT)))
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
len -= sizeof(struct packed_ether_header);
|
||
|
|
i = (int)ntohs (ipRecv_local.ip_len);
|
||
|
|
if ( len < i )
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "corrupted IP packet of size=%d and ip_len=%d discarded\n", len,i);
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
len = i - (ipRecv_local.ip_hl << 2);
|
||
|
|
i = (int)ntohs(udp_msg_recv_hdr->uh_ulen);
|
||
|
|
if ( len < i )
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "corrupted UDP msg of size=%d and uh_ulen=%d discarded\n", len,i);
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( iface->client_options->do_checksum && (len = udpipchk((udpiphdr *)tmp_ip)))
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
switch ( len )
|
||
|
|
{
|
||
|
|
case -1:
|
||
|
|
syslog(LOG_DEBUG, "corrupted IP packet with ip_len=%d discarded\n", (int)ntohs(ipRecv_local.ip_len));
|
||
|
|
break;
|
||
|
|
case -2:
|
||
|
|
syslog(LOG_DEBUG, "corrupted UDP msg with uh_ulen=%d discarded\n", (int)ntohs(udp_msg_recv_hdr->uh_ulen));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
dhcp_msg_recv = (dhcpMessage *)&(tmp_ip[(ipRecv_local.ip_hl << 2) + sizeof(struct udphdr)]);
|
||
|
|
if ( dhcp_msg_recv->xid != iface->xid )
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( dhcp_msg_recv->htype != ARPHRD_ETHER && dhcp_msg_recv->htype != (char)ARPHRD_IEEE802_TR )
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog (LOG_DEBUG,"wrong msg htype 0x%X discarded\n", dhcp_msg_recv->htype);
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( dhcp_msg_recv->op != DHCP_BOOTREPLY )
|
||
|
|
{
|
||
|
|
free (udp_msg_recv);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
while ((iface->foo_sk > 0) && recvfrom (iface->foo_sk, (void *)foobuf, sizeof(foobuf), 0, NULL, NULL) != -1);
|
||
|
|
|
||
|
|
/* Copy DHCP response options from received packet into local options list */
|
||
|
|
reply_type = parseDhcpMsgRecv (udp_msg_recv, &(iface->dhcp_options));
|
||
|
|
if ( expected_reply_type == reply_type )
|
||
|
|
{
|
||
|
|
*return_msg = udp_msg_recv;
|
||
|
|
return RET_DHCP_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
free (udp_msg_recv);
|
||
|
|
udp_msg_recv = NULL;
|
||
|
|
|
||
|
|
if (reply_type == DHCP_NAK)
|
||
|
|
{
|
||
|
|
if ( iface->dhcp_options.val[dhcpMsg] )
|
||
|
|
syslog(LOG_ERR, "DHCP_NAK server response received: %s\n", (char *)iface->dhcp_options.val[dhcpMsg]);
|
||
|
|
else
|
||
|
|
syslog(LOG_ERR, "DHCP_NAK server response received\n");
|
||
|
|
return RET_DHCP_NAK;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = peekfd (iface, local_timeout);
|
||
|
|
if (iface->cease || (err == RET_DHCP_CEASED))
|
||
|
|
return RET_DHCP_CEASED;
|
||
|
|
} while ((local_timeout > 0) && (err == RET_DHCP_SUCCESS));
|
||
|
|
|
||
|
|
return RET_DHCP_TIMEOUT;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_reboot (dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
/* Client has a cached IP and wants to request it again from the server
|
||
|
|
* if possible. DHCP state INIT-REBOOT.
|
||
|
|
*
|
||
|
|
* If no response from the server is received, we assume that we can still
|
||
|
|
* use the cached IP address.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* FIXME: get the IP address to renew from somewhere */
|
||
|
|
|
||
|
|
if (!iface) return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
releaseDhcpOptions (iface);
|
||
|
|
return dhcp_request (iface, &buildDhcpReboot);
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_init (dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
udpipMessage *msg = NULL;
|
||
|
|
dhcpMessage *dhcp_msg = NULL;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
if (!iface) return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
releaseDhcpOptions (iface);
|
||
|
|
|
||
|
|
#ifdef DEBUG
|
||
|
|
fprintf(stderr,"ClassID = \"%s\"\n"
|
||
|
|
"ClientID = \"%u.%u.%u.%02X.%02X.%02X.%02X.%02X.%02X\"\n", iface->cls_id,
|
||
|
|
iface->cli_id[0], iface->cli_id[1], iface->cli_id[2],
|
||
|
|
iface->cli_id[3], iface->cli_id[4], iface->cli_id[5],
|
||
|
|
iface->cli_id[6], iface->cli_id[7], iface->cli_id[8]);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog (LOG_DEBUG,"broadcasting DHCP_DISCOVER\n");
|
||
|
|
iface->xid = random ();
|
||
|
|
err = dhcpSendAndRecv (iface, DHCP_OFFER, &buildDhcpDiscover, &msg);
|
||
|
|
if (err != RET_DHCP_SUCCESS)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
if ( iface->client_options->send_second_discover )
|
||
|
|
{
|
||
|
|
udpipMessage *msg2 = NULL;
|
||
|
|
|
||
|
|
if (DebugFlag)
|
||
|
|
syslog (LOG_DEBUG,"broadcasting second DHCP_DISCOVER\n");
|
||
|
|
|
||
|
|
iface->xid = dhcp_msg->xid;
|
||
|
|
err = dhcpSendAndRecv (iface, DHCP_OFFER, &buildDhcpDiscover, &msg2);
|
||
|
|
if (err == RET_DHCP_SUCCESS)
|
||
|
|
{
|
||
|
|
free (msg);
|
||
|
|
msg = msg2;
|
||
|
|
}
|
||
|
|
dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
}
|
||
|
|
|
||
|
|
iface->ciaddr = dhcp_msg->yiaddr;
|
||
|
|
memcpy (&(iface->siaddr), iface->dhcp_options.val[dhcpServerIdentifier], 4);
|
||
|
|
memcpy (iface->shaddr, msg->ethhdr.ether_shost, ETH_ALEN);
|
||
|
|
iface->xid = dhcp_msg->xid;
|
||
|
|
|
||
|
|
/* DHCP_OFFER received */
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG,"DHCP_OFFER received from %s (%u.%u.%u.%u)\n", dhcp_msg->sname,
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[0],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[1],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[2],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[3]);
|
||
|
|
}
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
return dhcp_request (iface, &buildDhcpRequest);
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_request(dhcp_interface *iface, dhcp_msg_build_proc buildDhcpMsg)
|
||
|
|
{
|
||
|
|
udpipMessage *msg = NULL;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
/* DHCP state REQUEST: request an address from a _particular_ DHCP server */
|
||
|
|
|
||
|
|
if (!iface) return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG,"broadcasting DHCP_REQUEST for %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
err = dhcpSendAndRecv (iface, DHCP_ACK, buildDhcpMsg, &msg);
|
||
|
|
if (err != RET_DHCP_SUCCESS)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
dhcpMessage *dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
syslog(LOG_DEBUG, "DHCP_ACK received from %s (%u.%u.%u.%u)\n", dhcp_msg->sname,
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[0],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[1],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[2],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
iface->req_sent_time = time (NULL);
|
||
|
|
|
||
|
|
#ifdef ARPCHECK
|
||
|
|
/* check if the offered IP address already in use */
|
||
|
|
if ( arpCheck(iface) )
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "requested %u.%u.%u.%u address is in use\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
dhcpDecline();
|
||
|
|
iface->ciaddr = 0;
|
||
|
|
return RET_DHCP_ADDRESS_IN_USE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG, "verified %u.%u.%u.%u address is not in use\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Successfull ACK: Use the fields obtained for future requests */
|
||
|
|
memcpy (&(iface->siaddr), iface->dhcp_options.val[dhcpServerIdentifier], 4);
|
||
|
|
memcpy (iface->shaddr, msg->ethhdr.ether_shost, ETH_ALEN);
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
return RET_DHCP_BOUND;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
#if 0
|
||
|
|
int dhcp_bound(dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
int i;
|
||
|
|
|
||
|
|
i = iface->req_sent_time + ntohl(*(unsigned int *)(iface->dhcp_options.val[dhcpT1value])) - time (NULL);
|
||
|
|
if ( i > 0 )
|
||
|
|
alarm(i);
|
||
|
|
else
|
||
|
|
return STATE_DHCP_RENEW;
|
||
|
|
sleep (ntohl(*(u_int *)(iface->dhcp_options.val[dhcpT1value])));
|
||
|
|
|
||
|
|
return STATE_DHCP_RENEW;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_renew(dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
udpipMessage *msg = NULL;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
/* DHCP state RENEW: request renewal of our lease from the original DHCP server */
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
i = iface->req_sent_time + ntohl(*(unsigned int *)(iface->dhcp_options.val[dhcpT2value])) - time(NULL);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (!iface) return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG,"sending DHCP_REQUEST for %u.%u.%u.%u to %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3],
|
||
|
|
((unsigned char *)&(iface->siaddr))[0], ((unsigned char *)&(iface->siaddr))[1],
|
||
|
|
((unsigned char *)&(iface->siaddr))[2], ((unsigned char *)&(iface->siaddr))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
iface->xid = random();
|
||
|
|
err = dhcpSendAndRecv (iface, DHCP_ACK, &buildDhcpRenew, &msg);
|
||
|
|
if (err != RET_DHCP_SUCCESS);
|
||
|
|
return err;
|
||
|
|
|
||
|
|
iface->req_sent_time = time (NULL);
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
dhcpMessage *dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
syslog(LOG_DEBUG, "DHCP_ACK received from %s (%u.%u.%u.%u)\n", dhcp_msg->sname,
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[0],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[1],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[2],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[3]);
|
||
|
|
}
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
return RET_DHCP_BOUND;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_rebind(dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
udpipMessage *msg = NULL;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
/* DHCP state REBIND: request renewal of our lease from _any_ DHCP server */
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
i = iface->req_sent_time + ntohl(*(unsigned int *)(iface->dhcp_options.val[dhcpIPaddrLeaseTime])) - time(NULL);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (!iface) return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG,"broadcasting DHCP_REQUEST for %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
iface->xid = random ();
|
||
|
|
err = dhcpSendAndRecv(iface, DHCP_ACK, &buildDhcpRebind, &msg);
|
||
|
|
if (err != RET_DHCP_SUCCESS)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
iface->req_sent_time = time (NULL);
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
dhcpMessage *dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
syslog(LOG_DEBUG, "DHCP_ACK received from %s (%u.%u.%u.%u)\n", dhcp_msg->sname,
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[0],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[1],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[2],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Successfull ACK: Use the fields obtained for future requests */
|
||
|
|
memcpy (&(iface->siaddr), iface->dhcp_options.val[dhcpServerIdentifier], 4);
|
||
|
|
memcpy (iface->shaddr, msg->ethhdr.ether_shost, ETH_ALEN);
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
return RET_DHCP_BOUND;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_release(dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
struct sockaddr addr;
|
||
|
|
udpipMessage *msg;
|
||
|
|
|
||
|
|
if ( iface->ciaddr == 0 )
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
iface->xid = random();
|
||
|
|
if (!(msg = buildDhcpRelease (iface)))
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
if (DebugFlag)
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG,"sending DHCP_RELEASE for %u.%u.%u.%u to %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3],
|
||
|
|
((unsigned char *)&(iface->siaddr))[0], ((unsigned char *)&(iface->siaddr))[1],
|
||
|
|
((unsigned char *)&(iface->siaddr))[2], ((unsigned char *)&(iface->siaddr))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
memset (&addr, 0, sizeof(struct sockaddr));
|
||
|
|
memcpy (addr.sa_data, iface->iface, strlen (iface->iface));
|
||
|
|
if ( sendto (iface->sk, msg, sizeof(struct packed_ether_header) + sizeof(udpiphdr) + sizeof(dhcpMessage), 0, &addr, sizeof(struct sockaddr)) == -1 )
|
||
|
|
syslog(LOG_ERR,"dhcpRelease: sendto: %m\n");
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
arpRelease (iface); /* clear ARP cache entries for client IP addr */
|
||
|
|
iface->ciaddr = 0;
|
||
|
|
|
||
|
|
return RET_DHCP_SUCCESS;
|
||
|
|
}
|
||
|
|
/*****************************************************************************/
|
||
|
|
#ifdef ARPCHECK
|
||
|
|
int dhcp_decline(dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
udpipMessage *msg;
|
||
|
|
struct sockaddr addr;
|
||
|
|
|
||
|
|
iface->xid = random ();
|
||
|
|
if (!(msg = buildDhcpDecline (iface)))
|
||
|
|
return RET_DHCP_ERROR;
|
||
|
|
|
||
|
|
memset (&addr, 0, sizeof(struct sockaddr));
|
||
|
|
memcpy (addr.sa_data, iface->iface, strlen (iface->iface));
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog (LOG_DEBUG,"broadcasting DHCP_DECLINE\n");
|
||
|
|
|
||
|
|
if ( sendto (iface->sk, msg, sizeof(struct packed_ether_header) + sizeof(udpiphdr)+sizeof(dhcpMessage), 0, &addr, sizeof(struct sockaddr)) == -1 )
|
||
|
|
syslog (LOG_ERR,"dhcpDecline: sendto: %m\n");
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
return RET_DHCP_SUCCESS;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
/*****************************************************************************/
|
||
|
|
int dhcp_inform(dhcp_interface *iface)
|
||
|
|
{
|
||
|
|
udpipMessage *msg = NULL;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG, "broadcasting DHCP_INFORM for %u.%u.%u.%u\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
}
|
||
|
|
|
||
|
|
iface->xid = random ();
|
||
|
|
err = dhcpSendAndRecv (iface, DHCP_ACK, buildDhcpInform, &msg);
|
||
|
|
if (err != RET_DHCP_SUCCESS)
|
||
|
|
return err;
|
||
|
|
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
dhcpMessage *dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
syslog(LOG_DEBUG, "DHCP_ACK received from %s (%u.%u.%u.%u)\n", dhcp_msg->sname,
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[0],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[1],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[2],
|
||
|
|
((unsigned char *)(iface->dhcp_options.val[dhcpServerIdentifier]))[3]);
|
||
|
|
}
|
||
|
|
free (msg);
|
||
|
|
|
||
|
|
#ifdef ARPCHECK
|
||
|
|
/* check if the offered IP address already in use */
|
||
|
|
if ( arpCheck(iface) )
|
||
|
|
{
|
||
|
|
if ( DebugFlag )
|
||
|
|
syslog(LOG_DEBUG, "requested %u.%u.%u.%u address is in use\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
dhcpDecline (iface);
|
||
|
|
return RET_DHCP_SUCCESS;
|
||
|
|
}
|
||
|
|
if ( DebugFlag )
|
||
|
|
{
|
||
|
|
syslog(LOG_DEBUG, "verified %u.%u.%u.%u address is not in use\n",
|
||
|
|
((unsigned char *)&(iface->ciaddr))[0], ((unsigned char *)&(iface->ciaddr))[1],
|
||
|
|
((unsigned char *)&(iface->ciaddr))[2], ((unsigned char *)&(iface->ciaddr))[3]);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return RET_DHCP_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
char *get_dhcp_option_name (int i)
|
||
|
|
{
|
||
|
|
char *buf = NULL;
|
||
|
|
if (i <= dhcpClientIdentifier)
|
||
|
|
buf = strdup (dhcp_opt_table [i].name);
|
||
|
|
else
|
||
|
|
buf = strdup ("unknown");
|
||
|
|
return buf;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*****************************************************************************/
|
||
|
|
void debug_dump_dhcp_options (udpipMessage *udp_msg, dhcpOptions *options)
|
||
|
|
{
|
||
|
|
int i,j;
|
||
|
|
dhcpMessage *dhcp_msg = (dhcpMessage *)&(udp_msg->udpipmsg[sizeof(udpiphdr)]);
|
||
|
|
|
||
|
|
fprintf (stderr,"parseDhcpMsgRecv: %d options received:\n", options->num);
|
||
|
|
for (i = 1; i < 255; i++)
|
||
|
|
{
|
||
|
|
if ( options->val[i] )
|
||
|
|
{
|
||
|
|
switch ( i )
|
||
|
|
{
|
||
|
|
case 1: /* subnet mask */
|
||
|
|
case 3: /* routers on subnet */
|
||
|
|
case 4: /* time servers */
|
||
|
|
case 5: /* name servers */
|
||
|
|
case 6: /* dns servers */
|
||
|
|
case 28:/* broadcast addr */
|
||
|
|
case 33:/* staticRoute */
|
||
|
|
case 41:/* NIS servers */
|
||
|
|
case 42:/* NTP servers */
|
||
|
|
case 50:/* dhcpRequestdIPaddr */
|
||
|
|
case 54:/* dhcpServerIdentifier */
|
||
|
|
for (j = 0; j < options->len[i]; j += 4)
|
||
|
|
{
|
||
|
|
char *opt_name = get_dhcp_option_name (i);
|
||
|
|
fprintf (stderr,"i=%-2d (%s) len=%-2d option = %u.%u.%u.%u\n",
|
||
|
|
i, opt_name, options->len[i],
|
||
|
|
((unsigned char *)options->val[i])[0+j],
|
||
|
|
((unsigned char *)options->val[i])[1+j],
|
||
|
|
((unsigned char *)options->val[i])[2+j],
|
||
|
|
((unsigned char *)options->val[i])[3+j]);
|
||
|
|
free (opt_name);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 2: /* time offset */
|
||
|
|
case 51:/* dhcpAddrLeaseTime */
|
||
|
|
case 57:/* dhcpMaxMsgSize */
|
||
|
|
case 58:/* dhcpT1value */
|
||
|
|
case 59:/* dhcpT2value */
|
||
|
|
{
|
||
|
|
char *opt_name = get_dhcp_option_name (i);
|
||
|
|
fprintf (stderr,"i=%-2d (%s) len=%-2d option = %d\n", i, opt_name,
|
||
|
|
options->len[i], ntohl(*(int *)options->val[i]));
|
||
|
|
free (opt_name);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 23:/* defaultIPTTL */
|
||
|
|
case 29:/* performMaskdiscovery */
|
||
|
|
case 31:/* performRouterdiscovery */
|
||
|
|
case 53:/* dhcpMessageType */
|
||
|
|
{
|
||
|
|
char *opt_name = get_dhcp_option_name (i);
|
||
|
|
fprintf(stderr,"i=%-2d (%s) len=%-2d option = %u\n", i, opt_name,
|
||
|
|
options->len[i],*(unsigned char *)options->val[i]);
|
||
|
|
free (opt_name);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
{
|
||
|
|
char *opt_name = get_dhcp_option_name (i);
|
||
|
|
fprintf(stderr,"i=%-2d (%s) len=%-2d option = \"%s\"\n",
|
||
|
|
i, opt_name, options->len[i], (char *)options->val[i]);
|
||
|
|
free (opt_name);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fprintf(stderr,"dhcp_msg->yiaddr = %u.%u.%u.%u\n"
|
||
|
|
"dhcp_msg->siaddr = %u.%u.%u.%u\n"
|
||
|
|
"dhcp_msg->giaddr = %u.%u.%u.%u\n"
|
||
|
|
"dhcp_msg->sname = \"%s\"\n"
|
||
|
|
"ServerHardwareAddr = %02X.%02X.%02X.%02X.%02X.%02X\n",
|
||
|
|
((unsigned char *)&dhcp_msg->yiaddr)[0], ((unsigned char *)&dhcp_msg->yiaddr)[1],
|
||
|
|
((unsigned char *)&dhcp_msg->yiaddr)[2], ((unsigned char *)&dhcp_msg->yiaddr)[3],
|
||
|
|
((unsigned char *)&dhcp_msg->siaddr)[0], ((unsigned char *)&dhcp_msg->siaddr)[1],
|
||
|
|
((unsigned char *)&dhcp_msg->siaddr)[2], ((unsigned char *)&dhcp_msg->siaddr)[3],
|
||
|
|
((unsigned char *)&dhcp_msg->giaddr)[0], ((unsigned char *)&dhcp_msg->giaddr)[1],
|
||
|
|
((unsigned char *)&dhcp_msg->giaddr)[2], ((unsigned char *)&dhcp_msg->giaddr)[3],
|
||
|
|
dhcp_msg->sname,
|
||
|
|
udp_msg->ethhdr.ether_shost[0], udp_msg->ethhdr.ether_shost[1],
|
||
|
|
udp_msg->ethhdr.ether_shost[2], udp_msg->ethhdr.ether_shost[3],
|
||
|
|
udp_msg->ethhdr.ether_shost[4], udp_msg->ethhdr.ether_shost[5]);
|
||
|
|
}
|
||
|
|
|