Pull request 2270: AGDNS-2374-slog-ipset
Squashed commit of the following: commit 51ff7d8c49d174d057b4f508f3e113e1ca86bd1a Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Aug 22 13:50:10 2024 +0300 dnsforward: imp code commit a1c0011273fc83ec1b509a9d930bca5e278e1e2c Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Aug 21 21:53:01 2024 +0300 dnsforward: imp code commit a64fd6b3f037712927a583d04296fcaf821f6442 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Aug 21 21:28:48 2024 +0300 dnsforward: imp code commit 37ccae4e923a7e688e79a135b0e49a746e9b2a06 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Aug 21 20:23:58 2024 +0300 all: imp code commit 03c69ab2729eb424d768def986cba83731ad3e3b Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Aug 21 19:08:30 2024 +0300 all: imp code commit 72adfb101fcdb42635702c1f1c4e13ddcc95bfdc Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Aug 21 16:42:44 2024 +0300 all: slog ipset
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
)
|
||||
|
||||
@@ -10,24 +12,33 @@ import (
|
||||
// TODO(a.garipov): Perhaps generalize this into some kind of a NetFilter type,
|
||||
// since ipset is exclusive to Linux?
|
||||
type Manager interface {
|
||||
Add(host string, ip4s, ip6s []net.IP) (n int, err error)
|
||||
Add(ctx context.Context, host string, ip4s, ip6s []net.IP) (n int, err error)
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
// NewManager returns a new ipset manager. IPv4 addresses are added to an
|
||||
// ipset with an ipv4 family; IPv6 addresses, to an ipv6 ipset. ipset must
|
||||
// exist.
|
||||
// Config is the configuration structure for the ipset manager.
|
||||
type Config struct {
|
||||
// Logger is used for logging the operation of the ipset manager. It must
|
||||
// not be nil.
|
||||
Logger *slog.Logger
|
||||
|
||||
// Lines is the ipset configuration with the following syntax:
|
||||
//
|
||||
// DOMAIN[,DOMAIN].../IPSET_NAME[,IPSET_NAME]...
|
||||
//
|
||||
// Lines must not contain any blank lines or comments.
|
||||
Lines []string
|
||||
}
|
||||
|
||||
// NewManager returns a new ipset manager. IPv4 addresses are added to an ipset
|
||||
// with an ipv4 family; IPv6 addresses, to an ipv6 ipset. ipset must exist.
|
||||
//
|
||||
// The syntax of the ipsetConf is:
|
||||
//
|
||||
// DOMAIN[,DOMAIN].../IPSET_NAME[,IPSET_NAME]...
|
||||
//
|
||||
// If ipsetConf is empty, msg and err are nil. The error's chain contains
|
||||
// If conf.Lines is empty, mgr and err are nil. The error's chain contains
|
||||
// [errors.ErrUnsupported] if current OS is not supported.
|
||||
func NewManager(ipsetConf []string) (mgr Manager, err error) {
|
||||
if len(ipsetConf) == 0 {
|
||||
func NewManager(ctx context.Context, conf *Config) (mgr Manager, err error) {
|
||||
if len(conf.Lines) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return newManager(ipsetConf)
|
||||
return newManager(ctx, conf)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ package ipset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/golibs/container"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/digineo/go-ipset/v2"
|
||||
"github.com/mdlayher/netlink"
|
||||
"github.com/ti-mo/netfilter"
|
||||
@@ -34,8 +36,8 @@ import (
|
||||
// resolved IP addresses.
|
||||
|
||||
// newManager returns a new Linux ipset manager.
|
||||
func newManager(ipsetConf []string) (set Manager, err error) {
|
||||
return newManagerWithDialer(ipsetConf, defaultDial)
|
||||
func newManager(ctx context.Context, conf *Config) (set Manager, err error) {
|
||||
return newManagerWithDialer(ctx, conf, defaultDial)
|
||||
}
|
||||
|
||||
// defaultDial is the default netfilter dialing function.
|
||||
@@ -180,6 +182,8 @@ type manager struct {
|
||||
nameToIpset map[string]props
|
||||
domainToIpsets map[string][]props
|
||||
|
||||
logger *slog.Logger
|
||||
|
||||
dial dialer
|
||||
|
||||
// mu protects all properties below.
|
||||
@@ -254,7 +258,7 @@ func parseIpsetConfigLine(confStr string) (hosts, ipsetNames []string, err error
|
||||
|
||||
// parseIpsetConfig parses the ipset configuration and stores ipsets. It
|
||||
// returns an error if the configuration can't be used.
|
||||
func (m *manager) parseIpsetConfig(ipsetConf []string) (err error) {
|
||||
func (m *manager) parseIpsetConfig(ctx context.Context, ipsetConf []string) (err error) {
|
||||
// The family doesn't seem to matter when we use a header query, so query
|
||||
// only the IPv4 one.
|
||||
//
|
||||
@@ -278,7 +282,7 @@ func (m *manager) parseIpsetConfig(ipsetConf []string) (err error) {
|
||||
}
|
||||
|
||||
var ipsets []props
|
||||
ipsets, err = m.ipsets(ipsetNames, currentlyKnown)
|
||||
ipsets, err = m.ipsets(ctx, ipsetNames, currentlyKnown)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting ipsets from config line at idx %d: %w", i, err)
|
||||
}
|
||||
@@ -328,7 +332,11 @@ func (m *manager) ipsetProps(name string) (p props, err error) {
|
||||
|
||||
// ipsets returns ipset properties of currently known ipsets. It also makes an
|
||||
// additional ipset header data query if needed.
|
||||
func (m *manager) ipsets(names []string, currentlyKnown map[string]props) (sets []props, err error) {
|
||||
func (m *manager) ipsets(
|
||||
ctx context.Context,
|
||||
names []string,
|
||||
currentlyKnown map[string]props,
|
||||
) (sets []props, err error) {
|
||||
for _, n := range names {
|
||||
p, ok := currentlyKnown[n]
|
||||
if !ok {
|
||||
@@ -336,10 +344,12 @@ func (m *manager) ipsets(names []string, currentlyKnown map[string]props) (sets
|
||||
}
|
||||
|
||||
if p.family != netfilter.ProtoIPv4 && p.family != netfilter.ProtoIPv6 {
|
||||
log.Debug("ipset: getting properties: %q %q unexpected ipset family %q",
|
||||
p.name,
|
||||
p.typeName,
|
||||
p.family,
|
||||
m.logger.DebugContext(
|
||||
ctx,
|
||||
"got unexpected ipset family while getting set properties",
|
||||
"set_name", p.name,
|
||||
"set_type", p.typeName,
|
||||
"set_family", p.family,
|
||||
)
|
||||
|
||||
p, err = m.ipsetProps(n)
|
||||
@@ -357,7 +367,7 @@ func (m *manager) ipsets(names []string, currentlyKnown map[string]props) (sets
|
||||
|
||||
// newManagerWithDialer returns a new Linux ipset manager using the provided
|
||||
// dialer.
|
||||
func newManagerWithDialer(ipsetConf []string, dial dialer) (mgr Manager, err error) {
|
||||
func newManagerWithDialer(ctx context.Context, conf *Config, dial dialer) (mgr Manager, err error) {
|
||||
defer func() { err = errors.Annotate(err, "ipset: %w") }()
|
||||
|
||||
m := &manager{
|
||||
@@ -366,6 +376,8 @@ func newManagerWithDialer(ipsetConf []string, dial dialer) (mgr Manager, err err
|
||||
nameToIpset: make(map[string]props),
|
||||
domainToIpsets: make(map[string][]props),
|
||||
|
||||
logger: conf.Logger,
|
||||
|
||||
dial: dial,
|
||||
|
||||
addedIPs: container.NewMapSet[ipInIpsetEntry](),
|
||||
@@ -376,7 +388,7 @@ func newManagerWithDialer(ipsetConf []string, dial dialer) (mgr Manager, err err
|
||||
if errors.Is(err, unix.EPROTONOSUPPORT) {
|
||||
// The implementation doesn't support this protocol version. Just
|
||||
// issue a warning.
|
||||
log.Info("ipset: dialing netfilter: warning: %s", err)
|
||||
m.logger.WarnContext(ctx, "dialing netfilter", slogutil.KeyError, err)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -384,12 +396,12 @@ func newManagerWithDialer(ipsetConf []string, dial dialer) (mgr Manager, err err
|
||||
return nil, fmt.Errorf("dialing netfilter: %w", err)
|
||||
}
|
||||
|
||||
err = m.parseIpsetConfig(ipsetConf)
|
||||
err = m.parseIpsetConfig(ctx, conf.Lines)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting ipsets: %w", err)
|
||||
}
|
||||
|
||||
log.Debug("ipset: initialized")
|
||||
m.logger.DebugContext(ctx, "initialized")
|
||||
|
||||
return m, nil
|
||||
}
|
||||
@@ -476,6 +488,7 @@ func (m *manager) addIPs(host string, set props, ips []net.IP) (n int, err error
|
||||
|
||||
// addToSets adds the IP addresses to the corresponding ipset.
|
||||
func (m *manager) addToSets(
|
||||
ctx context.Context,
|
||||
host string,
|
||||
ip4s []net.IP,
|
||||
ip6s []net.IP,
|
||||
@@ -498,7 +511,13 @@ func (m *manager) addToSets(
|
||||
return n, fmt.Errorf("%q %q unexpected family %q", set.name, set.typeName, set.family)
|
||||
}
|
||||
|
||||
log.Debug("ipset: added %d ips to set %q %q", nn, set.name, set.typeName)
|
||||
m.logger.DebugContext(
|
||||
ctx,
|
||||
"added ips to set",
|
||||
"ips_num", nn,
|
||||
"set_name", set.name,
|
||||
"set_type", set.typeName,
|
||||
)
|
||||
|
||||
n += nn
|
||||
}
|
||||
@@ -507,7 +526,7 @@ func (m *manager) addToSets(
|
||||
}
|
||||
|
||||
// Add implements the [Manager] interface for *manager.
|
||||
func (m *manager) Add(host string, ip4s, ip6s []net.IP) (n int, err error) {
|
||||
func (m *manager) Add(ctx context.Context, host string, ip4s, ip6s []net.IP) (n int, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
@@ -516,9 +535,9 @@ func (m *manager) Add(host string, ip4s, ip6s []net.IP) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
log.Debug("ipset: found %d sets", len(sets))
|
||||
m.logger.DebugContext(ctx, "found sets", "set_num", len(sets))
|
||||
|
||||
return m.addToSets(host, ip4s, ip6s, sets)
|
||||
return m.addToSets(ctx, host, ip4s, ip6s, sets)
|
||||
}
|
||||
|
||||
// Close implements the [Manager] interface for *manager.
|
||||
|
||||
@@ -6,8 +6,11 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/digineo/go-ipset/v2"
|
||||
"github.com/mdlayher/netlink"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -15,6 +18,9 @@ import (
|
||||
"github.com/ti-mo/netfilter"
|
||||
)
|
||||
|
||||
// testTimeout is a common timeout for tests and contexts.
|
||||
const testTimeout = 1 * time.Second
|
||||
|
||||
// fakeConn is a fake ipsetConn for tests.
|
||||
type fakeConn struct {
|
||||
ipv4Header *ipset.HeaderPolicy
|
||||
@@ -58,7 +64,7 @@ func (c *fakeConn) listAll() (sets []props, err error) {
|
||||
}
|
||||
|
||||
func TestManager_Add(t *testing.T) {
|
||||
ipsetConf := []string{
|
||||
ipsetList := []string{
|
||||
"example.com,example.net/ipv4set",
|
||||
"example.org,example.biz/ipv6set",
|
||||
}
|
||||
@@ -89,7 +95,11 @@ func TestManager_Add(t *testing.T) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
m, err := newManagerWithDialer(ipsetConf, fakeDial)
|
||||
conf := &Config{
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
Lines: ipsetList,
|
||||
}
|
||||
m, err := newManagerWithDialer(testutil.ContextWithTimeout(t, testTimeout), conf, fakeDial)
|
||||
require.NoError(t, err)
|
||||
|
||||
ip4 := net.IP{1, 2, 3, 4}
|
||||
@@ -100,7 +110,7 @@ func TestManager_Add(t *testing.T) {
|
||||
0x00, 0x00, 0x56, 0x78,
|
||||
}
|
||||
|
||||
n, err := m.Add("example.net", []net.IP{ip4}, nil)
|
||||
n, err := m.Add(testutil.ContextWithTimeout(t, testTimeout), "example.net", []net.IP{ip4}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, n)
|
||||
@@ -110,7 +120,7 @@ func TestManager_Add(t *testing.T) {
|
||||
gotIP4 := ipv4Entries[0].IP.Value
|
||||
assert.Equal(t, ip4, gotIP4)
|
||||
|
||||
n, err = m.Add("example.biz", nil, []net.IP{ip6})
|
||||
n, err = m.Add(testutil.ContextWithTimeout(t, testTimeout), "example.biz", nil, []net.IP{ip6})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, n)
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
)
|
||||
|
||||
func newManager(_ []string) (mgr Manager, err error) {
|
||||
func newManager(_ context.Context, _ *Config) (mgr Manager, err error) {
|
||||
return nil, aghos.Unsupported("ipset")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user