2019-05-03 16:54:09 -05:00
|
|
|
// Copyright 2018 Google LLC. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package nftables
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
|
2019-07-08 11:01:35 -05:00
|
|
|
"github.com/google/nftables/binaryutil"
|
2019-05-03 16:54:09 -05:00
|
|
|
"github.com/google/nftables/expr"
|
2022-07-29 11:32:59 -05:00
|
|
|
"github.com/google/nftables/internal/parseexprfunc"
|
2019-05-03 16:54:09 -05:00
|
|
|
"github.com/mdlayher/netlink"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
|
|
|
|
2025-02-26 05:27:31 -06:00
|
|
|
const (
|
2023-12-13 01:23:07 -06:00
|
|
|
newRuleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWRULE)
|
|
|
|
delRuleHeaderType = netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE)
|
|
|
|
)
|
2019-05-03 16:54:09 -05:00
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
// This constant is missing at unix.NFTA_RULE_POSITION_ID.
|
|
|
|
const nfta_rule_position_id = 0xa
|
|
|
|
|
2020-01-01 09:50:27 -06:00
|
|
|
type ruleOperation uint32
|
|
|
|
|
|
|
|
// Possible PayloadOperationType values.
|
|
|
|
const (
|
|
|
|
operationAdd ruleOperation = iota
|
|
|
|
operationInsert
|
|
|
|
operationReplace
|
|
|
|
)
|
|
|
|
|
2019-05-03 16:54:09 -05:00
|
|
|
// A Rule does something with a packet. See also
|
|
|
|
// https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management
|
|
|
|
type Rule struct {
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
Table *Table
|
|
|
|
Chain *Chain
|
|
|
|
// Position can be set to the Handle of another Rule to insert the new Rule
|
|
|
|
// before (InsertRule) or after (AddRule) the existing rule.
|
2019-07-08 11:01:35 -05:00
|
|
|
Position uint64
|
2022-01-29 12:26:06 -06:00
|
|
|
// The list of possible flags are specified by nftnl_rule_attr, see
|
|
|
|
// https://git.netfilter.org/libnftnl/tree/include/libnftnl/rule.h#n21
|
|
|
|
// Current nftables go implementation supports only
|
|
|
|
// NFTNL_RULE_POSITION flag for setting rule at position 0
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
Flags uint32
|
|
|
|
// PositionID can be set to the ID of another Rule, same as Position, for when
|
|
|
|
// the existing rule is not yet committed.
|
|
|
|
PositionID uint32
|
|
|
|
// Handle identifies an existing Rule. For a new Rule, this field is set
|
|
|
|
// during the Flush() in which the rule is committed. Make sure to not access
|
|
|
|
// this field concurrently with this Flush() to avoid data races.
|
|
|
|
Handle uint64
|
|
|
|
// ID is an identifier for a new Rule, which is assigned by
|
|
|
|
// AddRule/InsertRule, and only valid before the rule is committed by Flush().
|
|
|
|
// The field is set to 0 during Flush().
|
|
|
|
ID uint32
|
2019-07-08 11:01:35 -05:00
|
|
|
Exprs []expr.Any
|
2019-08-15 01:25:30 -05:00
|
|
|
UserData []byte
|
2019-05-03 16:54:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetRule returns the rules in the specified table and chain.
|
2022-05-08 13:39:12 -05:00
|
|
|
//
|
|
|
|
// Deprecated: use GetRules instead.
|
2019-05-03 16:54:09 -05:00
|
|
|
func (cc *Conn) GetRule(t *Table, c *Chain) ([]*Rule, error) {
|
2022-05-08 13:39:12 -05:00
|
|
|
return cc.GetRules(t, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRules returns the rules in the specified table and chain.
|
|
|
|
func (cc *Conn) GetRules(t *Table, c *Chain) ([]*Rule, error) {
|
2022-05-09 06:25:29 -05:00
|
|
|
conn, closer, err := cc.netlinkConn()
|
2019-05-03 16:54:09 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-09 06:25:29 -05:00
|
|
|
defer func() { _ = closer() }()
|
2019-05-03 16:54:09 -05:00
|
|
|
|
|
|
|
data, err := netlink.MarshalAttributes([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_TABLE, Data: []byte(t.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(c.Name + "\x00")},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := netlink.Message{
|
|
|
|
Header: netlink.Header{
|
|
|
|
Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETRULE),
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
Flags: netlink.Request | netlink.Acknowledge | netlink.Dump,
|
2019-05-03 16:54:09 -05:00
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(t.Family), 0), data...),
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := conn.SendMessages([]netlink.Message{message}); err != nil {
|
|
|
|
return nil, fmt.Errorf("SendMessages: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-10-02 09:01:48 -05:00
|
|
|
reply, err := receiveAckAware(conn, message.Header.Flags)
|
2019-05-03 16:54:09 -05:00
|
|
|
if err != nil {
|
2024-08-11 03:00:48 -05:00
|
|
|
return nil, fmt.Errorf("receiveAckAware: %v", err)
|
2019-05-03 16:54:09 -05:00
|
|
|
}
|
|
|
|
var rules []*Rule
|
|
|
|
for _, msg := range reply {
|
2022-05-14 11:45:18 -05:00
|
|
|
r, err := ruleFromMsg(t.Family, msg)
|
2019-05-03 16:54:09 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rules = append(rules, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
return rules, nil
|
|
|
|
}
|
|
|
|
|
2020-01-01 09:50:27 -06:00
|
|
|
func (cc *Conn) newRule(r *Rule, op ruleOperation) *Rule {
|
2022-05-09 06:25:29 -05:00
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
2019-05-03 16:54:09 -05:00
|
|
|
exprAttrs := make([]netlink.Attribute, len(r.Exprs))
|
|
|
|
for idx, expr := range r.Exprs {
|
|
|
|
exprAttrs[idx] = netlink.Attribute{
|
|
|
|
Type: unix.NLA_F_NESTED | unix.NFTA_LIST_ELEM,
|
2022-05-14 11:45:18 -05:00
|
|
|
Data: cc.marshalExpr(byte(r.Table.Family), expr),
|
2019-05-03 16:54:09 -05:00
|
|
|
}
|
|
|
|
}
|
2020-01-01 09:50:27 -06:00
|
|
|
|
2019-05-03 16:54:09 -05:00
|
|
|
data := cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_TABLE, Data: []byte(r.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(r.Chain.Name + "\x00")},
|
|
|
|
})
|
2020-01-01 09:50:27 -06:00
|
|
|
|
|
|
|
if r.Handle != 0 {
|
|
|
|
data = append(data, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_HANDLE, Data: binaryutil.BigEndian.PutUint64(r.Handle)},
|
|
|
|
})...)
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
} else {
|
|
|
|
r.ID = cc.allocateTransactionID()
|
|
|
|
data = append(data, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_ID, Data: binaryutil.BigEndian.PutUint32(r.ID)},
|
|
|
|
})...)
|
2020-01-01 09:50:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
data = append(data, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NLA_F_NESTED | unix.NFTA_RULE_EXPRESSIONS, Data: cc.marshalAttr(exprAttrs)},
|
|
|
|
})...)
|
|
|
|
|
2022-12-08 02:05:15 -06:00
|
|
|
if compatPolicy, err := getCompatPolicy(r.Exprs); err != nil {
|
|
|
|
cc.setErr(err)
|
|
|
|
} else if compatPolicy != nil {
|
|
|
|
data = append(data, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NLA_F_NESTED | unix.NFTA_RULE_COMPAT, Data: cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_COMPAT_PROTO, Data: binaryutil.BigEndian.PutUint32(compatPolicy.Proto)},
|
|
|
|
{Type: unix.NFTA_RULE_COMPAT_FLAGS, Data: binaryutil.BigEndian.PutUint32(compatPolicy.Flag & nft_RULE_COMPAT_F_MASK)},
|
|
|
|
})},
|
|
|
|
})...)
|
|
|
|
}
|
|
|
|
|
2019-07-08 11:01:35 -05:00
|
|
|
msgData := []byte{}
|
2020-01-01 09:50:27 -06:00
|
|
|
|
2019-07-08 11:01:35 -05:00
|
|
|
msgData = append(msgData, data...)
|
2019-08-15 01:25:30 -05:00
|
|
|
if r.UserData != nil {
|
2019-07-08 11:01:35 -05:00
|
|
|
msgData = append(msgData, cc.marshalAttr([]netlink.Attribute{
|
2019-08-15 01:25:30 -05:00
|
|
|
{Type: unix.NFTA_RULE_USERDATA, Data: r.UserData},
|
2019-07-08 11:01:35 -05:00
|
|
|
})...)
|
|
|
|
}
|
2020-01-01 09:50:27 -06:00
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
var flags netlink.HeaderFlags
|
|
|
|
var handleReply func(reply netlink.Message) error
|
2020-01-01 09:50:27 -06:00
|
|
|
switch op {
|
|
|
|
case operationAdd:
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
flags = netlink.Request | netlink.Acknowledge | netlink.Create | netlink.Echo | netlink.Append
|
|
|
|
handleReply = r.handleCreateReply
|
2020-01-01 09:50:27 -06:00
|
|
|
case operationInsert:
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
flags = netlink.Request | netlink.Acknowledge | netlink.Create | netlink.Echo
|
|
|
|
handleReply = r.handleCreateReply
|
2020-01-01 09:50:27 -06:00
|
|
|
case operationReplace:
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
flags = netlink.Request | netlink.Acknowledge | netlink.Replace
|
2020-01-01 09:50:27 -06:00
|
|
|
}
|
|
|
|
|
2022-01-29 12:26:06 -06:00
|
|
|
if r.Position != 0 || (r.Flags&(1<<unix.NFTA_RULE_POSITION)) != 0 {
|
2019-07-08 11:01:35 -05:00
|
|
|
msgData = append(msgData, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_POSITION, Data: binaryutil.BigEndian.PutUint64(r.Position)},
|
|
|
|
})...)
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
} else if r.PositionID != 0 {
|
|
|
|
msgData = append(msgData, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: nfta_rule_position_id, Data: binaryutil.BigEndian.PutUint32(r.PositionID)},
|
|
|
|
})...)
|
2019-07-08 11:01:35 -05:00
|
|
|
}
|
2019-08-19 16:35:24 -05:00
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
cc.messages = append(cc.messages, netlinkMessage{
|
2019-05-03 16:54:09 -05:00
|
|
|
Header: netlink.Header{
|
2023-12-13 01:23:07 -06:00
|
|
|
Type: newRuleHeaderType,
|
2019-07-08 11:01:35 -05:00
|
|
|
Flags: flags,
|
2019-05-03 16:54:09 -05:00
|
|
|
},
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
Data: append(extraHeader(uint8(r.Table.Family), 0), msgData...),
|
|
|
|
handleReply: handleReply,
|
2019-05-03 16:54:09 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
func (r *Rule) handleCreateReply(reply netlink.Message) error {
|
|
|
|
ad, err := netlink.NewAttributeDecoder(reply.Data[4:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
var handle uint64
|
|
|
|
for ad.Next() {
|
|
|
|
switch ad.Type() {
|
|
|
|
case unix.NFTA_RULE_HANDLE:
|
|
|
|
handle = ad.Uint64()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ad.Err() != nil {
|
|
|
|
return ad.Err()
|
|
|
|
}
|
|
|
|
if handle == 0 {
|
|
|
|
return fmt.Errorf("missing rule handle in create reply")
|
|
|
|
}
|
|
|
|
r.Handle = handle
|
|
|
|
r.ID = 0
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-01 09:50:27 -06:00
|
|
|
func (cc *Conn) ReplaceRule(r *Rule) *Rule {
|
|
|
|
return cc.newRule(r, operationReplace)
|
|
|
|
}
|
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
// AddRule inserts the specified Rule after the existing Rule referenced by
|
|
|
|
// Position/PositionID if set, otherwise at the end of the chain.
|
2020-01-01 09:50:27 -06:00
|
|
|
func (cc *Conn) AddRule(r *Rule) *Rule {
|
|
|
|
if r.Handle != 0 {
|
|
|
|
return cc.newRule(r, operationReplace)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cc.newRule(r, operationAdd)
|
|
|
|
}
|
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
// InsertRule inserts the specified Rule before the existing Rule referenced by
|
|
|
|
// Position/PositionID if set, otherwise at the beginning of the chain.
|
2020-01-01 09:50:27 -06:00
|
|
|
func (cc *Conn) InsertRule(r *Rule) *Rule {
|
|
|
|
if r.Handle != 0 {
|
|
|
|
return cc.newRule(r, operationReplace)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cc.newRule(r, operationInsert)
|
|
|
|
}
|
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
// DelRule deletes the specified Rule. Either the Handle or ID of the
|
|
|
|
// rule must be set.
|
2019-07-14 07:49:03 -05:00
|
|
|
func (cc *Conn) DelRule(r *Rule) error {
|
2022-05-09 06:25:29 -05:00
|
|
|
cc.mu.Lock()
|
|
|
|
defer cc.mu.Unlock()
|
2019-07-14 07:49:03 -05:00
|
|
|
data := cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_TABLE, Data: []byte(r.Table.Name + "\x00")},
|
|
|
|
{Type: unix.NFTA_RULE_CHAIN, Data: []byte(r.Chain.Name + "\x00")},
|
|
|
|
})
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
if r.Handle != 0 {
|
|
|
|
data = append(data, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_HANDLE, Data: binaryutil.BigEndian.PutUint64(r.Handle)},
|
|
|
|
})...)
|
|
|
|
} else if r.ID != 0 {
|
|
|
|
data = append(data, cc.marshalAttr([]netlink.Attribute{
|
|
|
|
{Type: unix.NFTA_RULE_ID, Data: binaryutil.BigEndian.PutUint32(r.ID)},
|
|
|
|
})...)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("rule must have a handle or ID")
|
2019-07-14 07:49:03 -05:00
|
|
|
}
|
|
|
|
flags := netlink.Request | netlink.Acknowledge
|
|
|
|
|
Set rule handle during flush
This change makes it possible to delete rules after inserting them,
without needing to query the rules first. Rules can be deleted both
before and after they are flushed. Additionally, this allows positioning
a new rule next to an existing rule, both before and after the existing
rule is flushed.
There are two ways to refer to a rule: Either by ID or by handle. The ID
is assigned by userspace, and is only valid within a transaction, so it
can only be used before the flush. The handle is assigned by the kernel
when the transaction is committed, and can thus only be used after the
flush. We thus need to set an ID on each newly created rule, and
retrieve the handle of the rule during the flush.
There was an existing mechanism to allocate IDs for sets, but this was
using a global counter without any synchronization to prevent data
races. I replaced this by a new mechanism which uses a connection-scoped
counter.
I implemented a new mechanism for retrieving replies in Flush, and
handling these replies by adding a callback to netlink messages. There
was some existing code to handle "overrun", which I deleted, because it
was nonsensical and just worked by accident. NLMSG_OVERRUN is in fact
not a flag, but a complete message type, so the (re&netlink.Overrun)
masking makes no sense. Even better, NLMSG_OVERRUN is never actually
used by Linux. What this code was actually doing was skipping over the
NFT_MSG_NEWRULE replies, and possibly a NFT_MSG_NEWGEN reply.
I had to update all existing tests which compared generated netlink
messages against a reference, by inserting the newly added ID attribute.
We also need to generate replies for the NFT_MSG_NEWRULE messages with a
handle added.
2025-02-20 13:12:30 -06:00
|
|
|
cc.messages = append(cc.messages, netlinkMessage{
|
2019-07-14 07:49:03 -05:00
|
|
|
Header: netlink.Header{
|
2023-12-13 01:23:07 -06:00
|
|
|
Type: delRuleHeaderType,
|
2019-07-14 07:49:03 -05:00
|
|
|
Flags: flags,
|
|
|
|
},
|
|
|
|
Data: append(extraHeader(uint8(r.Table.Family), 0), data...),
|
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-14 11:45:18 -05:00
|
|
|
func ruleFromMsg(fam TableFamily, msg netlink.Message) (*Rule, error) {
|
2023-12-13 01:23:07 -06:00
|
|
|
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)
|
2019-05-03 16:54:09 -05:00
|
|
|
}
|
|
|
|
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ad.ByteOrder = binary.BigEndian
|
|
|
|
var r Rule
|
|
|
|
for ad.Next() {
|
|
|
|
switch ad.Type() {
|
|
|
|
case unix.NFTA_RULE_TABLE:
|
2022-05-14 11:45:18 -05:00
|
|
|
r.Table = &Table{
|
|
|
|
Name: ad.String(),
|
|
|
|
Family: fam,
|
|
|
|
}
|
2019-05-03 16:54:09 -05:00
|
|
|
case unix.NFTA_RULE_CHAIN:
|
|
|
|
r.Chain = &Chain{Name: ad.String()}
|
|
|
|
case unix.NFTA_RULE_EXPRESSIONS:
|
|
|
|
ad.Do(func(b []byte) error {
|
2022-07-29 11:32:59 -05:00
|
|
|
exprs, err := parseexprfunc.ParseExprMsgFunc(byte(fam), b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.Exprs = make([]expr.Any, len(exprs))
|
|
|
|
for i := range exprs {
|
|
|
|
r.Exprs[i] = exprs[i].(expr.Any)
|
|
|
|
}
|
|
|
|
return nil
|
2019-05-03 16:54:09 -05:00
|
|
|
})
|
2019-07-08 11:01:35 -05:00
|
|
|
case unix.NFTA_RULE_POSITION:
|
|
|
|
r.Position = ad.Uint64()
|
|
|
|
case unix.NFTA_RULE_HANDLE:
|
|
|
|
r.Handle = ad.Uint64()
|
|
|
|
case unix.NFTA_RULE_USERDATA:
|
2019-08-15 01:25:30 -05:00
|
|
|
r.UserData = ad.Bytes()
|
2019-05-03 16:54:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return &r, ad.Err()
|
|
|
|
}
|