Esempio n. 1
0
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;
    }
}
Esempio n. 2
0
/* 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;

}
Esempio n. 3
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);
}
Esempio n. 4
0
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);
}
Esempio n. 5
0
/* 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;
}
Esempio n. 6
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;
}
Esempio n. 7
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);
}
Esempio n. 8
0
/* 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;
}
Esempio n. 9
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(&copy);
    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, &copy, 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);
                }
            }
        }
    }
}
Esempio n. 10
0
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;
    }
}
Esempio n. 11
0
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);
    }
}