Linux内核
Linux内核发送构造数据包的方式 - 内核研究 -哥德巴赫猜想Page 1of 3 博客首页 注册 建议与交流 排行榜 加入友情链接
哥德巴赫猜想 达则兼济天下,穷则独善其身。
http://doc.100lw.com
首页文章相册
音乐博客圈收藏夹留言发表文章管理博客
关于作者Linux内核发送构造数据包的方式
本文欢迎自由转载,但请标明出处,并保证本文的完整性。
作者:Godbach
日期:2009/09/01
姓名:Godbach
职业:
年龄:
位置:
这里并不详细介绍如何在内核中构造数据包,下文如有需要会在适当的位置进行分析。这里简单的分析讲一下内核态基于Netfilter框架构造数据包的方式。 内核中可以用到的构造数据包的方式,个人认为可以分为两种。 其一,我们直接用alloc_skb申请一个skb结构体,然后根据实际的应用填充不同的成员,或者基于当前数据包的skb,调用skb_copy_expand()函数等新申请一个nskb,并且拷贝skb的内容。 其二,也是个人比较常用的,就是直接在先前接收到的数据包skb上作修改,主要有源IP、目IP,如果是TCP/UDP协议的话,还有源端口目的端口号。总之,就是根据自己的需求去调整数据包的相关成员即可。 通常,这两种方式最终可能都要涉及到重新计算各个部分的校验和,这也是必须的。 我的分类
我的文章分类
内核研究
网络安全
流量管理
C编程
SVN版本管理
Shell
Linux系统相关
FreeBSD
电子基础与设计
影视
随笔
我的图片分类
我的链接分类
博友链接
我的音乐分类二、如何发送构造的数据包 承接上文,数据包已经构造完毕,下一步关键就是如何发送数据包了。个人这里总结的有两种方法。 方法一,就是让数据包接着按照Netfilter的流程进行传输。因为数据包的一些内容已经被更改,尤其是当源IP和目的IP被更改,主要是交换的情况下,是需要确保有路由可查的。 NF框架中查路由的位置一是在PREROUTING之后,而是在LOCALOUT之后。又由于这里是需要将数据包从本地发送出去。因此,可以考虑让修改后的数据包从LOCALOUT点发出。 内核代码中有这种方式的典型体现。本文涉及的相关内核代码的版本都是2.6.18.3。源文件为ipt_REJECT.c,函数send_reset用于往当前接收到数据包的源IP上发送RST包,整个函数涉及了数据包的构造和发送,这里一起做个简单分析。 /* Send RST reply */ static void send_reset(struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; struct iphdr *iph = oldskb->nh.iph; struct tcphdr _otcph, *oth, *tcph; struct rtable *rt; u_int16_t tmp_port; u_int32_t tmp_addr; int needs_ack; int hh_len; /* 判断是否是分片包*/ if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)) return; /*得到TCP头部指针*/ oth = skb_header_pointer(oldskb, oldskb->nh.iph->ihl * 4, sizeof(_otcph), &_otcph); if (oth == NULL) return; /* 当期收到的包就是RST包,就不用再发送RST包了*/ if (oth->rst) return; /*检查数据包的校验和是否正确*/
if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP))
return;
/*这一步比较关键,做的就是更新路由的工作。该函数的主要工作就是将当前数据包的源IP当做路由的目的IP,同时考虑数据包
的目的IP,得到去往该源IP的路由*/
if ((rt = route_reverse(oldskb, oth, hook)) == NULL)
return;
hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
/* 拷贝当前的oldskb,包括skb结构体和数据部分。这就是我们上面提到的构造数据包的第一种方式*/
nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
GFP_ATOMIC);
if (!nskb) {
dst_release(&rt->u.dst);
return;
}
/*因为是拷贝的oldskb,这里不需要再引用了,因此释放对该路由项的引用*/
dst_release(nskb->dst);
/*将新构造数据包引用的路由指向上面由route_reverse函数返回的新的路由项 */
nskb->dst = &rt->u.dst;
/* 清除nskb中拷贝过来的oldskb中链接跟踪相关的内容*/
nf_reset(nskb);
nskb->nfmark = 0;
skb_init_secmark(nskb);
/*以下就是构造数据包的实际数据部分。如果我们将这里不为nskb新申请缓冲区,而直接指向oldskb的缓冲区,就使我们上面
提到的第二种构造数据包的方法。*/
/*获取nskb的tcp header*/
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
/*交换源和目的IP */
tmp_addr = nskb->nh.iph->saddr;
nskb->nh.iph->saddr = nskb->nh.iph->daddr;
nskb->nh.iph->daddr = tmp_addr;
/*交换源和目的端口 */
tmp_port = tcph->source;