linux-kernel-ecmp-ipv4
当使用ip route add/del添加或者删除路由时,通过触发netlink发送信息到各协议路由系统注册的netlink处理函数,如add时调用函数为inet_rtm_newroute。Equal Cost Multi Path,在ip交换网络中存在到达同一目的地址的多条不同的路径,而且每条路径消耗的资源(cost)一样时。内核定制了CONFIG_IP_ROUTE_MULTIPATH时,ip层在收到等价的ip报文时,会根据配置的策略通过不通的路径均衡转发出去,使得转发达到负载均衡的目的。
1、路由初始化大概结构
路由初始化主要函数为:
//路由缓存初始化
int __init ip_rt_init(void)
{int rc = 0;#ifdef CONFIG_IP_ROUTE_CLASSID//基于路由的分类器,每个CPU256个变量ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct));if (!ip_rt_acct)panic("IP: failed to allocate ip_rt_acct\n");
#endif//路由缓存池ipv4_dst_ops.kmem_cachep =kmem_cache_create("ip_dst_cache", sizeof(struct rtable), 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);ipv4_dst_blackhole_ops.kmem_cachep = ipv4_dst_ops.kmem_cachep;//初始化每CPU变量if (dst_entries_init(&ipv4_dst_ops) < 0)panic("IP: failed to allocate ipv4_dst_ops counter\n");//初始化每CPU变量if (dst_entries_init(&ipv4_dst_blackhole_ops) < 0)panic("IP: failed to allocate ipv4_dst_blackhole_ops counter\n");//建立路由缓存hash表rt_hash_table = (struct rt_hash_bucket *)alloc_large_system_hash("IP route cache",sizeof(struct rt_hash_bucket),rhash_entries,(totalram_pages >= 128 * 1024) ?15 : 17,0,&rt_hash_log,&rt_hash_mask,rhash_entries ? 0 : 512 * 1024);//初始化路由缓存hash表memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket));//每个hash表rt_hash_lock_init();//设置gc时间和缓存最大数量ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1);ip_rt_max_size = (rt_hash_mask + 1) * 16;//初始化devinet_init();//注册通知链和创建alias缓存ip_fib_init();//注册gc任务INIT_DELAYED_WORK_DEFERRABLE(&expires_work, rt_worker_func);expires_ljiffies = jiffies;schedule_delayed_work(&expires_work,net_random() % ip_rt_gc_interval + ip_rt_gc_interval);if (ip_rt_proc_init())pr_err("Unable to create route proc files\n");
#ifdef CONFIG_XFRMxfrm_init();xfrm4_init(ip_rt_max_size);
#endif//注册netlink消息rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, NULL);#ifdef CONFIG_SYSCTLregister_pernet_subsys(&sysctl_route_ops);
#endifregister_pernet_subsys(&rt_genid_ops);return rc;
}
当使用ip route add/del添加或者删除路由时,通过触发netlink发送信息到各协议路由系统注册的netlink处理函数,如add时调用函数为inet_rtm_newroute。
void __init ip_fib_init(void)
{//注册netlink路由添加、删除和dump命令处理函数rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);//初始化路由表和路由缓存register_pernet_subsys(&fib_net_ops);//注册通知链处理函数,监听系统其它模块信息register_netdevice_notifier(&fib_netdev_notifier);register_inetaddr_notifier(&fib_inetaddr_notifier);//初始化路由用到的缓存池fib_trie_init();
}
而当协议报文下发时,调用ip_rt_init注册的inet_rtm_getroute进行路由转发。
sys_socketcall()-->sys_connect()-->inet_stream_connect()-->tcp_v4_connect()-->ip_route_connect()-->inet_rtm_getroute()。
2、inet_rtm_getroute
inet_rtm_getroute分配sk的路由信息,通过fib_lookup查询该dip的路由信息,最终由函数ip_mkroute_input创建路由缓存项。
int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
{
...skb = inet_rtm_getroute_build_skb(src, dst, ip_proto, sport, dport);if (!skb)return -ENOBUFS;memset(&fl4, 0, sizeof(fl4));fl4.daddr = dst;fl4.saddr = src;fl4.flowi4_tos = rtm->rtm_tos;fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0;fl4.flowi4_mark = mark;fl4.flowi4_uid = uid;if (sport)fl4.fl4_sport = sport;if (dport)fl4.fl4_dport = dport;fl4.flowi4_proto = ip_proto;rcu_read_lock();if (iif) {struct net_device *dev;dev = dev_get_by_index_rcu(net, iif);if (!dev) {err = -ENODEV;goto errout_rcu;}fl4.flowi4_iif = iif; /* for rt_fill_info */skb->dev = dev;skb->mark = mark;//路由此skb,获取路由信息err = ip_route_input_rcu(skb, dst, src, rtm->rtm_tos,dev, &res);rt = skb_rtable(skb);if (err == 0 && rt->dst.error)err = -rt->dst.error;} else {fl4.flowi4_iif = LOOPBACK_IFINDEX;rt = ip_route_output_key_hash_rcu(net, &fl4, &res, skb);err = 0;if (IS_ERR(rt))err = PTR_ERR(rt);elseskb_dst_set(skb, &rt->dst);}
...
}
int ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,u8 tos, struct net_device *dev, struct fib_result *res)
{...return ip_route_input_slow(skb, daddr, saddr, tos, dev, res);
}static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,u8 tos, struct net_device *dev, struct fib_result *res)
{
...err = fib_lookup(net, &fl4, res, 0);if (err != 0) {if (!IN_DEV_FORWARD(in_dev))err = -EHOSTUNREACH;goto no_route;}make_route:err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos, flkeys);
...
}
3、 ip_mkroute_input--ecmp选择出口路由
如果在上述fib_lookup流程中查找到该dip包含多个路径,则由函数fib_multipath_hash计算hash值,之后,函数fib_select_multipath通过hash值选择其中的某个下一跳。
static int ip_mkroute_input(struct sk_buff *skb,struct fib_result *res,struct in_device *in_dev,__be32 daddr, __be32 saddr, u32 tos,struct flow_keys *hkeys)
{
#ifdef CONFIG_IP_ROUTE_MULTIPATHif (res->fi && res->fi->fib_nhs > 1) {int h = fib_multipath_hash(res->fi->fib_net, NULL, skb, hkeys);fib_select_multipath(res, h);}
#endif/* create a routing cache entry */return __mkroute_input(skb, res, in_dev, daddr, saddr, tos);
}
路径数量判断:
如果fib_info的成员nh(下一跳对象)有值,根据其获得路径的数量。其次,nh为空时,使用fib_info结构成员fib_nhs的数量值。如果下一跳对象为组,并且组内有多个路径,返回组内路径数量。否则,返回1。
static inline unsigned int fib_info_num_path(const struct fib_info *fi)
{if (unlikely(fi->nh))return nexthop_num_path(fi->nh);return fi->fib_nhs;
}
static inline unsigned int nexthop_num_path(const struct nexthop *nh)
{unsigned int rc = 1;if (nh->is_group) {struct nh_group *nh_grp;nh_grp = rcu_dereference_rtnl(nh->nh_grp);if (nh_grp->mpath)rc = nh_grp->num_nh;}return rc;
}
3.1 fib_multipath_hash--多路径hash值的计算:
proc文件中fib_multipath_hash_policy参数用于指定路径选择时使用的hash策略。
# cat /proc/sys/net/ipv4/fib_multipath_hash_policy
0:基于三层头部数据做hash
1:基于四层hash
int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,const struct sk_buff *skb, struct flow_keys *flkeys)
{struct flow_keys hash_keys;u32 mhash;switch (net->ipv4.sysctl_fib_multipath_hash_policy) {case 0 :break;case 1:break;mhash = flow_hash_from_keys(&hash_keys);return mhash >> 1;
}
如果哈希策略fib_multipath_hash_policy值为0,使用流结构fl4中保存的源和目的IP地址,但是,如果skb有值,将使用函数ip_multipath_l3_keys获取源和目的IP地址,对于ICMP报文,此函数将得到内部IP头部中的IP地址信息;即,hash = sip xor dip。
case 0:memset(&hash_keys, 0, sizeof(hash_keys));hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;if (skb) {ip_multipath_l3_keys(skb, &hash_keys);} else {hash_keys.addrs.v4addrs.src = fl4->saddr;hash_keys.addrs.v4addrs.dst = fl4->daddr;}break;static void ip_multipath_l3_keys(const struct sk_buff *skb,struct flow_keys *hash_keys)
{const struct iphdr *outer_iph = ip_hdr(skb);const struct iphdr *key_iph = outer_iph;const struct iphdr *inner_iph;const struct icmphdr *icmph;struct iphdr _inner_iph;struct icmphdr _icmph;if (likely(outer_iph->protocol != IPPROTO_ICMP))goto out;if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0))goto out;icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph),&_icmph);if (!icmph)goto out;if (icmph->type != ICMP_DEST_UNREACH &&icmph->type != ICMP_REDIRECT &&icmph->type != ICMP_TIME_EXCEEDED &&icmph->type != ICMP_PARAMETERPROB)goto out;inner_iph = skb_header_pointer(skb,outer_iph->ihl * 4 + sizeof(_icmph),sizeof(_inner_iph), &_inner_iph);if (!inner_iph)goto out;key_iph = inner_iph;
out:hash_keys->addrs.v4addrs.src = key_iph->saddr;hash_keys->addrs.v4addrs.dst = key_iph->daddr;
}
当哈希策略值为1,根据skb是否为空,由以下两种处理。如果skb有值,并且已经计算了四层的哈希值,这里直接使用此值。否则,根据流的key值得到四层数据,包括:源和目的地址,源和目的端口号以及协议号。另外,如果skb为空,由flowl4结构的参数fl4中获取四层信息。
case 1:/* skb is currently provided only when forwarding */if (skb) {unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;struct flow_keys keys;/* short-circuit if we already have L4 hash present */if (skb->l4_hash)return skb_get_hash_raw(skb) >> 1;memset(&hash_keys, 0, sizeof(hash_keys));if (!flkeys) {skb_flow_dissect_flow_keys(skb, &keys, flag);flkeys = &keys;}hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;hash_keys.ports.src = flkeys->ports.src;hash_keys.ports.dst = flkeys->ports.dst;hash_keys.basic.ip_proto = flkeys->basic.ip_proto;} else {memset(&hash_keys, 0, sizeof(hash_keys));hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;hash_keys.addrs.v4addrs.src = fl4->saddr;hash_keys.addrs.v4addrs.dst = fl4->daddr;hash_keys.ports.src = fl4->fl4_sport;hash_keys.ports.dst = fl4->fl4_dport;hash_keys.basic.ip_proto = fl4->flowi4_proto;}break;}
3.2 fib_select_multipath-->路由出口选择
proc系统下的fib_multipath_use_neigh的值用于表示是否根据邻居表的状态选择路径,默认为0,表示不使用邻居表状态信息。
# cat /proc/sys/net/ipv4/fib_multipath_use_neigh
遍历所有的下一跳,如果没有开启fib_multipath_use_neigh,判断hash值是否小于当前下一跳的fib_nh_upper_bound值,为真则在结果中记录下当前下一跳的索引值和相关信息。在开启fib_multipath_use_neigh的情况下,将通过函数fib_good_nh来判断是否为可用的下一跳,fib_nh_upper_bound的值不仅与自身下一跳地址的权重相关,而且与当前路由的其它下一跳地址的权重也相关(fib_rebalance)。在下一跳地址数组中,fib_nh_upper_bound的值有小到大,被设置RTNH_F_DEAD标记的下一跳的fib_nh_upper_bound值为-1,不会被选择。
void fib_select_multipath(struct fib_result *res, int hash)
{struct fib_info *fi = res->fi;struct net *net = fi->fib_net;bool first = false;for_nexthops(fi) {if (net->ipv4.sysctl_fib_multipath_use_neigh) {if (!fib_good_nh(nh))continue;if (!first) {res->nh_sel = nhsel;first = true;}}if (hash > atomic_read(&nh->nh_upper_bound))continue;res->nh_sel = nhsel;return;} endfor_nexthops(fi);
}
相关文章:
linux-kernel-ecmp-ipv4
当使用ip route add/del添加或者删除路由时,通过触发netlink发送信息到各协议路由系统注册的netlink处理函数,如add时调用函数为inet_rtm_newroute。Equal Cost Multi Path,在ip交换网络中存在到达同一目的地址的多条不同的路径,而且每条路径…...
蒙特卡洛树搜索(MTCS)
一、目标 一种启发式的搜索算法,在搜索空间巨大的场景下比较有效 算法完成后得到一棵树,这棵树可以实现:给定一个游戏状态,直接选择最佳的下一步 二、算法四阶段 1、选择(Selection) 父节点选择UCB值最…...
【Verilog】——Verilog简介
目录 1.简介 2.什么是HDL以及HDL的功能 3.Verilog和C语言的比较 4.Verilog的用途 5.数字系统的抽象层次 1.系统级 2.算法级 3.RTL级(寄存器变换级) 6.数字系统抽象层级 7.自顶向下的结构化设计方法 8.Verilog建模 9.Verilog概述 10.Verilog模块的基本…...
【Python从入门到进阶】10、流程控制语句-循环语句(for-while)
接上篇《9、流程控制语句-条件语句(if-else)》 上一篇我们学习了Python的控制流语句的概念,以及其中的条件语句(if/else),本篇我们来学习控制流语句中的循环语句(for/while)。 一、Python中的循环 Python的循环结构就是让程序“杀个回马枪”࿰…...
超全的命令(代码)执行漏洞无回显的姿势总结(附带详细代码和测试分析过程)
目录 漏洞代码 突破方式 重定向 dnslog外部通信 burpsuite burpcollaborator外部通信 日志监听 netcat监听 反弹shell的各种姿势 漏洞代码 <?php shell_exec($_GET[a]); ?>这里使用了无回显的shell执行函数shell_exec,给html目录的权限是777 突破方…...
STM32MP157-Linux音频应用编程-简易语音助手
文章目录前言STM32MP157简易语音助手alsa-lib简介:移植alsa-lib库:libcurl库简介:移植libcurl库:API调用修改asrmain.c文件修改token.c文件录音文件IO打开音频文件硬件控制sysfs文件系统数据解析和控制多线程主循环实现效果及注意…...
Python-OpenCV图像处理:学习图像算术运算,如加减法、图像混合、按位运算,以及如何实现它们
目录 目标 图像添加 图像混合算法 按位运算 目标 学习对图像的几种算术运算,如加法、减法、位运算等。了解这些功能:cv.add()、...
并发编程——ReentrantLock
如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:耶瞳空间 一:基本介绍 从Java 5开始,引入了一个高级的处理并发的java.util.concurrent包,它提供了大量更高级的并发功能,能大大简化多线程程序的编写…...
English Learning - L2 第 3 次小组纠音 [ʌ] [ɒ] [ʊ] [ɪ] [ə] [e] 2023.3.4 周六
English Learning - L2 第 3 次小组纠音 [ʌ] [ɒ] [ʊ] [ɪ] [ə] [e] 2023.3.4 周六共性问题小元音 [ʌ]小元音 [ɒ]小元音 [ʊ]小元音 [ɪ]小元音 [ə]小元音 [e]我的发音问题纠音过程共性问题 小元音 [ʌ] 口型容易偏大 解决办法:因为嘴角没有放松,…...
STM32之关门狗
看门狗介绍在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入…...
Apollo控制部分1-- ControlComponent组件介绍
Apollo控制部分1-- ControlComponent组件介绍摘要一、ControlComponent1、启动文件解析2、ControlComponent()组件函数解析1)ControlComponent::ControlComponent() 构造函数2)ControlComponent::Init() 初始化函数(执行一次)3&am…...
0626-0631韩顺平Java Buffered字节处理流 学习笔记
如何去构建字节流package com.hspedu.outputstream_;import java.io.*;/*** author abner* version 1.0*/ public class BufferedCopy02 {public static void main(String[] args) {String srcFilePath "D:\\Users\\Pictures\\Camera Roll\\Pierre-Auguste_Renoir,_Le_Mo…...
【网络】序列化和反序列化
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
【代码随想录训练营】【Day32】第八章|贪心算法|122.买卖股票的最佳时机II |55. 跳跃游戏|45.跳跃游戏II
买卖股票的最佳时机II 题目详细:LeetCode.122 买卖股票的最佳时机,怎么都能够想出来个思路,假如我们每天都能预知明天的股票是涨是降,那么贪心策略就是在涨之前买股票,在降的前一天卖掉,这就是买卖股票的…...
constexpr 和 常量表达式
👀👀常量表达式 常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。 那么是什么来就决定是不是常量表达式呢?一个对象是不是常量表达式主要…...
Vue响应式原理————Object.defineProperty()和proxy的用法分享
Vue框架一个比较核心的功能就是我们的数据是响应式的,这样我们在修改数据的时候,页面会自动帮我们更新,那么想要实现这个功能就要实现对一个数据的劫持,即在取值和设置值的同时我们能够检测到即数据劫持。vue2响应式的实现原理所依…...
CSDN 编程竞赛三十四期题解
竞赛总览 CSDN 编程竞赛三十四期:比赛详情 (csdn.net) 本期的题目和第三十一期竞赛的题目竟然高度重合,真不知道该写点什么了。 不过,上次那道测试数据有bug的题已经修复了,答题过程挺顺利的,没有遇到新的问题。 竞…...
C#教程06 运算符
文章目录 一、算术运算符加法运算符(+)减法运算符(-)乘法运算符(*)除法运算符(/)二、逻辑运算符与运算符(&&)或运算符(||)非运算符(!)三、比较运算符等于运算符(==)大于运算符(>)小于运算符(<)大于等于运算符(>=)小于等于运算符(<=…...
软测入门(六)pytest单元测试
pytest pytest是python的一种单元测试框架,同自带的unit test测试框架类似,但pytest更简洁高效。 单元测试: 测试 函数、类、方法能不能正常运行测试的结果是否符合我们的预期结果 安装 pip install -U pytest基本使用 通过pytest包使用…...
经典分类模型回顾5—DenseNet实现图像分类(matlab)
DenseNet,全称为Densely Connected Convolutional Networks,中文名为密集连接卷积网络,是由李沐等人在2017年提出的一种深度神经网络架构。 DenseNet旨在解决深度神经网络中的梯度消失问题和参数数量过多的问题,通过构建密集连接…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
