函数原型: int ip_output(struct mbuf *m, struct mbuf * opt, struct route *ro, int flags, struct ip_moptions *imo)
参数说明: m指向要发送的分组,opt指向分组的ip首部的选项部分,r0标识系统为该目的地址选择的路由,
flags标志设定了若干预设值: IP_FORWARDING 表示这是一个转发的分组;
IP_ROUTEIF 表示直接路由到接口;
IP_ALLOWBROADCAST表示允许广播发送该分组;
IP_RAWOUTPUT 表示分组有预购首部.
ip分组输出代码从两个地方接受分组:1.ip分组转发函数ip_forward();2.运输协议(ICMP,IGMP,TCP,UDP) 的输出函数.
从ip_forward()中调用ip_output的代码如下:
error = ip_output(m, (struct mbuf *)0, &ipforward_t, IP_FORWARDING | IP_ALLOWBROADCAST, 0);
下面详细描述ip_output处理输出分组的详细过程:
(1)如果选项非空(opt != NULL)则调用ip_insertoptions(m,opt, &len)将选项插入到m指向的分组里面.我们注意到从ip_forward()调用ip_output时传给ip_output的opt参数为空,这是因为在调用ip_output之前,ip首部选项已经是ip分组的一部分, 故opt参数为空.
(2)如果分组不是转发分组或者没有预构首部,则在此初始化ip首部中的某些选项.如版本号,id,首部长度等.
(3)路由选择,首先验证传递给ip_output函数的高速路由缓存,如ipforward_rt,是否为所需要的路由,如果不是,释放高速缓存所指向的路由表项,重新查询路由表,找到所需路由项,获取转发分组的接口ifp及接口地址ia.
在此需要注意的是,如果调用方明确设置了IP_ROUTETOIF标志,意味着它要求禁止对分组进行路由选择,那么ip_output必须找到一个与分组指定的目的地网络直接相连的接口。如果没找到,丢弃该分组,返回出错值
(4)确定了发送分组的接口了以后,去检测一下要被发送的分组的ip首部,如果发现ip首部的源地址为INADDR_ANY(0.0.0.0),表示该分组产生于本机,此时,将转发的接口所配置的ip地址填写到ip首部的源地址字段中.
(5)判断分组的目的地址是否为广播地址,如果是广播地址,则进行如下检查:
检查接口是否支持广播,如果接口不支持广播,释放分组,返回出错值;
检查标志flags,确定调用方明确使能广播,否则丢弃分组,返回出错值;
检查分组长度,由于协议明确规定不允许广播分组被分片,所以如果ip_len > 接口的mtu,丢弃该分组,返回出错值
(6)如果分组长度<=接口的mtu,分组不需要分片,计算检验和后直接调用接口的输出函数发送分组。
(7)如果分组长度>接口的mtu,分组将会按照以下步骤被分片后发送:
检查ip首部的ip_off字段中DF字段是否被设置,如果被设置,意味着分组的发送方不希望分组被分片,ip_output丢弃分组,返回出错值。
进行分片,将原始分组m作为第一个分片,并创建其他分片(创建mbuf,拷贝分组首部及部分选项字段,拷贝相应的数据段,计算检验和),通过每个分片的mbuf首部中的m_nextpkt字段将所有分片链接在一块;
启动接口发送函数将所有分组一一发送;
(8)发送完成后,如果调用方没有指定高速路由发送缓存(在这种情况下引用的是临时路由缓存struct route iproute),那么需要释放所引用的路由项(递减路由项的引用计数)。
(9)返回
需要注意的是,ip_output发现出错时并不发送icmp出错报告报文,而是直接丢弃分组,返回相应的出错值,将发送icmp差错报告报文的工作留给调用方去做。由于ip_output在出错情况下直接将所要发送的报文丢弃,所以调用方在将分组传递给ip_output前,必须将分组做相应的备份,以用来在分组出错的情况下发送相应的icmp报文。
参数说明: m指向要发送的分组,opt指向分组的ip首部的选项部分,r0标识系统为该目的地址选择的路由,
flags标志设定了若干预设值: IP_FORWARDING 表示这是一个转发的分组;
IP_ROUTEIF 表示直接路由到接口;
IP_ALLOWBROADCAST表示允许广播发送该分组;
IP_RAWOUTPUT 表示分组有预购首部.
ip分组输出代码从两个地方接受分组:1.ip分组转发函数ip_forward();2.运输协议(ICMP,IGMP,TCP,UDP) 的输出函数.
从ip_forward()中调用ip_output的代码如下:
error = ip_output(m, (struct mbuf *)0, &ipforward_t, IP_FORWARDING | IP_ALLOWBROADCAST, 0);
下面详细描述ip_output处理输出分组的详细过程:
(1)如果选项非空(opt != NULL)则调用ip_insertoptions(m,opt, &len)将选项插入到m指向的分组里面.我们注意到从ip_forward()调用ip_output时传给ip_output的opt参数为空,这是因为在调用ip_output之前,ip首部选项已经是ip分组的一部分, 故opt参数为空.
(2)如果分组不是转发分组或者没有预构首部,则在此初始化ip首部中的某些选项.如版本号,id,首部长度等.
(3)路由选择,首先验证传递给ip_output函数的高速路由缓存,如ipforward_rt,是否为所需要的路由,如果不是,释放高速缓存所指向的路由表项,重新查询路由表,找到所需路由项,获取转发分组的接口ifp及接口地址ia.
在此需要注意的是,如果调用方明确设置了IP_ROUTETOIF标志,意味着它要求禁止对分组进行路由选择,那么ip_output必须找到一个与分组指定的目的地网络直接相连的接口。如果没找到,丢弃该分组,返回出错值
(4)确定了发送分组的接口了以后,去检测一下要被发送的分组的ip首部,如果发现ip首部的源地址为INADDR_ANY(0.0.0.0),表示该分组产生于本机,此时,将转发的接口所配置的ip地址填写到ip首部的源地址字段中.
(5)判断分组的目的地址是否为广播地址,如果是广播地址,则进行如下检查:
检查接口是否支持广播,如果接口不支持广播,释放分组,返回出错值;
检查标志flags,确定调用方明确使能广播,否则丢弃分组,返回出错值;
检查分组长度,由于协议明确规定不允许广播分组被分片,所以如果ip_len > 接口的mtu,丢弃该分组,返回出错值
(6)如果分组长度<=接口的mtu,分组不需要分片,计算检验和后直接调用接口的输出函数发送分组。
(7)如果分组长度>接口的mtu,分组将会按照以下步骤被分片后发送:
检查ip首部的ip_off字段中DF字段是否被设置,如果被设置,意味着分组的发送方不希望分组被分片,ip_output丢弃分组,返回出错值。
进行分片,将原始分组m作为第一个分片,并创建其他分片(创建mbuf,拷贝分组首部及部分选项字段,拷贝相应的数据段,计算检验和),通过每个分片的mbuf首部中的m_nextpkt字段将所有分片链接在一块;
启动接口发送函数将所有分组一一发送;
(8)发送完成后,如果调用方没有指定高速路由发送缓存(在这种情况下引用的是临时路由缓存struct route iproute),那么需要释放所引用的路由项(递减路由项的引用计数)。
(9)返回
需要注意的是,ip_output发现出错时并不发送icmp出错报告报文,而是直接丢弃分组,返回相应的出错值,将发送icmp差错报告报文的工作留给调用方去做。由于ip_output在出错情况下直接将所要发送的报文丢弃,所以调用方在将分组传递给ip_output前,必须将分组做相应的备份,以用来在分组出错的情况下发送相应的icmp报文。