Bandwidth Management

One common task for a network perimeter device is bandwidth management. Network managers must control how much bandwidth is used for certain tasks, and must also reserve bandwidth for vital functions. If one of your minions loads the latest blockbuster comic book movie on the web server, you must be able to make an SSH connection to the server, find out why your server is overloaded, and fix the problem. PF includes the ALTQ bandwidth management system.

The most important thing to remember about bandwidth management is that you cannot control how much traffic other people send you. You can stop traffic at the point it enters your network. You can send hints that the bandwidth is saturated. You can arbitrarily restrict bandwidth from your servers. But nothing you do can stop 10,000 people a second from clicking a link to that server. You cannot prevent a distributed denial-of-service attack from saturating your inbound bandwidth. The best you can do is control how you respond to those requests.

When I run content farms, I usually put dedicated bandwidth control machines in front of my servers. This setup controls how much traffic actually reaches my server network, reduces load on the servers in case of a sudden spike, and prevents one overly busy customer from taking down other customers on the same server.

Queues for Bandwidth Management

ALTQ manages bandwidth by queues. A queue is a list of packets waiting to be processed.

By dividing your bandwidth into separate queues, and processing those queues as you configure, you can manage server bandwidth. Queues are somewhat like the checkout lines at the grocery store; some lines are for 10 packets or less and get you out quickly, and others are for people who shop once a month and fill up three carts. You can define just about any characteristics for queues, as if you could create a “meats only” or “white wine with fish” register.

Engineers have defined many different queuing algorithms, and the most proper queue method for a given situation is a topic that sparks heated discussions. TCP/IP quality-of-service queue handling is one of those topics that make angelic children cry. By default, all BSD-based systems use first-in, first-out (FIFO) queuing, where packets are processed in the order in which they are received. Newer packets wait in a queue until older packets move on.

OpenBSD also supports priority queuing (PRIQ or prio), where the kernel considers packets of certain types to have “priority” and processes them first. This means that if you assign web packets highest priority, all web packets jump to the head of the queue. Packets of lower priority might never be processed at all under this scheme. These days, just about everything supports priority queuing, especially switches. The goal of priority queuing is to reduce latency for specific traffic, such as voice or video, paying for that reduced latency by increasing the latency of less urgent traffic.

However, in most operational settings where you must regulate bandwidth, class-based queuing (CBQ) is appropriate. CBQ allows the network administrator to allocate a certain amount of bandwidth to different types of traffic through hierarchical classes. Each class has its own queue, with its own bandwidth characteristics. You can assign different sorts of traffic to different classes: SSH to one class, HTTP and HTTPS to another, and so on. One of the nice features of CBQ is that its hierarchical nature allows lower classes to borrow available bandwidth from classes above them.

As I find CBQ appropriate for most environments, I focus on it here. Once you master CBQ, if you need PRIQ, you’ll find it easy to understand.

Parent Queue Definitions

Queuing starts with defining the parent queue. All other queues are children of the parent queue. The parent queue is attached to a network interface, most commonly the Internet-facing interface. Place your queue definitions in pf.conf. I put queues at the top of the file, before any packet-filtering rules.

Here’s how you define a parent queue on an interface:

1altq on 2
interface 3cbq bandwidth 4
bw qlimit 5
qlim tbrsize 6
size 7queue { 8
queue1, 9
queue2}

Start all ALTQ parent queue definitions with the altq keyword 1, and then give the interface to which this queue is attached 2. (Each interface can have no more than one parent queue.) Then give the queue type you’re using 3. For CBQ queuing, the queue type is always cbq.

Now define the total amount of bandwidth in the parent queue 4. This is not the same as the amount of bandwidth the interface can pass, but the amount of bandwidth you reasonably expect to pass upstream. If your OpenBSD machine has a gigabit network card, but you have only 10 megabits of bandwidth to the Internet, use 10Mb as your bandwidth (or fiddle with the bandwidth value until you hit your actually usable allocation). You can use the following case-sensitive abbreviations for bandwidth:

  • b . bits per second

  • Kb . kilobits per second

  • Mb . megabits per second

  • Gb . gigabits per second

