Intel N150 can run snort3 on symmetrical 1000 MBit line without CPU saturation

I got an Intel N150 based mini PC a few month back to act as my OpenWrt router/firewall. Based on some tests I did on a RPi5B with snort3, I never bothered playing with snort3 on the Intel thinking it would cap the bandwidth.

I am pretty amazed that I can run snort3 with a full rule set (~40k rules) and still saturate a symmetrical 1000 MBit line.

Those numbers are ballpark for this connection (down ~980 Mbps and up ~650).

Just wanted to share this result for other snort-would-be users on x86 hardware. @efahl @xxxx

Output of htop -u snort during the above speed test:

EDIT: note to people reading this thread for the first time, pretty much ALL of the replies below questioning this result are due to no CPU load lead me to discover that I had to disable software off loading in order for all the packets to flow through snort.

The conclusion that the N150 is powerful enough to handle the connect is valid. The screenshot above has been updated to reflect the the test with it disabled.

Note that the above result was on an image compiled with the default C library implementation of musl which

2 Likes

Congrats, N150 is a little beast. I imagine SQM on a symmetric 1Gbit line would be easy for it too. Care to also run this Wireguard benchmark so we can see?

Post the result in A Wireguard comparison DB.

I'm not surprised my Celeron J4125 is enough to run my 290Mbit line with Wireguard and Snort at full bandwidth and it's from 2019. Depending on the Vpn load, I even get an A grade in the bufferbload test via the Vpn. But what else you could do is change “snortver=31470” to “snortver=3900” in the snort-rules script, with snort 3.7.x.x and snort 3.9.x.x there were Api updates with new rules I don't know if this has any influence.

I think the results are too good to be true. Below is my setup, could there be something misconfigured causing traffic to not be fully routed through snort?

In the box, eth0 is LAN facing (to a managed switch) and eth1 is WAN facing.

/etc/config/snort
config snort 'snort'
	option config_dir '/etc/snort/'
	option interface 'eth1
/etc/init.d/snort
#!/bin/sh /etc/rc.common

START=99
STOP=10

USE_PROCD=1
PROG=/usr/bin/snort

validate_snort_section() {
	uci_validate_section snort snort "${1}" \
		'config_dir:string' \
		'interface:string'
}

start_service() {
	local config_file interface

	validate_snort_section snort || {
		echo "validation failed"
		return 1
	}

	procd_open_instance
	procd_set_param env SNORT_LUA_PATH="$config_dir"
	procd_set_param command nice -n -20 $PROG -c "${config_dir%/}/snort.lua" --tweaks local
	fw4 reload
	procd_set_param file $CONFIGFILE
	procd_set_param respawn
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_close_instance
}

stop_service()
{
	service_stop ${PROG}
	nft delete chain inet fw4 IPS_output
	nft delete chain inet fw4 IPS_input
}

service_triggers()
{
	procd_add_reload_trigger "snort"
	procd_add_validation validate_snort_section
}
/etc/snort/homenet.lua
HOME_NET = [[ 10.9.8.0/24 10.9.7.0/24 10.9.6.0/24 10.9.5.0/24 ]]
EXTERNAL_NET = "!$HOME_NET"
/etc/snort/local.lua
snort  = {
	['-Q'] = true,
	['--max-packet-threads'] = 4,
}

suppress = {
	-- this kills stuff in lxc
	{
		gid = 1, sid = 650, track = 'by_dst', ip = '10.9.8.101'
	},
}

network = {
	checksum_eval = 'none',
}

daq = {
	module_dirs = { '/usr/lib/daq' },
	inputs = { '4', '5', '6', '7' },
	snaplen = 65531,
	modules = {
		{
			name = 'nfq',
			mode = 'inline',
			variables = {
				'queue_maxlen=8192',
				'fail_open',
				'device=eth1'
			}
		}
	}
}

ips = {
	mode = inline,
	variables = default_variables,
	action_override = 'drop',
	include = RULE_PATH .. '/snort.rules',
}

output.logdir = '/mnt/data'
alert_fast = {
	file = true,
	packet = false,
}

file_policy = {
	enable_type = true,
	enable_signature = true,
	rules = {
		use = {
			verdict = 'log', enable_file_type = true, enable_file_signature = true
		}
	}
}

search_engine = { 
	search_method = "hyperscan",
	offload_search_method ="hyperscan",
	detect_raw_tcp = true,
}

detection = { 
	hyperscan_literals = true,
	pcre_to_regex = true,
}
/etc/snort/snort-table.sh
#!/bin/sh

#verbose=false

nft list ruleset | grep -q 'IPS_' && nft delete chain inet fw4 IPS_output && nft delete chain inet fw4 IPS_input

nft -f - <<TABLE
table inet fw4 {
	chain IPS_output {
		type filter hook postrouting priority 225
		policy accept

		oifname { $(uci get network.wan.device),wg0 } tcp flags ack ct state established counter accept
		oifname $(uci get network.wan.device) udp dport 14500 counter accept
		oifname { $(uci get network.wan.device),wg0 } counter queue flags bypass to 4-7
	}

	chain IPS_input {
		type filter hook prerouting priority 300
		policy accept

		iifname $(uci get network.wan.device) udp sport 14500 ct state established counter accept
		iifname { $(uci get network.wan.device),wg0 } counter queue flags bypass to 4-7
	}
}
TABLE

#$verbose

exit 0

And at the end of /etc/config/firewall I have:

config include
  option enabled '1'
  option type 'script'
  option path '/etc/snort/snort-table.sh'
  option fw4_compatible '1'

For reference starting snort manually without the init.d script give this output:

snort.log
# snort -c /etc/snort/snort.lua --tweaks local
--------------------------------------------------
o")~   Snort++ 3.9.1.0
--------------------------------------------------
Loading /etc/snort/snort.lua:
Loading homenet.lua:
Finished homenet.lua:
Loading snort_defaults.lua:
Finished snort_defaults.lua:
Loading local.lua:
Finished local.lua:
	snort
	ssh
	host_cache
	pop
	so_proxy
	stream_tcp
	mms
	smtp
	gtp_inspect
	packets
	dce_http_proxy
	detection
	alert_fast
	suppress
	cip
	ips
	stream_icmp
	hosts
	normalizer
	binder
	wizard
	appid
	js_norm
	file_id
	http2_inspect
	http_inspect
	stream_udp
	ftp_data
	ftp_server
	search_engine
	port_scan
	dce_http_server
	dce_tcp
	dce_smb
	iec104
	telnet
	ssl
	sip
	rpc_decode
	netflow
	modbus
	host_tracker
	stream_user
	stream_ip
	process
	back_orifice
	classifications
	dnp3
	active
	trace
	ftp_client
	decode
	alerts
	stream
	references
	daq
	arp_spoof
	output
	network
	dns
	dce_udp
	imap
	file_policy
	s7commplus
	stream_file
Finished /etc/snort/snort.lua:
Loading file_id.rules_file:
Loading file_magic.rules:
Finished file_magic.rules:
Finished file_id.rules_file:
Loading rules/snort.rules:
Finished rules/snort.rules:
--------------------------------------------------
pcre counts
               pcre_rules: 11141
            pcre_to_hyper: 8540
              pcre_native: 2601
--------------------------------------------------
ips policies rule stats
              id  loaded  shared enabled    file
               0   40857       0   40857    /etc/snort/snort.lua
--------------------------------------------------
rule counts
       total rules loaded: 40857
               text rules: 40857
            option chains: 40857
            chain headers: 1695
                 flowbits: 701
     flowbits not checked: 82
--------------------------------------------------
port rule counts
             tcp     udp    icmp      ip
     any    1802     380     455     287
     src    1055     149       0       0
     dst    4498     827       0       0
    both      28      48       0       0
   total    7383    1404     455     287
