5908 lines
186 KiB
C
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
|
|
****************************************************************************
|
|
*/
|