d7dfdb0ca2446613e4f3b92547048674d65aef6e
[project/firewall4.git] / root / usr / share / firewall4 / main.uc
1 {%
2
3 let fw4 = require("fw4");
4
5 /* Find existing sets.
6 *
7 * Unfortunately, terse mode (-t) is incompatible with JSON output so
8 * we either need to parse a potentially huge JSON just to get the set
9 * header data or scrape the ordinary nft output to obtain the same
10 * information. Opt for the latter to avoid parsing potentially huge
11 * JSON documents.
12 */
13 function find_existing_sets() {
14 let fd = fs.popen("nft -t list sets inet", "r");
15
16 if (!fd) {
17 warn(sprintf("Unable to execute 'nft' for listing existing sets: %s\n",
18 fs.error()));
19 return {};
20 }
21
22 let line, table, set;
23 let sets = {};
24
25 while ((line = fd.read("line")) !== "") {
26 let m;
27
28 if ((m = match(line, /^table inet (.+) \{\n$/)) != null) {
29 table = m[1];
30 }
31 else if ((m = match(line, /^\tset (.+) \{\n$/)) != null) {
32 set = m[1];
33 }
34 else if ((m = match(line, /^\t\ttype (.+)\n$/)) != null) {
35 if (table == "fw4" && set)
36 sets[set] = split(m[1], " . ");
37
38 set = null;
39 }
40 }
41
42 fd.close();
43
44 return sets;
45 }
46
47 function read_state() {
48 let state = fw4.read_state();
49
50 if (!state) {
51 warn("Unable to read firewall state - do you need to start the firewall?\n");
52 exit(1);
53 }
54
55 return state;
56 }
57
58 function reload_sets() {
59 let state = read_state(),
60 sets = find_existing_sets();
61
62 for (let set in state.ipsets) {
63 if (!set.loadfile || !length(set.entries))
64 continue;
65
66 if (!exists(sets, set.name)) {
67 warn(sprintf("Named set '%s' does not exist - do you need to restart the firewall?\n",
68 set.name));
69 continue;
70 }
71 else if (fw4.concat(sets[set.name]) != fw4.concat(set.types)) {
72 warn(sprintf("Named set '%s' has a different type - want '%s' but is '%s' - do you need to restart the firewall?\n",
73 set.name, fw4.concat(set.types), fw4.concat(sets[set.name])));
74 continue;
75 }
76
77 let first = true;
78 let printer = (entry) => {
79 if (first) {
80 print("add element inet fw4 ", set.name, " {\n");
81 first = false;
82 }
83
84 print("\t", join(" . ", entry), ",\n");
85 };
86
87 print("flush set inet fw4 ", set.name, "\n");
88
89 map(set.entries, printer);
90 fw4.parse_setfile(set, printer);
91
92 if (!first)
93 print("}\n\n");
94 }
95 }
96
97 function render_ruleset(use_statefile) {
98 let devices = [];
99
100 fw4.load(use_statefile);
101
102 map(fw4.zones(), zone => push(devices, ...zone.match_devices));
103
104 include("templates/ruleset.uc", { fw4, type, exists, length, include, devices: sort(devices) });
105 }
106
107 function lookup_network(net) {
108 let state = read_state();
109
110 for (let zone in state.zones) {
111 for (let network in (zone.network || [])) {
112 if (network.device == net) {
113 print(zone.name, "\n");
114 exit(0);
115 }
116 }
117 }
118
119 exit(1);
120 }
121
122 function lookup_device(dev) {
123 let state = read_state();
124
125 for (let zone in state.zones) {
126 for (let rule in (zone.match_rules || [])) {
127 if (dev in rule.devices_pos) {
128 print(zone.name, "\n");
129 exit(0);
130 }
131 }
132 }
133
134 exit(1);
135 }
136
137 function lookup_zone(name, dev) {
138 let state = read_state();
139
140 for (let zone in state.zones) {
141 if (zone.name == name) {
142 let devices = [];
143 map(zone.match_rules, (r) => push(devices, ...(r.devices_pos || [])));
144
145 if (dev) {
146 if (dev in devices) {
147 print(dev, "\n");
148 exit(0);
149 }
150
151 exit(1);
152 }
153
154 if (length(devices))
155 print(join("\n", devices), "\n");
156
157 exit(0);
158 }
159 }
160
161 exit(1);
162 }
163
164
165 switch (getenv("ACTION")) {
166 case "start":
167 return render_ruleset(true);
168
169 case "print":
170 return render_ruleset(false);
171
172 case "reload-sets":
173 return reload_sets();
174
175 case "network":
176 return lookup_network(getenv("OBJECT"));
177
178 case "device":
179 return lookup_device(getenv("OBJECT"));
180
181 case "zone":
182 return lookup_zone(getenv("OBJECT"), getenv("DEVICE"));
183 }