ipset: imp docs

This commit is contained in:
Stanislav Chzhen 2023-10-18 20:17:16 +03:00
parent 544982b5d7
commit 4ef03c9e00
2 changed files with 67 additions and 40 deletions

View File

@ -14,7 +14,6 @@ import (
"github.com/digineo/go-ipset/v2"
"github.com/mdlayher/netlink"
"github.com/ti-mo/netfilter"
"golang.org/x/exp/slices"
"golang.org/x/sys/unix"
)
@ -48,14 +47,19 @@ func defaultDial(pf netfilter.ProtoFamily, conf *netlink.Config) (conn ipsetConn
return &queryConn{c}, nil
}
// queryConn is the [ipsetConn] implementation.
type queryConn struct {
*ipset.Conn
}
func (qc *queryConn) ListAll() (sets []props, err error) {
// type check
var _ ipsetConn = (*queryConn)(nil)
// queryConn returns the list of all ipsets.
func (qc *queryConn) listAll() (sets []props, err error) {
msg, err := netfilter.MarshalNetlink(
netfilter.Header{
Family: netfilter.ProtoIPv4,
Family: qc.Conn.Family,
SubsystemID: netfilter.NFSubsysIPSet,
MessageType: netfilter.MessageType(ipset.CmdList),
Flags: netlink.Request | netlink.Dump,
@ -66,19 +70,19 @@ func (qc *queryConn) ListAll() (sets []props, err error) {
}},
)
if err != nil {
return nil, err
return nil, fmt.Errorf("marshaling netlink msg: %w", err)
}
ms, err := qc.Conn.Conn.Query(msg)
if err != nil {
return nil, err
return nil, fmt.Errorf("querying netlink msg: %w", err)
}
for _, s := range ms {
p := props{}
err = p.unmarshalMessage(s)
if err != nil {
return nil, err
return nil, fmt.Errorf("unmarshaling netlink msg: %w", err)
}
sets = append(sets, p)
@ -91,7 +95,7 @@ func (qc *queryConn) ListAll() (sets []props, err error) {
type ipsetConn interface {
Add(name string, entries ...*ipset.Entry) (err error)
Close() (err error)
ListAll() (sets []props, err error)
listAll() (sets []props, err error)
}
// dialer creates an ipsetConn.
@ -104,9 +108,12 @@ type props struct {
temp bool
}
// unmarshalMessage unmarshals netlink message and sets the properties of the
// ipset.
func (p *props) unmarshalMessage(msg netlink.Message) (err error) {
_, attrs, err := netfilter.UnmarshalNetlink(msg)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
@ -117,6 +124,8 @@ func (p *props) unmarshalMessage(msg netlink.Message) (err error) {
return nil
}
// parseAttribute parses netfilter attribute and sets the name and family of
// the ipset.
func (p *props) parseAttribute(a netfilter.Attribute) {
switch ipset.AttributeType(a.Type) {
case ipset.AttrData:
@ -130,6 +139,7 @@ func (p *props) parseAttribute(a netfilter.Attribute) {
}
}
// parseAttrData parses attribute data and sets the timeout of the ipset.
func (p *props) parseAttrData(a netfilter.Attribute) {
for _, a := range a.Children {
switch ipset.AttributeType(a.Type) {
@ -195,8 +205,8 @@ func (m *manager) dialNetfilter(conf *netlink.Config) (err error) {
return nil
}
// parseIpsetConfig parses one ipset configuration string.
func parseIpsetConfig(confStr string) (hosts, ipsetNames []string, err error) {
// parseIpsetConfigLine parses one ipset configuration line.
func parseIpsetConfigLine(confStr string) (hosts, ipsetNames []string, err error) {
confStr = strings.TrimSpace(confStr)
hostsAndNames := strings.Split(confStr, "/")
if len(hostsAndNames) != 2 {
@ -224,22 +234,53 @@ func parseIpsetConfig(confStr string) (hosts, ipsetNames []string, err error) {
return hosts, ipsetNames, nil
}
// ipsets returns currently known ipsets.
func (m *manager) ipsets(names []string) (sets []props, err error) {
// The family doesn't seem to matter when we use a header query, so
// query only the IPv4 one.
// 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) {
// The family doesn't seem to matter when we use a list query, so query
// only the IPv4 one.
//
// TODO(a.garipov): Find out if this is a bug or a feature.
all, err := m.ipv4Conn.ListAll()
all, err := m.ipv4Conn.listAll()
if err != nil {
return nil, err
// Don't wrap the error since it's informative enough as is.
return err
}
for _, p := range all {
if slices.Contains(names, p.name) {
m.nameToIpset[p.name] = p
sets = append(sets, p)
m.nameToIpset[p.name] = p
}
for i, confStr := range ipsetConf {
var hosts, ipsetNames []string
hosts, ipsetNames, err = parseIpsetConfigLine(confStr)
if err != nil {
return fmt.Errorf("config line at idx %d: %w", i, err)
}
var ipsets []props
ipsets, err = m.ipsets(ipsetNames)
if err != nil {
return fmt.Errorf("getting ipsets from config line at idx %d: %w", i, err)
}
for _, host := range hosts {
m.domainToIpsets[host] = append(m.domainToIpsets[host], ipsets...)
}
}
return nil
}
// ipsets returns currently known ipsets.
func (m *manager) ipsets(names []string) (sets []props, err error) {
for _, n := range names {
p, ok := m.nameToIpset[n]
if !ok {
return nil, fmt.Errorf("unknown ipset %q", n)
}
sets = append(sets, p)
}
return sets, nil
@ -274,26 +315,9 @@ func newManagerWithDialer(ipsetConf []string, dial dialer) (mgr Manager, err err
return nil, fmt.Errorf("dialing netfilter: %w", err)
}
for i, confStr := range ipsetConf {
var hosts, ipsetNames []string
hosts, ipsetNames, err = parseIpsetConfig(confStr)
if err != nil {
return nil, fmt.Errorf("config line at idx %d: %w", i, err)
}
var ipsets []props
ipsets, err = m.ipsets(ipsetNames)
if err != nil {
return nil, fmt.Errorf(
"getting ipsets from config line at idx %d: %w",
i,
err,
)
}
for _, host := range hosts {
m.domainToIpsets[host] = append(m.domainToIpsets[host], ipsets...)
}
err = m.parseIpsetConfig(ipsetConf)
if err != nil {
return nil, fmt.Errorf("getting ipsets: %w", err)
}
return m, nil

View File

@ -24,6 +24,9 @@ type fakeConn struct {
sets []props
}
// type check
var _ ipsetConn = (*fakeConn)(nil)
// Add implements the [ipsetConn] interface for *fakeConn.
func (c *fakeConn) Add(name string, entries ...*ipset.Entry) (err error) {
if strings.Contains(name, "ipv4") {
@ -44,8 +47,8 @@ func (c *fakeConn) Close() (err error) {
return nil
}
// ListAll implements the [ipsetConn] interface for *fakeConn.
func (c *fakeConn) ListAll() (sets []props, err error) {
// listAll implements the [ipsetConn] interface for *fakeConn.
func (c *fakeConn) listAll() (sets []props, err error) {
return c.sets, nil
}