Building a Linux firewall

Learn about the three types of firewalls—application proxy gateway, circuit level relay, and packet filter—and how they are used to protect your network from unauthorized access.
How It's Done

The firewall code includes three facilities—accounting, blocking, and forwarding. Accounting rules are used for maintaining packet and byte count statistics for selected IP traffic. Blocking rules specify rules for accepting and rejecting packets to and from the firewall itself. Forwarding rules specify which packets will be forwarded by the firewall; this implies a source and destination address of something other than the firewall. You can specify any type of rule based on source and/or destination IP addresses; TCP or UDP ports; protocols such as TCP, UDP, or ICMP; or by combinations of the three.

The services are activated when the kernel boots, and the rules are set and modified with the setsockopt(2) system call. The current accounting statistics and firewall rules can be viewed by looking at the files ip_acct, ip_block, and ip_forward in the /proc/net directory. The contents of ip_acct look like this:

# cat /proc/net/ip_acct
IP accounting rules
C0A80101/FFFFFFFF->00000000/00000000 00000000
 0 0 0 386 392946 0 0 0 0 0 0 0 0 0 0

In this example, one rule is present, which basically says to keep statistics on connections from 192.168.1.1 to anywhere for all ports and all protocols.

Changing the accounting and firewall facilities has to be done via a C program, Perl script, or some other language that supports the setsockopt(2) system call. Here is a sample program that will change the default policy for the forwarding rules:

# cat set_policy.c
#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/ip_fw.h>

main(int argc, char **argv)
{
  int    p, sfd;
  struct ip_fw fw;

  fw.fw_flg = 0;

  if (strcmp(argv[1], "accept") == 0) {
    p = IP_FW_F_ACCEPT;
  }
  else if (strcmp(argv[1], "reject") == 0) {
    p = IP_FW_F_ICMPRPL;
  }
  else if (strcmp(argv[1], "deny") == 0) {
    p = 0;
  }

  sfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  setsockopt(sfd, IPPROTO_IP, IP_FW_POLICY_FWD,
             &p, sizeof(int));
}

# cat /proc/net/ip_forward
IP firewall forward rules, default 4

# set_policy deny

# cat /proc/net/ip_forward
IP firewall forward rules, default 0

As you can see, it is just a matter of opening up a raw IP socket and using setsockopt() to change the environment. The setsockopt call is setting the default policy for the forwarding rules (IP_FW_POLICY_FWD). The value for the forward policy command is kept in p and determined by the command-line arguments accept, reject, and deny. The words are equated to the defined values of IP_FW_F_ACCEPT, IP_FW_F_ICMPRPL, and 0. The difference between deny and reject is that the deny policy will just throw packets away, while the reject policy will throw packets away, and also respond with an “ICMP port unreachable” message to the originating host.

Don't Reinvent The Wheel

Writing the C or Perl code to manipulate firewall rules may sound like a lot of fun, but some administrators may not have the time to roll their own firewall interface. ipfw is a utility in the net-tools (version 1.3.6) package that will allow the root user to add, delete, or list information dealing with the accounting and firewall rules. Figure 1 shows the output of the blocking rules in the current host.

Obviously, this is a lot better than looking at the direct contents of the /proc/net/ip_block file. The -n option just tells ipfw not to resolve addresses to names.

Adding a rule is done quite easily. Let's say we wanted to accept SNMP queries from a remote management station (note that the author does not really advocate this kind of behavior—this is just an example). We could add the rule:

# ipfw add b accept udp from 0.0.0.0/0 \
  to 20.2.51.105 161

to give us the new list shown in Figure 2.

Before we move on, let me explain some of the notation in the ipfw command given above. The arguments tell us we're adding a blocking (b) rule for the UDP protocol. The rule is accepting UDP datagrams from any host (0.0.0.0/0) to 20.2.51.105, but only those destined for port 161. 20.2.51.105 is the address for one of the interfaces on the firewall.

Assume the above rule for SNMP is not what we wanted. Let's say we only wanted to allow SNMP queries from one particular network—for example 20.2.61.0 (subnetted of course). We can delete the rule we just added and then put in our new rule.

