Bandwidth Shaping and Network Filtering by Operating SystemA guide to better network filtering, by Avleen Vig |
You manage a heterogeneous network and want to provide different quality of service agreements and network restrictions based on the client operating system. With pf and altq, you can now limit how much bandwidth is available to users of different operating systems, or force outbound web traffic through a transparent filtering proxy. This article describes how to install pf, altq and Squid on your FreeBSD router and web proxy to achieve these goals.
In an ideal environment, there would be no need for bandwidth shaping, OS fingerprint based filtering or even Quality of Service (QoS). Several factors in the real world require us to change our game plan. Bandwidth is not free and many Internet service providers charge customers based on bandwidth usage. Worms, viruses and compromised systems can all lead to higher bandwidth costs. In the wake of the W32.Slammer worm which saturated the connections of infected networks, many companies saw their monthly connectivity bills skyrocket due to the worm's traffic. Filtering your connections based on operating systems can go part way to helping keep such situations from running away. While I will focus on filtering traffic from Windows systems, this process can equally be applied to BSD, Linux, Mac OS, or a host of other operating systems listed in the pf.os file on your system. This may be especially useful to people running older versions of operating systems which have not or cannot be patched but still require some network connectivity.
As an extension of transparent filtering, content filtering is also possible with tools such as squidGuard allowing children, and corporate desktops alike to browse in relative safety.
During my research for this paper, I was asked by several people why I chose to use BSD, pf, altq and Squid for this task. There are other tools which come close to providing the required functionality, but none offer to fill our requirements as readily as these. Linux and iptables can work with Squid to provide a transparent proxy, but cannot filter connections by operating system. Though otherproxy servers exist, Squid is considered to be one of the best available today.
It is important to note that OS fingerprinting only works on TCP SYN packets, which initiate TCP sessions, and not on currently established connections or UDP sessions. While this will not be a problem for most systems and network administrators, you may want to pay more attention to your UDP filtering rules.
pf and altq provide us with packet filtering and bandwidth shaping functionality, respectively. Their relationship is not unlike IPFIREWALL and DUMMYNET, where pf and altq are both configured through the same rules file.
While pf can be used universally, altq requires a supported network card.
The good news is that most network cards in common use are supported. Look
at the SUPPORTED DEVICES section of man 4 altq to find a list
of supported network cards.
Once you have confirmed you have a supported device, it's time to add pf and altq to your kernel. You will need to recompile your kernel as described in the FreeBSD Handbook, adding the following options to the end of your kernel configuration file:
device pf
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_CDNR
options ALTQ_PRIQ
NOTE: If you are installing altq on a multi-processor system, you
will also need to add options ALTQ_NOPPC to your configuration
before you recompile your kernel.
After you have recompiled your kernel and rebooted, you should test pf to
make sure it installed correctly, with the command pfctl -s
rules. If you see the error pfctl: /dev/pf: No such file or
directory, pf did not install correctly. If you see the error
No ALTQ support in kernel ALTQ related functions disabled, pf
is working but altq is not. In the latter case, you will still be able to
force users through a transparent proxy, but you will not be able to limit
bandwidth using altq.
Install Squid with the following command:
cd /usr/ports/www/squid && make config install clean
You will be presented with a list of options you can use when compiling
Squid. To enable transparent proxy support select SQUID_PF. You
can also select or deselect any other options. I often find
SQUID_SNMP useful for gathering and graphing statistics using
RRDTool.
Once Squid is installed, edit /usr/local/etc/squid/squid.conf.
You will need to set at least the following options:
http_port YOUR_PROXY_IP:3128
http_access deny to_localhost
acl our_networks src YOUR_NETWORK/24
http_access allow our_networks
visible_hostname YOUR_HOSTNAME
httpd_accel_host virtual
httpd_accel_port 80
httpd_accel_with_proxy on
httpd_accel_uses_host_header on
If you use the default squid.conf, make sure to comment out
the following line or we will be restricted to connecting to only SSL
ports:
http_access deny CONNECT !SSL_ports
Replace YOUR_PROXY_IP with the IP address your proxy server will listen
on, YOUR_NETWORK/24 with your internal network address range (e.g.,
192.168.0.0/24), and YOUR_HOSTNAME with the hostname you want shown to users
in error messages. YOUR_HOSTNAME is not required but extremely useful if you
have a cluster of proxy servers sharing a common front end such as a load
balancer. While you can get by with changing only the above, you should
spend some time going through the remainder of your squid.conf
file and tuning it to your needs. Over time you may need to tune various
other options such as cache sizes or connection timeouts. The Squid
configuration file is a behemoth and spending an hour now getting familiar
with various options may save you time and trouble in the future.
This step is an optional step for those who wish to do content filtering. I use squidGuard to filter content on my home network to prevent my young children from running into sites I feel are unsuitable. The possible applications for this are endless as squidGuard offers blacklists for ads, adult content, drugs, gambling sites, hate sites and more.
Install squidGuard with the following command:
cd /usr/ports/www/squidguard && make install clean
Next to Squid, squidGuard is very simple to configure which is probably a good thing as you spent the last 2 hours configuring Squid, right?
Copy /usr/local/etc/squid/squidGuard.conf.sample to
/usr/local/etc/squid/squidGuard.conf and open it in your editor
of choice. If you wish to filter by time of day, you should read the
squidGuard Configuration
guide. For now the filters should always be enabled. Remove the existing
source sample-clients block and create a new block in its place
with your own network range, e.g.:
source localnet {
ip 192.168.0.0/24
}
Now at the end of the file, replace the existing acl block
and create this new block:
acl {
default {
pass !ads !drugs !gambling !porn all
redirect http://YOUR_WEBSERVER/cgi-bin/squidGuard.cgi?clientaddr=%a&clientname=%n&clientuser=%i&clientgroup=%s&url=%u
}
}
This acl will deny access to any URLs listed in the ads, drugs, gambling
or porn databases. Other databases are also available and listed in the
configuration file above the acl blocks for you to choose from. You should
pay particular attention to the redirect statement which makes
reference to a CGI. In the event that a user visits a restricted site they
will be redirected to this URL. A sample squidGuard.cgi is available here
for you to download. You should place this on a web server and alter the
redirect and change YOUR_WEBSERVER to the name of
your webserver.
Squid also needs to be told how to use squidGuard. This is done with the
redirect_program directive in
/usr/local/etc/squid/squid.conf, so open this file one last
time and search for the line:
# TAG: redirect_program
Below this, add the command:
redirect_program /usr/local/bin/squidGuard
The time has come to bring all of our installed programs and changes
together by configuring our packet filter and bandwidth shaper. By default,
FreeBSD keeps pf's configuration in /etc/pf.conf. The sample
configuration file is very well documented, we will start by looking at an
example of filtering with Network Address Translation (NAT). This example
will assume an Internet connection with 3Mb downstream and 512Kb upstream of
bandwidth available:
ext_if = "fxp0"
external_addr = "1.2.3.4"
int_if = "fxp1"
internal_net = "192.168.0.0/24"
proxy_server = "192.168.0.10"
altq on $ext_if bandwidth 512Kb cbq queue { windows_out, trusted_out }
queue windows_out bandwidth 20%
queue trusted_out bandwidth 80%
altq on $int_if bandwidth 3Mb cbq queue { windows_in, trusted_in }
queue windows_in bandwidth 20%
queue trusted_in bandwidth 80%
rdr on $int_if inet proto tcp from $internal_net os "Windows" to any \
port www -> $proxy_server port 3128
nat on $ext_if from $internal_net to any -> ($ext_if)
pass out quick on $ext_if inet proto tcp from $proxy_server \
to any port www keep state queue windows_out
pass out quick on $ext_if inet proto tcp from $internal_net os "Windows" \
to any keep state queue windows_out
pass out quick on $ext_if inet proto tcp from $internal_net os "unknown" \
to any keep state queue windows_out
pass out on $ext_if inet proto tcp from $internal_net \
to any keep state queue trusted_out
pass out quick on $int_if proto tcp from any to $proxy_server \
queue windows_in
pass out on $int_if proto tcp from any to $internal_net queue trusted_in
In the first five lines, we declare variables which will be used
repeatedly in the rule set. Variables are quite helpful in configuration
files. By using them correctly, we can save much time in future if we need
to change IP addresses, network interfaces, protocols, or almost anything
else. When you declare a variable in your rules file, you can access it
later as $variable_name.
The six altq configuration lines that follow, set the amount of bandwidth
we have available. Bandwidth is controlled on the interface where packets
leave the router. Packets going from our network to the Internet, leave the
router on $ext_if, so we set $ext_if to 512Kb.
Similarly, packets coming into our network from the Internet leave the
router in $int_if, so we set $int_if to 3Mb. pf
understands b, Kb, Mb and Gb, which represent bits, kilobits, megabits and
gigabits per second.
The altq line then specifies the type of scheduler to use to queue packets.
We use cbq in this example. The schedulers decide which order
the queues are processed in. Class Based Queuing (CBQ) splits the available
network bandwidth between two or more queues, where each queue has packets
assigned to it by source or destination addresses, port or other
identifiable factor (in our case, operating system). Queues can also have a
priority so that some packets are dealt with before others. The OpenBSD Packet
Queuing page has more detail on the different types of
schedulers.
At the end of this line we specify which queues are limited to this amount
of bandwidth.
The next two lines define the queues themselves which will be handled by altq. The format is quite understandable and explicitly states how much bandwidth is available to each queue. This can be specified either as a percentage or a fixed amount. If you think of your Internet connection as a road, a queue defines how many lanes different packets can travel on. The more lanes they can use, the more data gets transferred. This configuration is then repeated for the incoming bandwidth.
The rdr line is the key to our meeting our filtering
objective. We specify that all TCP traffic (proto tcp) from the
internal network ($internet_net) that comes from a Windows
system and is going to any other address on port 80 (os "Windows" to
any port www), should be redirected to the proxy server on port 3128
( -> $proxy_server port 3128).
The nat line sets up our network address translation, which
lets our internal network communicate with the Internet.
You will have noticed that each line of the pf rules also ends in either
windows_in, windows_out, trusted_in,
or trusted_out. These are the four queues we set up previously
as part of the altq rules, and lets pf know which queues pf and altq should
use when processing the packets. These queues are entirely optional. Leaving
them out of the pf rules, would cause any packets matching those rules to
not be bandwidth limited by altq.
The simplest format of the pf rules is:
<pass|block> <in|out> on <interface> from <src> to <dst> [keep state] [queue <queue_name>]
In addition to this, port numbers and protocols can also be given.
We add a special rule for os "unknown" traffic. Patches to operating systems
and IP stacks can change the fingerprint of packets, and filtering all
unrecognized traffic through our Windows queue helps us avoid problems in
future. The last two rules control inbound traffic. Since it is not possible
to know if the destination system is Windows or not, pf cannot filter the
incoming traffic based on an operating system that is not already using the
proxy. In practice, this does not pose a serious problem, as all of the web
traffic from these systems is already traveling across the
bandwidth-restricted proxy. If the Windows systems are running Peer-to-Peer
software such as BitTorrent, it would not be forced across the proxy. In
this case the traffic can then be queued by giving port numbers used:
bittorrent_ports = "6881:6999"
pass out quick on $int_if proto { tcp, udp } from any to any \
port $bittorrent_ports queue windows_in
| Counter: 15649 page |
| views since Apr 2006 |