* Added experimental version of DHCP-Splash for Kamikaze
authorSteven Barth <steven@midlink.org>
Sat, 26 Apr 2008 17:14:22 +0000 (17:14 +0000)
committerSteven Barth <steven@midlink.org>
Sat, 26 Apr 2008 17:14:22 +0000 (17:14 +0000)
* Added MAC-Address matching to luci_fw
* Added interface alias hack for Kamikaze
* ffluci.sys: Added several networking helper functions
* ffluci.http: Added function remote_addr
* Updated Haserl to 0.9.24

19 files changed:
contrib/package-source/haserl-lua-0.9.23.tar.bz2 [deleted file]
contrib/package-source/haserl-lua-0.9.24.tar.bz2 [new file with mode: 0644]
contrib/package/ffluci/Makefile
contrib/package/haserl-lua/Makefile
contrib/package/luci-splash/Makefile [new file with mode: 0644]
contrib/package/luci-splash/ipkg/conffiles [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash.init [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash.uci [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash/htdocs/cgi-bin/index.cgi [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash/htdocs/index.html [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash/splash.lua [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash/sync.lua [new file with mode: 0644]
contrib/package/luci-splash/src/luci_splash_httpd.conf [new file with mode: 0644]
core/contrib/hotplug.d-20-aliases [new file with mode: 0644]
core/src/ffluci/http.lua
core/src/ffluci/sys.lua
module/admin-core/contrib/init.d/luci_fw
module/admin-core/src/controller/admin/uci.lua
module/admin-core/src/model/cbi/admin_network/firewall.lua

diff --git a/contrib/package-source/haserl-lua-0.9.23.tar.bz2 b/contrib/package-source/haserl-lua-0.9.23.tar.bz2
deleted file mode 100644 (file)
index d48e653..0000000
Binary files a/contrib/package-source/haserl-lua-0.9.23.tar.bz2 and /dev/null differ
diff --git a/contrib/package-source/haserl-lua-0.9.24.tar.bz2 b/contrib/package-source/haserl-lua-0.9.24.tar.bz2
new file mode 100644 (file)
index 0000000..95fdc4c
Binary files /dev/null and b/contrib/package-source/haserl-lua-0.9.24.tar.bz2 differ
index 837df78..32afd4a 100644 (file)
@@ -43,9 +43,11 @@ define Package/ffluci/install
        $(INSTALL_DIR) $(1)/etc/config
        $(INSTALL_DIR) $(1)/etc/init.d
        $(INSTALL_DIR) $(1)/sbin
+       $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
        
        $(CP) $(PKG_BUILD_DIR)/core/dist/* $(1)/usr/lib/lua/ -R
        $(CP) $(PKG_BUILD_DIR)/core/contrib/uci/* $(1)/etc/config/
+       $(CP) $(PKG_BUILD_DIR)/core/contrib/hotplug.d-20-aliases $(1)/etc/hotplug.d/iface -R
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/core/contrib/ffluci $(1)/www/cgi-bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/core/contrib/ffluci-upload $(1)/www/cgi-bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/core/contrib/index.cgi $(1)/www/cgi-bin
index 6d656db..ed4279b 100644 (file)
@@ -7,13 +7,13 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=haserl-lua
-PKG_VERSION:=0.9.23
+PKG_VERSION:=0.9.24
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=http://dev.leipzig.freifunk.net/svn/ff-luci/trunk/contrib/package-source/ \
        http://firmware.freifunk-halle.net/ffluci/package-source/
-PKG_MD5SUM:=f3a0f329904791c5662d93638f92bdfd
+PKG_MD5SUM:=b004005594b84e35839b1d5c330f8e03
 
 
 include $(INCLUDE_DIR)/package.mk
diff --git a/contrib/package/luci-splash/Makefile b/contrib/package/luci-splash/Makefile
new file mode 100644 (file)
index 0000000..e1e194c
--- /dev/null
@@ -0,0 +1,36 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-splash
+PKG_VERSION:=0.1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
+PKG_INSTALL_DIR:=$(PKG_BUILD_DIR)/ipkg-install
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-splash
+  SECTION:=admin
+  CATEGORY:=Administration
+  SUBMENU:=ffluci
+  DEPENDS:=+ffluci
+  TITLE:=FFLuCI DHCP-Splash
+endef
+
+define Package/luci-splash/install
+       $(INSTALL_DIR) $(1)/usr/lib/luci_splash/htdocs
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_DIR) $(1)/etc/init.d
+       
+       $(CP) -a ./src/luci_splash/* $(1)/usr/lib/luci_splash/ -R
+       $(INSTALL_BIN) ./src/luci_splash/sync.lua $(1)/usr/lib/luci_splash
+       $(INSTALL_BIN) ./src/luci_splash/htdocs/cgi-bin/index.cgi $(1)/usr/lib/luci_splash/htdocs/cgi-bin
+       
+       $(CP) -a ./src/luci_splash.init $(1)/etc/init.d/luci_splash
+       $(CP) -a ./src/luci_splash.uci $(1)/etc/config/luci_splash
+       $(CP) -a ./src/luci_splash_httpd.conf $(1)/etc/
+       
+       $(CP) -a ./ipkg/conffiles $(1)/CONTROL/conffiles
+endef
+
+$(eval $(call BuildPackage,luci-splash))
diff --git a/contrib/package/luci-splash/ipkg/conffiles b/contrib/package/luci-splash/ipkg/conffiles
new file mode 100644 (file)
index 0000000..dcbe1ad
--- /dev/null
@@ -0,0 +1 @@
+/etc/config/luci_splash
\ No newline at end of file
diff --git a/contrib/package/luci-splash/src/luci_splash.init b/contrib/package/luci-splash/src/luci_splash.init
new file mode 100644 (file)
index 0000000..049d121
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/sh /etc/rc.common
+START=70
+
+iface_add() {
+       local cfg="$1"
+       
+       config_get net "$cfg" network
+       [ -n "$net" ] || return 0
+       
+       config_get iface "$net" ifname
+       [ -n "$iface" ] || return 0
+       iface="${iface%%:*}"
+       
+       config_get ipaddr "$net" ipaddr
+       [ -n "$ipaddr" ] || return 0
+       
+       config_get netmask "$net" netmask
+       [ -n "$netmask" ] || return 0
+       
+       eval "$(ipcalc.sh $ipaddr $netmask)"
+       
+       iptables -t nat -A luci_splash -i "$iface" -s "$IP/$PREFIX" -j luci_splash_portal       
+}
+
+blacklist_add() {
+       local cfg="$1"
+       
+       config_get mac "$cfg" mac
+       [ -n "$mac" ] && iptables -t nat -A luci_splash_portal -m mac --mac-source "$mac" -j REJECT
+}
+
+whitelist_add() {
+       local cfg="$1"
+       
+       config_get mac "$cfg" mac
+       [ -n "$mac" ] && iptables -t nat -A luci_splash_portal -m mac --mac-source "$mac" -j RETURN
+}
+
+start() {
+       ### Read chains from config
+       include /lib/network
+       scan_interfaces
+       config_load luci_splash
+       
+       ### Create subchains
+       iptables -t nat -N luci_splash
+       iptables -t nat -N luci_splash_portal
+       iptables -t nat -N luci_splash_leases
+       
+       ### Build the main rule
+       config_foreach iface_add iface
+       
+       ### Build the portal rule
+       config_foreach blacklist_add blacklist
+       config_foreach whitelist_add whitelist
+       iptables -t nat -A luci_splash_portal -j luci_splash_leases
+       
+       ### Build the leases rule
+       iptables -t nat -A luci_splash_leases -p tcp --dport 80 -j REDIRECT --to-ports 8082
+       iptables -t nat -A luci_splash_leases -j REJECT
+       
+       ### Start the splash httpd
+       httpd -c /etc/luci_splash_httpd.conf -p 8082 -h /usr/lib/luci_splash/htdocs
+       
+       ### Sync leases
+       /usr/lib/luci_splash/sync.lua
+       
+       ### Hook in the chain
+       iptables -t nat -A prerouting_rule -j luci_splash
+}
+
+stop() {
+       ### Hook out the chain
+       iptables -t nat -D prerouting_rule -j luci_splash
+       
+       ### Clear subchains
+       iptables -t nat -F luci_splash
+       iptables -t nat -F luci_splash_portal
+       iptables -t nat -F luci_splash_leases
+       
+       ### Delete subchains
+       iptables -t nat -X luci_splash
+       iptables -t nat -X luci_splash_portal
+       iptables -t nat -X luci_splash_leases
+}
+
diff --git a/contrib/package/luci-splash/src/luci_splash.uci b/contrib/package/luci-splash/src/luci_splash.uci
new file mode 100644 (file)
index 0000000..cf1232b
--- /dev/null
@@ -0,0 +1,8 @@
+config iface
+       option  network wldhcp
+       
+config whitelist
+       option mac      02:CA:FF:EE:BA:BE
+       
+config blacklist
+       option mac      03:CA:FF:EE:BA:BE
\ No newline at end of file
diff --git a/contrib/package/luci-splash/src/luci_splash/htdocs/cgi-bin/index.cgi b/contrib/package/luci-splash/src/luci_splash/htdocs/cgi-bin/index.cgi
new file mode 100644 (file)
index 0000000..0117198
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/haserl --shell=luac
+dofile("/usr/lib/luci_splash")
+
+require("ffluci.template")
+
+function dispatch()
+       local mac = get_usermac()
+       if not mac then
+               return action_nodata()
+       end
+       
+       if isblacklisted(mac) then
+               return action_blocked()
+       end
+       
+       if iswhitelisted(mac) or haslease(mac) then
+               return action_allowed()
+       end
+
+       return action_splash(mac)
+end
+
+function action_splash(mac)
+       if ffluci.http.formvalue("activate") then
+               add_lease(mac)
+               ffluci.http.textheader()
+               print("Got splashed!")
+       else
+               ffluci.http.textheader()
+               print("Get splashed!")
+       end
+end
+
+function action_allowed()
+       ffluci.http.textheader()
+       print("Already allowed!")
+end
+
+function action_blocked()
+       ffluci.http.textheader()
+       print("Blocked!")
+end
+
+function action_nodata()
+       ffluci.http.textheader()
+       print("No data!")
+end
+
+dispatch()
\ No newline at end of file
diff --git a/contrib/package/luci-splash/src/luci_splash/htdocs/index.html b/contrib/package/luci-splash/src/luci_splash/htdocs/index.html
new file mode 100644 (file)
index 0000000..58387a5
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="refresh" content="0; URL=/cgi-bin/index.cgi" />
+</head>
+<body style="background-color: black">
+<a style="color: white; text-decoration: none" href="/cgi-bin/index.cgi">FFLuCI - Freifunk Lua Configuration Interface</a>
+</body>
+</html>
\ No newline at end of file
diff --git a/contrib/package/luci-splash/src/luci_splash/splash.lua b/contrib/package/luci-splash/src/luci_splash/splash.lua
new file mode 100644 (file)
index 0000000..b659d20
--- /dev/null
@@ -0,0 +1,74 @@
+package.path  = "/usr/lib/lua/?.lua;/usr/lib/lua/?/init.lua;" .. package.path
+package.cpath = "/usr/lib/lua/?.so;" .. package.cpath
+
+require("ffluci.http")
+require("ffluci.sys")
+require("ffluci.model.uci")
+
+ucis = ffluci.model.uci.Session("/var/state")
+
+function add_lease(mac)
+       local key = ucis:add("luci_splash", "lease")
+       ucis:set("luci_splash", key, "mac", mac)
+       add_rule(mac)
+end
+
+function add_rule(mac)
+       return os.execute("iptables -t nat -I luci_splash_leases -m mac --source-mac '"..mac.."' -j RETURN")
+end
+
+function remove_rule(mac)
+       return os.execute("iptables -t nat -D luci_splash_leases -m mac --source-mac '"..mac.."' -j RETURN")
+end
+
+function get_usermac()
+       local ip  = ffluci.http.remote_addr()
+       local mac = nil
+       
+       for i, l in ipairs(ffluci.sys.net.arptable()) do
+               if l["IP address"] == ip then
+                       mac = l["HW address"]
+               end
+       end
+       
+       return mac
+end
+
+function haslease(mac)
+       mac = mac:lower()
+       local list = ucis:show("luci_splash").luci_splash
+       
+       for k, v in pairs(list) do
+               if v[".type"] == "lease" and v.mac and v.mac:lower() == mac then
+                       return true
+               end
+       end
+       
+       return false
+end
+
+function isblacklisted(mac)
+       mac = mac:lower()
+       local list = ucis:show("luci_splash").luci_splash
+       
+       for k, v in pairs(list) do
+               if v[".type"] == "blacklist" and v.mac and v.mac:lower() == mac then
+                       return true
+               end
+       end
+       
+       return false
+end
+
+function iswhitelisted(mac)
+       mac = mac:lower()
+       local list = ucis:show("luci_splash").luci_splash
+       
+       for k, v in pairs(list) do
+               if v[".type"] == "whitelist" and v.mac and v.mac:lower() == mac then
+                       return true
+               end
+       end
+       
+       return false
+end
\ No newline at end of file
diff --git a/contrib/package/luci-splash/src/luci_splash/sync.lua b/contrib/package/luci-splash/src/luci_splash/sync.lua
new file mode 100644 (file)
index 0000000..fd32a1c
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/haserl --shell=luac --accept-none
+dofile("splash.lua")
+
diff --git a/contrib/package/luci-splash/src/luci_splash_httpd.conf b/contrib/package/luci-splash/src/luci_splash_httpd.conf
new file mode 100644 (file)
index 0000000..f481c22
--- /dev/null
@@ -0,0 +1 @@
+E404:/index.html
\ No newline at end of file
diff --git a/core/contrib/hotplug.d-20-aliases b/core/contrib/hotplug.d-20-aliases
new file mode 100644 (file)
index 0000000..b9986e3
--- /dev/null
@@ -0,0 +1,24 @@
+add_aliases() {
+        local config="$1"
+
+        config_get base "$INTERFACE" ifname
+        config_get iface "$config" ifname
+        config_get ipaddr "$config" ipaddr
+        config_get auto "$config" auto
+
+        [ "${iface%%:*}" == "$base" -a "$iface" != "$base" ] && {
+                case "$auto" in
+                        1|on|enabled) setup_interface "$iface" "$config";;
+                        *) return 1;;
+                esac
+        }
+}
+
+
+case "$ACTION" in
+        ifup)
+                include /lib/network
+                scan_interfaces
+                config_foreach "add_aliases" interface
+        ;;
+esac
index 06e1c43..44d1a92 100644 (file)
@@ -59,6 +59,12 @@ function request_redirect(category, module, action, ...)
 end
 
 
+-- Returns the User's IP
+function remote_addr()
+       return ENV.REMOTE_ADDR
+end
+
+
 -- Returns the script name
 function script_name()
        return ENV.SCRIPT_NAME
index 44855f8..77d45cf 100644 (file)
@@ -115,18 +115,14 @@ group = {}
 group.getgroup = posix.getgroup
 
 net = {}
+-- Returns the ARP-Table
+function net.arptable()
+       return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
+end
+
 -- Returns whether an IP-Adress belongs to a certain net
-function net.belongs(ip, net)
-       local netparts = ffluci.util.split(net, "/")
-       
-       if #netparts ~= 2 then
-               return nil
-       end
-       
-       local binadr = net.ip4bin(ip)
-       local binnet = net.ip4bin(netparts[1])
-       
-       return (binadr:sub(1, netparts[2]) == binnet:sub(1, netparts[2]))
+function net.belongs(ip, net, prefix)
+       return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(net):sub(1, prefix))
 end
 
 -- Returns all available network interfaces
@@ -138,6 +134,17 @@ function net.devices()
        return devices
 end
 
+-- Returns the prefix to a given netmask
+function net.mask4prefix(mask)
+       local bin = net.ip4bin(mask)
+       
+       if not bin then
+               return nil
+       end
+       
+       return #ffluci.util.split(bin, "1")-1
+end
+
 -- Returns the kernel routing table
 function net.routes()
        return _parse_delimited_table(io.lines("/proc/net/route"))
@@ -267,7 +274,7 @@ end
 -- Internal functions
 
 function _parse_delimited_table(iter, delimiter)
-       delimiter = delimiter or "\t+"
+       delimiter = delimiter or "%s+"
        
        local data  = {}
        local trim  = ffluci.util.trim
index 880c87d..e98b3f7 100644 (file)
@@ -18,13 +18,13 @@ apply_portfw() {
        ip=$(echo $to | cut -d: -f1)
        
        if ([ "$proto" == "tcpudp" ] || [ "$proto" == "tcp" ]); then
-               iptables -t nat -A luci_prerouting -i "$iface" -p tcp --dport "$dport" -j DNAT --to "$to"
-               iptables -A luci_forward -i "$iface" -p tcp -d "$ip" $ports -j ACCEPT
+               iptables -t nat -A luci_fw_prerouting -i "$iface" -p tcp --dport "$dport" -j DNAT --to "$to"
+               iptables -A luci_fw_forward -i "$iface" -p tcp -d "$ip" $ports -j ACCEPT
        fi
 
        if ([ "$proto" == "tcpudp" ] || [ "$proto" == "udp" ]); then
-               iptables -t nat -A luci_prerouting -i "$iface" -p udp --dport "$dport" -j DNAT --to "$to"
-               iptables -A luci_forward -i "$iface" -p udp -d "$ip" $ports -j ACCEPT
+               iptables -t nat -A luci_fw_prerouting -i "$iface" -p udp --dport "$dport" -j DNAT --to "$to"
+               iptables -A luci_fw_forward -i "$iface" -p udp -d "$ip" $ports -j ACCEPT
        fi
 }
 
@@ -34,11 +34,11 @@ apply_rule() {
 
        config_get chain "$cfg" chain
        [ -n "$chain" ] || return 0
-       [ "$chain" == "forward" ] && cmd="$cmd -A luci_forward"
-       [ "$chain" == "input" ] && cmd="$cmd -A luci_input"
-       [ "$chain" == "output" ] && cmd="$cmd -A luci_output"
-       [ "$chain" == "prerouting" ] && cmd="$cmd -t nat -A luci_prerouting"
-       [ "$chain" == "postrouting" ] && cmd="$cmd -t nat -A luci_postrouting"
+       [ "$chain" == "forward" ] && cmd="$cmd -A luci_fw_forward"
+       [ "$chain" == "input" ] && cmd="$cmd -A luci_fw_input"
+       [ "$chain" == "output" ] && cmd="$cmd -A luci_fw_output"
+       [ "$chain" == "prerouting" ] && cmd="$cmd -t nat -A luci_fw_prerouting"
+       [ "$chain" == "postrouting" ] && cmd="$cmd -t nat -A luci_fw_postrouting"
        
        config_get iface "$cfg" iface
        [ -n "$iface" ] && cmd="$cmd -i $iface" 
@@ -66,6 +66,9 @@ apply_rule() {
 
        config_get tosrc "$cfg" tosrc
        [ -n "$tosrc" ] && cmd="$cmd --to-source $tosrc"        
+       
+       config_get mac "$cfg" mac
+       [ -n "$mac" ] && cmd="$cmd -m mac --mac-source $mac"
 
        config_get jump "$cfg" jump
        [ -n "$jump" ] && cmd="$cmd -j $jump"   
@@ -78,18 +81,18 @@ apply_rule() {
 
 start() {
        ### Create subchains
-       iptables -N luci_input
-       iptables -N luci_output
-       iptables -N luci_forward
-       iptables -t nat -N luci_prerouting
-       iptables -t nat -N luci_postrouting
+       iptables -N luci_fw_input
+       iptables -N luci_fw_output
+       iptables -N luci_fw_forward
+       iptables -t nat -N luci_fw_prerouting
+       iptables -t nat -N luci_fw_postrouting
        
        ### Hook in the chains
-       iptables -A input_rule -j luci_input
-       iptables -A output_rule -j luci_output
-       iptables -A forwarding_rule -j luci_forward
-       iptables -t nat -A prerouting_rule -j luci_prerouting
-       iptables -t nat -A postrouting_rule -j luci_postrouting
+       iptables -A input_rule -j luci_fw_input
+       iptables -A output_rule -j luci_fw_output
+       iptables -A forwarding_rule -j luci_fw_forward
+       iptables -t nat -A prerouting_rule -j luci_fw_prerouting
+       iptables -t nat -A postrouting_rule -j luci_fw_postrouting
        
        ### Read chains from config
        config_load luci_fw
@@ -99,23 +102,23 @@ start() {
 
 stop() {
        ### Hook out the chains
-       iptables -D input_rule -j luci_input
-       iptables -D output_rule -j luci_output
-       iptables -D forwarding_rule -j luci_forward
-       iptables -t nat -D prerouting_rule -j luci_prerouting
-       iptables -t nat -D postrouting_rule -j luci_postrouting 
+       iptables -D input_rule -j luci_fw_input
+       iptables -D output_rule -j luci_fw_output
+       iptables -D forwarding_rule -j luci_fw_forward
+       iptables -t nat -D prerouting_rule -j luci_fw_prerouting
+       iptables -t nat -D postrouting_rule -j luci_fw_postrouting      
        
        ### Clear subchains
-       iptables -F luci_input
-       iptables -F luci_output
-       iptables -F luci_forward
-       iptables -t nat -F luci_prerouting
-       iptables -t nat -F luci_postrouting
+       iptables -F luci_fw_input
+       iptables -F luci_fw_output
+       iptables -F luci_fw_forward
+       iptables -t nat -F luci_fw_prerouting
+       iptables -t nat -F luci_fw_postrouting
        
        ### Delete subchains
-       iptables -X luci_input
-       iptables -X luci_output
-       iptables -X luci_forward
-       iptables -t nat -X luci_prerouting
-       iptables -t nat -X luci_postrouting
+       iptables -X luci_fw_input
+       iptables -X luci_fw_output
+       iptables -X luci_fw_forward
+       iptables -t nat -X luci_fw_prerouting
+       iptables -t nat -X luci_fw_postrouting
 }
index 3c9fc87..2d19db1 100644 (file)
@@ -14,7 +14,7 @@ function action_apply()
                for i, line in ipairs(ffluci.util.split(changes)) do
                        local r = line:match("^-?([^.]+)")
                        if r then
-                               apply[r] = true
+                               table.insert(apply, ffluci.config.uci_oncommit[r])
                        end
                end
                
@@ -23,8 +23,7 @@ function action_apply()
                
                -- Search for post-commit commands
                if ffluci.config.uci_oncommit then
-                       for k, v in pairs(apply) do
-                               local cmd = ffluci.config.uci_oncommit[k]
+                       for i, cmd in ipairs(apply) do
                                if cmd then
                                        output = output .. cmd .. ":" .. ffluci.sys.exec(cmd)
                                end
index c926626..4ff15db 100644 (file)
@@ -24,6 +24,7 @@ proto:value("udp", "UDP")
 
 s:option(Value, "source", "Quelladresse").optional = true
 s:option(Value, "destination", "Zieladresse").optional = true
+s:option(Value, "mac", "MAC-Adresse").optional = true
 
 sport = s:option(Value, "sport", "Quellport")
 sport.optional = true