编译IMQ过程

来源:互联网 发布:软件测试培训班靠谱吗 编辑:程序博客网 时间:2024/05/14 14:59

   IMQ(Intermediate queueing device)中介队列设备通常用于解决两个问题:

   简单来说,IMQ可以让你往一个队列规定中放任何东西,例如对刚刚进入网卡的数据包打上标记去进行入口整形等。但由于一些原因,IMQ设备并没有加入Linux的标准核心中,所以,使用前需要先单独编译。

一、运行平台
关于最新的IMQ patch见这里:Linux IMQ - Intermediate Queueing Device,其中包括新核心使用的IMQ patch补丁,以及iptables设别imq设备的补丁。不同的核心版本需要的补丁不同,请根据您运行的环境决定
安装内核源码:
yum install rpm-build redhat-rpm-config unifdef
wget http://vault.centos.org/5.7/updates/SRPMS/kernel-2.6.18-274.7.1.el5.src.rpm
rpm -Uvh kernel-2.6.18-274.7.1.el5.src.rpm

cd /usr/src/redhat/SPECS
rpmbuild -bp --target=`uname -m` kernel.spec
最终源码在/usr/src/redhat/build目录中:/usr/src/redhat/BUILD/kernel-2.6.18/linux-2.6.18-274.7.1.el5.x86_64/

二、编译核心模块
1、准备原核心的Makefile文件

这一步的作用是,让编译出来的模块核心版本和当前版本是一致的。(若使用默认Makefile,则版本是EXTRAVERSION = -42.7AXcustom,模块会加载不上去)
引用
# uname -r
2.6.18-274.7.1.el5
mv Makefile Makefile.old
cp /lib/modules/2.6.18-274.7.1.el5/build/Makefile .

2、打imq核心补丁
patch -p1 <linux-2.6.18-imq.diff
patching file drivers/net/imq.c
patching file drivers/net/Kconfig
Hunk #1 succeeded at 94 (offset 1 line).
patching file drivers/net/Makefile
Hunk #1 succeeded at 146 (offset 18 lines).
patching file include/linux/imq.h
patching file include/linux/netfilter_ipv4/ipt_IMQ.h
patching file include/linux/netfilter_ipv6/ip6t_IMQ.h
patching file include/linux/skbuff.h
Hunk #1 succeeded at 307 (offset 16 lines).
patching file net/core/dev.c
Hunk #1 succeeded at 97 (offset 3 lines).
Hunk #2 succeeded at 1416 with fuzz 2 (offset 71 lines).
patching file net/core/skbuff.c
Hunk #1 succeeded at 371 with fuzz 2 (offset -110 lines).
Hunk #2 FAILED at 439.
1 out of 2 hunks FAILED -- saving rejects to file net/core/skbuff.c.rej
patching file net/ipv4/netfilter/ipt_IMQ.c
patching file net/ipv4/netfilter/Kconfig
patching file net/ipv4/netfilter/Makefile
patching file net/ipv6/netfilter/ip6t_IMQ.c
patching file net/ipv6/netfilter/Kconfig
patching file net/ipv6/netfilter/Makefile
patching file net/sched/sch_generic.c
Hunk #1 succeeded at 87 (offset -1 lines).
Hunk #2 succeeded at 181 with fuzz 1.
Hunk #3 succeeded at 635 (offset -10 lines). 
查看出错的文件:并根据这些文件修改对应的文件。
[root@localhost linux-2.6.18-274.7.1.el5.x86_64]# find  .  -name  *.rej
./net/Makefile.rej
./net/core/skbuff.c.rej
./net/Kconfig.rej

3、引用原核心config文件,增加IMQ配置

# make mrproper (初始化编译环境)
# cp /boot/config-2.6.18-274.7.1.el5 .config

原核心的配置文件,一般都放在boot目录下。然后,可以选择IMQ的配置。

yum install ncurses-devel ncurses
make oldconfig
make menuconfig

从下面地方选择:
引用
IMQ (intermediate queueing device) support (IMQ)
Location:
   -> Device Drivers
    -> Networking support
     -> Network device support (NETDEVICES)
      -> IMQ (intermediate queueing device) support (IMQ)

选择<M>编译为模块后,有两个参数可以设置:
引用
Number of IMQ devices (IMQ_NUM_DEVS):默认IMQ设备的数量
IMQ behavior (PRE/POSTROUTING):IMQ的处理方法在nat表的勾取位置
默认是BA,也就是:PREROUTING(Before NAT),POSTROUTING(After NAT),可根据实际情况选择。

接下来是NETFILTER部分的选择:
引用
IMQ target support (IP_NF_TARGET_IMQ)
Location:
 -> Device Drivers
  -> Networking support
   -> Networking support (NET)
    -> Networking options
     -> Network packet filtering (replaces ipchains) (NETFILTER)
      -> IP: Netfilter Configuration

