添加如下的路由策略:
# ip rule add from 10.0.1.3 oif ens33 lookup 10
#
# ip rule
0: from all lookup local
32760: from 10.0.1.3 lookup 10
32766: from all lookup main
32767: from all lookup default
在策略路由表10中,添加默认网关192.168.0.40。查看可见出接口为ens33。
# ip route add default via 192.168.0.40 table 10
#
#
# ip route show table 10
default via 192.168.0.40 dev ens33
如下删除接口ens33接口上的IP地址,路由规则还在,但是路由表10中的ens33相关表项已经被清除,导致策略路由失效。
# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.4 netmask 255.255.255.0 broadcast 192.168.0.255
#
# ip addr del 192.168.0.4/24 dev ens33
#
# ip rule
0: from all lookup local
32760: from 10.0.1.3 lookup 10
32766: from all lookup main
32767: from all lookup default
#
# ip route show table 10
#
或者将出接口ens33设置为down状态,也将清除路由表10中的路由表项。这导致在接口UP/DOWN,或者修改接口地址时,要重新下发策略路由表中的表项。
# ip link set ens33 down
路由项删除
如下在地址删除处理中,向inetaddr_chain链发送NETDEV_DOWN消息。
static void __inet_del_ifa(struct in_device *in_dev,
struct in_ifaddr __rcu **ifap,
int destroy, struct nlmsghdr *nlh, u32 portid)
{
rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
路由子系统在inetaddr_chain上注册了处理函数fib_inetaddr_event,由函数fib_del_ifaddr处理IP地址删除事件。如果此接口上没有IP地址,由函数fib_disable_ip处理。
static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
switch (event) {
case NETDEV_DOWN:
fib_del_ifaddr(ifa, NULL);
atomic_inc(&net->ipv4.dev_addr_genid);
if (!ifa->ifa_dev->ifa_list) {
/* Last address was deleted from this interface.
* Disable IP.
*/
fib_disable_ip(dev, event, true);
}
如下fib_disable_ip函数,处理路由表项及缓存的删除。
static void fib_disable_ip(struct net_device *dev, unsigned long event,
bool force)
{
if (fib_sync_down_dev(dev, event, force))
fib_flush(dev_net(dev));
else
rt_cache_flush(dev_net(dev));
arp_ifdown(dev);
遍历接口设备相关联的路由表项,将下一跳设置为RTNH_F_DEAD。
int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force)
{
int scope = RT_SCOPE_NOWHERE;
unsigned int hash = fib_devindex_hashfn(dev->ifindex);
struct hlist_head *head = &fib_info_devhash[hash];
hlist_for_each_entry(nh, head, nh_hash) {
struct fib_info *fi = nh->nh_parent;
if (nh->fib_nh_dev != dev || fi == prev_fi)
continue;
change_nexthops(fi) {
if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD)
dead++;
else if (nexthop_nh->fib_nh_dev == dev &&
nexthop_nh->fib_nh_scope != scope) {
switch (event) {
case NETDEV_DOWN:
case NETDEV_UNREGISTER:
nexthop_nh->fib_nh_flags |= RTNH_F_DEAD;
最后,fib_flush遍历命名空间中所有的路由表,已经每个表中的路由项,删除设置了RTNH_F_DEAD标志的表项。
void fib_flush(struct net *net)
{
for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
struct hlist_head *head = &net->ipv4.fib_table_hash[h];
struct hlist_node *tmp;
struct fib_table *tb;
hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
flushed += fib_table_flush(net, tb, false);
}
if (flushed)
rt_cache_flush(net);
内核版本 5.10