IPTables: Fun with MARK

One thing that’s always bugged me about IPTables is the lack of a way to use groups when writing rules, which can complicate things if you’ve got a potentially large rulebase. One way round this is to use something like fwbuilder, which gives you a graphical interface not unlike Checkpoint‘s SmartDashboard GUI for their Firewall-1 devices. The downside to this, though, is that the resulting IPTables ruleset is far from legible – which, to be fair, isn’t the goal of fwbuilder – and this makes hacking about with the rules nearly impossible.

So what options are there? One way is to repeat the same rule for different sources or destinations, but this can quickly get messy, especially if there’s multiple ports involved. If there was a way we could group things together and keep them tidy, maintaining the rulebase would be a lot easier. This is where MARK comes in.

The MARK target lets us set a 32-bit value (or 0xFFFFFFFF) on a packet, which we can then look for later with the mark match. This in itself can be useful, but where it gets really handy is adding the values together.

Starting out

To start off with, here’s an example

# Create a new chain, which will in effect be our source 'group'
iptables -N S-TRUSTED
# Add our sources
iptables -A S-TRUSTED -s -j MARK --set-xmark 0x8/0x0
iptables -A S-TRUSTED -s -j MARK --set-xmark 0x8/0x0
iptables -A S-TRUSTED -s -j MARK --set-xmark 0x8/0x0

# Create a chain for the destination group
iptables -N D-DMZ
# Add our DMZ machines
iptables -A D-DMZ -d -j MARK --set-xmark 0x4/0x0
iptables -A D-DMZ -d -j MARK --set-xmark 0x4/0x0

# Create a chain for the services
iptables -N D-SRV-MGMT
# Add our service
iptables -A D-SRV-MGMT -p tcp -m tcp --dport 22 -j MARK --set-xmark 0x2/0x0
iptables -A D-SRV-MGMT -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x2/0x0

So what we have now are three chains, which do the following

  • If the source matches, add 0x8 to the packet mark
  • If the destination matches, add 0x4 to the packet mark
  • If the service matches, add 0x2 to the packet mark

If you know your hexadecimal, you’ll already know that if all of these are true, we’ll come out with 0xE (or 14, in decimal).

Making the rule

Hopefully you’ll see where we’re going with this with the next example, which is our actual rule

# Create a new chain for our rule and add it to our FORWARD chain
iptables -N R-ALLOW-DMZ-MGMT
# Zero out the packet mark to make sure no previous rules interfere
iptables -A R-ALLOW-DMZ-MGMT -j MARK --set-xmark 0x0/0x0
# Jump to our 'source group' chain
# Jump to our 'destination group' chain
iptables -A R-ALLOW-DMZ-MGMT -j D-DMZ
# Jump to our 'service group' chain
# If the packet mark matches 0xE, then ACCEPT
iptables -A R-ALLOW-DMZ-MGMT -m mark --mark 0xE -j ACCEPT

And there we have it – if the packet matches the source, destination and service, the packet mark will be 0xE. If, say, it matches everything except the destination, it’ll come out as 0xC, which won’t match and so netfilter will carry on along the rest of the rules. If you want processing to stop here, you could always add a LOG and REJECT/DROP target at the end of the R-ALLOW-DMZ-MGMT chain.


Sometimes we want to be able to say ‘everything but that particular network’, whether it be for accepting or dropping packets. We can do that with this, too

# Add a new chain for negating the source
iptables -N S-NEGATE
# XOR the current packet mark with 0x8 - our 'source match' identifier
iptables -A S-NEGATE -j MARK --xor-mark 0x8

To use it, simply drop it in the R-ALLOW-DMZ-MGMT rule above after the jump to the S-TRUSTED chain, and if S-TRUSTED matched, it won’t any more, and vice versa. To negate the destination and service matches, you’ll need to create similar chains for (for example) D-NEGATE and D-SRV-NEGATE, replacing the 0x8 with 0x4 and 0x2 respectively.

Things to note

One downside of this method is that because of the way IPTables works, if you want to use the same set of networks and hosts as a source and a destination, you’ll need to duplicate them, but match on the source or destination as appropriate. Using the example given above, if we wanted a group with the same entries as S-TRUSTED, but matching on traffic going to them, we’d need to create another group (for example, D-TRUSTED), which will be identical save for the IP matches (which will need changing to -d) and the mask (which will need setting to 0x4 instead of 0x8).

Also, be careful if you’re using packet marks to do something outside of netfilter (say, for traffic control – which I’ll cover in a future post). One way round this is the facility to save the current packet mark to the current connection mark, or vice versa – if you go down this path then having a look at the iptables manpage for the MARK and CONNMARK targets will be useful.


This is an effective way of grouping hosts, networks and services within IPTables. It can be quite a bit of work to start with to add all the groups, but once in place it makes writing rules a lot more logical.

Taking things further

One way which you could take this further would be to group interfaces together in a similar fashion, say by adding 0x10 to the packet, and then matching on 0x1E rather than 0xE.

Usefully, if you send the packet out to syslog with the LOG target, netfilter will print out the current packet mark at the end of the log message as MARK=0xN, which can be useful when debugging.

7 comments to IPTables: Fun with MARK

  • avatar Sehcel

    Hi. Have you ever heard about ipsets?

  • avatar Carl


    I’m learning/researching IPtables and a little confused. I have found documentation on the option –set-mark, but nothing on –set-xmark as used in your example here.

    I have seen examples like –set-mark 12/8 where the 1100 is masked by 1000 to result in 1000, but your example seems to be different.

    As best I can figure out, if both examples are correct, –set-mark ANDs with the mask and –set-xmark XORs with the mask – is that what is happening here?

    Thanks -

    • Hi Carl,

      Just had a quick look and –set-xmark is mentioned in the iptables manpage – it states that it zeroes out the bits given by the mask and XORs in the value given.


      • avatar hicham

        Hi Andy,

        There exist an arithmetic operation in the mangle target which let us add a new nb on an exist one. e.g:
        rule 1: …–dport 25 … -j MARK –set-mark 1
        rule 2: … –dport :1024… -j MARK –set-mark mark+1

        this is a conflict scenario which I try to do for my Adv Network class


  • […] to this:…fun-with-mark/ It says: "The MARK target lets us set a 32-bit value (or 0xFFFFFFFF) on a packet, which we […]

  • Hai,

    i want to limit bandwidth and route to 3 gateway, my rules show like this:
    ip rule add fwmark 11 table $TABLE_1
    ip rule add fwmark 12 table $TABLE_2
    ip rule add fwmark 14 table $TABLE_3

    iptables -t mangle -A PREROUTING -m state –state new -j MARK –set-xmark 0x1
    iptables -t mangle -A PREROUTING -m state –state new -m statistic –mode random –probability 0.3 -j MARK –set-xmark 0x2
    iptables -t mangle -A PREROUTING -m state –state new -m statistic –mode random –probability 0.3 -j MARK –set-xmark 0x4

    for tc:

    iptables -t mangle -A FORWARD -o eth0 -p tcp –sport 80 -j MARK –set-xmark 10
    iptables -t mangle -A FORWARD -o eth0 -p udp –sport 80 -j MARK –set-xmark 10
    iptables -t mangle -A PREROUTING -p tcp –dport 80 -j MARK –set-xmark 10

    it’s that mean if packet goes to port 80 and table1 will marked 11? If I have a lot of port marking rule, do i have to write complex ip rule?

    Do you have something more simple? or it really have to be complicated like that?

Leave a Reply




You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>