#!/bin/sh
# Copyright (C) 2024 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter
#
# nDPId consumer for omr-bypass.
# Reads flow detection events from the ndpisrvd distributor socket and adds
# destination IPs to the appropriate nftables named sets so that traffic
# classified by nDPId is bypassed according to omr-bypass protocol rules.
#
# Usage: ndpid-consumer <proto_list>
#   proto_list: space-separated entries of the form  proto:intf:intfid
#               as written by _bypass_proto_collect_ndpid in omr-bypass-nft.

PIDFILE="/var/run/omr-bypass-ndpid.pid"
SOCK="/var/run/ndpid/distributor.sock"

proto_list="$1"
[ -z "$proto_list" ] && exit 1

echo $$ > "$PIDFILE"

while true; do
	[ ! -S "$SOCK" ] && sleep 5 && continue
	nc -U "$SOCK" 2>/dev/null | while IFS= read -r line; do
		# nDPId frames are prefixed with a 5-character decimal length field
		json="${line:5}"
		[ -z "$json" ] && continue

		event=$(echo "$json" | jsonfilter -q -e '@.flow_event_name' 2>/dev/null)
		[ "$event" != "detected" ] && [ "$event" != "detection-update" ] && continue

		proto_str=$(echo "$json" | jsonfilter -q -e '@.ndpi.proto' 2>/dev/null)
		[ -z "$proto_str" ] && continue

		dst_ip=$(echo "$json" | jsonfilter -q -e '@.l3_dst4' 2>/dev/null)
		is_v6=0
		if [ -z "$dst_ip" ]; then
			dst_ip=$(echo "$json" | jsonfilter -q -e '@.l3_dst6' 2>/dev/null)
			[ -n "$dst_ip" ] && is_v6=1
		fi
		[ -z "$dst_ip" ] && continue

		# Normalize nDPI proto string (e.g. "TLS.Google" -> app="google", base="tls")
		app=$(echo "$proto_str" | tr '.' '\n' | tail -1 | tr '[:upper:]' '[:lower:]')
		base=$(echo "$proto_str" | tr '.' '\n' | head -1 | tr '[:upper:]' '[:lower:]')

		for entry in $proto_list; do
			bp=$(echo "$entry" | cut -d: -f1)
			intf=$(echo "$entry" | cut -d: -f2)
			bp_lower=$(echo "$bp" | tr '[:upper:]' '[:lower:]')
			if [ "$bp_lower" = "$app" ] || [ "$bp_lower" = "$base" ] || \
			   echo "$proto_str" | grep -qi "^${bp}$" || \
			   echo "$proto_str" | grep -qi "\.${bp}$" || \
			   echo "$proto_str" | grep -qi "^${bp}\."; then
				if [ "$is_v6" = "1" ]; then
					if [ "$intf" = "all" ]; then
						nft add element inet fw4 bypass6_${bp} { "$dst_ip" } 2>/dev/null
					else
						nft add element inet fw4 omr_dst_bypass_${intf}_6 { "$dst_ip" } 2>/dev/null
					fi
				else
					if [ "$intf" = "all" ]; then
						nft add element inet fw4 bypass_${bp} { "$dst_ip" } 2>/dev/null
					else
						nft add element inet fw4 omr_dst_bypass_${intf}_4 { "$dst_ip" } 2>/dev/null
					fi
				fi
			fi
		done
	done
	sleep 2
done
