local 本地路由生成


ifconfig调用流程

ioctl(4, SIOCSIFADDR, {ifr_name="eth6", ifr_addr={AF_INET, inet_addr("20.20.20.20")}}) = 0
ioctl(4, SIOCGIFFLAGS, {ifr_name="eth6", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_MULTICAST}) = 0
ioctl(4, SIOCSIFFLAGS, {ifr_name="eth6", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(4, SIOCSIFNETMASK, {ifr_name="eth6", ifr_netmask={AF_INET, inet_addr("255.255.255.0")}}) = 0

  ifconfig配置IP地址的函数调用,第一个内核里被调用到的函数是inet_ioctl(),经过devinet_ioctl函数会调用fib_inetaddr_event():

net/ipv4/af_inet.c inet_ioctl()
net/ipv4/devinet.c devinet_ioctl()
    blocking_notifier_call_chain(&inetaddr_chain,NETDEV_UP, ifa); 执行接口通知连

static struct notifier_block fib_inetaddr_notifier = {
  .notifier_call = fib_inetaddr_event,
};
  register_inetaddr_notifier(&fib_inetaddr_notifier);
int register_inetaddr_notifier(struct notifier_block *nb)
{
  return blocking_notifier_chain_register(&inetaddr_chain, nb);
}

  fib_inetaddr_event()这个函数是注册的通知链函数,从这个函数的命名(fib forward information base)就可以看出其和路由是有直接关系关系的,就从这个函数开始。 

static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
{
    struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
    struct net_device *dev = ifa->ifa_dev->dev;
    struct net *net = dev_net(dev);
    switch (event) {
    case NETDEV_UP:
        fib_add_ifaddr(ifa);
-----------------------------------
        break;
----------------------
    return NOTIFY_DONE;
}
/*
在添加IP地址时,内核会自动维护本机相关的路由项,具体如下:
在local表中添加一条目的地址为该IP地址的主机路由项(子网掩码长度为32),使得接收数据时可以精准匹配;
在local表中添加一条目的地址为该IP广播地址的主机路由项,使得收发的广播数据可以精准匹配;
在main表添加一条到该IP地址所在网络的网络路由项,使得可以到达该网络;
在local表中添加目的地址为该子网的广播地址路由项;

*/
void fib_add_ifaddr(struct in_ifaddr *ifa)
{
    struct in_device *in_dev = ifa->ifa_dev;
    struct net_device *dev = in_dev->dev;
    struct in_ifaddr *prim = ifa;
    __be32 mask = ifa->ifa_mask;
    __be32 addr = ifa->ifa_local;
    __be32 prefix = ifa->ifa_address & mask;

    if (ifa->ifa_flags & IFA_F_SECONDARY) {
        prim = inet_ifa_byprefix(in_dev, prefix, mask);
        if (!prim) {//如果flag参数指定了配置IP对象为从属设备或者临时设备,但是根据索引找不到该设备,返回错误
            pr_warn("%s: bug: prim == NULL\n", __func__);
            return;
        }
    }
/*/在ID等于255的表中,插入一条目的地址为主地址的主机路由项(子网掩码长度为32)。
//ID等于255的表示是LOCAL表,即该表中的所有IP项的目的地址均是本机。类型见IP地址类型。*/
    fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);

    if (!(dev->flags & IFF_UP))// 网络设备尚未使能,后续地址不添加
        return;

    /* Add broadcast address, if it is explicitly assigned. 处理广播包的路由项。主机号全为1和主机号全为0,实际的过程有点复杂*/
    //添加一条到所在网络广播地址的主机路由项
    if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
        fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);

    if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&
        (prefix != addr || ifa->ifa_prefixlen < 32)) {
        if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))// 添加的地址是某个子网中的一个主机地址,那么也需要生成到达该子网的路由项
            fib_magic(RTM_NEWROUTE, dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
                  prefix, ifa->ifa_prefixlen, prim);

        /* Add network specific broadcasts, when it takes a sense
        生成到达该子网广播地址的路由项,这里可以看出,主机号全0和全1都是去往该网络的广播地址*/
        if (ifa->ifa_prefixlen < 31) {
            fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
            fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask,
                  32, prim);
        }
    }
}
/*
在添加IP地址时,内核会自动维护本机相关的路由项,具体如下:
在local表中添加一条目的地址为该IP地址的主机路由项(子网掩码长度为32),使得接收数据时可以精准匹配;
在local表中添加一条目的地址为该IP广播地址的主机路由项,使得收发的广播数据可以精准匹配;
在main表添加一条到该IP地址所在网络的网络路由项,使得可以到达该网络;
在local表中添加目的地址为该子网的广播地址路由项;

*/
void fib_add_ifaddr(struct in_ifaddr *ifa)
{
    struct in_device *in_dev = ifa->ifa_dev;
    struct net_device *dev = in_dev->dev;
    struct in_ifaddr *prim = ifa;
    __be32 mask = ifa->ifa_mask;
    __be32 addr = ifa->ifa_local;
    __be32 prefix = ifa->ifa_address & mask;

    if (ifa->ifa_flags & IFA_F_SECONDARY) {
        prim = inet_ifa_byprefix(in_dev, prefix, mask);
        if (!prim) {//如果flag参数指定了配置IP对象为从属设备或者临时设备,但是根据索引找不到该设备,返回错误
            pr_warn("%s: bug: prim == NULL\n", __func__);
            return;
        }
    }
/*/在ID等于255的表中,插入一条目的地址为主地址的主机路由项(子网掩码长度为32)。
//ID等于255的表示是LOCAL表,即该表中的所有IP项的目的地址均是本机。类型见IP地址类型。*/
    fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);

    if (!(dev->flags & IFF_UP))// 网络设备尚未使能,后续地址不添加
        return;

    /* Add broadcast address, if it is explicitly assigned. 处理广播包的路由项。主机号全为1和主机号全为0,实际的过程有点复杂*/
    //添加一条到所在网络广播地址的主机路由项
    if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
        fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);

    if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&
        (prefix != addr || ifa->ifa_prefixlen < 32)) {
        if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))// 添加的地址是某个子网中的一个主机地址,那么也需要生成到达该子网的路由项
            fib_magic(RTM_NEWROUTE, dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
                  prefix, ifa->ifa_prefixlen, prim);

        /* Add network specific broadcasts, when it takes a sense
        生成到达该子网广播地址的路由项,这里可以看出,主机号全0和全1都是去往该网络的广播地址*/
        if (ifa->ifa_prefixlen < 31) {
            fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
            fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask,
                  32, prim);
        }
    }
}

相关