一、L7-filter介绍
 
TCP/IP模块总共包含四层,在L7-filter之前,netfilter可以过滤前三层数据,分别为网络接口层,网络层,传输层,具体实现方法如下:
Network access layer: iptables -A CHAIN -m mac --mac-source …"
Internet: iptables -A CHAIN -s IP_ADDRESS …"
Transport: iptables -A CHAIN -p tcp --dport 80 …
 
三层间可以进行任意的组合,如下实例:
iptables –A FORWARD –s 192.168.0.2 –p tcp -–dport 80 –m mac –-mac-source 00:01:BC:2D:EF:2A –j DROP"
 
L7-filter在增加了一个新的功能至netfilter中,匹配的数据包属于哪一个应用程序,发现TCP/IP四层,一个非常重要的事情是,L7-filter是iptables的一个匹配选项,所以,可以做为所有规则的另一个匹配选项,因此,可以在所有的操作上使用iptables上是使用L7-filter来匹配数据包.
 
L7-filter包含了下列三个重要的部分:
内核补丁:这是提供了内核分析IP数据包头部信息的方法。
iptables 补丁:其为iptables提供了匹配选项
集合模式档案:其提供了正则表达式所支持的协议
 
二、安装L7-filter
 
Applying the Kernel Patch
The first step is to download the kernel source we want from http://www.kernel.org. Next, we need to download L7-filter from http://l7-filter.sourceforge.net.
For this chapter, we used kernel source 2.6.12.5 and L7-filter version 2.0 beta. After downloading what you need to the /usr/src folder, unzip the L7-filter TAR archive as follows:
router:/usr/src# tar xfvz netfilter-layer7-v2.0-beta.tar.gz
netfilter-layer7-v2.0-beta/
netfilter-layer7-v2.0-beta/stray_code
netfilter-layer7-v2.0-beta/for_older_kernels/
netfilter-layer7-v2.0-beta/for_older_kernels/
kernel-2.6.9-2.6.10-layer7-1.2.patch
netfilter-layer7-v2.0-beta/for_older_kernels/
kernel-2.6.0-2.6.8.1-layer7-0.9.2.patch
netfilter-layer7-v2.0-beta/for_older_kernels/
kernel-2.6.11-2.6.12-layer7-1.4.patch
netfilter-layer7-v2.0-beta/CHANGELOG
 