--------------------------------------------------
service rule counts          to-srv  to-cli
                      bgp:        5       1
                   dcerpc:     1400    1055
                     dhcp:       19       5
                      dns:      269     111
                     drda:        5       0
                     file:      773     784
                      ftp:      194      23
                 ftp-data:     1070    9132
                   gopher:        0       1
                     http:    15289   12166
                    http2:    15289   12166
                    http3:    15289   12166
                    ident:        1       0
                     imap:     1124    9387
                      ipp:        0       1
                      irc:       40      14
                     ircd:        9       3
                 java_rmi:       55       3
                 kerberos:       32       6
                     ldap:       42       8
                      ldp:        1       0
                    mysql:        5       0
              netbios-dgm:        2       2
               netbios-ns:        8       4
              netbios-ssn:     1325    1055
                  netware:        2       0
                     nntp:        2       2
                      ntp:       36       7
                  openvpn:       16      16
                     pop3:     1081    9390
               postgresql:       10       0
                  printer:        3       1
                   radius:        3       2
                      rdp:        3       8
                     rtmp:        1       4
                      rtp:        1       1
                     rtsp:       17       2
                      sip:      340      44
                     smtp:     8398    1028
                     snmp:       46       9
                     ssdp:       13       0
                      ssh:       13       6
                      ssl:      176     208
                   sunrpc:      118       9
                   syslog:        4       0
                 teamview:        1       2
                   telnet:       34       2
                     tftp:        6       5
                      vnc:        1       1
               vnc-server:       12      10
                    total:    62583   68850
--------------------------------------------------
fast pattern groups
                      src: 478
                      dst: 1533
                      any: 8
                to_server: 125
                to_client: 95
--------------------------------------------------
search engine (hyperscan)
                instances: 1230
                 patterns: 145965
        fast pattern only: 95590
appid: MaxRss diff: 512
appid: patterns loaded: 300
--------------------------------------------------
nfq DAQ configured to inline.
Commencing packet processing
++ [0] 4
++ [1] 5
++ [2] 6
++ [3] 7

Again, the alert_fast txt files are getting populated over time.

# ls -lh *.txt            
-rw------- 1 root root 117K Jul 18 17:19 0_alert_fast.txt
-rw------- 1 root root    0 Jul 18 15:17 1_alert_fast.txt
-rw------- 1 root root 2.5K Jul 18 17:21 2_alert_fast.txt
-rw------- 1 root root 2.4K Jul 18 17:17 3_alert_fast.txt

That should be fine, I only work with 3 of 4 cores on mine. The only thing that makes me wonder are you really working in the 10.x.x.x area in the home network? Because Openwrt actually uses the 192.168.x.x range in the home network by default. If you want to be absolutely sure, I could build you an apk (use only the stable Openwrt version ipk) where everything is already configured so that Snort runs automatically in ips mode and nfq mode, you would then only have to change the oinkcode and the homenet in the config.

Loading all rules is anything but optimal, especially since there is also Filemagic, which reports every recognized gif and jpg, which is quite suboptimal.

This can't be right... during a speed test pulling 900 Mbps. How can snort not be using more CPU:

1 Like

I'll bet it's using Intel-specific bits of the hyperscan and gperftools suites that you integrated a while back. Those can make a huge impact on performance.

Right, that's a bit low even with hyperscan, could it be that “detect_raw_tcp = true” is not set in snort.lua? Have you loaded the appropriate rules for the Snort version?

search_engine = { 
		search_method = "hyperscan",
		offload_search_method ="hyperscan",
		detect_raw_tcp = true,
}   

I would provide you with my version but I haven't been able to build an apk yet again the same crap with the modules.builtin and when I use the trick that works with Openwrt 24.10.x the next error comes up. The build system is so broken it's just annoying....

@efahl That is clearly underutilization.

PYes, the appropriate rules have been loaded. You can see that in the output. Are you recommending removing the top raw line?

No detect_raw_tcp = true must be set otherwise it will not process all tcp packets. And you cannot see whether the correct rules are loaded because they also work for the older Snort versions for Snort 3.9.xx they must be loaded https://snort.org/rules/snortrules-snapshot-3900.tar.gz

//edit/ I managed to build an apk if you want to test. Uninstall Snort, please also delete the Snort folder on /etc and remove the entries from /etc/config/firewall. Then update the lists and install the apk via upload package add the oinkcode in /etc/config/snort-config.conf then run snort-update (otherwise only the community rules will be used for now). In the system log everything is now shown as if you start snort via the command line and the date rules are also shown. With snort-log -a you can display the snort logs with snort-log -app it shows you a summary of the appid logs with snort-log -sum a summary of the events and with snort-log -s term1 term2 you can search in the logs.

Your apk will not work for me as my image is built with glibc.

As a test, I built the snort package without gpertools and hyperscan. Same results (essentially CPU usage <1%). So something is not right.

Can you build my version with glibc? The build files are in the Snort3 folder in the Openwrt-snort3-testing repository. Yes, that is definitely too little, which also gave me the idea that snaplen 1518 is set instead of 65535 because it would only check the beginning of the package.

I can ... which in files/ should I customize to my system?

Theoretically none except maybe the HOME_NET in the Snort.lua if you use the 10.x.x.x Ip range in the home network and in the snort-config.conf the Oinkcode but both can also be done after the installation. Otherwise the package is built in such a way that it should automatically run perfectly with a 4 core.

Anything with firewall settings?

Remove the old entries in the firewall config, the old apk and the Snort folder before installation. The entries in the firewall are automatically added during installation and deleted again during uninstallation.

//edit/ And don't be surprised openappid is now listed as Depends.

Got some issues:

Sat Jul 19 11:56:18 2025 daemon.info root: Running Snort
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: cat: can't open '/tmp/rules/version.txt': No such file or directory
Sat Jul 19 11:56:18 2025 daemon.info root: Snort rules date:
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: cat: can't open '/tmp/rules/version-community.txt': No such file or directory
Sat Jul 19 11:56:18 2025 daemon.info root: Snort Community rules date:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: --------------------------------------------------
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: o")~   Snort++ 3.9.1.0
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: --------------------------------------------------
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading /etc/snort/snort.lua:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading snort_defaults.lua:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished snort_defaults.lua:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading security.lua:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished security.lua:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	alert_syslog
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	ssh
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	host_cache
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	pop
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	so_proxy
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream_tcp
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	mms
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	smtp
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	gtp_inspect
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	packets
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dce_http_proxy
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	alert_fast
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	suppress
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	ips
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream_icmp
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	hosts
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	normalizer
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	binder
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	wizard
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	reputation
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: Unable to open address file /etc/snort/rules/ipblocklist.txt, Error: No such file or directory
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	appid
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	js_norm
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	http2_inspect
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	http_inspect
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	ftp_data
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream_udp
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	ftp_server
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	search_engine
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dce_http_server
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dce_tcp
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dce_smb
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	iec104
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	cip
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	telnet
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	ssl
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	sip
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	rpc_decode
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	netflow
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	modbus
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	host_tracker
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream_user
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream_ip
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	detection
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	process
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	back_orifice
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	classifications
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dnp3
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	active
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	trace
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	ftp_client
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	decode
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	alerts
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	references
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	daq
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	output
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	network
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dns
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	dce_udp
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	imap
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	s7commplus
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: 	stream_file
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished /etc/snort/snort.lua:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading rule args:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading com.include:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading /etc/snort/builtins.rules:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished /etc/snort/builtins.rules:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Loading /tmp/rules/snort3-community.rules:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished /tmp/rules/snort3-community.rules:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished com.include:
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: Finished rule args:
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: ERROR: /tmp/rules/snort3-community.rules:1 unable to open rules file '/tmp/rules/snort3-community.rules': No such file or directory
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: --------------------------------------------------
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: ips policies rule stats
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:               id  loaded  shared enabled    file
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:                0       6       0       6    /etc/snort/snort.lua
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: --------------------------------------------------
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: rule counts
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:        total rules loaded: 6
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:             builtin rules: 6
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:             option chains: 6
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:             chain headers: 1
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: --------------------------------------------------
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: port rule counts
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:              tcp     udp    icmp      ip
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:      any       6       0       0       0
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]:    total       6       0       0       0
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: ERROR: appid: could not open /usr/lib/openappid//odp/appMapping.data
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: appid: MaxRss diff: 5524
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: appid: patterns loaded: 300
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: --------------------------------------------------
Sat Jul 19 11:56:18 2025 daemon.info snort-start[16545]: nfq DAQ configured to inline.
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: FATAL: see prior 2 errors (0 warnings)
Sat Jul 19 11:56:18 2025 daemon.err snort-start[16545]: Fatal Error, Quitting..

I ended up commented some lines in /etc/snort/snort.lua to get it working with my rules but the result was the same: no CPU use when doing a speed test :confused: