<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<article>
  <!--$Id$-->

  <articleinfo>
    <title>Simple Traffic Shaping/Control</title>

    <authorgroup>
      <author>
        <firstname>Tom</firstname>

        <surname>Eastep</surname>
      </author>
    </authorgroup>

    <pubdate><?dbtimestamp format="Y/m/d"?></pubdate>

    <copyright>
      <year>2009</year>

      <year>2010</year>

      <holder>Thomas M. Eastep</holder>
    </copyright>

    <legalnotice>
      <para>Permission is granted to copy, distribute and/or modify this
      document under the terms of the GNU Free Documentation License, Version
      1.2 or any later version published by the Free Software Foundation; with
      no Invariant Sections, with no Front-Cover, and with no Back-Cover
      Texts. A copy of the license is included in the section entitled
      <quote><ulink url="GnuCopyright.htm">GNU Free Documentation
      License</ulink></quote>.</para>
    </legalnotice>
  </articleinfo>

  <section>
    <title>Introduction</title>

    <para>Traffic shaping and control was originally introduced into Shorewall
    in version 2.2.5. That facility was based on Arne Bernin's
    <firstterm>tc4shorewall</firstterm> and is generally felt to be complex
    and difficult to use.</para>

    <para>In Shorewall 4.4.6, a second traffic shaping facility that is simple
    to understand and to configure was introduced. This newer facility is
    described in this document while the original facility is documented in
    <ulink url="traffic_shaping.htm">Complex Traffic
    Shaping/Control</ulink>.</para>

    <para>In the absense of any traffic shaping, interfaces are configured
    automatically with the pfifo_fast <firstterm>queuing
    discipline</firstterm> (qdisc). From tc-pfifo_fast (8):</para>

    <blockquote>
      <para>The algorithm is very similar to that of the classful tc-prio(8)
      qdisc. pfifo_fast is like three tc-pfifo(8) queues side by side, where
      packets can be enqueued in any of the three bands based on their Type of
      Service bits or assigned priority.</para>

      <para>Not all three bands are dequeued simultaneously - as long as lower
      bands have traffic, higher bands are never dequeued. This can be used to
      prioritize interactive traffic or penalize ’lowest cost’ traffic.</para>

      <para>Each band can be txqueuelen packets long, as configured with
      ifconfig(8) or ip(8). Additional packets coming in are not enqueued but
      are instead dropped.</para>

      <para>See tc-prio(8) for complete details on how TOS bits are translated
      into bands.</para>
    </blockquote>

    <para>In other words, if all you want is strict priority queuing, then do
    nothing.</para>

    <para>Shorewall's Simple Traffic Shaping configures the prio
    qdisc(rx-prio(8)) on the designated interface then adds a
    <firstterm>Stochastic Fair Queuing</firstterm> sfq (tc-sfq (8)) qdisc to
    each of the classes that are implicitly created for the prio qdisc. The
    sfq qdisc ensures fairness among packets queued in each of the classes
    such that each <firstterm>flow</firstterm> (session) gets its turn to send
    packets. The definition of flows can be altered to include all traffic
    being sent <emphasis>by</emphasis> a given IP address (normally defined
    for an external interface) or all traffic being sent
    <emphasis>to</emphasis> a given IP address (internal interface).</para>

    <para>Finally, Simple Traffic Shaping allows you to set a limit on the
    total bandwidth allowed out of an interface. It does this by inserting a
    Token Bucket Filter (tbf) qdisc ahead of the prio qdisc. Note that this
    can have the effect of defeating the priority queuing provided by the prio
    qdisc but seems to provide a benefit when the actual link output
    temporarily drops below the limit imposed by tbf or when tbf allows a
    burst of traffic to be released.</para>
  </section>

  <section>
    <title>Enabling Simple Traffic Shaping</title>

    <para>Simple traffic shaping is enabled by setting TC_ENABLED=Simple in
    <ulink url="manpages/shorewall.conf.html">shorewall.conf</ulink>(5). You
    then add an entry for your external interface to <ulink
    url="manpages/shorewall-tcinterfaces.html">shorewall-tcinterfaces</ulink>(5)
    (<filename>/etc/shorewall/tcinterfaces</filename>).</para>

    <para>Assuming that your external interface is eth0:</para>

    <programlisting>#INTERFACE             TYPE          IN-BANDWIDTH        OUT-BANDWIDTH
eth0                   External</programlisting>

    <note>
      <para>If you experience an error such as the following during
      <command>shorewall start</command> or <command>shorewall
      restart</command>, your kernel and iproute do not support the <emphasis
      role="bold">flow</emphasis> classifier. In that case, you must leave the
      TYPE column empty (or specify '-').</para>

      <programlisting>Unknown filter "flow", hence option "hash" is unparsable
   ERROR: Command "tc filter add dev eth0 protocol all prio 1 parent 11: handle 11 flow hash keys nfct-src divisor 1024" Failed</programlisting>

      <para>RHEL5-based systems such as <trademark>CentOS</trademark> 5 and
      <trademark>Foobar</trademark> 5 are known to experience this
      error.</para>

      <para><emphasis role="bold">Update</emphasis>: Beginning with Shorewall
      4.4.7, Shorewall can determine that some environments, such as RHEL5 and
      derivatives, are incapable of using the TYPE parameter and simply ignore
      it.</para>
    </note>

    <para>With this simple configuration, packets to be sent through interface
    eth0 will be assigned to a priority band based on the value of their TOS
    field:</para>

    <programlisting>TOS     Bits  Means                    Linux Priority    BAND
