opensnitch/daemon/firewall/common/common.go
Gustavo Iñiguez Goia efc05663eb
fw: allow to configure interception queue number
- Added new configuration field to allow configure fw interception
   number queue (default to 0):
   "FwOptions": {
      "QueueNum": 0
   }
   (we still need to reconfigure nfqueue queues in order for this to
take effect).
 - If the fw configuration path is not supplied, default to
   /etc/opensnitchd/system-fw.json
2024-05-14 23:41:25 +02:00

186 lines
4.3 KiB
Go

package common
import (
"sync"
"time"
"github.com/evilsocket/opensnitch/daemon/log"
)
// default arguments for various functions
var (
EnableRule = true
DoLogErrors = true
ForcedDelRules = true
ReloadRules = true
RestoreChains = true
BackupChains = true
ReloadConf = true
DefaultCheckInterval = 10 * time.Second
RulesCheckerDisabled = "0s"
)
type (
callback func()
callbackBool func() bool
// Common holds common fields and functionality of both firewalls,
// iptables and nftables.
Common struct {
RulesChecker *time.Ticker
ErrChan chan string
stopChecker chan struct{}
RulesCheckInterval time.Duration
QueueNum uint16
Running bool
Intercepting bool
FwEnabled bool
sync.RWMutex
}
)
// ErrorsChan returns the channel where the errors are sent to.
func (c *Common) ErrorsChan() <-chan string {
return c.ErrChan
}
// ErrChanEmpty checks if the errors channel is empty.
func (c *Common) ErrChanEmpty() bool {
return len(c.ErrChan) == 0
}
// SendError sends an error to the channel of errors.
func (c *Common) SendError(err string) {
log.Warning("%s", err)
if len(c.ErrChan) >= cap(c.ErrChan) {
log.Debug("fw errors channel full, emptying errChan")
for e := range c.ErrChan {
log.Warning("%s", e)
if c.ErrChanEmpty() {
break
}
}
return
}
select {
case c.ErrChan <- err:
case <-time.After(100 * time.Millisecond):
log.Warning("SendError() channel locked? REVIEW")
}
}
func (c *Common) SetRulesCheckerInterval(interval string) {
dur, err := time.ParseDuration(interval)
if err != nil {
log.Warning("Invalid rules checker interval (falling back to %s): %s", DefaultCheckInterval, err)
c.RulesCheckInterval = DefaultCheckInterval
return
}
c.RulesCheckInterval = dur
}
// SetQueueNum sets the queue number used by the firewall.
// It's the queue where all intercepted connections will be sent.
func (c *Common) SetQueueNum(qNum uint16) {
c.Lock()
defer c.Unlock()
c.QueueNum = qNum
}
// IsRunning returns if the firewall is running or not.
func (c *Common) IsRunning() bool {
c.RLock()
defer c.RUnlock()
return c != nil && c.Running
}
// IsFirewallEnabled returns if the firewall is running or not.
func (c *Common) IsFirewallEnabled() bool {
c.RLock()
defer c.RUnlock()
return c != nil && c.FwEnabled
}
// IsIntercepting returns if the firewall is running or not.
func (c *Common) IsIntercepting() bool {
c.RLock()
defer c.RUnlock()
return c != nil && c.Intercepting
}
// NewRulesChecker starts monitoring interception rules.
// We expect to have 2 rules loaded: one to intercept DNS responses and another one
// to intercept network traffic.
func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callback) {
c.Lock()
defer c.Unlock()
if c.RulesCheckInterval.String() == RulesCheckerDisabled {
log.Info("Fw rules checker disabled ...")
return
}
if c.RulesChecker != nil {
c.RulesChecker.Stop()
select {
case c.stopChecker <- struct{}{}:
case <-time.After(5 * time.Millisecond):
log.Error("NewRulesChecker: timed out stopping monitor rules")
}
}
c.stopChecker = make(chan struct{}, 1)
log.Info("Starting new fw checker every %s ...", c.RulesCheckInterval)
c.RulesChecker = time.NewTicker(c.RulesCheckInterval)
go startCheckingRules(c.stopChecker, c.RulesChecker, areRulesLoaded, reloadRules)
}
// StartCheckingRules monitors if our rules are loaded.
// If the rules to intercept traffic are not loaded, we'll try to insert them again.
func startCheckingRules(exitChan <-chan struct{}, rulesChecker *time.Ticker, areRulesLoaded callbackBool, reloadRules callback) {
for {
select {
case <-exitChan:
goto Exit
case _, active := <-rulesChecker.C:
if !active {
goto Exit
}
if areRulesLoaded() == false {
reloadRules()
}
}
}
Exit:
log.Info("exit checking firewall rules")
}
// StopCheckingRules stops checking if firewall rules are loaded.
func (c *Common) StopCheckingRules() {
c.Lock()
defer c.Unlock()
if c.RulesChecker != nil {
select {
case c.stopChecker <- struct{}{}:
close(c.stopChecker)
case <-time.After(5 * time.Millisecond):
// We should not arrive here
log.Error("StopCheckingRules: timed out stopping monitor rules")
}
c.RulesChecker.Stop()
c.RulesChecker = nil
}
}
func (c *Common) reloadCallback(callback func()) {
callback()
}