DHCP源码分析-dhcp模块


一, DHCP函数流程




        函数void dhcp (struct packet *packet)是处理DHCP包的入口函数,在进入这个函数的时候,对包的解析已经完成,已经将包的内容转换成内部结构struct packet。dhcp()函数主要做的工作就是对不同的packet -> packet_type对应的包进行处理。

二, locate_network()流程分析


        进入dhcp函数,首先就是调用int locate_network (struct packet *packet)来确定client所属subnet,以便从这个subnet下分配正确的IP
        

        1,查找link selection和subnet selection options

if ((oc = lookup_option(&agent_universe, packet->options, RAI_LINK_SELECT)) == NULL)
        oc=lookup_option(&dhcp_universe,packet->options, DHO_SUBNET_SELECTION);

        首先,先查找DHCP包中是否有relay-agent-option下linkselection sub-option,这个option用于当client与server处于不同网段,需要DHCP relay转发时,client想要得到一个relay agent不同网段的IP。具体信息请参考RFC 3527。

        如果没有该option,就继续在包中查找subnet selection option。这个option用于在client与server同一网段的情况下,client需要获得不同网段的IP,具体信息参考RFC3011。

        这两个option在功能上有相似之处,都是client要求获得一个指定网段的IP。不同之处在于,relay-agent-option是用于DHCP relay的,也就说DHCP包是通过relay转发到server端的,client与server处于不同的物理网段。而subnet selection option,是client与server处于同一个物理网段,但是想要获得不同网段的IP,而由client将这个option加入到DHCP包中。


        2,使用网卡所在网段为预分配的subnet

if (!oc && !packet -> raw -> giaddr.s_addr) {
    if (packet -> interface -> shared_network) {
  	  shared_network_reference
 		    (&packet -> shared_network,
 		   packet -> interface -> shared_network, MDL);
	    return 1;  }
       return 0;  }

        如果没有找到这两个option,那么通过packet -> raw -> giaddr.s_addr来判断这个包是否是由relay转发过来的。packet -> raw -> giaddr.s_addr是relay的地址,由relay填写。如果不是转发过来的包,那么到此就可以确定client与server同一网段,并且没有要求不同网段的IP。那么就确定了接收到该包网卡所属的网段,为分配IP的网段,并赋给packet->shared_network。

        3,使用option或relay地址确定subnet

if (oc) {
   memset (&data, 0, sizeof data);
   if (!evaluate_option_cache (&data, packet, (struct lease *)0, (struct client_state *)0,packet -> options,
    (struct option_state *)0,&global_scope, oc, MDL)) 
      {   return 0;   }
   if (data.len != 4)  
      {    return 0;   }
   ia.len = 4;
   memcpy (ia.iabuf, data.data, 4);
   data_string_forget (&data, MDL);
   } 
else {
   ia.len = 4;
   memcpy (ia.iabuf, &packet -> raw -> giaddr, 4);}
   if (find_subnet (&subnet, ia, MDL)) {
      shared_network_reference (&packet -> shared_network,subnet -> shared_network, MDL);
      subnet_dereference (&subnet, MDL);
      return 1;
}

         在没有前面两个option的情况下,server将通过packet -> raw -> giaddr来确定分配IP的subnet。
        

        通过对locate_network的分析,我们可以确定subnet选择因素的优先级为

linkselection sub-option > subnet selection option > relay address > 网卡的subnet

三, find_lease()流程分析

        

        函数dhcpdiscover通过调用find_lease来确定分配哪个IP给client

        1, 通过client identifer或hardware确定host

        
        首先sever会尝试用DHCP请求中的client identifer或者hardware来确定该请求是否来自于一个配置文件指定的host。(通常当client的管理员没有指定client indentifier的时候,client会把自己的网卡的MAC地址作为client identifer发给server。)

        通过find_hosts_by_uid(&hp,client_identifier.data, client_identifier.len, MDL)来使用client identifier找到host即hp,通过find_hosts_by_haddr(&hp,packet->raw->htype, packet->raw->chaddr, packet->raw->hlen,MDL)来使用MAC地址来找到host。找到host后,通过host尝试查找fixedlease——即在配置文件的host中,指定的IP地址。这是通过mockup_lease (&fixed_lease, packet, share, host)这个函数实现的。这里注意的是,传入参数share为预分配IP的subnet。将它作为参数,是为了排除不在该subnet中的fixed ip,即使这个IP是host的指定IP。

       其实除了使用clientindentifer和MAC地址来查找host的方式外,还支持通过指定option来查找host,这种用法并不常见。

       通过host确定的IP,在ISC DHCP中被称为fixedIP,其对应的lease自然被称为fixedlease。


        2,通过client identifier获得fixed lease

if (find_hosts_by_uid (&hp, client_identifier.data,client_identifier.len, MDL)) {   
/* Remember if we know of this client. */
packet -> known = 1;
mockup_lease (&fixed_lease, packet, share, hp);
}

        在通过host来确定要分配IP的同时,也会使用函数find_lease_by_uid(&uid_lease,client_identifer.data, client_identifier.len, MDL)和find_lease_by_hw_addr(&hw_lease, h.hbuf, h.hlen,MDL)来直接通过client_identifier和MAC地址确定lease。

       在代码中,有两个lease的hash表,分别以client identifier和hardware address为key。从这里可以看出,DHCP server一般情况下,都是会给client上次同样的IP。


        3,获得client的请求地址

        client除了被动的接受server给分配的IP,它还可以主动向server提出要求,要求想要得到的IP。这是通过client在DHCP包中的client地址,或者使用requestedaddress option来实现的。

if (packet -> raw -> ciaddr.s_addr)
{cip.len = 4;
  memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
} 
else 
{/* Look up the requested address. */
oc = lookup_option (&dhcp_universe, packet -> ptions,DHO_DHCP_REQUESTED_ADDRESS);
memset (&d1, 0, sizeof d1);
	if (oc &&evaluate_option_cache (&d1, packet, (struct lease *)0,(struct client_state *)0,packet -> options,(struct option_state *)0,&global_scope, oc, MDL)) 
	{packet -> got_requested_address = 1;
		cip.len = 4;
		memcpy (cip.iabuf, d1.data, cip.len);
		data_string_forget (&d1, MDL);
	}else
	cip.len = 0;
}


        server会使用这个cip来分配一个lease,然后再通过一系列检查来判断是否可以使用这个lease。在不与其他配置的不冲突的情况下,尽量满足client的要求,分配给其要求的IP。
确定分配IP的优先级是fixed lease > uid lease > hw lease > ip lease,其 fixed lease是配置文件中host所配置的lease,uid lease 是通过uid确定的lease,hw lease是通过硬件地址查找到的lease,ip lease是client希望请求得到的lease。


四, DISCOVER报文的处理流程









分析 neutron 的源码需要对 OpenStack 的整体架构有一定的了解,因为 neutron 作为 OpenStack 中的网络服务模块,需要与其他模块进行协同工作,如 Nova、Cinder 等。 neutron 的源码主要分为以下几个部分: 1. API:neutron 的 API 部分负责接收来自用户的请求,并将请求转发给 neutron 的后端服务。API 部分的代码位于 `neutron/api` 目录下。 2. Plugin:neutron 的插件部分负责实现具体的网络功能,如虚拟网络、路由、安全组等。在 neutron 中,插件是通过扩展 neutron 的核心服务来实现的,因此插件的代码位于 `neutron/services` 目录下。 3. Agent:neutron 的代理部分负责将插件提供的网络功能在计算节点上实现,如 DHCP、L3 路由等。代理部分的代码位于 `neutron/agent` 目录下。 4. DB:neutron 的数据库部分负责存储和管理网络相关的数据,如网络拓扑、地址池等。数据库部分的代码位于 `neutron/db` 目录下。 5. Common:neutron 的通用部分包括一些用于网络操作的公共模块,如网络异常处理、配置文件解析等。通用部分的代码位于 `neutron/common` 目录下。 在源码分析过程中,我们可以从以下几个方面入手: 1. 核心服务:了解 neutron 核心服务的实现,包括 API、插件、代理、数据库等。 2. 数据库模型:了解 neutron 数据库模型的设计,包括数据表结构、表关系等。 3. API 接口:了解 neutron API 的接口设计和处理流程,包括请求处理、响应处理等。 4. 插件实现:了解 neutron 插件的实现方式和功能,包括虚拟网络、路由、安全组等。 5. 代理实现:了解 neutron 代理的实现方式和功能,包括 DHCP、L3 路由等。 总之,分析 neutron 的源码需要深入了解整个 OpenStack 的架构和设计理念,同时需要对网络技术有一定的理解和应用经验。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值