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 ...
随机推荐
- js 前端请求头里传 token
参考:https://blog.csdn.net/qq_34309704/article/details/80572077 1.Token:token是客户端频繁向服务器端请求数据,服务器频繁的去数据 ...
- C++虚函数和纯虚函数的用法和区别
C++虚函数与纯虚函数用法与区别(转) 1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能 ...
- java_day06_java高级特性
Advance Java Programming 第六章: java语言高级特性(part1) 1.static修饰符 1)static变量 在类中,使用static修饰的成员变量,就是静态变量,反之 ...
- STM32定义变量位于指定的SRAM地址
1.定义一个数组比如value[],让数组的首地址指向特定的SRAM地址,比如0x20000100 1)__align(8) uint8_t value[20] __attribute__((at(0 ...
- 数据总线&地址总线&控制总线
数据总线 (1) 是CPU与内存或其他器件之间的数据传送的通道. (2)数据总线的宽度决定了CPU和外界的数据传送速度. (3)每条传输线一次只能传输1位二进制数据.eg: 8根数据线一次可传送一个8 ...
- 美登杯”上海市高校大学生程序设计邀请赛 Problem E 、 小 花梨 的数组 (线段树)
Problem E E . 小 花梨 的数组 时间限制:1000ms 空间限制:512MB Description 小花梨得到了一个长度为
- for(auto count:counts)
c++中for(auto count : counts) 这是C++11中的语法,即:Range-based for loop.其中counts应满足:begin(counts), end(count ...
- 网络编程-UDP echo server
1. UDP简介 UDP 和TCP 的区别包括 1. 面向字节流和面向报文 2. TCP必须要建立连接后才能进行数据交换,但是UDP则并没有连接的建立和释放过程.面向字节流说明,tcp报文段(segm ...
- nginx负载均衡 页面缓存
nginx的upstream目前支持4种方式的分配 1.轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除. 2.weight 指定轮询几率,weight ...
- EntityManager的merge()方法
EntityManager的merge()方法相当于hibernate中session的saveOrUpdate()方法: 用于实体的插入和更新操作: