snort3: fix issue caused by ucode semantics change
[feed/packages.git] / net / snort3 / files / main.uc
1 {%
2 //------------------------------------------------------------------------------
3 // Copyright (c) 2023-2024 Eric Fahlgren <eric.fahlgren@gmail.com>
4 // SPDX-License-Identifier: GPL-2.0
5 //
6 // The tables defined using 'config_item' are the source of record for the
7 // configuration file, '/etc/config/snort'. If you wish to add new items,
8 // do that only in the tables and propagate that use into the templates.
9 //
10 //------------------------------------------------------------------------------
11
12 QUIET; // Reference globals passed from CLI, so we get errors when missing.
13 TYPE;
14
15 import { cursor } from 'uci';
16 let uci = cursor();
17
18 function wrn(fmt, ...args) {
19 if (QUIET)
20 exit(1);
21
22 let msg = "ERROR: " + sprintf(fmt, ...args);
23
24 if (getenv("TTY"))
25 warn(`\033[33m${msg}\033[m\n`);
26 else
27 warn(`[!] ${msg}\n`);
28 exit(1);
29 }
30
31 function rpad(str, fill, len)
32 {
33 str = rtrim(str) + ' ';
34 while (length(str) < len) {
35 str += fill;
36 }
37 return str;
38 }
39
40 //------------------------------------------------------------------------------
41
42 const ConfigItem = {
43 contains: function(value) {
44 // Check if the value is contained in the listed values,
45 // depending on the item type.
46 switch (this.type) {
47 case "enum":
48 return value in this.values;
49 case "range":
50 return value >= this.values[0] && value <= this.values[1];
51 default:
52 return true;
53 }
54 },
55
56 allowed: function() {
57 // Show a pretty version of the possible values, for error messages.
58 switch (this.type) {
59 case "enum":
60 return "one of [" + join(", ", this.values) + "]";
61 case "range":
62 return `${this.values[0]} <= x <= ${this.values[1]}`;
63 case "path":
64 return "a path string";
65 case "str":
66 return "a string";
67 default:
68 return "???";
69 }
70 },
71 };
72
73 function config_item(type, values, def) {
74 // If no default value is provided explicity, then values[0] is used as default.
75 if (! type in [ "enum", "range", "path", "str" ]) {
76 wrn(`Invalid item type '${type}', must be one of "enum", "range", "path" or "str".`);
77 return;
78 }
79 if (type == "enum") {
80 // Convert values to strings, so 'in' works in 'contains'.
81 values = map(values, function(i) { return "" + i; });
82 }
83 if (type == "range" && (length(values) != 2 || values[0] > values[1])) {
84 wrn(`A 'range' type item must have exactly 2 values in ascending order.`);
85 return;
86 }
87 // Maybe check 'path' values for existence???
88
89 return proto({
90 type: type,
91 values: values,
92 default: def ?? values[0],
93 }, ConfigItem);
94 };
95
96 const snort_config = {
97 enabled: config_item("enum", [ 0, 1 ], 0), // Defaults to off, so that user must configure before first start.
98 manual: config_item("enum", [ 0, 1 ], 1), // Allow user to manually configure, legacy behavior when enabled.
99 oinkcode: config_item("str", [ "" ]), // User subscription oinkcode. Much more in 'snort-rules' script.
100 home_net: config_item("str", [ "" ], "192.168.1.0/24"),
101 external_net: config_item("str", [ "" ], "any"),
102
103 config_dir: config_item("path", [ "/etc/snort" ]), // Location of the base snort configuration files.
104 temp_dir: config_item("path", [ "/var/snort.d" ]), // Location of all transient snort config, including downloaded rules.
105 log_dir: config_item("path", [ "/var/log" ]), // Location of the generated logs, and oh-by-the-way the snort PID file (why?).
106 logging: config_item("enum", [ 0, 1 ], 1),
107 openappid: config_item("enum", [ 0, 1 ], 0),
108
109 mode: config_item("enum", [ "ids", "ips" ]),
110 method: config_item("enum", [ "pcap", "afpacket", "nfq" ]),
111 action: config_item("enum", [ "default", "alert", "block", "drop", "reject" ]),
112 interface: config_item("str", [ uci.get("network", "wan", "device") ]),
113 snaplen: config_item("range", [ 1518, 65535 ]), // int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
114
115 include: config_item("path", [ "" ]), // User-defined snort configuration, applied at end of snort.lua.
116 };
117
118 const nfq_config = {
119 queue_count: config_item("range", [ 1, 16 ], 4), // Count of queues to allocate in nft chain when method=nfq, usually 2-8.
120 queue_start: config_item("range", [ 1, 32768], 4), // Start of queue numbers in nftables.
121 queue_maxlen: config_item("range", [ 1024, 65536 ], 1024), // --daq-var queue_maxlen=int
122 fanout_type: config_item("enum", [ "hash", "lb", "cpu", "rollover", "rnd", "qm"], "hash"), // See below.
123 thread_count: config_item("range", [ 0, 32 ], 0), // 0 = use cpu count
124 chain_type: config_item("enum", [ "prerouting", "input", "forward", "output", "postrouting" ], "input"),
125 chain_priority: config_item("enum", [ "raw", "filter", "300"], "filter"),
126 include: config_item("path", [ "" ]), // User-defined rules to include inside queue chain.
127 };
128
129
130 let _snort_config_doc =
131 "
132 This is not an exhaustive list of configuration items, just those that
133 require more explanation than is given in the tables that define them, below.
134
135 https://openwrt.org/docs/guide-user/services/snort
136
137 snort
138 manual - When set to 1, use manual configuration for legacy behavior.
139 When disabled, then use this config.
140 interface - Default should usually be 'uci get network.wan.device',
141 something like 'eth0'
142 home_net - IP range/ranges to protect. May be 'any', but more likely it's
143 your lan range, default is '192.168.1.0/24'
144 external_net - IP range external to home. Usually 'any', but if you only
145 care about true external hosts (trusting all lan devices),
146 then '!$HOME_NET' or some specific range
147 mode - 'ids' or 'ips', for detection-only or prevention, respectively
148 oinkcode - https://www.snort.org/oinkcodes
149 config_dir - Location of the base snort configuration files. Default /etc/snort
150 temp_dir - Location of all transient snort config, including downloaded rules
151 Default /var/snort.d
152 logging - Enable external logging of events thus enabling 'snort-mgr report',
153 otherwise events only go to system log (i.e., 'logread -e snort:')
154 log_dir - Location of the generated logs, and oh-by-the-way the snort
155 PID file (why?). Default /var/log
156 openappid - Enabled inspection using the 'openappid' package
157 See 'opkg info openappid'
158 action - Override the specified action of your rules. One of 'default',
159 'alert', 'block', 'reject' or 'drop', where 'default' means use
160 the rule as defined and don't override.
161 method - 'pcap', 'afpacket' or 'nfq'
162 snaplen - int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
163 include - User-defined snort configuration, applied at end of generated snort.lua
164
165 nfq - https://github.com/snort3/libdaq/blob/master/modules/nfq/README.nfq.md
166 queue_maxlen - nfq's '--daq-var queue_maxlen=int'
167 queue_count - Count of queues to use when method=nfq, usually 2-8
168 fanout_type - Sets kernel load balancing algorithm*, one of hash, lb, cpu,
169 rollover, rnd, qm.
170 thread_count - int snort.-z: <count> maximum number of packet threads
171 (same as --max-packet-threads); 0 gets the number of
172 CPU cores reported by the system; default is 1 { 0:max32 }
173 chain_type - Chain type when generating nft output
174 chain_priority - Chain priority when generating nft output
175 include - Full path to user-defined extra rules to include inside queue chain
176
177 * - for details on fanout_type, see these pages:
178 https://github.com/florincoras/daq/blob/master/README
179 https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
180 ";
181
182 function snort_config_doc(comment) {
183 if (comment == null) comment = "";
184 if (comment != "") comment += " ";
185 for (let line in split(_snort_config_doc, "\n")) {
186 let msg = rtrim(sprintf("%s%s", comment, line));
187 print(msg, "\n");
188 }
189 }
190
191 //------------------------------------------------------------------------------
192
193 function load(section, config) {
194 let self = {
195 ".name": section,
196 ".config": config,
197 };
198
199 // Set the defaults from definitions in table.
200 for (let item in config) {
201 self[item] = config[item].default;
202 }
203
204 // Overwrite them with any uci config settings.
205 let cfg = uci.get_all("snort", section);
206 for (let item in cfg) {
207 // If you need to rename, delete or change the meaning of a
208 // config item, just intercept it and do the work here.
209
210 if (exists(config, item)) {
211 let val = cfg[item];
212 if (config[item].contains(val))
213 self[item] = val;
214 else {
215 wrn(`In option ${item}='${val}', must be ${config[item].allowed()}`);
216 // ??? self[item] = config[item][0]; ???
217 }
218 }
219 }
220
221 return self;
222 }
223
224 let snort = null;
225 let nfq = null;
226 function load_all() {
227 snort = load("snort", snort_config);
228 nfq = load("nfq", nfq_config);
229 }
230
231 function dump_config(settings) {
232 let section = settings[".name"];
233 let config = settings[".config"];
234 printf("config %s '%s'\n", section, section);
235 for (let item in config) {
236 printf("\toption %-15s %-17s# %s\n", item, `'${settings[item]}'`, config[item].allowed());
237 }
238 print("\n");
239 }
240
241 function render_snort() {
242 include("templates/snort.uc", { snort, nfq, rpad });
243 }
244
245 function render_nftables() {
246 include("templates/nftables.uc", { snort, nfq, rpad });
247 }
248
249 function render_config() {
250 snort_config_doc("#");
251 dump_config(snort);
252 dump_config(nfq);
253 }
254
255 function render_help() {
256 snort_config_doc();
257 }
258
259 //------------------------------------------------------------------------------
260
261 load_all();
262
263 let table_type = TYPE; // Supply on cli with '-D TYPE=snort'...
264 switch (table_type) {
265 case "snort":
266 render_snort();
267 return;
268
269 case "nftables":
270 render_nftables();
271 return;
272
273 case "config":
274 render_config();
275 return;
276
277 case "help":
278 render_help();
279 return;
280
281 default:
282 print(`Invalid table type '${table_type}', should be one of snort, nftables, config, help.\n`);
283 return;
284 }
285
286 //------------------------------------------------------------------------------
287 -%}