另外,IPv6也是可选的:
引用
IPv6: Netfilter Configuration

保存后,可查看新增加的配置:
引用
# cat .config|grep IMQ
CONFIG_IP_NF_TARGET_IMQ=m
CONFIG_IP6_NF_TARGET_IMQ=m
CONFIG_IMQ=m
# CONFIG_IMQ_BEHAVIOR_AA is not set
# CONFIG_IMQ_BEHAVIOR_AB is not set
CONFIG_IMQ_BEHAVIOR_BA=y
# CONFIG_IMQ_BEHAVIOR_BB is not set
CONFIG_IMQ_NUM_DEVS=2

4、编译核心镜像
由于增加了IMQ设备,核心中设备表有改变,所以需要重新编译新的核心镜像。
引用
make prepare
make modules_prepare
vi net/core/skbuff.c     375和376行注释掉
 make bzImage
cp arch/x86_64/boot/bzImage /boot/vmlinuz-2.6.18-274.7.1.el5_imq
cp /boot/System.map-2.6.18-274.7.1.el5 /boot/System.map-2.6.18-274.7.1.el5_imq

5、编译模块
根据patch修改的文件,编译对应的模块。
首先,是编译IMQ网卡设备:

# make clean
# make modules_prepare
# make SUBDIRS='drivers/net/' modules
# make SUBDIRS='drivers/net/' modules_install

※SUBDIRS可指定含源码的目录,即只编译该目录中的模块。也可以这样运行:
引用
# make M=drivers/net/

接着,加载模块,测试:

# insmod drivers/net/imq.ko numdevs=2

激活设备:

# ifconfig imq0 up
# ifconfig imq1 up

若没问题,则继续编译netfilter及ipv4、ipv6等模块:

 make SUBDIRS='net/ipv4/' modules && make SUBDIRS='net/ipv4/' modules_install
 make SUBDIRS='net/ipv6/' modules && make SUBDIRS='net/ipv6/' modules_install
 make SUBDIRS='net/sched/' modules && make SUBDIRS='net/sched/' modules_install
 depmod -a

☆当然,你也可以直接运行make modules && make modules_install,编译所有的模块,但这个时间很长。
若你按上面的步骤编译后,发现原有的模块有问题,建议你把全部模块都编译吧。安装后的驱动模块,会放在/lib/modules/(核心名称)/kernel/目录中。


6、生成initrd文件

# cd /boot/
# mkinitrd initrd-2.6.18-274.7.1.el5_img.img `uname -r`

7、修改Grub
修改/boot/grub/menu.lst,让其从新核心默认启动:
引用
title Asianux 2.0 SP2 new (2.6.9-42.7AX_new)
       root (hd0,2)
       kernel /boot/vmlinuz-2.6.9-42.7AX_new ro root=LABEL=/
       initrd /boot/initrd-2.6.9-42.7AX_new.img

※注意,千万记得改为从新核心启动,否则,旧核心加载模块时,可能会发生kernel panic错误的。
三、为iptables增加IMQ支持
激活IMQ设备后,需要由iptables对数据包打上标记,所以还需要让iptables增加IMQ支持。
1、准备工作
iptables的Makefile默认使用/usr/src/linux作为核心源码目录,为方便,做个软链接:

# cd /usr/src
# ln -s linux-2.6.9-42.7AX/ linux

把下面文件这几行注释掉:
引用
# vi /usr/src/linux/include/linux/config.h
//#if !defined (__KERNEL__) && !defined(__KERNGLUE__)
//#error including kernel header in userspace; use the glibc headers instead!
//#endif

否则在编译的时候,会报错退出的。
2、编译iptables
为保持与原系统的兼容性,这里以DC Server带的1.2.11版本进行说明,使用iptables-1.2.11-3.1.RHEL4.src.rpm源码包,最终生成rpm文件。

# rpm -ivh iptables-1.2.11-3.1.RHEL4.src.rpm

拷贝补丁:

# cd /usr/src/asianux/SOURCES/
# cp /root/iptables-1.2.9-imq1.diff ./iptables-1.2.9-imq1.patch

修改spec文件:
引用
# cd ../SPECS
# vi iptables.spec
在适当的地方加入:
Patch10: iptables-1.2.9-imq1.patch
%prep
# IMQ support
%patch10 -p1
chmod +x ./extensions/.IMQ-test*
以及修改所有KERNEL_DIR=/usr,为KERNEL_DIR=/usr/src/linux,即:
make COPT_FLAGS="$OPT" KERNEL_DIR=/usr/src/linux LIBDIR=/%{_lib}
make COPT_FLAGS="$OPT" KERNEL_DIR=/usr/src/linux LIBDIR=/%{_lib} iptables-save iptables-restore
make COPT_FLAGS="$OPT" KERNEL_DIR=/usr/src/linux LIBDIR=/%{_lib} ip6tables-save ip6tables-restore