------------------------------------------------------------
0x0     0     Normal Service           0 Best Effort     2
0x2     1     Minimize Monetary Cost   1 Filler          3
0x4     2     Maximize Reliability     0 Best Effort     2
0x6     3     mmc+mr                   0 Best Effort     2
0x8     4     Maximize Throughput      2 Bulk            3
0xa     5     mmc+mt                   2 Bulk            3
0xc     6     mr+mt                    2 Bulk            3
0xe     7     mmc+mr+mt                2 Bulk            3
0x10    8     Minimize Delay           6 Interactive     1
0x12    9     mmc+md                   6 Interactive     1
0x14    10    mr+md                    6 Interactive     1
0x16    11    mmc+mr+md                6 Interactive     1
0x18    12    mt+md                    4 Int. Bulk       2
0x1a    13    mmc+mt+md                4 Int. Bulk       2
0x1c    14    mr+mt+md                 4 Int. Bulk       2
0x1e    15    mmc+mr+mt+md             4 Int. Bulk       2</programlisting>

    <para>When dequeueing, band 1 is tried first and only if it did not
    deliver a packet does the system try band 2, and so onwards. Maximum
    reliability packets should therefore go to band 1, minimum delay to band 2
    and the rest to band 3.</para>

    <note>
      <para>If you run both an IPv4 and an IPv6 firewall on your system, you
      should define each interface in only one of the two
      configurations.</para>
    </note>
  </section>

  <section>
    <title>Customizing Simple Traffic Shaping</title>

    <para>The default mapping of TOS to bands can be changed using the
    TC_PRIOMAP setting in <ulink
    url="manpages/shorewall.conf.html">shorewall.conf</ulink>(5). The default
    setting of this option is:</para>

    <programlisting>TC_PRIOMAP="2 3 3 3 2 3 1 1 2 2 2 2 2 2 2 2"</programlisting>

    <para>These entries map Linux Priority to priority BAND. So only entries
    0, 1, 2, 4 and 6 in the map are relevant to TOS-&gt;BAND mapping.</para>

    <para>Further customizations can be defined in <ulink
    url="manpages/shorewall-tcpri.html">shorewall-tcpri</ulink>(5)
    (<filename>/etc/shorewall/tcpri</filename>). Using that file, you
    can:</para>

    <orderedlist>
      <listitem>
        <para>Assign traffic entering the firewall on a particular interface
        to a specific priority band:</para>

        <programlisting>#BAND         PROTO         PORT(S)         ADDRESS             INTERFACE        HELPER
2               -             -                -                eth1</programlisting>

        <para>In this example, traffic from eth1 will be assigned to priority
        band 2.</para>

        <note>
          <para>When an INTERFACE is specified, the PROTO, PORT(S) and ADDRESS
          column must contain '-'.</para>
        </note>
      </listitem>

      <listitem>
        <para>Assign traffic from a particular IP address to a specific
        priority band:</para>

        <programlisting>#BAND         PROTO         PORT(S)         ADDRESS             INTERFACE        HELPER
1               -             -             192.168.1.44</programlisting>

        <para>In this example, traffic from 192.168.1.44 will be assigned to
        priority band 1.</para>

        <note>
          <para>When an ADDRESS is specified, the PROTO, PORT(S) and INTERFACE
          columns must be empty.</para>
        </note>
      </listitem>

      <listitem>
        <para>Assign traffic to/from a particular application to a specific
        priority band:</para>

        <programlisting>#BAND         PROTO         PORT(S)         ADDRESS             INTERFACE        HELPER
1             udp           1194</programlisting>

        <para>In that example, OpenVPN traffic is assigned to priority band
        1.</para>
      </listitem>

      <listitem>
        <para>Assign traffic that uses a particular Netfilter helper to a
        particular priority band:</para>

        <programlisting>#BAND         PROTO         PORT(S)         ADDRESS             INTERFACE        HELPER