netfilter-layer7-v2.0-beta/README
netfilter-layer7-v2.0-beta/iptables-layer7-2.0.patch
netfilter-layer7-v2.0-beta/kernel-2.6.13-layer7-2.0.patch
netfilter-layer7-v2.0-beta/kernel-2.4-layer7-2.0.patch
router:/usr/src#
Next, go to the kernel source root and patch the kernel using the appropriate patch:
router:/usr/src/linux-2.6.12.5# patch -p1 < ../netfilter-layer7-v2.
0-beta/for_older_kernels/kernel-2.6.11-2.6.12-layer7-1.4.patch
patching file include/linux/netfilter_ipv4/ip_conntrack.h
patching file include/linux/netfilter_ipv4/ipt_layer7.h
patching file net/ipv4/netfilter/Kconfig
patching file net/ipv4/netfilter/Makefile
patching file net/ipv4/netfilter/ip_conntrack_core.c
patching file net/ipv4/netfilter/ip_conntrack_standalone.c
Hunk #1 succeeded at 189 with fuzz 2 (offset 37 lines).
patching file net/ipv4/netfilter/ipt_layer7.c
patching file net/ipv4/netfilter/regexp/regexp.c
patching file net/ipv4/netfilter/regexp/regexp.h
patching file net/ipv4/netfilter/regexp/regmagic.h
patching file net/ipv4/netfilter/regexp/regsub.c
router:/usr/src/linux-2.6.12.5#
Next, run make config, make menuconfig, or make Xconfig. You need to enable the following options:
Code maturity level options | Prompt for development and/or incomplete code/drivers
Netfilter (Device Drivers | Networking support | Networking Options | Network packet filtering)
Connection tracking (Network packet filtering | IP: Netfilter Configuration | Connection tracking)
Connection tracking flow accounting and IP tables support (on the
same screen)
Layer 7 match support
Next, compile the kernel, install it as usual, and reboot your machine with the
new kernel.
When compiling, you might see a warning, depending the compiler version:
CC [M] net/ipv4/netfilter/ipt_layer7.o
net/ipv4/netfilter/ipt_layer7.c:457: warning: initialization from incompatible pointer type
Just ignore the warning and go .
Applying the iptables Patch
To apply the iptables patch, we need the iptables sources from
http://www.netfilter.org. Go to the iptables source root and patch it with
the patch provided by the L7-filter project.
router:/usr/src/iptables-1.3.4# patch -p1 < ../
netfilter-layer7-v2.0-beta/iptables-layer7-2.0.patch
patching file extensions/.layer7-test
patching file extensions/libipt_layer7.c
patching file extensions/libipt_layer7.man
router:/usr/src/iptables-1.3.4#
Because file permissions can't be included in a patch, you need to set execute permission for the file extensions/.layer7-test.
router:/usr/src/iptables-1.3.4# chmod +x extensions/.layer7-test
Next, we will compile iptables using make and specifying the path to our patched kernel. In our case:
router:/usr/src/iptables-1.3.4# make KERNEL_DIR=/usr/src/
linux-2.6.12.5
Making dependencies: please wait...
Extensions found: IPv4:CLUSTERIP IPv4:layer7 IPv4:recent IPv6:ah IPv6:esp IPv6:frag IPv6:ipv6header IPv6:hbh IPv6:dst IPv6:rt
...
Now we will install iptables using the make install command and also specifying the path to the patched kernel. In our case:
router:/usr/src/iptables-1.3.4# make install KERNEL_DIR=/usr/src/
linux-2.6.12.5
cp iptables /usr/local/sbin/iptables
cp iptables.8 /usr/local/man/man8/iptables.8
...
Now, we're almost done. Please note that the new iptables tool might be in a different folder than the original. For example, we can see that the make install command installed iptables in /usr/local/sbin/iptables because we didn't specify the BINDIR option when compiling iptables. We also need to make sure that we're using the right tool when issuing commands. We can verify that using iptables –V and comparing the versions we have:
router:~# iptables -V
iptables v1.2.11
router:~# type iptables
iptables is hashed (/sbin/iptables)
router:~# /usr/local/sbin/iptables -V
iptables v1.3.4
Protocol Definitions
First, we need to download the protocol definitions archive from the L7-filter project page at sourceforge, http://prdownloads.sourceforge.net/l7-filter/
l7-protocols-2006-06-03.tar.gz?download. Next, we need to copy the pattern files (.pat) from the archive to the /etc/l7-protocols folder.
router:/usr/src/l7-protocols-YYYY-MM-DD# mkdir /etc/l7-protocols
router:/usr/src/l7-protocols-YYYY-MM-DD# cp protocols/* /etc/
l7-protocols
The /etc/l7-protocols folder is the default folder for pattern files, and iptables will look into it and its subfolders, but not recursively. This means that iptables will search for pattern files in /etc/l7-protocols and in any /etc/l7-protocols/subdir, but not in /etc/l7-protocols/subdir/subsubdir.
If you don't wish to set up the /etc/l7-protocols folder, you can specify the pattern files folder by doing:
iptables […] –m layer7 –-l7dir /path-to/patterns –-l7proto […]
Please note that you have to specify the patterns folder before the protocol.
Testing the Installation
First, we might want to see if our module is in place. We can do that using the modinfo command:
router:~# modinfo ipt_layer7
filename: /lib/modules/2.6.12.5-home.made/kernel/net/ipv4/
netfilter/ipt_layer7.ko
author: Matthew Strait < quadong@users.sf.net>,
Ethan Sommer < sommere@users.sf.net>
license: GPL
description: iptables application layer match module
vermagic: 2.6.12.5-home.made preempt PENTIUMIII gcc-3.3
depends: ip_tables
The output shows that we have a module called ipt_layer7 and some information about it, such as filename, author, license, description, version, and other module dependencies.
Next, we will try to load the module using the modprobe command:
router:~# modprobe ipt_layer7
router:~# lsmod
Module Size Used by
ipt_layer7 12364 0
The modprobe command didn't produce any errors. By using the lsmod command, we can see the module loaded into the kernel, its size, and the number of processes it is used by (in our case 0), because we didn't used it yet.
Next, we might want to test it and see if it works. We will do that by using Apache web server, placing some files in the web folder and downloading them.
When downloading the files, we should see that all packets are matched. First, we will use the command iptables –Z to zero the counters of all the rules in all chains, and then we will insert an accounting rule in the OUTPUT chain to match all the outgoing HTTP traffic.
router:~# iptables -Z
router:~# iptables -A OUTPUT -m layer7 --l7proto http
router:~# iptables -L OUTPUT -n -v
Chain OUTPUT (policy ACCEPT 10168 packets, 3433K bytes)
pkts bytes target prot opt in out source destination
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto http
Next, we download the file whale.qt (for example) from this server; and look in the apache access.log:
"GET /whale.qt HTTP/1.1" 200 11727970 "-"
So we must have 11 Mb matched the accounting rule in the OUTPUT chain.
router:~# iptables -L OUTPUT -n -v
Chain OUTPUT (policy ACCEPT 172K packets, 65M bytes)
pkts bytes target prot opt in out source destination
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto http
router:~#
Oops! No packet was matched. Did we do something wrong or is L7-filter not good at all? Well, in most cases, the first option is valid, so when looking again at the
ipt_layer7 module, we can see that it depends ly the ip_tables module.
We now quickly verify what modules are loaded in the kernel, but we don't see the ip_conntrack module. Normally, ipt_layer7 should have had ip_conntrack in the dependencies, but it doesn't. That is why we neither got any errors while loading the module nor did we get any result.
We know that L7-filter uses the ip_conntrack module; so we need to load it. Let's see what happens now.
router:~# modprobe ip_conntrack
router:~# iptables -L OUTPUT -n -v
Chain OUTPUT (policy ACCEPT 457K packets, 159M bytes)
pkts bytes target prot opt in out source destination
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto http
router:~# wget http://127.0.0.1/whale.qt
--00:37:21-- http://127.0.0.1/whale.qt
=> `whale.qt'
Connecting to 127.0.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 11,727,970 [video/quicktime]
100%[==================================================================================>] 11,727,970 12.74M/s
00:37:22 (12.71 MB/s) - `whale.qt' saved [11727970/11727970]
router:~# iptables -L OUTPUT -n -v
Chain OUTPUT (policy ACCEPT 467K packets, 175M bytes)
pkts bytes target prot opt in out source destination
1433 12M all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto http
Well, as you can see, it worked. Now we have a Linux router with application layer filtering capabilities.
L7-filter Applications
We can use L7-filter with any iptables option; after all, L7-filter provides just another match option. However, not all the things we can do with our new match option are recommended, because L7-filter might match packets belonging to other applications than the e you want.
Filtering Application Data
Blocking application data that passes through your router is e of the non-recommended things that you can do with L7-filter.
Traffic from different applications might look similar; so you might experience problems when dropping data based the L7-filter match. For example, if you drop packets that belong to eDonkey, there might be some other protocols that will experience problems. The eDonkey pattern matches about 1% of other streams with random data.
If you still want to use L7-filter for blocking several applications passing through your Linux router, it can be done as follows:
router:~# iptables -A FORWARD -m layer7 --l7proto edonkey -j DROP
You can also use L7-filter in conjunction with port numbers or IP addresses.
For example:
router:~# iptables -A FORWARD -m layer7 --l7proto edonkey –d 192.168.1.131 -j DROP
will drop eDonkey packets that have the destination 192.168.1.131.
Application Bandwidth Limiting
The creators of the L7-filter project describe this as being the best way to use their project. To limit bandwidth consumed by applications or users, we will use the tc tool described in Chapter 3.
L7-filter can match some or all application data in IP packets. Therefore, we can set a mark the packets belonging to a specific application. Due to the fact that L7-filter is a match option, we can use netfilter mark to mark the packets, TOS to set a specific type of service, or DSCP to set a DSCP value, whatever we think is best to use
with tc.
As an example, we will build a script that will limit BitTorrent to 2 Mbps for all our users, and FTP traffic to 512 Kbps for e user and to 1 Mbps for another user.
First, we need to mark BitTorrent and FTP data with arbitrary integer values. We can do it like this:
router:~# iptables -t mangle -A POSTROUTING -m layer7 --
l7proto bittorrent -j MARK --set-mark 5
router:~# iptables -t mangle -A POSTROUTING -m layer7 --
l7proto ftp –d 192.168.1.100 -j MARK --set-mark 6
router:~# iptables -t mangle -A POSTROUTING -m layer7 --
l7proto ftp –d 192.168.1.112 -j MARK --set-mark 7
Now, let's verify the POSTROUTING chain:
router:~# iptables -t mangle -L POSTROUTING -n -v
Chain POSTROUTING (policy ACCEPT 5855K packets, 4686M bytes)
pkts bytes target prot opt in out source destination
3183 1404K MARK all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto bittorrent MARK set 0x5
119 1012K MARK all -- * * 0.0.0.0/0
192.168.1.100 LAYER7 l7proto ftp MARK set 0x6
12 100K MARK all -- * * 0.0.0.0/0
192.168.1.112 LAYER7 l7proto ftp MARK set 0x7
OK, it seems that packets are matched and marked with the values we want (5 for BitTorrent and 7 for FTP).
We will use CBQ in this example; so we need to create the cbq classes we want. Let's create a script called limits like this:
#!/bin/bash
#delete root class. When running this
#script we need to delete limits first
tc qdisc del dev eth1 root
#create the root class
tc qdisc add dev eth1 root handle 10: cbq bandwidth 100Mbit avpkt 1000
tc class add dev eth1 parent 10:0 classid 10:1 cbq bandwidth 100Mbit rate \
100Mbit allot 1514 weight 10Mbit prio 8 maxburst 20 avpkt 1000
#create a 2 mbps class for bittorrent
tc class add dev eth1 parent 10:1 classid 10:100 cbq bandwidth 100Mbit rate \
2Mbit allot 1514 weight 256Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 10:100 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 25 handle 5 fw \
flowid 10:100
#create a 512 kbps class for ftp for the client 192.168.1.100
tc class add dev eth1 parent 10:1 classid 10:200 cbq bandwidth 100Mbit rate \
512Kbit allot 1514 weight 64Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 10:200 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 25 handle 6 fw \
flowid 10:200
#create a 1 mbps class for ftp for the client 192.168.1.112
tc class add dev eth1 parent 10:1 classid 10:300 cbq bandwidth 100Mbit rate \
1Mbit allot 1514 weight 128Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 10:300 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 25 handle 7 fw \
flowid 10:300
After running the script, to verify the configuration, we need to zero the POSTROUTING chain in the mangle table and run the script.
router:~# chmod +x limits
router:~# iptables -t mangle -Z POSTROUTING; ./limits
RTNETLINK answers: No such file or directory
router:~#
The error we got when running the limits script was generated because no limits existed that interface, meaning that no root class was present. If we will run it again, we will not get any errors.
Now, the commands iptables –t mangle –L POSTROUTING –n –v and tc –s class show dev eth1 should generate the same number of bytes and packets matched for each rule and corresponding class.
Accounting with L7-filter
Accounting is the easiest application that you can do with L7-filter. You can set
up accounting scripts to see how many bytes are consumed by applications in
your network.
Let's take the following example a building router with 11 clients connected. We want to see what the traffic looks like at the application layer; so we will set up an accounting script like this:
iptables -A FORWARD -m layer7 --l7proto directconnect
iptables -A FORWARD -m layer7 --l7proto bittorrent
iptables -A FORWARD -m layer7 --l7proto http
iptables -A FORWARD -m layer7 --l7proto ftp
iptables -A FORWARD -m layer7 --l7proto yahoo
iptables -Z
After a few minutes, we can get an idea what type of traffic is most popular with those clients:
router:~# iptables -L FORWARD -n -v
Chain FORWARD (policy ACCEPT 289K packets, 209M bytes)
pkts bytes target prot opt in out source destination
62318 55M all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto directconnect
6978 1202K all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto bittorrent
8037 6116K all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto http
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto ftp
108 10724 all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto yahoo
And the winner is dc++ with about 25% of the traffic!
IPP2P: A P2P Match Option
IPP2P is an application that matches P2P data in IP traffic, mostly in the way that
L7-filter does, except that IPP2P is oriented ly towards P2P applications. The project home page is http://www.ipp2p.org.
Installing IPP2P
Installing IPP2P is much simpler than L7-filter. We need to check its web page for the latest stable version and download it.
router:~# wget http://www.ipp2p.org/downloads/ipp2p-X.Y.Z.tar.gz
--19:59:51-- http://www.ipp2p.org/downloads/ipp2p-X.Y.Z.tar.gz
=> `ipp2p-X.Y.Z.tar.gz'
Resolving www.ipp2p.org... 81.169.145.64
Connecting to www.ipp2p.org[81.169.145.64]:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 18,910 [application/x-tar]
100%[==================================================================================>] 18,910 --.--K/s
19:59:52 (181.98 KB/s) - `ipp2p-X.Y.Z.tar.gz' saved [18910/18910]
Next, we extract the files from the archive:
router:~# tar xfvz ipp2p-X.Y.Z.tar.gz
The README file says "modify the Makefile (change "IUSER = -I/usr/src/iptables/include" to wherever iptables.h is located)". For example, we have the iptables sources in /usr/src/iptables-1.3.4. However, some versions don't have IUSER defined in the makefile, but they have:
ifndef $(IPTABLES_SRC)
IPTVER ?= \
$(shell $(IPTABLES_BIN) --version | $(SED) -e 's/^iptables v//')
IPTABLES_SRC = $(wildcard /usr/src/iptables-$(IPTVER))
endif
which means that the script will check the iptables version and search for the iptables sources in /usr/src/iptables-<VERSION>; so, in our situation, we don't have to modify anything.
Now type make. The application compiles, after which we have to copy
libipt_ipp2p.so to the iptables lib folder, in our case /usr/local/lib/iptables/, and ipt_ipp2p.ko to the module's directory and run depmod -a. In
this case:
router:~/ipp2p-0.8.0# cp ipt_ipp2p.ko /lib/modules/2.6.12.5-home.made/kernel/net/ipv4/
router:~# depmod -a
router:~# modinfo ipt_ipp2p
filename: /lib/modules/2.6.12.5-home.made/kernel/net/ipv4/
ipt_ipp2p.ko
author: Eicke Friedrich/Klaus Degner < ipp2p@ipp2p.org>
description: An extension to iptables to identify P2P traffic.
license: GPL
vermagic: 2.6.12.5-home.made preempt PENTIUMIII gcc-3.3
depends: ip_tables
Next, we need to load the module, and it's all set.
router:~/ipp2p-0.8.0# modprobe ipt_ipp2p
Using IPP2P
IPP2P provides another match option for iptables; so the syntax is:
iptables … -m ipp2p –-option ...
where option can be:
Option
P2P network
Protocol
Quality
--edk
eDonkey, eMule, Kademlia
TCP and UDP
very good
--kazaa
KaZaA, FastTrack
TCP and UDP
good
--gnu
Gnutella
TCP and UDP
good
--dc
Direct Connect
TCP ly
good
--bit
BitTorrent, extended BT
TCP and UDP
good
--apple
AppleJuice
TCP ly
(need feedback)
--winmx
WinMX
TCP ly
(need feedback)
--soul
SoulSeek
TCP ly
good (need feedback)
--ares
Ares, AresLite
TCP ly
moderate (DROP ly)
Another possibility is to use as option --ipp2p, which matches all the protocols
stated earlier.
This new match option has the same rules as the L7-filter project, and it's basically the same. Let's make some tests and compare the results between IPP2P and L7-filter.
IPP2P versus L7-filter
In order to test the results of L7-filter and IPP2P matches, we will set up accounting rules and see the results. We will use three of the most popular P2P applications: DirectConnect (DC++), BitTorrent, and eDonkey.
Let's set up a script like this:
iptables -I FORWARD -m layer7 --l7proto directconnect
iptables -I FORWARD -m ipp2p --dc
iptables -I FORWARD -m layer7 --l7proto bittorrent
iptables -I FORWARD -m ipp2p --bit
iptables -I FORWARD -m layer7 --l7proto edonkey
iptables -I FORWARD -m ipp2p --edk
After a few minutes, we pick up the results:
router:~/ipp2p-0.8.0# iptables -L FORWARD -n -v
Chain FORWARD (policy ACCEPT 25M packets, 18G bytes)
pkts bytes target prot opt in out source destination
2797 253K all -- * * 0.0.0.0/0 0.0.0.0/0
ipp2p v0.8.0 --edk
1533 434K all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto edonkey
6665 1069K all -- * * 0.0.0.0/0 0.0.0.0/0
ipp2p v0.8.0 --bit
7375 1273K all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto bittorrent
192 36558 all -- * * 0.0.0.0/0 0.0.0.0/0
ipp2p v0.8.0 --dc
693K 640M all -- * * 0.0.0.0/0 0.0.0.0/0
LAYER7 l7proto directconnect
The results confirm our expectations that there are a lot of differences between these two applications.
For example, for eDonkey, IPP2P matched more packets (but less data) than L7-filter. That doesn't mean that IPP2P matches edk better; it means that IPP2P and L7-filter don't match the same packets. The explanation is found in the edonkey.pat file in /etc/l7-protocols:
edonkey
# http://gd.tuwien.ac.at/opsys/linux/sf/p/pdonkey/eDonkey-protocol-0.6
#
# In addition to \xe3, \xc5 and \xd4, I see a lot of \xe5
#
# God this is a mess. What an irritating protocol.
# This will match about 1% of streams with random data in them!
^[\xe3\xc5\xe5\xd4].?.?.?.?([\x01\x02\x05\x14\x15\x16\x18\x19\x1a\x1b\x1c\x20\x21\x32\x33\x34\x35\x36\x38\x40\x41\x42\x43\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x5b\x5c\x60\x81\x82\x90\x91\x93\x96\x97\x98\x99\x9a\x9b\x9c\x9e\xa0\xa1\xa2\xa3\xa4]|\x59................?[ -~]|\x96....$)
# matches everything and too much
# ^(\xe3|\xc5|\xd4)
# ipp2p essentially uses "\xe3....\x47", which doesn't seem at all right to me.
# bandwidtharbitrator uses
# e0.*@.*6[a-z].*p$|e0.*@.*[a-z]6[a-z].*p0$|e.*@.*[0-9]6.*p$|emule|edonkey
# no comments to explain what all the mush is, of course...
Well, they all use different patterns, which explains the difference.
BitTorrent values are closer between IPP2P and L7-filter, but we can't tell which e is closer to the truth. To be able to make such an affirmation, we have to set up a test lab and I really don't think it's worth the trouble.
DC++ data, however, has the most differences. In this case, I'm 90% sure that
L7-filter was way more accurate that IPP2P, because I saw a few of the users behind that Linux router our DC++ hub during the time that I waited for these values.