ifwatch.fnl (2054B)
1 (local { : system : join } (require :anoia)) 2 (local svc (require :anoia.svc)) 3 (local ll (require :lualinux)) 4 5 ;; ifwatch.fnl wan:/nix/store/eee/.outputs/ifname wan:/nix/store/ffff/.outputs/ifname lan:/nix/store/abc123/.outputs/ifname 6 7 (fn parse-options [cmdline] 8 (let [interfaces {}] 9 (each [_ s (ipairs cmdline)] 10 (let [(zone service) (string.match s "(.-):(.+)")] 11 (tset interfaces (svc.open service) zone))) 12 interfaces)) 13 14 (local POLLIN 1) 15 (local POLLHUP 16) 16 17 (fn zone-contents [interfaces] 18 (accumulate [zones {} 19 intf zone (pairs interfaces)] 20 (let [ifs (or (. zones zone) [])] 21 (table.insert ifs (intf:output "ifname")) 22 (tset zones zone ifs) 23 zones))) 24 25 (fn wait-for-change [interfaces] 26 (let [pollfds (icollect [k _ (pairs interfaces)] 27 (bor (lshift (k:fileno) 32) 28 (lshift (bor POLLIN POLLHUP) 16)))] 29 (ll.poll pollfds))) 30 31 (fn fail [msg] 32 (io.stderr:write (.. "ERROR: " msg "\n"))) 33 34 (macro with-popen [[handle command mode] & body] 35 `(let [,handle (assert (io.popen ,command ,mode)) 36 val# (do ,(unpack body))] 37 (case (: ,handle :close) 38 ok# val# 39 (nil :exit code#) (fail (.. ,command " exited " code#)) 40 (nil :signal sig#) (fail (.. ,command " killed by " sig#))))) 41 42 (fn update-zone-str [zone ifnames] 43 (if (> (# ifnames) 0) 44 (.. 45 "flush set ip table-ip " zone " ; add element ip table-ip " zone " { " (table.concat ifnames ", ") " };\n" 46 "flush set ip6 table-ip6 " zone " ; add element ip6 table-ip6 " zone " { " (table.concat ifnames ", ") " };\n" 47 ) 48 (.. 49 "flush set ip table-ip " zone "; \n" 50 "flush set ip6 table-ip6 " zone "; \n" 51 ))) 52 53 (fn run [] 54 (while true 55 (let [interfaces (parse-options arg)] 56 (with-popen [nft "nft -f -" :w] 57 (each [zone ifnames (pairs (zone-contents interfaces))] 58 (nft:write (update-zone-str zone ifnames)))) 59 (wait-for-change interfaces) 60 (each [k _ (pairs interfaces)] 61 (k:close))))) 62 63 { : run }