Pull request: aghnet: fix ipset init errors

Updates #4027.

Squashed commit of the following:

commit 9ac0cc27ca94e630cc321c90b60b271499af4d9b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 20:26:22 2021 +0300

    aghnet: fix ipset init errors
This commit is contained in:
Ainar Garipov 2021-12-27 20:54:00 +03:00
parent dea8a585f8
commit 2ed1f939b5
4 changed files with 35 additions and 14 deletions

View File

@ -29,6 +29,7 @@ and this project adheres to
### Fixed ### Fixed
- `ipset` initialization bugs ([#4027]).
- Legacy DNS rewrites from a wildcard pattern to a subdomain ([#4016]). - Legacy DNS rewrites from a wildcard pattern to a subdomain ([#4016]).
- Service not being stopped before running the `uninstall` service action - Service not being stopped before running the `uninstall` service action
([#3868]). ([#3868]).
@ -46,6 +47,7 @@ and this project adheres to
[#3987]: https://github.com/AdguardTeam/AdGuardHome/issues/3987 [#3987]: https://github.com/AdguardTeam/AdGuardHome/issues/3987
[#4008]: https://github.com/AdguardTeam/AdGuardHome/issues/4008 [#4008]: https://github.com/AdguardTeam/AdGuardHome/issues/4008
[#4016]: https://github.com/AdguardTeam/AdGuardHome/issues/4016 [#4016]: https://github.com/AdguardTeam/AdGuardHome/issues/4016
[#4027]: https://github.com/AdguardTeam/AdGuardHome/issues/4027

View File

@ -20,7 +20,12 @@ type IpsetManager interface {
// //
// DOMAIN[,DOMAIN].../IPSET_NAME[,IPSET_NAME]... // DOMAIN[,DOMAIN].../IPSET_NAME[,IPSET_NAME]...
// //
// The error is of type *aghos.UnsupportedError if the OS is not supported. // If ipsetConf is empty, msg and err are nil. The error is of type
// *aghos.UnsupportedError if the OS is not supported.
func NewIpsetManager(ipsetConf []string) (mgr IpsetManager, err error) { func NewIpsetManager(ipsetConf []string) (mgr IpsetManager, err error) {
if len(ipsetConf) == 0 {
return nil, nil
}
return newIpsetMgr(ipsetConf) return newIpsetMgr(ipsetConf)
} }

View File

@ -14,6 +14,7 @@ import (
"github.com/digineo/go-ipset/v2" "github.com/digineo/go-ipset/v2"
"github.com/mdlayher/netlink" "github.com/mdlayher/netlink"
"github.com/ti-mo/netfilter" "github.com/ti-mo/netfilter"
"golang.org/x/sys/unix"
) )
// How to test on a real Linux machine: // How to test on a real Linux machine:
@ -42,11 +43,17 @@ import (
// newIpsetMgr returns a new Linux ipset manager. // newIpsetMgr returns a new Linux ipset manager.
func newIpsetMgr(ipsetConf []string) (set IpsetManager, err error) { func newIpsetMgr(ipsetConf []string) (set IpsetManager, err error) {
dial := func(pf netfilter.ProtoFamily, conf *netlink.Config) (conn ipsetConn, err error) { return newIpsetMgrWithDialer(ipsetConf, defaultDial)
return ipset.Dial(pf, conf) }
// defaultDial is the default netfilter dialing function.
func defaultDial(pf netfilter.ProtoFamily, conf *netlink.Config) (conn ipsetConn, err error) {
conn, err = ipset.Dial(pf, conf)
if err != nil {
return nil, err
} }
return newIpsetMgrWithDialer(ipsetConf, dial) return conn, nil
} }
// ipsetConn is the ipset conn interface. // ipsetConn is the ipset conn interface.
@ -103,8 +110,8 @@ func (m *ipsetMgr) dialNetfilter(conf *netlink.Config) (err error) {
// The kernel API does not actually require two sockets but package // The kernel API does not actually require two sockets but package
// github.com/digineo/go-ipset does. // github.com/digineo/go-ipset does.
// //
// TODO(a.garipov): Perhaps we can ditch package ipset altogether and // TODO(a.garipov): Perhaps we can ditch package ipset altogether and just
// just use packages netfilter and netlink. // use packages netfilter and netlink.
m.ipv4Conn, err = m.dial(netfilter.ProtoIPv4, conf) m.ipv4Conn, err = m.dial(netfilter.ProtoIPv4, conf)
if err != nil { if err != nil {
return fmt.Errorf("dialing v4: %w", err) return fmt.Errorf("dialing v4: %w", err)
@ -214,6 +221,14 @@ func newIpsetMgrWithDialer(ipsetConf []string, dial ipsetDialer) (mgr IpsetManag
err = m.dialNetfilter(&netlink.Config{}) err = m.dialNetfilter(&netlink.Config{})
if err != nil { if err != nil {
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)
return nil, nil
}
return nil, fmt.Errorf("dialing netfilter: %w", err) return nil, fmt.Errorf("dialing netfilter: %w", err)
} }

View File

@ -24,20 +24,19 @@ type ipsetCtx struct {
func (c *ipsetCtx) init(ipsetConf []string) (err error) { func (c *ipsetCtx) init(ipsetConf []string) (err error) {
c.ipsetMgr, err = aghnet.NewIpsetManager(ipsetConf) c.ipsetMgr, err = aghnet.NewIpsetManager(ipsetConf)
if errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrPermission) { if errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrPermission) {
// ipset cannot currently be initialized if the server was // ipset cannot currently be initialized if the server was installed
// installed from Snap or when the user or the binary doesn't // from Snap or when the user or the binary doesn't have the required
// have the required permissions, or when the kernel doesn't // permissions, or when the kernel doesn't support netfilter.
// support netfilter.
// //
// Log and go on. // Log and go on.
// //
// TODO(a.garipov): The Snap problem can probably be solved if // TODO(a.garipov): The Snap problem can probably be solved if we add
// we add the netlink-connector interface plug. // the netlink-connector interface plug.
log.Info("warning: cannot initialize ipset: %s", err) log.Info("ipset: warning: cannot initialize: %s", err)
return nil return nil
} else if unsupErr := (&aghos.UnsupportedError{}); errors.As(err, &unsupErr) { } else if unsupErr := (&aghos.UnsupportedError{}); errors.As(err, &unsupErr) {
log.Info("warning: %s", err) log.Info("ipset: warning: %s", err)
return nil return nil
} else if err != nil { } else if err != nil {