Files
m241Neelima/components/ip_net2-6.9/ipappl/src/ipdhcpc.c
T
2026-07-30 11:19:44 +05:30

5908 lines
186 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipdhcpc.c,v $ $Revision: 1.131.10.72 $
* $Source: /home/interpeak/CVSRoot/ipappl/src/ipdhcpc.c,v $
* $Author: jren $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Lennart Bang <lob@interpeak.se>
* and Jonas Rendel <jonas@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipdhcpc_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_clib.h>
#include <ipcom_syslog.h>
#include <ipcom_os.h>
#include <ipcom_shell.h>
#include <ipcom_sock.h>
#include <ipcom_netif.h>
#include <ipcom_err.h>
#include <ipcom_tmo.h>
#include <ipcom_sysvar.h>
#include <ipcom_ipd.h>
#include <ipcom_time.h>
#include <ipcom_inet.h>
#include <ipnet_eth.h>
#include <ipnet_routesock.h>
#include <ipcom_sysctl.h>
#include "ipdhcpc_h.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/* Syslog facility code */
#ifdef IPCOM_SYSLOG_FACILITY
#undef IPCOM_SYSLOG_FACILITY
#endif
#define IPCOM_SYSLOG_FACILITY IPCOM_LOG_IPDHCPC
/* Select level of verbosity for syslog messages if it is not defined */
#ifdef IPCOM_SYSLOG_PRIORITY
#undef IPCOM_SYSLOG_PRIORITY
#endif
#if defined(_WRS_CONFIG_IPDHCPC_SYSLOG_PRIORITY)
#define IPCOM_SYSLOG_PRIORITY _WRS_CONFIG_IPDHCPC_SYSLOG_PRIORITY
#else
#define IPCOM_SYSLOG_PRIORITY IPCOM_LOG_WARNING
#endif
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
IP_EXTERN void
ipnet_cmd_init_addrs(void *msg, Ip_u32 addrs, struct Ip_sockaddr **sas);
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC Ip_err ipdhcpc_init(void);
IP_STATIC void ipdhcpc_exit(void);
IP_STATIC void ipdhcpc_state_init(Ipdhcpc_if *ift, Ipdhcpc_handle *handle);
IP_STATIC void ipdhcpc_state_reboot(Ipdhcpc_if *ift, Ipdhcpc_handle *handle);
IP_STATIC void ipdhcpc_state_restart(Ipdhcpc_handle *handle);
IP_STATIC Ip_err ipdhcpc_wakeup(Ipdhcpc_handle *handle, Ip_err status,
int event);
IP_STATIC void ipdhcpc_output(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
struct Ip_sockaddr_in *addr);
IP_STATIC void ipdhcpc_output_request(Ipdhcpc_if *ift,
Ipdhcpc_handle *handle,
Ip_u8 msgtype);
IP_STATIC void ipdhcpc_reply_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle);
IP_STATIC void ipdhcpc_offer_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
Ip_u8 *opt_serverid);
IP_STATIC void ipdhcpc_ack_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
Ip_u8 *opt_serverid);
IP_STATIC void ipdhcpc_nak_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
Ip_u8 *opt_serverid);
IP_STATIC void ipdhcpc_bootp_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle);
IP_STATIC Ip_err ipdhcpc_ifunlease_locked(Ipdhcpc_if *ift,
Ipdhcpc_handle **handlep);
IP_STATIC Ip_u8 *ipdhcpc_koption_get(Ip_u8 *options, int options_length,
Ip_u8 code);
IP_STATIC void ipdhcpc_daemon(void);
IP_STATIC Ip_err ipdhcpc_run(void);
IP_STATIC Ip_err ipdhcpc_if_eventcb(char *ifname, Ip_u32 ifindex,
Ip_u32 ifflags, int event, void *data);
IP_STATIC void ipdhcpc_ifclose_all(void);
IP_STATIC void ipdhcpc_handle_free_pending(void);
IP_STATIC void ipdhcpc_timer_cancel(Ipdhcpc_handle *handle, int which);
IP_STATIC void ipdhcpc_timer_set(Ipdhcpc_handle *handle, Ip_u32 msec,
int which);
IP_STATIC Ip_bool ipdhcpc_if_has_addr(Ip_u32 ifindex, Ip_u32 ipaddr_n);
IP_STATIC Ip_err ipdhcpc_get_interface_flags (char *name, Ip_u32 *flags);
IP_STATIC void ipdhcpc_route_init_addrs(void *msg, Ip_u32 addrs, struct Ip_sockaddr **sas);
IP_GLOBAL Ip_err ipdhcpc_option_add(Ipdhcpc_handle *handle,
Ip_u8 opt_code,
Ip_u8 *opt_data,
Ip_u32 opt_len);
IP_PUBLIC Ip_err ipdhcpc_create(void);
IP_PUBLIC Ip_err ipdhcpc_start(void);
IP_PUBLIC const char *ipdhcpc_version(void);
IP_PUBLIC Ip_err ipdhcpc_renew_get(char *ifname);
IP_PUBLIC Ip_err ipdhcpc_inform_get(char *ifname, char *addr);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
IP_STATIC Ip_u8 chaddr_bcast[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
IP_STATIC const char *ipdhcpc_msg_name[9] =
{
"INVALID",
"DISCOVER",
"OFFER",
"REQUEST",
"DECLINE",
"ACK",
"NAK",
"RELEASE",
"INFORM"
};
IP_STATIC const char *ipdhcpc_state_name[] =
{
"INVALID",
"INIT",
"INIT_REBOOT",
"REBOOTING",
"SELECTING",
"REQUESTING",
"INFORMING",
"BOUND",
"RENEWING",
"REBINDING",
"STOPPED",
"ASSIGNING"
};
IP_GLOBAL Ipdhcpc_data ipdhcpc;
IP_STATIC const Ipdhcpc_options ipdhcpc_options[] =
{
/*
* optcode,
* minlen, maxlen,
* msgmust,
* msgmustnot,
*/
/*
* 9.1. Requested IP Address
*/
{
IPDHCPC_OPTCODE_DHCP_REQUESTED_ADDRESS,
4, 4,
IPDHCPC_MB_DECLINE,
IPDHCPC_MB_RELEASE | IPDHCPC_MB_INFORM
},
/*
* 9.2. IP Address Lease Time
*/
{
IPDHCPC_OPTCODE_DHCP_LEASE_TIME,
4, 4,
0,
IPDHCPC_MB_INFORM | IPDHCPC_MB_DECLINE | IPDHCPC_MB_RELEASE
},
/*
* 9.5. Server Identifier
*/
{
IPDHCPC_OPTCODE_DHCP_SERVER_IDENTIFIER,
4, 4,
IPDHCPC_MB_DECLINE | IPDHCPC_MB_RELEASE,
IPDHCPC_MB_DISCOVER | IPDHCPC_MB_INFORM
},
/*
* 9.8. Maximum DHCP Message Size
*/
{
IPDHCPC_OPTCODE_DHCP_MAX_MESSAGE_SIZE,
2, 2,
0,
IPDHCPC_MB_DECLINE | IPDHCPC_MB_RELEASE
},
/*
* 9.11. Class-identifier
*/
{
IPDHCPC_OPTCODE_VENDOR_CLASS_IDENTIFIER,
1, 255,
0,
IPDHCPC_MB_DECLINE | IPDHCPC_MB_RELEASE
},
{
IPDHCPC_OPTCODE_END, 1, 1,
0,
0
}
};
IP_STATIC Ip_u32 option_confvar = 0;
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcpc_remove_existing_addresses
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_remove_existing_addresses(char *ifname)
{
Ip_err retval = IPCOM_SUCCESS;
char buf[50];
struct Ip_ifreq ifreq;
struct Ip_sockaddr_in *addrp = IP_NULL;
int remove_addrs;
int name[6];
Ip_u8 *if_data = IP_NULL;
Ip_u8 *ifa_data;
Ip_size_t if_data_len;
Ip_size_t ifa_offset;
Ip_size_t ifa_len;
struct Ipnet_if_msghdr *ifm;
struct Ipnet_ifa_msghdr *ifa;
struct Ip_sockaddr_in *addrs[IPNET_RTAX_MAX];
if (ipcom_strlen (ifname) >= IP_IFNAMSIZ)
return IPCOM_ERR_FAILED;
remove_addrs = ipcom_sysvar_get_as_int("ipdhcpc.remove_existing_addresses",
IPDHCPC_REMOVE_EXISTING_ADDRESSES);
if (remove_addrs > 0)
{
ipcom_memset(&ifreq, 0, sizeof(ifreq));
ipcom_strcpy(ifreq.ifr_name, ifname);
/* Remove all unicast address on interface. */
name[0] = IP_CTL_NET;
name[1] = IP_AF_ROUTE;
name[2] = 0; /* Always 0 */
name[3] = IP_AF_INET;
name[4] = IP_NET_RT_IFLIST;
name[5] = (int)ipcom_if_nametoindex(ifname);
if (ipcom_sysctl(name, 6, IP_NULL, &if_data_len, IP_NULL, 0) < 0)
{
IPCOM_LOG2(ERR,"ipdhcpc[%s] :: ipcom_sysctl(IP_NET_RT_IFLIST) failed, errno = %d",
ifreq.ifr_name,
ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
if_data = ipcom_malloc(if_data_len);
if (if_data == IP_NULL)
{
IPCOM_LOG1(ERR,"ipdhcpc[%s] :: ipcom_malloc() failed", ifreq.ifr_name);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
if (ipcom_sysctl(name, 6, if_data, &if_data_len, IP_NULL, 0) < 0)
{
IPCOM_LOG2(ERR,
"ipdhcpc[%s] :: ipcom_sysctl(IP_NET_RT_IFLIST) failed, errno = %d",
ifreq.ifr_name,
ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
ifm = (struct Ipnet_if_msghdr *) &if_data[0];
ifa_data = (Ip_u8 *) /* (struct Ipnet_ifa_msghdr *) */ &if_data[ifm->ifm_msglen];
ifa_offset = 0;
ifa_len = if_data_len - ifm->ifm_msglen;
while(ifa_offset < ifa_len)
{
ifa = (struct Ipnet_ifa_msghdr *)&ifa_data[ifa_offset];
ipnet_cmd_init_addrs(ifa + 1, ifa->ifam_addrs, (struct Ip_sockaddr **) addrs);
ifa_offset += ifa->ifam_msglen;
if (ifa->ifam_type != IPNET_RTM_NEWADDR
|| IP_IN_CLASSD(addrs[IPNET_RTAX_IFA]->sin_addr.s_addr))
continue;
addrp = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr;
addrp->sin_addr.s_addr = addrs[IPNET_RTAX_IFA]->sin_addr.s_addr;
addrp->sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(addrp, sizeof(struct Ip_sockaddr_in));
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: removed address = %s",
ifname,
ipcom_inet_ntop(IP_AF_INET,
&addrp->sin_addr.s_addr,
buf,
sizeof(buf)));
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCDIFADDR, &ifreq) < 0)
{
IP_PANIC2();
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipcom_ioctl(IP_SIOCDIFADDR) failed, errno = %d",
ifreq.ifr_name,
ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
}
}
leave:
if(if_data != IP_NULL)
ipcom_free(if_data);
return retval;
}
/*
*===========================================================================
* ipdhcpc_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_init(void)
{
int num = 0;
Ip_u16 port;
int max_msg_size;
int defrcvbuf = 0x10000;
Ip_socklen_t getoptlen = sizeof(int);
Ip_socklen_t setoptlen = sizeof(int);
ipcom_memset(&ipdhcpc, 0, sizeof(Ipdhcpc_data));
ipdhcpc.fd = IP_INVALID_SOCKET;
ipdhcpc.ipd_fd = IP_INVALID_SOCKET;
max_msg_size = ipcom_sysvar_get_as_int("ipdhcpc.option.max_message_size",
IPDHCPC_MAX_MESSAGE_SIZE);
if (max_msg_size < IPDHCPC_MAX_MESSAGE_SIZE)
{
IPCOM_LOG1(WARNING, "ipdhcpc_init :: configured max message size (%d) too low", max_msg_size);
max_msg_size = IPDHCPC_MAX_MESSAGE_SIZE;
}
ipdhcpc.max_message_size = (Ip_u16)max_msg_size;
(void)ipcom_mutex_create(&ipdhcpc.code_mutex);
ipdhcpc.out_pkt = ipcom_malloc(ipdhcpc.max_message_size - IPDHCPC_RAW_HSIZE);
if (ipdhcpc.out_pkt == IP_NULL)
goto fail;
ipdhcpc.in_raw = ipcom_malloc(ipdhcpc.max_message_size);
if (ipdhcpc.in_raw == IP_NULL)
goto fail;
ipdhcpc.in_pkt = (Ipdhcpc_pkt *)(ipdhcpc.in_raw + IPDHCPC_RAW_HSIZE);
/* Must have a working timer starting at non-zero.*/
while (ipcom_time(0) == 0)
{
(void)ipcom_sleep(1);
num++;
ip_assert(num < 10);
}
/* Get default DHCP UDP client socket. */
ipdhcpc.fd = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0);
if (ipdhcpc.fd == IP_INVALID_SOCKET)
{
IPCOM_LOG1(ERR, "ipdhcpc_open :: ipcom_socket() failed, errno =%d", ipcom_errno);
goto fail;
}
/* Bind the socket. */
ipcom_memset(&ipdhcpc.addr, 0, sizeof(struct Ip_sockaddr_in));
IPCOM_SA_LEN_SET(&ipdhcpc.addr, sizeof(struct Ip_sockaddr_in));
ipdhcpc.addr.sin_family = IP_AF_INET;
port = (Ip_u16)ipcom_sysvar_get_as_int("ipdhcpc.client_port", IPDHCPC_CLIENT_PORT);
ipdhcpc.addr.sin_port = ip_htons(port);
ipdhcpc.addr.sin_addr.s_addr = ip_htonl(IP_INADDR_ANY);
if (ipcom_bind(ipdhcpc.fd, (struct Ip_sockaddr *)&ipdhcpc.addr, sizeof(ipdhcpc.addr)) == IP_SOCKERR)
{
/* Failed to bind socket */
IPCOM_LOG1(ERR, "ipdhcpc_open :: ipcom_bind() failed, errno =%d", ipcom_errno);
goto fail;
}
(void)ipcom_getsockopt(ipdhcpc.fd, IP_SOL_SOCKET, IP_SO_RCVBUF, &defrcvbuf, &getoptlen);
if(IPDHCPC_SOCK_MIN_RECV_BUFSIZE > defrcvbuf)
{
defrcvbuf = IPDHCPC_SOCK_MIN_RECV_BUFSIZE;
(void)ipcom_setsockopt(ipdhcpc.fd, IP_SOL_SOCKET, IP_SO_RCVBUF, &defrcvbuf, setoptlen);
defrcvbuf = 0;
}
/* Get routing socket */
#ifndef IPLITE
ipdhcpc.rt_fd = ipcom_socket(IP_AF_ROUTE, IP_SOCK_RAW, 0);
if (ipdhcpc.rt_fd == IP_INVALID_SOCKET)
{
IPCOM_LOG1(ERR, "ipdhcpc_open :: ipcom_socket(routing) failed, errno =%d", ipcom_errno);
goto fail;
}
{
int i = 1;
if (ipcom_socketioctl(ipdhcpc.rt_fd, IP_FIONBIO, (char *)&i) != 0)
{
IPCOM_LOG1(WARNING, "ipdhcpc_open ::Failed to set routing socket to non blocking, errno = %d",
ipcom_errno);
}
}
(void)ipcom_getsockopt(ipdhcpc.rt_fd, IP_SOL_SOCKET, IP_SO_RCVBUF, &defrcvbuf, &getoptlen);
if(IPDHCPC_SOCK_MIN_RECV_BUFSIZE > defrcvbuf)
{
defrcvbuf = IPDHCPC_SOCK_MIN_RECV_BUFSIZE;
(void)ipcom_setsockopt(ipdhcpc.rt_fd, IP_SOL_SOCKET, IP_SO_RCVBUF, &defrcvbuf, setoptlen);
defrcvbuf = 0;
}
#endif
ipdhcpc.pkt_fd = IP_INVALID_SOCKET;
/* Success. */
IPCOM_LOG0(DEBUG2, "ipdhcpc_open() :: opened");
ipdhcpc.opened = IP_TRUE;
return IPCOM_SUCCESS;
fail:
return IPCOM_ERR_FAILED;
}
/*
*===========================================================================
* ipdhcpc_exit
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_exit(void)
{
IPDHCPC_LOCK();
IPCOM_LOG0(DEBUG2, "ipdhcpc_exit() :: exiting");
ipdhcpc.opened = IP_FALSE;
if (ipdhcpc.fd != IP_INVALID_SOCKET)
{
/* Close Routing socket socket */
#ifndef IPLITE
(void)ipcom_socketclose(ipdhcpc.rt_fd);
#endif
ipdhcpc.rt_fd = IP_INVALID_SOCKET;
/* Close DHCP socket */
(void)ipcom_socketclose(ipdhcpc.fd);
ipdhcpc.fd = IP_INVALID_SOCKET;
}
if (ipdhcpc.pkt_fd != IP_INVALID_SOCKET)
{
(void)ipcom_socketclose(ipdhcpc.pkt_fd);
ipdhcpc.pkt_fd = IP_INVALID_SOCKET;
}
/* Free memory */
if (ipdhcpc.out_pkt != IP_NULL)
ipcom_free(ipdhcpc.out_pkt);
/* same memory with in_pkt */
if (ipdhcpc.in_raw != IP_NULL)
ipcom_free(ipdhcpc.in_raw);
IPDHCPC_UNLOCK();
(void)ipcom_mutex_delete(&ipdhcpc.code_mutex);
#ifdef IPDHCPC_INCLUDE_CMD
(void)ipcom_shell_remove_cmd("dhcpc",(Ipcom_shell_cmd_type) ipdhcpc_cmd_dhcpc);
#endif
}
/*
*===========================================================================
* ipdhcpc_number
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_u32
ipdhcpc_number(int from, int to)
{
Ip_u32 val;
ip_assert(from < to);
val = (Ip_u32)ipcom_random();
val %= (Ip_u32)(to - from + 1);
val += (Ip_u32)from;
return val;
}
/*
*===========================================================================
* ipdhcpc_wakeup
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_wakeup(Ipdhcpc_handle *handle, Ip_err status, int event)
{
Ip_err ret = 0;
handle->info.status = status;
/* Wakeup blocking request and call the callback. */
if (handle->reply_cb != IP_NULL)
{
IPDHCPC_UNLOCK();
ret = handle->reply_cb(handle, event);
IPDHCPC_LOCK();
}
return ret;
}
/*
*===========================================================================
* ipdhcpc_print_packet
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_print_packet(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
Ipdhcpc_pkt *pkt, Ip_bool input, Ip_ssize_t length)
{
#ifdef IP_DEBUG
static char buf[2000];
Ip_u8 *option;
int i, len, optlen, tlen;
Ip_u8 msgtype = 0;
char addr1[16], addr2[16];
int bootp_mode;
int priority;
optlen = (int) length - ip_offsetof(Ipdhcpc_pkt, options);
option = ipdhcpc_koption_get(&pkt->options[4] /*lint -e416 */, optlen,
IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE);
if (option != IP_NULL && option[1] == 1 && option[2] < 9)
msgtype = option[2];
bootp_mode = ipcom_sysvar_get_as_int("ipdhcpc.bootp_mode", IPDHCPC_BOOTP_MODE);
len = ipcom_sprintf(buf, IP_LF "*******************" IP_LF);
if (!bootp_mode)
{
len += ipcom_sprintf(buf + len,
"ipdhcpc[%s] :: %s in %s state (len %d)" IP_LF,
ift->ifname,
input ? "Receiving DHCP Reply" :
"Sending DHCP Request",
ipdhcpc_state_name[handle->priv->state],
length);
len += ipcom_sprintf(buf + len,
"[ DHCP %s ] ",
ipdhcpc_msg_name[msgtype]);
}
else
{
len += ipcom_sprintf(buf + len,
"ipdhcpc[%s] :: %s" IP_LF,
ift->ifname,
input ? "Receiving BOOTP Reply" :
"Sending BOOTP Request",
ipdhcpc_state_name[handle->priv->state]);
}
len += ipcom_sprintf(buf + len,
"[ xid: 0x%lx ] [ secs: %d ] [ flags: %d ]" IP_LF,
pkt->xid,
(int) ip_ntohs(pkt->secs),
(int) ip_ntohs(pkt->flags));
len += ipcom_sprintf(buf + len,
"[ ciaddr: %s ] [ yiaddr: %s ]" IP_LF,
ipcom_inet_ntop(IP_AF_INET, &pkt->ciaddr,
addr1,
sizeof(addr1)),
ipcom_inet_ntop(IP_AF_INET, &pkt->yiaddr,
addr2,
sizeof(addr2)));
len += ipcom_sprintf(buf + len,
"[ siaddr: %s ] [ giaddr: %s ]" IP_LF,
ipcom_inet_ntop(IP_AF_INET, &pkt->siaddr,
addr1,
sizeof(addr1)),
ipcom_inet_ntop(IP_AF_INET, &pkt->giaddr,
addr2,
sizeof(addr2)));
if (ipcom_strlen(pkt->sname) > 0)
len += ipcom_sprintf(buf + len,
"[ sname: %s ]" IP_LF, &pkt->sname);
if (ipcom_strlen(pkt->file) > 0)
len += ipcom_sprintf(buf + len,
"[ file: %s ]" IP_LF, &pkt->file);
len += ipcom_sprintf(buf + len, "[ Options (len %d): ]" IP_LF, optlen);
option = pkt->options;
for (i = 4; i < optlen; i++)
{
if (option[i] == IPDHCPC_OPTCODE_PAD)
continue;
if (option[i] == IPDHCPC_OPTCODE_END)
break;
if (i + 1 + option[i + 1] >= optlen)
{
len += ipcom_sprintf(buf + len,
" %2d: length = %d, truncated[%d/%d]"
IP_LF, option[i], option[i + 1],
i + 1 + option[i + 1], optlen);
break;
}
tlen = ipdhcpc_option_sprintf(&option[i],
buf + len,
sizeof(buf) - len);
if (tlen < 0)
break;
len += tlen;
i += 1 + option[i + 1];
continue;
}
ipcom_sprintf(buf + len, "*******************" IP_LF);
priority = ipcom_getlogmask_on(IPCOM_LOG_IPDHCPC);
if (priority >= IPCOM_LOG_DEBUG)
ipcom_printf("%s", buf);
#else
(void)ift;
(void)handle;
(void)pkt;
(void)input;
(void)length;
(void)ipdhcpc_msg_name;
(void)ipdhcpc_state_name;
#endif
}
/*
*===========================================================================
* ipdhcpc_sec_to_msec
*===========================================================================
* Description: Converts seconds to milliseconds and check for overflow.
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_u32
ipdhcpc_sec_to_msec(Ip_u32 seconds)
{
if (seconds > (Ip_u32) (0xffffffff / 1000))
return 0xffffffff;
return seconds * 1000;
}
/*
*===========================================================================
* ipdhcpc_tmo_cb
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_s32
ipdhcpc_tmo_cb(struct Ipcom_tmo_struct *tmo, void *cookie)
{
Ipdhcpc_handle *handle = cookie;
Ipdhcpc_if *ift;
IPDHCPC_LOCK();
/* Check for timeouts that were ready to run when cancelled */
if (tmo == &handle->priv->tmo && handle->priv->tmo_ignore == 1)
{
handle->priv->tmo_ignore = 0;
goto leave;
}
if (tmo == &handle->priv->tmo_t2 && handle->priv->tmo2_ignore == 1)
{
handle->priv->tmo2_ignore = 0;
goto leave;
}
/* Get interface. */
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == (Ip_u32)handle->ifindex)
break;
/* Interface gone, release the timeout. */
if (ift == IP_NULL)
{
(void)ipdhcpc_wakeup(handle, IPCOM_ERR_INVALID_INTERFACE,
IPDHCPC_REPLY_CB_EVENT_FAIL_IFLOST);
goto leave;
}
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: DHCP timeout in %s state",
ift->ifname,
ipdhcpc_state_name[handle->priv->state]);
/* Act on the state.*/
switch(handle->priv->state)
{
case IPDHCPC_STATE_REBOOTING:
if (++handle->priv->retrans_counter > 4)
{
ip_assert(tmo == &handle->priv->tmo);
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo_t2));
ipdhcpc_state_restart(handle);
}
else
{
/*
* Timeout waiting for DHCP request packet. Resend the REQUEST
* packet.
*/
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
}
break;
case IPDHCPC_STATE_INIT:
ip_assert(tmo == &handle->priv->tmo);
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo_t2));
ipdhcpc_state_init(ift, handle);
break;
case IPDHCPC_STATE_INIT_REBOOT:
ip_assert(tmo == &handle->priv->tmo);
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo_t2));
ipdhcpc_state_reboot(ift, handle);
break;
case IPDHCPC_STATE_SELECTING:
{
int num_retries;
ip_assert(tmo == &handle->priv->tmo);
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo_t2));
num_retries = ipcom_sysvar_get_as_int("ipdhcpc.discover_retries",
IPDHCPC_DISCOVER_RETRIES);
/* Increment to double the timeout for retransmission. */
/* retrans_counter = 4 means 64 seconds timeout, which will be repeated. */
if (++handle->priv->retrans_counter > num_retries)
{
(void)ipdhcpc_wakeup(handle, IPCOM_ERR_TIMEOUT, IPDHCPC_REPLY_CB_EVENT_FAIL_RETRY);
}
else
{
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_DISCOVER);
}
}
break;
case IPDHCPC_STATE_REBINDING:
ip_assert(ift->handle == handle);
if (tmo == &handle->priv->tmo_t2)
{
/* Lease expired -> restart. */
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Lease expired, restarting", ift->ifname);
if (handle->reply_cb != IP_NULL)
(void)handle->reply_cb(handle, IPDHCPC_REPLY_CB_EVENT_EXPIRE);
ipdhcpc_timer_cancel(ift->handle, 0);
(void)ipdhcpc_ifunlease_locked(ift, IP_NULL);
ift->handle = handle;
ipdhcpc_state_restart(handle);
break;
}
#if 0
goto send_request;
#else
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
break;
#endif
case IPDHCPC_STATE_RENEWING:
ip_assert(ift->handle == handle);
if (tmo == &handle->priv->tmo_t2)
{
/* T2 expired -> try any DHCP server in rebinding state. */
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Entering REBINDING state", ift->ifname);
handle->priv->state = IPDHCPC_STATE_REBINDING;
ipdhcpc_timer_set(handle, ipdhcpc_sec_to_msec((Ip_u32)(ift->lease.tlease - ift->lease.t2)), 2);
handle->priv->retrans_counter = 0;
ipdhcpc_timer_cancel(handle, 1);
handle->priv->next = ipdhcpc.handle_head;
ipdhcpc.handle_head = handle;
}
#if 0
goto send_request;
#else
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
break;
#endif
case IPDHCPC_STATE_REQUESTING:
#if 0
send_request:
#endif
if (++handle->priv->retrans_counter > 4)
{
/* Never received reply from selected server. Reboot state machine. */
ipdhcpc_timer_cancel(handle, 2);
handle->priv->state = IPDHCPC_STATE_INIT;
ipdhcpc_state_init(ift, handle);
}
else
{
/*
* Timeout waiting for DHCP request packet. Resend the REQUEST
* packet.
*/
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
}
break;
case IPDHCPC_STATE_ASSIGNING:
/* Timeout waiting for address to be assigned to interface.
* Could not be assigned. Possibly due to address conflict */
/* Send a decline to the server */
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_DECLINE);
/* Restart */
ipdhcpc_state_restart(handle);
break;
case IPDHCPC_STATE_BOUND:
ip_assert(tmo == &handle->priv->tmo);
ip_assert(ift->handle == handle);
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo_t2));
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Entering RENEWING state", ift->ifname);
handle->priv->tmo2_ignore = 0;
handle->priv->state = IPDHCPC_STATE_RENEWING;
ipdhcpc_timer_set(handle, ipdhcpc_sec_to_msec((Ip_u32)(ift->lease.t2 - ift->lease.t1)), 2);
handle->priv->retrans_counter = 0;
/*clear global flag of the option when the option set on this interface*/
if((handle->option_confvar & IPDHCPC_OPTION_NTPSERVER_CONF) != IP_FALSE)
{
option_confvar = option_confvar & (0xFF ^ IPDHCPC_OPTION_NTPSERVER_CONF);
}
if((handle->option_confvar & IPDHCPC_OPTION_DNSSERVER_CONF) != IP_FALSE)
{
option_confvar = option_confvar & (0xFF ^ IPDHCPC_OPTION_DNSSERVER_CONF);
}
if((handle->option_confvar & IPDHCPC_OPTION_DOMAIN_CONF) != IP_FALSE)
{
option_confvar = option_confvar & (0xFF ^ IPDHCPC_OPTION_DOMAIN_CONF);
}
handle->option_confvar = 0;
ift->handle->option_confvar = 0;
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
break;
case IPDHCPC_STATE_INFORMING:
/*Time out*/
if (++handle->priv->retrans_counter > 4)
{
/*try four times if no response from server*/
ipdhcpc_timer_cancel (handle, 0);
(void)ipdhcpc_wakeup (handle, IPCOM_ERR_TIMEOUT, IPDHCPC_REPLY_CB_EVENT_FAIL_RETRY);
}
else
ipdhcpc_output_request (ift, handle, IPDHCPC_MSG_INFORM);
break;
default:
IP_PANIC();
break;
}
leave:
IPDHCPC_UNLOCK();
return 0;
}
/*
*===========================================================================
* ipdhcpc_state_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_state_init(Ipdhcpc_if *ift, Ipdhcpc_handle *handle)
{
ip_assert(handle->priv->state <= IPDHCPC_STATE_INIT_REBOOT);
ip_assert(ift->handle == handle);
/* !!if not remember active lease, go to reboot. */
/* !!clean data on socket */
/* Init new request data. */
handle->priv->state = IPDHCPC_STATE_SELECTING;
handle->priv->xid_n =(Ip_u32) ipcom_random();
handle->priv->serverid_n = 0;
handle->priv->yiaddr = 0;
handle->priv->retrans_counter = 0;
/* Get default DHCP PACKET client socket, must temporary open. */
if (IP_INVALID_SOCKET == ipdhcpc.pkt_fd)
ipdhcpc.pkt_fd = ipcom_socket(IP_AF_PACKET, IP_SOCK_DGRAM, ip_htons(IPNET_ETH_P_IP));
if (IP_INVALID_SOCKET == ipdhcpc.pkt_fd)
{
IPCOM_LOG1(ERR, "ipdhcpc_state_init: ipcom_socket(IP_AF_PACKET) failed, errno =%d", ipcom_errno);
}
/* Send the discover message. */
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_DISCOVER);
}
/*
*===========================================================================
* ipdhcpc_state_reboot
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_state_reboot(Ipdhcpc_if *ift, Ipdhcpc_handle *handle)
{
ip_assert(handle->priv->state <= IPDHCPC_STATE_INIT_REBOOT);
ip_assert(ift->handle == handle);
/* If DHCP rebooting, must temporary open pkt_fd to get default DHCP PACKET client socket. */
if (IP_INVALID_SOCKET == ipdhcpc.pkt_fd)
ipdhcpc.pkt_fd = ipcom_socket(IP_AF_PACKET, IP_SOCK_DGRAM, ip_htons(IPNET_ETH_P_IP));
handle->priv->state = IPDHCPC_STATE_REBOOTING;
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
}
/*
*===========================================================================
* ipdhcpc_state_restart
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_state_restart(Ipdhcpc_handle *handle)
{
char ifname[IP_IFNAMSIZ + 1];
int rfc_init_delay;
Ip_u32 msec;
ipdhcpc_timer_cancel(handle, 0);
rfc_init_delay = ipcom_sysvar_get_as_int("ipdhcpc.rfc2131_init_delay",
IPDHCPC_RFC2131_INIT_DELAY);
if (rfc_init_delay)
msec = ipdhcpc_number(9000, 11000);
else
msec = 0;
(void)ipcom_if_indextoname(handle->ifindex, ifname);
IPCOM_LOG2(INFO,
"ipdhcpc[%s] :: restarting after %d milliseconds",
ifname, msec);
handle->priv->state = IPDHCPC_STATE_INIT;
ipdhcpc_timer_set(handle, msec, 1);
}
IP_STATIC void
ipdhcpc_state_init_reboot(Ipdhcpc_handle *handle)
{
char ifname[IP_IFNAMSIZ + 1];
int rfc_init_delay;
Ip_u32 msec;
ipdhcpc_timer_cancel(handle, 0);
rfc_init_delay = ipcom_sysvar_get_as_int("ipdhcpc.rfc2131_init_delay",
IPDHCPC_RFC2131_INIT_DELAY);
if (rfc_init_delay)
msec = ipdhcpc_number(9000, 11000);
else
msec = 0;
(void)ipcom_if_indextoname(handle->ifindex, ifname);
IPCOM_LOG2(INFO,
"ipdhcpc[%s] :: restarting after %d milliseconds",
ifname, msec);
handle->priv->state = IPDHCPC_STATE_INIT_REBOOT;
ipdhcpc_timer_set(handle, msec, 1);
}
/*
*===========================================================================
* ipdhcpc_output_request
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_output_request(Ipdhcpc_if *ift, Ipdhcpc_handle *handle, Ip_u8 msgtype)
{
struct Ip_sockaddr_in addr;
Ip_u32 msec;
Ip_u32 offer_timout;
int rfc_backoff_delay;
ipcom_memset(&addr, 0, sizeof(struct Ip_sockaddr_in));
IPCOM_SA_LEN_SET(&addr, sizeof(struct Ip_sockaddr_in));
addr.sin_family = IP_AF_INET;
addr.sin_port = handle->serverport_n;
if (handle->priv->state == IPDHCPC_STATE_INFORMING)
{
addr.sin_addr.s_addr = IP_INADDR_BROADCAST;
if (handle->priv->retrans_counter == 0)
{
if (handle->serveraddr_n != IP_INADDR_BROADCAST)
addr.sin_addr.s_addr = handle->serveraddr_n;
else
{
if (handle->priv->serverid_n != 0)
addr.sin_addr.s_addr = handle->priv->serverid_n;
}
}
}
else if (handle->serveraddr_n != IP_INADDR_BROADCAST)
addr.sin_addr.s_addr = handle->serveraddr_n;
else if ((handle->priv->state == IPDHCPC_STATE_RENEWING) || (msgtype == IPDHCPC_MSG_RELEASE))
{
ip_assert(handle->priv->serverid_n != 0);
addr.sin_addr.s_addr = handle->priv->serverid_n;
}
else
addr.sin_addr.s_addr = handle->serveraddr_n;
/* If broadcast, adjust outgoing interface. */
if (ipdhcpc.last_brd_ifindex != ift->ifindex)
{
struct Ip_ip_mreqn mreqn_data;
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: setting broadcast interface to %d", ift->ifname, ift->ifindex);
mreqn_data.imr_ifindex = ift->ifindex;
/* Sent outgoing multicast interface for IPv4 */
if (ipcom_setsockopt(ipdhcpc.fd, IP_IPPROTO_IP, IP_IP_MULTICAST_IF, &mreqn_data, sizeof(mreqn_data)) == 0)
ipdhcpc.last_brd_ifindex = ift->ifindex;
}
/* Send the discover message. */
handle->priv->msgtype = msgtype;
ipdhcpc_output(ift, handle, &addr);
if( IPDHCPC_MSG_DECLINE == msgtype)
{
handle->clientaddr_n = 0;
}
if ((IPDHCPC_STATE_RENEWING == handle->priv->state)
|| (IPDHCPC_STATE_REBINDING == handle->priv->state))
{
/* renewing or rebinding state - RFC 2131 4.4.5 */
msec = IP_MAX(60000, ipcom_tmo_get(&handle->priv->tmo_t2)/2);
}
else
{
/* Request a new timeout */
rfc_backoff_delay = ipcom_sysvar_get_as_int("ipdhcpc.rfc2131_exponential_backoff",
IPDHCPC_RFC2131_EXP_BACKOFF_DELAY);
offer_timout = (Ip_u32)ipcom_sysvar_get_as_int("ipdhcpc.offer_timeout",
IPDHCPC_OFFER_TIMEOUT);
if (rfc_backoff_delay)
/* Exponential backoff (4,8,16,32 and 64 seconds) */
msec = IP_MIN(64000, offer_timout * (2 << handle->priv->retrans_counter));
else
/* Exponential back-off disabled */
msec = offer_timout;
}
/* Use T1 for retransmission timer */
msec = IP_MAX(msec, 200);
ipdhcpc_timer_cancel(handle, 1);
ipdhcpc_timer_set(handle, ipdhcpc_number((int)msec - 100, (int)msec + 100), 1);
}
/*
*===========================================================================
* ipdhcpc_pkt_out
*===========================================================================
* Description: always send BROADCAST packet.
* Parameters:
* Returns: NA
*
*/
IP_STATIC void
ipdhcpc_pkt_out(Ipdhcpc_if *ift,
Ipdhcpc_handle *handle,
Ip_ssize_t length)
{
Ip_ssize_t wrote = 0;
Ip_size_t pkt_len = 0;
Ip_u8 *pkt_buf = IP_NULL;
Ipcom_udp_header *udph = IP_NULL;
Ipcom_ip_header *iph = IP_NULL;
struct Ip_sockaddr_ll dest;
Ip_fd pkt_fd = IP_INVALID_SOCKET;
ip_assert(handle->priv->msgtype != IPDHCPC_MSG_INVALID);
/* Get default DHCP PACKET client socket, much better for temporary open. */
pkt_fd = ipcom_socket(IP_AF_PACKET, IP_SOCK_DGRAM, ip_htons(IPNET_ETH_P_IP));
if (IP_INVALID_SOCKET == pkt_fd)
{
IPCOM_LOG1(ERR, "ipdhcpc :: ipcom_socket(IP_AF_PACKET) failed, errno =%d", ipcom_errno);
return;
}
/* 1,already align, no need to roundup(if lenght OK)
* 2,if needed, alloc this mem when INIT */
pkt_len = sizeof(Ipcom_ip_header) + sizeof(Ipcom_udp_header) + length;
pkt_buf = ipcom_malloc(pkt_len);
if (pkt_buf == IP_NULL)
{
(void)ipcom_socketclose(pkt_fd);
IPCOM_LOG0(ERR, "ipdhcpc :: no MEM for sending packet.");
return;
}
ipcom_memset((void *)pkt_buf, 0, pkt_len);
iph = (Ipcom_ip_header *)pkt_buf;
udph = (Ipcom_udp_header *)(pkt_buf + sizeof(Ipcom_ip_header));
ipcom_memcpy((void *)((Ip_u8 *)udph + sizeof(Ipcom_udp_header)),
(void *)ipdhcpc.out_pkt,
length);
/* Setup destination ll address */
ipcom_memset((void *)&dest, 0, sizeof(dest));
dest.sll_family = IP_AF_PACKET;
dest.sll_protocol = ip_htons(IPNET_ETH_P_IP);
dest.sll_ifindex = ift->ifindex;
dest.sll_halen = IPNET_ETH_ADDR_SIZE;
ipcom_memcpy((void *)dest.sll_addr, chaddr_bcast, IPNET_ETH_ADDR_SIZE);
/* Init headers */
iph->protocol = IP_IPPROTO_UDP;
iph->src.addr32 = ipdhcpc.out_pkt->ciaddr;
iph->dst.addr32 = IP_INADDR_BROADCAST;
udph->src_port = ip_htons(IPDHCPC_CLIENT_PORT);
udph->dst_port = ip_htons(IPDHCPC_SERVER_PORT);
udph->length = (Ip_u16)(ip_htons(sizeof(Ipcom_udp_header) + length));
iph->length = udph->length;
/* Calculate UDP checksum (pseudo header+header+payload) */
udph->checksum = ipcom_in_checksum((Ip_u16 *)pkt_buf, pkt_len);
iph->length = (Ip_u16)(ip_htons(pkt_len));
iph->vers_len = 0x40;
iph->vers_len |= sizeof(Ipcom_ip_header) / sizeof(Ip_u32);
iph->ttl = (Ip_u8)ipcom_sysvar_get_as_int("ipdhcpc.ttl", 64);
iph->checksum = ipcom_in_checksum((Ip_u16 *)iph, sizeof(Ipcom_ip_header));
wrote = ipcom_sendto(pkt_fd,
pkt_buf,
pkt_len,
0,
(struct Ip_sockaddr*)&dest,
sizeof(struct Ip_sockaddr_ll));
if ((Ip_ssize_t)pkt_len != wrote)
{
IPCOM_LOG3(WARNING, "ipdhcpc[%s] :: sendto(%d) failed, errno = %d",
ift->ifname,
length, wrote == -1 ? ipcom_errno : wrote);
}
else
{
ipdhcpc_print_packet(ift, handle, ipdhcpc.out_pkt, IP_FALSE,
length);
}
(void)ipcom_socketclose(pkt_fd);
ipcom_free(pkt_buf);
return;
}
/*
*===========================================================================
* ipdhcpc_output
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_output(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
struct Ip_sockaddr_in *addr)
{
Ip_ssize_t wrote;
Ip_ssize_t length;
int n, i;
int len;
Ip_time_t now;
Ip_u16 secs;
int bootp_mode;
Ip_u8 *option = IP_NULL;
ip_assert(handle->priv->msgtype != IPDHCPC_MSG_INVALID);
ipcom_memset(ipdhcpc.out_pkt, 0, ipdhcpc.max_message_size - IPDHCPC_RAW_HSIZE);
ipdhcpc.out_pkt->op = IPDHCPC_OP_BOOTREQUEST;
ipdhcpc.out_pkt->htype = IPDHCPC_HTYPE_ETHERNET;
ipdhcpc.out_pkt->hlen = ift->hlen;
ipdhcpc.out_pkt->xid = handle->priv->xid_n;
if (ipcom_sysvar_get_as_int("ipdhcpc.flags.bit.broadcast",IPDHCPC_FLAGS_BIT_BROADCAST))
IP_BIT_SET (ipdhcpc.out_pkt->flags, IPDHCPC_FLAGS_BROADCAST);
ipdhcpc.out_pkt->flags = ip_htons(ipdhcpc.out_pkt->flags);
if (handle->priv->retrans_counter == 0)
{
ift->time_addr_req = ipcom_time(0);
secs = 0;
}
else
{
now = ipcom_time(0);
if (now - ift->time_addr_req > 65535)
secs = 0xffff;
else if (now < ift->time_addr_req)
{
/* Old clock, reset time. */
secs = 0;
ift->time_addr_req = ipcom_time(0);
}
else
secs =(Ip_u16)(now - ift->time_addr_req);
}
ipdhcpc.out_pkt->secs = ip_htons(secs);
/*
* Only specify IP address if we own it. See RFC1542:p11 Discussion,
* interpretation #1.
*/
if (handle->priv->msgtype == IPDHCPC_MSG_INFORM)
{
ip_assert(handle->clientaddr_n != 0);
ipdhcpc.out_pkt->ciaddr = handle->clientaddr_n;
}
else if (handle->priv->msgtype == IPDHCPC_MSG_RELEASE ||
(handle->priv->msgtype == IPDHCPC_MSG_REQUEST &&
(handle->priv->state == IPDHCPC_STATE_BOUND ||
handle->priv->state == IPDHCPC_STATE_RENEWING ||
handle->priv->state == IPDHCPC_STATE_REBINDING)))
{
ip_assert(handle->priv->yiaddr != 0);
ipdhcpc.out_pkt->ciaddr = handle->priv->yiaddr;
}
ipcom_memcpy(ipdhcpc.out_pkt->chaddr, ift->chaddr, ift->hlen);
ipcom_memcpy(ipdhcpc.out_pkt->file,handle->file,128);
ipcom_memcpy(ipdhcpc.out_pkt->sname,handle->sname,64);
/* Add option cookie. */
/*lint -e416 */
ipdhcpc.out_pkt->options[0] =(Ip_u8)((handle->magic_cookie >> 24) & 0xff);
/*lint -e415 */
ipdhcpc.out_pkt->options[1] =(Ip_u8)((handle->magic_cookie >> 16) & 0xff);
/*lint -e415 */
ipdhcpc.out_pkt->options[2] =(Ip_u8)((handle->magic_cookie >> 8) & 0xff);
/*lint -e415 */
ipdhcpc.out_pkt->options[3] =(Ip_u8)((handle->magic_cookie >> 0) & 0xff);
i = 4;
bootp_mode = ipcom_sysvar_get_as_int("ipdhcpc.bootp_mode",IPDHCPC_BOOTP_MODE);
if (!bootp_mode)
{
/* Add mandatory DHCP Message Type option. */
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE;
ipdhcpc.out_pkt->options[i++] = 1;
ipdhcpc.out_pkt->options[i++] = handle->priv->msgtype;
/* Add client-identifier option. */
if( ift->clientid_len != 0 )
{
len = ift->clientid_len;
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER;
ipdhcpc.out_pkt->options[i++] =(Ip_u8) (1 + len);
ipdhcpc.out_pkt->options[i++] = 0; /*!! HW type: none */
ipcom_memcpy(&ipdhcpc.out_pkt->options[i], ift->clientid, len);
i += len;
}
else
{
len = ift->hlen;
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER;
ipdhcpc.out_pkt->options[i++] =(Ip_u8)(1 + len);
ipdhcpc.out_pkt->options[i++] = 1; /*!! HW type: Ethernet */
ipcom_memcpy(&ipdhcpc.out_pkt->options[i], ift->chaddr, len);
i += len;
}
/* Optionally add server-identifier option. */
if (handle->priv->msgtype == IPDHCPC_MSG_DECLINE ||
handle->priv->msgtype == IPDHCPC_MSG_RELEASE ||
(handle->priv->msgtype == IPDHCPC_MSG_REQUEST &&
handle->priv->state == IPDHCPC_STATE_REQUESTING))
{
ip_assert(handle->priv->serverid_n != 0);
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_DHCP_SERVER_IDENTIFIER;
ipdhcpc.out_pkt->options[i++] = 4;
ipcom_memcpy(&ipdhcpc.out_pkt->options[i],
&handle->priv->serverid_n,
4);
i += 4;
}
/* Optionally add Requested IP address option.*/
if (handle->clientaddr_n != 0 &&
(handle->priv->msgtype == IPDHCPC_MSG_DISCOVER ||
(handle->priv->msgtype == IPDHCPC_MSG_REQUEST &&
handle->priv->state == IPDHCPC_STATE_REBOOTING)))
{
ipdhcpc.out_pkt->options[i++] =
IPDHCPC_OPTCODE_DHCP_REQUESTED_ADDRESS;
ipdhcpc.out_pkt->options[i++] = 4;
ipcom_memcpy(&ipdhcpc.out_pkt->options[i],
&handle->clientaddr_n,
4);
i += 4;
}
else if (handle->priv->msgtype == IPDHCPC_MSG_DECLINE ||
(handle->priv->msgtype == IPDHCPC_MSG_REQUEST &&
handle->priv->state == IPDHCPC_STATE_REQUESTING))
{
ip_assert(handle->priv->yiaddr != 0);
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_DHCP_REQUESTED_ADDRESS;
ipdhcpc.out_pkt->options[i++] = 4;
ipcom_memcpy(&ipdhcpc.out_pkt->options[i],
&handle->priv->yiaddr, 4);
i += 4;
}
/* Add Maximum message size. */
if (handle->priv->msgtype == IPDHCPC_MSG_DISCOVER ||
handle->priv->msgtype == IPDHCPC_MSG_REQUEST ||
handle->priv->msgtype == IPDHCPC_MSG_INFORM)
{
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_DHCP_MAX_MESSAGE_SIZE;
ipdhcpc.out_pkt->options[i++] = 2;
ipdhcpc.out_pkt->options[i++] =(Ip_u8)((ipdhcpc.max_message_size >> 8) & 0xff);
ipdhcpc.out_pkt->options[i++] =(Ip_u8)((ipdhcpc.max_message_size >> 0) & 0xff);
}
}
/* Copy over valid user options. */
for (n = 0; n < handle->optlen; n++)
{
if (handle->options[n] == IPDHCPC_OPTCODE_PAD)
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_PAD;
else if (handle->options[n] == IPDHCPC_OPTCODE_END)
break; /* end -> strip it and breakout. */
else
{
int msg;
/* We should select the option value if server has set it */
option = ipdhcpc_koption_get(handle->info.options,
handle->info.optlen,
handle->options[n]);
/* We check the receive option buffer which is gotten from server, but no record */
if (IP_NULL == option)
{
/* In this case we choose the user's configuration */
option = &(handle->options[n]);
}
/* Should consider that END will occupy 1 byte, so here use >= */
if (2 + option[1] >= IPDHCPC_LEFT_OPTION_SPACE(i))
{
/* check if option is truncated, if so - skip it and continue. */
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: USER: option(%d) is too long to put into dhcp message.\n",
ift->ifname,
(int) option[0]);
goto next_opt;
}
/* Check the option.*/
for (msg = 0; ipdhcpc_options[msg].optcode != IPDHCPC_OPTCODE_END; msg++)
if (ipdhcpc_options[msg].optcode == handle->options[n])
{
#ifdef IP_ASSERT
/* These options are handled by IPDHCPC.*/
if (option[0] == IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE ||
option[0] == IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER ||
option[0] == IPDHCPC_OPTCODE_DHCP_MAX_MESSAGE_SIZE)
IP_PANIC();
#endif
/* Verify min length field.*/
if (ipdhcpc_options[msg].minlen && option[1] < ipdhcpc_options[msg].minlen)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: USER: too short option %d - not sent",
ift->ifname,
(int) option[0]);
goto next_opt;
}
/* Verify max length field. */
if (ipdhcpc_options[msg].maxlen && option[1] > ipdhcpc_options[msg].maxlen)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: USER: too long option %d - not sent",
ift->ifname,
(int) option[0]);
goto next_opt;
}
/* Option must not be included with this message type, skip it. */
if (IP_BIT_GET(ipdhcpc_options[msg].msgmustnot, handle->priv->msgtype - 1))
{
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: USER: ignoring option %d due to msgtype - not sent",
ift->ifname,
(int) option[0]);
goto next_opt;
}
/* Option valid. */
break;
}
/*
* RFC2131:p38 - Not many options used with DHCP
* decline & release.
*/
if (handle->priv->msgtype == IPDHCPC_MSG_DECLINE ||
handle->priv->msgtype == IPDHCPC_MSG_RELEASE)
{
if (option[0] != IPDHCPC_OPTCODE_DHCP_REQUESTED_ADDRESS &&
option[0] != IPDHCPC_OPTCODE_DHCP_SERVER_IDENTIFIER &&
option[0] != IPDHCPC_OPTCODE_DHCP_MESSAGE)
{
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: USER: ignoring option %d due to dec/rel - not sent",
ift->ifname,
(int)option[0]);
goto next_opt;
}
}
/* Copy over option. */
ipcom_memcpy(&ipdhcpc.out_pkt->options[i],
&option[0],
2 + option[1]);
i +=(2 + option[1]);
/* fall through to next option. */
}
next_opt:
n += 1 + handle->options[n + 1];
continue;
}
/* End of options. */
ipdhcpc.out_pkt->options[i++] = IPDHCPC_OPTCODE_END;
/* Send the DHCP client packet. */
ip_assert(i <= IPDHCPC_MAX_OPTION_SIZE);
length = ip_offsetof(Ipdhcpc_pkt, options) + i;
length = IP_MAX(IPDHCPC_PKT_MINLEN, length);
/* if BROADCAST dest needed, set the SRC address 0 */
if (addr->sin_addr.s_addr == IP_INADDR_BROADCAST || handle->serveraddr_n == IP_NULL)
{
ipdhcpc_pkt_out(ift, handle, length);
return;
}
#ifdef IPNET
{
struct Ip_in_pktinfo *pktinfo = IP_NULL;
struct Ip_iovec vec;
struct Ip_msghdr msg;
struct Ip_cmsghdr *c;
Ip_u8 cmsg_buf[IP_CMSG_SPACE(sizeof(struct Ip_in_pktinfo))];
ipcom_memset(&msg, 0, sizeof(msg));
/* Initialize vector */
vec.iov_base = ipdhcpc.out_pkt;
vec.iov_len = length;
/* Initialize name */
msg.msg_name = addr;
msg.msg_namelen = sizeof(struct Ip_sockaddr);
/* Point to the IO vector */
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
/* Use ancillary data to specify outgoing interface. */
c = (struct Ip_cmsghdr *) cmsg_buf;
c->cmsg_len = IP_CMSG_LEN(sizeof(struct Ip_in_pktinfo));
c->cmsg_level = IP_IPPROTO_IP;
c->cmsg_type = IP_IP_PKTINFO;
pktinfo = IP_CMSG_DATA(c);
ipcom_memset(pktinfo, 0, sizeof(*pktinfo));
pktinfo->ipi_addr.s_addr = 0;
pktinfo->ipi_ifindex = ift->ifindex;
msg.msg_control = c;
msg.msg_controllen = sizeof(cmsg_buf);
/* Send advertisement. */
wrote = ipcom_sendmsg(ipdhcpc.fd, &msg, 0);
}
#else
wrote = ipcom_sendto(ipdhcpc.fd,
ipdhcpc.out_pkt,
length,
0,
(struct Ip_sockaddr *) addr,
sizeof(struct Ip_sockaddr));
#endif
/* If no ARP of DHCP server, waiting for ARP reply from DHCP server. */
if (handle->priv->msgtype == IPDHCPC_MSG_RELEASE)
ipcom_millisleep(1000);
if (length != wrote)
{
/* if failed to send unicast DHCP release message to DHCP server, broadcast DHCP release message */
if(handle->priv->msgtype == IPDHCPC_MSG_RELEASE)
{
ipdhcpc_pkt_out(ift, handle, length);
return;
}
IPCOM_LOG3(WARNING, "ipdhcpc[%s] :: sendto(%d) failed, errno = %d",
ift->ifname,
length, wrote == -1 ? ipcom_errno : wrote);
}
else
ipdhcpc_print_packet(ift, handle, ipdhcpc.out_pkt, IP_FALSE,
length);
}
/*
*===========================================================================
* ipdhcpc_check_overload_opt
*===========================================================================
* Description: check whether the overload context valid or not.
* Parameters:
* Returns: IP_TRUE - valid
* IP_FALSE - invalid
*/
IP_STATIC Ip_bool
ipdhcpc_check_overload_opt(Ip_u8 *opt, Ip_u32 opt_len)
{
Ip_u8 *options = opt;
/* RFC 2131 page 24 :
* If the 'option overload' option is present in the 'options' field, the
* options in the 'options' field MUST be terminated by an 'end' option,
* and MAY contain one or more 'pad' options to fill the options field.
* The options in the 'sname' and 'file' fields (if in use as indicated
* by the 'options overload' option) MUST begin with the first octet of
* the field, MUST be terminated by an 'end' option, and MUST be
* followed by 'pad' options to fill the remainder of the field. Any
* individual option in the 'options', 'sname' and 'file' fields MUST be
* entirely contained in that field.
*/
if (IPDHCPC_OPTCODE_PAD == *options)
return IP_FALSE;
while (opt_len > 0 && IPDHCPC_OPTCODE_END != *options)
{
Ip_u8 tmp_optlen;
tmp_optlen = options[IPDHCPC_OPT_LEN];
if (tmp_optlen < IPDHCPC_OPT_MIN_LEN)
return IP_FALSE;
if (tmp_optlen + IPDHCPC_OPT_MIN_LEN > opt_len)
return IP_FALSE;
options += tmp_optlen + IPDHCPC_OPT_MIN_LEN;
opt_len -= tmp_optlen + IPDHCPC_OPT_MIN_LEN;
}
if (IPDHCPC_OPTCODE_END != *options)
return IP_FALSE;
options++;
opt_len--;
while (opt_len > 0)
{
if (IPDHCPC_OPTCODE_PAD != *options)
return IP_FALSE;
options++;
opt_len--;
}
return IP_TRUE;
}
/*
*===========================================================================
* ipdhcpc_is_overload_option_valid
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_copy_sname_and_file(Ipdhcpc_handle *handle)
{
Ip_u8 *opt_ol = IP_NULL;
opt_ol = ipdhcpc_koption_get(&ipdhcpc.in_pkt->options[4],
handle->priv->in_optlen,
IPDHCPC_OPTCODE_OPTION_OVERLOAD);
if (opt_ol)
{
ipcom_memset(handle->info.sname,
0,
sizeof(handle->info.sname));
ipcom_memset(handle->info.file,
0,
sizeof(handle->info.file));
if ((IPDHCPC_OVERLOAD_FILE == opt_ol[IPDHCPC_OPT_DATA])
|| (IPDHCPC_OVERLOAD_BOTH == opt_ol[IPDHCPC_OPT_DATA]))
{
if (ipdhcpc_check_overload_opt((Ip_u8*)ipdhcpc.in_pkt->file,
IPDHCPC_PKT_HDR_FILE_LEN))
{
ipcom_memcpy(handle->info.file,
ipdhcpc.in_pkt->file,
sizeof(ipdhcpc.in_pkt->file));
}
}
if ((IPDHCPC_OVERLOAD_SNAME == opt_ol[IPDHCPC_OPT_DATA])
|| (IPDHCPC_OVERLOAD_BOTH == opt_ol[IPDHCPC_OPT_DATA]))
{
if (ipdhcpc_check_overload_opt((Ip_u8*)ipdhcpc.in_pkt->sname,
IPDHCPC_PKT_HDR_SNAME_LEN))
{
ipcom_memcpy(handle->info.sname,
ipdhcpc.in_pkt->sname,
sizeof(ipdhcpc.in_pkt->sname));
}
}
}
else
{
ipcom_memcpy(handle->info.sname,
ipdhcpc.in_pkt->sname,
sizeof(ipdhcpc.in_pkt->sname));
ipcom_memcpy(handle->info.file,
ipdhcpc.in_pkt->file,
sizeof(ipdhcpc.in_pkt->file));
/* To ensure sname and file ends with NULL-terminated */
handle->info.sname[IPDHCPC_SNAME_LEN-1] = 0;
handle->info.file[IPDHCPC_BOOTFILE_NAME_LEN-1] = 0;
}
return;
}
/*
*===========================================================================
* ipdhcpc_reply_input
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_reply_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle)
{
Ip_u8 *option;
Ip_u8 *opt_serverid;
int bootp_mode;
/* hlen - hardware address length must match. */
if (ift->hlen != ipdhcpc.in_pkt->hlen)
return;
/* chaddr - hardware address must match. */
if (ipcom_memcmp(ipdhcpc.in_pkt->chaddr, ift->chaddr, ift->hlen) != 0)
return;
/* Optional options[0-4] - magic cookie must match. */
if (ipdhcpc.in_pkt->options[0] != (Ip_u8)((handle->magic_cookie >> 24) & 0xff)
|| ipdhcpc.in_pkt->options[1] !=(Ip_u8)((handle->magic_cookie >> 16) & 0xff)
|| ipdhcpc.in_pkt->options[2] !=(Ip_u8)((handle->magic_cookie >> 8) & 0xff)
|| ipdhcpc.in_pkt->options[3] !=(Ip_u8)((handle->magic_cookie >> 0) & 0xff))
{
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: input discard - bad magic cookie", ift->ifname);
return;
}
handle->priv->in_optlen =(Ip_u16)(ipdhcpc.in_pkt_len - ip_offsetof(Ipdhcpc_pkt, options) - 4);
option = ipdhcpc_koption_get(&ipdhcpc.in_pkt->options[4],
handle->priv->in_optlen,
IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE);
bootp_mode = ipcom_sysvar_get_as_int("ipdhcpc.bootp_mode",IPDHCPC_BOOTP_MODE);
if (bootp_mode && ipdhcpc.in_pkt->op == IPDHCPC_OP_BOOTREPLY)
{
handle->priv->msgtype = IP_NULL;
ipdhcpc_bootp_input(ift, handle);
return;
}
/***** Get mandatory reply message type. */
if (option == IP_NULL || option[2] == 0 || option[2] > IPDHCPC_MSG_INFORM)
{
IPCOM_LOG1(WARNING, "ipdhcpc[%s] :: input discard - missing/illegal message type option", ift->ifname);
return;
}
handle->priv->msgtype = option[2];
/***** Get mandatory server id. *****/
opt_serverid = ipdhcpc_koption_get(&ipdhcpc.in_pkt->options[4],
handle->priv->in_optlen,
IPDHCPC_OPTCODE_DHCP_SERVER_IDENTIFIER);
if (opt_serverid == IP_NULL || opt_serverid[1] != 4)
{
IPCOM_LOG1(WARNING, "ipdhcpc[%s] :: input discard - missing/illegal server id option", ift->ifname);
return;
}
/*
*Compare the client identifier option to the one it is configured to send,
*discard the message if they do not match.
*/
option = ipdhcpc_koption_get(&ipdhcpc.in_pkt->options[4],
handle->priv->in_optlen,
IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER);
if (option)
{
int clen;
Ip_u8 *cid;
int ctype;
if (ift->clientid_len != 0 )
{
clen = ift->clientid_len;
cid = (Ip_u8 *)ift->clientid;
ctype = 0;
}
else
{
clen = ift->hlen;
cid = ift->chaddr;
ctype = 1;
}
if (ctype != option[IPDHCPC_OPT_DATA] ||
(clen+1) != option[IPDHCPC_OPT_LEN]||
ipcom_memcmp (&option[IPDHCPC_OPT_DATA+1], cid, clen) != 0)
{
IPCOM_LOG1(WARNING, "ipdhcpc[%s] :: input discard -illegal client id option", ift->ifname);
return ;
}
}
/*
* Check that no "MUST NOT" options are included in the NAK.
* RFC2131:p29
*/
option = &ipdhcpc.in_pkt->options[4];
if (handle->priv->msgtype == IPDHCPC_MSG_NAK)
{
int i;
for (i = 0; i < handle->priv->in_optlen; i++)
{
if (option[i] == IPDHCPC_OPTCODE_PAD)
continue; /* padding, skip. */
if (option[i] == IPDHCPC_OPTCODE_END)
break; /* option end code. */
if (i + 1 + option[i + 1] >= handle->priv->in_optlen)
break; /* truncated option. */
if (option[i] != IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE
&& option[i] != IPDHCPC_OPTCODE_DHCP_MESSAGE
&& option[i] != IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER
&& option[i] != IPDHCPC_OPTCODE_VENDOR_CLASS_IDENTIFIER
&& option[i] != IPDHCPC_OPTCODE_DHCP_SERVER_IDENTIFIER)
{
IPCOM_LOG1(DEBUG, "ipdhcpc[%s] :: input discard - illegal option included in NAK", ift->ifname);
return;
}
i+=1+option[i+1];
}
}
/*
* Check that no "MUST NOT" options are included in the OFFER or ACK.
* RFC2131:p29
*/
else
{
if (ipdhcpc_koption_get(option, handle->priv->in_optlen,
IPDHCPC_OPTCODE_DHCP_REQUESTED_ADDRESS) ||
ipdhcpc_koption_get(option, handle->priv->in_optlen,
IPDHCPC_OPTCODE_DHCP_PARAMETER_REQUEST_LIST) ||
ipdhcpc_koption_get(option, handle->priv->in_optlen,
IPDHCPC_OPTCODE_DHCP_MAX_MESSAGE_SIZE))
{
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: input discard - illegal option included in OFFER/ACK", ift->ifname);
return;
}
}
/* Act on the reply msgtype. */
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: parsing %s reply message",
ift->ifname,
ipdhcpc_msg_name[handle->priv->msgtype]);
switch(handle->priv->msgtype)
{
case IPDHCPC_MSG_OFFER:
ipdhcpc_offer_input(ift, handle, opt_serverid);
break;
case IPDHCPC_MSG_ACK:
ipdhcpc_ack_input(ift, handle, opt_serverid);
break;
case IPDHCPC_MSG_NAK:
ipdhcpc_nak_input(ift, handle, opt_serverid);
break;
/* Client messages, should not happen. */
default:
IPCOM_LOG2(WARNING, "ipdhcpc[%s] :: input discard - bad reply message %s",
ift->ifname,
ipdhcpc_msg_name[handle->priv->msgtype]);
return;
}
}
/*
*===========================================================================
* ipdhcpc_koption_get
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_u8 *
ipdhcpc_koption_get(Ip_u8 *options, int options_length, Ip_u8 code)
{
int i;
/* Scan options for msgtype and check client-identifier. */
for (i = 0; i < options_length; i++)
{
/* padding, skip. */
if (options[i] == IPDHCPC_OPTCODE_PAD)
continue;
/* option end code. */
if (options[i] == IPDHCPC_OPTCODE_END)
break;
/* check that option is not truncated. */
if (i + 1 + options[i + 1] >= options_length)
{
IPCOM_LOG1(WARNING, "ipdhcpc_koption_get() - truncated option %d",
(int) options[i]);
break;
}
/* Found option we are looking for. */
if (options[i] == code)
return &options[i];
/* Next option. */
i += 1 + options[i + 1];
continue;
}
/* Not found. */
return IP_NULL;
}
/*
*===========================================================================
* ipdhcpc_offer_input
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_offer_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
Ip_u8 *opt_serverid)
{
/* Only collect OFFER replies in selecting state. */
if (handle->priv->state != IPDHCPC_STATE_SELECTING)
{
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: input discard - only collecting offers in selecting state", ift->ifname);
return;
}
/* Save server ID. */
handle->priv->serverid_n =(Ip_u32)((opt_serverid[2] << 24) +
(opt_serverid[3] << 16) +
(opt_serverid[4] << 8) +
(opt_serverid[5] << 0));
handle->priv->serverid_n = IP_SWAP32(handle->priv->serverid_n);
if (handle->priv->serverid_n == 0)
{
IPCOM_LOG1(WARNING, "ipdhcpc[%s] :: input discard - zero server id option", ift->ifname);
return;
}
handle->priv->state = IPDHCPC_STATE_REQUESTING;
/* Ok packet, save some parameters. */
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Valid OFFER -> REQUESTING state", ift->ifname);
/* 'your'(client) IP address. The address the server offers us. */
handle->priv->yiaddr = ipdhcpc.in_pkt->yiaddr;
/* Send DHCP REQUEST */
handle->priv->retrans_counter = 0;
/* Update USER info handle structure.*/
handle->info.ipaddr_n = ipdhcpc.in_pkt->yiaddr;
handle->info.serveraddr_n = handle->priv->serverid_n;
handle->info.optlen = handle->priv->in_optlen;
ipcom_memcpy(handle->info.options,
&ipdhcpc.in_pkt->options[4],
handle->info.optlen);
handle->info.bootaddr_n = ipdhcpc.in_pkt->siaddr;
ipdhcpc_copy_sname_and_file(handle);
if (handle->reply_cb != IP_NULL)
{
if(handle->reply_cb(handle, IPDHCPC_REPLY_CB_EVENT_OFFER) == IPDHCPC_REPLY_CB_ERROR)
{
handle->priv->state = IPDHCPC_STATE_SELECTING;
return;
}
}
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_REQUEST);
}
/*
*===========================================================================
* ipdhcpc_check_addr
*===========================================================================
* Description: check the address is a valid unicast address or not.
* Parameters:
* Returns: IPCOM_SUCCESS if the address is valid, otherwise IPCOM_ERR_FAILED
*
*/
IP_STATIC Ip_err
ipdhcpc_check_addr (Ip_in_addr_t yiaddr)
{
Ip_in_addr_t a = ip_ntohl (yiaddr);
if ((IP_INADDR_ANY == a)
||(IP_INADDR_LOOPBACK == a)
||((a & 0xFFFF0000) == 0xA9FE0000))
{
return IPCOM_ERR_FAILED;
}
/* IP_IN_CLASSX is not same as IN_CLASSX */
if (IP_IN_CLASSA(yiaddr)
||IP_IN_CLASSB(yiaddr)
||IP_IN_CLASSC(yiaddr))
{
return IPCOM_SUCCESS;
}
return IPCOM_ERR_FAILED;
}
/*
*===========================================================================
* ipdhcpc_ack_input
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_ack_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle,
Ip_u8 *opt_serverid)
{
#if 0
int prev_state = handle->priv->state;
#endif
struct Ip_in_aliasreq ifalias;
struct Ip_sockaddr_in *addrp;
struct Ip_sockaddr_in *maskp;
struct Ip_ifreq ifreq;
Ip_err ret = 0;
ipcom_memset(&ifalias, 0, sizeof(ifalias));
/* Discard ACK in the following states: */
if (handle->priv->state <= IPDHCPC_STATE_INIT_REBOOT ||
handle->priv->state == IPDHCPC_STATE_SELECTING ||
handle->priv->state == IPDHCPC_STATE_ASSIGNING ||
handle->priv->state == IPDHCPC_STATE_BOUND)
{
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: discarding ACK in %s state",
ift->ifname,
ipdhcpc_state_name[handle->priv->state]);
return;
}
if (handle->priv->state == IPDHCPC_STATE_REBINDING||
handle->priv->state == IPDHCPC_STATE_INFORMING)
{
/* Found server that can extend the lease. Save its id */
handle->priv->serverid_n =(Ip_u32)((opt_serverid[2] << 24) +
(opt_serverid[3] << 16) +
(opt_serverid[4] << 8) +
(opt_serverid[5] << 0));
handle->priv->serverid_n = IP_SWAP32(handle->priv->serverid_n);
if (handle->priv->serverid_n == 0)
{
IPCOM_LOG1(WARNING, "ipdhcpc[%s] :: input discard - zero server id option",
ift->ifname);
return;
}
}
else if (ipcom_memcmp
(&opt_serverid[2], &handle->priv->serverid_n, 4))
{
/* Ignore ACK if not from the chosen server. */
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: input ACK discard - not my server", ift->ifname);
return;
}
/* ACK on existing lease. Update lease. */
ip_assert(ift->handle == IP_NULL || handle->priv->state >= IPDHCPC_STATE_REBOOTING);
ip_assert(ift->handle != IP_NULL || handle->priv->state < IPDHCPC_STATE_RENEWING);
/* Cancel the timers. */
ipdhcpc_timer_cancel(handle, 0);
if ( handle->priv->state != IPDHCPC_STATE_ASSIGNING)
{
/* Wakeup blocking request or call the callback. */
if(IPDHCPC_REPLY_CB_ERROR == ipdhcpc_wakeup(handle,
IPCOM_SUCCESS,
IPDHCPC_REPLY_CB_EVENT_ACK))
{
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_DECLINE);
ipdhcpc_state_restart(handle);
return;
}
}
/* Set IPv4 address on this interface.*/
if (handle->priv->state < IPDHCPC_STATE_RENEWING&&
handle->priv->state != IPDHCPC_STATE_INFORMING)
{
Ip_u8 *option;
Ip_u16 optlen = handle->priv->in_optlen;
/* Act on the DHCP options we got from the server.*/
for (option = ipdhcpc_option_get_first(&ipdhcpc.in_pkt->options[4]);
option != IP_NULL;
option = ipdhcpc_option_limit_get_next(option, &optlen))
{
#if 0
/* Let user application look at/act on the option */
if (ipdhcpc_option_callback(handle, option) == IP_FALSE)
continue;
#endif
if (*option == IPDHCPC_OPTCODE_SUBNET_MASK)
{
/* Set the mask, as the address may be in use while/if we
* perform duplicate address detection
*/
maskp = (struct Ip_sockaddr_in *)&ifalias.ifra_mask;
maskp->sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(maskp, sizeof(struct Ip_sockaddr_in));
maskp->sin_addr.s_addr = IP_GET_32ON8(&option[2]);
break;
}
}
ipcom_memset(&ifreq, 0, sizeof(ifreq));
ipcom_strcpy(ifreq.ifr_name, ift->ifname);
(void)ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFADDR, &ifreq);
addrp = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr;
if (addrp->sin_addr.s_addr != ipdhcpc.in_pkt->yiaddr)
{
(void)ipdhcpc_remove_existing_addresses(ift->ifname);
addrp = &ifalias.ifra_addr;
(void)ipcom_if_indextoname(handle->ifindex, ifalias.ifra_name);
addrp->sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(addrp, sizeof(struct Ip_sockaddr_in));
addrp->sin_addr.s_addr = ipdhcpc.in_pkt->yiaddr;
if (IPCOM_SUCCESS != ipdhcpc_check_addr(addrp->sin_addr.s_addr))
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ack_input(): address(0x%x) is invalid",
ifalias.ifra_name,
ip_ntohl(addrp->sin_addr.s_addr));
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_DECLINE);
ipdhcpc_state_restart(handle);
return;
}
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCAIFADDR, &ifalias) < 0)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ack_input(): ipcom_ioctl(IP_SIOCSIFADDR) failed, errno = %d",
ifalias.ifra_name, ipcom_errno);
ipdhcpc_output_request(ift, handle, IPDHCPC_MSG_DECLINE);
ipdhcpc_state_restart(handle);
return;
}
#ifdef IPNET
{
/*
* RFC2131:p17. Perform gratuitious ARP and send DHCPDECLINE
* if address is busy. If busy, wait minimum 10 seconds to avoid
* excessive network traffic.
*/
int sec;
char buf[50];
sec = IPDHCPC_ASSIGN_IP_TIMEOUT;
ipdhcpc_timer_set(handle, sec * 1000, 1);
/* We are now waiting for address to be assigned and available on the interface */
handle->priv->state = IPDHCPC_STATE_ASSIGNING;
IPCOM_LOG3(INFO, "ipdhcpc[%s] :: ipdhcpc_ack_input(): Waiting up to %d seconds for address %s to be assigned",
ift->ifname, sec, ipcom_inet_ntop(IP_AF_INET,
&addrp->sin_addr.s_addr,
buf,
sizeof(buf)));
}
#endif /* IPNET */
}
}
/* We are now in bound state. */
if (handle->priv->state != IPDHCPC_STATE_ASSIGNING)
handle->priv->state = IPDHCPC_STATE_BOUND;
handle->priv->yiaddr = ipdhcpc.in_pkt->yiaddr;
handle->clientaddr_n = handle->priv->yiaddr;
/* Update USER info handle structure.*/
ip_assert(handle->ifindex == ift->ifindex);
handle->info.ipaddr_n = ipdhcpc.in_pkt->yiaddr;
handle->info.serveraddr_n = handle->priv->serverid_n;
handle->info.optlen = handle->priv->in_optlen;
ipcom_memcpy(handle->info.options,
&ipdhcpc.in_pkt->options[4],
handle->info.optlen);
handle->info.bootaddr_n = ipdhcpc.in_pkt->siaddr;
ipdhcpc_copy_sname_and_file(handle);
#if 0
if (prev_state == IPDHCPC_STATE_RENEWING)
{
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Successful ACK, renewing lease", ift->ifname);
ipdhcpc_timer_set(handle, ipdhcpc_sec_to_msec(ift->lease.t1), 1);
}
else
#endif
if ( handle->priv->state != IPDHCPC_STATE_ASSIGNING)
{
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Successful ACK, ready to lease", ift->ifname);
ret = ipdhcpc_iflease(handle->ifindex, handle);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR, "ipdhcpc_reply_cb :: ipdhcpc_iflease() failed on interface '%d', err = %d.",
handle->ifindex, ret);
}
}
}
/*
*===========================================================================
* ipdhcpc_nak_input
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_nak_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle, Ip_u8 *opt_serverid)
{
(void)ift;
(void)opt_serverid; /* !!should it be checked? */
/* Discard NAK in the following states: */
if (handle->priv->state <= IPDHCPC_STATE_INIT_REBOOT ||
handle->priv->state == IPDHCPC_STATE_SELECTING ||
handle->priv->state == IPDHCPC_STATE_BOUND)
{
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: input discard - ignoring NAK in %s state",
ift->ifname, ipdhcpc_state_name[handle->priv->state]);
return;
}
/*fix for defect WIND00201995 start*/
if (handle->reply_cb != IP_NULL)
(void)handle->reply_cb(handle, IPDHCPC_REPLY_CB_EVENT_NAK);
/*fix for defect WIND00201995 end*/
switch(handle->priv->state)
{
case IPDHCPC_STATE_RENEWING:
handle->priv->next = ipdhcpc.handle_head;
ipdhcpc.handle_head = handle;
/* this is an intentional fall through to the next case */
case IPDHCPC_STATE_REBINDING:
/* NAK on this lease, unlease. */
if (handle == ift->handle)
(void)ipdhcpc_ifunlease_locked(ift, IP_NULL);
/*unlease means cancel Timers and remove address*/
ift->handle = handle;
/* fall through */
case IPDHCPC_STATE_REQUESTING:
case IPDHCPC_STATE_REBOOTING:
/*ift->handle!=handle??*/
ip_assert(ift->handle == handle);
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: DHCP NAK -> restarting in %s state",
ift->ifname,
ipdhcpc_state_name[handle->priv->state]);
ipdhcpc_state_restart(handle);
break;
case IPDHCPC_STATE_INFORMING:
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: DHCP NAK in %s state",
ift->ifname,
ipdhcpc_state_name[handle->priv->state]);
break;
default:
IP_PANIC();
break;
}
}
/*
*===========================================================================
* ipdhcpc_bootp_input
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_bootp_input(Ipdhcpc_if *ift, Ipdhcpc_handle *handle)
{
/* ip_assert(ift->handle == IP_NULL); */
Ip_err ret = 0;
/* We are now in bound state. */
handle->priv->state = IPDHCPC_STATE_BOUND;
handle->priv->yiaddr = ipdhcpc.in_pkt->yiaddr;
/* Update USER info handle structure.*/
ip_assert(handle->ifindex == ift->ifindex);
handle->info.ipaddr_n = ipdhcpc.in_pkt->yiaddr;
handle->info.serveraddr_n = handle->priv->serverid_n;
handle->info.optlen = handle->priv->in_optlen;
ipcom_memcpy(handle->info.options,
&ipdhcpc.in_pkt->options[4],
handle->info.optlen);
handle->info.bootaddr_n = ipdhcpc.in_pkt->siaddr;
ipdhcpc_copy_sname_and_file(handle);
handle->priv->tmo_ignore = 1;
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Successful ACK, ready to lease", ift->ifname);
if( IPDHCPC_REPLY_CB_ERROR == ipdhcpc_wakeup(handle,
IPCOM_SUCCESS,
IPDHCPC_REPLY_CB_EVENT_BOOTREPLY))
{
ipdhcpc_state_restart(handle);
return;
}
ret = ipdhcpc_iflease(handle->ifindex, handle);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR, "ipdhcpc_reply_cb :: ipdhcpc_iflease() failed on interface '%d', err = %d.",
handle->ifindex, ret);
}
}
/*
*===========================================================================
* ipdhcpc_ifunlease_locked
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_ifunlease_locked(Ipdhcpc_if *ift, Ipdhcpc_handle **handlep)
{
struct Ip_ifreq ifreq;
struct Ip_sockaddr_in *addrp;
char buf[16];
int remove_addr = 0;
/* No lease. */
if (ift->handle == IP_NULL)
return IPCOM_ERR_NOT_INSTALLED;
/* Cancel timers */
ipdhcpc_timer_cancel(ift->handle, 0);
if (ipdhcpc_if_has_addr(ift->ifindex, ift->handle->info.ipaddr_n) != IP_TRUE)
{
goto done;
}
remove_addr = ipcom_sysvar_get_as_int("ipdhcpc.remove_leased_address",
IPDHCPC_REMOVE_LEASED_ADDRESS);
if (remove_addr > 0)
{
/* Remove address. */
ipcom_memset(&ifreq, 0, sizeof(struct Ip_ifreq));
ipcom_strcpy(ifreq.ifr_name, ift->ifname);
addrp = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr;
addrp->sin_family = IP_AF_INET;
addrp->sin_len = sizeof(struct Ip_sockaddr_in);
addrp->sin_addr.s_addr = ift->handle->info.ipaddr_n;
if (addrp->sin_addr.s_addr == 0)
goto done;
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: removed address = %s",
ift->ifname,
ipcom_inet_ntop(IP_AF_INET,
&addrp->sin_addr.s_addr,
buf,
sizeof(buf)));
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCDIFADDR, &ifreq) < 0)
{
IP_PANIC2();
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ifunlease_locked(): ipcom_ioctl(IP_SIOCDIFADDR) failed, errno = %d",
ifreq.ifr_name, ipcom_errno);
return IPCOM_ERR_FAILED;;
}
}
done:
/* Remove lease. */
if (handlep)
*handlep = ift->handle;
ift->handle = IP_NULL;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcpc_if_isiniting
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_bool
ipdhcpc_if_isiniting()
{
Ip_bool retval = IP_FALSE;
Ipdhcpc_if *ift;
IPDHCPC_LOCK();
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->handle && IPDHCPC_ISINIT(ift->handle))
{
retval = IP_TRUE;
break;
}
IPDHCPC_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_validate_input
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_validate_packet(Ip_u8 *pkt, Ip_ssize_t *pktlen)
{
Ipcom_udp_header *udph = IP_NULL;
Ipcom_ip_header *iph = IP_NULL;
Ip_u16 chksum;
iph = (Ipcom_ip_header *)pkt;
udph = (Ipcom_udp_header *)(pkt + sizeof(Ipcom_ip_header));
/* Is packet IPv4 */
if(iph->vers_len != 0x45)
return(IPCOM_ERR_IGNORED_PACKET);
/* Is packet UDP */
if(iph->protocol != IP_IPPROTO_UDP)
return(IPCOM_ERR_IGNORED_PACKET);
/*Broadcast, udp socket will handle it later*/
if (iph->dst.addr32 == IP_INADDR_BROADCAST)
return(IPCOM_ERR_IGNORED_PACKET);
/* Is packet DHCP or BOOTP */
if(udph->dst_port != ip_htons(IPDHCPC_CLIENT_PORT))
return(IPCOM_ERR_IGNORED_PACKET);
/* Sanity check packet length */
if(ip_ntohs(iph->length) > *pktlen)
return(IPCOM_ERR_INVALID_PACKET);
/*adjust the length*/
*pktlen = ip_ntohs(iph->length);
if(ip_ntohs(iph->length) < IPDHCPC_RAW_HSIZE + sizeof(Ipdhcpc_pkt))
return(IPCOM_ERR_INVALID_PACKET);
/* Test IP checksum */
chksum = iph->checksum;
iph->checksum = 0;
if( chksum != ipcom_in_checksum((Ip_u16*)iph, sizeof(Ipcom_ip_header)))
return(IPCOM_ERR_BAD_PACKET);
if (udph->checksum != 0)
{
/* Test UDP checksum */
chksum = udph->checksum;
iph->length = udph->length;
iph->vers_len = 0;
iph->ttl = 0;
iph->tos = 0;
iph->id = 0;
iph->frag = 0;
udph->checksum = 0;
if(chksum != ipcom_in_checksum((Ip_u16*)pkt, (Ip_size_t)*pktlen))
return(IPCOM_ERR_BAD_PACKET);
}
return(IPCOM_SUCCESS);
}
/*
*===========================================================================
* ipdhcpc_proc
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC
IPCOM_PROCESS(ipdhcpc_proc)
{
Ip_err ret;
#ifdef IPLITE
Ip_fd cbfd = 0;
#endif
int defrcvbuf = 0x10000;
Ip_socklen_t getoptlen = sizeof(int);
Ip_socklen_t setoptlen = sizeof(int);
ipcom_proc_init();
#ifdef IPLITE
cbfd = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0);
ipcom_socketioctl(cbfd, IP_SIOCXSIFFEVENTCB, (void *)ipdhcpc_if_eventcb);
#endif
ret = ipdhcpc_init();
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipdhcpc_proc :: ipdhcpc_init() failed, ret=%ld", ret);
goto exit;
}
ret = ipdhcpc_run();
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipdhcpc_proc :: ipdhcpc_run() failed, ret=%ld", ret);
goto exit;
}
/* IPD init */
ret = ipcom_ipd_init("ipdhcpc", IPCOM_SUCCESS, &ipdhcpc.ipd_fd);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipdhcpc_proc :: ipcom_ipd_init() failed, ret=%ld", ret);
goto exit;
}
(void)ipcom_getsockopt(ipdhcpc.ipd_fd, IP_SOL_SOCKET, IP_SO_RCVBUF, &defrcvbuf, &getoptlen);
if(IPDHCPC_SOCK_MIN_RECV_BUFSIZE > defrcvbuf)
{
defrcvbuf = IPDHCPC_SOCK_MIN_RECV_BUFSIZE;
(void)ipcom_setsockopt(ipdhcpc.ipd_fd, IP_SOL_SOCKET, IP_SO_RCVBUF, &defrcvbuf, setoptlen);
defrcvbuf = 0;
}
/* Run daemon */
ipdhcpc_daemon();
/* Clean up */
ipdhcpc_ifclose_all();
/* Remove all interfaces */
{
Ipdhcpc_if *ift;
while ((ift = ipdhcpc.ift_head) != IP_NULL)
ipdhcpc_ifclose(ift->ifindex);
}
ipdhcpc_handle_free_pending();
exit:
IPCOM_LOG0(INFO,"ipdhcpc_proc :: shutting down");
if (ipdhcpc.ipd_fd == IP_INVALID_SOCKET)
(void)ipcom_ipd_init("ipdhcpc", ret, &ipdhcpc.ipd_fd);
ipdhcpc_exit();
ip_assert(ipdhcpc.ipd_fd != IP_INVALID_SOCKET);
(void)ipcom_ipd_exit("ipdhcpc", ipdhcpc.ipd_fd);
ipcom_proc_exit();
#ifdef IPLITE
ipcom_socketioctl(cbfd, IP_SIOCXDIFFEVENTCB, (void *)ipdhcpc_if_eventcb);
ipcom_socketclose(cbfd);
#endif
}
/*
*===========================================================================
* ipdhcpc_daemon
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_daemon(void)
{
struct Ip_sockaddr_in addr;
Ip_socklen_t socklen;
Ipdhcpc_handle *handle;
Ipdhcpc_if *ift;
int num;
int max_fd;
Ip_fd_set read_set;
struct Ip_sockaddr_in *addrs[IPNET_RTAX_MAX];
int offset;
struct Ipnet_ifa_msghdr *ifa;
char buf[50];
struct Ip_timeval tv;
Ip_err ret = 0;
ip_assert(ipdhcpc.opened == IP_TRUE);
tv.tv_usec = 0;
tv.tv_sec = 2;
for (;;)
{
IP_FD_ZERO(&read_set);
max_fd = IP_MAX(ipdhcpc.fd, ipdhcpc.ipd_fd);
#ifndef IPLITE
max_fd = IP_MAX(max_fd, ipdhcpc.rt_fd);
#endif
#ifndef IPLITE
IP_FD_SET(ipdhcpc.rt_fd, &read_set);
#endif
IP_FD_SET(ipdhcpc.ipd_fd, &read_set);
IP_FD_SET(ipdhcpc.fd, &read_set);
if (ipdhcpc_if_isiniting())
{
/* Get default DHCP PACKET client socket, must temporary open. */
if (IP_LIKELY(IP_INVALID_SOCKET != ipdhcpc.pkt_fd))
{
max_fd = IP_MAX(max_fd, ipdhcpc.pkt_fd);
IP_FD_SET(ipdhcpc.pkt_fd, &read_set);
}
}
else
{
if (IP_INVALID_SOCKET != ipdhcpc.pkt_fd)
{
/* for the next usage */
ipcom_socketclose(ipdhcpc.pkt_fd);
ipdhcpc.pkt_fd = IP_INVALID_SOCKET;
}
}
num = ipcom_socketselect(max_fd + 1, &read_set, IP_NULL, IP_NULL, &tv);
if (num < 0)
{
IPCOM_LOG1(WARNING, "ipcom_socketselect() failed, errno = %d", ipcom_errno);
(void)ipcom_sleep(1);
continue;
}
if (num > 0 && IP_FD_ISSET(ipdhcpc.ipd_fd, &read_set))
{
int event;
event = ipcom_ipd_input(ipdhcpc.ipd_fd);
num--;
switch(event)
{
case IPCOM_IPD_EVENT_RECONFIGURE:
IPCOM_LOG0(INFO, "received IPD reconfigure message");
break;
case IPCOM_IPD_EVENT_KILL:
IPCOM_LOG0(INFO, "received IPD kill message");
return;
default:
/* Ignore all other messages */
break;
}
}
#ifndef IPLITE
if (num > 0 && IP_FD_ISSET(ipdhcpc.rt_fd, &read_set))
{
char ifname[IP_IFNAMSIZ + 1];
Ip_ssize_t length;
char rtmsg[256];
union rtmsg_union
{
struct Ipnet_rt_msghdr *rthdr;
struct Ipnet_if_msghdr *ifmsg;
struct Ipnet_ifa_msghdr *ifamsg;
struct Ipnet_if_announcemsghdr *ifannmsg;
void *buffer;
} rt_type;
rt_type.buffer = rtmsg;
length = ipcom_recv(ipdhcpc.rt_fd, rt_type.buffer, 256 , 0);
if (length == IP_SOCKERR)
{
if (ipcom_errno == IP_ERRNO_EWOULDBLOCK)
goto more;
IPCOM_LOG1(WARNING, "ipcom_recv(routing) failed, errno = %d", ipcom_errno);
}
else
{
if ((Ip_size_t)length < sizeof(struct Ipnet_rs_msghdr))
goto more;
switch(rt_type.rthdr->rtm_type)
{
case IPNET_RTM_IFINFO:
if ((Ip_size_t)length < sizeof(struct Ipnet_ifa_msghdr))
goto more;
if (ipcom_if_indextoname(rt_type.ifamsg->ifam_index, ifname) == IP_NULL)
goto more;
IPCOM_LOG1(DEBUG2, "received RT IFINFO for interface %s", ifname);
(void)ipdhcpc_if_eventcb(ifname, rt_type.ifamsg->ifam_index, rt_type.ifamsg->ifam_flags, IP_EIOXCHANGE, IP_NULL);
break;
case IPNET_RTM_IFANNOUNCE:
if ((Ip_size_t)length < sizeof(struct Ipnet_if_announcemsghdr))
goto more;
if (rt_type.ifannmsg->ifan_what == IPNET_IFAN_DEPARTURE)
{
IPCOM_LOG1(DEBUG2, "received RT IFANNOUNCE=departure for interface %s", ifname);
if (ipdhcpc_if_isopen(rt_type.ifannmsg->ifan_index))
(void)ipdhcpc_ifclose(rt_type.ifannmsg->ifan_index);
}
else
{
if (ipcom_if_indextoname(rt_type.ifannmsg->ifan_index, ifname) == IP_NULL)
goto more;
IPCOM_LOG1(DEBUG2, "received RT IFANNOUNCE=arrival for interface %s", ifname);
/*!! do what? */
}
break;
case IPNET_RTM_NEWADDR:
/* A new address is available for the interface. Check to see if it is
* DHCPC assigned and in that case go to BOUND state */
ipcom_if_indextoname(rt_type.ifamsg->ifam_index, ifname);
offset = 0;
while (offset < rt_type.ifamsg->ifam_msglen)
{
ifa = (struct Ipnet_ifa_msghdr *) ((Ip_u8 *)rt_type.ifamsg + offset);
ipdhcpc_route_init_addrs(ifa + 1, ifa->ifam_addrs, (struct Ip_sockaddr **) addrs);
/* Check only AF_INET addrs */
if (ifa->ifam_type != IPNET_RTM_NEWADDR || addrs[IPNET_RTAX_IFA]->sin_family != IP_AF_INET)
break;
IPCOM_LOG2(DEBUG2, "received RT NEWADDR for %s on interface %s",
ipcom_inet_ntop(IP_AF_INET,
&addrs[IPNET_RTAX_IFA]->sin_addr.s_addr,
buf,
sizeof(buf)),
ifname);
/* Lock required before wakeup */
IPDHCPC_LOCK();
/* look for handle of pending address assign */
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
{
handle = ift->handle;
if (handle == IP_NULL) continue;
if((handle->priv->state == IPDHCPC_STATE_ASSIGNING)
&& (addrs[IPNET_RTAX_IFA]->sin_addr.s_addr == handle->clientaddr_n))
{
/* OK. Address has now successfully been assigned to the interface */
ipdhcpc_timer_cancel(handle, 1);
/* We are now in bound state. */
handle->priv->state = IPDHCPC_STATE_BOUND;
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Successful ACK, ready to lease", ifname);
/* Wakeup blocking request or call the callback. */
ret = ipdhcpc_iflease(handle->ifindex, handle);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR, "ipdhcpc_reply_cb :: ipdhcpc_iflease() failed on interface '%d', err = %d.",
handle->ifindex, ret);
}
}
}
IPDHCPC_UNLOCK();
offset += ifa->ifam_msglen;
}
break;
case IPNET_RTM_ADDRCONFLICT:
/* There was an address conflict on the interface */
ipcom_if_indextoname(rt_type.ifamsg->ifam_index, ifname);
offset = 0;
while (offset < rt_type.ifamsg->ifam_msglen)
{
ifa = (struct Ipnet_ifa_msghdr *) ((Ip_u8 *)rt_type.ifamsg + offset);
ipdhcpc_route_init_addrs(ifa + 1, ifa->ifam_addrs, (struct Ip_sockaddr **) addrs);
/* Only check AF_INET addrs */
if (ifa->ifam_type != IPNET_RTM_ADDRCONFLICT || addrs[IPNET_RTAX_IFA]->sin_family != IP_AF_INET)
break;
IPCOM_LOG2(DEBUG2, "received IPNET_RTM_ADDRCONFLICT for %s on interface %s",
ipcom_inet_ntop(IP_AF_INET,
&addrs[IPNET_RTAX_IFA]->sin_addr.s_addr,
buf,
sizeof(buf)),
ifname);
/* Is the address DHCPC assigned? */
IPDHCPC_LOCK();
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == rt_type.ifamsg->ifam_index)
break;
if((ift != IP_NULL) &&
(ift->handle->clientaddr_n == addrs[IPNET_RTAX_IFA]->sin_addr.s_addr))
{
if (ift->handle->priv->state == IPDHCPC_STATE_BOUND)
{
/* A bound address was removed on the interface. Possibly due to an address conflict.
* Get a new address */
IPCOM_LOG2(INFO, "ipdhcpc[%s] :: Bound address %s has been removed from interface",
ifname,
ipcom_inet_ntop(IP_AF_INET,
&addrs[IPNET_RTAX_IFA]->sin_addr.s_addr,
buf,
sizeof(buf)));
/* Send a decline to the server for the removed address */
ipdhcpc_output_request(ift, ift->handle, IPDHCPC_MSG_DECLINE);
/* Re-add to pending handle list */
ift->handle->priv->next = ipdhcpc.handle_head;
ipdhcpc.handle_head = ift->handle;
/* restart */
ipdhcpc_state_restart(ift->handle);
}
}
IPDHCPC_UNLOCK();
offset += ifa->ifam_msglen;
}
break;
default:
break;
}
}
}
#endif
more:
/* only handle the packet when DHCP session setup,
* if multi-ifs, also handle the opened DHCP session */
if (num > 0 && IP_INVALID_SOCKET != ipdhcpc.pkt_fd && IP_FD_ISSET(ipdhcpc.pkt_fd, &read_set))
{
Ip_ssize_t n;
Ip_err ret;
n = ipcom_recvfrom(ipdhcpc.pkt_fd,
ipdhcpc.in_raw,
ipdhcpc.max_message_size,
0,
IP_NULL,
IP_NULL);
if (n <= (Ip_ssize_t)IPDHCPC_RAW_HSIZE)
{
IPCOM_LOG1(DEBUG2, "ipdhcpc_daemon :: recvfrom() failed, errno = %d", ipcom_errno);
continue;
}
ret = ipdhcpc_validate_packet(ipdhcpc.in_raw, &n);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG1(DEBUG2, "validate return %d.\n", ret);
continue;
}
ipdhcpc.in_pkt_len = n - IPDHCPC_RAW_HSIZE;
goto setup_continue;
}
if (num == 0 || !IP_FD_ISSET(ipdhcpc.fd, &read_set))
continue;
/* Received DHCP packet. */
socklen = sizeof(struct Ip_sockaddr);
ipdhcpc.in_pkt_len = ipcom_recvfrom(ipdhcpc.fd,
ipdhcpc.in_pkt,
ipdhcpc.max_message_size - IPDHCPC_RAW_HSIZE,
0,
(struct Ip_sockaddr *)&addr,
&socklen);
/* read error. */
if (ipdhcpc.in_pkt_len < 0)
{
IPCOM_LOG1(DEBUG2, "ipdhcpc_daemon :: recvfrom() failed, errno = %d", ipcom_errno);
continue;
}
setup_continue:
/* too short packet. */
if (ipdhcpc.in_pkt_len < (Ip_ssize_t)sizeof(Ipdhcpc_pkt))
continue;
/* not a bootp/dhcp reply. */
if (ipdhcpc.in_pkt->op != IPDHCPC_OP_BOOTREPLY)
continue;
IPDHCPC_LOCK();
/* Check our outstanding list of requests. */
for (handle = ipdhcpc.handle_head; handle != IP_NULL;
handle = handle->priv->next)
{
/* xid must match. */
if (handle->priv->xid_n != ipdhcpc.in_pkt->xid)
continue;
/* interface must still be active. */
for (ift = ipdhcpc.ift_head; ift != IP_NULL;
ift = ift->next)
if (ift->ifindex == (Ip_u32)handle->ifindex)
break;
if (ift == IP_NULL)
continue;
/* Possible reply match, parse it.*/
ipdhcpc_print_packet(ift,
handle,
ipdhcpc.in_pkt,
IP_TRUE,
ipdhcpc.in_pkt_len);
ipdhcpc_reply_input(ift, handle);
goto next;
}
/* Check our interfaces for current leases. */
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->handle && ift->handle->priv->xid_n == ipdhcpc.in_pkt->xid)
{
/* Possible reply match, parse it. */
ipdhcpc_print_packet(ift,
ift->handle,
ipdhcpc.in_pkt,
IP_TRUE,
ipdhcpc.in_pkt_len);
ipdhcpc_reply_input(ift, ift->handle);
goto next;
}
next:
IPDHCPC_UNLOCK();
}
}
/*
*===========================================================================
* ipdhcpc_if_eventcb
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_if_eventcb(char *ifname, Ip_u32 ifindex, Ip_u32 ifflags, int event, void *data)
{
Ip_err ret = IPCOM_SUCCESS;
Ipdhcpc_handle *handle;
Ip_bool isopen;
struct Ip_ifreq ifreq;
struct Ip_sockaddr_in *addrp;
char buf[50];
int bootp_mode;
int remove_addr;
Ipdhcpc_if *ift = IP_NULL;
Ip_bool renew_event = IP_FALSE;
Ip_bool inform_event = IP_FALSE;
(void)ifname;
(void)data;
switch (event)
{
case IPDHCPC_IF_EVENT_INFORM:
inform_event = IP_TRUE;
break;
case IPDHCPC_IF_EVENT_RENEW:
renew_event = IP_TRUE;
break;
case IP_EIOXUP:
case IP_EIOXDOWN:
case IP_EIOXCHANGE:
case IP_EIOXRUNNING:
case IP_EIOXSTOP:
break;
default:
return IPCOM_ERR_NOT_FOUND;
}
IPDHCPC_LOCK();
if (ipdhcpc.opened == IP_FALSE)
isopen = IP_FALSE;
else
{
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
isopen = (ift != IP_NULL);
}
IPDHCPC_UNLOCK();
if ((ifflags & (IP_IFF_RUNNING | IP_IFF_UP | IP_IFF_X_DHCPRUNNING)) == (IP_IFF_RUNNING | IP_IFF_UP | IP_IFF_X_DHCPRUNNING))
{
Ipdhcpc_ifopen conf;
/* UP with DHCP */
if (isopen)
{
if (ift != IP_NULL && ift->isdown)
{
IPCOM_LOG1(INFO,"ipdhcpc_if_eventcb :: ipdhcpc_ifrequest() interface '%d' is up."IP_LF, ifindex);
ift->isdown = IP_FALSE;
/* Do verify immediately */
if (ift->handle != IP_NULL && !ift->information_only)
{
Ipdhcpc_handle *tmp_handle;
for (tmp_handle = ipdhcpc.handle_head;tmp_handle != IP_NULL;
tmp_handle = tmp_handle->priv->next)
if (tmp_handle == ift->handle)
break;
if (!tmp_handle)
{
/* Insert first into DHCP handle list. */
ift->handle->priv->next = ipdhcpc.handle_head;
ipdhcpc.handle_head = ift->handle;
}
/**/
if (ift->handle->priv->state > IPDHCPC_STATE_SELECTING)
ipdhcpc_state_init_reboot(ift->handle);
else
ipdhcpc_state_restart(ift->handle);
}
}
else
{
if (ift->handle != IP_NULL && !renew_event &&ift->information_only)
{
/*interface's in inform mode, then inform event occurs, send INFORM message to the server*/
if (ift->handle->priv->state == IPDHCPC_STATE_INFORMING)
return IPCOM_ERR_ALREADY_STARTED;
ift->handle->priv->xid_n = (Ip_u32) ipcom_random();
ift->handle->priv->state = IPDHCPC_STATE_INFORMING;
if (IP_NULL!= data)
ift->handle->clientaddr_n =*((int*)data);
else
{
ipcom_strcpy(ifreq.ifr_name, ift->ifname);
addrp = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr;
addrp->sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(addrp, sizeof(struct Ip_sockaddr_in));
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFADDR, &ifreq) < 0 || addrp->sin_addr.s_addr == 0)
{
ret = IPCOM_ERR_FAILED;
IPCOM_LOG0 (ERR, "Seting address for INFROM is necessary"IP_LF);
}
else
ift->handle->clientaddr_n = addrp->sin_addr.s_addr;
}
ipdhcpc_output_request(ift, ift->handle, IPDHCPC_MSG_INFORM);
}
if (ift->handle != IP_NULL && renew_event)
{
if (! ift->information_only)
{
/**/
if (ift->handle->priv->state == IPDHCPC_STATE_BOUND)
{
ipdhcpc_timer_cancel (ift->handle, 1);
ipdhcpc_timer_set(ift->handle, 0, 1);
}
else
{
ret = IPCOM_ERR_PERMISSION_DENIED;
IPCOM_LOG0 (ERR, "Current state is not allowed to send RENEW"IP_LF);
}
}
else
{
ret = IPCOM_ERR_PERMISSION_DENIED;
IPCOM_LOG0 (ERR, "Current mode doesn't support RENEW"IP_LF);
}
}
}
return ret;
}
ipcom_memset(&conf, 0, sizeof(Ipdhcpc_ifopen));
conf.api_version = 1;
conf.conf[0] = IP_TAG_END;
ret = ipdhcpc_ifopen(ifindex, &conf);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR,"ipdhcpc_if_eventcb :: ipdhcpc_ifopen() failed on interface '%s', err = %d."IP_LF, ifname, ret);
return ret;
}
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
if(ift == IP_NULL)
goto fail;
/* Allocate DHCP client handle. */
if (ift->information_only)
{
/*Inform mode, inform event occurs first time
allocate handle for it.*/
ret = ipdhcpc_handle_malloc (ifindex, IPDHCPC_MODE_INFORM_STATIC, &handle);
if (ret == IPCOM_SUCCESS)
{
handle->priv->xid_n = (Ip_u32) ipcom_random();
handle->priv->state = IPDHCPC_STATE_INFORMING;
if (data != IP_NULL)
handle->clientaddr_n = *((int*)data);
else
{
/*set the primary address as default*/
ipcom_strcpy(ifreq.ifr_name, ift->ifname);
addrp = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr;
addrp->sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(addrp, sizeof(struct Ip_sockaddr_in));
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFADDR, &ifreq) < 0 || addrp->sin_addr.s_addr == 0)
{
ret = IPCOM_ERR_FAILED;
IPCOM_LOG0 (ERR, "Seting address for INFROM is necessary"IP_LF);
(void)ipdhcpc_ifclose(ifindex);
return ret;
}
else
handle->clientaddr_n = addrp->sin_addr.s_addr;
}
}
}
else
/*dynamic mode*/
ret = ipdhcpc_handle_malloc (ifindex, IPDHCPC_MODE_ALLOC_DYNAMIC, &handle);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR,"ipdhcpc_if_eventcb :: ipdhcpc_handle_malloc() failed on interface '%d', err = %d."IP_LF, ifindex, ret);
goto fail;
}
/* Initiate DHCP client request */
handle->reply_cb = ipdhcpc_reply_cb;
ret = ipdhcpc_ifrequest(ifindex, handle);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR,"ipdhcpc_if_eventcb :: ipdhcpc_ifrequest() failed on interface '%d', err = %d."IP_LF, ifindex, ret);
(void)ipdhcpc_handle_free(ifindex, handle);
goto fail;
}
/* Success. */
return IPCOM_SUCCESS;
fail:
ret = ipdhcpc_ifclose(ifindex);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR,"ipdhcpc_if_eventcb :: ipdhcpc_ifclose() failed on interface '%d', err = %d."IP_LF, ifindex, ret);
}
return ret;
}
else if (isopen)
{
if ((ifflags & IP_IFF_X_DHCPRUNNING) == IP_IFF_X_DHCPRUNNING)
{
if (ift != IP_NULL && ift->handle != IP_NULL && !ift->isdown)
{
IPCOM_LOG1(INFO,"ipdhcpc_if_eventcb :: ipdhcpc_ifrequest() interface '%d' is down."IP_LF, ifindex);
ift->isdown = IP_TRUE;
}
return ret;
}
bootp_mode = ipcom_sysvar_get_as_int("ipdhcpc.bootp_mode",IPDHCPC_BOOTP_MODE);
/* Send out DHCP RELEASE if interface is valid and we are not in BOOTP mode */
if (ift == IP_NULL || ift->handle == IP_NULL || bootp_mode)
{
ret = ipdhcpc_ifclose(ifindex);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR,"ipdhcpc_if_eventcb :: ipdhcpc_ifclose() failed on interface '%d', err = %d."IP_LF, ifindex, ret);
}
return ret;
}
else
{
if (ift->information_only)
goto fail;
if (ipcom_tmo_cancel(&ift->handle->priv->tmo) == IPCOM_ERR_PENDING)
ift->handle->priv->tmo_ignore = 1;
if (ift->handle->priv->state > IPDHCPC_STATE_SELECTING)
ipdhcpc_output_request(ift, ift->handle, IPDHCPC_MSG_RELEASE);
remove_addr = ipcom_sysvar_get_as_int("ipdhcpc.remove_leased_address",
IPDHCPC_REMOVE_LEASED_ADDRESS);
if (remove_addr > 0)
{
/* We have released the address. Remove it on the interface. */
ipcom_strcpy(ifreq.ifr_name, ift->ifname);
addrp = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr;
addrp->sin_family = IP_AF_INET;
addrp->sin_len = sizeof(struct Ip_sockaddr_in);
addrp->sin_addr.s_addr = ift->handle->info.ipaddr_n;
if (addrp->sin_addr.s_addr == 0)
goto fail;
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: removed address = %s",
ift->ifname,
ipcom_inet_ntop(IP_AF_INET,
&addrp->sin_addr.s_addr,
buf,
sizeof(buf)));
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCDIFADDR, &ifreq) < 0)
{
IP_PANIC2();
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ifopen(): ipcom_ioctl(IP_SIOCDIFADDR) failed, errno = %d",
ifreq.ifr_name, ipcom_errno);
goto fail;
}
}
}
ret = ipdhcpc_ifclose(ifindex);
if (ret != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR,"ipdhcpc_if_eventcb :: ipdhcpc_ifclose() failed on interface '%d', err = %d."IP_LF, ifindex, ret);
}
}
return ret;
}
/*
*===========================================================================
* ipdhcpc_run
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipdhcpc_run(void)
{
struct Ip_if_nameindex *oifrs, *ifrs;
struct Ip_ifreq ifreq;
Ip_u16 ifru_flags;
/* Search for interfaces preconfigured for dhcp */
ifrs = ipcom_if_nameindex();
if (ifrs == IP_NULL || ipcom_strlen(ifrs->if_name) >= IP_IFNAMSIZ)
{
ipcom_if_freenameindex(ifrs);
return IPCOM_ERR_FAILED;
}
for (oifrs = ifrs; ifrs && ifrs->if_name != IP_NULL; ifrs++)
{
/* Check if interface is up. */
strcpy(ifreq.ifr_name, ifrs->if_name);
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFFLAGS,(char *)&ifreq) < 0)
{
IPCOM_LOG2(ERR,
"ipdhcpc[%s] :: ipcom_ioctl(SIOCGIFFLAGS) failed, errno =%d",
ifrs->if_name, ipcom_errno);
continue;
}
if (IP_BIT_ISFALSE(ifreq.ifr_ifru.ifru_flags, IP_IFF_UP))
continue;
ifru_flags = ifreq.ifr_ifru.ifru_flags;
/* Check if DHCP is enabled for this interface. */
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCXGDHCPRUNNING,(char *)&ifreq) < 0)
{
IPCOM_LOG2(ERR,
"ipdhcpc[%s] :: ipcom_ioctl(IP_SIOCXGDHCPRUNNING) failed, errno =%d",
ifrs->if_name, ipcom_errno);
continue;
}
if (ifreq.ifr_ifru.ifru_opt == 1)
{
/* Start DHCP client on interface that has DHCPRUNNING flag set. */
(void)ipdhcpc_if_eventcb((char *)ifrs->if_name, ifrs->if_index,
ifru_flags | IP_IFF_X_DHCPRUNNING,
IP_EIOXCHANGE, IP_NULL);
}
}
ipcom_if_freenameindex(oifrs);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcpc_reply_cb
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL int
ipdhcpc_reply_cb(Ipdhcpc_handle *handle, int event)
{
Ip_err ret = IPDHCPC_REPLY_CB_SUCCESS;
/* Let user application look at/act on the event first*/
if(ipdhcpc_reply_callback(handle, event) != IPCOM_SUCCESS)
{
return IPDHCPC_REPLY_CB_ERROR;
}
switch(event)
{
case IPDHCPC_REPLY_CB_EVENT_OFFER:
/* Informational only in this implementation. */
IPCOM_LOG1(INFO, "ipdhcpc_reply_cb :: received OFFER on interface '%d'.", handle->ifindex);
break;
case IPDHCPC_REPLY_CB_EVENT_ACK:
case IPDHCPC_REPLY_CB_EVENT_BOOTREPLY:
break;
case IPDHCPC_REPLY_CB_EVENT_NAK:
/* Informational only in this implementation. */
IPCOM_LOG1(INFO, "ipdhcpc_reply_cb :: received NAK on interface '%d'.", handle->ifindex);
break;
case IPDHCPC_REPLY_CB_EVENT_EXPIRE:
/* Informational only in this implementation. */
IPCOM_LOG1(INFO, "ipdhcpc_reply_cb :: Lease expired on interface '%d'.", handle->ifindex);
break;
case IPDHCPC_REPLY_CB_EVENT_FAIL_RETRY:
IPCOM_LOG1(INFO, "ipdhcpc_reply_cb :: Retries expired on interface '%d'.", handle->ifindex);
(void)ipdhcpc_ifclose(handle->ifindex);
break;
case IPDHCPC_REPLY_CB_EVENT_FAIL_IFLOST:
IPCOM_LOG1(INFO, "ipdhcpc_reply_cb :: Interface '%d' lost.", handle->ifindex);
break;
default:
(void)ipdhcpc_handle_free(handle->ifindex, handle);
break;
}
return ret;
}
/*
*===========================================================================
* ipdhcpc_route_mod
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC int
ipdhcpc_route_mod(Ip_u32 ipaddr_n, Ip_bool add)
{
struct Ip_ortentry *ort;
struct Ip_sockaddr_in *sin;
int ret;
ort = ipcom_malloc(sizeof(struct Ip_ortentry));
if(ort == IP_NULL)
{
return IPCOM_ERR_FAILED;
}
memset(ort, 0, sizeof(struct Ip_ortentry));
/*!!ort.rt_flags = IPNET_RTF_UP | IPNET_RTF_GATEWAY | IPNET_RTF_STATIC;*/
ort->rt_flags = 0x1 | 0x2 | 0x800;
ort->rt_dst.sa_family = IP_AF_INET;
ort->rt_gateway.sa_family = IP_AF_INET;
sin = (struct Ip_sockaddr_in *)&(ort->rt_gateway);
IPCOM_SA_LEN_SET(sin, sizeof(struct Ip_sockaddr_in));
sin->sin_addr.s_addr = ipaddr_n;
ret = ipcom_socketioctl(ipdhcpc.fd, add ? IP_SIOCADDRT : IP_SIOCDELRT, ort);
if (ret != 0 && ipcom_errno == IP_ERRNO_EEXIST)
ret = 0; /* ignore route already exists error. */
ipcom_free(ort);
return ret;
}
/*
*===========================================================================
* ipdhcpc_timer_cancel
*===========================================================================
* Description: Cancel timers
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_timer_cancel(Ipdhcpc_handle *handle, int which)
{
char ifname[IP_IFNAMSIZ + 1];
ip_assert(handle != IP_NULL);
ip_assert(handle->priv != IP_NULL);
ip_assert(which == 0 || which == 1 || which == 2);
(void)ipcom_if_indextoname(handle->ifindex, ifname);
if (which == 0 || which == 1)
{
handle->priv->tmo_ignore = 0;
if (IPCOM_TMO_PENDING(&handle->priv->tmo))
{
IPCOM_LOG2(DEBUG2,
"ipdhcpc[%s] :: cancelling timer T1 (0x%lx)",
ifname, &handle->priv->tmo);
if (ipcom_tmo_cancel(&handle->priv->tmo) == IPCOM_ERR_PENDING)
handle->priv->tmo_ignore = 1;
}
}
if (which == 0 || which == 2)
{
handle->priv->tmo2_ignore = 0;
if (IPCOM_TMO_PENDING(&handle->priv->tmo_t2))
{
IPCOM_LOG2(DEBUG2,
"ipdhcpc[%s] :: cancelling timer T2 (0x%lx)",
ifname, &handle->priv->tmo_t2);
if (ipcom_tmo_cancel(&handle->priv->tmo_t2) == IPCOM_ERR_PENDING)
handle->priv->tmo2_ignore = 1;
}
}
}
/*
*===========================================================================
* ipdhcpc_timer_set
*===========================================================================
* Description: Set T1 timer
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_timer_set(Ipdhcpc_handle *handle, Ip_u32 msec, int which)
{
char ifname[IP_IFNAMSIZ + 1];
ip_assert(which == 1 || which == 2);
(void)ipcom_if_indextoname(handle->ifindex, ifname);
IPCOM_LOG4(DEBUG2,
"ipdhcpc[%s] :: setting timer %s (0x%lx) to %ld msec",
ifname,
which == 1 ? "T1" : "T2",
which == 1 ? &handle->priv->tmo : &handle->priv->tmo_t2,
msec);
(void)ipcom_tmo_request(which == 1 ? &handle->priv->tmo : &handle->priv->tmo_t2,
ipdhcpc_tmo_cb, handle,
msec);
}
/*
*===========================================================================
* ipdhcpc_ifclose_all
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_ifclose_all(void)
{
Ipdhcpc_if *ift;
Ipdhcpc_if *ift_next;
struct Ip_ifreq ifreq;
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift_next)
{
ift_next = ift->next;
/* Disable DHCP running on this interface. */
strcpy(ifreq.ifr_name, ift->ifname);
ifreq.ifr_ifru.ifru_opt = 0;
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCXSDHCPRUNNING, &ifreq) < 0)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipcom_ioctl(IP_SIOCXSDHCPRUNNING) failed, errno =%d",
ift->ifname, ipcom_errno);
continue;
}
}
}
/*
*===========================================================================
* ipdhcpc_if_has_addr
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_route_init_addrs(void *msg, Ip_u32 addrs, struct Ip_sockaddr **sas)
{
int i;
Ip_size_t offset = 0;
for (i = 0; i < IPNET_RTAX_MAX; i++)
{
if (IP_BIT_ISSET(addrs, 1 << i))
{
sas[i] = (struct Ip_sockaddr *) &((Ip_u8 *) msg)[offset];
offset += IPCOM_SA_LEN_GET(sas[i]);
}
else
sas[i] = IP_NULL;
}
}
/*
*===========================================================================
* ipdhcpc_if_has_addr
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_bool
ipdhcpc_if_has_addr(Ip_u32 ifindex, Ip_u32 ipaddr_n)
{
int name[6];
int ret = IP_FALSE;
Ip_u8 *if_data = IP_NULL;
struct Ipnet_ifa_msghdr *ifa;
struct Ipnet_if_msghdr *ifm;
Ip_size_t if_data_len;
Ip_size_t if_data_offset;
Ip_size_t offset;
struct Ip_sockaddr_in *addrs[IPNET_RTAX_MAX];
name[0] = IP_CTL_NET;
name[1] = IP_AF_ROUTE;
name[2] = 0;
name[3] = IP_AF_INET;
name[4] = IP_NET_RT_IFLIST;
name[5] = ifindex;
/* Get length of data */
if (ipcom_sysctl(name, 6, IP_NULL, &if_data_len, IP_NULL, 0) < 0)
{
IPCOM_LOG1(ERR, "ipdhcpc_if_has_addr: ipcom_sysctl() failed: %s",
ipcom_strerror(ipcom_errno));
goto cleanup;
}
/* Allocate space for data */
if_data = ipcom_malloc(if_data_len);
if (if_data == IP_NULL)
{
IPCOM_LOG0(ERR, "ipdhcpc_if_has_addr: out of memory");
goto cleanup;
}
/* Get the actual data */
if (ipcom_sysctl(name, 6, if_data, &if_data_len, IP_NULL, 0) < 0)
{
IPCOM_LOG1(ERR, "ipdhcpc_if_has_addr: ipcom_sysctl() failed: %s",
ipcom_strerror(ipcom_errno));
ret = -1;
goto cleanup;
}
ifm = (struct Ipnet_if_msghdr *) &if_data[0];
if_data_offset = ifm->ifm_msglen;
offset = 0;
while (offset < if_data_len - if_data_offset)
{
ifa = (struct Ipnet_ifa_msghdr *) &if_data[if_data_offset + offset];
ipdhcpc_route_init_addrs(ifa + 1, ifa->ifam_addrs, (struct Ip_sockaddr **) addrs);
/* Only AF_INET addrs */
if (ifa->ifam_type != IPNET_RTM_NEWADDR || addrs[IPNET_RTAX_IFA]->sin_family != IP_AF_INET)
break;
if (addrs[IPNET_RTAX_IFA]->sin_addr.s_addr == ipaddr_n)
{
/* Found the requested address */
ret = IP_TRUE;
}
offset += ifa->ifam_msglen;
}
cleanup:
ipcom_free(if_data);
return(ret);
}
/*
*===========================================================================
* ipdhcpc_parse_cidarray
*===========================================================================
* Description: If the type of client identifier is uuid, then analyze the client identifier
and length of client identifier
* Parameters:client_id: client identifier, length: the length of client identifier
* Returns:
*
*/
IP_STATIC void
ipdhcpc_parse_cidarray(char *client_id, Ip_u8 *length)
{
Ip_u32 len;
Ip_u32 uuid_len = 0;
Ip_u8 cid[IPDHCPC_MAX_CID_LEN+1] = {0};
char *ptr;
char *cstr = client_id;
char *last;
if( client_id == IP_NULL )
return;
*length = 0;
len = (Ip_u32)ipcom_strlen(client_id);
if(len == 0)
{
return;
}
if( ipdhcpc.cid_uuid_type == IP_FALSE )
{
*length = (Ip_u8)len;
return;
}
ptr = ipcom_strtok_r(cstr, IPDHCPC_CID_CFG_DELIMITER, &last);
while (ptr != IP_NULL)
{
uuid_len = (Ip_u32)ipcom_strlen(ptr);
if(uuid_len != 2)
break;
if( (ipcom_isxdigit((int)ptr[0])) &&(ipcom_isxdigit((int)ptr[1])))
{
cid[*length] = (Ip_u8)ipcom_strtol(ptr, IP_NULL, 16);
*length = *length + 1;
if( *length == IPDHCPC_MAX_CID_LEN )
{
IPCOM_LOG0(WARNING, "ipdhcpc: client id is too long.");
break;
}
ptr = ipcom_strtok_r(IP_NULL, IPDHCPC_CID_CFG_DELIMITER, &last);
}
else
{
break;
}
}
if(*length == 0)
return;
ipcom_memcpy(client_id,cid, *length);
return;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcpc_ifopen
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_ifopen(Ip_u32 ifindex, Ipdhcpc_ifopen * config)
{
Ip_err retval;
Ipdhcpc_if *ift = IP_NULL;
char buf[50];
char *list;
struct Ip_ifreq ifreq;
Ip_size_t sz = sizeof(buf);
IPDHCPC_LOCK();
if (ipdhcpc.opened == IP_FALSE)
IPDHCPC_LEAVE(IPCOM_ERR_NOT_OPENED);
if (config->api_version > 1)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_VERSION);
if (config->conf[0] != IP_TAG_END)
IPDHCPC_LEAVE(IPCOM_ERR_UNSUPPORTED_TAG);
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
{
ift->num_opened++;
ift = IP_NULL; /* trick to not release the interface. */
IPDHCPC_LEAVE(IPCOM_SUCCESS);
}
ift = ipcom_malloc(sizeof(Ipdhcpc_if));
if (ift == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
/* Init interface structure. */
ipcom_memset(ift, 0, sizeof(Ipdhcpc_if));
ift->ifindex = ifindex;
(void)ipcom_if_indextoname(ift->ifindex, ift->ifname);
/*Get the mode of the interface*/
ipcom_snprintf(buf,sizeof(buf), "ipdhcpc.if.%s.information_only", ift->ifname);
if ((list = ipcom_sysvar_get(buf,IP_NULL,IP_NULL)) !=IP_NULL)
{
if (ipcom_strcasecmp(list, "enable")==0)
ift->information_only = IP_TRUE;
ipcom_free (list);
}
/* Check if we have a client identifier */
ipcom_snprintf(buf,sizeof(buf), "ipdhcpc.%s.option.client_identifier", ift->ifname);
if ((ift->clientid = ipcom_sysvar_get(buf,IP_NULL,IP_NULL)) == IP_NULL)
ift->clientid = ipcom_sysvar_get("ipdhcpc.option.client_identifier", IP_NULL, IP_NULL);
(void)ipcom_sysvar_get("ipdhcpc.client_identifier_uuid", buf, &sz);
ipdhcpc.cid_uuid_type = ipcom_strcmp(buf,"true") == 0 ? IP_TRUE : IP_FALSE;
ipdhcpc_parse_cidarray(ift->clientid, &(ift->clientid_len));
if (ipdhcpc.cid_uuid_type == IP_FALSE &&ift->clientid && ipcom_strlen(ift->clientid) > 255)
{
IPCOM_LOG1(ERR, "ipdhcpc[%s] :: client id too long", ift->ifname);
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_CONFIG);
}
/* Make sure that IPv4 address auto configuration is disabled on this
* interface.
*/
{
const char* str_ac = "inet.AutoConf";
char *name;
int val;
const Ipcom_cmd_int_str_map map[] = {
{ IP_TRUE , "yes" },
{ IP_FALSE, "no" },
{ IP_TRUE , "enable" },
{ IP_TRUE , "enabled" },
{ IP_FALSE, "disable" },
{ IP_FALSE, "disabled" },
{ IP_TRUE , "on" },
{ IP_FALSE, "off" },
{ IP_FALSE, "false" },
{ IP_FALSE, "true" },
{ IP_NULL, 0 }
};
name = ipcom_malloc(ipcom_strlen(str_ac) + IP_IFNAMSIZ + 1);
if (name == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
/* Check interface specific autoconf setting */
ipcom_sprintf(name, "%s.%s", ift->ifname, str_ac);
if (ipcom_sysvar_get_descriptive_int0(name, 0, &val, map) < 0)
{
/* Check global autoconf setting */
ipcom_sprintf(name, "%s.%s", "ipnet", str_ac);
if (ipcom_sysvar_get_descriptive_int0(name, 0, &val, map) < 0)
goto done;
}
if (val == 1)
{
/* AutoConf is enabled. Turn it off */
IPCOM_LOG1(INFO, "ipdhcpc[%s] :: Disabling IPv4 address auto configuration", ift->ifname);
ipcom_sprintf(name, "%s.%s", ift->ifname, str_ac);
(void)ipcom_sysvar_set(name,"no",IPCOM_SYSVAR_FLAG_OVERWRITE);
/* Turn interface down/up to make the setting take effect */
ipcom_memset(&ifreq, 0, sizeof(ifreq));
ipcom_strcpy(ifreq.ifr_name, ift->ifname);
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFFLAGS, &ifreq) < 0)
{
IP_PANIC2();
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ifopen(): ipcom_ioctl(IP_SIOCGIFFLAGS) failed, errno = %d",
ifreq.ifr_name, ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
IP_BIT_CLR(ifreq.ip_ifr_flags,(Ip_u16)IP_IFF_UP);
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCSIFFLAGS, &ifreq) < 0)
{
IP_PANIC2();
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ifopen(): ipcom_ioctl(IP_SIOCSIFFLAGS,down) failed, errno = %d",
ifreq.ifr_name, ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
IP_BIT_SET(ifreq.ip_ifr_flags,IP_IFF_UP);
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCSIFFLAGS, &ifreq) < 0)
{
IP_PANIC2();
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ifopen(): ipcom_ioctl(IP_SIOCSIFFLAGS,up) failed, errno = %d",
ifreq.ifr_name, ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
}
done:
ipcom_free(name);
}
if (ipdhcpc_remove_existing_addresses(ift->ifname) != IPCOM_SUCCESS)
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
/* Get interface hardware address and length. */
(void)ipcom_if_indextoname(ift->ifindex, ifreq.ifr_name);
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFLLADDR, &ifreq) < 0)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_ifopen(): ipcom_ioctl(IP_SIOCGIFLLADDR) failed, errno = %d",
ifreq.ifr_name, ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
#ifdef IPCOM_USE_SA_LEN
ift->hlen = (Ip_u8)IP_MIN(sizeof(ift->chaddr), ifreq.ip_ifr_addr.sa_len);
#else
ift->hlen = 6;
#endif
if (ift->hlen > sizeof(ifreq.ip_ifr_addr.sa_data))
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
ipcom_memcpy(ift->chaddr, ifreq.ip_ifr_addr.sa_data, ift->hlen);
/* Insert first into interface list. */
ift->next = ipdhcpc.ift_head;
ipdhcpc.ift_head = ift;
/* Increment interface counter */
ipdhcpc.open_interfaces++;
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: DHCP opened", ift->ifname);
/* Success. */
retval = IPCOM_SUCCESS;
ift = IP_NULL; /* trick to not release the interface. */
leave:
if (ift != IP_NULL)
{
if (ift->clientid != IP_NULL)
ipcom_free(ift->clientid);
ipcom_free(ift);
}
IPDHCPC_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_ifclose
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_ifclose(Ip_u32 ifindex)
{
Ip_err retval;
Ipdhcpc_if **iftp;
Ipdhcpc_if *ift;
Ipdhcpc_handle *handle;
if (ipdhcpc.opened == IP_FALSE)
IPDHCPC_LEAVE(IPCOM_ERR_NOT_OPENED);
for (ift = IP_NULL, iftp = &ipdhcpc.ift_head;(*iftp) != IP_NULL;
iftp = &(*iftp)->next)
if ((*iftp)->ifindex == ifindex)
{
ift = *iftp;
break;
}
if (ift == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_INTERFACE);
if (ift->num_opened > 0)
{
ift->num_opened--;
IPDHCPC_LEAVE(IPCOM_SUCCESS);
}
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: DHCP closed", ift->ifname);
/* Remove lease if any. */
retval = ipdhcpc_ifunlease_locked(ift, &handle);
if (retval == IPCOM_SUCCESS)
(void)ipdhcpc_handle_free(ifindex, handle);
/* Remove Client ID entry */
if (ift->clientid != IP_NULL)
ipcom_free(ift->clientid);
/* Remove entry from list and free it. */
*iftp = ift->next;
ipcom_free(ift);
/* Decrement interface counter */
ipdhcpc.open_interfaces--;
retval = IPCOM_SUCCESS;
leave:
return retval;
}
/*
*===========================================================================
* ipdhcpc_if_isopen
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_bool
ipdhcpc_if_isopen(Ip_u32 ifindex)
{
Ipdhcpc_if *ift;
Ip_bool retval;
IPDHCPC_LOCK();
if (ipdhcpc.opened == IP_FALSE)
IPDHCPC_LEAVE(IP_FALSE);
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
if (ift == IP_NULL)
IPDHCPC_LEAVE(IP_FALSE);
retval = IP_TRUE;
leave:
IPDHCPC_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_handle_malloc
*===========================================================================
* Description: Mallocate and initialize a DHCP client request packet for
* use with ipdhcpc_ifrequest().
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_handle_malloc(Ip_u32 ifindex, int mode, Ipdhcpc_handle **handlep)
{
Ipdhcpc_if *ift;
Ipdhcpc_handle *handle = IP_NULL;
Ip_err retval = IPCOM_ERR_FAILED;
char ifname[IP_IFNAMSIZ];
char *opt = IP_NULL;
char *opt_ptr;
char *next;
char *last;
char buf[50];
Ip_size_t opt_len;
Ip_u16 port;
Ip_u32 bootp_mode;
char opt_buf[IPDHCPC_MAX_OPTION_SIZE];
Ip_u32 ipaddress_lease_time = 0;
Ip_u32 htonl_tmp = 0;
ip_assert(handlep != IP_NULL);
if (mode < IPDHCPC_MODE_ALLOC_DYNAMIC ||
mode > IPDHCPC_MODE_INFORM_STATIC)
return IPCOM_ERR_INVALID_ARG;
IPDHCPC_LOCK();
/* Look up the interface. */
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
if (ift == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_INTERFACE);
/* Mallocate DHCP handle.*/
handle = ipcom_malloc(sizeof(Ipdhcpc_handle) +
sizeof(Ipdhcpc_handle_priv));
if (handle == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
/* Initialize DHCP handle. */
ipcom_memset(handle, 0, sizeof(Ipdhcpc_handle) + sizeof(Ipdhcpc_handle_priv));
/* Mallocate space for user options */
handle->options = ipcom_malloc(IPDHCPC_MAX_OPTION_SIZE);
if (handle->options == IP_NULL)
{
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
}
ipcom_memset(handle->options, 0, IPDHCPC_MAX_OPTION_SIZE);
/* Mallocate space for info options */
handle->info.options = ipcom_malloc(IPDHCPC_MAX_OPTION_SIZE);
if (handle->info.options == IP_NULL)
{
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
}
ipcom_memset(handle->info.options, 0, IPDHCPC_MAX_OPTION_SIZE);
/* Initialize DHCP handle. */
handle->priv = (Ipdhcpc_handle_priv *)((Ip_u8 *) handle +
sizeof(Ipdhcpc_handle));
handle->ifindex = ifindex;
handle->serveraddr_n = IP_INADDR_BROADCAST;
opt= ipcom_sysvar_get ("ipdhcpc.server", IP_NULL, IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0&& ift->information_only)
{
struct Ip_in_addr serveraddr;
if (ipcom_inet_pton(IP_AF_INET, opt, &serveraddr) == 1)
handle->serveraddr_n = serveraddr.s_addr;
ipcom_free(opt);
opt = IP_NULL;
}
if (opt)
ipcom_free (opt);
port = (Ip_u16)ipcom_sysvar_get_as_int("ipdhcpc.server_port", IPDHCPC_SERVER_PORT);
handle->serverport_n = ip_htons(port);
handle->magic_cookie = IPDHCPC_MAGIC_COOKIE;
IPCOM_TMO_INIT(&handle->priv->tmo);
IPCOM_TMO_INIT(&handle->priv->tmo_t2);
handle->priv->mode = mode;
/* Fill in valid BOOTP/DHCP options */
ipcom_memset(opt_buf, 0, IPDHCPC_MAX_OPTION_SIZE);
opt = ipcom_sysvar_get("ipdhcpc.option.hostname",IP_NULL,IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
retval = ipdhcpc_option_add(handle,
IPDHCPC_OPTCODE_HOST_NAME,
(Ip_u8 *)opt,
(Ip_u32)opt_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
ipcom_free(opt);
opt = IP_NULL;
}
if(opt)
ipcom_free(opt);
opt = ipcom_sysvar_get("ipdhcpc.option.netmask",IP_NULL,IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len >= 7)
{
opt_len = 4;
if (ipcom_inet_pton(IP_AF_INET, opt, opt_buf) != 1)
{
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
retval = ipdhcpc_option_add(handle,
IPDHCPC_OPTCODE_SUBNET_MASK,
(Ip_u8 *)opt_buf,
(Ip_u32)opt_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
ipcom_free(opt);
opt = IP_NULL;
ipcom_memset(opt_buf, 0, IPDHCPC_MAX_OPTION_SIZE);
}
if(opt)
{
ipcom_free(opt);
opt = IP_NULL;
}
/* Fill in DHCP only options */
bootp_mode = ipcom_sysvar_get_as_int("ipdhcpc.bootp_mode",IPDHCPC_BOOTP_MODE);
if (!bootp_mode)
{
(void)ipcom_if_indextoname(ifindex,ifname);
ipcom_snprintf(buf, sizeof(buf), "ipdhcpc.%s.option.requested_options", ifname);
if ((opt = ipcom_sysvar_get(buf,IP_NULL,IP_NULL)) == IP_NULL)
opt = ipcom_sysvar_get("ipdhcpc.option.requested_options",IP_NULL,IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
opt_ptr = opt;
for (opt_len = 0; opt != IP_NULL && *opt != 0; opt = next, opt_len++)
{
if (opt_len >= IPDHCPC_MAX_OPTION_SIZE)
{
IPCOM_LOG1(ERR, "ipdhcpc :: User defined requested_options is too long. Configure error.",ift->ifname);
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
}
next = ipcom_strchr(opt, ',');
if (next != IP_NULL)
*next++ = 0;
opt_buf[opt_len] = (Ip_u8)ipcom_atoi(opt);
}
retval = ipdhcpc_option_add(handle,
IPDHCPC_OPTCODE_DHCP_PARAMETER_REQUEST_LIST,
(Ip_u8 *)opt_buf,
(Ip_u32)opt_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
ipcom_free(opt_ptr);
opt = IP_NULL;
ipcom_memset(opt_buf, 0, IPDHCPC_MAX_OPTION_SIZE);
}
if(opt)
ipcom_free(opt);
opt = ipcom_sysvar_get("ipdhcpc.option.vendor_class_id",IP_NULL,IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
retval = ipdhcpc_option_add(handle,
IPDHCPC_OPTCODE_VENDOR_CLASS_IDENTIFIER,
(Ip_u8 *)opt,
(Ip_u32)opt_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
ipcom_free(opt);
opt = IP_NULL;
}
if(opt)
ipcom_free(opt);
/* fill in vendor identifying vendor class option */
opt = ipcom_sysvar_get("ipdhcpc.option.vendor_identifying_vendor_class",IP_NULL,IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
Ip_u8 *vivc_options = (Ip_u8 *)opt_buf;
char *vivc_opt = opt;
char *vivc_data;
Ip_size_t vivc_data_len;
Ip_u32 vivc_enterprise_number;
char *vivc_enterprise_string;
Ip_size_t vivc_enterprise_string_len;
Ip_size_t each_vivc_data_len = 0;
/*
* Expected format: <enterprise number>=<data1>;<enterprise number>=<data2>;...
*/
for (opt_len = 0; opt != IP_NULL && *opt != 0; opt = next)
{
/* get enterprise number */
vivc_enterprise_string = ipcom_strtok_r(opt, "=;", &last);
if(vivc_enterprise_string == IP_NULL)
break;
vivc_enterprise_string_len = ipcom_strlen(vivc_enterprise_string);
if (vivc_enterprise_string_len == 0)
break;
vivc_enterprise_number = ip_htonl(ipcom_atoi(vivc_enterprise_string));
/* get option data */
vivc_data = ipcom_strtok_r(IP_NULL, "=;", &last);
if (vivc_data == IP_NULL)
break;
vivc_data_len = ipcom_strlen(vivc_data);
if (vivc_data_len == 0)
break;
/* fill in enterprise number */
each_vivc_data_len = sizeof(Ip_u32) + sizeof(Ip_u8) + vivc_data_len;
if (opt_len + each_vivc_data_len > IPDHCPC_MAX_OPTION_SIZE)
{
IPCOM_LOG1(ERR, "ipdhcpc :: User defined vendor_identifying_vendor_class is too long. Configure error.",ift->ifname);
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
}
ipcom_memcpy(vivc_options, &vivc_enterprise_number, sizeof(Ip_u32));
vivc_options += sizeof(Ip_u32);
/* fill in data length */
ipcom_memcpy(vivc_options, &vivc_data_len, sizeof(Ip_u8));
vivc_options += sizeof(Ip_u8);
/* fill in vendor specific data */
ipcom_strncpy((char *)vivc_options, vivc_data, vivc_data_len);
vivc_options += vivc_data_len;
/* bump total option length (enterprise # + data length field + data) */
opt_len += each_vivc_data_len;
/* next option string is pointed by last fix for defect WIND00169820 */
next = last;
}
retval = ipdhcpc_option_add(handle,
IPDHCPC_OPTCODE_VI_VENDOR_CLASS,
(Ip_u8 *)opt_buf,
(Ip_u32)opt_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
ipcom_free(vivc_opt);
opt = IP_NULL;
ipcom_memset(opt_buf, 0, IPDHCPC_MAX_OPTION_SIZE);
}
if(opt)
ipcom_free(opt);
/* Fill in any user specified options. Parse this sysvar just before adding
* the END option so that in the case of duplicate options, the fully supported
* options specified above take precidence.
*/
opt = ipcom_sysvar_get("ipdhcpc.option.user_specified",IP_NULL,IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
Ip_u8 opt_data_len;
char *opt_data_string;
Ip_size_t opt_data_string_len;
Ip_u8 opt_code;
char *opt_code_string;
Ip_size_t opt_code_string_len;
int i;
unsigned int k;
opt_ptr = opt;
opt_len = ipcom_strlen(opt);
/*
* Expected format: <option code1>=<option data1>;<option code2>=<option data2>;...
*/
for (next = IP_NULL; opt != IP_NULL && *opt != 0; opt = next)
{
/* get option code */
opt_code_string = ipcom_strtok_r(opt, "=;", &last);
if(opt_code_string == IP_NULL)
break;
opt_code_string_len = ipcom_strlen(opt_code_string);
if (opt_code_string_len <= 0)
break;
opt_code = (Ip_u8)ipcom_atoi(opt_code_string);
/* get option data */
opt_data_string = ipcom_strtok_r(IP_NULL, "=;", &last);
if (opt_data_string == IP_NULL)
break;
opt_data_string_len = ipcom_strlen(opt_data_string);
if (opt_data_string_len <= 0)
break;
/* get option length */
opt_data_len = (Ip_u8)(opt_data_string_len / 2);
if (opt_data_len > IPDHCPC_MAX_OPTION_SIZE)
{
IPCOM_LOG1(ERR, "ipdhcpc :: User defined user_specified is too long. Configure error.",ift->ifname);
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
}
/* copy over option data */
for (i = 0; ipcom_sscanf(opt_data_string + 2*i, "%02x", &k); i++)
opt_buf[i] = (Ip_u8)k;
retval = ipdhcpc_option_add(handle,
opt_code,
(Ip_u8 *)opt_buf,
opt_data_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
/* if not succeed, continue. fix for defect WIND00169820 */
ipcom_memset(opt_buf, 0, IPDHCPC_MAX_OPTION_SIZE);
/* next option string is pointed by last fix for defect WIND00169820 */
next = last;
if ((next - opt_ptr) >= (int)opt_len)
break;
}
ipcom_free(opt_ptr);
opt = IP_NULL;
} /* End of user specified options */
if(opt)
{
ipcom_free(opt);
opt = IP_NULL;
}
ipaddress_lease_time = (Ip_u32) ipcom_sysvar_get_as_int("ipdhcpc.option.lease_time", 0);
if (ipaddress_lease_time != 0)
{
opt_len = 4;
htonl_tmp = ip_htonl(ipaddress_lease_time);
retval = ipdhcpc_option_add(handle,
IPDHCPC_OPTCODE_DHCP_LEASE_TIME,
(Ip_u8 *) &htonl_tmp,
(Ip_u32)opt_len);
if (retval != IPCOM_SUCCESS)
{
IPDHCPC_LEAVE(retval);
}
}
} /* End of DHCP only options */
/* fill in filename / hostname */
opt = ipcom_sysvar_get("ipdhcpc.boot_filename", IP_NULL, IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
if (opt_len < sizeof(handle->file))
ipcom_strncpy((char *)handle->file, opt, opt_len);
ipcom_free(opt);
opt = IP_NULL;
}
if(opt)
ipcom_free(opt);
opt = ipcom_sysvar_get("ipdhcpc.server_hostname", IP_NULL, IP_NULL);
opt_len = opt? ipcom_strlen(opt) : 0;
if (opt_len > 0)
{
if (opt_len < sizeof(handle->sname))
ipcom_strncpy((char *)handle->sname, opt, opt_len);
ipcom_free(opt);
opt = IP_NULL;
}
if(opt)
ipcom_free(opt);
switch(mode)
{
case IPDHCPC_MODE_ALLOC_DYNAMIC:
handle->priv->state = IPDHCPC_STATE_INIT;
break;
case IPDHCPC_MODE_REUSE_DYNAMIC:
break;
case IPDHCPC_MODE_RELEASE_DYNAMIC:
break;
case IPDHCPC_MODE_INFORM_STATIC:
break;
}
/* Insert first into DHCP handle list. */
handle->priv->next = ipdhcpc.handle_head;
ipdhcpc.handle_head = handle;
/* Success. */
*handlep = handle;
IPDHCPC_UNLOCK();
return IPCOM_SUCCESS;
leave:
if (IP_NULL != handle)
{
if (IP_NULL != handle->options)
{
ipcom_free(handle->options);
}
ipcom_free(handle);
}
if (IP_NULL != opt)
{
ipcom_free(opt);
}
IPDHCPC_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_handle_free
*===========================================================================
* Description: Free a DHCP client handle.
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_handle_free(Ip_u32 ifindex, Ipdhcpc_handle *handle)
{
Ipdhcpc_handle **handlep;
Ipdhcpc_if *ift;
(void)ifindex;
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->handle == handle)
ift->handle = IP_NULL;
ip_assert(handle != IP_NULL);
IPDHCPC_LOCK();
/* Remove entry from list and free it. */
for (handlep = &ipdhcpc.handle_head;(*handlep) != IP_NULL;
handlep = &(*handlep)->priv->next)
if ((*handlep) == handle)
{
*handlep = handle->priv->next;
break;
}
/*clear global flag of the option when the option set on this interface*/
if((handle->option_confvar & IPDHCPC_OPTION_NTPSERVER_CONF) != IP_FALSE)
{
option_confvar = option_confvar & (0xFF ^ IPDHCPC_OPTION_NTPSERVER_CONF);
}
if((handle->option_confvar&IPDHCPC_OPTION_DNSSERVER_CONF) != IP_FALSE)
{
option_confvar = option_confvar & (0xFF ^ IPDHCPC_OPTION_DNSSERVER_CONF);
}
if((handle->option_confvar&IPDHCPC_OPTION_DOMAIN_CONF) != IP_FALSE)
{
option_confvar = option_confvar & (0xFF ^ IPDHCPC_OPTION_DOMAIN_CONF);
}
/* Free user option buffer */
if (handle->info.options != IP_NULL)
ipcom_free(handle->info.options);
if (handle->options != IP_NULL)
{
ipcom_free(handle->options);
}
/* Free handle */
ipcom_free(handle);
IPDHCPC_UNLOCK();
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcpc_handle_free_pending
*===========================================================================
* Description: Free all DHCP client handles.
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_handle_free_pending(void)
{
Ipdhcpc_handle *handle;
Ipdhcpc_handle *handle_next;
IPDHCPC_LOCK();
/* Remove entry from list and free it. */
for (handle = ipdhcpc.handle_head; handle != IP_NULL; handle = handle_next)
{
handle_next = handle->priv->next;
/* Cancel timers */
ipdhcpc_timer_cancel(handle, 0);
/* Free user option buffer */
if (handle->info.options != IP_NULL)
{
ipcom_free(handle->info.options);
}
if (handle->options != IP_NULL)
{
ipcom_free(handle->options);
}
/* Free handle */
ipcom_free(handle);
}
IPDHCPC_UNLOCK();
}
/*
*===========================================================================
* ipdhcpc_ifrequest
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_ifrequest(Ip_u32 ifindex, Ipdhcpc_handle *handle)
{
Ip_err retval;
Ipdhcpc_if *ift;
Ip_time_t time_diff;
ip_assert(handle != IP_NULL);
IPDHCPC_LOCK();
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
if (ift == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_INTERFACE);
else
time_diff = ipcom_time(0) - ift->time_addr_req;
if (ift->handle != IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_ALREADY_STARTED);
/* Save handle in interface structure.*/
ift->handle = handle;
IPDHCPC_UNLOCK();
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: Initiating DHCP request, mode=%d",
ift->ifname,
handle->priv->mode);
if (handle->priv->state <= IPDHCPC_STATE_INIT_REBOOT && time_diff < 10 && 0 != ift->time_addr_req)
{
ipdhcpc_state_restart(handle);
}
else if (handle->priv->state == IPDHCPC_STATE_INFORMING)
ipdhcpc_output_request (ift, handle, IPDHCPC_MSG_INFORM);
else
{
/* Output the request immediately by triggering instant timeout. */
(void)ipdhcpc_tmo_cb(&handle->priv->tmo, handle);
}
/* Non blocking mode, reply instant success. */
if (handle->reply_cb != IP_NULL)
return IPCOM_SUCCESS;
IPDHCPC_LOCK();
retval = IPCOM_SUCCESS;
leave:
IPDHCPC_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_handle_options
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipdhcpc_handle_options(Ipdhcpc_if *ift,
Ipdhcpc_handle *handle,
Ip_u8 *dhcp_options,
Ip_u16 dhcp_optlen,
Ip_u8 *option_overload,
struct Ip_ifaliasreq *ifalias,
Ip_u32 *gw_n)
{
Ip_u32 addr_n;
Ip_u8 *option;
char *buf = IP_NULL;
struct Ip_sockaddr_in *addrp;
ip_assert(IP_NULL != ift);
ip_assert(IP_NULL != handle);
ip_assert(IP_NULL != dhcp_options);
ip_assert(IP_NULL != ifalias);
buf = ipcom_malloc(255);
if (buf == IP_NULL)
{
ip_assert(0);
return;
}
/* Act on the DHCP options we got from the server.*/
for (option = ipdhcpc_option_get_first(dhcp_options);
option != IP_NULL;
option = ipdhcpc_option_limit_get_next(option, &dhcp_optlen))
{
/* Let user application look at/act on the option */
if (ipdhcpc_option_callback(handle, option) == IP_FALSE)
continue;
switch(*option)
{
case IPDHCPC_OPTCODE_SUBNET_MASK:
/* Set subnet mask */
(void)ipcom_if_indextoname(handle->ifindex, ifalias->ifra_name);
addrp = (struct Ip_sockaddr_in *)&ifalias->ifra_mask;
addrp->sin_family = IP_AF_INET;
IPCOM_SA_LEN_SET(addrp, sizeof(struct Ip_sockaddr_in));
addrp->sin_addr.s_addr = IP_GET_32ON8(&option[2]);
break;
case IPDHCPC_OPTCODE_ROUTERS:
if (IP_NULL != gw_n)
{
/* Only pick first default route.*/
*gw_n = IP_GET_32ON8(&option[2]);
/* Ignore router == 0.0.0.0 */
if (*gw_n == 0)
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Got bogus default router (0.0.0.0), skipping", ift->ifname);
}
break;
case IPDHCPC_OPTCODE_DHCP_RENEWAL_TIME:
ift->lease.t1 = IP_GET_NTOHL(&option[2]);
break;
case IPDHCPC_OPTCODE_DHCP_REBINDING_TIME:
ift->lease.t2 = IP_GET_NTOHL(&option[2]);
break;
case IPDHCPC_OPTCODE_DHCP_LEASE_TIME:
ift->lease.tlease = IP_GET_NTOHL(&option[2]);
break;
case IPDHCPC_OPTCODE_DOMAIN_NAME:
/* Skip if already set this*/
if ( (option_confvar & IPDHCPC_OPTION_DOMAIN_CONF) == IPDHCPC_OPTION_DOMAIN_CONF )
break;
ift->handle->option_confvar |= IPDHCPC_OPTION_DOMAIN_CONF;
option_confvar |= IPDHCPC_OPTION_DOMAIN_CONF;
ipcom_strncpy(buf,(char *) &option[2], option[1]);
buf[option[1]] = 0;
(void)ipcom_sysvar_set("ipdnsc.domainname", buf, IPCOM_SYSVAR_FLAG_OVERWRITE);
break;
case IPDHCPC_OPTCODE_DOMAIN_NAME_SERVERS:
/* Skip if already set this*/
if ((option_confvar & IPDHCPC_OPTION_DNSSERVER_CONF) == IPDHCPC_OPTION_DNSSERVER_CONF)
break;
ift->handle->option_confvar |= IPDHCPC_OPTION_DNSSERVER_CONF;
option_confvar |= IPDHCPC_OPTION_DNSSERVER_CONF;
/* Set primary NS */
addr_n = IP_GET_32ON8(&option[2]);
(void)ipcom_inet_ntop(IP_AF_INET, &addr_n, buf, 255);
(void)ipcom_sysvar_set("ipdnsc.primaryns", buf, IPCOM_SYSVAR_FLAG_OVERWRITE);
/* Set optional secondary NS */
if (option[1] >= 8)
{
addr_n = IP_GET_32ON8(&option[6]);
(void)ipcom_inet_ntop(IP_AF_INET, &addr_n, buf, 255);
(void)ipcom_sysvar_set("ipdnsc.secondaryns", buf, IPCOM_SYSVAR_FLAG_OVERWRITE);
}
/* Set optional tertiary NS */
if (option[1] >= 12)
{
addr_n = IP_GET_32ON8(&option[10]);
(void)ipcom_inet_ntop(IP_AF_INET, &addr_n, buf, 255);
(void)ipcom_sysvar_set("ipdnsc.tertiaryns", buf, IPCOM_SYSVAR_FLAG_OVERWRITE);
}
/* Set optional quaternary NS */
if (option[1] >= 16)
{
addr_n = IP_GET_32ON8(&option[14]);
(void)ipcom_inet_ntop(IP_AF_INET, &addr_n, buf, 255);
(void)ipcom_sysvar_set("ipdnsc.quaternaryns", buf, IPCOM_SYSVAR_FLAG_OVERWRITE);
}
break;
case IPDHCPC_OPTCODE_NTP_SERVERS:
if ((option_confvar & IPDHCPC_OPTION_NTPSERVER_CONF) == IPDHCPC_OPTION_NTPSERVER_CONF)
break;
ift->handle->option_confvar |= IPDHCPC_OPTION_NTPSERVER_CONF;
option_confvar |= IPDHCPC_OPTION_NTPSERVER_CONF;
addr_n = IP_GET_32ON8(&option[2]);
(void)ipcom_inet_ntop(IP_AF_INET, &addr_n, buf, 255);
(void)ipcom_sysvar_set("ipsntp.client.primary.addr", buf, IPCOM_SYSVAR_FLAG_OVERWRITE);
break;
case IPDHCPC_OPTCODE_OPTION_OVERLOAD:
/* file and/or sname field in dhcp header overloaded with options */
if (option_overload)
*option_overload = option[2];
break;
default:
break;
}
}
ipcom_free(buf);
return;
}
/*
*===========================================================================
* ipdhcpc_iflease
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_iflease(Ip_u32 ifindex, Ipdhcpc_handle *handle)
{
Ip_err retval;
Ipdhcpc_if *ift;
Ipdhcpc_handle **handlep;
Ip_u8 end = IPDHCPC_OPTCODE_END;
Ip_u8 option_overload = IPDHCPC_OVERLOAD_NONE;
struct Ip_sockaddr_in *addrp;
int bootp_mode;
struct Ip_ifaliasreq ifalias;
Ip_u32 gw_n = 0;
char addr1[16];
Ip_size_t len = 0;
ip_assert(handle != IP_NULL);
IPDHCPC_UNLOCK();
if (ipdhcpc.opened == IP_FALSE)
IPDHCPC_LEAVE(IPCOM_ERR_NOT_OPENED);
ipcom_memset(&ifalias, 0, sizeof(ifalias));
if (handle->priv->state != IPDHCPC_STATE_BOUND)
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
if (ift == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_INTERFACE);
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Starting lease", ift->ifname);
/* Remove entry from pending handle list. */
for (handlep = &ipdhcpc.handle_head;(*handlep) != IP_NULL;
handlep = &(*handlep)->priv->next)
if ((*handlep) == handle)
{
*handlep = handle->priv->next;
break;
}
#if 0
if ((*handlep) != handle)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_ARG);
*handlep = handle->priv->next;
#endif
/* Reset options to default values */
ift->lease.t1 = 0;
ift->lease.t2 = 0;
ift->lease.tlease = (Ip_time_t)0xffffffff;
/* Notify the user about the boot server address in dhcp header */
if (handle->info.bootaddr_n)
{
Ip_u8 *addr_buf;
addr_buf = ipcom_malloc(sizeof(Ip_u32) + 2 * sizeof(Ip_u8));
if (addr_buf == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
addr_buf[0] = IPDHCPC_OPTCODE_TFTP_SERVER_ADDRESS;
addr_buf[1] = sizeof(Ip_u32);
ipcom_memcpy(&addr_buf[2],&handle->info.bootaddr_n, sizeof(Ip_u32));
(void)ipdhcpc_option_callback(handle, addr_buf);
ipcom_free(addr_buf);
}
ipdhcpc_handle_options(ift,
handle,
handle->info.options,
(Ip_u16)handle->info.optlen,
&option_overload,
&ifalias,
&gw_n);
/* Notify the user about the boot server name in dhcp header */
if (option_overload == IPDHCPC_OVERLOAD_NONE
&& handle->info.sname[0] != 0)
{
Ip_u8 *name_buf;
len = ipcom_strlen((const char*)handle->info.sname);
if (len > IPDHCPC_SNAME_LEN)
{
len = IPDHCPC_SNAME_LEN;
}
name_buf = ipcom_malloc(len + 2 * sizeof(Ip_u8));
if (name_buf == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
name_buf[0] = IPDHCPC_OPTCODE_TFTP_SERVER_NAME;
name_buf[1] = (Ip_u8)len;
ipcom_memcpy(&name_buf[2],handle->info.sname,len);
(void)ipdhcpc_option_callback(handle, name_buf);
ipcom_free(name_buf);
}
/* Notify the user about the boot file name in DHCP header */
if (option_overload == IPDHCPC_OVERLOAD_NONE
&& handle->info.file[0] != 0)
{
Ip_u8 *name_buf;
len = ipcom_strlen((const char*)handle->info.file);
if (len > IPDHCPC_BOOTFILE_NAME_LEN)
{
len = IPDHCPC_BOOTFILE_NAME_LEN;
}
name_buf = ipcom_malloc(len + 2 * sizeof(Ip_u8));
if (name_buf == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
name_buf[0] = IPDHCPC_OPTCODE_BOOTFILE_NAME;
name_buf[1] = (Ip_u8)len;
ipcom_memcpy(&name_buf[2],handle->info.file,len);
(void)ipdhcpc_option_callback(handle, name_buf);
ipcom_free(name_buf);
}
if ((option_overload == IPDHCPC_OVERLOAD_SNAME ||
option_overload == IPDHCPC_OVERLOAD_BOTH)
&& handle->info.sname[0] != 0)
{
len = ipcom_strlen((const char*)handle->info.sname);
if (len > IPDHCPC_SNAME_LEN)
{
len = IPDHCPC_SNAME_LEN;
}
ipdhcpc_handle_options(ift,
handle,
handle->info.sname,
(Ip_u16)len,
IP_NULL,
&ifalias,
&gw_n);
}
if ((option_overload == IPDHCPC_OVERLOAD_FILE ||
option_overload == IPDHCPC_OVERLOAD_BOTH)
&& handle->info.file[0] != 0)
{
len = ipcom_strlen((const char*)handle->info.file);
if (len > IPDHCPC_BOOTFILE_NAME_LEN)
{
len = IPDHCPC_BOOTFILE_NAME_LEN;
}
ipdhcpc_handle_options(ift,
handle,
handle->info.file,
(Ip_u16)len,
IP_NULL,
&ifalias,
&gw_n);
}
bootp_mode = ipcom_sysvar_get_as_int("ipdhcpc.bootp_mode",IPDHCPC_BOOTP_MODE);
/* If we're in bootp mode, set IPv4 address on this interface.*/
if (bootp_mode)
{
(void)ipcom_if_indextoname(ifindex, ifalias.ifra_name);
addrp = (struct Ip_sockaddr_in *)&ifalias.ifra_addr;
addrp->sin_family = IP_AF_INET;
addrp->sin_len = sizeof(struct Ip_sockaddr_in);
addrp->sin_addr.s_addr = handle->info.ipaddr_n;
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCAIFADDR, &ifalias) < 0)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_iflease(): ipcom_ioctl(IP_SIOCSIFADDR) failed, errno = %d",
ifalias.ifra_name, ipcom_errno);
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
}
}
if (gw_n)
{
if (ift->handle->priv->gw_n != 0)
{
/* Delete previous DHCP default route. */
if (ipdhcpc_route_mod(ift->handle->priv->gw_n, IP_FALSE) != 0)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_iflease(): ipcom_ioctl(SIOCDELRT) failed, errno = %d",
ift->ifname, ipcom_errno);
}
else
{
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: deleted default gateway = %s",
ift->ifname,
ipcom_inet_ntop(IP_AF_INET, &ift->handle->priv->gw_n, addr1, sizeof(addr1)));
(void)addr1;
}
ift->handle->priv->gw_n = 0;
}
/* Add default gateway. */
if (ipdhcpc_route_mod(gw_n, IP_TRUE) != 0)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: ipdhcpc_iflease(): ipcom_ioctl(SIOCADDRT) failed, errno = %d",
ift->ifname, ipcom_errno);
}
else
{
ift->handle->priv->gw_n = gw_n;
IPCOM_LOG2(DEBUG2, "ipdhcpc[%s] :: added default gateway = %s",
ift->ifname,
ipcom_inet_ntop(IP_AF_INET, &gw_n, addr1, sizeof(addr1)));
(void)addr1;
}
}
/* Tell the DHCP option callback that there are no more options */
(void)ipdhcpc_option_callback(handle, &end);
if (!bootp_mode)
{
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo));
ip_assert(!IPCOM_TMO_PENDING(&handle->priv->tmo_t2));
if (ift->lease.t1 == 0)
ift->lease.t1 = ift->lease.tlease / 2; /* 50% */
if (ift->lease.t2 == 0)
ift->lease.t2 = ift->lease.tlease * 7 / 8; /* 87,5% */
/* Update user info with timeout information */
handle->info.t1 = ift->lease.t1;
handle->info.t2 = ift->lease.t2;
/* Set T1 timeout for renewing. */
if (ift->lease.t1 > 0 && ift->lease.tlease != (Ip_time_t)0xffffffff)
{
ipdhcpc_timer_set(handle, ipdhcpc_sec_to_msec((Ip_u32)ift->lease.t1), 1);
}
}
retval = IPCOM_SUCCESS;
leave:
IPDHCPC_LOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_ifunlease
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_ifunlease(Ip_u32 ifindex, Ipdhcpc_handle **handlep)
{
Ip_err retval;
Ipdhcpc_if *ift;
IPDHCPC_LOCK();
if (ipdhcpc.opened == IP_FALSE)
IPDHCPC_LEAVE(IPCOM_ERR_NOT_OPENED);
for (ift = ipdhcpc.ift_head; ift != IP_NULL; ift = ift->next)
if (ift->ifindex == ifindex)
break;
if (ift == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_INTERFACE);
/* No lease. */
if (ift->handle == IP_NULL)
IPDHCPC_LEAVE(IPCOM_ERR_NOT_INSTALLED);
retval = ipdhcpc_ifunlease_locked(ift, handlep);
if (retval == IPCOM_SUCCESS)
{
IPCOM_LOG1(DEBUG2, "ipdhcpc[%s] :: Stopping lease", ift->ifname);
}
leave:
IPDHCPC_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipdhcpc_option_sprintf
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL int
ipdhcpc_option_sprintf(Ip_u8 *option, char *buf, int buflen)
{
int len = 0;
int n;
char addr1[16];
Ip_u32 val32;
Ip_u16 val16;
Ip_u8 val8;
len = ipcom_snprintf(buf, buflen, " [ Option #%3d: ", *option);
switch(*option)
{
case IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE:
val8 = option[2];
len += ipcom_snprintf(buf + len, buflen - len, "Message Type: ");
switch (val8)
{
case IPDHCPC_MSG_DISCOVER:
len += ipcom_snprintf(buf + len, buflen - len, "DISCOVER");
break;
case IPDHCPC_MSG_OFFER:
len += ipcom_snprintf(buf + len, buflen - len, "OFFER");
break;
case IPDHCPC_MSG_REQUEST:
len += ipcom_snprintf(buf + len, buflen - len, "REQUEST");
break;
case IPDHCPC_MSG_DECLINE:
len += ipcom_snprintf(buf + len, buflen - len, "DECLINE");
break;
case IPDHCPC_MSG_ACK:
len += ipcom_snprintf(buf + len, buflen - len, "ACK");
break;
case IPDHCPC_MSG_NAK:
len += ipcom_snprintf(buf + len, buflen - len, "NACK");
break;
case IPDHCPC_MSG_RELEASE:
len += ipcom_snprintf(buf + len, buflen - len, "RELEASE");
break;
case IPDHCPC_MSG_INFORM:
len += ipcom_snprintf(buf + len, buflen - len, "INFORM");
break;
case IPDHCPC_MSG_INVALID:
default:
len += ipcom_snprintf(buf + len, buflen - len, "Invalid");
}
len += ipcom_snprintf(buf + len, buflen - len, " ]" IP_LF);
break;
case IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER:
len += ipcom_snprintf(buf + len, buflen - len, "Client Identifier: ");
if (option[1] >= 2 && option[2] == 1)
{
len += ipcom_snprintf(buf + len,
buflen - len,
"Ethernet: ");
for (n = 0; n < option[1] - 1; n++)
len += ipcom_snprintf(buf + len, buflen - len, "%.2x:", option[3 + n]);
}
else if (option[1] >= 2 && option[2] == 0)
{
for (n = 0; n < option[1] - 1; n++)
len += ipcom_snprintf(buf + len,
buflen - len,
"0x%02x ",
option[3 + n]);
}
else
{
len += ipcom_snprintf(buf + len, buflen - len, "invalid ");
}
len += ipcom_snprintf(buf + len, buflen - len, " ]" IP_LF);
break;
case IPDHCPC_OPTCODE_DHCP_MAX_MESSAGE_SIZE:
val16 = IP_GET_NTOHS(&option[2]);
len += ipcom_snprintf(buf + len,
buflen - len,
"Maximum DHCP Message Size: %d ]" IP_LF,
(int) val16);
break;
case IPDHCPC_OPTCODE_DHCP_SERVER_IDENTIFIER:
val32 = IP_GET_32ON8(&option[2]);
len += ipcom_snprintf(buf + len,
buflen - len,
"Server Identifier: %s ]" IP_LF,
ipcom_inet_ntop(IP_AF_INET,
&val32,
addr1,
sizeof(addr1)));
break;
case IPDHCPC_OPTCODE_DHCP_REQUESTED_ADDRESS:
val32 = IP_GET_32ON8(&option[2]);
len += ipcom_snprintf(buf + len, buflen - len,
"Requested IP Address: %s ]" IP_LF,
ipcom_inet_ntop(IP_AF_INET,
&val32,
addr1,
sizeof(addr1)));
break;
case IPDHCPC_OPTCODE_DHCP_LEASE_TIME:
val32 = IP_GET_NTOHL(&option[2]);
len += ipcom_snprintf(buf + len, buflen - len,"IP Address Lease Time: %ld ]" IP_LF,
val32);
break;
case IPDHCPC_OPTCODE_SUBNET_MASK:
val32 = IP_GET_32ON8(&option[2]);
len += ipcom_snprintf(buf + len,
buflen - len,
"Subnet Mask: %s ]" IP_LF,
ipcom_inet_ntop(IP_AF_INET,
&val32,
addr1,
sizeof(addr1)));
break;
case IPDHCPC_OPTCODE_IP_FORWARDING:
len += ipcom_snprintf(buf + len, buflen - len, "Forwarding: %s ]"IP_LF,
option[2] == 1 ? "enable" : "disable");
break;
case IPDHCPC_OPTCODE_ROUTERS:
case IPDHCPC_OPTCODE_NTP_SERVERS:
case IPDHCPC_OPTCODE_DOMAIN_NAME_SERVERS:
switch(*option)
{
case IPDHCPC_OPTCODE_ROUTERS:
len += ipcom_snprintf(buf + len, buflen - len, "Routers:");
break;
case IPDHCPC_OPTCODE_NTP_SERVERS:
len += ipcom_snprintf(buf + len, buflen - len, "NTP Servers:");
break;
case IPDHCPC_OPTCODE_DOMAIN_NAME_SERVERS:
len += ipcom_snprintf(buf + len, buflen - len, "Domain Name Server:");
break;
default:
IP_PANIC();
break;
}
for (n = 0; n < option[1] / 4; n++)
{
val32 = IP_GET_32ON8(&option[2 + n * 4]);
len += ipcom_snprintf(buf + len,
buflen - len,
" %s",
ipcom_inet_ntop(IP_AF_INET, &val32, addr1, sizeof(addr1)));
}
len += ipcom_snprintf(buf + len, buflen - len, " ]" IP_LF);
break;
case IPDHCPC_OPTCODE_DHCP_RENEWAL_TIME:
val32 = IP_GET_NTOHL(&option[2]);
len += ipcom_snprintf(buf + len, buflen - len,
"Renewal(T1) Time Value: %ld ]" IP_LF, val32);
break;
case IPDHCPC_OPTCODE_DHCP_REBINDING_TIME:
val32 = IP_GET_NTOHL(&option[2]);
len += ipcom_snprintf(buf + len, buflen - len,
"Renewal(T1) Time Value: %ld ]" IP_LF, val32);
break;
case IPDHCPC_OPTCODE_DOMAIN_NAME:
len += ipcom_snprintf(buf + len,
buflen - len,
"Domain Name: ");
for (n = 0; n < option[1]; n++)
len += ipcom_snprintf(buf + len, buflen - len, "%c", option[2 + n]);
len += ipcom_snprintf(buf + len, buflen - len, " ]" IP_LF);
break;
case IPDHCPC_OPTCODE_DHCP_PARAMETER_REQUEST_LIST:
len += ipcom_snprintf(buf + len,
buflen - len,
"Parameter Request List: ");
for (n = 0; n < option[1]; n++)
len += ipcom_snprintf(buf + len, buflen - len, "%d ", option[2 + n]);
len += ipcom_snprintf(buf + len, buflen - len, " ]" IP_LF);
break;
default:
len += ipcom_snprintf(buf + len,
buflen - len,
"length = %d, Val = ",
option[1]);
for (n = 0; n < option[1]; n++)
len += ipcom_snprintf(buf + len, buflen - len, "0x%x ",(int)option[2 + n]);
len += ipcom_snprintf(buf + len, buflen - len, "]" IP_LF);
break;
}
return len;
}
/*
*===========================================================================
* ipdhcpc_option_find
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_u8 *
ipdhcpc_option_find(Ipdhcpc_handle *handle, Ip_u8 code)
{
return ipdhcpc_koption_get(handle->info.options, handle->info.optlen, code);
}
/*
*===========================================================================
* ipdhcpc_option_get_first
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_u8 *
ipdhcpc_option_get_first(Ip_u8 *options)
{
return options;
}
#if 0
/*
*===========================================================================
* ipdhcpc_option_get_next
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_u8 *
ipdhcpc_option_get_next(Ip_u8 *options)
{
Ip_u8 optlen;
/* Fix for WIND00195671 start */
if (*options == IPDHCPC_OPTCODE_END)
return IP_NULL;
/* Fix for WIND00195671 end */
optlen = options[1];
options += optlen + 2;
while (*options == IPDHCPC_OPTCODE_PAD)
options++;
if (*options == IPDHCPC_OPTCODE_END)
return IP_NULL;
return options;
}
#endif
/*
*===========================================================================
* ipdhcpc_option_limit_get_next
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_u8 *
ipdhcpc_option_limit_get_next(Ip_u8 * options, Ip_u16 *size)
{
Ip_u8 optlen = 0;
if (*size < 2)
return IP_NULL;
if (*options == IPDHCPC_OPTCODE_END)
return IP_NULL;
optlen = options[1];
if (optlen + 2 > *size)
return IP_NULL;
options += optlen + 2 * sizeof(Ip_u8);
*size = (Ip_u16)(*size-(optlen + 2 * sizeof(Ip_u8)));
while (*size > 0 && *options == IPDHCPC_OPTCODE_PAD)
{
options++;
(*size)--;
}
if (*size < 2)
return IP_NULL;
if (*options == IPDHCPC_OPTCODE_END)
return IP_NULL;
return options;
}
/*
*===========================================================================
* ipdhcpc_option_add
*===========================================================================
* Description: Add user defined options to option buffer
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcpc_option_add(Ipdhcpc_handle *handle, Ip_u8 opt_code, Ip_u8 *opt_data, Ip_u32 opt_len)
{
Ip_u8 *option = IP_NULL;
Ip_err retval;
char ifname[IP_IFNAMSIZ + 1];
/* Ensure the option is not one supported inherently by IPDHCPC */
if (opt_code == IPDHCPC_OPTCODE_DHCP_MESSAGE_TYPE ||
opt_code == IPDHCPC_OPTCODE_DHCP_CLIENT_IDENTIFIER ||
opt_code == IPDHCPC_OPTCODE_DHCP_MAX_MESSAGE_SIZE ||
opt_code == IPDHCPC_OPTCODE_PAD ||
opt_code == IPDHCPC_OPTCODE_END)
{
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: User addition of option %d is restricted\n",
ifname,
opt_code);
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_ARG);
}
(void)ipcom_if_indextoname(handle->ifindex, ifname);
/* For each option 255 is the max length */
if (opt_len > 255 || opt_len == 0)
{
IPCOM_LOG3(ERR, "ipdhcpc[%s] :: User addition of option %d length %d is invalid. Must be 1~255.\n",
ifname,
opt_code,
opt_len);
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_ARG);
}
/* Ensure we've got enough space for the new option */
if ((opt_len + 2 + handle->optlen) > IPDHCPC_MAX_OPTION_SIZE)
{
/* check if option is truncated, if so - skip it and breakout. */
IPCOM_LOG2(ERR, "ipdhcpc[%s] :: User option(%d) is too long to put into dhcp message buffer.\n",
ifname,
opt_code);
IPDHCPC_LEAVE(IPCOM_ERR_NO_MEMORY);
}
/* Ensure the option is not already present */
option = ipdhcpc_koption_get(handle->options, handle->optlen, opt_code);
if (option != IP_NULL && opt_code != IPDHCPC_OPTCODE_PAD)
{
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_ARG);
}
/*
* place new option at the end of the option list
*/
option = &handle->options[handle->optlen];
/* Copy the option over */
if (opt_data != IP_NULL && option != IP_NULL)
{
*option++ = opt_code;
*option++ = (Ip_u8)opt_len;
ipcom_memcpy(option, opt_data, opt_len);
}
/* Bump option length by the size of the option data, option size, and option code fields */
handle->optlen += opt_len + 2;
retval = IPCOM_SUCCESS;
leave:
return retval;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcpc_create
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipdhcpc_create(void)
{
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcpc_start
*===========================================================================
* Description: Start DHCP client process.
* Parameters:
* Returns:
*/
IP_PUBLIC Ip_err
ipdhcpc_start(void)
{
Ip_err ret;
Ipcom_proc_attr attr;
ipcom_proc_attr_init(&attr);
attr.stacksize = IPCOM_PROC_STACK_LARGE;
ret = ipcom_proc_acreate("ipdhcpc", ipdhcpc_proc, &attr, IP_NULL);
if(ret != IPCOM_SUCCESS)
IPCOM_LOG0(ERR, "ipdhcpc_create :: ipcom_proc_create() failed");
#ifdef IPDHCPC_INCLUDE_CMD
ret = ipcom_shell_add_cmd("dhcpc",
"dhcpc ...",
"DHCP client shell tool",
(Ipcom_shell_cmd_type) ipdhcpc_cmd_dhcpc,
IPCOM_SHELL_PRIO_SAME,
IPCOM_PROC_STACK_DEFAULT);
if(ret != IPCOM_SUCCESS)
/* ipcom_shell_add_cmd could be defined to 0 in ipcom_shell.h. */
/* coverity[dead_error_begin] */
IPCOM_LOG0(ERR, "ipdhcpc_create :: ipcom_shell_add_cmd(dhcpc) failed");
#endif
return ret;
}
/*
*===========================================================================
* ipdhcpc_get_interface_flags
*===========================================================================
* Description:
* Parameters:
*
* Returns:
*/
IP_STATIC Ip_err
ipdhcpc_get_interface_flags (char *name, Ip_u32 *flags)
{
Ip_err retval = IPCOM_SUCCESS;
struct Ip_ifreq ifreq;
if (ipcom_strlen (name )>= IP_IFNAMSIZ)
IPDHCPC_LEAVE(IPCOM_ERR_INVALID_INTERFACE);
ipcom_memset(&ifreq, 0, sizeof(ifreq));
ipcom_strcpy(ifreq.ifr_name, name);
if (ipcom_socketioctl(ipdhcpc.fd, IP_SIOCGIFFLAGS, &ifreq) < 0)
IPDHCPC_LEAVE(IPCOM_ERR_FAILED);
if (IP_NULL != flags)
*flags = ifreq.ip_ifr_flags;
leave:
return retval;
}
/*
*===========================================================================
* ipdhcpc_renew_get
*===========================================================================
* Description:
* Parameters:
*
* Returns:
*/
IP_PUBLIC Ip_err ipdhcpc_renew_get(char *ifname)
{
Ip_u32 ifindex;
Ip_u32 flags;
Ip_err retval = IPCOM_SUCCESS;
if (IP_NULL == ifname)
IPDHCPC_LEAVE (IPCOM_ERR_FAILED);
ifindex = ipcom_if_nametoindex (ifname);
if (!ifindex)
{
IPCOM_LOG1 (ERR, "[ipdhcpc]::%s is invalid \n", ifname);
IPDHCPC_LEAVE (IPCOM_ERR_INVALID_INTERFACE);
}
if (ipdhcpc_get_interface_flags (ifname, &flags) != IPCOM_SUCCESS)
{
IPCOM_LOG1 (ERR, "[ipdhcpc]::get %s flags failed \n", ifname);
IPDHCPC_LEAVE ( IPCOM_ERR_FAILED);
}
retval = ipdhcpc_if_eventcb (ifname, ifindex, flags, IPDHCPC_IF_EVENT_RENEW, IP_NULL);
leave:
return retval;
}
/*
*===========================================================================
* ipdhcpc_inform_get
*===========================================================================
* Description:
* Parameters:
*
* Returns:
*/
IP_PUBLIC Ip_err
ipdhcpc_inform_get (char *ifname, char *addr)
{
Ip_u32 ifindex;
Ip_u32 flags;
struct Ip_in_addr iaddr ;
Ip_err retval = IPCOM_SUCCESS;
if (IP_NULL == ifname)
IPDHCPC_LEAVE (IPCOM_ERR_FAILED);
ifindex = ipcom_if_nametoindex (ifname);
if (!ifindex)
{
IPCOM_LOG1 (ERR, "[ipdhcpc]::%s is invalid \n", ifname);
IPDHCPC_LEAVE (IPCOM_ERR_INVALID_INTERFACE);
}
if (ipdhcpc_get_interface_flags (ifname, &flags) != IPCOM_SUCCESS)
{
IPCOM_LOG1 (ERR, "[ipdhcpc]::get %s flags failed \n", ifname);
IPDHCPC_LEAVE (IPCOM_ERR_FAILED);
}
if (IP_NULL == addr)
{
retval = ipdhcpc_if_eventcb (ifname, ifindex, flags, IPDHCPC_IF_EVENT_INFORM, IP_NULL);
IPDHCPC_LEAVE (retval);
}
if (ipcom_inet_pton(IP_AF_INET, addr, &iaddr) != 1)
{
IPCOM_LOG1 (ERR, "[ipdhcpc]::convert %s failed, please check the address \n", addr);
IPDHCPC_LEAVE ( IPCOM_ERR_INVALID_ARG);
}
retval = ipdhcpc_if_eventcb (ifname, ifindex, flags, IPDHCPC_IF_EVENT_INFORM, &iaddr.s_addr);
leave:
return retval;
}
/*
*===========================================================================
* ipdhcpc_version
*===========================================================================
* Description::
* Parameters::
* Returns:
*
*/
IP_PUBLIC const char *
ipdhcpc_version(void)
{
return "@(#) IPDHCPC $Name: VXWORKS_ITER40_RCPL8_2024030303 $ - INTERPEAK_COPYRIGHT_STRING";
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/