feat: adjust parse function for monitor object parsion

This commit is contained in:
singchia 2023-05-17 12:34:07 -04:00
parent 46100e8684
commit 23abc25997
7 changed files with 168 additions and 44 deletions

View File

@ -223,9 +223,10 @@ func (cc *Conn) ListChainsOfTableFamily(family TableFamily) ([]*Chain, error) {
}
func chainFromMsg(msg netlink.Message) (*Chain, error) {
chainHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWCHAIN)
if got, want := msg.Header.Type, chainHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
newChainHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWCHAIN)
delChainHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELCHAIN)
if got, want1, want2 := msg.Header.Type, newChainHeaderType, delChainHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
var c Chain

View File

@ -23,6 +23,7 @@ import (
type MonitorAction uint8
// Possible MonitorAction values.
const (
MonitorActionNew MonitorAction = 1 << iota
MonitorActionDel
@ -40,7 +41,6 @@ const (
MonitorObjectRules
MonitorObjectElements
MonitorObjectRuleset
MonitorObjectTrace
MonitorObjectMask MonitorObject = (1 << iota) - 1
MonitorObjectAny MonitorObject = MonitorObjectMask
)
@ -50,7 +50,8 @@ var (
monitorFlagsInitOnce sync.Once
)
func lazyInitOnce() {
// A lazy init function to define flags.
func lazyInit() {
monitorFlagsInitOnce.Do(func() {
monitorFlags = map[MonitorAction]map[MonitorObject]uint32{
MonitorActionAny: {
@ -66,7 +67,6 @@ func lazyInitOnce() {
1<<unix.NFT_MSG_NEWSET | 1<<unix.NFT_MSG_DELSET |
1<<unix.NFT_MSG_NEWSETELEM | 1<<unix.NFT_MSG_DELSETELEM |
1<<unix.NFT_MSG_NEWOBJ | 1<<unix.NFT_MSG_DELOBJ,
MonitorObjectTrace: 1 << unix.NFT_MSG_TRACE,
},
MonitorActionNew: {
MonitorObjectAny: 1<<unix.NFT_MSG_NEWTABLE |
@ -84,7 +84,6 @@ func lazyInitOnce() {
1<<unix.NFT_MSG_NEWSET |
1<<unix.NFT_MSG_NEWSETELEM |
1<<unix.NFT_MSG_NEWOBJ,
MonitorObjectTrace: 0,
},
MonitorActionDel: {
MonitorObjectAny: 1<<unix.NFT_MSG_DELTABLE |
@ -93,20 +92,49 @@ func lazyInitOnce() {
1<<unix.NFT_MSG_DELSET |
1<<unix.NFT_MSG_DELSETELEM |
1<<unix.NFT_MSG_DELOBJ,
MonitorObjectTrace: 0,
},
}
})
}
// A Monitor
type Monitor struct {
action MonitorAction
object MonitorObject
type EventType int
const (
EventTypeNewTable EventType = unix.NFT_MSG_NEWTABLE
EventTypeDelTable EventType = unix.NFT_MSG_DELTABLE
EventTypeNewChain EventType = unix.NFT_MSG_NEWCHAIN
EventTypeDELChain EventType = unix.NFT_MSG_DELCHAIN
EventTypeNewRule EventType = unix.NFT_MSG_NEWRULE
EventTypeDelRule EventType = unix.NFT_MSG_DELRULE
EventTypeNewSet EventType = unix.NFT_MSG_NEWSET
EventTypeDelSet EventType = unix.NFT_MSG_DELSET
EventTypeNewSetElem EventType = unix.NFT_MSG_NEWSETELEM
EventTypeDelSetElem EventType = unix.NFT_MSG_DELSETELEM
EventTypeNewObj EventType = unix.NFT_MSG_NEWOBJ
EventTypeDelObj EventType = unix.NFT_MSG_DELOBJ
)
type Event struct {
Type EventType
Data interface{}
Error error
}
const (
monitorOK = iota
monitorClosed
)
// A Monitor to track actions on objects.
type Monitor struct {
action MonitorAction
object MonitorObject
monitorFlags uint32
// mtx covers eventCh and status
mtx sync.Mutex
eventCh chan *Event
status int
conn *netlink.Conn
closer netlinkCloser
}
@ -119,22 +147,27 @@ func WithMonitorEventBuffer(size int) MonitorOption {
}
}
// WithMonitorAction to set monitor actions like new, del or any.
func WithMonitorAction(action MonitorAction) MonitorOption {
return func(monitor *Monitor) {
monitor.action = action
}
}
// WithMonitorObject to set monitor objects.
func WithMonitorObject(object MonitorObject) MonitorOption {
return func(monitor *Monitor) {
monitor.object = object
}
}
// NewMonitor returns a Monitor with options to be started.
func NewMonitor(opts ...MonitorOption) *Monitor {
lazyInitOnce()
lazyInit()
monitor := &Monitor{}
monitor := &Monitor{
status: monitorOK,
}
for _, opt := range opts {
opt(monitor)
}
@ -169,19 +202,78 @@ func (monitor *Monitor) monitor() {
}
switch msgType {
case unix.NFT_MSG_NEWTABLE, unix.NFT_MSG_DELTABLE:
table, err := tableFromMsg(msg)
event := &Event{
Type: EventType(msgType),
Data: table,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWCHAIN, unix.NFT_MSG_DELCHAIN:
chain, err := chainFromMsg(msg)
event := &Event{
Type: EventType(msgType),
Data: chain,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWRULE, unix.NFT_MSG_DELRULE:
rule, err := parseRuleFromMsg(msg)
event := &Event{
Type: EventType(msgType),
Data: rule,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWSET, unix.NFT_MSG_DELSET:
set, err := setsFromMsg(msg)
event := &Event{
Type: EventType(msgType),
Data: set,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWSETELEM, unix.NFT_MSG_DELSETELEM:
elems, err := elementsFromMsg(msg)
event := &Event{
Type: EventType(msgType),
Data: elems,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_NEWOBJ, unix.NFT_MSG_DELOBJ:
obj, err := objFromMsg(msg)
event := &Event{
Type: EventType(msgType),
Data: obj,
Error: err,
}
monitor.eventCh <- event
case unix.NFT_MSG_TRACE:
}
}
}
monitor.mtx.Lock()
if monitor.status != monitorClosed {
monitor.status = monitorClosed
monitor.closer()
close(monitor.eventCh)
}
monitor.mtx.Unlock()
}
func (monitor *Monitor) Close() {
monitor.closer()
close(monitor.eventCh)
monitor.mtx.Lock()
if monitor.status != monitorClosed {
monitor.status = monitorClosed
monitor.closer()
close(monitor.eventCh)
}
monitor.mtx.Unlock()
}
type Event struct{}
// AddMonitor to perform the monitor immediately. The channel will be closed after
// calling Close on Monitor or encountering a netlink conn error while Receive.
func (cc *Conn) AddMonitor(monitor *Monitor) (chan *Event, error) {
conn, closer, err := cc.netlinkConn()
if err != nil {
@ -190,24 +282,21 @@ func (cc *Conn) AddMonitor(monitor *Monitor) (chan *Event, error) {
monitor.conn = conn
monitor.closer = closer
if monitor.monitorFlags&(1<<unix.NFT_MSG_TRACE) != 0 {
err = conn.JoinGroup(uint32(unix.NFNLGRP_NFTRACE))
if err != nil {
monitor.closer()
return nil, err
}
}
if monitor.monitorFlags&^(1<<unix.NFT_MSG_TRACE) != 0 {
if monitor.monitorFlags != 0 {
err = conn.JoinGroup(uint32(unix.NFNLGRP_NFTABLES))
if err != nil {
monitor.closer()
return nil, err
}
conn.JoinGroup(uint32(unix.NFNLGRP_NFTRACE))
}
go monitor.monitor()
return monitor.eventCh, nil
}
func parseRuleFromMsg(msg netlink.Message) (*Rule, error) {
return nil, nil
genmsg := &NFGenMsg{}
genmsg.Decode(msg.Data[:4])
return ruleFromMsg(TableFamily(genmsg.NFGenFamily), msg)
}

9
obj.go
View File

@ -22,7 +22,10 @@ import (
"golang.org/x/sys/unix"
)
var objHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWOBJ)
var (
newObjHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWOBJ)
delObjHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELOBJ)
)
// Obj represents a netfilter stateful object. See also
// https://wiki.nftables.org/wiki-nftables/index.php/Stateful_objects
@ -125,8 +128,8 @@ func (cc *Conn) ResetObjects(t *Table) ([]Obj, error) {
}
func objFromMsg(msg netlink.Message) (Obj, error) {
if got, want := msg.Header.Type, objHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
if got, want1, want2 := msg.Header.Type, newObjHeaderType, delObjHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {

13
rule.go
View File

@ -25,7 +25,10 @@ import (
"golang.org/x/sys/unix"
)
var ruleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWRULE)
var (
newRuleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWRULE)
delRuleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE)
)
type ruleOperation uint32
@ -168,7 +171,7 @@ func (cc *Conn) newRule(r *Rule, op ruleOperation) *Rule {
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: ruleHeaderType,
Type: newRuleHeaderType,
Flags: flags,
},
Data: append(extraHeader(uint8(r.Table.Family), 0), msgData...),
@ -215,7 +218,7 @@ func (cc *Conn) DelRule(r *Rule) error {
cc.messages = append(cc.messages, netlink.Message{
Header: netlink.Header{
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE),
Type: delRuleHeaderType,
Flags: flags,
},
Data: append(extraHeader(uint8(r.Table.Family), 0), data...),
@ -225,8 +228,8 @@ func (cc *Conn) DelRule(r *Rule) error {
}
func ruleFromMsg(fam TableFamily, msg netlink.Message) (*Rule, error) {
if got, want := msg.Header.Type, ruleHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
if got, want1, want2 := msg.Header.Type, newRuleHeaderType, delRuleHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", msg.Header.Type, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {

18
set.go
View File

@ -664,11 +664,14 @@ func (cc *Conn) FlushSet(s *Set) {
})
}
var setHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSET)
var (
newSetHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSET)
delSetHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSET)
)
func setsFromMsg(msg netlink.Message) (*Set, error) {
if got, want := msg.Header.Type, setHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
if got, want1, want2 := msg.Header.Type, newSetHeaderType, delSetHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {
@ -738,11 +741,14 @@ func parseSetDatatype(magic uint32) (SetDatatype, error) {
return dt, nil
}
var elemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSETELEM)
var (
newElemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWSETELEM)
delElemHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELSETELEM)
)
func elementsFromMsg(msg netlink.Message) ([]SetElement, error) {
if got, want := msg.Header.Type, elemHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
if got, want1, want2 := msg.Header.Type, newElemHeaderType, delElemHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
if err != nil {

View File

@ -21,7 +21,10 @@ import (
"golang.org/x/sys/unix"
)
var tableHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWTABLE)
var (
newTableHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWTABLE)
delTableHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELTABLE)
)
// TableFamily specifies the address family for this table.
type TableFamily byte
@ -140,8 +143,8 @@ func (cc *Conn) ListTablesOfFamily(family TableFamily) ([]*Table, error) {
}
func tableFromMsg(msg netlink.Message) (*Table, error) {
if got, want := msg.Header.Type, tableHeaderType; got != want {
return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
if got, want1, want2 := msg.Header.Type, newTableHeaderType, delTableHeaderType; got != want1 && got != want2 {
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", got, want1, want2)
}
var t Table

19
util.go
View File

@ -15,6 +15,8 @@
package nftables
import (
"encoding/binary"
"github.com/google/nftables/binaryutil"
"golang.org/x/sys/unix"
)
@ -25,3 +27,20 @@ func extraHeader(family uint8, resID uint16) []byte {
unix.NFNETLINK_V0,
}, binaryutil.BigEndian.PutUint16(resID)...)
}
// General form of address family dependent message, see
// https://git.netfilter.org/libnftnl/tree/include/linux/netfilter/nfnetlink.h#29
type NFGenMsg struct {
NFGenFamily uint8
Version uint8
ResourceID uint16
}
func (genmsg *NFGenMsg) Decode(b []byte) {
if len(b) < 16 {
return
}
genmsg.NFGenFamily = b[0]
genmsg.Version = b[1]
genmsg.ResourceID = binary.BigEndian.Uint16(b[2:])
}