/* ****************************************************************************** * 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 * and Jonas Rendel ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipdhcpc_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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: =;=;... */ 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: