PfSense supports only outbound traffic shapping so you can’t shape multiple LAN/VLAN interfaces without putting another PfSense box in front of it. The only way to shape it is to use only one physical interface LAN and tag other VLANS on that interface. You need to select only WAN and LAN interface for traffic shaping. All traffic that will pass from VLANs will go trough LAN interface where QOS traffic shaper will catch it. If you don’t do it via only one interface, traffic shaping will work, but the VLAN to VLAN traffic will be limited to the speed of a WAN download bandwidth.

For start, you can use traffic shaping wizard and modify rules after.

Go to:

Firewall -> Traffic Shaper -> Wizards -> Multiple LAN/WAN

Select one WAN connection and one LAN connection:

For interface select LAN and WAN, scheduler should be HFSC (you can choose another one if you like, but this post is about HFSC setup).

Define your WAN upload and download speed and continue wizzard till the end and save.

Go to:

Firewall -> Traffic shaper

Click on LAN and set bandwidth to your physical interface speed.

Set qLink bandwidth percentage to: ((LAN bandwidth – WAN download bandwidth) / 10)


My LAN bandwidth = 1000 Mbit

My WAN download banwidth = 200 Mbit

(1000 – 200) / 10 = 80%

The sum of parent trees has to be 100%


All you have to do now is add two more floating rules. Rules added by the wizzard are good enough to get an idea how it works. You can later add custom ports, depends on what you need.

Go to:

Firewall -> Rules -> Floating

We will add a rule to catch all traffic that does not fall under defined floating rules created by the wizzard. We will put all not defined traffic to qOtherLow queue. The important thing is to have rules added at the top of the floating rules and not at the bottom.


Add rule 1:

Match, interface: WAN, direction: any, protocol: TCP, source: any, destination: any, destination port range: from any to any

Advanced options: Ackqueue / Queue: qACK / qOtherLow


Add rule 2: 

Match, interface: WAN, direction: any, protocol: UDP, source: any, destination: any, destination port range: from any to any

Advanced options: Ackqueue / Queue: none / qOtherLow

The two created rules have to be at the top:

Basic traffic shaping should work now. It’s up to you know to fine tune the rules. Check the status of traffic shaper at Status -> Queues

qLink queue is VLAN <-> VLAN traffic while all the queues bellow +/-qInternet are VLAN <-> WAN traffic

Downsides of this setup:

  • You are limited to only one physical interface for VLAN traffic meaning your VLAN to VLAN bandwidth can suffer with multiple heavy users on a local network (like transferring a lot of files from local servers to local clients). You could probably solve that with LAN bridges but I don’t know how a QOS would behave in that case.
  • You can’t run squid proxy service because download traffic on port 80 and 443 will bypass traffic shaper (it can probably be done with some tweaking but I haven’t tested it yet).

This is useful when you can’t use peer to peer (site to site) tunnel. For example, when you don’t have administrative access to a remote network (you can’t open ports, you can only go out – egress). To bypass this and gain access to remote network devices, you can simply install a VPN client on the remote network and make it act as a gateway for your local network. I will not go trough basic OpenVPN server configuration (generating certs, adding users etc), I will only pinpoint the parts that differ from a normal VPN client server setup.


Remote network: (Client side)

Local network: (Server side)

1. Go to OpenVPN server settings, under advanced configuration, custom options and enter:

 push "route";


2. Go to OpenVPN client specific overrides tab and add a new rule. Select your OpenVPN server, enter common name (name of the user – VPN client), under IPv4 remote network/s enter:

3. If you haven’t already, you have to assign an interface to your VPN server. Go to Interfaces, Assignments, Available network ports: ovpns1, click Add and save. Click on your newly created interface, check box Enable interface and add a description: OpenVPN1 (name it however you want), save.

4. Go to System, routing, static routes.

Add a new route, destination network:

Gateway: OpenVPN1

5. You need to enable NAT and forwarding on a client, this example is for a linux client:

 sysctl -w net.ipv4.ip_forward=1

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE 

6. Connect your VPN client, you should be able to access devices behind the client from your local network.

To connect via RTSP:




If you are filtering outgoing connections, allow this outgoing ports to camera:

554 TCP

6970-6990 UDP

You can test RTSP stream with VLC media player.

Possible bug: When you connect to the camera with your phone, disconnect from it and then try to open rtsp stream in VLC media player, it will only load the first frame and stuck there. You need to reboot the camera, connect to rtsp with VLC media player without accessing it via mobile device.

Extra note: It’s advisable to block outgoing traffic from camera into internet. My camera is connecting to some IP with weird ports.

Quick whois showed me this IP belongs to I did not sniff the traffic, so I’m not sure if the nature of it is spyware. It’s probably their cloud service or some other service.

For the extra paranoid, physically disconnect the microphone on the camera:


Xerox phaser 3020 black ink percentage remaining – SNMP cacti data and graph template at the bottom of this post.


With some tweaks this method should also works for other brand printers.

With the help of this article: I could set up a cacti template for my Xerox 3020 printer. Some printers return ink level value in percents but in my case I got only the raw value.

1. Get SNMP toner max capacity value, OID for that is

root@cacti: snmpget -v2c -c public
SNMPv2-SMI::mib- = INTEGER: 700

700 is the raw value for 100% toner capacity.

2. Get SNMP toner current levels value, OID for that is

root@cacti: snmpget -v2c -c public
SNMPv2-SMI::mib- = INTEGER: 686

686 is the current raw value of my toner levels. To calculate toner ink percentage remaining we need to divide current raw value (686) with max raw value (700) and multiply it by 100. 686 / 700 * 100 = 98 (percentage of ink remaining). Since the max raw value is a nice number (700), we can just divide current raw value with 7, 686/7 = 98. We can use this formula for our CDEF definitions in cacti later. Remember, this is only for Xerox 3020, other brand printers can output different raw values and you need to correct this formula accordingly.

Xerox 3020 ink remaining percentage formula: raw_current_ink_level / 7

3. Login to cacti and go to Console -> Presets -> CDEFs

Click plus sign and create new CDEF and name it Xerox toner percentage

Click plus at CDEF Items.

CDEF Item Type: Special Data Source

CDEF Item Value: Current Graph Item Data Source

Click Save

Click plus at CDEF Items.

CDEF Item Type: Custom String

CDEF Item Value: 7 (this is the value cacti will use to divide raw data)

Click Save

Click plus at CDEF Items.

CDEF Item Type: Operator

CDEF Item Value: / (this will tell cacti to use a divide operation with the custom string we defined in a previous step).

Click Save

4. Go to Console -> Templates -> Data Source

Click plus to create new data source template and name it Printer – black toner current

Name: |host_description| – black toner current

Data Input Method: Get SNMP Data

Data Source Active: tick the right box

Internal Data Source Name: toner_current

Click Create

New Custom Data field will appear.


Click Save

5.1 Go to Console -> Templates -> Graph

Click plus sign

Name: Printer – black toner levels

Title: Printer – black toner levels

Vertical Label: percent

Tick Rigid Boundaries Mode

Upper Limit: 100

Click Create

5.2 Now click plus sign at Graph Template Items

Graph Item Type: AREA

Data Source: Printer – black toner current

Color: select what you like

Consolidation Function: AVERAGE

CDEF Function: Xerox toner percentage

Text Format: Available

Click save

Add another Graph template item

Graph Item Type: GPRINT

Data Source: Printer – black toner current

Consolidation Function: LAST

CDEF Function: Xerox toner percentage

GPRINT Type: Percent(Round down to the nearest decimal)

Text format: Current:

Click Save

Add another Graph template item

Graph Item Type: LINE1

Data Source: Printer – black toner current

Consolidation function: AVERAGE

CDEF function: Xeror toner percentage


Your graph is now ready to device assignment.

Final result:

Download data and graph templates for xerox phaser 3020:

xerox_3020_cacti_toner_level_template [Dropbox mirror]




  1. SSH login to QNAP
  2. Identify virtual switch you want to put into monitoring mode, in my case qvs1
  3. Set ageing to 0
brctl show
brctl setageing qvs1 0

My example:

I’ve created a virtual machine  (SecurityOnion) on my QNAP virtualization station to monitor my home network traffic. I have setup a port mirroring on my switch to send all traffic to the QNAP ethernet adapter number 2. (My QNAP has 4 ethernet adapters). Sniffing OS usually needs two ethernet adapters, one for management and one dedicated for monitoring (sniffing). I’ve created a new virtual switch in QNAP with adapter number 2 and set it to external mode (no IP address), then I assigned this virtual switch to monitoring interface in SecurityOnion. I should’ve been able to see all the traffic now, but that wasn’t the case. There were no packets flowing to my monitoring ethernet adapter. After some investigating I found out a reddit user had the same problem. This is the solution:

