NetworkManager/dhcpcd/client.c
Dan Williams 2ad25b238e 2004-11-15 Dan Williams <dcbw@redhat.com>
Major rework of link detection code.  We now use DHCP
	as part of the link detection which proves to be much more robust,
	and also supports Open System authentication for wireless networks.

	We no longer use external DHCP client programs.  Instead, we use
	our own DHCP client, based on substantially reworked bits of 'dhcpcd'
	which was written by:
		Yoichi Hariguchi <yoichi@fore.com>
		Sergei Viznyuk <sv@phystech.com>
		http://www.phystech.com/download/
	It resides in the "dhcpcd" directory and was refactored into a general
	purpose DHCP client library by me.

	Also misc fixes (CPPFLAGS->AM_CPPFLAGS, move some stuff around),
	move src/backends/NetworkManagerSystem.h -> src/NetworkManagerSystem.h


git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@314 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2004-11-16 02:41:53 +00:00

1179 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 (&current, NULL);
if (timeval_subtract (&diff, &overall_end, &current))
{
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 (&current, NULL);
timeval_subtract (&diff, &current, &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, &current))
{
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]);
}