然后编译及安装:

# rpmbuild -bb iptables.spec
# rpm -Uvh ../RPMS/i386/iptables-* --force

这方式适用于对现有iptables扩展模块,也可参考附录中给iptables添加模块的方法编译。

修改后的spec文件下载:
下载文件
点击这里下载文件

※实际上,修改spec的操作是实现了下面的工作:
引用
cd /iptables源码
patch -p1 <../../iptables-1.2.9-imq1.diff  #打补丁
chmod +x extensions/.IMQ-test*  #激活IMQ模块
make KERNEL_DIR=/linux核心源码路径  #必须指向已打上IMQ补丁的kernel源码路径
cp extensions/*IMQ* /lib/iptables/  #拷贝IMQ模块到系统适当的位置
chmod +x /lib/iptables/*IMQ*
四、总结
1、通过上面的步骤可见,若要成功编译IMQ模块,需要进行三个步骤,缺一不可:
引用
a)为核心打入IMQ补丁,并编译成imq网卡模块;
b)编译核心的netfilter模块,为下面iptables的调用做准备;
c)为iptables打IMQ补丁,生成iptables的IMQ模块。

※编译时,请注意保持kernel源码一致,iptables源码也一致。但netfilter的源码和iptables源码一般没有关联,也不必完全和原系统版本相同的。我这里只是考虑到前后兼容而已。

2、为保持原系统的一致性,我建议您把修改后核心源码的库文件同步到发行版相应目录中。
一般发行版有个开发包文件kernel-devel,可参考上面打imq patch时的提示做同步工作:

# cd /usr/src/linux
# cp .config /usr/src/kernels/2.6.9-42.7AX-i686/
# cp drivers/net/{Kconfig,Makefile} /usr/src/kernels/2.6.9-42.7AX-i686/drivers/net/
# cp -r include/config/imq /usr/src/kernels/2.6.9-42.7AX-i686/include/config/
# cp -r include/config/ip/nf/target/imq /usr/src/kernels/2.6.9-42.7AX-i686/include/config/ip/nf/target/
# cp -r include/config/ip6/nf/target/imq /usr/src/kernels/2.6.9-42.7AX-i686/include/config/ip6/nf/target/
# cp include/linux/{autoconf.h,compile.h,imq.h,skbuff.h} /usr/src/kernels/2.6.9-42.7AX-i686/include/linux/
# cp include/linux/netfilter_ipv4/ipt_IMQ.h /usr/src/kernels/2.6.9-42.7AX-i686/include/linux/netfilter_ipv4/
# cp include/linux/netfilter_ipv6/ip6t_IMQ.h /usr/src/kernels/2.6.9-42.7AX-i686/include/linux/netfilter_ipv6/
# cp net/ipv4/netfilter/{Kconfig,Makefile} /usr/src/kernels/2.6.9-42.7AX-i686/net/ipv4/netfilter/
# cp net/ipv6/netfilter/{Kconfig,Makefile} /usr/src/kernels/2.6.9-42.7AX-i686/net/ipv6/netfilter/
# cp Module.symvers /usr/src/kernels/2.6.9-42.7AX-i686/

目录/usr/src/kernels/2.6.9-42.7AX-i686/请根据您修改的核心版本决定。这是一个可选的步骤,仅为了提高兼容性。

五、附录
参考资料:
How to Install IMQ
终于实现在kernel2.6下为iptables 添加扩展模块
How To Compile A Kernel - The CentOS Way
Linux核心编译与管理
 前面两篇日志都是为了做使用IMQ设备的准备。因为IMQ模块并没有包括在标准核心中,所以,需要我们另行编译。而这今天的日志中,我用一个几乎是最简单的例子,来说明如何使用IMQ设备模块。其中使用到tc的相关知识,如果您还不知道该怎么做,请先回头看看前几天的日志,否则会有点不知所云的。

一、准备工作
首要的准备工作,当然是要让您的系统支持IMQ模块,有三个条件:
1、核心支持imq设备
引用
# uname -a
Linux openvz91 2.6.9-42.7AX.imq #1 Fri May 30 10:50:00 CST 2008 i686 i686 i386 GNU/Linux
# ll /lib/modules/2.6.9-42.7AX.imq/kernel/drivers/net/imq.ko
-rwxr--r--  1 root root 8340  5月 30 14:05 /lib/modules/2.6.9-42.7AX.imq/kernel/drivers/net/imq.ko

2、netfilter支持IMQ模块
引用
# ll /lib/modules/2.6.9-42.7AX.imq/kernel/net/ipv4/netfilter/ipt_IMQ.ko
-rwxr--r--  1 root root 3264  5月 30 14:05 /lib/modules/2.6.9-42.7AX.imq/kernel/net/ipv4/netfilter/ipt_IMQ.ko
# ll /lib/modules/2.6.9-42.7AX.imq/kernel/net/ipv6/netfilter/ip6t_IMQ.ko
-rwxr--r--  1 root root 3264  5月 30 14:05 /lib/modules/2.6.9-42.7AX.imq/kernel/net/ipv6/netfilter/ip6t_IMQ.ko

3、iptables支持IMQ模块
引用
# ll /lib/iptables/libip*_IMQ.so
-rwxr-xr-x  1 root root 3860  6月  2 17:04 /lib/iptables/libip6t_IMQ.so
-rwxr-xr-x  1 root root 3856  6月  2 17:04 /lib/iptables/libipt_IMQ.so

以上要求缺一不可。(如果不需要支持ipv6,可以不考虑ipv6的模块)
4、加载模块
引用
# modprobe imq numdevs=2
# modprobe ipt_IMQ
# ifconfig imq0 up
# ifconfig imq1 up

一切正常的话,会发现多了两个虚拟网卡,他们是支持tc的:
引用
# ifconfig imq0 && ifconfig imq1
imq0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
         UP RUNNING NOARP  MTU:1500  Metric:1
         RX packets:9726 errors:0 dropped:0 overruns:0 frame:0
         TX packets:9726 errors:0 dropped:0 overruns:0 carrier:0
         collisions:0 txqueuelen:30
         RX bytes:3903679 (3.7 MiB)  TX bytes:3903679 (3.7 MiB)

imq1      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
         UP RUNNING NOARP  MTU:1500  Metric:1
         RX packets:12223 errors:0 dropped:0 overruns:0 frame:0
         TX packets:12220 errors:0 dropped:0 overruns:0 carrier:0
         collisions:0 txqueuelen:30
         RX bytes:13794660 (13.1 MiB)  TX bytes:13791620 (13.1 MiB)
# tc qdisc show
qdisc pfifo_fast 0: dev eth0 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc pfifo_fast 0: dev imq0 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc pfifo_fast 0: dev imq1 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

二、演示脚本
IMQ模块虚拟出来的两个网卡(默认两个:imq0、imq1),可用于把队列规定qdiscs分别附加到它们上面,然后由iptables转发数据包到这两个设备,以实现利用egress qdiscs (出口队列规定)来对流经设备的数据整形。这样结果就是,无论出口流量,还是入口流量都可得到分类和控制的目的了。
脚本如下:
引用
#!/bin/bash
modprobe imq numdevs=2
modprobe ipt_IMQ
ifconfig imq0 up
ifconfig imq1 up

tc qdisc del dev imq0 root
tc qdisc del dev imq1 root

#IMQ 0
tc qdisc add dev imq0 root handle 1: htb default 20
tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k
tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit
tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit
tc qdisc add dev imq0 parent 1:10 handle 10: pfifo
tc qdisc add dev imq0 parent 1:20 handle 20: sfq
tc filter add dev imq0 parent 1:0 protocol ip prio 1 u32 match ip dst 192.168.228.30 flowid 1:10

iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0

#IMQ 1
tc qdisc add dev imq1 root handle 2: htb default 20
tc class add dev imq1 parent 2: classid 2:1 htb rate 4mbit burst 15k
tc class add dev imq1 parent 2:1 classid 2:10 htb rate 1mbit ceil 4mbit
tc class add dev imq1 parent 2:1 classid 2:20 htb rate 1mbit ceil 4mbit
tc qdisc add dev imq1 parent 2:10 handle 10: pfifo
tc qdisc add dev imq1 parent 2:20 handle 20: sfq
tc filter add dev imq1 parent 2:0 protocol ip prio 1 u32 match ip dst 192.168.228.30 flowid 2:10

iptables -t mangle -A POSTROUTING -o eth0 -j IMQ --todev 1

这里的机器只有一块网卡eth0,而根据tc的原理,原来只能对上传进行整形(限速)的。但通过该脚本,我们即可发现,下载时,数据流会由iptables转交给imq0,以实现入口整形的效果。(下载被分类、限速了)同样的,上传时,也会由imq1处理。
※请特别留意,不能让一个imq设备同时处理PREROUTING和POSTROUTING,否则,会出现kernel panic的。
脚本下载:
下载文件
点击这里下载文件

这例子比较简单,但能充分的说明问题:把imq设备作为一个中间介质,对经其发送的数据整形以达到最终目的。分类可通过u32或iptables MARK实现,这里就不再详细说明了。有需要的话,可参考前面的日志内容,或这里:
linux下TC控制流量文档(脚本)
使用 IMQ+HTB+iptable 统一流量控制心得

原创粉丝点击