void multi_link_remove_ap(struct multi_link_info *li){ if(!multi_link_modify_route(RTM_DELROUTE, 0, RT_TABLE_MAIN, li, 0)){ MULTI_DEBUG_PRINT(stderr, "Removed automatic AP route!\n"); } else{ MULTI_DEBUG_PRINT(stderr, "Failed to remove automatic AP route!\n"); return; } }
/* Add/delete ip rule */ static int32_t multi_link_modify_rule(uint32_t msg_type, uint32_t flags, uint32_t table_id, struct multi_link_info *li){ uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct rtmsg *rt; nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = NLM_F_REQUEST | flags; nlh->nlmsg_seq = 0; rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); rt->rtm_family = AF_INET; rt->rtm_dst_len = 0; rt->rtm_table = table_id; //The table to perform the lookup in rt->rtm_protocol = RTPROT_BOOT; rt->rtm_scope = RT_SCOPE_UNIVERSE; rt->rtm_type = RTN_UNICAST; //Need the length of the src address that will be provided later on rt->rtm_src_len = 32; mnl_attr_put_u32(nlh, FRA_SRC, li->cfg.address.s_addr); if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ MULTI_DEBUG_PRINT(stderr,"Could not send gateway to kernel " "(can be ignored if caused by an interface that went down, " "iface idx %u)\n", li->ifi_idx); return -1; } return 0; }
void multi_link_get_iface_info(struct multi_link_info *li){ //MNL_SOCKET_BUFFER_SIZE is 8k, which is the max nlmsg size (see //linux/netlink.h) uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct rtgenmsg *rt; uint32_t seq; //It seems like I cant request one interface, has to dump! ////Play with this later and see what is up nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = RTM_GETADDR; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; nlh->nlmsg_seq = seq = time(NULL); //How will this work with event? Send 0? rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); //I need all interfaces, also those without IP (check) rt->rtgen_family = AF_UNSPEC; if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ MULTI_DEBUG_PRINT(stderr, "Cannot request info dump\n"); return; } if(li->state == LINK_DOWN_PPP) multi_link_filter(seq, multi_link_filter_ppp, (void*) li); else if(li->state == LINK_DOWN_AP) multi_link_filter(seq, multi_link_filter_ap, (void*) li); }
void multi_link_configure_link(struct multi_link_info *li){ /* Add IP address. PPP/AP has already set the IP of the interface*/ //It is safe to do this twice in case of GOT_IP_STATIC_UP/GOT_IP_STATIC. An //interface can only be assigned the same IP address one time. Error will be //returned the following times. if(li->state != GOT_IP_PPP && li->state != GOT_IP_AP) multi_link_modify_ip(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, li); //Only set IP when link is only up (not running) */ if(li->state == GOT_IP_STATIC_UP) return; /* Use metric as table ID for now */ multi_link_modify_route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_APPEND, li->metric, li, 0); /*multi_link_modify_route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_APPEND, RT_TABLE_MAIN, li, li->metric);*/ MULTI_DEBUG_PRINT(stderr, "Done setting direct routes (iface %s idx %u)\n", li->dev_name, li->ifi_idx); if(li->state == GOT_IP_AP || (li->state == GOT_IP_STATIC && !li->cfg.gateway.s_addr)){ MULTI_DEBUG_PRINT(stderr, "Not setting gateway for %s (idx %u)\n", li->dev_name, li->ifi_idx); } else { multi_link_modify_gateway(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_APPEND, RT_TABLE_MAIN, li, li->metric); multi_link_modify_gateway(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_APPEND, li->metric, li, 0); MULTI_DEBUG_PRINT(stderr, "Done setting routes in main table " "(iface %s idx %u)\n", li->dev_name, li->ifi_idx); } multi_link_modify_rule(RTM_NEWRULE, NLM_F_CREATE | NLM_F_EXCL, li->metric, li); MULTI_DEBUG_PRINT(stderr, "Done adding rule (iface %s idx %u)\n", li->dev_name, li->ifi_idx); }
/* Adds/deletes route. The reason for having metric as a seperate parameter is * that the value depends on wether this is the private table (0) or not. If the * route is intended for the private table, then ignore metric */ static int32_t multi_link_modify_route(uint32_t msg_type, uint32_t flags, uint32_t table_id, struct multi_link_info *li, uint32_t metric){ uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct rtmsg *rt; uint32_t nw_ip = 0; //The desired destination IP is store in different places for PPP and //"normal" interfaces. This is the network route! if(li->state == GOT_IP_PPP || li->state == LINK_UP_PPP) nw_ip = li->cfg.broadcast.s_addr; else nw_ip = li->cfg.address.s_addr & li->cfg.netmask.s_addr; nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = NLM_F_REQUEST | flags; nlh->nlmsg_seq = 0; rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); rt->rtm_scope = RT_SCOPE_NOWHERE; rt->rtm_type = RTN_UNICAST; rt->rtm_family = AF_INET; rt->rtm_dst_len = 32 - (ffs(ntohl(li->cfg.netmask.s_addr)) - 1); rt->rtm_table = table_id; if(msg_type != RTM_DELROUTE){ rt->rtm_protocol = RTPROT_BOOT; rt->rtm_scope = RT_SCOPE_LINK; } mnl_attr_put_u32(nlh, RTA_DST, nw_ip); mnl_attr_put_u32(nlh, RTA_PREFSRC, li->cfg.address.s_addr); mnl_attr_put_u32(nlh, RTA_OIF, li->ifi_idx); if(metric) mnl_attr_put_u32(nlh, RTA_PRIORITY, metric); if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ MULTI_DEBUG_PRINT(stderr,"Could not send private route to kernel " "(can be ignored if caused by an interface that went down, " "iface idx %u)\n", li->ifi_idx); return -1; } return 0; }
/* Add/delete gateway */ static int32_t multi_link_modify_gateway(uint32_t msg_type, uint32_t flags, uint32_t table_id, struct multi_link_info *li, uint32_t metric){ uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct rtmsg *rt; nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = NLM_F_REQUEST | flags; nlh->nlmsg_seq = 0; rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); rt->rtm_family = AF_INET; //There is no destination (the destination is global, i.e. netmask 0) rt->rtm_dst_len = 0; rt->rtm_table = table_id; rt->rtm_protocol = RTPROT_UNSPEC; /* This is all copied from iproute */ if(msg_type != RTM_DELROUTE){ rt->rtm_scope = RT_SCOPE_UNIVERSE; rt->rtm_type = RTN_UNICAST; rt->rtm_protocol = RTPROT_BOOT; } else rt->rtm_scope = RT_SCOPE_NOWHERE; if(li->cfg.gateway.s_addr > 0) mnl_attr_put_u32(nlh, RTA_GATEWAY, li->cfg.gateway.s_addr); mnl_attr_put_u32(nlh, RTA_PREFSRC, li->cfg.address.s_addr); mnl_attr_put_u32(nlh, RTA_OIF, li->ifi_idx); if(metric) mnl_attr_put_u32(nlh, RTA_PRIORITY, metric); if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ MULTI_DEBUG_PRINT(stderr,"Could not send gateway to kernel " "(can be ignored if caused by an interface that went down, " "iface idx %u)\n", li->ifi_idx); return -1; } return 0; }
/* Maybe replace this with a command for flushing */ void multi_link_remove_link(struct multi_link_info *li){ multi_link_modify_rule(RTM_DELRULE, 0, 0, li); /* This seems to be done by the kernel, but does it depend on something or not? Maybe have a check here */ if(li->state != GOT_IP_AP) multi_link_modify_gateway(RTM_DELROUTE, 0, li->metric, li, 0); multi_link_modify_gateway(RTM_DELROUTE, 0, RT_TABLE_MAIN, li, li->metric); //multi_link_modify_route(RTM_DELROUTE, 0, RT_TABLE_MAIN, li, li->metric); multi_link_modify_route(RTM_DELROUTE, 0, li->metric, li, 0); /* Delete IP address */ if(li->state != GOT_IP_PPP && li->state != LINK_UP_PPP && li->state != GOT_IP_AP && li->state != LINK_UP_AP && li->state != LINK_UP_STATIC) multi_link_modify_ip(RTM_DELADDR, 0, li); MULTI_DEBUG_PRINT(stderr, "Cleaned up after %s (iface idx %u)\n", li->dev_name, li->ifi_idx); }
/* Adds or deletes the IP of an interface. This function is never called for PPP * interfaces, thus, there are no special cases. */ static int32_t multi_link_modify_ip(uint32_t msg_type, uint32_t flags, struct multi_link_info *li){ uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; struct ifaddrmsg *ifa; nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = NLM_F_REQUEST | flags; nlh->nlmsg_seq = 0; ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); /* Fill in info related to address */ ifa->ifa_family = AF_INET; //Currently only IPv4 //To avoid this rule that is generated automatically, set bitlen to 32 ifa->ifa_prefixlen = 32 - (ffs(ntohl(li->cfg.netmask.s_addr)) - 1); //Only reason for changing this is if loopback ifa->ifa_scope = RT_SCOPE_UNIVERSE; ifa->ifa_index = li->ifi_idx; mnl_attr_put_u32(nlh, IFA_LOCAL, li->cfg.address.s_addr); mnl_attr_put_u32(nlh, IFA_ADDRESS, li->cfg.address.s_addr); if(li->cfg.broadcast.s_addr) mnl_attr_put_u32(nlh, IFA_BROADCAST, li->cfg.broadcast.s_addr); if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ MULTI_DEBUG_PRINT(stderr,"Could not send IP to kernel (can be ignored " "if caused by an interface that went down, iface idx %u)\n", li->ifi_idx); return -1; } return 0; }
void multi_test_visible_loop(struct multi_config *mc){ uint8_t buf[MAX_BUFSIZE]; int32_t retval; int32_t i; int32_t netlink_sock = 0; uint32_t ifi_idx = 0; struct iface *ni = NULL; /* Needed to create the netlink messages */ struct sockaddr_nl src_addr, dest_addr; struct iovec iov; struct msghdr msg; /* Select is used for easier timeouts */ fd_set copy, master; int32_t fdmax; struct timeval tv; memset(buf, 0, MAX_BUFSIZE); /* Initialise list */ LIST_INIT(&iface_list); /* Define a private constant later! Needs to be set in netlink.h so that the * kernel will allow the socket to be created! */ if((netlink_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) < 0){ perror("Could not create netlink socket: "); exit(EXIT_FAILURE); } /* These are both constant! */ memset(&src_addr, 0, sizeof(src_addr)); memset(&dest_addr, 0, sizeof(dest_addr)); memset(&msg, 0, sizeof(msg)); src_addr.nl_family = AF_NETLINK; //If PID is set to zero, then the kernel assigns the unique value src_addr.nl_pid = 0; //This is the source, it only multicasts, so it does not have to be //member of any groups! src_addr.nl_groups = 0; if(bind(netlink_sock, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0){ perror("Could not bind netlink socket: "); exit(EXIT_FAILURE); } dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; dest_addr.nl_groups = 1; MULTI_DEBUG_PRINT(stderr, "Multi manager is ready! Netlink socket %d\n", netlink_sock); MULTI_DEBUG_PRINT(stderr, "M S\n"); //Look at the Wikipedia site for iovec and man(7) netlink for examples on //how to properly parse netlink and have multiple iovec-entries msg.msg_name = (void *) &dest_addr; //This is the message's destination msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; //Initialise everything related to select FD_ZERO(&master); FD_ZERO(©); FD_SET(mc->socket_pipe[0], &master); fdmax = mc->socket_pipe[0]; tv.tv_sec = 30; tv.tv_usec = 0; pthread_t multi_thread = multi_start(mc); while(1){ copy = master; retval = select(fdmax + 1, ©, NULL, NULL, &tv); //Repeat every UP message if(retval == 0){ for(ni = iface_list.lh_first; ni != NULL; ni = ni->next.le_next){ printf("Hei\n"); iov.iov_base = (void *) ni->nlmsg; iov.iov_len = ni->nlmsg->nlmsg_len; sendmsg(netlink_sock, &msg, 0); } tv.tv_sec = 30; tv.tv_usec = 0; continue; } memset(buf, 0, MAX_BUFSIZE); //Sufficient to just memcpy this one and broadcast the netlink message retval = read(mc->socket_pipe[0], buf, MAX_BUFSIZE); if(retval == -1) perror("Failed to read from pipe"); else { memcpy(&ifi_idx, (buf+1), sizeof(uint32_t)); //This check needs to be performed irrespective of if link goes up //or down. LIST_FIND_CUSTOM(ni, &iface_list, next, &ifi_idx, multi_mc_cmp_ifi); if(buf[0] == LINK_UP){ //Sanity check. If the interface is already found, ignore the //announcement from MULTI. if(ni) continue; //Create a new iface, buffer the up message and add to list ni = (struct iface*) malloc(sizeof(struct iface)); ni->ifi_idx = ifi_idx; ni->nlmsg = (struct nlmsghdr *) malloc(NLMSG_SPACE(retval)); memset(ni->nlmsg, 0, NLMSG_SPACE(retval)); ni->nlmsg->nlmsg_pid = getpid(); ni->nlmsg->nlmsg_flags = 0; ni->nlmsg->nlmsg_len = NLMSG_SPACE(retval); memcpy(NLMSG_DATA(ni->nlmsg), buf, retval); LIST_INSERT_HEAD(&iface_list, ni, next); //Adjust the base pointer of the message and broadcast message iov.iov_base = (void *) ni->nlmsg; iov.iov_len = ni->nlmsg->nlmsg_len; retval = sendmsg(netlink_sock, &msg, 0); MULTI_DEBUG_PRINT(stderr,"Broadcasted %d bytes about an UP " "change in network state\n", retval); } else { if(ni){ //Forward message from MULTI ni->nlmsg->nlmsg_len = NLMSG_SPACE(retval); memcpy(NLMSG_DATA(ni->nlmsg), buf, retval); iov.iov_base = (void *) ni->nlmsg; iov.iov_len = ni->nlmsg->nlmsg_len; retval = sendmsg(netlink_sock, &msg, 0); MULTI_DEBUG_PRINT(stderr,"Broadcasted %d bytes about a " "DOWN change in network state\n", retval); LIST_REMOVE(ni, next); free(ni); } } } } }
void multi_dhcp_parse_dhcp_msg(struct multi_dhcp_info *di, struct multi_dhcp_message *dm, struct multi_link_info *li){ struct multi_dhcp_config cfg; uint32_t t_now, t_diff; uint8_t ipaddr[INET_ADDRSTRLEN]; uint8_t baddr[INET_ADDRSTRLEN]; uint8_t gwaddr[INET_ADDRSTRLEN]; uint8_t netmask[INET_ADDRSTRLEN]; multi_dhcp_parse_options(dm, &cfg); if (dm->xid != di->xid || dm->hlen != 6 || memcmp(dm->chaddr, &(di->mac_addr),6)){ //fprintf(stderr, "Message intended for someone else!\n"); return; } switch(di->state){ case SELECTING: //One typical scenario here is if the lease expires before the //DHCP ACK for final REBIND is received if(cfg.dhcpmsgtype != DHCP_TYPE_OFFER){ MULTI_DEBUG_PRINT(stderr,"Mismatch state. In INIT but did not " "get OFFER. Got %u\n", cfg.dhcpmsgtype); return; } /* Move on to the next state, retrans count must be reset */ di->retrans_count = 0; MULTI_DEBUG_PRINT(stderr,"Received DHCP OFFER on interface %s " "(iface idx %u), will send DHCP REQUEST\n", li->dev_name, li->ifi_idx); di->cfg = cfg; di->state = REQUESTING; multi_dhcp_create_dhcp_msg(di); break; case RENEWING: case REBINDING: case REQUESTING: case REBOOTING: /* All these states */ if(cfg.dhcpmsgtype == DHCP_TYPE_NAK){ /* According to the RFC, a NAK involves moving straight back to * INIT and resending request. Moving to INIT implies resetting * variables and state, just in case */ MULTI_DEBUG_PRINT(stderr,"Got NAK in state %u. Resetting and " "retrying DISCOVER! (iface idx %u)\n", di->state, di->ifidx); di->state = INIT; di->req_sent_time = 0; //Since next packet is sent immideatly, this can 0 (as opposed //to -1 for ACK) di->retrans_count = 0; /* Set state as waiting. I can here if a) rebooting fails b) * requesting fails c) renewing fails d) rebinding fails. In the * last two, the link can be in UP state */ g_static_rw_lock_writer_lock(&(li->state_lock)); li->state = WAITING_FOR_DHCP; g_static_rw_lock_writer_unlock(&(li->state_lock)); multi_dhcp_create_dhcp_msg(di); } else if(cfg.dhcpmsgtype == DHCP_TYPE_ACK){ //Always decline DHCP address di->cfg = cfg; //Just in case, I know these are the good values di->state = BOUND; inet_ntop(AF_INET, &(cfg.address), (char*) ipaddr, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(cfg.broadcast), (char*) baddr, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(cfg.gateway), (char*) gwaddr, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(cfg.netmask), (char*) netmask, INET_ADDRSTRLEN); //Do the timeout calculation. Be warned that inet_ntoa is NOT //reentrant. In other words, the IP adresses are wrong! MULTI_DEBUG_PRINT(stderr,"Got DHCP ACK on interface %s " "(iface idx %u). %s will be bound to IP: %s Broadcast: " "%s Gateway: %s Netmask %s (%u) Lease: %u T1: %u T2: " "%u\n", li->dev_name, li->ifi_idx, li->dev_name, ipaddr, baddr, gwaddr, netmask, 32 - (ffs(ntohl(cfg.netmask.s_addr)) - 1), cfg.lease, cfg.t1, cfg.t2); //TODO: I need some variable or check to prevent adding the same IP twice. Compare cfg is maybe sufficient? Or at least address? //pthread_mutex_lock(&(li->link_state_lock)); g_static_rw_lock_writer_lock(&(li->state_lock)); /* This is needed if one interface switches network. Otherwise, * the main thread will not know that it has to clean up (it * will just see a new set of addresses)! */ /* Need to wireless access points in order to test this, with * different subnets */ if(li->cfg.address.s_addr != 0 && (li->cfg.address.s_addr != cfg.address.s_addr || li->cfg.broadcast.s_addr != cfg.broadcast.s_addr || li->cfg.gateway.s_addr != cfg.gateway.s_addr || li->cfg.netmask.s_addr != cfg.netmask.s_addr)){ li->state = DHCP_IP_CHANGED; li->new_cfg = cfg; multi_dhcp_notify_link_module(li->write_pipe); } else{ li->cfg = cfg; /* This is correct becuase if the information has not * changed, then there is no need to update the state. The * cfg must be updated due to leases and so on */ if(li->state != LINK_UP){ li->state = GOT_IP_DHCP; multi_dhcp_notify_link_module(li->write_pipe); } } g_static_rw_lock_writer_unlock(&(li->state_lock)); t_now = time(NULL); t_diff = t_now - di->req_sent_time; di->lease = cfg.lease; di->t1 = cfg.t1 ? cfg.t1 : cfg.lease / 2; di->t2 = cfg.t2 ? cfg.t2 : cfg.lease * 0.875; /* Not exactly sure what to do in this case */ assert(t_diff < di->t1 || t_diff < di->t2); assert(di->t1 < di->t2); /* Lease is from WHEN the request was sent */ di->lease -= t_diff; di->t1 -= t_diff; di->t2 -= t_diff; /* Convert values to be absolute */ di->lease += t_now; di->t1 += t_now; di->t2 += t_now; /* Every packet has been accounted for, so timers and everything can be reset */ di->req_sent_time = 0; //This will overflow, but it is ok. When the next timeout (T1) //is triggered, retrans_count will be increased by 1 and, thus, //be 0 again (but a little hackish) di->retrans_count = -1; /* New timeout event started */ di->output_timer = 1; } default: break; } }
void multi_dhcp_create_dhcp_msg(struct multi_dhcp_info *di){ struct multi_dhcp_message dhcp_msg; uint8_t dhcp_type; uint8_t iface_id[7]; struct in_addr ipaddr; uint32_t lease_time = ~0; //uint32_t lease_time = htonl(60); uint32_t t_now; uint8_t ip_addr[INET_ADDRSTRLEN]; multi_dhcp_dm_init(&dhcp_msg); //Unique ID to separate DHCP requests from one another dhcp_msg.xid = di->xid; dhcp_msg.op = BOOTP_OPCODE_REQUEST; //Found in RFC1700, seems to be a default value for all Ethernet-standards dhcp_msg.htype = 1; //Length of MAC-address dhcp_msg.hlen = 6; memcpy(dhcp_msg.chaddr, &(di->mac_addr), 6); t_now = time(NULL); /* Which message to send depends on the client's state. Also, changes state * if needed */ switch(di->state){ case INIT: case SELECTING: /* If I get here, it is either the first packet or a timeout has * occured */ di->state = SELECTING; dhcp_type = DHCP_TYPE_DISCOVER; ipaddr.s_addr = 0; multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, &dhcp_type); /* According to RFC, it is time of ORIGINAL request */ if(di->req_sent_time == 0) di->req_sent_time = t_now; di->req_retrans = t_now + (4 * (di->retrans_count + 1)); MULTI_DEBUG_PRINT(stderr,"Sending DHCP DISCOVER (iface idx %u).\n", di->ifidx); di->output_timer = 1; break; case INIT_REBOOT: case REBOOTING: di->state = REBOOTING; ipaddr.s_addr = 0; dhcp_type = DHCP_TYPE_REQUEST; multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, &dhcp_type); multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_REQADDR, 4, &di->cfg.address.s_addr); /* As always, this is a NEW request so time will be set for the * first packet */ if(di->req_sent_time == 0) di->req_sent_time = t_now; di->req_retrans = t_now + (4 * (di->retrans_count + 1)); inet_ntop(AF_INET, &di->cfg.address, (char*) ip_addr, INET_ADDRSTRLEN); MULTI_DEBUG_PRINT(stderr,"REBOOTING and requesting %s, Sending " "DHCP REQUEST (iface idx %u).\n", ip_addr, di->ifidx); di->output_timer = 1; break; case REQUESTING: ipaddr.s_addr = 0; dhcp_type = DHCP_TYPE_REQUEST; multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, &dhcp_type); multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_REQADDR, 4, &di->cfg.address.s_addr); multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_SERVER, 4, &di->cfg.dhcpd_addr.s_addr); //Not updating req_sent_time, as this is just a step in a request di->req_retrans = t_now + (4 * (di->retrans_count + 1)); MULTI_DEBUG_PRINT(stderr,"Sending DHCP REQUEST (iface idx %u).\n", di->ifidx); di->output_timer = 1; break; case BOUND: case RENEWING: di->state = RENEWING; ipaddr = di->cfg.dhcpd_addr; //Messages for renewing is sent unicast dhcp_type = DHCP_TYPE_REQUEST; multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, &dhcp_type); dhcp_msg.ciaddr = di->cfg.address.s_addr; /* As always, this is a NEW request so time will be set for the * first packet */ if(di->req_sent_time == 0) di->req_sent_time = t_now; di->req_retrans = t_now + (4 * (di->retrans_count + 1)); MULTI_DEBUG_PRINT(stderr,"RENEWING, sending DHCP REQUEST (iface " "idx %u).\n", di->ifidx); di->output_timer = 1; break; case REBINDING: di->state = REBINDING; //According to RFC, the old IP must be stored here ipaddr = di->cfg.address; dhcp_type = DHCP_TYPE_REQUEST; multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, &dhcp_type); dhcp_msg.ciaddr = di->cfg.address.s_addr; di->req_retrans = t_now + (4 * (di->retrans_count + 1)); MULTI_DEBUG_PRINT(stderr,"REBINDING, sending DHCP REQUEST " "(iface idx %u).\n", di->ifidx); di->output_timer = 1; break; case DECLINE: dhcp_type = DHCP_TYPE_DECLINE; multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, &dhcp_type); multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_REQADDR, 4, &di->cfg.address.s_addr); //According to the RFC, move back to init //di->state = INIT; di->retrans_count = 0; di->req_sent_time = 0; di->req_retrans = t_now + 10; //Timeout after decline is 10 seconds break; default: break; } /* Must be done manually due to the pair (see RFC2132) */ iface_id[0] = 1; memcpy(iface_id+1, &(di->mac_addr), 6); multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_CLIENT_IDENTIFIER, 7, iface_id); /* The server will only reply when I specify what info I want, naturally. However, RFC states it should return something, check at work tomorrow */ if(dhcp_type == DHCP_TYPE_DISCOVER || dhcp_type == DHCP_TYPE_REQUEST){ multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_OPTIONREQ, sizeof(multi_dhcp_optlist), multi_dhcp_optlist); multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_LEASE, sizeof(lease_time), &lease_time); } multi_dhcp_dm_finish_options(&dhcp_msg); //Send packet. I suspect that the reason I don't receive a unicast reply is //because the interface don't have a source IP set if(di->state == RENEWING){ multi_dhcp_snd_msg_udp(di->udp_sock, &ipaddr, &dhcp_msg); } else { multi_dhcp_snd_msg_raw(di->raw_sock, ipaddr, di->ifidx, &dhcp_msg, 1); } }