You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
staticintip_rcv_finish(structnet*net, structsock*sk, structsk_buff*skb)
{
structnet_device*dev=skb->dev;
intret;
/* * if ingress device is enslaved to an L3 master device pass the * skb to its handler for processing */// 未开启CONFIG_NET_L3_MASTER_DEV时// l3mdev_ip_rcv do nothingskb=l3mdev_ip_rcv(skb);
if (!skb)
returnNET_RX_SUCCESS;
// 查找路由表ret=ip_rcv_finish_core(net, sk, skb, dev, NULL);
if (ret!=NET_RX_DROP)
ret=dst_input(skb);
returnret;
}
staticinlineintfib_lookup(structnet*net, structflowi4*flp,
structfib_result*res, unsigned intflags)
{
structfib_table*tb;
interr=-ENETUNREACH;
flags |= FIB_LOOKUP_NOREF;
if (net->ipv4.fib_has_custom_rules)
// 如果有策略路由(Policy-based routing (PBR))// 进行PBR匹配// 下面的fib_table_lookup查找的是 ip route show 展示的路由// 也就是PBR的main routing table// 所以不需要再调用fib_table_lookup// ip rule list// Priority: 32766, Selector: match anything, Action: lookup// routing table main (ID 254). The main table is the normal// routing table containing all non-policy routesreturn__fib_lookup(net, flp, res, flags);
rcu_read_lock();
res->tclassid=0;
// 默认三张表// local main default// 这里只查了 main 和 default表,没有 local// 如果开启策略路由,在上面调用 __fib_lookup 并返回// 否则 local 合并到 main table 查询// 好处:代码尽量复用,如果用户不开启自定义rule,速度会有提升// 开启 RPDB ,kernel调用 fib_trie_unmerge(),将 local 从 main 剥离// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ddcf43d5d4a03ded1ee3f6b3b72a0cbed4e90b1 tb=rcu_dereference_rtnl(net->ipv4.fib_main);
if (tb)
err=fib_table_lookup(tb, flp, res, flags);
if (!err)
goto out;
// 查default表tb=rcu_dereference_rtnl(net->ipv4.fib_default);
if (tb)
err=fib_table_lookup(tb, flp, res, flags);
out:
if (err==-EAGAIN)
err=-ENETUNREACH;
rcu_read_unlock();
returnerr;
}
使用 ip rule 列出/添加/删除 PBR
$ ip rule list
# PRIORITY SELECTOR ACTION
0: from 127.0.0.1 iif lo ipproto tcp lookup 127
0: from 127.0.0.1 iif lo ipproto udp lookup 127
0: from all iif lo ipproto tcp lookup 128
0: from all iif lo ipproto udp lookup 128
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
// 路由查找算法// default via 10.0.2.2 dev enp0s3// 10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15// 最长前缀匹配intfib_table_lookup(structfib_table*tb, conststructflowi4*flp,
structfib_result*res, intfib_flags)
{
/* Step 1: Travel to the longest prefix match in the trie */for (;;) {
}
/* Step 2: Sort out leaves and begin backtracing for longest prefix */for (;;) {
}
found:
/* Step 3: Process the leaf, if that fails fall back to backtracing */// ...
}
驱动层
驱动向内核注册 softirq,里面包含回调函数。驱动收到数据触发中断,kernel读取
NAPI
基于已有thread_struct封装的新的任务调度库
kernel thread通过thread_struct调度,napi本身封装了thread_struct,内部有kernel thread
驱动初始化代码里创建napi结构,kernel会创建对应的调度上下文,napi 被 kernel调度执行,最后回调驱动代码
为什么NAPI收到第一个包需要关闭中断?
内核网络层
receive_skb*
有list和非list版本。list可以做GRO合并,根据条件合并多个skb,减少重复处理次数。以list版本继续分析调用次序
netif_receive_skb_list
__netif_receive_skb_list_core
__netif_receive_skb_core
deliver_skb
回调 packet_type 指向的handler
IP 层
ipv4 的handler在init阶段注册inet_init
deliver_skb -> ip_rcv,继续ipv4 流程处理
ip_rcv 逻辑很少,在计数,包检查后交给netfilter hook处理,处理完调用
ip_rcv_finish
skb刚进入IP层第一时间调用
PRE_ROUTING
hookPRE_ROUTING
hooknetfilter hook 执行过程后面会详细讲解
ip_rcv_finish_core
: 查路由表,详细路由查询在 路由选择ip_route_input_slow
:路由查询入口函数单就路由而言,路由是根据目的地IP进行选择的过程。而 PBR(policy-based routing)的引入使得路由结果还受 source ip address, skb->mark, tcp/udp source/destination port 等变量影响。
struct flowi4
包含了上述可以被参考的字段值。struct flowi4
fib_lookup
/fib_table_lookup
需struct flowi4
和struct fib_result
参数。给接下来的路由选择准备所需参数。
struct flowi4
:给PBR提供除 destination ip 之外的参考信息struct fib_result
:fib_lookup 的 in_out 返回结果。fib_lookup
返回的struct fib_result
构造route cachestruct dst_entry
字段调用
fib_lookup
开始路由选择路由选择
路由选择总览
fib(forwarding information base)
kernel kbuild有IP_MULTIPLE_TABLES选项。关闭则只有一张main表
使用如下 fib_lookup 函数
开启则引入多路由表,代码如下
fib_lookup
使用 ip rule 列出/添加/删除 PBR
每个规则包含三部分,优先级,选择器,执行结果。优先级从小到大是规则被匹配的顺序。选择器匹配包信息,ACTION 执行具体的操作。
from all
:任意source ip数据包都命中,执行actionlookup local
: 到local表继续查找lookup main
: 到main表继续查找lookup default
: 到default表继续查找local, main, default 表的查找顺序也对应了
fib_lookup
的表查找顺序如果 ip rule 过程有一条规则命中,查找马上停止,并使用此规则作为最后的路由规则。
fib_lookup
=>
__fib_lookup
:策略路由查找=> fib_table_lookup: 路由查找算法实现
struct rtable
代表一条路由规则,路由结束绑定到
skb#_skb_refdst
struct dst_entry dst
下面详细介绍
_u16 rt_type
本路由类型
RTN_UNICAST
unicast
skb#_skb_refdst
是此值,数据需要被转发RTN_LOCAL
local
RNT_BROADCAST
RTN_MULTICAST
RNT_UNREACHABLE
unreachable
__u8 rt_uses_gateway
via 10.0.0.1
的格式),那么rt_gw4
包含网关IP地址。u8 rt_gw_family
rt_uses_gateway
是0,那rt_gw_family
是0。如果网关地址是IPV4,=AF_INET。IPV6, =AF_INET6union {__be32 rt_gw4; struct in6_addr rt_gw6;}
rt_gw4
或rt_gw6
字段struct dst_entry
struct net_device *dev
struct xfrm_state *xfrm
int (_input)(struct sk_buff_skb)
根据路由结果,input是不同值。决定数据包接下来如果处理。dest ip本机则继续向上传递。不是本机的ip则转发
unreachable
,数据包丢弃,返回ICMP host unreachableint (_output)(struct net_net, struct sock _sk, struct sk_buff_skb);
根据路由结果 output 指向不同函数。决定数据包如何发送
尝试重组skb。如果此skb分片包的最后一个那么
ip_defrag
返回0,获得完成的ip数据包。ip_local_deliver call stack
dst_input
是很薄的中间函数展开
INDIRECT_CALL_INET
如果 input 不等于
ip_local_deliver
,调用skb_dst(skb)->input(skb);
,否则调用ip_local_deliver
这样就包含多条路径,这些路径的 input 都在路由选择阶段赋值。
如果 input ==
ip_forward
说明数据包需要转发到本机外ip_forward
ip forward 调用栈
调用 netfilter forward hook
调用网络层output handler,通常会调用
ip_output
ip_output
: 利用IP协议发送xfrm4_output
: IPsec送达ip_mc_output
: multicast packets执行 Netfilter POSTROUTING HOOK
ip_finish_output
__ip_finish_output
ip_finish_output2
进入
netghbouring subsystem
,查找ARP cache,找到链路层 MACip_local_deliver
ip_local_deliver 在创建
dst_entry
时初始化。如果ip packet目的地为本机,继续传递到上层协议栈假设packet送往本机,分析 ip_local_deliver
继续调用
ip_local_deliver_finish
ip_protocol_deliver_rcu
传输层
tcp_ipv4.c
注册 tcp 回调,IP层根据protocol type回调传输层handler
udp send
udp receive
UDP sock receive
tcp send
tcp connect
tcp sendmsg
tcp receive
tcp rev + tcp syn send
netfilter hook
入口点
数据包流
通常在协议栈有很多的决策点,根据不同的条件调用C的各种函数回调,将包送到不同的路径。
绿色部分是和路由相关的两个分流点。
skb_dst(skb)->output
不同值,决定如何调用三层协议发送下图还针对收包展示了两个分流点。
deliver_skb
: 根据链路层Type字段区分三层协议,IPv4, IPv6, ARP等等。iphdr->protocol
字段区分传输层协议TCP,UDP回调handler
从图的左下角看起
数据包可以一个个处理
也可以作为lists一同处理
两种处理方式最后都回调上层 non-list 或者 list 的 handler
ipv4 的 handler 在init阶段注册inet_init
协议分发利用EtherType - Wikipedia字段,通过hashtable查找handle,ipv4的两个回调函数分别为
ip_rcv
和ip_list_rcv
。一到达网络层,首先进行 Netfilter Prerouting,如果没有被丢弃,继续调用
ip_rcv_finish/ip_list_rcv_finish
ip_rcv_finish_core
没有list版本,每个skb单独调用。开始routing
参考
The text was updated successfully, but these errors were encountered: