zoukankan      html  css  js  c++  java
  • udhcpd源码分析3--IP租赁管理

    1:重要的结构体

      全局链表的成员struct dhcpOfferedAddr *leases 记录了当前租赁出去的IP信息

    /* leases.h */

    struct dhcpOfferedAddr {
        u_int8_t chaddr[16];
        u_int32_t yiaddr;    /* network order */
        u_int32_t expires;    /* host order */
    };

      结构体三个成员分别记录客户端MAC(为什么不是6字节?),租赁出去的IP地址,以及到期时间(time(0) + server_config.lease).

    2:读入lease_file

    /* dhcpd.c */

    leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
    memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases);
    read_leases(server_config.lease_file);

      根据配置文件中的max_leases参数分配空间,read_leases读入lease_file文件,将记录的IP租赁信息更新到leases链表中,有了leases链表还要有lease_file文件的原因是一旦udhcpd异常挂掉,重启之后能够恢复之前的租赁IP信息到leases链表里.

    /* files.c */

    /*
        * 将lease_file文件中记录的租赁出去的IP信息更新到链表struct dhcpOfferedAddr *leases中
        * read_leases函数主要是在udhcpd意外重启后恢复之前租赁出去IP的信息到struct dhcpOfferedAddr *leases中
        * add_lease是具体更新struct dhcpOfferedAddr *leases链表的函数,比如在server发送offer报文后应该将租赁
        出去的IP记录到链表中,这时候会调用add_lease函数
    */
    void read_leases(char *file)
    {
        FILE *fp;
        unsigned int i = 0;
        struct dhcpOfferedAddr lease;
        
        if (!(fp = fopen(file, "r"))) {
            LOG(LOG_ERR, "Unable to open %s for reading", file);
            return;
        }
        
        while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) {
            /* ADDME: is it a static lease */
            if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
                lease.expires = ntohl(lease.expires);
                if (!server_config.remaining) lease.expires -= time(0);
                if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
                    LOG(LOG_WARNING, "Too many leases while loading %s
    ", file);
                    break;
                }                
                i++;
            }
        }
        DEBUG(LOG_INFO, "Read %d leases", i);
        fclose(fp);
    }

      与其方向相反的,在程序适当时候需要将leases链表中的信息更新到lease_file文件中就调用write_lease函数

    /*
        通过遍历struct dhcpOfferedAddr *leases指向的链表更新lease_file文件内容,
        server_config.remaining 为真表示lease_file文件中存储的过期时间是绝对时间
        (time(0) + expires)否则存储相对时间(expires)
    */
    void write_leases(void)
    {
        FILE *fp;
        unsigned int i;
        char buf[255];
        time_t curr = time(0);
        unsigned long lease_time;
        
        if (!(fp = fopen(server_config.lease_file, "w"))) {
            LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file);
            return;
        }
        
        for (i = 0; i < server_config.max_leases; i++) {
            if (leases[i].yiaddr != 0) {
                if (server_config.remaining) {
                    if (lease_expired(&(leases[i])))//如果地址过期设置为0
                        lease_time = 0;
                    else lease_time = leases[i].expires - curr;
                } else lease_time = leases[i].expires;
                lease_time = htonl(lease_time);
                fwrite(leases[i].chaddr, 16, 1, fp);
                fwrite(&(leases[i].yiaddr), 4, 1, fp);
                fwrite(&lease_time, 4, 1, fp);
            }
        }
        fclose(fp);
        
        if (server_config.notify_file) {
            sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file);
            system(buf);
        }
    }

      有关leases链表的操作函数都在leases.c文件中,主要有下面这些:

    /* leases.c */

    1: struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease)

    /* 
        add a lease into the table, clearing out any old ones 
        先清空chaddr,yiaddr对应的链表节点,找到最早到期的节点,将新节点赋值到最早到期的节点位置
        如果没有到期的IP则返回NULL
    */
    struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease)
    {
        struct dhcpOfferedAddr *oldest;
        
        /* clean out any old ones */
        clear_lease(chaddr, yiaddr);
            
        oldest = oldest_expired_lease();
        
        if (oldest) {
            memcpy(oldest->chaddr, chaddr, 16);
            oldest->yiaddr = yiaddr;
            oldest->expires = time(0) + lease;
        }
        
        return oldest;
    }

    2: void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr)

    /* 
        clear every lease out that chaddr OR yiaddr matches and is nonzero 
        遍历leases链表找到chaddr或yiaddr对应的节点,将节点置0.
    */
    void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr)
    {
        unsigned int i, j;
        
        for (j = 0; j < 16 && !chaddr[j]; j++);
        /* j==16 表示chaddr数组为空,只需要比较yiaddr(小技巧) */
    
        for (i = 0; i < server_config.max_leases; i++)
            if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
                (yiaddr && leases[i].yiaddr == yiaddr)) {
                memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
            }
    }

    3: struct dhcpOfferedAddr *oldest_expired_lease(void)

    /*
        Find the oldest expired lease, NULL if there are no expired leases 
        找到leases链表中最早到期的节点,返回节点地址.没有到期节点返回NULL
    */
    struct dhcpOfferedAddr *oldest_expired_lease(void)
    {
        struct dhcpOfferedAddr *oldest = NULL;
        unsigned long oldest_lease = time(0);
        unsigned int i;
    
        
        for (i = 0; i < server_config.max_leases; i++)
            if (oldest_lease > leases[i].expires) {
                oldest_lease = leases[i].expires;
                oldest = &(leases[i]);
            }
        return oldest;
            
    }

    4: int lease_expired(struct dhcpOfferedAddr *lease)

    /* 
        true if a lease has expired 
        IP租赁到期返回true否则返回false
    */
    int lease_expired(struct dhcpOfferedAddr *lease)
    {
        return (lease->expires < (unsigned long) time(0));
    }

    5: struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr)

    /* 
        Find the first lease that matches chaddr, NULL if no match 
        通过chaddr值找到leases中节点,返回节点地址.
    */
    struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr)
    {
        unsigned int i;
    
        for (i = 0; i < server_config.max_leases; i++)
            if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
        
        return NULL;
    }

    6: struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr)

    /*
        Find the first lease that matches yiaddr, NULL is no match 
        通过yiaddr值找到leases中节点,返回节点地址.
    */
    struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr)
    {
        unsigned int i;
    
        for (i = 0; i < server_config.max_leases; i++)
            if (leases[i].yiaddr == yiaddr) return &(leases[i]);
        
        return NULL;
    }

    7: u_int32_t find_address(int check_expired)

    /* find an assignable address, it check_expired is true, we check all the expired leases as well.
     * Maybe this should try expired leases by age... 
     * 在地址池中返回一个没有被分配的地址.
    */
    u_int32_t find_address(int check_expired) 
    {
        u_int32_t addr, ret;
        struct dhcpOfferedAddr *lease = NULL;        
    
        addr = ntohl(server_config.start); /* addr is in host order here */
        for (;addr <= ntohl(server_config.end); addr++) {
    
            /* 排除地址池中.0和.255结尾的地址 */
            /* ie, 192.168.55.0 */
            if (!(addr & 0xFF)) continue;
    
            /* ie, 192.168.55.255 */
            if ((addr & 0xFF) == 0xFF) continue;
    
            /* lease is not taken */
            ret = htonl(addr);
            if ((!(lease = find_lease_by_yiaddr(ret)) ||
    
                 /* or it expired and we are checking for expired leases */
                 (check_expired  && lease_expired(lease))) &&
    
                 /* and it isn't on the network */
                     !check_ip(ret)) {
                return ret;
                break;
            }
        }
        return 0;
    }

    8: int check_ip(u_int32_t addr)

    /* 
        check is an IP is taken, if it is, add it to the lease table 
        检测此地址是否有在被其它lan pc所使用,检测的方式是用此IP广播arp报文,
        根据是否有回应判断此IP是否被占用.(比如某个lan pc 是使用的static IP,
        且此IP在udhcpd的地址池中,在udhcpd分配ip给客户端时必须做此检查(检查有就要将此IP添加到leases链表中表示已被分配),
        否则会造成IP冲突).
    */
    int check_ip(u_int32_t addr)
    {
        struct in_addr temp;
        /* arpping 发送一个arp广播包,经过一段时间等待后如果此IP没有被局域网内的主机使用就收不到单播回复,返回1 */ 
        if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
            temp.s_addr = addr;
             LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds", 
                 inet_ntoa(temp), server_config.conflict_time);
         /* blank_chaddr 黑户? 因为不知道使用这个IP的主机的MAC地址 */ add_lease(blank_chaddr, addr, server_config.conflict_time);
    return 1; } else return 0; }

     3:总结

      所有被租赁出去的IP地址,客户端MAC和到期时间(绝对时间-->leases链表中的到期时间都是绝对时间,而保存到lease_file中的到期时间有两种,绝对时间和相对时间<配置文件中remaining为no则和leases链表一致保存绝对时间,为yes则保存相对时间>),维护在两个地方,一个当然就是struct dhcpOfferedAddr *leases指针指向的全局链表,另一个就是配置文件指定的lease_file本地文件.本地文件主要是为了防止udhcpd异常重启后租赁信息的丢失而设置的,在files.c文件中的read_leases函数就是读取lease_file文件中的记录通过add_lease来恢复之前的被租赁出去的IP信息.而write_leases函数的方向刚好相反,它是将leases链表中的信息更新到lease_file文件中做记录.

      add_lease是更新struct dhcpOfferedAddr *leases链表的触发者,所以租赁IP的更新主要靠add_lease函数,add_lease只在sendACK和sendOffer中被调用,也就是说有新的客户端来连接就会更新一下链表(将旧的信息替换为新的信息)。而write_leases函数则会在server收到SIGUSER1信号或者socket空闲时不停将struct dhcpOfferedAddr *leases链表信息记录到lease_file中。

       

  • 相关阅读:
    补交20145226蓝墨云班课 -- 程序设计中临时变量的使用
    补交20145226蓝墨云班课 -- MyCP
    补交20145226蓝墨云班课 -- MyOD
    补交20145226蓝墨云班课 -- Arrays和String单元测试
    补交20145226蓝墨云班课 -- 后缀表达式
    20145226夏艺华 《Java程序设计》 课堂实践
    20145226夏艺华 网络对抗技术 EXP9 web安全基础实践
    (转载)充分理解QML的属性绑定
    (转载)UML类图中的六大关系:关联、聚合、组合、依赖、继承、实现
    (转载)链路层MTU的概念?为什么MTU值普遍都是1500?
  • 原文地址:https://www.cnblogs.com/Flychown/p/6710041.html
Copyright © 2011-2022 走看看