The optional qlimit parameter gives the number of packets the queue can hold 5. The default value is 50, which suffices for almost all cases. I recommend not setting qlimit unless specific debugging shows that you need a larger queue size.

This example includes the token bucket regulator size configuration because tbrsize lets you dictate how quickly packets can be transmitted 6. ALTQ defaults to transmitting packets as fast as the wire permits. As with qlimit, I recommend not setting tbrsize unless you encounter a problem.

Next, identify this as a parent queue 7, and define child queues queue1 8 and queue2 9.

Here’s how to configure a parent queue with a 50-megabit uplink, with the child queues ssh, web, and mgmt:

altq on em0 bandwidth 50Mb queue {ssh, web, mgmt}

The tbrsize and qlim keywords are not set, so they’re at their defaults.

Child Queue Definitions

Once you have a parent queue, you can define child queues. Define CBQ queues with the following syntax:

queue 1
name on 2
interface bandwidth 3
bw [priority 4pri] [qlimit 5
qlim] cbq 6(options) 7{child_queues}

Each queue needs a name 1, defined in the parent queue definition, of 15 characters or less. The names don’t need to be unique—you could use a queue of the same name on a different interface—but I recommend that you use unique names.

The interface is the specific interface to which this queue is applied 2. If you don’t define an interface, traffic that passes through any interface can be assigned to this queue.

The bandwidth term uses the same bandwidth labels that the parent queue uses, but the total bandwidth assigned to all child queues cannot exceed the total amount of bandwidth available on the parent queue 3. You can also use a percentage value for bandwidth, indicating the percentage of the parent queue that this queue can consume. Bandwidth and queue are the only mandatory terms in a child queue description.

The following defines the ssh child queue and gives it a bandwidth of 2 megabits:

queue ssh bandwidth 2Mb

Here’s a child queue called web, which is allowed to use three-quarters of the parent queue bandwidth:

queue web bandwidth 75%

You can assign a priority to a queue 4. CBQ priorities run from 0 to 7, with 7 being the highest. The default priority is 1. A CBQ queue with a higher priority does not run to the exclusion of other queues, but PF processes it more quickly than other queues.

As with a parent queue, you can assign a qlimit to a child queue 5, but don’t do this unless you have a specific problem that can be solved with this value.

You can assign options to a CBQ child queue 6. We’ll look at these options in the next section.

Finally, child queues can have their own children. Define a queue’s children in the queue 7. You’ll see an example of this in A CBQ Ruleset.

Queue Options

Modify how a child queue processes packets by assigning options to a queue. Options let you decide how the queue should respond to a variety of network conditions and bandwidth availability.

Default

Every parent queue must have one and only one default child. If a packet crossing a queued interface is assigned to no other queue, it is assigned to the default queue.

Random Early Detection

Random early detection (RED) is a method for handling packet loss when a queue starts to fill up. As the queue fills up, more and more packets are dropped. RED randomly chooses packets to drop. The net effect is that short transfers, such as HTTP requests and interactive SSH sessions, respond more quickly, while large data transfers become slower.

TCP clients and servers react to dropped packets by reducing their throughput. UDP, ICMP, and other protocols don’t have any built-in reaction to packet loss. Using RED on queues expected to carry TCP is sensible, but not on queues for other protocols.

Explicit Congestion Notification

Explicit Congestion Notification (ECN) is a modification to RED that sets flags in the packet rather than dropping the packet. If a device recognizes the ECN flag, it will reduce transmission rates.

Not all platforms understand ECN, however, and many that can recognize ECN disable it by default. Microsoft’s Windows Vista and newer, Apple OS X, FreeBSD, and OpenBSD can support ECN, but disable it by default. Newer Linux versions support ECN if the other host requests it. I have successfully used ECN, in corporate environments where I could make the support guys enable ECN on the desktops.

Unless you know the operating systems in use and can control their settings, stick with standard RED.

borrow

The borrow option is available only in CBQ. A queue with borrow set may borrow bandwidth from its parent queue, if the bandwidth is available. For example, you might have a queue that reserves 20 percent of your bandwidth for VoIP. If you don’t have that much VoIP traffic at any particular moment, the parent will have excess bandwidth. Other queues could borrow bandwidth from that allocation. When your VoIP traffic spikes, however, PF revokes the bandwidth loan, and the VoIP traffic gets what’s reserved for it.

Use the borrow option on the queues that you want to permit to borrow bandwidth, not on the queues whose bandwidth might be borrowed.

A CBQ Ruleset

Before configuring queues, figure out how you want to divide your bandwidth. While you could use bits per second to manage bandwidth, for most of us, percentages are easier to deal with. Here’s how you might divide Internet bandwidth for a company with a 10-megabit link. Start by making a list of your desired bandwidth reservations, and then assign a name to each category, like this:

  • 5 percent for SSH (ssh)

  • 50 percent for inbound traffic to our e-commerce server, with RED (web)

  • 5 percent for inbound VoIP, high priority (voip)

  • 40 percent for other traffic, including DNS, SMTP, and so on

All of these queues can borrow from the parent queue.

Start by defining the parent queue.

altq on em0 cbq bandwidth 10Mb queue {ssh, web, voip, other}

This parent queue is attached to interface em0, and has 10 megabits of bandwidth and four child queues. Leave all the other options alone.

Now define the first child queue.

queue ssh bandwidth 5% cbq (borrow)

Start with the queue name and the bandwidth percentage you’ve chosen. This percentage is calculated from the parent of this particular queue, so it’s about 5 percent of 10 megabits, or 500 kilobits per second. That should be plenty to log in remotely and fix any problems. Adding the borrow option lets you use more bandwidth for SSH if it’s available.

Building from this example, you can define the other child queues.

queue web bandwidth 50% cbq (borrow, red)
queue voip bandwidth 5% cbq (borrow)
queue other bandwidth 5% cbq (borrow, default)

The other queue is your default. Any traffic that isn’t assigned its own queue is assigned to this queue.

Assigning Traffic to Queues

Assign traffic to a queue with the queue keyword at the end of a packet-filtering rule. To allow all SSH (port 22) traffic into the network and assign it to the queue named ssh, use a rule like this:

pass in on egress proto tcp from any to lan:network port 22 queue ssh

Using the match Keyword

Sometimes you must classify traffic without filtering it. The previous example let you assign inbound SSH traffic to the ssh queue, but what if you want to capture outbound SSH as well? Consider the following rule snippet:

pass in on egress proto tcp from <customers> to <sshservers> port 22
pass out on egress from lan:network to any

This allows hosts in the customers table to connect to hosts in the sshservers table on port 22. The second rule allows the local network to send any traffic, or any protocol. Some of that outbound traffic will be SSH traffic. Should you write a separate rule just for queuing traffic?

This is where the match keyword comes in. Using match, you can change how PF classifies traffic without changing how it filters traffic. Here’s how to send all TCP port 22 traffic to the ssh queue, without changing any filtering characteristics:

match proto tcp from any to any port 22 queue ssh
pass in on egress proto tcp from <customers> to <sshservers> port 22
pass out on egress from lan:network to any

The first rule matches all traffic on TCP port 22 and assigns it to the ssh queue. The rules that follow control who can send and receive SSH connections.

Viewing Queues

To view the queues currently in the packet filter, run pfctl -s queues.

# pfctl -sq
queue root_em0 on em0 bandwidth 10Mb priority 0 cbq( wrr root ) {ssh, web, voip, other}
queue  ssh on em0 bandwidth 500Kb cbq( borrow )
queue  web on em0 bandwidth 5Mb cbq( red borrow )
queue  voip on em0 bandwidth 500Kb priority 7 cbq( borrow )
queue  other on em0 bandwidth 500Kb cbq( borrow default )

Adding -v gives you a brief snapshot of the state of each queue. For a constantly updating view of all queues, including how much traffic is borrowed from each, what gets dropped, and so on, use -vvsq or systat queues instead.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.130.199