无论是从本地输出的数据还是转发的数据报文,经过路由后都要输出到网络设备,而输出到网络设备的接口就是dst_output(output)函数

路由的时候,dst_output函数设置为ip_output ip_mc_output等

1、TCP输出接口

L4 层在发送数据时会根据协议的不同调用上面提到的几个辅助函数之一,tcp协议打包成ip数据包文的方法根据tcp段的不同而选择不同的接口,

其中ip_queue_xmit为常用接口,ip_build_and_send_pkt、ip_send_reply只有在发送特定段的时候才使用

1-1 int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)

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;/* Skip all of this if the packet is already routed,* f.e. by something like SCTP.*/rcu_read_lock();/** 如果待输出的数据包已准备好路由缓存,* 则无需再查找路由,直接跳转到packet_routed* 处作处理。*/inet_opt = rcu_dereference(inet->inet_opt);fl4 = &fl->u.ip4;rt = skb_rtable(skb);if (rt)goto packet_routed;/* 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; /* 目的地址 */if (inet_opt && inet_opt->opt.srr)daddr = inet_opt->opt.faddr; /* 严格路由选项 *//* If this fails, retransmit mechanism of transport layer will* keep trying until route appears or the connection times* itself out.*/ /* 查找路由缓存 */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);/* 将路由设置到skb中 */packet_routed:/** 查找到输出路由后,先进行严格源路由* 选项的处理。如果存在严格源路由选项,* 并且数据包的下一跳地址和网关地址不* 一致,则丢弃该数据包。*/if (inet_opt && inet_opt->opt.is_strictroute && rt->rt_uses_gateway)goto no_route;/* OK, we know where to send it, allocate and build IP header. *//** 设置IP首部中各字段的值。如果存在IP选项,* 则在IP数据包首部中构建IP选项。*/skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : 0));skb_reset_network_header(skb);iph = ip_hdr(skb);/* 构造ip头 */*((__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);elseiph->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. *//* 构造ip选项 */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 ? *//** 设置输出数据包的QoS类型。*/skb->priority = sk->sk_priority;skb->mark = sk->sk_mark;res = ip_local_out(net, sk, skb);  /* 输出 */rcu_read_unlock();return res;no_route:rcu_read_unlock();/** 如果查找不到对应的路由缓存项,* 在此处理,将该数据包丢弃。*/IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);kfree_skb(skb);return -EHOSTUNREACH;
}

1、2 ip_build_and_send_pkt

在tcp建立连接过程中,内核封包输出syn+ack类型的tcp报文。用此函数封包ip报文段

int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,__be32 saddr, __be32 daddr, struct ip_options_rcu *opt)
{struct inet_sock *inet = inet_sk(sk);struct rtable *rt = skb_rtable(skb);struct net *net = sock_net(sk);struct iphdr *iph;/* Build the IP header. 构造ip首部*/skb_push(skb, sizeof(struct iphdr) + (opt ? opt->opt.optlen : 0));skb_reset_network_header(skb);iph = ip_hdr(skb);iph->version  = 4;iph->ihl      = 5;iph->tos      = inet->tos;iph->ttl      = ip_select_ttl(inet, &rt->dst);iph->daddr    = (opt && opt->opt.srr ? opt->opt.faddr : daddr);iph->saddr    = saddr;iph->protocol = sk->sk_protocol;/* 分片与否 */if (ip_dont_fragment(sk, &rt->dst)) {iph->frag_off = htons(IP_DF);iph->id = 0;} else {iph->frag_off = 0;__ip_select_ident(net, iph, 1);}/*处理ip选项字段*/if (opt && opt->opt.optlen) {iph->ihl += opt->opt.optlen>>2;ip_options_build(skb, &opt->opt, daddr, rt, 0);}/* QOS优先级 */skb->priority = sk->sk_priority;skb->mark = sk->sk_mark;/* Send it out. */return ip_local_out(net, skb->sk, skb);
}

View Code

1.3 ip_send_reply

此函数主要用于输出rst 以及ack段报文 在tcp_v4_send_reset 和tcp_v4_send_ack 中调用

void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,const struct ip_options *sopt,__be32 daddr, __be32 saddr,const struct ip_reply_arg *arg,unsigned int len)
{struct ip_options_data replyopts;struct ipcm_cookie ipc;struct flowi4 fl4;struct rtable *rt = skb_rtable(skb);struct net *net = sock_net(sk);struct sk_buff *nskb;int err;int oif;/* 获取ip选项用于处理原路由选项 */if (__ip_options_echo(&replyopts.opt.opt, skb, sopt))return;ipc.addr = daddr;ipc.opt = NULL;ipc.tx_flags = 0;ipc.ttl = 0;ipc.tos = -1;
/* 如果输入的ip 数据包文启用了路由选项
将得到的下一跳地址作为目的ip地址*/if (replyopts.opt.opt.optlen) {ipc.opt = &replyopts.opt;if (replyopts.opt.opt.srr)daddr = replyopts.opt.opt.faddr;}oif = arg->bound_dev_if; /*设置 输出接口 */if (!oif && netif_index_is_l3_master(net, skb->skb_iif))oif = skb->skb_iif;/* 查路由 根据目的地址 原地址 查找 */flowi4_init_output(&fl4, oif,IP4_REPLY_MARK(net, skb->mark),RT_TOS(arg->tos),RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,ip_reply_arg_flowi_flags(arg),daddr, saddr,tcp_hdr(skb)->source, tcp_hdr(skb)->dest);security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));rt = ip_route_output_key(net, &fl4);if (IS_ERR(rt))/*如果没有命中路由,终止*/return;inet_sk(sk)->tos = arg->tos;sk->sk_priority = skb->priority;sk->sk_protocol = ip_hdr(skb)->protocol;sk->sk_bound_dev_if = arg->bound_dev_if;sk->sk_sndbuf = sysctl_wmem_default;/*将数据报文添加到队列末尾中或者复制到新生成的skb中去 并添加到输出队列*/err = ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base,len, 0, &ipc, &rt, MSG_DONTWAIT);if (unlikely(err)) {ip_flush_pending_frames(sk);goto out;}nskb = skb_peek(&sk->sk_write_queue);if (nskb) {/* 如果发送队列有skb,则计算校验和,发送 */if (arg->csumoffset >= 0)*((__sum16 *)skb_transport_header(nskb) +arg->csumoffset) = csum_fold(csum_add(nskb->csum,arg->csum));nskb->ip_summed = CHECKSUM_NONE;ip_push_pending_frames(sk, &fl4);// 发送数据
    }
out:ip_rt_put(rt);
}
int ip_send_skb(struct net *net, struct sk_buff *skb)
{int err;err = ip_local_out(net, skb->sk, skb);if (err) {if (err > 0)err = net_xmit_errno(err);if (err)IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);}return err;
}int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4)
{struct sk_buff *skb;skb = ip_finish_skb(sk, fl4);if (!skb)return 0;/* Netfilter gets whole the not fragmented skb. */return ip_send_skb(sock_net(sk), skb);
}

ip_queue_xmit 流程图

转载于:https://www.cnblogs.com/codestack/p/9195001.html

IP 层收发报文简要剖析4--ip 报文发送相关推荐

  1. IP 层收发报文简要剖析1-ip报文的输入

    ip层数据包处理场景如下: 网络层处理数据包文时需要和路由表以及邻居系统打交道.输入数据时,提供输入接口给链路层调用,并调用传输层的输入接口将数据输入到传输层. 在输出数据时,提供输出接口给传输层,并 ...

  2. Linux 网卡驱动学习(六)(应用层、tcp 层、ip 层、设备层和驱动层作用解析)

    本文将介绍网络连接建立的过程.收发包流程,以及其中应用层.tcp层.ip层.设备层和驱动层各层发挥的作用. 1.应用层 对于使用socket进行网络连接的服务器端程序,我们会先调用socket函数创建 ...

  3. linux下ip层的一些概念

    首先来看这个ip层的结构: 这里看到非常多的netfilter hook,这是因为netfilter主要是针对ip层的. ip层的主要任务有下面5个方面: 1 ip数据包的校验 2 防火墙的处理(也就 ...

  4. linux内核网络协议栈--ip层报文转发之ip_local_out()函数(六)

    IP层本地报文发送有两个函数ip_local_out和ip_local_out_sk,实际实现两者是等同的,因为本地发送的报文,skb必然关联着一个sock对象. 1.ip_local_out函数 s ...

  5. 一文讲解Linux 内核网络协议栈-数据从接收到ip层

    [推荐阅读] 一文了解Linux上TCP的几个内核参数调优 一文剖析Linux内核中内存管理 分析linux启动内核源码 此处主要讲的是从数据来到,中断到最终数据包被处理的过程. 0:首先来介绍一下I ...

  6. 【Sofice小司笔记】5 计算机网络,包含数据传输原理、网络各层协议详细说明、TCP/IP协议栈各常用协议说明、TCP握手挥手、可靠传输、网络加密技术

    ❓ 在浏览器地址栏输入一个 URL 后回车,背后发生了什么 解析 URL 浏览器封装 HTTP 请求报文 DNS 域名解析获取 IP 地址 建立 TCP 连接(长链接) 浏览器发送请求 负责传输的 I ...

  7. linux 内核网络协议栈--IP层开始直到包被处理(三)

    先看看ip头结构: struct iphdr struct iphdr {#if defined(__LITTLE_ENDIAN_BITFIELD) // 小端__u8 ihl:4, // 首部长度( ...

  8. linux 内核网络协议栈--数据从接收到IP层(二)

    此处主要讲的是从数据来到,中断到最终数据包被处理的过程. 首先来介绍一下IO端口访问问题,内核提供了这样一组函数处理: /kernel/io.c中 inb( ).inw( ).inl( )函数 分别从 ...

  9. gns3中两个路由器分别连接主机然后分析ip数据转发报文arp协议_TCP/IP协议知识总结...

    总体 首先,展示下总体的思维导图.接下来,按照每个点解释. OSI七层模型 开放式系统互联模型(英语:Open System Interconnection Model,缩写:OSI:简称为OSI模型 ...

最新文章

  1. android studio github 项目导入问题
  2. Linux不能上网ping:unknown host问题怎么解决?
  3. Java变量(静态变量/成员变量/局部变量)初始化的问题
  4. Runnable案例 我赚钱她取钱案例 java1615475726
  5. shell中的重定向(21)
  6. jmail mysql_教你怎么使用Jmail发送匿名的邮件(不要身份认证)
  7. 从程序员到项目经理(9):程序员加油站 -- 再牛也要合群
  8. 201507152326_《Javascript实现跨域有4种方法——介绍jsonp和html5方法》
  9. 联想ideapad710s的win10转win7
  10. 签字后被开除_我的易到经历以及老易到员工是如何被乐视派驻高管从易到开除的...
  11. python+django高速公路收费管理系统的设计
  12. CSS3效果 光斑动画(渐变)
  13. C盘满了,要怎么清理才不会误删?
  14. html年龄0-120岁正则,正则表达式
  15. yapi接口测试--自定义脚本编写(高级mock)
  16. python event对象
  17. JavaScript实现树结构(一)
  18. Windows10各版本的区别、我们应该如何选择Windows10的系统版本?
  19. 2022-2028年中国体声波滤波器(BAW)行业市场专项调研及投资前景规划报告
  20. java学习笔记--计算器和日历

热门文章

  1. 高精度人脸表情识别(附GitHub地址)
  2. 阿里巴巴提出Auto-Context R-CNN算法,刷出Faster RCNN目标检测新高度
  3. dubbo 路由配置_Dubbo-go v1.5.1发布,Dubbo 的 Go实现
  4. Java二十三种设计模式 之代理(proxy)
  5. 十年程序员将Python分成7个阶段学习,你会发现Python真的很简单
  6. mysql5.7.18压缩包下载_MySQL 5.7.18 解压版安装
  7. opencv-4.5.3 + opencv_contrib-4.5.3 + vtk-9.0.3编译(全流程)
  8. 神经网络瘦身:SqueezeNet
  9. 详解ROS中的TF使用
  10. 新版本vsphere支持最大单个vmdk超过2T,理论上支持最大62T