September 9, 2007
笔记本上装的是CentOS5.0,最近注意到在关机的时候,最后硬盘总是嘎吱一声响;主要是声音太响了,和windows下关机的声音不同。于是google了一下中文,发现有好多人在问,还给出了在ubuntu和fedora7上的workaround;我在CentOS5.0上看了一下阿,涉及到的sysfs项目根本不存在。没办法,又google了一下英文,终于找到了问题的原由。
这是广泛存在于2.6.22以前版本内核的bug,通常它会出现于使用SATA PATA硬盘和采用udev的linux版本上;别的系统不清楚,反正我笔记本上的CentOS是满足了-_-!。
Bug #63937 in linux-source-2.6.17 (Ubuntu) and
Kernel Bug Tracker Bug 7838
在2.6.22内核版本发布的说明中提到了该bug被修复。所以我就把CentOS5.0的kernel升级到了2.6.22-手动编译的,没发现现成的rpm包。
安装完成后,硬盘的问题没有了。哈哈,如释重负。不过,又发现了一个新的问题:系统在启动过程中提示vmware的服务没法启动,需要重新运行/usr/bin/vmware-config.pl来修复此问题。
按照说明一步一步来,在编译vmnet的内核模块驱动时出错了。出错的原因是linux2.6.22的面向网络的内核api和一些数据结构发生了改动,vmnet在编译过程中无法找到相对应的数据结构。
然后就求助google了,google到一篇blog
vmware workstation 6.0 on Linux 2.6.22
它提供了一个patch,或者用它提供的vmnet.tar把vmware对应安装目录下的vmnet.tar给替换掉就可以了。
替换掉之后,重新运行vmware-config.pl,编译安装就成功了。
enjoy it!
Leave a Comment » |
Linux Kernel |
Permalink
Posted by mxi1
September 9, 2007
4. 根据概率丢弃送往特定ip的packets
涉及到的api:
void kfree_skb (struct sk_buff * skb);
Drop a reference to the buffer and free it if the usage count has hit zero.
与前面的处理方式不太一样的地方在于,kfree_skb()的工作放在了内核源代码的原来的地方,而不是放在kernel module里面。如果在module里面将skb给释放掉,然后返回到kernel原来的程序处继续执行的话,可能会出现下面的代码继续存取该skb包的情况,因此引起严重的错误。
所以这次在kernel module中只是判断丢包的条件成立与否,真正的丢包工作就放在了原来的内核代码处。
涉及到的内核代码片段:dev_queue_xmit(skb);
int dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct Qdisc *q;
int rc = -ENOMEM;
//...
}
在原先第一个实例的地方,可以添加一个HOOK:
if(ee_drop_func && (*ee_drop_func)(skb))Ã {
kfree_skb(skb);
return 0;
}
内核模块的内容:
在内核模块中,主要完成的工作是,实现这样一个函数:
int drop_packet_func(struct sk_buff *skb);
判断skb包的目的地址是否是需要丢包的地址,如果是的话,判断符合不符合丢包的条件,来设置返回值为1否,为1则表示需要丢弃。
代码略。
Leave a Comment » |
Linux Kernel |
Permalink
Posted by mxi1
September 8, 2007
3.利用Module来获得收到的SYN封包数
基本思路:设置一个全局变量count来作计数器。当kernel收到一个SYN包时,计数加一;这样就可以得到具体的数值了。
kernel接收packets流程:netif_rx()->ip_rcv()->tcp_v4_rcv()->tcp_v4_do_rcv();其中,tcp_v4_rcv()函数是TCP用来建立SYN封包时调用的函数。所以,我们需要做的就是在该函数中插入一个HOOK。
位置:Linux 2.6.20 net/ipv4/tcp_ipv4.c:1611 line
int tcp_v4_rcv(struct sk_buff *skb);
在此函数之前,声明全局变量:
int ee_syn_count = 0;
EXPORT_SYMBOL(ee_syn_count);
int(*eefunc_syn)(struct sk_buff *skb) = 0;
EXPORT_SYMBOL(eefunc_syn);
需要将eefunc_syn函数的实现放在内核模块中,将对它的调用放在tcp_v4_rcv()函数中的bh_lock_sock(sk);…bh_unlock_sock(sk)之间。
此外,还要提供一个procfs接口来读取ee_syn_count所记录的数值。不过LDD3现在建议使用sysfs来导出信息,不再建议使用/proc文件系统。
内核模块的内容如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <net/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
extern int ee_syn_count;
extern int (*eefunc_syn)(struct sk_buff *skb);
static int ee_packet_monitor(struct sk_buff *skb)
{
struct iphdr *iph;
struct tcphdr *th;
iph = skb->nh.iph;
if(iph->protocol == IPPROTO_TCP) {//useless?
th = skb->h.th;
if(th->syn) { //judge SYN packet
ee_syn_count ++;
if(ee_syn_count >= (1<<16 -1))
ee_syn_count = 0;
}
}
return 0;
}
static int ee_read_syn_count(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
int len = sprintf(buf, "syn count = %d\\n", ee_syn_count);
return len;
}
static int __init ee_init(void)
{
eefunc_syn = ee_packet_monitor;
//create one /proc node
create_proc_read_entry("ee_packet_monitor", 0 /*default mode*/,
NULL/*parent dir*/, ee_read_syn_count,
NULL/*client data*/);
printk("ee_syn_module inserted\\n");
return 0;
}
static void __exit ee_exit(void)
{
eefunc_syn = 0;
remove_proc_entry("ee_packet_monitor", NULL/*parent dir*/);
printk("ee_syn_module removed\\n");
}
module_init(ee_init);
module_exit(ee_exit);
MODULE_LICENSE("GPL");
SYN的packet只是在TCP建立连接的时候才会有,因此在client或者server端只能在创建tcp连接的时候才能看到一个。因为ftp是使用tcp协议的,所以我就用ftp连接了一下别的电脑,然后关闭ftp连接;这样会使得ee_syn_count加1。可以使用如下的命令来看一下/proc文件系统的输出:
[root@localhost:lkm]cat /proc/ee_packet_monitor
会得到结果: syn count = 1;
Leave a Comment » |
Linux Kernel |
Permalink
Posted by mxi1
September 7, 2007
在阅读《利用Module修改Linux TCP/IP Kernel》的时候的笔记。
1. 基本的修改方法:
预先在需要修改的内核代码部分插入简单的代码(HOOK),比如在源文件中加入一个全局的函数指针变量,在代码中加入判断语句,如果该函数指针不为空,则执行之,否则忽略。
然后重新编译,安装内核。
真正的实现部分放在了kernel module的编写上。需要实现一个函数,并把它的地址赋给前面所声明的全局的函数指针变量。
2. 实例1
修改kernel,使得kernel可以在每发出一个packet后,都能在/var/log/messages中产生一条log记录;
涉及到的源文件:net/core/dev.c中的dev_queue_xmit(struct sk_buff *skb);
我们可以在该函数的最开始阶段就加入HOOK代码,
if(hook_func_pointer){ hook_func_pointer;}
然后,需要编写一个kernel module来实现这个hook_func_pointer的功能,这个很简单了.
3. 实例2
利用IP层的Module来修改输出packet的IP header的内容;
IP header的字段可以从网上查到。因为它的绝大多数字段都有实际的用途,如果修改不当的话,可能会造成网络功能的不正常。这里要对TOS字段下手,type of service它可以使得packet具有更高的优先权;修改它应该不会有什么大问题。现在默认的包的TOS为BE,我们想改成EF或者AF。EF>AF>BE. 这个要看路由器是否支持TOS,所以影响不大。
涉及到的文件和位置:ip packet在发送出去的时候所涉及到的函数是ip_finish_output(struct sk_buff *skb).它在net/ipv4/ip_output.c中。照例插入HOOK
实现kernel module: 代码如下
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/ip.h>
extern int (*eefunc02)(struct sk_buff*);
int change_TOS(struct sk_buff *skb)
{
struct iphdr *iph = skb->nh.iph;
if(iph) {
iph->tos = 0xef;
ip_send_check(skb->nh.iph);
}
return 0;
}
static int __init eeFunc_init(void)
{
eefunc02 = change_TOS;
printk("\\n---module init--\\n");
return 0;
}
static void __exit eeFunc_exit(void)
{
eefunc02 = 0;
printk("\\n--module exit--\\n");
}
module_init(eeFunc_init);
module_exit(eeFunc_exit);
MODULE_LICENSE("GPL");
接着的改进:只修改TCP的ACK包的tos值,书上将其修改为0xb8,说是对应EF.
int change_ack_TOS(struct sk_buff *skb)
{
struct iphdr *iph = skb->nh.iph;
struct tcphdr *th;
if(iph->protocol == IPPROTO_TCP) {
skb->h.raw = (unsigned char *)(skb->nh.raw + iph->ihl*4);
th = skb->h.th;
}
if((tcp_flag_word(th) & TCP_FLAG_ACK)) {
skb->nh.iph->tos = 0xB8;
ip_send_check(skb->nh.iph);
}
return 0;
}
2 Comments |
Linux Kernel |
Permalink
Posted by mxi1