I have been working to set up a Soekris net5501 to act as a home router/server. My efforts are documented here to serve as an example for others attempting similar setup scenarios. Note that this guide is only intended to cover post-installation software setup and so should be applicable to nearly any hardware.

Under construction. I hope to get this finished eventually.

If you notice any errors please mail me, leave a ?comment, or fix them.

If you know of a better place in the wiki for this to go, please ?suggest it.

Requirements

Here is a list of requirements with links to the relevant sections.

Document Conventions

SixXS

Register with SixXS and request an tunnel and subnet. Choose your tunnel type carefully. I chose the less efficient AYIYA type because it will work behind masquerading.

Use aiccu to bring up the tunnel.

OPTIONAL: To avoid using your SixXS password in plaintext in the aiccu configuration file, add a TIC Password for the tunnel, then use "$HANDLE/$TUNNELID" as your username and the password you chose in the configuration.

aiccu.conf:

username $HANDLE
password $PASSWORD
protocol tic
server tic.sixxs.net
ipv6_interface aiccu
tunnel_id $TUNNELID
automatic true
requiretls false

Now you should have an interface named "aiccu" and ipv6 connectivity. Test with:

ping6 -c 2 www.kame.net

Basic Networking

/etc/network/interfaces:

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface (WAN)
auto eth0
#allow-hotplug eth0 # hotplugging does not seem to work reliably
iface eth0 inet dhcp

# Network bridge (LAN)
auto br0
iface br0 inet static
  hostapd /etc/hostapd/hostapd.conf  # This line starts hostapd
  address 192.0.2.1
  netmask 255.255.255.0
  network 192.0.2.0
  broadcast 192.0.2.255
  bridge_ports eth1 eth2 eth3 wlan0
iface br0 inet6 static
  address (from sixxs)
  netmask 64

# Public wireless network
auto wlan0_0
iface wlan0_0 inet static
  address 192.0.3.1
  netmask 255.255.255.0
  network 192.0.3.0
  broadcast 192.0.3.255
iface wlan0_0 inet6 static
  address (from sixxs)
  netmask 64


Set up /etc/hosts to make local DNS work correctly:

Change this line:

127.0.1.1      hostname.example.org hostname

To:

192.168.68.1    hostname.example.org hostname2.example2.org hostname

Test: make sure both hostname -s and hostname -f work correctly now.

dnsmasq

Fill dnsmasq.conf:

interface=br0
dhcp-range=private,192.0.2.51,192.0.2.250,48h

interface=wlan0_0
dhcp-range=public,192.0.3.51,192.0.3.250,48h

domain-needed
bogus-priv

# Set the NTP time server address to be the same machine as
# is running dnsmasq
dhcp-option=42,0.0.0.0

# Send microsoft-specific option to tell windows to release the DHCP lease
# when it shuts down. 
dhcp-option=vendor:MSFT,2,1i

# Set the limit on DHCP leases, the default is 150
## here, raised to the maximum number of hosts on networks
dhcp-lease-max=506

# Set the DHCP server to authoritative mode. In this mode it will barge in
# and take over the lease for any client which broadcasts on the network,
# whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's
# the slighest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:
# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
#dhcp-authoritative


OPTIONAL: For static host configuration, create /etc/dnsmasq.d/static-hosts.conf:

# static DHCP hosts
# Optionally, use IPs from 2 to 50 (outside of DHCP range).

# example for private network
#dhcp-host=xx:xx:xx:xx:xx:xx,192.0.2.10

# example for public network
#dhcp-host=xx:xx:xx:xx:xx:xx,192.0.3.16


OPTIONAL: To use custom nameservers, create /etc/alt.dns:

# Google Public DNS
nameserver      8.8.8.8
nameserver      8.8.4.4

And uncomment line in /etc/default/dnsmasq:

IGNORE_RESOLVCONF=yes

And add line to /etc/dnsmasq.conf:

resolv-file=/etc/alt.dns

Firewall

Firewall shell scripts are slow (see ?http://www.faqs.org/docs/iptables/saveandrestore.html), and scripts for iptables-restore/ip6tables-restore must be maintained individually. In order to keep performance, use a single script, and gain readability to easy maintenance, I built a script that uses iptables-restore and family.

Create /usr/local/sbin/build-firewall.sh:

 #!/bin/sh  # What markup will make this line correct?
set -e

case "$1" in
 apply)
  ;;
 restore)
  ;;
 build)
  ;;
 save)
  ;;
 *)
  echo "Usage: $0 {apply|build|restore|save}"
  echo
  echo "apply: iptables-apply (includes connection check)"
  echo "build: just write iptables and ip6tables scripts"
  echo "restore: iptables-restore (apply immediately without check)"
  echo "save: restore, then save iptables and ip6tables scripts to /etc/network/"
  exit 1
  ;;
esac

policies() {
ipboth RAW :PREROUTING ACCEPT
ipboth RAW :OUTPUT ACCEPT
ipboth MANGLE :PREROUTING ACCEPT
ipboth MANGLE :INPUT ACCEPT
ipboth MANGLE :FORWARD ACCEPT
ipboth MANGLE :OUTPUT ACCEPT
ipboth MANGLE :POSTROUTING ACCEPT
ip4 NAT :PREROUTING ACCEPT
ip4 NAT :OUTPUT ACCEPT
ip4 NAT :POSTROUTING ACCEPT
ipboth FILTER :INPUT DROP
ipboth FILTER :FORWARD DROP
ipboth FILTER :OUTPUT DROP
}

rules() { # Use no "double quotes" in rules!
# DNAT
 # modify DNAT
 ip4 NAT :DNAT_MOD -
 ip4 NAT -A PREROUTING -j DNAT_MOD

 # do not change destination IP/port for these packets
 ip4 NAT -A DNAT_MOD -i eth0 -p icmp -j RETURN
 ip4 NAT :DNAT_SKIP -
 ip4 NAT -A DNAT_MOD -i eth0 -g DNAT_SKIP

 # manual port forwarding
 ip4 NAT :MANUAL_DNAT -
 ip4 NAT -A DNAT_MOD -i eth0 -j MANUAL_DNAT
 ## example: to specific host (modify static-hosts.conf if necessary)
 #ip4 NAT -A MANUAL_DNAT -p tcp -m tcp --dport 33333 -j DNAT --to-destination 192.0.2.10
 ## example: to specific host and different destination port (modify static-hosts.conf if necessary)
 #ip4 NAT -A MANUAL_DNAT -p tcp -m tcp --dport 81 -j DNAT --to-destination 192.0.3.11:80
 ## example: to different destination port on router
 #ip4 NAT -A MANUAL_DNAT -p tcp -m tcp --dport 81 -j DNAT --to-destination :80

 # upnp
 ip4 NAT :DNAT_UPNP -
 ip4 NAT -A DNAT_MOD -i eth0 -j DNAT_UPNP
 ## example, from linux-igd upnpd
 ##ip4 NAT -A DNAT_UPNP -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.0.2.96:80

 # forward all remaining ports
 ip4 NAT :DNAT_ALLPORTS -
 ip4 NAT -A DNAT_MOD -i eth0 -j DNAT_ALLPORTS
 #ip4 NAT -A DNAT_ALLPORTS -j DNAT --to-destination 192.0.2.10
# END DNAT

# masquerading
ip4 NAT -A POSTROUTING -s 192.0.2.0/24 ! -d 192.0.2.0/24 -o eth0 -j MASQUERADE
ip4 NAT -A POSTROUTING -s 192.0.3.0/24 ! -d 192.0.3.0/24 -o eth0 -j MASQUERADE

# IPv6
ip4 FILTER -A INPUT -p ipv6 -j ACCEPT
ip4 FILTER -A FORWARD -p ipv6 -j ACCEPT
ip4 FILTER -A OUTPUT -p ipv6 -j ACCEPT

# loopback
ipboth FILTER -A INPUT -i lo -j ACCEPT
ipboth FILTER -A OUTPUT -o lo -j ACCEPT

# BLOCKING
 # drop RH0
 ip6tables -A INPUT -m rt --rt-type 0 -j DROP
 ip6tables -A FORWARD -m rt --rt-type 0 -j DROP
 ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP
  
 # host block
 ipboth FILTER :HOST_BLOCK -
 ipboth FILTER -A INPUT -j HOST_BLOCK
 ipboth FILTER -A FORWARD -j HOST_BLOCK
 ipboth FILTER -A OUTPUT -j HOST_BLOCK
 ## examples
 # ipboth FILTER -A HOST_BLOCK --source 75.0.1.25 -j HOST_BLOCK_EXEC
 # ipboth FILTER -A HOST_BLOCK --destination 75.0.1.25 -j HOST_BLOCK_EXEC
 ipboth FILTER :HOST_BLOCK_EXEC -
 ipboth FILTER -A HOST_BLOCK_EXEC -m limit --limit 1/min --limit-burst 1 -j LOG --log-prefix Blocked-host_ --log-level notice
 ipboth FILTER -A HOST_BLOCK_EXEC -j DROP
  
 # invalid
 ipboth FILTER :VALIDITY_CHECK -
 ipboth FILTER :INVALID -
 ipboth FILTER -A INPUT -j VALIDITY_CHECK
 ipboth FILTER -A FORWARD -j VALIDITY_CHECK  
 ipboth FILTER -A VALIDITY_CHECK -m state --state INVALID -j INVALID
 ipboth FILTER -A INVALID -m limit --limit 6/min --limit-burst 2 -j LOG --log-prefix Invalid-packet_ --log-level info
 ipboth FILTER -A INVALID -j DROP
 ## more validity checks here, like previous line

 # fragmented
 ipboth FILTER :FRAGMENT_CHECK -
 ipboth FILTER -A INPUT -j FRAGMENT_CHECK
 ipboth FILTER -A FORWARD -j FRAGMENT_CHECK
 ipboth FILTER :FRAGMENTED -
 ip4 FILTER -A FRAGMENT_CHECK -f -j FRAGMENTED
 ip6 FILTER -A FRAGMENT_CHECK -m frag --fragmore -m length --length 0:1279 -j FRAGMENTED
 ipboth FILTER -A FRAGMENTED -m limit --limit 3/min --limit-burst 1 -j LOG --log-prefix Fragmented-packet_ --log-level info
 ipboth FILTER -A FRAGMENTED -j DROP  
# END BLOCKING

# local dhcp
ipboth FILTER -A INPUT -p udp -i br0 --sport 67:68 --dport 67:68 -j ACCEPT
ipboth FILTER -A INPUT -p udp -i wlan0_0 --sport 67:68 --dport 67:68 -j ACCEPT
# out
ipboth FILTER -A OUTPUT -p udp -o br0 --sport 67:68 --dport 67:68 -j ACCEPT
ipboth FILTER -A OUTPUT -p udp -o wlan0_0 --sport 67:68 --dport 67:68 -j ACCEPT

# spoofed
ipboth FILTER :SPOOFED -
ip4 FILTER -A INPUT -j SPOOFED # TODO: add ip6 here
ip4 FILTER -A FORWARD -j SPOOFED # TODO: add ip6 here
ipboth FILTER -A SPOOFED -i eth0 -j RETURN
ip6 FILTER -A SPOOFED -i aiccu -j RETURN
ip4 FILTER -A SPOOFED -i br0 -s 192.0.2.0/24 -j RETURN
#ip6 FILTER -A SPOOFED -i br0 -s :: -j RETURN # TODO: fix with correct ipv6 addresses
ip4 FILTER -A SPOOFED -i wlan0_0 -s 192.0.3.0/24 -j RETURN
#ip6 FILTER -A SPOOFED -i wlan0_0 -s :: -j RETURN # TODO: fix with correct ipv6 addresses
ipboth FILTER -A SPOOFED -m limit --limit 6/min -j LOG --log-prefix Spoofed-packet_ --log-level notice
ip4 FILTER -A SPOOFED -j REJECT --reject-with icmp-net-unreachable
ip6 FILTER -A SPOOFED -j REJECT --reject-with icmp6-adm-prohibited

# ICMP
ip4 FILTER -A OUTPUT -p icmp -j ACCEPT
ip6 FILTER -A OUTPUT -p ipv6-icmp -j ACCEPT
ipboth FILTER :ICMP -
ip4 FILTER -A INPUT -p icmp -j ICMP
ip6 FILTER -A INPUT -p ipv6-icmp -j ICMP
ip4 FILTER -A FORWARD -p icmp -j ICMP
ip6 FILTER -A FORWARD -p ipv6-icmp -j ICMP
ipboth FILTER -A ICMP -m limit --limit 20/sec --limit-burst 100 -j ACCEPT
ip4 FILTER -A ICMP -p icmp -m icmp --icmp-type echo-request -j DROP
ip6 FILTER -A ICMP -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -j DROP
ipboth FILTER -A ICMP -m limit --limit 20/sec -j ACCEPT
ipboth FILTER -A ICMP -j DROP

# established connections
ipboth FILTER -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
ipboth FILTER -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
ipboth FILTER -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# SPECIFIC SERVICES
 # ssh
 ip4 NAT -A DNAT_SKIP -p tcp --dport ssh -j RETURN
 ipboth FILTER -A INPUT -p tcp --dport ssh -j ACCEPT
 ipboth FILTER -A OUTPUT -p tcp --dport ssh -j ACCEPT

 # upstream dhcp
 ip4 NAT -A DNAT_SKIP -p tcp --dport 67:68 -j RETURN
 ipboth FILTER -A INPUT -p udp -i eth0 --sport 67:68 --dport 67:68 -j ACCEPT
 ipboth FILTER -A OUTPUT -p udp -o eth0 --sport 67:68 --dport 67:68 -j ACCEPT

 # ddns
 ipboth FILTER -A OUTPUT -p tcp -o eth0 --dport 5000 -j ACCEPT #*check*# port 5000?
 ipboth FILTER -A OUTPUT -p tcp -o aiccu --dport 5000 -j ACCEPT #*check*# port 5000?   

 # SixXS
 ip4 NAT -A DNAT_SKIP -p tcp --dport 3874 -j RETURN
 ip4 NAT -A DNAT_SKIP -p udp -m multiport --dport 3740,5072 -j RETURN
 ip4 FILTER -A OUTPUT -p tcp -o eth0 --dport 3874 -j ACCEPT
 ip4 FILTER -A OUTPUT -p udp -o eth0 -m multiport --dport 3740,5072 -j ACCEPT

 # dns
 ip4 NAT -A DNAT_SKIP -p tcp -m multiport --dport domain,mdns -j RETURN
 ip4 NAT -A DNAT_SKIP -p udp -m multiport --dport domain,mdns -j RETURN
 ipboth FILTER -A INPUT -p tcp -i br0 -m multiport --dport domain,mdns -j ACCEPT
 ipboth FILTER -A INPUT -p udp -i br0 -m multiport --dport domain,mdns -j ACCEPT
 ipboth FILTER -A INPUT -p tcp -i wlan0_0 -m multiport --dport domain,mdns -j ACCEPT
 ipboth FILTER -A INPUT -p udp -i wlan0_0 -m multiport --dport domain,mdns -j ACCEPT
 ipboth FILTER -A OUTPUT -p udp -o br0 --dport mdns -j ACCEPT
 ipboth FILTER -A OUTPUT -p udp -o wlan0_0 --dport mdns -j ACCEPT  
 ipboth FILTER -A OUTPUT -p tcp -o eth0 --dport domain -j ACCEPT
 ipboth FILTER -A OUTPUT -p udp -o eth0 --dport domain -j ACCEPT
 ip6 FILTER -A OUTPUT -p tcp -o aiccu --dport domain -j ACCEPT
 ip6 FILTER -A OUTPUT -p udp -o aiccu --dport domain -j ACCEPT

 # ntp
 ip4 NAT -A DNAT_SKIP -p udp --dport ntp -j RETURN
 ipboth FILTER -A INPUT -p udp --dport ntp -j ACCEPT
 ipboth FILTER -A OUTPUT -p udp --sport ntp -j ACCEPT

 # www/https
 ip4 NAT -A DNAT_SKIP -p tcp --dport www -j RETURN
 ipboth FILTER -A INPUT -p tcp --dport www -j ACCEPT
 ipboth FILTER -A OUTPUT -p tcp -o eth0 -m multiport --dports www,https -j ACCEPT      
 ip6 FILTER -A OUTPUT -p tcp -o aiccu -m multiport --dports www,https -j ACCEPT

 # upnp
 ip4 NAT -A DNAT_SKIP -p tcp -m multiport --dports 5000,49152 -j RETURN
 ip4 NAT -A DNAT_SKIP -p udp --dport 1900 -j RETURN
 ip4 FILTER :UPNP_FORWARD -
 ip4 FILTER -A INPUT -i br0 -p udp -m multiport --ports 1900 -j ACCEPT
 ip4 FILTER -A INPUT -i br0 -p tcp -m multiport --ports 2869,5000,49152 -j ACCEPT
 ip4 FILTER -A INPUT -i br0 -p igmp -j ACCEPT
 ip4 FILTER -A OUTPUT -o br0 -p udp -m multiport --ports 1900 -j ACCEPT
 ip4 FILTER -A OUTPUT -o br0 -p tcp -m multiport --ports 2869,5000,49152 -j ACCEPT
 ip4 FILTER -A OUTPUT -o br0 -p igmp -j ACCEPT
 ip4 FILTER -A FORWARD -i eth0 -o br0 -j UPNP_FORWARD
 ## example, from linux-igd upnpd
 ##ip4 FILTER -A UPNP_FORWARD -d 192.0.2.10/24 -p tcp -m tcp --dport 80 -j ACCEPT
# END SPECIFIC SERVICES

# from private
ipboth FILTER :FROM_PRIVATE -
ipboth FILTER -A FORWARD -i br0 -j FROM_PRIVATE
ipboth FILTER -A FROM_PRIVATE -o br0 -j ACCEPT
ipboth FILTER -A FROM_PRIVATE -o eth0 -j ACCEPT
ipboth FILTER -A FROM_PRIVATE -o aiccu -j ACCEPT
ipboth FILTER -A FROM_PRIVATE -o wlan0_0 -m limit --limit 3/min -j LOG --log-prefix Rejected-private-to-public_ --log-level info
ip4 FILTER -A FROM_PRIVATE -o wlan0_0 -j REJECT --reject-with icmp-net-prohibited
ip6 FILTER -A FROM_PRIVATE -o wlan0_0 -j REJECT --reject-with icmp6-adm-prohibited

# from public
ipboth FILTER :FROM_PUBLIC -
ipboth FILTER -A FORWARD -i wlan0_0 -j FROM_PUBLIC
ipboth FILTER -A FROM_PUBLIC -o eth0 -j ACCEPT
ipboth FILTER -A FROM_PUBLIC -o aiccu -j ACCEPT
ipboth FILTER -A FROM_PUBLIC -o br0 -m limit --limit 3/min -j LOG --log-prefix Rejected-public-to-private_ --log-level info
ip4 FILTER -A FROM_PUBLIC -o br0 -j REJECT --reject-with icmp-net-prohibited
ip6 FILTER -A FROM_PUBLIC -o br0 -j REJECT --reject-with icmp6-adm-prohibited
ipboth FILTER -A FROM_PUBLIC -o wlan0_0 -m limit --limit 3/min -j LOG --log-prefix Rejected-public-to-public_ --log-level info
ip4 FILTER -A FROM_PUBLIC -o wlan0_0 -j REJECT --reject-with icmp-host-prohibited
ip6 FILTER -A FROM_PUBLIC -o wlan0_0 -j REJECT --reject-with icmp6-adm-prohibited

# DROP/REJECT
 # unmatched packets
 ip4 FILTER -A INPUT -m limit --limit 20/min -j LOG --log-prefix Unmatched-INPUT_ --log-level debug
 ip6 FILTER -A INPUT -m limit --limit 20/min -j LOG --log-prefix Unmatched-v6-INPUT_ --log-level debug
 ip4 FILTER -A FORWARD -m limit --limit 20/min -j LOG --log-prefix Unmatched-FORWARD_ --log-level debug
 ip6 FILTER -A FORWARD -m limit --limit 20/min -j LOG --log-prefix Unmatched-v6-FORWARD_ --log-level debug
 ip4 FILTER -A OUTPUT -j LOG --log-prefix Unmatched-OUTPUT_ --log-level debug
 ip6 FILTER -A OUTPUT -j LOG --log-prefix Unmatched-v6-OUTPUT_ --log-level debug
# end DROP/REJECT

# TOS
 ipboth MANGLE :TOS_CHAIN -
 ipboth MANGLE -A PREROUTING -j TOS_CHAIN

 # Correcting TOS for incorrectly marked packets
 ipboth MANGLE :FIX_TOS -
 ipboth MANGLE -A TOS_CHAIN -m tos ! --tos Normal-Service -j FIX_TOS
  # Large packets with Minimize-Delay TOS
  ipboth MANGLE -A FIX_TOS -m tos ! --tos Minimize-Delay -j RETURN
  ipboth MANGLE -A FIX_TOS -p tcp -m length --length 0:512  -j RETURN
  ipboth MANGLE -A FIX_TOS -p udp -m length --length 0:1024 -j RETURN
  ipboth MANGLE -A FIX_TOS -j TOS --set-tos Maximize-Throughput

 # Add TOS for unmarked packets
 ipboth MANGLE :ADD_TOS -
 ipboth MANGLE -A TOS_CHAIN -m tos --tos Normal-Service -j ADD_TOS
  # TCP control packets (from www.docum.org / Stef Coene)
  ipboth MANGLE :ACK_TOS -
  ipboth MANGLE -A ADD_TOS -p tcp -m tcp --tcp-flags SYN,RST,ACK ACK -j ACK_TOS
  ipboth MANGLE -A ACK_TOS -m length --length 0:256 -j TOS --set-tos Minimize-Delay
  ipboth MANGLE -A ACK_TOS -m length --length 256: -j TOS --set-tos Maximize-Throughput
  ipboth MANGLE -A ADD_TOS -p tcp --tcp-flags SYN,RST,ACK ACK -j RETURN
  # Specify general TOS settings here for protocol/port
  ipboth MANGLE -A ADD_TOS -p icmp -j TOS --set-tos Minimize-Delay
  ipboth MANGLE -A ADD_TOS -p tcp -m multiport --ports smtp -j TOS --set-tos Minimize-Cost
  #ipboth MANGLE -A ADD_TOS -p tcp -m multiport --ports 000 -j TOS --set-tos Maximize-Reliability
  ipboth MANGLE -A ADD_TOS -p tcp -m multiport --ports www,https -j TOS --set-tos Maximize-Throughput
  ipboth MANGLE -A ADD_TOS -p tcp -m multiport --ports domain,mdns,auth,5154,3389,5900 -j TOS --set-tos Minimize-Delay
  #ipboth MANGLE -A ADD_TOS -p udp -m multiport --ports 000 -j TOS --set-tos Minimize-Cost
  #ipboth MANGLE -A ADD_TOS -p udp -m multiport --ports 000 -j TOS --set-tos Maximize-Reliability
  #ipboth MANGLE -A ADD_TOS -p udp -m multiport --ports 000 -j TOS --set-tos Maximize-Throughput
  ipboth MANGLE -A ADD_TOS -p udp -m multiport --ports domain,67:68,ntp,5154,5060:5064,iax,1720,1731 -j TOS --set-tos Minimize-Delay
# END TOS

# SHAPING
 # add shaping assist fwmarks
 ipboth MANGLE :MARKING_IN -
 ipboth MANGLE -A PREROUTING -j MARKING_IN
  ipboth MANGLE -A MARKING_IN -i eth0 -j RETURN
  ipboth MANGLE -A MARKING_IN -i aiccu -j RETURN
  ipboth MANGLE -A MARKING_IN -i br0 -j MARK --set-mark 0x2/0xf
  ipboth MANGLE -A MARKING_IN -i br0 -j RETURN
  ipboth MANGLE -A MARKING_IN -i wlan0_0 -j MARK --set-mark 0x3/0xf
 ip4 MANGLE -A OUTPUT -j MARK --set-mark 0x4/0xf #*check* breaks radvd on ipv6 side
# END SHAPING

# TCPMSS clamp
ipboth MANGLE -A POSTROUTING -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
ipboth MANGLE -A POSTROUTING -o aiccu -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
}


# build
IPTABLES=$( mktemp /tmp/iptables.XXX )
IP6TABLES=$( mktemp /tmp/ip6tables.XXX )
RAW_IP4=$( mktemp )
MANGLE_IP4=$( mktemp )
NAT_IP4=$( mktemp )
FILTER_IP4=$( mktemp )
RAW_IP6=$( mktemp )
MANGLE_IP6=$( mktemp )
FILTER_IP6=$( mktemp )
ip4() {
 TABLE=$( eval "echo \$${1}_IP4" )
 shift 1
 echo $* >> $TABLE
}
ip6() {
 TABLE=$( eval "echo \$${1}_IP6" )
 shift 1
 echo $* >> $TABLE
}
ipboth() {
 ip4 $@  
 ip6 $@
}
ipboth RAW "*raw"
ipboth MANGLE "*mangle"
ip4 NAT "*nat"
ipboth FILTER "*filter"
policies
rules
for TABLE in RAW_IP4 MANGLE_IP4 NAT_IP4 FILTER_IP4 RAW_IP6 MANGLE_IP6 FILTER_IP6 ; do
 eval "echo COMMIT >> \$${TABLE}"
done
echo "# iptables firewall for owenhs" >> $IPTABLES
echo "# ip6tables firewall for owenhs" >> $IP6TABLES
cat $RAW_IP4 $MANGLE_IP4 $NAT_IP4 $FILTER_IP4 >> $IPTABLES
cat $RAW_IP6 $MANGLE_IP6 $FILTER_IP6 >> $IP6TABLES
rm -f $RAW_IP4 $MANGLE_IP4 $NAT_IP4 $FILTER_IP4 $RAW_IP6 $MANGLE_IP6 $FILTER_IP6
case "$1" in
 apply)
  iptables-apply -t 30 $IPTABLES
  ip6tables-apply -t 30 $IP6TABLES
  echo "iptables and ip6tables applied successfully"
  rm -f $IPTABLES $IP6TABLES
  ;;
 restore)
  iptables-restore $IPTABLES
  ip6tables-restore $IP6TABLES
  echo "iptables and ip6tables restored successfully"
  rm -f $IPTABLES $IP6TABLES
  ;;
 build) 
  echo "new: $IPTABLES"
  echo "new: $IP6TABLES"
  ;;   
 save)
  iptables-restore $IPTABLES
  ip6tables-restore $IP6TABLES
  mv /etc/network/iptables /tmp/iptables.bak0
  mv /etc/network/ip6tables /tmp/ip6tables.bak0
  mv $IPTABLES /etc/network/iptables
  mv $IP6TABLES /etc/network/ip6tables
  mv /tmp/iptables.bak0 $IPTABLES
  mv /tmp/ip6tables.bak0 $IP6TABLES
  echo "old: $IPTABLES"
  echo "old: $IP6TABLES"
  ;;
esac

UPnP

Note that UPnP has some flaws. Use it only if you understand and accept them. If you choose not to use UPnP, you should remove the applicable parts of the firewall script.

/etc/default/linux-igd:

EXTIFACE=eth0
INTIFACE=br0
ALLOW_MULTICAST=yes

/etc/upnpd.conf:

iptables_location = "/sbin/iptables"
debug_mode = 3
create_forward_rules = yes
forward_rules_append = no
forward_chain_name = UPNP_FORWARD
prerouting_chain_name = DNAT_UPNP
duration = 86400 # One day from time of addition
description_document_name = gatedesc.xml
xml_document_path = /etc/linuxigd
listenport = 49152
paranoid = 1

hostapd

It seemed to work best to start hostapd from /etc/network/interfaces rather than init.

hostapd.conf:

interface=wlan0
bridge=br0
driver=nl80211
logger_syslog=-1
logger_stdout=-1
logger_syslog_level=2
logger_stdout_level=4
dump_file=/tmp/hostapd.dump
ctrl_interface=/var/run/hostapd

ssid=private
country_code=US
ieee80211d=1
hw_mode=g
channel=10
beacon_int=100
max_num_sta=255
rts_threshold=2347
fragm_threshold=2346
preamble=1
macaddr_acl=0
accept_mac_file=/etc/hostapd/hostapd.accept
deny_mac_file=/etc/hostapd/hostapd.deny
ignore_broadcast_ssid=0
ap_max_inactivity=300
ieee8021x=0
eap_server=0
wpa=3
wpa_passphrase=passphrase
wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256
wpa_pairwise=TKIP CCMP
rsn_pairwise=CCMP
wpa_strict_rekey=1
ieee80211w=1

bss=wlan0_0
bssid=02:xx:xx:xx:xx:xx  # use your wireless MAC address, just change the first character pair
ssid=public
wpa=0

Traffic Control

Create /usr/local/sbin/shaping.sh:

 #!/bin/sh  # What markup will make this line correct?
set -e

# Default up/down values in kilobytes/s
DOWN=50
UP=30

# Tested up/down values in bytes/s
if DOWN_TESTED=$( cat /var/local/down-bytes.txt 2> /dev/null ) ; then
  DOWN=$DOWN_TESTED
 else
  DOWN=$( echo "$DOWN * 1024" | bc )
fi
if UP_TESTED=$( cat /var/local/up-bytes.txt 2> /dev/null ) ; then
  UP=$UP_TESTED
 else
  UP=$( echo "$UP * 1024" | bc )
fi

# Drop to safe percentage of tested bandwidth
DOWN=$(( $DOWN * 90 / 100 ))
UP=$(( $UP * 90 / 100 ))


remove() {
 for DEV in eth0 sit1 eth1 eth2 eth3 wlan0 wlan0_0 ; do
  tc qdisc del dev $DEV root 2> /dev/null || true
 done
}

case "$1" in
 start|restart)
  remove
  ;;
 stop)
  remove
  exit 0
  ;;
 status)
  echo "[qdisc]"
  tc -s qdisc show dev $DEV
  echo "[class]"
  tc -s class show dev $DEV
  echo "[filter]"
  tc -s filter show dev $DEV
  exit 0
  ;;
 rates)
  tc class change dev eth0 parent 0x1:0x0 classid 0x1:0x1 hfsc ls m2 7bps ul m2 ${UP}bps
  for DEV in eth1 eth2 eth3 wlan0 ; do
   tc class change dev $DEV parent 0x1:0x1 classid 0x1:0x2 hfsc ls m2 50bps ul m2 $(($DOWN *8/10))bps
  done
  tc class change dev wlan0_0 parent 0x1:0x1 classid 0x1:0x3 hfsc ls m2 25bps ul m2 $(($DOWN *4/10))bps
  exit
  ;;
 *)
  echo "Usage: $0 {start|stop|status|restart|rates}"
  exit 1
  ;;
esac

# DESCRIPTION     CLASSID       INTERFACES                 RT LS UL  RATES (bps & s; without commas=m2 only)
# root qdisc HFSC 0x1:0x0       all                        N  N  N
# + root class    0x1:0x1       all                        N  Y  N   ls=7
#   + private     0x1:0x2       eth0,eth1,eth2,eth3,wlan0  N  Y  Y   ls=50; ul(all,-eth0)=$DOWN*8/10
#     + prio 1    0x1:0x21                                 Y  Y  N   rt=3072; ls=6
#     + prio 2    0x1:0x22                                 Y  Y  N   rt=1024; ls=5,20,4
#     + prio 3    0x1:0x23                                 Y  Y  N   rt=1024; ls=4,7,3
#   + public      0x1:0x3       eth0,wlan0_0               N  Y  Y   ls=25; ul(wlan0_0)=$DOWN*5/10
#     + prio 1    0x1:0x31                                 Y  Y  N   rt=3072; ls=6
#     + prio 2    0x1:0x32                                 Y  Y  N   rt=1024; ls=5,20,4
#     + prio 3    0x1:0x33                                 Y  Y  N   rt=1024; ls=4,7,3
#   + router      0x1:0x4       all                        N  Y  N   ls=25
#     + prio 1    0x1:0x41                                 Y  Y  N   rt=3072; ls=6
#     + prio 2    0x1:0x42                                 Y  Y  N   rt=1024; ls=5,20,4
#     + prio 3    0x1:0x43                                 Y  Y  N   rt=1024; ls=4,7,3
# + default class 0x1:0x5       all                        N  Y  N   ls=3

# root HFSC qdisc 0x1:0x0
# default class   0x1:0x5
for DEV in eth0 eth1 eth2 eth3 wlan0 wlan0_0 ; do
 tc qdisc add dev $DEV handle 0x1:0x0 root hfsc default 0x5
 tc class add dev $DEV parent 0x1:0x0 classid 0x1:0x5 hfsc ls m2 3bps
done

# root class      0x1:0x1
tc class add dev eth0 parent 0x1:0x0 classid 0x1:0x1 hfsc ls m2 7bps ul m2 ${UP}bps
for DEV in eth1 eth2 eth3 wlan0 ; do
 tc class add dev $DEV parent 0x1:0x0 classid 0x1:0x1 hfsc ls m2 7bps
done
tc class add dev wlan0_0 parent 0x1:0x0 classid 0x1:0x1 hfsc ls m2 7bps

# primary classes 0x1:0x2-0x4  (private=2,public=3,router=4)
# filters at 0x1:0x0 to drop packets in one of 0x1:0x2-0x4
tc class add dev eth0 parent 0x1:0x1 classid 0x1:0x2 hfsc ls m2 50bps # private
tc class add dev eth0 parent 0x1:0x1 classid 0x1:0x3 hfsc ls m2 25bps # public
tc class add dev eth0 parent 0x1:0x1 classid 0x1:0x4 hfsc ls m2 25bps # router
tc filter add dev eth0 parent 0x1:0x0 protocol ip prio 1 handle 2 fw flowid 0x1:0x2 # private
tc filter add dev eth0 parent 0x1:0x0 protocol ip prio 1 handle 3 fw flowid 0x1:0x3 # public
tc filter add dev eth0 parent 0x1:0x0 protocol ip prio 1 handle 4 fw flowid 0x1:0x4 # router
for DEV in eth1 eth2 eth3 wlan0 ; do
 tc class add dev $DEV parent 0x1:0x1 classid 0x1:0x2 hfsc ls m2 50bps ul m2 $(($DOWN *8/10))bps # private
 tc class add dev $DEV parent 0x1:0x1 classid 0x1:0x4 hfsc ls m2 25bps # router
 tc filter add dev $DEV parent 0x1:0x0 protocol ip prio 1 handle 4 fw flowid 0x1:0x4 # router
 tc filter add dev $DEV parent 0x1:0x0 protocol ip prio 2 u32 match ip src 0.0.0.0/0 flowid 0x1:0x2 # other
done
tc class add dev wlan0_0 parent 0x1:0x1 classid 0x1:0x3 hfsc ls m2 25bps ul m2 $(($DOWN *4/10))bps # public
tc class add dev wlan0_0 parent 0x1:0x1 classid 0x1:0x4 hfsc ls m2 25bps # router
tc filter add dev wlan0_0 parent 0x1:0x0 protocol ip prio 1 handle 4 fw flowid 0x1:0x4 # router
tc filter add dev wlan0_0 parent 0x1:0x0 protocol ip prio 2 u32 match ip src 0.0.0.0/0 flowid 0x1:0x3 # other

# secondary classes and filters (priority)
# classids 0x1:0x21-0x23 (private group); filters at 0x1:0x2
secondary_classes() {
 tc class add dev $DEV parent 0x1:0x$PARENT classid 0x1:0x${PARENT}1 hfsc ls m2 6bps rt m2 3072bps # 0 interactive
 tc class add dev $DEV parent 0x1:0x$PARENT classid 0x1:0x${PARENT}2 hfsc ls m1 5bps d 20s m2 4bps rt m2 1024bps # 1 best-effort,int-bulk
 tc class add dev $DEV parent 0x1:0x$PARENT classid 0x1:0x${PARENT}3 hfsc ls m1 4bps d 7s m2 3bps rt m2 1024bps # 2 filler,bulk
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip protocol 1 0xff flowid 0x1:0x${PARENT}1
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip protocol 58 0xff flowid 0x1:0x${PARENT}1
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x10 0xff flowid 0x1:0x${PARENT}1
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x12 0xff flowid 0x1:0x${PARENT}1
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x14 0xff flowid 0x1:0x${PARENT}1
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x16 0xff flowid 0x1:0x${PARENT}1
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x00 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x04 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x06 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x18 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x1a 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x1c 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 1 u32 match ip tos 0x1e 0xff flowid 0x1:0x${PARENT}2
 tc filter add dev $DEV parent 0x1:0x$PARENT protocol ip prio 2 u32 match ip src 0.0.0.0/0 flowid 0x1:0x${PARENT}3
}

PARENT=2 # private
for DEV in eth0 eth1 eth2 eth3 wlan0 ; do
 secondary_classes
done

PARENT=3 # public
for DEV in eth0 wlan0_0 ; do
 secondary_classes
done

PARENT=4 # router
for DEV in eth0 eth1 eth2 eth3 wlan0 wlan0_0 ; do
 secondary_classes
done


For bandwidth testing, you could create /usr/local/sbin/bandwidth-test.sh:

 #!/bin/sh  # What markup will make this line correct?
set -e

MAXTIME=30

# get previous limits
UP_OLD=$( cat /var/local/up-bytes.txt 2> /dev/null ) || true
DOWN_OLD=$( cat /var/local/down-bytes.txt 2> /dev/null ) || true

# set limits high for testing
echo 2072576 >| /var/local/up-bytes.txt
echo 2072576 >| /var/local/down-bytes.txt

# set shaping rates
shaping.sh rates

# start measuring
OUTPUT=$( mktemp )
FILTERED=$( mktemp )
bwm-ng -T max -I eth0 -o csv -c 0 >> $OUTPUT &
MON_PID=$!

# Use the network as much as possible, both up and down, here
 TMPFILE=$( mktemp /tmp/testfile_rand_5m.XXX )
 dd if=/dev/urandom of=$TMPFILE bs=1048576 count=16 2> /dev/null &
 DD_PID=$!
## DOWN
 echo "starting DOWN"
 curl --ipv4 --silent --max-time $MAXTIME "http://linux.mirrors.es.net/fedora/releases/12/Fedora/i386/iso/Fedora-12-i386-disc1.iso" > /dev/null || true &
  DOWN1_PID=$!
 curl --ipv4 --silent --max-time $MAXTIME "http://nas1.itc.virginia.edu/fedora/releases/12/Fedora/i386/iso/Fedora-12-i386-DVD.iso" > /dev/null || true &
  DOWN2_PID=$!
 wait $DOWN1_PID
 wait $DOWN2_PID
 echo "finished DOWN"
## UP
 sleep 2s
 wait $DD_PID
 echo "starting UP"
 curl --ipv4 --silent --max-time $MAXTIME --form file=@$TMPFILE http://www.senduit.com/ > /dev/null || true &
  UP1_PID=$!
 wait $UP1_PID
 echo "finished UP"
 rm -f $TMPFILE

# stop measuring
kill -s INT $MON_PID
wait $MON_PID
tail -n 2 < $OUTPUT | grep eth0 >> $FILTERED

# Write new limits
gawk -F ";" '{ print $3 }' < $FILTERED | sed -r 's/\..*$//' >| /var/local/up-bytes.txt
gawk -F ";" '{ print $4 }' < $FILTERED | sed -r 's/\..*$//' >| /var/local/down-bytes.txt

# Apply new limits
shaping.sh rates

rm -f $OUTPUT
rm -f $FILTERED

rsyslogd

Logging everything to rsyslog and then to only a few files helps keep logging simpler and disk usage more easily controlled. To see a particular facility or priority, use grep.

First, a log rotation script /usr/local/sbin/clogrotate.sh:

 #!/bin/sh  # What markup will make this line correct?
# rotates channel logs; executed by rsyslog (see rsyslog.conf)
mv -f $1 ${1}.1
#gzip ${1}.1  # disabled because I'm running on Flash media

And run:  chmod u+x /usr/local/sbin/clogrotate.sh 


rsyslog.conf:

# [modules]
$ModLoad imuxsock # provides support for local system logging
$ModLoad imklog   # provides kernel logging support

# [templates]
$template FullFileFormat,"%timestamp:::date-rfc3339% %syslogfacility-text%.%syslogseverity-text% %syslogtag%%msg%\n"
$ActionFileDefaultTemplate FullFileFormat

# [log file permissions]
$FileOwner root
$FileGroup adm
$FileCreateMode 0640
$DirCreateMode 0755
$Umask 0022

# [channels]
$outchannel systemlog,/var/log/systemlog,104857600,clogrotate.sh /var/log/systemlog
$outchannel errlog,/var/log/errlog,26214400,clogrotate.sh /var/log/errlog

# [main]
$IncludeConfig /etc/rsyslog.d/*.conf
*.emerg                         *
*.err                           $errlog
*.notice                        $systemlog

Finish

Make those scripts executable:  chmod u+x /usr/local/sbin/* 

/etc/rc.local should look like this:

 #!/bin/sh  # What markup will make this line correct?
failed() {
 /usr/bin/logger -p local0.debug -t "`basename $0`[$$]" "$*"
 echo "$*" | mail -s "Startup failure" root
}

Save the firewall scripts:
{{{ build-firewall.sh save 

iptables-restore /etc/network/iptables || failed "iptables" ip6tables-restore /etc/network/ip6tables || failed "ip6tables" /usr/local/sbin/shaping.sh start || failed "shaping.sh" }}}

Contact/Comments

Feedback is appreciated.

You can contact me at greenfreedom10@gmail.com.