diff --git a/backend/nfqueue/go.mod b/backend/nfqueue/go.mod index abb80e6..57db091 100644 --- a/backend/nfqueue/go.mod +++ b/backend/nfqueue/go.mod @@ -4,4 +4,7 @@ go 1.18 require github.com/DomySh/go-netfilter-queue v0.0.0-20220713124014-7261f0df2c15 -require github.com/google/gopacket v1.1.19 // indirect +require ( + github.com/Jemmic/go-pcre2 v0.0.0-20190111114109-bd52ad5f7098 // indirect + github.com/google/gopacket v1.1.19 // indirect +) diff --git a/backend/nfqueue/go.sum b/backend/nfqueue/go.sum index 8ade0b0..fa84d31 100644 --- a/backend/nfqueue/go.sum +++ b/backend/nfqueue/go.sum @@ -1,5 +1,7 @@ github.com/DomySh/go-netfilter-queue v0.0.0-20220713124014-7261f0df2c15 h1:6v9D8bG3oR0dJFMuEeEAg8Xwn436Ziv+P7QWS04wAG8= github.com/DomySh/go-netfilter-queue v0.0.0-20220713124014-7261f0df2c15/go.mod h1:VdJ6kqHln0XlrhuxQM6eBjRIHCzvAMgcZDAtyD/GU5s= +github.com/Jemmic/go-pcre2 v0.0.0-20190111114109-bd52ad5f7098 h1:ZwFIi+5jGJWVrB2V4NvrEhIUy6uDkfnTtBsgj3HAImI= +github.com/Jemmic/go-pcre2 v0.0.0-20190111114109-bd52ad5f7098/go.mod h1:c+8WT1L7lfohb4xMaa3yAV7nlYNepqc2ZV09/CU8R/U= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/backend/nfqueue/main.go b/backend/nfqueue/main.go index f29ce85..eb813f3 100644 --- a/backend/nfqueue/main.go +++ b/backend/nfqueue/main.go @@ -2,25 +2,98 @@ package main import ( "bufio" + "encoding/hex" "fmt" "log" "os" "os/user" "strconv" + "strings" "github.com/DomySh/go-netfilter-queue" + "github.com/Jemmic/go-pcre2" ) const QUEUE_BASE_NUM = 1000 const MAX_PACKET_IN_QUEUE = 100 -func handle_packets(packets <-chan netfilter.NFPacket) { - for true { - select { - case p := <-packets: - //fmt.Println(p.Packet) - p.SetVerdict(netfilter.NF_ACCEPT) +var FILTER_TABLE *regex_filters = ®ex_filters{} + +type regex_filters struct { + input_whitelist []*pcre2.Matcher + input_blacklist []*pcre2.Matcher + output_whitelist []*pcre2.Matcher + output_blacklist []*pcre2.Matcher +} + +func (self regex_filters) add(raw_regex string) { + filter_type := strings.ToLower(raw_regex[0:2]) + + decoded_regex, err := hex.DecodeString(raw_regex[2:]) + if err != nil { + log.Printf("[add] Unable to decode regex '%s': %s", raw_regex, err) + return + } + + regex, err := pcre2.Compile(string(decoded_regex), 0) + if err != nil { + log.Printf("[add] Unable to compile regex '%s': %s", string(decoded_regex), err) + return + } + if filter_type[0] == 'i' { + if filter_type[1] == '0' { + self.input_whitelist = append(self.input_whitelist, regex.NewMatcher()) + } else { + self.input_blacklist = append(self.input_blacklist, regex.NewMatcher()) } + } else { + if filter_type[1] == '0' { + self.output_whitelist = append(self.output_whitelist, regex.NewMatcher()) + } else { + self.output_blacklist = append(self.output_blacklist, regex.NewMatcher()) + } + } +} + +func (self regex_filters) check(data []byte, is_input bool) bool { + if is_input { + for _, matcher := range self.input_blacklist { + if matcher.Match(data, 0) { + return false + } + } + for _, matcher := range self.input_whitelist { + if !matcher.Match(data, 0) { + return false + } + } + } else { + for _, matcher := range self.output_blacklist { + if matcher.Match(data, 0) { + return false + } + } + for _, matcher := range self.output_whitelist { + if !matcher.Match(data, 0) { + return false + } + } + } + return true +} + +func handle_packets(packets <-chan netfilter.NFPacket, is_input bool) { + for true { + filter := FILTER_TABLE + p := <-packets + log.Printf("Packet received: %s input: %t", p.Packet.TransportLayer().LayerPayload(), is_input) + if filter.check(p.Packet.TransportLayer().LayerPayload(), is_input) { + p.SetVerdict(netfilter.NF_ACCEPT) + } else { + log.Printf("Refused packet: %s", p.Packet.TransportLayer().LayerPayload()) + p.SetVerdict(netfilter.NF_DROP) + } + } } @@ -32,21 +105,6 @@ func isRoot() bool { return currentUser.Username == "root" } -/* - -starts = QUEUE_BASE_NUM -while True: - if starts >= 65536: - raise Exception("Netfilter queue is full!") - queue_ids = list(range(starts,starts+n_threads)) - try: - ictor.start(func_wrap, queue_ids=queue_ids) - break - except interceptor.UnableToBindException as e: - starts = e.queue_id + 1 -return ictor, (starts, starts+n_threads-1) - -*/ func create_queue_seq(num int) ([]*netfilter.NFQueue, int, int) { var queue_list = make([]*netfilter.NFQueue, num) var err error @@ -54,7 +112,6 @@ func create_queue_seq(num int) ([]*netfilter.NFQueue, int, int) { for queue_list[0] == nil { if starts+num-1 >= 65536 { log.Fatalf("Netfilter queue is full!") - os.Exit(1) } for i := 0; i < len(queue_list); i++ { queue_list[i], err = netfilter.NewNFQueue(uint16(starts+num-1-i), MAX_PACKET_IN_QUEUE, netfilter.NF_DEFAULT_PACKET_SIZE) @@ -73,9 +130,9 @@ func create_queue_seq(num int) ([]*netfilter.NFQueue, int, int) { } func main() { + log.SetOutput(os.Stderr) if !isRoot() { log.Fatalf("[main] You must be root to run this program") - os.Exit(1) } number_of_queues := 1 @@ -85,7 +142,6 @@ func main() { number_of_queues, err = strconv.Atoi(os.Args[1]) if err != nil { log.Fatalf("[main] Invalid number of queues: %s", err) - os.Exit(1) } } @@ -93,13 +149,13 @@ func main() { queue_list, starts_input, end_input := create_queue_seq(number_of_queues) for _, queue := range queue_list { defer queue.Close() - go handle_packets(queue.GetPackets()) + go handle_packets(queue.GetPackets(), true) } queue_list, starts_output, end_output := create_queue_seq(number_of_queues) for _, queue := range queue_list { defer queue.Close() - go handle_packets(queue.GetPackets()) + go handle_packets(queue.GetPackets(), false) } fmt.Println("QUEUE INPUT", starts_input, end_input, "OUTPUT", starts_output, end_output) @@ -108,10 +164,22 @@ func main() { reader := bufio.NewReader(os.Stdin) for true { text, err := reader.ReadString('\n') + log.Printf("[main] Regex rule updating...") if err != nil { log.Fatalf("[main] Unable to read from stdin: %s", err) - os.Exit(1) } - fmt.Print(text) + text = strings.Trim(text, "\n") + regexes := strings.Split(text, " ") + + new_filters := regex_filters{} + for _, regex := range regexes { + regex = strings.Trim(regex, " ") + if len(regex) < 2 { + continue + } + new_filters.add(regex) + } + FILTER_TABLE = &new_filters + log.Printf("[main] Regex filter rules updated!") } }