mirror of
https://github.com/vishvananda/netlink
synced 2024-12-17 20:24:44 +00:00
Implement chains support
This patch implements both tc and filter chains. We also need to align tc filter delition implementation with iprote2 to delete filters withichain by passing additional bits during filter deletion call.
This commit is contained in:
parent
e20cb98f77
commit
378a404a26
22
chain.go
Normal file
22
chain.go
Normal file
@ -0,0 +1,22 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Chain contains the attributes of a Chain
|
||||
type Chain struct {
|
||||
Parent uint32
|
||||
Chain uint32
|
||||
}
|
||||
|
||||
func (c Chain) String() string {
|
||||
return fmt.Sprintf("{Parent: %d, Chain: %d}", c.Parent, c.Chain)
|
||||
}
|
||||
|
||||
func NewChain(parent uint32, chain uint32) Chain {
|
||||
return Chain{
|
||||
Parent: parent,
|
||||
Chain: chain,
|
||||
}
|
||||
}
|
112
chain_linux.go
Normal file
112
chain_linux.go
Normal file
@ -0,0 +1,112 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ChainDel will delete a chain from the system.
|
||||
func ChainDel(link Link, chain Chain) error {
|
||||
// Equivalent to: `tc chain del $chain`
|
||||
return pkgHandle.ChainDel(link, chain)
|
||||
}
|
||||
|
||||
// ChainDel will delete a chain from the system.
|
||||
// Equivalent to: `tc chain del $chain`
|
||||
func (h *Handle) ChainDel(link Link, chain Chain) error {
|
||||
return h.chainModify(unix.RTM_DELCHAIN, 0, link, chain)
|
||||
}
|
||||
|
||||
// ChainAdd will add a chain to the system.
|
||||
// Equivalent to: `tc chain add`
|
||||
func ChainAdd(link Link, chain Chain) error {
|
||||
return pkgHandle.ChainAdd(link, chain)
|
||||
}
|
||||
|
||||
// ChainAdd will add a chain to the system.
|
||||
// Equivalent to: `tc chain add`
|
||||
func (h *Handle) ChainAdd(link Link, chain Chain) error {
|
||||
return h.chainModify(
|
||||
unix.RTM_NEWCHAIN,
|
||||
unix.NLM_F_CREATE|unix.NLM_F_EXCL,
|
||||
link,
|
||||
chain)
|
||||
}
|
||||
|
||||
func (h *Handle) chainModify(cmd, flags int, link Link, chain Chain) error {
|
||||
req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
|
||||
index := int32(0)
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
h.ensureIndex(base)
|
||||
index = int32(base.Index)
|
||||
}
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: index,
|
||||
Parent: chain.Parent,
|
||||
}
|
||||
req.AddData(msg)
|
||||
req.AddData(nl.NewRtAttr(nl.TCA_CHAIN, nl.Uint32Attr(chain.Chain)))
|
||||
|
||||
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// ChainList gets a list of chains in the system.
|
||||
// Equivalent to: `tc chain list`.
|
||||
// The list can be filtered by link.
|
||||
func ChainList(link Link, parent uint32) ([]Chain, error) {
|
||||
return pkgHandle.ChainList(link, parent)
|
||||
}
|
||||
|
||||
// ChainList gets a list of chains in the system.
|
||||
// Equivalent to: `tc chain list`.
|
||||
// The list can be filtered by link.
|
||||
func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
|
||||
req := h.newNetlinkRequest(unix.RTM_GETCHAIN, unix.NLM_F_DUMP)
|
||||
index := int32(0)
|
||||
if link != nil {
|
||||
base := link.Attrs()
|
||||
h.ensureIndex(base)
|
||||
index = int32(base.Index)
|
||||
}
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: index,
|
||||
Parent: parent,
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []Chain
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeTcMsg(m)
|
||||
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// skip chains from other interfaces
|
||||
if link != nil && msg.Ifindex != index {
|
||||
continue
|
||||
}
|
||||
|
||||
var chain Chain
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case nl.TCA_CHAIN:
|
||||
chain.Chain = native.Uint32(attr.Value)
|
||||
chain.Parent = parent
|
||||
}
|
||||
}
|
||||
res = append(res, chain)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
77
chain_test.go
Normal file
77
chain_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChainAddDel(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link, err := LinkByName("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkSetUp(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdisc := &Ingress{
|
||||
QdiscAttrs: QdiscAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Handle: MakeHandle(0xffff, 0),
|
||||
Parent: HANDLE_INGRESS,
|
||||
},
|
||||
}
|
||||
if err := QdiscAdd(qdisc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdiscs, err := SafeQdiscList(link)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(qdiscs) != 1 {
|
||||
t.Fatal("Failed to add qdisc")
|
||||
}
|
||||
_, ok := qdiscs[0].(*Ingress)
|
||||
if !ok {
|
||||
t.Fatal("Qdisc is the wrong type")
|
||||
}
|
||||
chainVal := new(uint32)
|
||||
*chainVal = 20
|
||||
chain := NewChain(HANDLE_INGRESS, *chainVal)
|
||||
err = ChainAdd(link, chain)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
chains, err := ChainList(link, HANDLE_INGRESS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(chains) != 1 {
|
||||
t.Fatal("Failed to add chain")
|
||||
}
|
||||
if chains[0].Chain != *chainVal {
|
||||
t.Fatal("Incorrect chain added")
|
||||
}
|
||||
if chains[0].Parent != HANDLE_INGRESS {
|
||||
t.Fatal("Incorrect chain parent")
|
||||
}
|
||||
if err := ChainDel(link, chain); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
chains, err = ChainList(link, HANDLE_INGRESS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(chains) != 0 {
|
||||
t.Fatal("Failed to remove chain")
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ type FilterAttrs struct {
|
||||
Parent uint32
|
||||
Priority uint16 // lower is higher priority
|
||||
Protocol uint16 // unix.ETH_P_*
|
||||
Chain *uint32
|
||||
}
|
||||
|
||||
func (q FilterAttrs) String() string {
|
||||
|
@ -206,19 +206,7 @@ func FilterDel(filter Filter) error {
|
||||
// FilterDel will delete a filter from the system.
|
||||
// Equivalent to: `tc filter del $filter`
|
||||
func (h *Handle) FilterDel(filter Filter) error {
|
||||
req := h.newNetlinkRequest(unix.RTM_DELTFILTER, unix.NLM_F_ACK)
|
||||
base := filter.Attrs()
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
Ifindex: int32(base.LinkIndex),
|
||||
Handle: base.Handle,
|
||||
Parent: base.Parent,
|
||||
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
|
||||
}
|
||||
req.AddData(msg)
|
||||
|
||||
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
return h.filterModify(filter, unix.RTM_DELTFILTER, 0)
|
||||
}
|
||||
|
||||
// FilterAdd will add a filter to the system.
|
||||
@ -230,7 +218,7 @@ func FilterAdd(filter Filter) error {
|
||||
// FilterAdd will add a filter to the system.
|
||||
// Equivalent to: `tc filter add $filter`
|
||||
func (h *Handle) FilterAdd(filter Filter) error {
|
||||
return h.filterModify(filter, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
|
||||
return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
|
||||
}
|
||||
|
||||
// FilterReplace will replace a filter.
|
||||
@ -242,11 +230,11 @@ func FilterReplace(filter Filter) error {
|
||||
// FilterReplace will replace a filter.
|
||||
// Equivalent to: `tc filter replace $filter`
|
||||
func (h *Handle) FilterReplace(filter Filter) error {
|
||||
return h.filterModify(filter, unix.NLM_F_CREATE)
|
||||
return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE)
|
||||
}
|
||||
|
||||
func (h *Handle) filterModify(filter Filter, flags int) error {
|
||||
req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK)
|
||||
func (h *Handle) filterModify(filter Filter, proto, flags int) error {
|
||||
req := h.newNetlinkRequest(proto, flags|unix.NLM_F_ACK)
|
||||
base := filter.Attrs()
|
||||
msg := &nl.TcMsg{
|
||||
Family: nl.FAMILY_ALL,
|
||||
@ -256,6 +244,9 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
|
||||
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
|
||||
}
|
||||
req.AddData(msg)
|
||||
if filter.Attrs().Chain != nil {
|
||||
req.AddData(nl.NewRtAttr(nl.TCA_CHAIN, nl.Uint32Attr(*filter.Attrs().Chain)))
|
||||
}
|
||||
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
|
||||
|
||||
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
|
||||
@ -470,6 +461,10 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
|
||||
default:
|
||||
detailed = true
|
||||
}
|
||||
case nl.TCA_CHAIN:
|
||||
val := new(uint32)
|
||||
*val = native.Uint32(attr.Value)
|
||||
base.Chain = val
|
||||
}
|
||||
}
|
||||
// only return the detailed version of the filter
|
||||
|
@ -2068,3 +2068,97 @@ func TestFilterU32PoliceAddDel(t *testing.T) {
|
||||
t.Fatal("Failed to remove qdisc")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterChainAddDel(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link, err := LinkByName("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkSetUp(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
redir, err := LinkByName("bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkSetUp(redir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdisc := &Ingress{
|
||||
QdiscAttrs: QdiscAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Handle: MakeHandle(0xffff, 0),
|
||||
Parent: HANDLE_INGRESS,
|
||||
},
|
||||
}
|
||||
if err := QdiscAdd(qdisc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdiscs, err := SafeQdiscList(link)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(qdiscs) != 1 {
|
||||
t.Fatal("Failed to add qdisc")
|
||||
}
|
||||
_, ok := qdiscs[0].(*Ingress)
|
||||
if !ok {
|
||||
t.Fatal("Qdisc is the wrong type")
|
||||
}
|
||||
classId := MakeHandle(1, 1)
|
||||
chainVal := new(uint32)
|
||||
*chainVal = 20
|
||||
filter := &U32{
|
||||
FilterAttrs: FilterAttrs{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Parent: MakeHandle(0xffff, 0),
|
||||
Priority: 1,
|
||||
Protocol: unix.ETH_P_IP,
|
||||
Chain: chainVal,
|
||||
},
|
||||
RedirIndex: redir.Attrs().Index,
|
||||
ClassId: classId,
|
||||
}
|
||||
if err := FilterAdd(filter); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filters, err := FilterList(link, MakeHandle(0xffff, 0))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(filters) != 1 {
|
||||
t.Fatal("Failed to add filter")
|
||||
}
|
||||
filterChain := filters[0].Attrs().Chain
|
||||
if filterChain != nil && *filterChain != *chainVal {
|
||||
t.Fatalf("Chain of the filter is the wrong value")
|
||||
}
|
||||
if err := FilterDel(filter); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filters, err = FilterList(link, MakeHandle(0xffff, 0))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(filters) != 0 {
|
||||
t.Fatal("Failed to remove filter")
|
||||
}
|
||||
if err := QdiscDel(qdisc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdiscs, err = SafeQdiscList(link)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(qdiscs) != 0 {
|
||||
t.Fatal("Failed to remove qdisc")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user