# ipfw del b accept udp from 0.0.0.0/0 \
  to 20.2.51.105 161
# ipfw add b accept udp from 20.2.61.0/24 \
  to 20.2.51.105 161

Figure 3 shows our new rule set. The syntax 20.2.61.0/24 allows you to specify a netmask with the address. The /24 says there is a 24 bit netmask or 255.255.255.0.

The more astute reader at this point may be asking, “What the heck do those rules mean anyway?” I'll get to that, but first, I want to talk about a utility which I find preferable to ipfw.

The ipfwadm (version 1.2) program by Jos Vos (available from www.xos.nl/linux/ipfwadm) is an administrative utility for IP firewalling and accounting which is similar to ipfw. It offers, I think, a slightly more intuitive interface, better output, and a better man page (not everyone reads the source code for documentation).

To list the rules shown in Figure 3, issue this command:

# ipfwadm -B -l -n
IP firewall block rules, default policy: accept
typ prot source         destination   ports
den tcp  0.0.0.0/0      20.2.51.105   * -> *
den tcp  0.0.0.0/0      192.168.1.1   * -> *
acc udp  20.2.61.0/24   20.2.51.105   * -> 161
rej udp  0.0.0.0/0      192.168.1.1   * -> *
acc udp  0.0.0.0/0      20.2.51.105   53 -> *
rej udp  0.0.0.0/0      20.2.51.105   * -> *

Notice a couple of things. First, ipfwadm always shows the default policy; I like to see this. Second, the type fields for these rules:

rej udp  0.0.0.0/0      192.168.1.1   * -> *
rej udp  0.0.0.0/0      20.2.51.105   * -> *

are set for reject, rather than deny, as shown in ipfw's output in Figure 3. Well, they really are set to reject. ipfw only supports the deny and accept policies, not reject.

Given our vast knowledge on setting and listing rules, let's rebuild the table again (except for the SNMP rule) while adding a few more details. But first, we'll define what our network looks like, shown in Figure 4.

We'll call the 20.2.51.0 network the “outside” network and the 192.168.1.0 the “inside” network. Since blocking rules apply to the firewall itself, the rules will be set on deathstar. First, we'll flush any rules we have and set the default policy to accept:

# ipfwadm -B -f
# ipfwadm -B -p accept

Now we'll define what we need to block. The protocols you can choose to block are TCP, UDP, and ICMP. We want to allow ICMP messages to the firewall, so we can't just block everything. We could block TCP by adding the rule:

# ipfwadm -B -a deny -P tcp -S 0.0.0.0/0 \
  -D 20.2.51.105

but that would be not be adequate. That rule would end up blocking all traffic from the firewall, as well as traffic to the firewall. Therefore, an administrator could not telnet or ftp from the machine. That may be desirable, but let's assume we'll let out TCP traffic originating from inside. What we would like is to block all connection attempts to the firewall while letting connections go out. We can modify the rule with the -y option. This will do what we want by blocking any TCP segments from any host to the firewall that have the SYN bit set.

# ipfwadm -B -y -a deny -P tcp -S 0.0.0.0/0 \
  -D 20.2.51.105

Remembering that the firewall has two interfaces, we block the second interface also:

# ipfwadm -B -y -a deny -P tcp -S 0.0.0.0/0 \
  -D 192.168.1.1

That is too restrictive, in that connections from the inside network to the firewall will be blocked also. We can refine the rule to block all TCP connection requests only if they come in over the outside interface (20.2.51.105).

# ipfwadm -B -y -a deny -P tcp -S 0.0.0.0/0 \
  -D 20.2.51.105 -I 20.2.51.105
# ipfwadm -B -y -a deny -P tcp -S 0.0.0.0/0 \
  -D 192.168.1.1 -I 20.2.51.105

There, that wasn't too bad.

Now that we've taken care of TCP traffic, let's write some rules for UDP. Since UDP has many problems that we won't discuss in detail here, we'll just block it all. The rules will be the same for TCP, except since there's no SYN bit, there's no need for -y, and so we'll reject the packet instead of denying it. The reason we reject it is that ICMP port unreachable messages make sense to UDP based applications, but are ignored by TCP applications. It would be nice if the ip_fw code sent TCP Resets if the rule was for TCP and marked for rejection, but it doesn't. So our rules for UDP will be:

# ipfwadm -B -a reject -P udp -S 0.0.0.0/0 \
  -D 192.168.1.1 -I 20.2.51.105
# ipfwadm -B -a reject -P udp -S 0.0.0.0/0 \
  -D 20.2.51.105 -I 20.2.51.105

Setting up those rules, we find that blocking all UDP traffic isn't such a good idea after all. If we have telnet, then we will probably want to be able to resolve hostnames. So we open up DNS. Before we do that, though, let's look at the traffic pattern for a DNS query so that we can gauge what we'll need to write. Figure 5 contains tcpdump output of a DNS query from deathstar to mccoy, an “outside” DNS server.

We can see we will have to have two rules—one for the query going out (sending to port 53 on the remote machine) and one for the response (sending to port 53 on the firewall from the remote machine). The rule can be written as:

# ipfwadm -B -a accept -P udp -S 20.2.51.105  \
  -D 0.0.0.0/0 53
# ipfwadm -B -a accept -P udp -S 0.0.0.0/0 53 \
  -D 20.2.51.105

Since this two-way traffic is common among many protocols there's an option you can set to condense the two rules into one. The -b option sets bi-directional mode, which installs a rule that matches traffic in both directions. We can then get away with

# ipfwadm -B -a accept -b -P udp -S 0.0.0.0/0 53 \
  -D 20.2.51.105

Now that we've recreated our table, we can get a listing of the rules with extended (-e) output. This is shown in Figure 6. Notice the extended output contains packet and byte counts for the blocking rules. The firewall code automatically maintains accounting information for the forwarding and blocking rules.

What we have accomplished so far is protecting the machine deathstar to suit our environment. To protect the internal network we will need to develop the proper forwarding rules. Before I just start typing in rulesets, I like to build a table of what's allowed and disallowed. Figure 7 shows the table I put together to establish the blocking rules. Note that the asterisk in the rules table indicates any host address or any port number.

We can build the same type of table for our forwarding ruleset. Building our table should be simple if we have a security policy in place. Let's assume that the part of our security policy that discusses firewalls will allow TELNET and FTP out, and e-mail (SMTP) in both directions. Further, e-mail is only allowed to go to the mailhub (since there's only one machine on the internal network, it will be the mailhub—humor me). Figure 8 shows the generic rules table for the forwarding ruleset.

The stance of the firewall is one of “deny everything”. This is a very common policy for firewalls because the only packets that will be forwarded are the ones which are explicitly allowed.

The rules are fairly straightforward except for the source and destination ports numbered 1024 or greater. The reasons for this are basically historical. Most Unix client programs, such as TELNET, assign their ephemeral port range between 1024 and 5000. Ports 1 through 1023 are known as “reserved ports”. These are for server applications such as telnetd, ftpd, etc. Almost all TCP/IP stacks follow this convention and because of it we can take advantage of it in our ruleset—to help guarantee client-only communications coming into the network. The reason I don't use the range 1024-5000 is because not all devices adhere to this Unix tradition. For example, annex terminal servers start their ephemeral port range at 10000.

Here are the commands to establish our forwarding rules:

# ipfwadm -F -f
# ipfwadm -F -p deny
# ipfwadm -F -a accept -b -P tcp -S 0.0.0.0/0 23 \
  -D 192.168.1.0/24 1024:65535
# ipfwadm -F -a accept -b -P tcp -S 0.0.0.0/0 21 \
  -D 192.168.1.0/24 1024:65535
# ipfwadm -F -a accept -b -P tcp -S >0.0.0.0/0 20 \
  -D 192.168.1.0/24 1024:65535
# ipfwadm -F -a accept -b -P tcp -S >0.0.0.0/0 25 \
  -D 192.168.1.2 1024:65535
# ipfwadm -F -a accept -b -P tcp -S >192.168.1.2 25 \
  -D 0.0.0.0/0 1024:65535

To set up deathstar as the firewall machine, the ipfwadm commands would be put into a file and executed as a shell script. Deathstar uses the file /usr/local/etc/set-rules.sh. To bring the machine up properly, it would be wise to establish the rules within the kernel before the network interfaces are brought up. The /etc/rc.d/rc.inet1 on deathstar contains the lines:

# set firewall rules
/bin/bash /usr/local/etc/set-rules.sh
# bring up ethernet
ifconfig eth0 192.168.1.1 192.168.1.255 up
# bring up ppp link
/usr/lib/ppp/ppp-on

Deathstar is, in reality, my desktop machine. I've loaded just about everything on it, so it doesn't make a very good firewall. A firewall, as we have described it, should run the bare minimum of software in order to function. Normally this means that compilers, X, games, or anything that doesn't have to do with the kernel or communications are taken off of the system.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

code

prat's picture

i m working on firewall on linux platform....could you please send me ypur code for assistance..on
prat.wap@gmail.com

linux firewall code needed.. urgent!

Anonymous's picture

has anyone out there got a complete working code on linux based firewall?? plz help!!

complete firewall code for linux...

Anonymous's picture

Hi... v r dng a project on firewall for linux... can u please give d code of a simple packet filter firewall for linux... my mail id is ramyavastrad@gmail.com

linux firewall

anonymous's picture

hey ,
we r doing project on a network security.. we r going to build a DMZ for a private network. In which we r supposed to build our own firewall on linux. so could u plz guide us regarding how to start with the project.. we would be very thankful to u if u provide us with the complete soursecode ..my id is rati_flyhigh@rediffmail.com

firewall code needed

thomas's picture

hey friends am planning to do a project on linux firewall.but dont hav any idea on how to do it.so pls if any1 has any info about the logic and got the complete code ,pls do mail it to me at so_thoms@yahoo.co.in pls HELP ME..i want it early

Re: Building a Linux firewall

Anonymous's picture

i'm building a linux firewall in bash en a configuration file can somebody help me with a complete code so i can have an idea how i can began,my e-mail is mgend_@hotmail.com

Re: Building a Linux firewall

Anonymous's picture

Can you provide a complete source code

My Mail ID : george_s_kalarikal@yahoo.co.uk

Re: Building a Linux firewall

Anonymous's picture

# cat set_policy.c

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <linux/ip.h>

#include <linux/tcp.h>

#include <linux/udp.h>

#include <linux/ip_fw.h>

main(int argc, char **argv)

{

int p, sfd;

struct ip_fw fw;

fw.fw_flg = 0;

if (strcmp(argv[1], "accept") == 0) {

p = IP_FW_F_ACCEPT;

}

else if (strcmp(argv[1], "reject") == 0) {

p = IP_FW_F_ICMPRPL;

}

else if (strcmp(argv[1], "deny") == 0) {

p = 0;

}

sfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

setsockopt(sfd, IPPROTO_IP, IP_FW_POLICY_FWD,

&p, sizeof(int));

}

Need a bit help

Shobhit Kumar's picture

This is Shobhit here....i m trying to build a kind of firewall in C...actually i m looking for specific URL blocking for a bandwidth manager box....i mean to say a program that can recognize the packet information from the network....and that sort them and then block the required packets.....if u ould help me with this....more over if u could send me the full ode than atleast i will get some idea...thanx

netfilter

Anonymous's picture

Can anyone help me to code a firewall using netfilter which will support multiple rules

can you provide me a complete code?

Anonymous's picture

hi,

i am developing a firewall for my system and i would like to look at you code for that purpose.

i would appreciate if u would email me the same at folowing id: guy_from_rolla@yahoo.com

thanks!

john.

need help

Anonymous's picture

hi,
we have a LAN and a gateway in it. We want to hold all the packets coming into the gateway for compression /modification. we want help for this.
thank you.
dhaubaji@yahoo.com

Give a complete code not just a part of it

Anonymous's picture

Would you please give a complete code not a part.

My email id:- avi_comp@rediffmail.com

Re: Building a Linux firewall

Anonymous's picture

Would you please give a complete code not a part.

My email id:- avi_comp@rediffmail.com

Building a Linux firewall

juno's picture

hi there..we are currently working on a firewall for our thesis..i was hoping if anybody can help me.. can you send me the complete source code..here's my email: juno_king2000@yahho.com..

thank you..

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix