{# Macro import #} {% from 'OPNsense/Macros/interface.macro' import physical_interface %} {# collect interfaces list (with / without captive portal enabled) #} {% set cp_interface_list = [] %} {% set no_cp_interface_list = [] %} {% if helpers.exists('OPNsense.captiveportal.zones.zone') %} {% for intf_key,interface in interfaces.items()%} {% set is_cp=[] %} {% for cp_item in helpers.toList('OPNsense.captiveportal.zones.zone') %} {% for cp_intf in cp_item.interfaces.split(',') %} {% if intf_key == cp_intf %} {% if cp_item.enabled|default('0') == '1' %} {% do cp_interface_list.append({'zone':cp_item.description, 'zoneid':cp_item.zoneid,'if':interface.if, 'obj':cp_item}) %} {% do is_cp.append(1) %} {% endif %} {% endif %} {% endfor %} {% endfor %} {% if not is_cp%} {% do no_cp_interface_list.append(interface) %} {% endif %} {% endfor %} {% else %} {% for intf_key,interface in interfaces.items() %} {% do no_cp_interface_list.append(interface) %} {% endfor %} {% endif %} #====================================================================================== # flush ruleset #====================================================================================== flush #====================================================================================== # define dummynet pipes #====================================================================================== {% if helpers.exists('OPNsense.TrafficShaper.pipes.pipe') %} {% for pipe in helpers.toList('OPNsense.TrafficShaper.pipes.pipe') %} pipe {{ pipe.number }} config bw {{ pipe.bandwidth }}{{ pipe.bandwidthMetric }}/s{% if pipe.queue %} queue {{ pipe.queue }}{% if pipe.queueMetric != 'slots' %}{{pipe.queueMetric}}{% endif %}{% endif %}{% if pipe.buckets %} buckets {{ pipe.buckets }}{% endif %}{% if pipe.mask != 'none' %} mask {{ pipe.mask }} 0xffffffff {% endif %} type {% if pipe.scheduler|default('') != '' %} {{pipe.scheduler}} {% else %} wf2q+ {% endif %}{% if pipe.delay|default('') != '' %} delay {{pipe.delay}} {% endif %}{% if pipe.codel_enable|default('0') == '1' and pipe.scheduler != 'fq_codel' %} codel {% endif %}{% if pipe.codel_enable|default('0') == '1' or pipe.scheduler == 'fq_codel' %}{% if pipe.codel_target|default('') != ''%} target {{pipe.codel_target}} {% endif %}{% if pipe.codel_interval|default('') != ''%} interval {{pipe.codel_interval}} {% endif %}{% if pipe.codel_ecn_enable|default('0') == '1'%} ecn {% else %} noecn {% endif %} {% if pipe.scheduler == 'fq_codel' %} {% if pipe.fqcodel_quantum|default('') != '' %} quantum {{pipe.fqcodel_quantum}} {% endif %} {% if pipe.fqcodel_limit|default('') != '' %} limit {{pipe.fqcodel_limit}} {% endif %} {% if pipe.fqcodel_flows|default('') != '' %} flows {{pipe.fqcodel_flows}} {% endif %} {% endif %}{% elif pipe.pie_enable|default('0') == '1' and pipe.scheduler != 'fq_pie' %} pie {% endif %} {% endfor %} {% endif %} #====================================================================================== # define dummynet queues #====================================================================================== {% if helpers.exists('OPNsense.TrafficShaper.queues.queue') %} {% for queue in helpers.toList('OPNsense.TrafficShaper.queues.queue') %} {% if helpers.getUUIDtag(queue.pipe) in ['pipe'] %} queue {{ queue.number }} config pipe {{ helpers.getUUID(queue.pipe).number }}{% if queue.buckets %} buckets {{ queue.buckets }}{% endif %}{% if queue.mask != 'none' %} mask {{ queue.mask }} 0xffffffff {% endif %} weight {{ queue.weight }}{% if queue.codel_enable|default('0') == '1' %} codel {% if queue.codel_target|default('') != ''%} target {{queue.codel_target}} {% endif %}{% if queue.codel_interval|default('') != ''%} interval {{queue.codel_interval}} {% endif %}{% if queue.codel_ecn_enable|default('0') == '1'%} ecn {% else %} noecn {% endif %}{% elif queue.pie_enable|default('0') == '1' %} pie {% endif %} {% endif %} {% endfor %} {% endif %} #====================================================================================== # general purpose rules 1...1000 #====================================================================================== add 100 allow pfsync from any to any add 110 allow carp from any to any # layer 2: pass ARP add 120 pass layer2 mac-type arp,rarp # OPNsense requires for WPA add 130 pass layer2 mac-type 0x888e,0x88c7 # PPP Over Ethernet Session Stage/Discovery Stage add 140 pass layer2 mac-type 0x8863,0x8864 # layer 2: block anything else non-IP(v4/v6) add 150 deny layer2 not mac-type ip,ipv6 # allow traffic send from localhost add 200 skipto 60000 ipv6 from ::1 to any add 201 skipto 60000 ipv4 from 127.0.0.0/8 to any add 202 skipto 60000 ipv6 from any to ::1 add 203 skipto 60000 ipv4 from any to 127.0.0.0/8 #====================================================================================== # Allow traffic to this host #====================================================================================== {% for item in cp_interface_list %} add {{loop.index + 1000}} skipto 60000 udp from any to me dst-port 53 via {{item.if}} keep-state add {{loop.index + 1000}} skipto 60000 ip from any to { 255.255.255.255 or me } in via {{item.if}} add {{loop.index + 1000}} skipto 60000 ip from { 255.255.255.255 or me } to any out via {{item.if}} add {{loop.index + 1000}} skipto 60000 icmp from { 255.255.255.255 or me } to any out via {{item.if}} icmptypes 0 add {{loop.index + 1000}} skipto 60000 icmp from any to { 255.255.255.255 or me } in via {{item.if}} icmptypes 8 {% endfor %} {% for item in cp_interface_list %} #=================================================================================== # zone {{item.zone}} ({{item.zoneid}}) / {{item.if}} configuration #=================================================================================== {# authenticated clients #} add {{3000 + item.zoneid|int }} skipto {{10001 + item.zoneid|int * 1000 }} ip from table({{item.zoneid|int}}) to any via {{item.if}} add {{3000 + item.zoneid|int }} skipto {{10001 + item.zoneid|int * 1000 }} ip from any to table({{item.zoneid|int}}) via {{item.if}} {% endfor %} #====================================================================================== # redirect non-authenticated clients to captive portal @ local port 8000 + zoneid #====================================================================================== {% for item in cp_interface_list %} add {{5000 + item.zoneid|int }} fwd 127.0.0.1,{{ item.zoneid|int + 8000 }} tcp from any to any dst-port 443 in via {{item.if}} add {{5000 + item.zoneid|int }} allow ip from any to any dst-port 443 via {{item.if}} add {{5000 + item.zoneid|int }} fwd 127.0.0.1,{{ item.zoneid|int + 9000 }} tcp from any to any dst-port 80 in via {{item.if}} add {{5000 + item.zoneid|int }} allow ip from any to any dst-port 80 via {{item.if}} {% endfor %} #====================================================================================== # accept traffic from all interfaces not used by captive portal #====================================================================================== # let the responses from the captive portal web server back out add 6000 skipto 60000 tcp from any to any out # forward unauthorized traffic from captiveportal interfaces to block rule {% for item in cp_interface_list %} add {{6001 + loop.index }} skipto 65534 all from any to any via {{item.if}} {% endfor %} # send all the rest to the traffic shaper rules add 6199 skipto 60000 all from any to any #====================================================================================== # setup zone accounting section #====================================================================================== {% for item in cp_interface_list %} # zone {{item.zone}} ({{item.zoneid}}) add {{ (item.zoneid|int * 1000) + 10001 }} count ip from any to any via {{item.if}} add {{ (item.zoneid|int * 1000) + 10998 }} skipto 30000 all from any to any via {{item.if}} add {{ (item.zoneid|int * 1000) + 10999 }} deny all from any to any not via {{item.if}} {% endfor %} #====================================================================================== # setup accounting section, first rule is counting all CP traffic #====================================================================================== add 30000 set 0 count ip from any to any #====================================================================================== # traffic shaping section, authorized traffic #====================================================================================== add 60000 return via any {% if helpers.exists('OPNsense.TrafficShaper.rules.rule') %} {% for rule in helpers.toList('OPNsense.TrafficShaper.rules.rule', 'sequence', 'int') %} {% if helpers.getUUIDtag(rule.target) in ['pipe','queue'] %} {% if physical_interface(rule.interface) and rule.enabled|default('0') == '1' %} {% if helpers.getUUID(rule.target).enabled|default('0') == '1' %} {% if helpers.getUUIDtag(rule.target) == 'pipe' or helpers.getUUID(helpers.getUUID(rule.target).pipe).enabled|default('0') == '1' %} {% if rule.interface2 and physical_interface(rule.interface2) %} {# 2 interface defined, use both to match packets (2 rules) #} {% if rule.direction == 'in' or not rule.direction %} add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{ helpers.getUUID(rule.target).number }} {{ rule.proto.split('_')[0] }} from {% if rule.source_not|default('0') == '1' %}not {% endif %}{{ rule.source }} to {% if rule.destination_not|default('0') == '1' %}not {% endif %}{{rule.destination }} src-port {{ rule.src_port }} dst-port {{ rule.dst_port }} recv {{ physical_interface(rule.interface) }} {% if rule.proto.split('_')[1]|default('') == 'ack' %} {{ rule.proto.split('_')[2]|default('') }} tcpflags ack {% endif %}{% if rule.dscp|default('') != '' %} dscp {{ rule.dscp }}{% endif %} xmit {{physical_interface(rule.interface2) }} // {{rule.interface}} -> {{rule.interface2}}: {{helpers.getUUID(rule.target).description}} {% endif %} {% if rule.direction == 'out' or not rule.direction %} add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{ helpers.getUUID(rule.target).number }} {{ rule.proto.split('_')[0] }} from {% if rule.source_not|default('0') == '1' %}not {% endif %}{{ rule.source }} to {% if rule.destination_not|default('0') == '1' %}not {% endif %}{{rule.destination }} src-port {{ rule.src_port }} dst-port {{ rule.dst_port }} xmit {{ physical_interface(rule.interface) }} {% if rule.proto.split('_')[1]|default('') == 'ack' %} {{ rule.proto.split('_')[2]|default('') }} tcpflags ack {% endif %}{% if rule.dscp|default('') != '' %} dscp {{ rule.dscp }}{% endif %} recv {{physical_interface(rule.interface2) }} // {{rule.interface2}} -> {{rule.interface}}: {{helpers.getUUID(rule.target).description}} {% endif %} {% else %} {# normal, single interface situation #} add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{ helpers.getUUID(rule.target).number }} {{ rule.proto.split('_')[0] }} from {% if rule.source_not|default('0') == '1' %}not {% endif %}{{ rule.source }} to {% if rule.destination_not|default('0') == '1' %}not {% endif %}{{rule.destination }} src-port {{ rule.src_port }} dst-port {{ rule.dst_port }} {{rule.direction}} {% if rule.proto.split('_')[1]|default('') == 'ack' %}{{ rule.proto.split('_')[2]|default('') }} tcpflags ack {% endif %} {% if rule.dscp|default('') != '' %} dscp {{ rule.dscp }}{% endif %} via {{ physical_interface(rule.interface) }} // {{rule.interface}}: {{helpers.getUUID(rule.target).description}} {% endif %} {% endif %} {% endif %} {% endif %} {% endif %} {% endfor %} {% endif %} {% include "OPNsense/IPFW/ipfw.fw.conf" ignore missing with context %} # pass authorized add 65533 pass ip from any to any # block all unmatched add 65534 deny all from any to any