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 ...
随机推荐
- shell 中的 set -e 和 set +e的区别
区别: set -e : 执行的时候如果出现了返回值为非零,整个脚本 就会立即退出 set +e: 执行的时候如果出现了返回值为非零将会继续执行下面的脚本 set -e 命令用法总结如下:1. 当命令 ...
- java实现spark常用算子之ReduceByKey
import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spa ...
- java.sql.SQLException: Could not retrieve transaction read-only status from server 问题解决
网上有2种主要说法 第一种 问题描述: java代码在开始事务后,先做了一个查询,再insert,此时会报: java.sql.SQLException: could not ret ...
- Delphi最新的路线图
没想到,官方又发布了一个路线图,同上一个相比,多了10.3.3版本,而其中,最主要的iOS 13支持,即下半年发布的新的苹果系统,再一个就是android 64的delphi支持!年底前发布,够官忙伙 ...
- jvm监控工具jconsole进行远程监控配置
[环境] SUSE linux11 + jdk1.6 + tomcat7 [场景] 最近在做性能测试,想通过我本地(win7)上的jdk来远程监控上述服务器的jvm相关信息. [配置] 配置上述服务器 ...
- 什么是PAM认证
PAM(Pluggable Authentication Modules )是由 Sun 提出的一种用于实现应用程序的认证机制.其核心是一套共享库,目的是提供一个框架和一套编程接口,将认证工作由程序员 ...
- collections:内建模块,提供额外的集合类
介绍 collections里面包含了很多除了内置类型之外的数据类型,我们使用它们有时可以很方便的完成一系列操作 ChainMap:搜索多个字典 from collections import Cha ...
- linux使用nginx配置web服务器
环境: CenterOS 7 1.安装nginx之前先安装nginx所需的依赖包 yum -y install zlib zlib-devel openssl openssl-devel pcre p ...
- B+(B)树和B-树
转载自 http://www.cnblogs.com/nullzx/ 1.B树 定义:B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点 ...
- PAT Basic 1022 D进制的A+B (20 分)
输入两个非负 10 进制整数 A 和 B (≤),输出 A+B 的 D (1)进制数. 输入格式: 输入在一行中依次给出 3 个整数 A.B 和 D. 输出格式: 输出 A+B 的 D 进制数. 输入 ...