IPv4路由下一跳合法性检测

函数fib_check_nh检测下一跳的合法性。

int fib_check_nh(struct net *net, struct fib_nh *nh, u32 table, u8 scope, struct netlink_ext_ack *extack)
{
    int err;

    if (nh->fib_nh_gw_family == AF_INET)
        err = fib_check_nh_v4_gw(net, nh, table, scope, extack);
    else if (nh->fib_nh_gw_family == AF_INET6)
        err = fib_check_nh_v6_gw(net, nh, table, extack);
    else
        err = fib_check_nh_nongw(net, nh, extack);

IPv4下一跳检查

内核代码中注释内容显示了下一跳网关检查的4种情况:

* a) gateway can be actually local interface address, so that gatewayed route is direct.
     网关可以是本地接口上的地址,所以目的网络是直连的。
* b) gateway must be on-link address, possibly described not by an ifaddr, but also by a direct route.
     网关必须是链路可达地址,可以不是和接口地址同网段,或是直连路由可达。
* c) If both gateway and interface are specified, they should not contradict.
     如果同时指定了网关和出接口,两者不能冲突。
* d) If we use tunnel routes, gateway could be not on-link.
     如果使用隧道路由,网关可以不是链路可达地址(需指定onlink命令字)。

如下路由配置:

# /a/
# ip address
4: ens35: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:ea:2e:3b brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.2/32 scope global ens35
       valid_lft forever preferred_lft forever
#
# ip route add 192.168.10.0/24 via 192.168.10.2
# ip route add 192.168.5.0/24 via 192.168.10.2
#
# ip route list
192.168.5.0/24 via 192.168.10.2 dev ens35 
192.168.10.0/24 via 192.168.10.2 dev ens35
#
# /c/
# ip route add 192.168.7.0/24 via 192.168.10.2 dev ens35
#
# /d/
# ip route add 10.10.1.0/24 via 192.168.10.1 dev ens35 onlink 

先看一下onlink路由的配置,由于指定了错误scope值,IP命令配置路由时返回以下错误:

# ip route add 10.10.1.0/24 via 192.168.10.1 scope 255 onlink 
Error: Invalid scope.
#
# ip route add 10.10.1.0/24 via 192.168.10.1 scope host onlink  
Error: Route with host scope can not have a gateway.

如下在fib_create_info中的代码,如果scope值大于RT_SCOPE_HOST(254),就认为是不合法的scope值,即到达目的网络最近的就是RT_SCOPE_HOST。另外,如果scope值等于RT_SCOPE_HOST,不能指定路由网关。

struct fib_info *fib_create_info(struct fib_config *cfg, struct netlink_ext_ack *extack)
{
    if (cfg->fc_scope > RT_SCOPE_HOST) {     /* RT_SCOPE_HOST */
        NL_SET_ERR_MSG(extack, "Invalid scope");
        goto err_inval;
    }
    if (fi->nh) {
        ...
    } else if (cfg->fc_scope == RT_SCOPE_HOST) {
        struct fib_nh *nh = fi->fib_nh;

        /* Local address is added. */
        if (nhs != 1) {
            NL_SET_ERR_MSG(extack, "Route with host scope can not have multiple nexthops");
            goto err_inval;
        }
        if (nh->fib_nh_gw_family) {
            NL_SET_ERR_MSG(extack, "Route with host scope can not have a gateway");
            goto err_inval;
        }

以下IP命令将触发内核函数fib_check_nh_v4_gw中的错误检查,对于当前链路路由(RTNH_F_ONLINK),如下所示:

# ip addr add 192.168.10.2/32 dev ens35
# 
# ip route add 10.10.1.0/24 via 192.168.10.1 scope 253 onlink   
Error: Nexthop has invalid scope.
#
# ip route add 10.10.1.0/24 via 192.168.10.1 onlink   
Error: Nexthop device required for onlink.
# 
# ip route add 10.10.1.0/24 via 192.168.10.1 dev ens35 onlink
Error: Nexthop device is not up.
#
# ip route add 10.10.1.0/24 via 192.168.10.1 dev ens35
Error: Nexthop has invalid gateway.

IP命令的onlink关键字对于与内核代码中的标志位RTNH_F_ONLINK,那么scope值不应大于等于RT_SCOPE_LINK。如果scope值等于RT_SCOPE_LINK,或者更大的值RT_SCOPE_HOST,就没有必要指定onlink命令字。

static int fib_check_nh_v4_gw(struct net *net, struct fib_nh *nh, u32 table, u8 scope, struct netlink_ext_ack *extack)
{
    if (nh->fib_nh_flags & RTNH_F_ONLINK) {
        unsigned int addr_type;

        if (scope >= RT_SCOPE_LINK) {
            NL_SET_ERR_MSG(extack, "Nexthop has invalid scope");
            return -EINVAL;
        }
        dev = __dev_get_by_index(net, nh->fib_nh_oif);
        if (!dev) {
            NL_SET_ERR_MSG(extack, "Nexthop device required for onlink");
            return -ENODEV;
        }
        if (!(dev->flags & IFF_UP)) {
            NL_SET_ERR_MSG(extack, "Nexthop device is not up");
            return -ENETDOWN;
        }
        addr_type = inet_addr_type_dev_table(net, dev, nh->fib_nh_gw4);
        if (addr_type != RTN_UNICAST) {
            NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
            return -EINVAL;
        }
        if (!netif_carrier_ok(dev))
            nh->fib_nh_flags |= RTNH_F_LINKDOWN;
        nh->fib_nh_dev = dev;
        dev_hold(dev);
        nh->fib_nh_scope = RT_SCOPE_LINK;
        return 0;
    }

以下为没有指定onlink关键字的情况(通常情况不指定),需要验证网关的可达性。对于可达网关,其scope应当等于RT_SCOPE_LINK或者RT_SCOPE_HOST,以下路由查询获取到达网关的路由,将scope的最小值限定在RT_SCOPE_LINK值。

如果指定了路由表(非主路由表),在其中进行路由查询;否则,未指定路由表,或者以上查询失败的情况下,在主路由表和本地路由表中(RT_TABLE_MAIN/RT_TABLE_LOCAL)进行查询。

    {
        struct fib_table *tbl = NULL;
        struct flowi4 fl4 = {
            .daddr = nh->fib_nh_gw4,
            .flowi4_scope = scope + 1,
            .flowi4_oif = nh->fib_nh_oif,
            .flowi4_iif = LOOPBACK_IFINDEX,
        };

        /* It is not necessary, but requires a bit of thinking */
        if (fl4.flowi4_scope < RT_SCOPE_LINK)
            fl4.flowi4_scope = RT_SCOPE_LINK;

        if (table && table != RT_TABLE_MAIN)
            tbl = fib_get_table(net, table);
        if (tbl)
            err = fib_table_lookup(tbl, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE | FIB_LOOKUP_NOREF);

        /* on error or if no table given do full lookup. This
         * is needed for example when nexthops are in the local
         * table rather than the given table
         */
        if (!tbl || err) {
            err = fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE);
        }
        if (err) {
            NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
            goto out;
        }
    }

合法的网关路由类型需要是单播类型(RTN_UNICAST)或者本地类型(RTN_LOCAL),否则为无效网关。如果路由查询结果的出接口为空,返回错误。最后,根据出口设备的链路状态设置下一跳结构的状态位。

    err = -EINVAL;
    if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) {
        NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
        goto out;
    }
    nh->fib_nh_scope = res.scope;
    nh->fib_nh_oif = FIB_RES_OIF(res);
    nh->fib_nh_dev = dev = FIB_RES_DEV(res);
    if (!dev) {
        NL_SET_ERR_MSG(extack, "No egress device for nexthop gateway");
        goto out;
    }
    dev_hold(dev);
    if (!netif_carrier_ok(dev))
        nh->fib_nh_flags |= RTNH_F_LINKDOWN;
    err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN;

另外,fl4结构成员flowi4_scope的值等于参数scope+1,例如scope值为RT_SCOPE_LINK,增加1之后变成RT_SCOPE_HOST。即对于RT_SCOPE_LINK类型的路由网关,要求查询到的到网关的路由的scope值小于大于等于RT_SCOPE_HOST。参见在路由查询函数fib_table_lookup中,对flowi4_scope的判断。

对于scope为RT_SCOPE_HOST的情况,不需要网关。对于scope为RT_SCOPE_NOWHERE(255)的情况,增加1之后为0,对于小于RT_SCOPE_LINK的scope,都按照RT_SCOPE_NOWHERE进行路由查询。

int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct fib_result *res, int fib_flags)
{

    /* Step 3: Process the leaf, if that fails fall back to backtracing */
    hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) {
        struct fib_info *fi = fa->fa_info;

        if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos)
            continue;
        if (fi->fib_dead)
            continue;
        if (fa->fa_info->fib_scope < flp->flowi4_scope)
            continue;

未设置网关下一跳检查

以下IP命令配置无网关的路由项:

# ip route add 192.168.30.0/24 dev ens35 onlink
Error: Invalid flags for nexthop - PERVASIVE and ONLINK can not be set.
#
# ip route add 192.168.30.0/24 dev ens35
Error: Device for nexthop is not up.

对于未配置网关的路由,不能设置RTNH_F_PERVASIVE或者RTNH_F_ONLINK标志,前者用于递归查找网关,或者用于指定网关在本地链路上。未配置网关的路由项需要指定出口设备,如果出口设备没有启用,返回错误。

static int fib_check_nh_nongw(struct net *net, struct fib_nh *nh, struct netlink_ext_ack *extack)
{
    struct in_device *in_dev;
    int err;

    if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) {
        NL_SET_ERR_MSG(extack, "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set");
        return -EINVAL;
    }
    rcu_read_lock();

    err = -ENODEV;
    in_dev = inetdev_by_index(net, nh->fib_nh_oif);
    if (!in_dev)
        goto out;
    err = -ENETDOWN;
    if (!(in_dev->dev->flags & IFF_UP)) {
        NL_SET_ERR_MSG(extack, "Device for nexthop is not up");
        goto out;
    }
    nh->fib_nh_dev = in_dev->dev;
    dev_hold(nh->fib_nh_dev);
    nh->fib_nh_scope = RT_SCOPE_HOST;
    if (!netif_carrier_ok(nh->fib_nh_dev))
        nh->fib_nh_flags |= RTNH_F_LINKDOWN;

内核版本 5.10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值