1               -             -             -                   -                sip</programlisting>

        <para>In this example, SIP and associated RTP traffic will be assigned
        to priority band 1 (assuming that the nf_conntrack_sip helper is
        loaded).</para>
      </listitem>
    </orderedlist>

    <para>It is suggested that entries specifying an INTERFACE be placed at
    the top of the file. That way, the band assigned to a particular packet
    will be the <emphasis role="bold">last</emphasis> entry matched by the
    packet. Packets which match no entry in <ulink
    url="manpages/shorewall-tcpri.html">shorewall-tcpri</ulink>(5) are
    assigned to priority bands using their TOS field as previously
    described.</para>

    <para>One cause of high latency on interactive traffic can be that queues
    are building up at your ISP's gateway router. If you suspect that is
    happening in your case, you can try to eliminate the problem by using the
    IN-BANDWIDTH setting in <ulink
    url="manpages/shorewall-tcinterfaces.html">shorewall-tcinterfaces</ulink>(5).
    The contents of the column are a <replaceable>rate</replaceable>. For
    defining the rate, use <emphasis role="bold">kbit</emphasis> or <emphasis
    role="bold">kbps</emphasis> (for Kilobytes per second) and make sure there
    is NO space between the number and the unit (it is 100kbit not 100 kbit).
    <emphasis role="bold">mbit</emphasis>, <emphasis
    role="bold">mbps</emphasis> or a raw number (which means bytes) can be
    used, but note that before Shorewall 4.4.13 only integer numbers were
    supported (0.5 was not valid). To pick an appropriate setting, we
    recommend that you start by setting IN-BANDWIDTH significantly below your
    measured download bandwidth (20% or so). While downloading, measure the
    ping response time from the firewall to the upstream router as you
    gradually increase the setting. The optimal setting is at the point beyond
    which the ping time increases sharply as you increase the setting.</para>

    <para>Simple Traffic Shaping is only appropriate on interfaces where
    output queuing occurs. As a consequence, you usually only use it on
    extermal interfaces. There are cases where you may need to use it on an
    internal interface (a VPN interface, for example). If so, just add an
    entry to <ulink
    url="manpages/shorewall-tcinterfaces.html">shorewall-tcinterfaces</ulink>(5):</para>

    <programlisting>#INTERFACE             TYPE          IN-BANDWIDTH
tun0                   Internal</programlisting>

    <para>For fast lines, the actual download rate may be significantly less
    than the specified IN-BANDWIDTH. Beginning with Shoreall 4.4.13, you can
    specify an optional burst</para>

    <para>Also beginning with Shorewall 4.4.13, an OUT-BANDWIDTH column is
    available in <ulink
    url="manpages/shorewall-tcpri.html">shorewall-tcpri</ulink>(5). Limiting
    to outgoing bandwidth can have a positive effect on latency for
    applications like VOIP. We recommend that you begin with a setting that is
    at least 20% less than your measured upload rate and then gradually
    increase it until latency becomes unacceptable. Then reduce it back to the
    point where latency is acceptable.</para>
  </section>

  <section>
    <title>Combined IPv4/IPv6 Simple TC Configuration</title>

    <para>Beginning with Shorewall 4.4.19, a combined configuration is
    possible. To do that:</para>

    <itemizedlist>
      <listitem>
        <para>Set TC_ENABLED=Simple in both
        <filename>/etc/shorewall/shorewall.conf</filename> and
        <filename>/etc/shorewall6/shorewall6.conf</filename>.</para>
      </listitem>

      <listitem>
        <para>Configure your interface(s) in
        <filename>/etc/shorewall/tcinterfaces</filename>.</para>
      </listitem>

      <listitem>
        <para>Add entries to <filename>/etc/shorewall/tcpri</filename> and
        <filename>/etc/shorewall6/tcpri</filename> as desired. Entries in the
        former classify IPv4 traffic and entries in the latter classify IPv6
        traffic.</para>
      </listitem>
    </itemizedlist>

    <para>Example:</para>

    <para><filename>/etc/shorewall/tcinterfaces</filename><programlisting>#INTERFACE	TYPE		IN-BANDWIDTH			OUT-BANDWIDTH
eth0		External	50mbit:200kb			6.0mbit:100kb:200ms:100mbit:1516   
</programlisting>etc/shorewall/tcpri:</para>

    <programlisting>#BAND	PROTO		PORT(S)		ADDRESS		INTERFACE	HELPER
COMMENT	 All DMZ traffic in band 3 by default
3	-	 	-		70.90.191.124/31
COMMENT Bit Torrent is in band 3
3	ipp2p:all	bit
COMMENT But give a boost to DNS queries
2	udp	   	53
COMMENT And place echo requests in band 1 to avoid false line-down reports
1	icmp	        8
</programlisting>

    <para>etc/shorewall6/tcpri:</para>

    <programlisting>#BAND	PROTO		PORT(S)		ADDRESS		INTERFACE	HELPER
COMMENT	 All DMZ traffic in band 3 by default
3	-	 	-		2001:470:b:227::40/124
COMMENT But give a boost to DNS queries
2	udp	   	53
COMMENT And place echo requests in band 1 to avoid false line-down reports
1	icmp	        8
</programlisting>
  </section>

  <section>
    <title>Additional Reading</title>

    <para>The PRIO(8) (tc-prio) manpage has additional information on the
    facility that Shorewall Simple Traffic Shaping is based on.</para>

    <caution>
      <para>Please note that Shorewall numbers the bands 1-3 whereas PRIO(8)
      refers to them as bands 0-2.</para>
    </caution>
  </section>
</article>
