aboutsummaryrefslogtreecommitdiff
path: root/net/snort3/files/main.uc
blob: 7db420f339d04f5193648de9d2c0f0cd8d2eb605 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
{%
//------------------------------------------------------------------------------
// Copyright (c) 2023 Eric Fahlgren <eric.fahlgren@gmail.com>
// SPDX-License-Identifier: GPL-2.0
//
// The tables defined using 'config_item' are the source of record for the
// configuration file, '/etc/config/snort'.  If you wish to add new items,
// do that only in the tables and propagate that use into the templates.
//
//------------------------------------------------------------------------------

import { cursor } from 'uci';
let uci = cursor();

function wrn(fmt, ...args) {
	if (getenv("QUIET"))
		exit(1);

	let msg = "ERROR: " + sprintf(fmt, ...args);

	if (getenv("TTY"))
		warn(`\033[33m${msg}\033[m\n`);
	else
		warn(`[!] ${msg}\n`);
	exit(1);
}

//------------------------------------------------------------------------------

function config_item(type, values, def) {
	// If no default value is provided explicity, then values[0] is used as default.
	if (! type in [ "enum", "range", "path", "str" ]) {
		wrn(`Invalid item type '${type}', must be one of "enum", "range", "path" or "str".`);
		return;
	}
	if (type == "range" && (length(values) != 2 || values[0] > values[1])) {
		wrn(`A 'range' type item must have exactly 2 values in ascending order.`);
		return;
	}
	// Maybe check paths for existence???
		
	return {
		type:     type,
		values:   values,
		default:  def ?? values[0],

		contains: function(value) {
			// Check if the value is contained in the listed values,
			// depending on the item type.
			switch (this.type) {
			case "enum":
				return value in this.values;
			case "range":
				return value >= this.values[0] && value <= this.values[1];
			default:
				return true;
			}
		},

		allowed: function() {
			// Show a pretty version of the possible values, for error messages.
			switch (this.type) {
			case "enum":
				return "one of [" + join(", ", this.values) + "]";
			case "range":
				return `${this.values[0]} <= x <= ${this.values[1]}`;
			case "path":
				return "a path string";
			case "str":
				return "a string";
			default:
				return "???";
			}
		},
	}
};

const snort_config = {
	enabled:         config_item("enum",  [ 0, 1 ], 0),         // Defaults to off, so that user must configure before first start.
	manual:          config_item("enum",  [ 0, 1 ], 1),         // Allow user to manually configure, legacy behavior when enabled.
	oinkcode:        config_item("str",   [ "" ]),              // User subscription oinkcode.  Much more in 'snort-rules' script.
	home_net:        config_item("str",   [ "" ], "192.168.1.0/24"),
	external_net:    config_item("str",   [ "" ], "any"),

	config_dir:      config_item("path",  [ "/etc/snort" ]),    // Location of the base snort configuration files.
	temp_dir:        config_item("path",  [ "/var/snort.d" ]),  // Location of all transient snort config, including downloaded rules.
	log_dir:         config_item("path",  [ "/var/log" ]),      // Location of the generated logs, and oh-by-the-way the snort PID file (why?).
	logging:         config_item("enum",  [ 0, 1 ], 1),
	openappid:       config_item("enum",  [ 0, 1 ], 0),

	mode:            config_item("enum",  [ "ids", "ips" ]),
	method:          config_item("enum",  [ "pcap", "afpacket", "nfq" ]),
	action:          config_item("enum",  [ "alert", "block", "drop", "reject" ]),
	interface:       config_item("str",   [ uci.get("network", "wan", "device") ]),
	snaplen:         config_item("range", [ 1518, 65535 ]),     // int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
};

const nfq_config = {
	queue_count:     config_item("range", [ 1, 16 ], 4),           // Count of queues to allocate in nft chain when method=nfq, usually 2-8.
	queue_start:     config_item("range", [ 1, 32768], 4),         // Start of queue numbers in nftables.
	queue_maxlen:    config_item("range", [ 1024, 65536 ], 1024),  // --daq-var queue_maxlen=int
	fanout_type:     config_item("enum",  [ "hash", "lb", "cpu", "rollover", "rnd", "qm"], "hash"), // See below.
	thread_count:    config_item("range", [ 0, 32 ], 0),           // 0 = use cpu count
	chain_type:      config_item("enum",  [ "prerouting", "input", "forward", "output", "postrouting" ], "input"),
	chain_priority:  config_item("enum",  [ "raw", "filter", "300"], "filter"),
	include:         config_item("path",  [ "" ]),                 // User-defined rules to include inside queue chain.
};


let _snort_config_doc =
"
This is not an exhaustive list of configuration items, just those that
require more explanation than is given in the tables that define them, below.

https://openwrt.org/docs/guide-user/services/snort

snort
    manual          - When set to 1, use manual configuration for legacy behavior.
                      When disabled, then use this config.
    interface       - Default should usually be 'uci get network.wan.device',
                      something like 'eth0'
    home_net        - IP range/ranges to protect. May be 'any', but more likely it's
                      your lan range, default is '192.168.1.0/24'
    external_net    - IP range external to home.  Usually 'any', but if you only
                      care about true external hosts (trusting all lan devices),
                      then '!$HOMENET' or some specific range
    mode            - 'ids' or 'ips', for detection-only or prevention, respectively
    oinkcode        - https://www.snort.org/oinkcodes
    config_dir      - Location of the base snort configuration files.  Default /etc/snort
    temp_dir        - Location of all transient snort config, including downloaded rules
                      Default /var/snort.d
    logging         - Enable external logging of events thus enabling 'snort-mgr report',
                      otherwise events only go to system log (i.e., 'logread -e snort:')
    log_dir         - Location of the generated logs, and oh-by-the-way the snort
                      PID file (why?).  Default /var/log
    openappid       - Enabled inspection using the 'openappid' package
                      See 'opkg info openappid'
    action          - 'alert', 'block', 'reject' or 'drop'
    method          - 'pcap', 'afpacket' or 'nfq'
    snaplen         - int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }

nfq - https://github.com/snort3/libdaq/blob/master/modules/nfq/README.nfq.md
    queue_maxlen    - nfq's '--daq-var queue_maxlen=int'
    queue_count     - Count of queues to use when method=nfq, usually 2-8
    fanout_type     - Sets kernel load balancing algorithm*, one of hash, lb, cpu,
                      rollover, rnd, qm.
    thread_count    - int snort.-z: <count> maximum number of packet threads
                      (same as --max-packet-threads); 0 gets the number of
                      CPU cores reported by the system; default is 1 { 0:max32 }
    chain_type      - Chain type when generating nft output
    chain_priority  - Chain priority when generating nft output
    include         - Full path to user-defined extra rules to include inside queue chain

    * - for details on fanout_type, see these pages:
        https://github.com/florincoras/daq/blob/master/README
        https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
";

function snort_config_doc(comment) {
	if (comment == null) comment = "";
	if (comment != "") comment += " ";
	for (let line in split(_snort_config_doc, "\n")) {
		let msg = rtrim(sprintf("%s%s", comment, line));
		print(msg, "\n");
	}
}

//------------------------------------------------------------------------------

function load(section, config) {
	let self = {
		".name":   section,
		".config": config,
	};

	// Set the defaults from definitions in table.
	for (let item in config) {
		self[item] = config[item].default;
	}

	// Overwrite them with any uci config settings.
	let cfg = uci.get_all("snort", section);
	for (let item in cfg) {
		// If you need to rename, delete or change the meaning of a
		// config item, just intercept it and do the work here.

		if (exists(config, item)) {
			let val = cfg[item];
			if (config[item].contains(val))
				self[item] = val;
			else {
				wrn(`In option ${item}='${val}', must be ${config[item].allowed()}`);
				// ??? self[item] = config[item][0]; ???
			}
		}
	}

	return self;
}

let snort = null;
let nfq   = null;
function load_all() {
	snort = load("snort", snort_config);
	nfq   = load("nfq", nfq_config);
}

function dump_config(settings) {
	let section = settings[".name"];
	let config  = settings[".config"];
	printf("config %s '%s'\n", section, section);
	for (let item in config) {
		printf("\toption %-15s %-17s# %s\n", item, `'${settings[item]}'`, config[item].allowed());
	}
	print("\n");
}

function render_snort() {
	include("templates/snort.uc", { snort, nfq });
}

function render_nftables() {
	include("templates/nftables.uc", { snort, nfq });
}

function render_config() {
	snort_config_doc("#");
	dump_config(snort);
	dump_config(nfq);
}

function render_help() {
	snort_config_doc();
}

//------------------------------------------------------------------------------

load_all();

switch (getenv("TYPE")) {
	case "snort":
		render_snort();
		return;

	case "nftables":
		render_nftables();
		return;

	case "config":
		render_config();
		return;

	case "help":
		render_help();
		return;

	default:
		print("Invalid table type.\n");
		return;
}

//------------------------------------------------------------------------------
-%}