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); } } }