IP输出 之 ip_output、ip_finish_output、ip_finish_output2
概述
ip_output-设置输出设备和协议,然后经过POST_ROUTING钩子点,最后调用ip_finish_output;
ip_finish_output-对skb进行分片判断,需要分片,则分片后输出,不需要分片则知直接输出;
ip_finish_output2-对skb的头部空间进行检查,看是否能够容纳下二层头部,若空间不足,则需要重新申请skb;然后,获取邻居子系统,并通过邻居子系统输出;
源码分析
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb_dst(skb)->dev; IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); /* 设置输出设备和协议 */
skb->dev = dev;
skb->protocol = htons(ETH_P_IP); /* 经过NF的POST_ROUTING钩子点 */
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
net, sk, skb, NULL, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
unsigned int mtu;
int ret; ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
if (ret) {
kfree_skb(skb);
return ret;
} #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
/* Policy lookup after SNAT yielded a new policy */
if (skb_dst(skb)->xfrm) {
IPCB(skb)->flags |= IPSKB_REROUTED;
return dst_output(net, sk, skb);
}
#endif
/* 获取mtu */
mtu = ip_skb_dst_mtu(sk, skb); /* 是gso,则调用gso输出 */
if (skb_is_gso(skb))
return ip_finish_output_gso(net, sk, skb, mtu); /* 长度>mtu或者设置了IPSKB_FRAG_PMTU标记,则分片 */
if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU))
return ip_fragment(net, sk, skb, mtu, ip_finish_output2); /* 输出数据包 */
return ip_finish_output2(net, sk, skb);
}
static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct rtable *rt = (struct rtable *)dst;
struct net_device *dev = dst->dev;
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
u32 nexthop; if (rt->rt_type == RTN_MULTICAST) {
IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
} else if (rt->rt_type == RTN_BROADCAST)
IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len); /* Be paranoid, rather than too clever. */
/* skb头部空间不能存储链路头 */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2; /* 重新分配skb */
skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
if (!skb2) {
kfree_skb(skb);
return -ENOMEM;
}
/* 关联控制块 */
if (skb->sk)
skb_set_owner_w(skb2, skb->sk); /* 释放skb */
consume_skb(skb); /* 指向新的skb */
skb = skb2;
} if (lwtunnel_xmit_redirect(dst->lwtstate)) {
int res = lwtunnel_xmit(skb); if (res < || res == LWTUNNEL_XMIT_DONE)
return res;
} rcu_read_lock_bh();
/* 获取下一跳 */
nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);
/* 获取邻居子系统 */
neigh = __ipv4_neigh_lookup_noref(dev, nexthop); /* 创建邻居子系统 */
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); /* 成功 */
if (!IS_ERR(neigh)) {
int res; /* 更新路由缓存确认 */
sock_confirm_neigh(skb, neigh); /* 通过邻居子系统输出 */
res = neigh_output(neigh, skb); rcu_read_unlock_bh();
return res;
}
rcu_read_unlock_bh(); net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
__func__);
/* 释放skb */
kfree_skb(skb);
return -EINVAL;
}
IP输出 之 ip_output、ip_finish_output、ip_finish_output2的更多相关文章
- IP输出 之 ip_local_out
概述 将要从本地发出的数据包,会在构造了ip头之后,调用ip_local_out函数,该函数设置数据包的总长度和校验和,然后经过netfilter的LOCAL_OUT钩子点进行检查过滤,如果通过,则调 ...
- IP输出 之 分片ip_fragment、ip_do_fragment
概述 ip_fragment函数用于判断是否进行分片,在没有设置DF标记的情况下进入分片,如果设置了DF标记,则继续判断,如果不允许DF分片或者收到的最大分片大于MTU大小,则回复ICMP,释放skb ...
- Windows 网络监测ping IP输出时间
本文出自:https://www.cnblogs.com/2186009311CFF/p/9489374.html 持续监测网络,打印时间的方法,不足没有精确到毫秒 vbs文件内容如下 Dim arg ...
- Linux内核IP层的报文处理流程(一)
本文主要讲解了Linux内核IP层的整体架构和对从网卡接受的报文处理流程,使用的内核的版本是2.6.32.27 为了方便理解,本文采用整体流程图加伪代码的方式对Linxu内核中IP整体实现架构和对网卡 ...
- Linux中处理需要传输的IP报文流程
本文主要讲解了Linux中处理需要传输的IP报文流程,使用的内核的版本是2.6.32.27 为了方便理解,本文采用整体流程图加伪代码的方式对Linux中处理需要传输的IP报文流程进行了讲解,希望可以对 ...
- 《TCP/IP具体解释卷2:实现》笔记--UDP:用户数据报协议
用户数据报协议.即UDP,是一个面向数据报的简单运输层协议:进程的每次输出操作仅仅产生一个UDP数据报,从而发送 一个IP数据报. 进程通过创建一个Internet域内的SOCK_DGRAM类型的插口 ...
- PHP中IP地址与整型数字互相转换详解
这篇文章主要介绍了PHP中IP地址与整型数字互相转换详解,本文介绍了使用PHP函数ip2long与long2ip的使用,以及它们的BUG介绍,最后给出自己写的两个算法,需要的朋友可以参考下 IP转换成 ...
- 字符串-06. IP地址转换
/* * Main.c * D6-字符串-06. IP地址转换 * Created on: 2014年8月19日 *******测试通过******** *转载:http://blog.csdn.ne ...
- 根据IP查地理位置信息
IP地址库下载地址: https://www.ipip.net/product/ip.html 使用方式(Python): https://github.com/ipipdotnet/datx-pyt ...
随机推荐
- O034、 Nova Pause / Resume Instance 操作详解
参考https://www.cnblogs.com/CloudMan6/p/5496825.html 本节通过日志详细分析 Nova Pause / Resume 操作. 有时需要短时间暂停 ...
- maven入门-- part1 简介
Maven是什么 maven是基于项目对象模型(pom:project object model),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具.对依赖关系的特性进行细致的分析和划 ...
- 【Zabbix】分布式监控系统Zabbix【一】
一.Zabbix功能及特性简介 Zabbix可以获取cpu,内存,网卡,磁盘,日志等信息 1.Zabbix数据收集方式: a.Agent客户端(Agent客户端支持多平台部署) b.如果是无法安装客户 ...
- JVM学习笔记(一):Java虚拟机和虚拟机内存区域
为什么Java程序需要运行在虚拟机上 因为Java在设计之初的跨平台特性,我们知道Java程序是运行在Java虚拟机上的.如果你要问为什么Java程序要运行在虚拟机上,我可以反问你几个问题. 为什么买 ...
- 使用docker compose部署服务
谈到微服务的话题,技术上我们往往会涉及到多服务.多容器的部署与管理. Docker 有三个主要的作用:Build, Ship和Run.使用docker compose我们可以在Run的层面解决很多实际 ...
- Spring面试题整理
1.https://blog.csdn.net/a745233700/article/details/80959716 2.https://ifeve.com/spring-interview-que ...
- python连接postgres方法
Python使用PyGreSQL操作PostgreSQL: import pg def operate_postgre_tbl_product(): try: #db = pg.connect(dbn ...
- Java十进制转二进制
闲着没事写了个简单的十进制转二进制的算法,很简单,个人记录一下,不妥之处请指正. public static String toBinaryString(int j) { if (j < 0) ...
- linux syslog支持 ubuntu
linux syslog支持 linux syslog支持 linux syslog支持 ??????? https://wenku.baidu.com/view/8cc6b50a0202074 ...
- 2019CCPC秦皇岛赛区(重现赛)- I
链接: http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1009&cid=872 题意: 在 dota2 中有一个叫做祈求 ...