趣谈 Linux 操作系统
刘超
前网易杭州研究院云计算技术部首席架构师
85459 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 72 讲
趣谈 Linux 操作系统
15
15
1.0x
00:00/00:00
登录|注册

46 | 发送网络包(下):如何表达我们想让合作伙伴做什么?

ip_finish_output2
fib_lookup
tcp_transmit_skb
sock_sendmsg
ixgb_xmit_frame
neigh_probe
ip_finish_output
ip_route_output_ports
tcp_write_xmit
sock_write_iter
net_tx_action
__neigh_lookup_noref
ip_queue_xmit
tcp_sendmsg
inet_sendmsg
file_operations
struct file
NET_TX_SOFTIRQ
dev_queue_xmit
ARP
icsk_af_ops
struct inet_connection_sock
sk_prot
struct sock
ops
struct socket
write系统调用
设备层
MAC层
IP层
Sock层
Socket层
VFS层
发送网络包的过程

该思维导图由 AI 生成,仅供参考

上一节我们讲网络包的发送,讲了上半部分,也即从 VFS 层一直到 IP 层,这一节我们接着看下去,看 IP 层和 MAC 层是如何发送数据的。

解析 ip_queue_xmit 函数

从 ip_queue_xmit 函数开始,我们就要进入 IP 层的发送逻辑了。
int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
{
struct inet_sock *inet = inet_sk(sk);
struct net *net = sock_net(sk);
struct ip_options_rcu *inet_opt;
struct flowi4 *fl4;
struct rtable *rt;
struct iphdr *iph;
int res;
inet_opt = rcu_dereference(inet->inet_opt);
fl4 = &fl->u.ip4;
rt = skb_rtable(skb);
/* Make sure we can route this packet. */
rt = (struct rtable *)__sk_dst_check(sk, 0);
if (!rt) {
__be32 daddr;
/* Use correct destination address if we have options. */
daddr = inet->inet_daddr;
......
rt = ip_route_output_ports(net, fl4, sk,
daddr, inet->inet_saddr,
inet->inet_dport,
inet->inet_sport,
sk->sk_protocol,
RT_CONN_FLAGS(sk),
sk->sk_bound_dev_if);
if (IS_ERR(rt))
goto no_route;
sk_setup_caps(sk, &rt->dst);
}
skb_dst_set_noref(skb, &rt->dst);
packet_routed:
/* OK, we know where to send it, allocate and build IP header. */
skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : 0));
skb_reset_network_header(skb);
iph = ip_hdr(skb);
*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
iph->frag_off = htons(IP_DF);
else
iph->frag_off = 0;
iph->ttl = ip_select_ttl(inet, &rt->dst);
iph->protocol = sk->sk_protocol;
ip_copy_addrs(iph, fl4);
/* Transport layer set skb->h.foo itself. */
if (inet_opt && inet_opt->opt.optlen) {
iph->ihl += inet_opt->opt.optlen >> 2;
ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
}
ip_select_ident_segs(net, skb, sk,
skb_shinfo(skb)->gso_segs ?: 1);
/* TODO : should we use skb->sk here instead of sk ? */
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
res = ip_local_out(net, sk, skb);
......
}
在 ip_queue_xmit 中,也即 IP 层的发送函数里面,有三部分逻辑。
第一部分,选取路由,也即我要发送这个包应该从哪个网卡出去。
这件事情主要由 ip_route_output_ports 函数完成。接下来的调用链为:ip_route_output_ports->ip_route_output_flow->__ip_route_output_key->ip_route_output_key_hash->ip_route_output_key_hash_rcu。
struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, struct fib_result *res, const struct sk_buff *skb)
{
struct net_device *dev_out = NULL;
int orig_oif = fl4->flowi4_oif;
unsigned int flags = 0;
struct rtable *rth;
......
err = fib_lookup(net, fl4, res, 0);
......
make_route:
rth = __mkroute_output(res, fl4, orig_oif, dev_out, flags);
......
}
ip_route_output_key_hash_rcu 先会调用 fib_lookup。
FIB 全称是 Forwarding Information Base,转发信息表。其实就是咱们常说的路由表。
static inline int fib_lookup(struct net *net, const struct flowi4 *flp, struct fib_result *res, unsigned int flags)
{ struct fib_table *tb;
......
tb = fib_get_table(net, RT_TABLE_MAIN);
if (tb)
err = fib_table_lookup(tb, flp, res, flags | FIB_LOOKUP_NOREF);
......
}
路由表可以有多个,一般会有一个主表,RT_TABLE_MAIN。然后 fib_table_lookup 函数在这个表里面进行查找。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入解析了网络包的发送过程,从IP层的发送逻辑入手,详细介绍了ip_queue_xmit函数的三部分逻辑。首先,通过ip_route_output_ports函数选取路由,确定数据包应该从哪个网卡出去,并介绍了路由表的查找过程。其次,讲解了准备IP层头部的填充内容,包括服务类型、标识位、TTL、协议、源地址、目标地址等设置。最后,通过ip_local_out函数发送IP包,并介绍了Netfilter机制在网络发送和转发的关键节点上加上hook函数,对数据包进行干预的过程。 文章通过深入的技术分析,帮助读者全面了解了网络包发送过程中IP层和MAC层的关键细节,对于深入理解网络通信机制具有重要参考价值。文章内容涉及IP层的发送逻辑、路由表查找过程、IP头部填充内容设置、Netfilter机制等,对于从事网络通信相关工作的技术人员具有较高的实用性和参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《趣谈 Linux 操作系统》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(22)

  • 最新
  • 精选
  • 安排
    老师。应用层调用socket 接口发送数据是到哪个阶段就返回了?是数据写到qdisc中应用就可以返回了吗?还是要等到写到硬件网卡中?

    作者回复: 发起软中断就返回

    2019-07-14
    2
    10
  • Mhy
    老师请问neigh_probe 发送 ARP后还需要阻塞等待其他主机返回mac吗,如果没有收到呢

    作者回复: 要等待返回,要不然不知道mac发不出去

    2020-05-13
    2
    4
  • 安排
    发送数据包时,源Mac地址是由协议栈软件加上的吗,还是等数据包到网卡后由网卡硬件自动加上的? 源Mac地址现在一般是写死在网卡里的吗?还是维护在软件协议栈里的一个变量?

    作者回复: 内核协议栈加上的。mac地址可以通过命令修改的

    2019-07-12
    3
  • 安排
    例如 192.168.2.0/24 和 192.168.0.0/16 都能匹配 192.168.2.100/24。 192.168.0.0/16为什么能匹配192.168.2.100/24 呢?其实对于目的IP我们是不知道子网掩码的,所以192.168.2.100/24这里的24感觉有点迷惑,如果确定它的掩码是24位,那和16位掩码的那个规则就不匹配了吧。

    作者回复: 只要通过本地的子网掩码算一下。192.168.2.100的前16位能够和192.168(192.168.0.0/16)完全一致,则说明能够匹配192.168.0.0/16。同理192.168.2.100的前24位能够和192.168.2(192.168.2.0/24)完全一致,则说明能够匹配192.168.2.0/24

    2019-07-12
    3
    3
  • 一笔一画
    老师,请教下qos功能是否也和硬件有关系?pfifo_fast是需要硬件支持的吗?

    作者回复: 不需要,是内核的功能

    2019-07-17
    2
  • geraltlaush
    最近用go实现了rtp的协议,协议头填充和字节大小计算等等很类似,这节内容有种似曾相识的感觉,借鉴下可以实现的更牛逼,哈

    作者回复: 赞,自己实现RTP,牛

    2019-07-14
    1
  • Linuxer
    设备层:网络包的发送回(这里应该是会吧?)触发一个软中断 NET_TX_SOFTIRQ 来处理队列中的数据。这个软中断的处理函数是 net_tx_action。 在软中断处理函数中,会将网络包从队列上拿下来,调用网络设备的传输函数 ixgb_xmit_frame,将网络包发的(这里应该是到吧?)设备的队列上去

    作者回复: 是的,谢谢指正

    2019-07-13
    1
  • humor
    这么复杂的调用关系,老师是怎么记住的啊? 我看一遍就会忘掉……
    2019-11-18
    10
  • Slience-0°C
    不是很趣味啊
    2021-09-05
    2
    2
  • 凌空飞起的剪刀腿
    socket--->route list---->netfilter(iptables)-->tc--->mac
    2021-04-07
    1
收起评论
显示
设置
留言
22
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部