Linux中处理需要传输的IP报文流程
本文主要讲解了Linux中处理需要传输的IP报文流程,使用的内核的版本是2.6.32.27
为了方便理解,本文采用整体流程图加伪代码的方式对Linux中处理需要传输的IP报文流程进行了讲解,希望可以对大家有所帮助。阅读本文章假设大家对C语言有了一定的了解
首先从IP的更高层传输层看看是如何管理的
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------- /*四层协议的注册,都注册为net_protocol结构,并hash到inet_protos表中进行统一管理*/
#ifdef CONFIG_IP_MULTICAST
static const struct net_protocol igmp_protocol = {
.handler = igmp_rcv,
.netns_ok = 1,
};
#endif static const struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
.gro_receive = tcp4_gro_receive,
.gro_complete = tcp4_gro_complete,
.no_policy = 1,
.netns_ok = 1,
}; static const struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.gso_send_check = udp4_ufo_send_check,
.gso_segment = udp4_ufo_fragment,
.no_policy = 1,
.netns_ok = 1,
}; static const struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
.no_policy = 1,
.netns_ok = 1,
}; static int __init inet_init(void)
{
/*
* Add all the base protocols.
*/
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
#endif
}
在4层处理完成之后,4层会调用IP层的接口ip_qeueu_xmit进行
报文发送
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------- /*ipv4.c中注册的让上层协议使用的接口*/
static const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
.queue_xmit = ip_queue_xmit,
}; /*将dccp_ipv4_af_ops注册到协议中*/
static int dccp_v4_init_sock(struct sock *sk)
{
inet_csk(sk)->icsk_af_ops = &dccp_ipv4_af_ops;
} /*TCP数据报文发送函数*/
static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask)
{
const struct inet_connection_sock *icsk = inet_csk(sk); /*使用ip_queue_xmit发送数据报文*/
err = icsk->icsk_af_ops->queue_xmit(skb, 0); } //----------------------------------------------------------------------------------------------------------------------------------------------------------------------- int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
{
struct sock *sk = skb->sk;
struct inet_sock *inet = inet_sk(sk);
struct ip_options *opt = inet->opt;
struct rtable *rt;
struct iphdr *iph; /*检查套接字结构中sk->dst中是否有一个指针指向路由缓存中的某个入口项
*如果有,再检查这个指针是否有效,由于套接字的所有包都去往同一个目标
*地址,因此路由就存放在skb->_skb_dst中,内容为dst_entry结构
*/ rt = skb_rtable(skb);
if (rt != NULL)
goto packet_routed; rt = (struct rtable *)__sk_dst_check(sk, 0);
{
if (dst && dst->obsolete && dst->ops->check(dst, cookie) == NULL)
{
sk->sk_dst_cache = NULL;
dst_release(dst);
return NULL;
}
} /*如果尚未设置路由,那么使用ip_route_output_flow进行路由选路*/
if (rt == NULL)
{
//......
if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))
goto no_route;
} //......
packet_routed:
/*填充IP报头*/
//.....
iph->ttl = ip_select_ttl(inet, &rt->u.dst);
iph->protocol = sk->sk_protocol;
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst; /*填充IP选项*/
if (opt && opt->optlen) {
iph->ihl += opt->optlen >> 2;
ip_options_build(skb, opt, inet->daddr, rt, 0);
} //......
return ip_local_out(skb); no_route:
//.....
} int ip_local_out(struct sk_buff *skb)
{
int err; err = __ip_local_out(skb);
if (likely(err == 1))
err = dst_output(skb); return err;
} int __ip_local_out(struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb); iph->tot_len = htons(skb->len);
ip_send_check(iph); /*进入 NF_INET_LOCAL_OUT 的序列钩子进行处理,处理之后放入dst_output中处理*/
return nf_hook(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
dst_output);
} static inline int dst_output(struct sk_buff *skb)
{
/*调用dst_entry中注册的output函数,IP单播也就是ip_output函数*/
return skb_dst(skb)->output(skb);
} /*在__mkroute_output中曾经对output和input进行过注册*/
static int __mkroute_output(struct rtable **result,
struct fib_result *res,
const struct flowi *fl,
const struct flowi *oldflp,
struct net_device *dev_out,
unsigned flags)
{
struct rtable *rth;
rth->u.dst.output=ip_output; if (flags & RTCF_LOCAL) {
rth->u.dst.input = ip_local_deliver;
} if (flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
if (flags & RTCF_LOCAL && !(dev_out->flags & IFF_LOOPBACK))
{
rth->u.dst.output = ip_mc_output;
}
if (res->type == RTN_MULTICAST)
{
rth->u.dst.input = ip_mr_input;
rth->u.dst.output = ip_mc_output;
}
}
} /*IPV4单播*/
int ip_output(struct sk_buff *skb)
{
struct net_device *dev = skb_dst(skb)->dev; skb->dev = dev;
skb->protocol = htons(ETH_P_IP); /*经过 NF_INET_POST_ROUTING 处理链后,进入ip_finish_output处理*/
return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
} static int ip_finish_output(struct sk_buff *skb)
{
/*IP分片后,进入ip_finish_output2处理*/
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
return ip_fragment(skb, ip_finish_output2);
else
return ip_finish_output2(skb);
} static inline int ip_finish_output2(struct sk_buff *skb)
{
/*如果没有二层头,启用ARP处理*/
if (dst->hh)
return neigh_hh_output(dst->hh, skb);
/*如果有二层头进行处理,侧使用dst->neighbour->output也就是 dev_queue_xmit*/
else if (dst->neighbour)
return dst->neighbour->output(skb);
} /*dev_queue_xmit在ARP中的注册过程如下*/
static const struct neigh_ops arp_hh_ops = {
.family = AF_INET,
.output = neigh_resolve_output,
.hh_output = dev_queue_xmit,
}; static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, __be16 protocol)
{
struct hh_cache *hh; //...... if (n->nud_state & NUD_CONNECTED)
hh->hh_output = n->ops->hh_output; /*也就是dev_queue_xmit*/
else
hh->hh_output = n->ops->output; //......
}
最终一路从ip_queue_xmit进行发送调用到二层发送的入口点dev_queue_xmit
关于处理流程的整体架构图,请参见我的上一篇博客
<<Linux内核IP层的报文处理流程--从网卡接收的报文处理流程>>
关于二层是如何继续处理报文并发送的,请参考博客
希望大家批评指正
Linux中处理需要传输的IP报文流程的更多相关文章
- 工具WinSCP:windows和Linux中进行文件传输
工具WinSCP:windows和Linux中进行文件传输 2016-09-21 [转自]使用WinSCP软件在windows和Linux中进行文件传输 当我们的开发机是Windows,服务器是Lin ...
- Linux中配置ftp传输
.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...
- Linux中FTP远程传输,SSH远程连接,以及SCP远程拷贝
常用服务器ftp.ssh 1. Linux常用服务器构建-ftp服务器 ftp服务器 FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”. 用于 ...
- Linux中仅主机模式下设ip
昨天在项目中接触到了Hadoop,需要用到linux来操作,以前有过接触,大致都忘了,在装备虚拟机的时候,遇到的IP ping不通的问题,模式是仅主机模式,今天分享一下,以便自己以后再遇到,也可以迎 ...
- Linux中常用文件传输命令及使用方法
sftp sftp即Secure Ftp 是一个基于SSH安全协议的文件传输管理工具.由于它是基于SSH的,会在传输过程中对用户的密码.数据等敏感信息进行加密,因此可以有效的防止用户信息在传输的过程中 ...
- 如何在Linux中使用sFTP上传或下载文件与文件夹
如何在Linux中使用sFTP上传或下载文件与文件夹 sFTP(安全文件传输程序)是一种安全的交互式文件传输程序,其工作方式与 FTP(文件传输协议)类似. 然而,sFTP 比 FTP 更安全;它通过 ...
- 【IP】Linux中检测IP地址冲突
在Windows系统中,如果本地网络IP地址出现冲突,会出现图标提示. 在Linux系统中,并没有提供相关的功能,如果本地网络采用静态IP地址配置,出现比较奇怪的网络连接问题,如ssh连接复位,可以考 ...
- 在linux中设置静态ip地址
在linux中设置静态ip地址1.在终端中输入:vi /etc/sysconfig/network-scripts/ifcfg-eth0 2.开始编辑,填写ip地址.子网掩码.网关.DNS等[root ...
- 如何在Linux中使用rz/sz工具进行文件传输
在Linux中,使用rz/sz工具能够进行Linux和windows之间的文件传输,那么要如何使用rz/sz工具工具呢?下面小编就给大家介绍下Linux下如何使用rz/sz工具进行文件传输,一起来学习 ...
随机推荐
- [置顶] P2P之我见,关于打洞的学问-------开篇
最近忙项目,有点累,无暇顾急博客,4月份本来想写写流媒体的文章,结果回家休了两个月回深圳后,接了P2P的项目,那就开始P2P吧. P2P起源于美国大学生Shawn Fanning 写的一个分享软件Na ...
- Oracle 字段是多个值的字符串的查询处理
1.创建两张表一张用户表(T_User),一张兴趣小组表T_Group,其中小组成员字段存储用户ID列表以逗号隔开, 表:T_User 编号(F_ID) 名称(用户名) 1 张三 2 李四 3 王五 ...
- 谓词--Predicate
去苹果的的技术官网搜索-Predicate就会找到相关的文档-Predicate Programming Guide 1,创建谓词时 %@是变量时不加单双引号,常量是加单引号,加双引号需要转义符号\ ...
- python none,null,,,,,类型
内建类型None表示一个空对象,没有方法和属性. None是一个特殊的常量. None和False不同. None不是0. None不是空字符串. None和任何其他的数据类型比较永远返回False. ...
- 基于visual Studio2013解决C语言竞赛题之0408素数
题目 解决代码及点评 判断一个数是不是素数的方法,一般是看n是不是能被n以内的某个整数(1除外)整除 为了提高效率,这个整数范围一般缩小到n的平方根 如果在这个范围内的整数都不能整除,那么 ...
- BZOJ 1613: [Usaco2007 Jan]Running贝茜的晨练计划
题目 1613: [Usaco2007 Jan]Running贝茜的晨练计划 Time Limit: 5 Sec Memory Limit: 64 MB Description 奶牛们打算通过锻炼来 ...
- 2014 北京邀请赛ABDHJ题解
A. A Matrix 点击打开链接 构造,结论是从第一行開始往下产生一条曲线,使得这条区间最长且从上到下递减, #include <cstdio> #include <cstrin ...
- HDU 4825 Xor Sum 字典树+位运算
点击打开链接 Xor Sum Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 132768/132768 K (Java/Others) ...
- TCP/IP之TCP交互数据流、成块数据流
建立在TCP协议上的网络协议有telnet,ssh,ftp,http等等.这些协议根据数据吞吐量来分成两大类: (1)交互数据类型,例如telnet,ssh,这种类型的协议在大多数情况下只是做小流量的 ...
- Android广播——短信拦截
MainActivity.java package com.example.broadcasttest; import android.content.Intent; import android.c ...