ipv6 udp sendto (发包->查找出接口(路由))流程分析

在发送ipv6 udp单播报文时,发包失败,sendto接口一直报-1,发包失败。经过排查socket状态
、ipv6 nd和路由状态也都正常,只好进入内核查看错误原因由此总结了ipv6 udp sendto 
(发包->查找出接口(路由))流程。

Linux版本4.1.15

1、问题分析:

        经过跟踪排查,发现在rt6_selete函数中的find_rr_leaf函数中无法找到合适的出接口导致报错
在find_match中,会使用rt6_score_route->rt6_check_dev流程对oif和rt_info路由信息进行对比
这时对比能正常找到路由的v6 ping流程,发现oif值存在问题(不为空),有一个奇怪的值。

        `oif` 参数表示发送数据包的出口网络接口(即出接口)。它指定了数据包从哪个网络接口发送。这个参数实际上是在IPv6协议栈传输数据时根据用户传递的参数在不同的函数之间进行传递的。对于当前流程来说,oif字段来源于udpv6_sendmsg函数中初始化的f16.flowi6_oif,而这个值来源于sendto接口中传入的sin6_scope_id。最终问题发现了,sockaddr_in6结构体没有正确初始化,scope_id存在非0值。
    这时我们还是要回头来看一下这个sin6_scope_id到底是什么东西。在其他博主的blog中发现了
解释
“这个字段在我们使用链路本地地址来编程的时候是必须要使用的,这个字段表示我们需要选择接口ID。为什么需要需要有这么一个字段,那是因为链路本地地址的特殊性,一个网络节点可以有多个网络接口,多个网络接口可以有相同的链路本地地址,例如我们需要bind一个本地链路地址,这个时候就会有冲突,操作系统无法决策需要绑定的是哪个接口的本地链路地址。又例如,如果我们在直连的2个主机之间直接用链路本地地址ping的话,会ping失败。因此IPv6引入了scope_id来解决这个问题,scope_id指定了使用哪个网络接口。“

struct sockaddr_in6
 {
    u_char sin6_len; 
    u_char sin6_family; 
    u_int16_t sin6_port; 
    u_int32_t sin6_flowinfo; 
    struct in6_addr sin6_addr;
    u_int32_t sin6_scope_id;
}

        其中ip6_dst_lookup_tail是路由查找中很重要的一环,是 IPv6 网络层中查找数据包应该发送到哪个目的地的最后一步。这个函数的作用是将网络层的源地址和目的地地址转换成实际的网络接口和终点地址,以便完成数据包的发送。该函数的原理是根据传入的目的地址和传输层提供的端口信息,查询 Linux 内核的路由表,以获取该数据包应该发送到哪个下一跳路由器或终点设备。如果该数据包的目的地就是本地接收机,那么该函数将返回表示该本地接收机的网卡信息,以便网络层将数据包传递给数据链路层发出。这个函数的代码大致流程是:

1. 以传入的目的地址为关键字搜索路由表中的匹配项,如果找到了匹配项则进入下一步,否则返回空指针。
2. 如果找到了匹配项,则将路径缓存和路由表项结合起来,形成最终的目的地网络信息。
3. 如果传输层提供了源/目的端口信息,则使用这些信息来查找并缓存匹配的本地连接信息。
4. 如果该数据包要发往本地接收机,则返回表示本地接收机的网卡接口信息。
5. 否则,使用转发表信息将目的地址转换成下一个跳的地址,返回表示下一个跳设备的网卡接口信息。

以下是sendto到查找路由的流程图

以下是在路由函数中传递的rt6_info结构体解释

struct rt6_info {
    struct in6_addr rt6i_dst; // 目的地址
    struct in6_addr rt6i_src; // 源地址
    struct net_device *rt6i_dev; // 与路由关联的网络设备
    atomic_t rt6i_ref; // 引用计数
    const struct in6_addr *rt6i_peer; // 对等体地址(例如,邻居发现中的邻居)
    struct inet6_dev *rt6i_idev; // 内嵌的 inet6_dev 结构,包含网络接口的 IPv6 信息
    u32 rt6i_flags; // 路由标志,例如 RTF_GATEWAY 表示有网关
    struct rt6key rt6i_dst_entry; // 目的地路由缓存的键
    struct list_head rt6i_list; // 链接到全局路由表的列表
    struct inet6_ifaddr *rt6i_ifaddr; // 关联的 IPv6 接口地址
    struct fib6_table *rt6i_table; // 路由表
    struct hlist_node rt6i_node; // 用于散列表的节点
    struct rt6_info *rt6i_peer; // 指向对等路由的指针
    u32 rt6i_metric; // 路由度量
    u32 rt6i_pmtu; // 路由的最大传输单元
    u32 rt6i_expmode; // 过期模式
    struct neighbour *rt6i_n; // 邻居结构体
    struct rt6_statistics rt6i_stats; // 路由统计信息
    struct {
        u32 base_btit; // 基本的二进制时间间隔
        u32 tbit; // 特定的二进制时间间隔
        u8 tbit_state; // tbit 状态
    } rt6i_peer_metrics;
};

2、命令记录

查看ipv6 nd状态

ip -6 neigh

查看ipv6路由

route -n -A inet6
ip -6 route show

添加、删除ipv6路由

ip -6 route add fe80::1000:1/128 dev eth0 metric 100 配置路由并指定出接口及metric(路由距离,到达指定网络所需的中转数。metric的值越小,优先级越高)
ipv6 -6 route del fe80::1000:1/128

引用文章·

Linux下IPV6 Connect_ipv6 scope id-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值