diff --git a/.gitignore b/.gitignore index 9593618..1c31cbd 100755 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /backend/db/firegex.db /backend/db/firegex.db-journal +/backend/nfqueue/main docker-compose.yml # misc diff --git a/backend/nfqueue/main b/backend/nfqueue/main index 74dc228..a902de0 100755 Binary files a/backend/nfqueue/main and b/backend/nfqueue/main differ diff --git a/backend/nfqueue/main.go b/backend/nfqueue/main.go index eb813f3..507bfc7 100644 --- a/backend/nfqueue/main.go +++ b/backend/nfqueue/main.go @@ -12,21 +12,37 @@ import ( "github.com/DomySh/go-netfilter-queue" "github.com/Jemmic/go-pcre2" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" ) const QUEUE_BASE_NUM = 1000 const MAX_PACKET_IN_QUEUE = 100 -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 +type regex_pair struct { + regex string + matcher *pcre2.Matcher } -func (self regex_filters) add(raw_regex string) { +type regex_filters struct { + input_whitelist []regex_pair + input_blacklist []regex_pair + output_whitelist []regex_pair + output_blacklist []regex_pair + regexes []*pcre2.Regexp +} + +func NewRegexFilter() *regex_filters { + res := new(regex_filters) + res.input_blacklist = make([]regex_pair, 0) + res.input_whitelist = make([]regex_pair, 0) + res.output_blacklist = make([]regex_pair, 0) + res.output_whitelist = make([]regex_pair, 0) + res.regexes = make([]*pcre2.Regexp, 0) + return res +} + +func (self *regex_filters) add(raw_regex string) { filter_type := strings.ToLower(raw_regex[0:2]) decoded_regex, err := hex.DecodeString(raw_regex[2:]) @@ -40,41 +56,46 @@ func (self regex_filters) add(raw_regex string) { log.Printf("[add] Unable to compile regex '%s': %s", string(decoded_regex), err) return } + self.regexes = append(self.regexes, regex) if filter_type[0] == 'i' { - if filter_type[1] == '0' { - self.input_whitelist = append(self.input_whitelist, regex.NewMatcher()) + if filter_type[1] == '1' { + self.input_whitelist = append(self.input_whitelist, regex_pair{raw_regex, regex.NewMatcher()}) } else { - self.input_blacklist = append(self.input_blacklist, regex.NewMatcher()) + self.input_blacklist = append(self.input_blacklist, regex_pair{raw_regex, regex.NewMatcher()}) } } else { - if filter_type[1] == '0' { - self.output_whitelist = append(self.output_whitelist, regex.NewMatcher()) + if filter_type[1] == '1' { + self.output_whitelist = append(self.output_whitelist, regex_pair{raw_regex, regex.NewMatcher()}) } else { - self.output_blacklist = append(self.output_blacklist, regex.NewMatcher()) + self.output_blacklist = append(self.output_blacklist, regex_pair{raw_regex, regex.NewMatcher()}) } } } -func (self regex_filters) check(data []byte, is_input bool) bool { +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) { + for _, rgx := range self.input_blacklist { + if rgx.matcher.Match(data, 0) { + fmt.Printf("BLOCKED %s\n", rgx.regex) return false } } - for _, matcher := range self.input_whitelist { - if !matcher.Match(data, 0) { + for _, rgx := range self.input_whitelist { + if !rgx.matcher.Match(data, 0) { + fmt.Printf("BLOCKED %s\n", rgx.regex) return false } } } else { - for _, matcher := range self.output_blacklist { - if matcher.Match(data, 0) { + for _, rgx := range self.output_blacklist { + if rgx.matcher.Match(data, 0) { + fmt.Printf("BLOCKED %s\n", rgx.regex) return false } } - for _, matcher := range self.output_whitelist { - if !matcher.Match(data, 0) { + for _, rgx := range self.output_whitelist { + if !rgx.matcher.Match(data, 0) { + fmt.Printf("BLOCKED %s\n", rgx.regex) return false } } @@ -82,18 +103,64 @@ func (self regex_filters) check(data []byte, is_input bool) bool { 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) - } +func (self *regex_filters) clear() { + for _, rgx := range self.input_whitelist { + rgx.matcher.Free() + } + for _, rgx := range self.input_blacklist { + rgx.matcher.Free() + } + for _, rgx := range self.output_whitelist { + rgx.matcher.Free() + } + for _, rgx := range self.output_blacklist { + rgx.matcher.Free() + } + for _, regex := range self.regexes { + regex.Free() + } +} +func handle_packets(packets <-chan netfilter.NFPacket, filter_table_channel chan regex_filters, is_input bool) { + filter_table := regex_filters{} + for true { + filter := filter_table + select { + case ft := <-filter_table_channel: + { + filter_table = ft + } + case p := <-packets: + { + transport_layer := p.Packet.TransportLayer() + data := transport_layer.LayerPayload() + if len(data) > 0 { + if filter.check(data, is_input) { + p.SetVerdict(netfilter.NF_ACCEPT) + } else { + if transport_layer.LayerType() == layers.LayerTypeTCP { + *p.Packet.ApplicationLayer().(*gopacket.Payload) = []byte{} + transport_layer.(*layers.TCP).Payload = []byte{} + transport_layer.(*layers.TCP).FIN = true + transport_layer.(*layers.TCP).SYN = false + transport_layer.(*layers.TCP).RST = false + transport_layer.(*layers.TCP).ACK = true + transport_layer.(*layers.TCP).SetNetworkLayerForChecksum(p.Packet.NetworkLayer()) + buffer := gopacket.NewSerializeBuffer() + options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true} + if err := gopacket.SerializePacket(buffer, options, p.Packet); err != nil { + p.SetVerdict(netfilter.NF_DROP) + } + p.SetVerdictWithPacket(netfilter.NF_ACCEPT, buffer.Bytes()) + } else { + p.SetVerdict(netfilter.NF_DROP) + } + } + } else { + p.SetVerdict(netfilter.NF_ACCEPT) + } + } + } } } @@ -144,24 +211,29 @@ func main() { log.Fatalf("[main] Invalid number of queues: %s", err) } } - + var filter_channels []chan regex_filters // Start the queue list 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(), true) + ch := make(chan regex_filters) + filter_channels = append(filter_channels, ch) + go handle_packets(queue.GetPackets(), ch, 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(), false) + ch := make(chan regex_filters) + filter_channels = append(filter_channels, ch) + go handle_packets(queue.GetPackets(), ch, false) } fmt.Println("QUEUE INPUT", starts_input, end_input, "OUTPUT", starts_output, end_output) //Reading for new configuration reader := bufio.NewReader(os.Stdin) + old_filter_table := NewRegexFilter() for true { text, err := reader.ReadString('\n') log.Printf("[main] Regex rule updating...") @@ -171,7 +243,7 @@ func main() { text = strings.Trim(text, "\n") regexes := strings.Split(text, " ") - new_filters := regex_filters{} + new_filters := NewRegexFilter() for _, regex := range regexes { regex = strings.Trim(regex, " ") if len(regex) < 2 { @@ -179,7 +251,12 @@ func main() { } new_filters.add(regex) } - FILTER_TABLE = &new_filters + for _, ch := range filter_channels { + ch <- *new_filters + } + old_filter_table.clear() + old_filter_table = new_filters log.Printf("[main] Regex filter rules updated!") } + }