技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 源码分析 --> TCP/IP源码学习——inet_select_addr函数分析

TCP/IP源码学习——inet_select_addr函数分析

浏览:1710次  出处信息
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
当一个网卡配置了多个IP时,那么kernel选择哪个IP作为源IP来发送数据包呢?对于IPv4来说,这是由函数inet_select_addr决定的。

  1. __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
  2. {
  3.     __be32 addr = 0;
  4.     struct in_device *in_dev;
  5.     struct net *net = dev_net(dev);
  6.     rcu_read_lock();
  7.     /* 
  8.     得到该device的IP地址配置
  9.     一直比较奇怪为什么IP的地址配置对应的结构,名字为in_device。device的internet地址?
  10.     */
  11.     in_dev = __in_dev_get_rcu(dev);
  12.     if (!in_dev)
  13.         goto no_in_dev;
      
      //遍历primary地址
  1.     for_primary_ifa(in_dev) {
  2.         /*
  3.         这里对IP地址的scope进行判,其实用的枚举与route的scope相同
  4. enum rt_scope_t {
  5.     RT_SCOPE_UNIVERSE=0,
  6. /* User defined values  */
  7.     RT_SCOPE_SITE=200,
  8.     RT_SCOPE_LINK=253,
  9.     RT_SCOPE_HOST=254,
  10.     RT_SCOPE_NOWHERE=255
  11. };
  12.       那么,scope的值越小,对于IP地址来说,其表示的可用范围也就越大。(关于IP address的scope,参见 http://www.embeddedlinux.org.cn/linux_net/0596002556/understandlni-CHP-30-SECT-2.html#understandlni-CHP-30-SECT-2.1 )因此,当ifa->ifa_scope的值大于参数scope时,那么其就不符合要求。
  13.         */
  14.         if (ifa->ifa_scope > scope)
  15.             continue;

         /*
         1. 当dst目的地址为0时,那么该地址就可以直接作为备选
         2. 当dst不为0时,需要地址与dst匹配,即处于同一网络。
         */
  1.         if (!dst || inet_ifa_match(dst, ifa)) {
  2.             /* 找到了匹配的地址,可以跳出循环了 */
  3.             addr = ifa->ifa_local;
  4.             break;
  5.         }

         /* 将第一个primary地址保存到addr中,作为一个默认候选地址 */
  1.         if (!addr)
  2.             addr = ifa->ifa_local;
  3.     } endfor_ifa(in_dev);

     /*
     找到了可以使用的地址。
     从上面可以看出,这里有两种成功找到的情况
     1. 真正找到了匹配的地址
     2. 如没有匹配的地址,那么就使用符合scope要求的第一个primary 地址
     */
  1.     if (addr)
  2.         goto out_unlock;
  3. no_in_dev:
  4.     /* Not loopback addresses on loopback should be preferred
  5.      in this case. It is importnat that lo is the first interface
  6.      in dev_base list.
  7.      */
  8.     /* 在指定的device上找不到匹配的地址,只能遍历所有的device了*/
  9.     for_each_netdev_rcu(net, dev) {
  10.         in_dev = __in_dev_get_rcu(dev);
  11.         if (!in_dev)
  12.             continue;

          /* 同样是遍历primary地址*/
  1.         for_primary_ifa(in_dev) {
  2.             /* 
  3.             只要符合scope的要求,就可以使用了
  4.             这里特意排除了RT_SCOPE_LINK这种情况。这样的IP地址被用于与整个儿LAN通信,但是为什么前面不需要这个检查呢?
  5.             还有一个问题,为什么这里不进行目的地址dst是否匹配的判断了呢?
  6.             */
  7.             if (ifa->ifa_scope != RT_SCOPE_LINK &&
  8.              ifa->ifa_scope <= scope) {
  9.                 addr = ifa->ifa_local;
  10.                 goto out_unlock;
  11.             }
  12.         } endfor_ifa(in_dev);
  13.     }
  14. out_unlock:
  15.     rcu_read_unlock();
  16.     return addr;
  17. }
再顺便看一下函数inet_ifa_match,该函数用于判断IP地址是否处于同一网段内。
  1. static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
  2. {
  3.     return !((addr^ifa->ifa_address)&ifa->ifa_mask);
  4. }

也就是!((IP1^IP2)&mask)。对于这个需求,最直观的作法是IP1&mask == IP2&mask。经过测试,前者要比后面那个直观的作法快10%以上。


那么kernel中的作法需要如何理解呢?其实我们完全可以从后面那个直观的作法得到kernel的作法。
IP1&mask == IP2&mask  ---> !((IP1&mask)^(IP2&mask)) ---->!((IP1^IP2)&mask)
这下理解了吧?两个IP做异或,如果其网络部分如果相同,那么网络部分异或的值应该为0.而主机部分的值则通过mask屏蔽掉。最后取反,就得到了是否匹配的结果。
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1