IP包的生成和发送接口(转载)

IP包的生成和发送接口(转载)

作者:collide 
原文地址:http://blog.csdn.net/collide/articles/127616.aspx

(1) Linux内核中有3种基本的IP包生成器, 它们分别为ip_build_xmit(), ip_queue_xmit(),
ip_build_and_send_pkt(). ip_build_and_send_pkt()是一简单的IP包头封装接口,
它接照输入包的路由添加一个IP包头后直接输出,不进行分片处理, 用于tcp_v4_send_synack()中.
ip_send_reply()是基于ip_build_xmit()的一个函数,
用于tcp_v4_send_ack()和tcp_v4_send_reset()中.

(2) ip_build_xmit()使用用户定义的回调函数直接读取用户数据片段生成IP包输出.
如果需要分片,ip_build_xmit()按照最后一个片段到第一个片段的顺序来生成IP包,
这是因为第一个IP包片段的数据区可能包含对整个IP包数据区的校验码,
在回调函数中用户可能会计算输出数据的校验码,
采用从后向前的输出顺序可使校验码自然地写到第一个片段中.

(3) ip_queue_xmit()完成面向连接套接字输出包的路由和IP包头封装. 当套接字处于连接状态时,
所有从套接字发出的包都具有确定的路由, 无需为每一个输出包查询它的目的入口,
可将套接字直接绑定到路由入口上, 这由套接字的目的缓冲指针(dst_cache)来完成.
ip_queue_xmit()首先为输入包建立IP包头, 经过本地包过滤器后,
再将IP包分片输出(ip_fragment), 如果需要的话.

(4) IP包生成器的输出经过本地包过滤器后输入包的路由入口, 对于点播地址来说,
输入到IP输出器中(ip_output); 对于广播或同播地址来说, 输入到IP同播输出器(ip_mc_output).
在IP输出器中, 再经过路由后过滤器,
进入路由的"邻居"入口(dst->neighbour->output)或硬件帧头缓冲入口(dst->hh->hh_output).
邻居是指与主机自已在网络接口设备层次上直达的相邻主机.
邻居负责解析输出包的硬件投送地址, 将包投递给相邻的目的主机或网关主机.
当邻居成功解析包的硬件投送地址时, 将在包的目的入口上创建硬件帧头缓冲结构(dst->hh),
使得后继包可以直接使用组装好的帧头, 直接将包传递给包调度器(dev_queue_xmit).
包调度器按照包的优先级进行重排, 最后将包提交给设备驱动程序发送(dev->hard_start_xmit).


IP包生成接口
------------
; net/ipv4/ip_output.c:

int sysctl_ip_default_ttl = IPDEFTTL; 缺省的IP包生存期为64

/*
* Add an ip header to a skbuff and send it out.
*/
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
对包的数据体添加IP头后直接输出
u32 saddr, u32 daddr, struct ip_options *opt)
{
struct rtable *rt = (struct rtable *)skb->dst;
struct iphdr *iph;

/* Build the IP header. */
if (opt)
iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
else
iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));

iph->version = 4;
iph->ihl = 5;
iph->tos = sk->protinfo.af_inet.tos;
iph->frag_off = 0;
if (ip_dont_fragment(sk, &rt->u.dst)) 如果IP包的目的入口禁止分片
iph->frag_off |= htons(IP_DF);
iph->ttl = sk->protinfo.af_inet.ttl; 取套接字协议选项中的生存期
iph->daddr = rt->rt_dst; 取IP包路由的目的地址
iph->saddr = rt->rt_src; 取IP包路由的源地址
iph->protocol = sk->protocol; 取套接字IP协议代码
iph->tot_len = htons(skb->len); IP包总长度
ip_select_ident(iph, &rt->u.dst); 为IP包分配标识号, 禁止分片的IP包标识为零
skb->nh.iph = iph;

if (opt && opt->optlen) {
iph->ihl += opt->optlen>>2;
ip_options_build(skb, opt, daddr, rt, 0); 设置IP选项区
}
ip_send_check(iph); 设置IP包头的校验和

/* Send it out. */
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
output_maybe_reroute); 过滤输出并且目的路径可能会被改变
}

int ip_build_xmit(struct sock *sk,
int getfrag (const void *,
char *,
unsigned int,
unsigned int), 取数据片段的函数指针
const void *frag, 以上函数的调用参数
unsigned length,
struct ipcm_cookie *ipc, IP包配置信息
struct rtable *rt,
int flags) 从用户数据建立IP包
{
int err;
struct sk_buff *skb;
int df;
struct iphdr *iph;

/*
* Try the simple case first. This leaves fragmented frames, and by
* choice RAW frames within 20 bytes of maximum size(rare) to the long path
*/

if (!sk->protinfo.af_inet.hdrincl) { 如果IP包头不由用户创建
length += sizeof(struct iphdr); 取IP包总长

/*
* Check for slow path.
*/
if (length > rt->u.dst.pmtu || ipc->opt != NULL) 如果包长度大于目的入口的最大片断长
return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags);
} else {
if (length > rt->u.dst.dev->mtu) { 如果包长大于目的入口设备的最大片段长
ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
return -EMSGSIZE;
}
}
if (flags&MSG_PROBE) 测试操作
goto out;

/*
* Do path mtu discovery if needed.
*/
df = 0;
if (ip_dont_fragment(sk, &rt->u.dst)) 如果禁止分片
df = htons(IP_DF);

/*
* Fast path for unfragmented frames without options.
*/
{
int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;

skb = sock_alloc_send_skb(sk, length+hh_len+15,
0, flags&MSG_DONTWAIT, &err); 为套接字分配发送包
if(skb==NULL)
goto error;
skb_reserve(skb, hh_len); 保留硬件帧头空间
}

skb->priority = sk->priority; 取套接字的优先级
skb->dst = dst_clone(&rt->u.dst); 取路由的目的入口

skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);

if(!sk->protinfo.af_inet.hdrincl) {
iph->version=4;
iph->ihl=5;
iph->tos=sk->protinfo.af_inet.tos;
iph->tot_len = htons(length);
iph->frag_off = df;
iph->ttl=sk->protinfo.af_inet.mc_ttl;
ip_select_ident(iph, &rt->u.dst);
if (rt->rt_type != RTN_MULTICAST)
iph->ttl=sk->protinfo.af_inet.ttl;
iph->protocol=sk->protocol;
iph->saddr=rt->rt_src;
iph->daddr=rt->rt_dst;
iph->check=0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
; 读取用户一片数据
}
else 如果IP包头由用户创建, 直接将用户数据读入IP头所在位置
err = getfrag(frag, (void *)iph, 0, length);

if (err)
goto error_fault;

err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
output_maybe_reroute);
if (err > 0)
err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;
if (err)
goto error;
out:
return 0;

error_fault:
err = -EFAULT;
kfree_skb(skb);
error:
IP_INC_STATS(IpOutDiscards);
return err;
}
static int ip_build_xmit_slow(struct sock *sk,
int getfrag (const void *,
char *,
unsigned int,
unsigned int),
const void *frag,
unsigned length,
struct ipcm_cookie *ipc,
struct rtable *rt,
int flags) 建立IP选项区或者分片输出
{
unsigned int fraglen, maxfraglen, fragheaderlen;
int err;
int offset, mf;
int mtu;
u16 id = 0;

int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
int nfrags=0;
struct ip_options *opt = ipc->opt;
int df = 0;

mtu = rt->u.dst.pmtu;
if (ip_dont_fragment(sk, &rt->u.dst))
df = htons(IP_DF);

length -= sizeof(struct iphdr);

if (opt) {
fragheaderlen = sizeof(struct iphdr) + opt->optlen;
maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
} else {
fragheaderlen = sizeof(struct iphdr);

/*
* Fragheaderlen is the size of 'overhead' on each buffer. Now work
* out the size of the frames to send.
*/

maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
} 求最大IP包长

if (length + fragheaderlen > 0xFFFF) {
ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
return -EMSGSIZE;
}

/*
* Start at the end of the frame by handling the remainder.
*/

offset = length - (length % (maxfraglen - fragheaderlen));
取最后一个片段的数据偏移量

/*
* Amount of memory to allocate for final fragment.
*/

fraglen = length - offset + fragheaderlen; 求取后一个片段IP包全长

if (length-offset==0) { 如果用户数据恰好是最大单片数据长度的整数倍
fraglen = maxfraglen;
offset -= maxfraglen-fragheaderlen;
}

/*
* The last fragment will not have MF (more fragments) set.
*/

mf = 0;

/*
* Don't fragment packets for path mtu discovery.
*/

if (offset > 0 && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) {
ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
return -EMSGSIZ
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值