SSH into your QNAP with your admin username and credentials. Check your virtual switches with a command “brctl show“, this will list all virtual switches you created. Now you need to select the virtual switch you assigned to your sniffing ethernet adapter in my case, that was adapter number 2 and run the following command: “brctl setageing qvs1 0″, where qvs1 is the number of your selected virtual switch (one that will do the sniffing, in my case adapter 2, identified as qvs1). That’s it, you should see all packets on the sniffing interface now. Thanks go to the reddit user I don’t want to name due privacy concerns.

SecuritOnion is now receiving packets on the monitoring interface:

Side note: Sniffing and analyzing traffic is heavy on CPU, HDD and RAM resources. Qnap is not a suitable candidate for that. My Qnap tests showed a CPU bottleneck (quad core celeron N3160) averaging around 70% cpu usage with low network traffic and less than 20 devices on the network.

I wanted to limit upload speed of my torrent clients (utorrent, qbittorent) with port forwarding enabled. This can be done on the client itself but I prefer the method via firewall.

For this example I forwarded port 17123 to my qbittorent client and limited upload speed to 1mbit/s. There are probably other more “proper” methods to achieve this on Pfsense, but this is working for me:

Set up a port in a client:

Go to Pfsense, Firewall, traffic shaper, limiters:

Click New limiter

Tick Enable limiter and its children

Name it upload1mbit

Set Bandwidth to 1 Mbit/s

Set Mask to Source addresses and set Description to something you like and save.

For limiters to work you also need to make a download limiter. Click new limiter and name it download1000mbit

Set bandwidth to 1000 Mbit/s

Set mask to Destination addresses

Set description and click save.

Now go to firewall, NAT and add a new rule:

Interface: WAN

Protocol: Depends on your needs, usually TCP, UDP or both

Destination: WAN address

Destination port range: 17123 to 17123

Redirect target IP: LAN IP of the machine torrent client is running on, example

Redirect target port: 17123

Description: Torrents

Click Save


Now go to firewall, rules, WAN and find the associated rule we created in the previous step, click edit.

Scroll down to the bottom and click Display Advanced, scroll down again to find In / Out pipe.

For In select download1000mbit, and for out select upload1mbit, save and apply changes. This is the opposite of what you do when you want to limit LAN IP bandwidth, because this rule is applied to WAN interface not LAN. Click save and the limiter should work. You should always reset the states when applying new settings to filters. You can do that on Diagnostics, states, reset states.

I will add more images later, this is only a quick draft. It should be sufficient to set up a rule though.


# Rtsp to youtube streaming with ffmpeg

VBR="1000k" # Bitrate of the output video, bandwidth 1000k = 1Mbit/s
QUAL="ultrafast" # Encoding speed
YOUTUBE_URL="rtmp://" # RTMP youtube URL
THREADS="0" # Number of cores, insert 0 for ffmpeg to autoselect, more threads = more FPS

VIDEOCHANNEL="videoSub" # videoMain and VideoSub for Foscam cameras

KEY="xxx-xxxx-xxxx-xxxx" # Youtube account key

# To download fonts
# wget -O /usr/local/share/fonts/ "";unzip

# Text allingment

# Other
box="1" # enable box
boxcolor="black@0.5" # box background color with transparency factor
reloadtext="1" # Reload textfile after each frame, usefull for overlaying changing data 
# like weather info. To update the textfile while streaming, you need to use mv command or a crash
# is going to happen when you update the textfile.
# Example:
# wget -q -O - | grep somevalue > ffmpegraw.txt; mv ffmpegraw.txt ffmpeg.txt

# Ffmpeg with drawtext, 
    ffmpeg -loglevel panic \
    -f lavfi -i anullsrc \
    -rtsp_transport tcp \
    -i "$SOURCE" \
    -vcodec libx264 -pix_fmt yuv420p -preset $QUAL -g 20 -b:v $VBR \
    -vf "drawtext="fontfile=${FONT}":textfile=${textfile}:x=${x}:y=${y}:reload=${reloadtext}: \
    fontcolor=white:fontsize=${FONTSIZE}:box=${box}:boxborderw=${boxborderwidth}:boxcolor=${boxcolor}" \
    -threads $THREADS -bufsize 512k \
    -f flv "$YOUTUBE_URL/$KEY"

# Copy stream only, don't encode
#ffmpeg \
#    -f lavfi -i anullsrc \
#    -rtsp_transport tcp \
#    -i "$SOURCE" \
#    -vcodec libx264 -pix_fmt yuv420p -preset $QUAL -g 20 -c:v copy -b:v $VBR \
#    -f flv "$YOUTUBE_URL/$KEY"

Overlayed data over webcam stream example:

To run the script in background you need to add nohup otherwise ffmpeg will hang.

nohup bash &

Ffmpeg likes to crash from time to time. Create a script to check for ffmpeg process and restart it if there is no process running.

# Description: Checks for existing ffmpeg process and starts one if needed

if ! pgrep -x "ffmpeg" > /dev/null
    /bin/bash $script > /dev/null 2>&1 &

Save script as

chmod +x

Run the script with crontab every minute.

crontab -e
* * * * * sudo bash /path_to_script/

1. Make sure .well-known and acme-challenge directories are set permissions to 755

2. Create random file test.txt in .well-known/acme-challenge/ and try to view it with browser

If all of this is good, your certificate should renew. The problem I had on my server is that even when step 1. and 2. were ok and working, I still  got permission denied message. The problem was I setup my Varnish engine to force SSL on non SSL request. When bot requested it was automatically redirected to So,

3. Make sure you disable force SSL during renewal.

Error message example:

Failed authorization procedure. (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from "<!DOCTYPE html>
<title>403 Forbidden</title>
<h1>Error 403 Forbidden</h1>

EDIT: I have experienced some lag with webm streaming, scroll down for swf configuration.

Live stream camera (Foscam in my case) over the internet cheat sheet:

apt-get install software-properties-common
add-apt-repository ppa:mc3man/trusty-media
apt-get update
apt-get install ffmpeg

Create ffserver.conf file

nano /etc/ffserver.conf
HTTPPort 8090 # Port to bind the server to
MaxHTTPConnections 2000
MaxClients 15
MaxBandwidth 50000 # Maximum bandwidth per client
 # set this high enough to exceed stream bitrate
CustomLog -

<Feed feed1.ffm>
 File /tmp/feed1.ffm
 FileMaxSize 1g
 ACL allow localhost
 ACL allow

<Stream live.webm>
 Format webm
 Feed feed1.ffm

 VideoCodec libvpx
 VideoSize 640x480
 VideoFrameRate 30
 VideoBitRate 512
 VideoBufferSize 512
 AVOptionVideo flags +global_header


<Stream status.html> # Server status URL
 Format status
 # Only allow local people to get the status
 ACL allow localhost
 ACL allow

<Redirect index.html> # Just an URL redirect for index
 # Redirect index.html to the appropriate site
 URL live.webm/

Save, exit and start the services

ffserver > /dev/null 2>&1 &
ffmpeg -loglevel error -r 30 -i rtsp://user:pass@camera_IP:port/url http://LAN_IP_WHERE_FFSERVER_IS_RUNNING:8090/feed1.ffm /dev/null 2>/var/log/ffmpeg.log &

Visit http://LAN_IP_WHERE_FFSERVER_IS_RUNNING:8090/live.webm
For status go to http://LAN_IP_WHERE_FFSERVER_IS_RUNNING:8090/status.html

I have experienced some lag with the above configuration, here is the config for a swf streaming:

HTTPPort 8090 # Port to bind the server to
MaxHTTPConnections 2000
MaxClients 15
MaxBandwidth 50000 # Maximum bandwidth per client
 # set this high enough to exceed stream bitrate
CustomLog -

<Feed feed1.ffm>
 File /tmp/feed1.ffm
 FileMaxSize 1g
 ACL allow localhost
 ACL allow

<Stream live.ffm>
 Feed feed1.ffm

Format swf
VideoCodec flv
VideoFrameRate 30
VideoBufferSize 80000
VideoBitRate 100
# quality ranges - 1-31 (1 = best, 31 = worst)
VideoQMin 1
VideoQMax 5
VideoSize 1024x786
PreRoll 0


<Stream status.html> # Server status URL
 Format status
 # Only allow local people to get the status
 ACL allow localhost
 ACL allow

<Redirect index.html> # Just an URL redirect for index
 # Redirect index.html to the appropriate site
 URL live.ffm/

Save and restart ffserver and ffmpeg.

Edit your index.html file to play swf file in the browser:

<meta charset="UTF-8">
<link rel="icon" href="favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<title>Site title</title>
<script type="text/javascript" src="swfobject.js"></script>
 <script type="text/javascript">
 swfobject.registerObject("myFlashContent", "9.0.0", "expressInstall.swf");
 <script type="text/javascript">
 swfobject.embedSWF("http://FF_SERVER_IP:8090/live.ffm", "myContent", "1024", "786", "9.0.0");
<p align="top">Some optional text.</p>
 <div id="myContent">
 <p>Live stream</p>