Skip to content

Commit 378a404

Browse files
e0neaboch
authored andcommitted
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.
1 parent e20cb98 commit 378a404

File tree

6 files changed

+318
-17
lines changed

6 files changed

+318
-17
lines changed

Diff for: chain.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package netlink
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// Chain contains the attributes of a Chain
8+
type Chain struct {
9+
Parent uint32
10+
Chain uint32
11+
}
12+
13+
func (c Chain) String() string {
14+
return fmt.Sprintf("{Parent: %d, Chain: %d}", c.Parent, c.Chain)
15+
}
16+
17+
func NewChain(parent uint32, chain uint32) Chain {
18+
return Chain{
19+
Parent: parent,
20+
Chain: chain,
21+
}
22+
}

Diff for: chain_linux.go

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package netlink
2+
3+
import (
4+
"github.com/vishvananda/netlink/nl"
5+
"golang.org/x/sys/unix"
6+
)
7+
8+
// ChainDel will delete a chain from the system.
9+
func ChainDel(link Link, chain Chain) error {
10+
// Equivalent to: `tc chain del $chain`
11+
return pkgHandle.ChainDel(link, chain)
12+
}
13+
14+
// ChainDel will delete a chain from the system.
15+
// Equivalent to: `tc chain del $chain`
16+
func (h *Handle) ChainDel(link Link, chain Chain) error {
17+
return h.chainModify(unix.RTM_DELCHAIN, 0, link, chain)
18+
}
19+
20+
// ChainAdd will add a chain to the system.
21+
// Equivalent to: `tc chain add`
22+
func ChainAdd(link Link, chain Chain) error {
23+
return pkgHandle.ChainAdd(link, chain)
24+
}
25+
26+
// ChainAdd will add a chain to the system.
27+
// Equivalent to: `tc chain add`
28+
func (h *Handle) ChainAdd(link Link, chain Chain) error {
29+
return h.chainModify(
30+
unix.RTM_NEWCHAIN,
31+
unix.NLM_F_CREATE|unix.NLM_F_EXCL,
32+
link,
33+
chain)
34+
}
35+
36+
func (h *Handle) chainModify(cmd, flags int, link Link, chain Chain) error {
37+
req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
38+
index := int32(0)
39+
if link != nil {
40+
base := link.Attrs()
41+
h.ensureIndex(base)
42+
index = int32(base.Index)
43+
}
44+
msg := &nl.TcMsg{
45+
Family: nl.FAMILY_ALL,
46+
Ifindex: index,
47+
Parent: chain.Parent,
48+
}
49+
req.AddData(msg)
50+
req.AddData(nl.NewRtAttr(nl.TCA_CHAIN, nl.Uint32Attr(chain.Chain)))
51+
52+
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
53+
return err
54+
}
55+
56+
// ChainList gets a list of chains in the system.
57+
// Equivalent to: `tc chain list`.
58+
// The list can be filtered by link.
59+
func ChainList(link Link, parent uint32) ([]Chain, error) {
60+
return pkgHandle.ChainList(link, parent)
61+
}
62+
63+
// ChainList gets a list of chains in the system.
64+
// Equivalent to: `tc chain list`.
65+
// The list can be filtered by link.
66+
func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
67+
req := h.newNetlinkRequest(unix.RTM_GETCHAIN, unix.NLM_F_DUMP)
68+
index := int32(0)
69+
if link != nil {
70+
base := link.Attrs()
71+
h.ensureIndex(base)
72+
index = int32(base.Index)
73+
}
74+
msg := &nl.TcMsg{
75+
Family: nl.FAMILY_ALL,
76+
Ifindex: index,
77+
Parent: parent,
78+
}
79+
req.AddData(msg)
80+
81+
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
var res []Chain
87+
for _, m := range msgs {
88+
msg := nl.DeserializeTcMsg(m)
89+
90+
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
91+
if err != nil {
92+
return nil, err
93+
}
94+
95+
// skip chains from other interfaces
96+
if link != nil && msg.Ifindex != index {
97+
continue
98+
}
99+
100+
var chain Chain
101+
for _, attr := range attrs {
102+
switch attr.Attr.Type {
103+
case nl.TCA_CHAIN:
104+
chain.Chain = native.Uint32(attr.Value)
105+
chain.Parent = parent
106+
}
107+
}
108+
res = append(res, chain)
109+
}
110+
111+
return res, nil
112+
}

Diff for: chain_test.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//go:build linux
2+
// +build linux
3+
4+
package netlink
5+
6+
import (
7+
"testing"
8+
)
9+
10+
func TestChainAddDel(t *testing.T) {
11+
tearDown := setUpNetlinkTest(t)
12+
defer tearDown()
13+
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
14+
t.Fatal(err)
15+
}
16+
if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
17+
t.Fatal(err)
18+
}
19+
link, err := LinkByName("foo")
20+
if err != nil {
21+
t.Fatal(err)
22+
}
23+
if err := LinkSetUp(link); err != nil {
24+
t.Fatal(err)
25+
}
26+
qdisc := &Ingress{
27+
QdiscAttrs: QdiscAttrs{
28+
LinkIndex: link.Attrs().Index,
29+
Handle: MakeHandle(0xffff, 0),
30+
Parent: HANDLE_INGRESS,
31+
},
32+
}
33+
if err := QdiscAdd(qdisc); err != nil {
34+
t.Fatal(err)
35+
}
36+
qdiscs, err := SafeQdiscList(link)
37+
if err != nil {
38+
t.Fatal(err)
39+
}
40+
if len(qdiscs) != 1 {
41+
t.Fatal("Failed to add qdisc")
42+
}
43+
_, ok := qdiscs[0].(*Ingress)
44+
if !ok {
45+
t.Fatal("Qdisc is the wrong type")
46+
}
47+
chainVal := new(uint32)
48+
*chainVal = 20
49+
chain := NewChain(HANDLE_INGRESS, *chainVal)
50+
err = ChainAdd(link, chain)
51+
if err != nil {
52+
t.Fatal(err)
53+
}
54+
chains, err := ChainList(link, HANDLE_INGRESS)
55+
if err != nil {
56+
t.Fatal(err)
57+
}
58+
if len(chains) != 1 {
59+
t.Fatal("Failed to add chain")
60+
}
61+
if chains[0].Chain != *chainVal {
62+
t.Fatal("Incorrect chain added")
63+
}
64+
if chains[0].Parent != HANDLE_INGRESS {
65+
t.Fatal("Incorrect chain parent")
66+
}
67+
if err := ChainDel(link, chain); err != nil {
68+
t.Fatal(err)
69+
}
70+
chains, err = ChainList(link, HANDLE_INGRESS)
71+
if err != nil {
72+
t.Fatal(err)
73+
}
74+
if len(chains) != 0 {
75+
t.Fatal("Failed to remove chain")
76+
}
77+
}

Diff for: filter.go

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type FilterAttrs struct {
1919
Parent uint32
2020
Priority uint16 // lower is higher priority
2121
Protocol uint16 // unix.ETH_P_*
22+
Chain *uint32
2223
}
2324

2425
func (q FilterAttrs) String() string {

Diff for: filter_linux.go

+12-17
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,7 @@ func FilterDel(filter Filter) error {
206206
// FilterDel will delete a filter from the system.
207207
// Equivalent to: `tc filter del $filter`
208208
func (h *Handle) FilterDel(filter Filter) error {
209-
req := h.newNetlinkRequest(unix.RTM_DELTFILTER, unix.NLM_F_ACK)
210-
base := filter.Attrs()
211-
msg := &nl.TcMsg{
212-
Family: nl.FAMILY_ALL,
213-
Ifindex: int32(base.LinkIndex),
214-
Handle: base.Handle,
215-
Parent: base.Parent,
216-
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
217-
}
218-
req.AddData(msg)
219-
220-
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
221-
return err
209+
return h.filterModify(filter, unix.RTM_DELTFILTER, 0)
222210
}
223211

224212
// FilterAdd will add a filter to the system.
@@ -230,7 +218,7 @@ func FilterAdd(filter Filter) error {
230218
// FilterAdd will add a filter to the system.
231219
// Equivalent to: `tc filter add $filter`
232220
func (h *Handle) FilterAdd(filter Filter) error {
233-
return h.filterModify(filter, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
221+
return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
234222
}
235223

236224
// FilterReplace will replace a filter.
@@ -242,11 +230,11 @@ func FilterReplace(filter Filter) error {
242230
// FilterReplace will replace a filter.
243231
// Equivalent to: `tc filter replace $filter`
244232
func (h *Handle) FilterReplace(filter Filter) error {
245-
return h.filterModify(filter, unix.NLM_F_CREATE)
233+
return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE)
246234
}
247235

248-
func (h *Handle) filterModify(filter Filter, flags int) error {
249-
req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK)
236+
func (h *Handle) filterModify(filter Filter, proto, flags int) error {
237+
req := h.newNetlinkRequest(proto, flags|unix.NLM_F_ACK)
250238
base := filter.Attrs()
251239
msg := &nl.TcMsg{
252240
Family: nl.FAMILY_ALL,
@@ -256,6 +244,9 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
256244
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
257245
}
258246
req.AddData(msg)
247+
if filter.Attrs().Chain != nil {
248+
req.AddData(nl.NewRtAttr(nl.TCA_CHAIN, nl.Uint32Attr(*filter.Attrs().Chain)))
249+
}
259250
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
260251

261252
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
@@ -470,6 +461,10 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
470461
default:
471462
detailed = true
472463
}
464+
case nl.TCA_CHAIN:
465+
val := new(uint32)
466+
*val = native.Uint32(attr.Value)
467+
base.Chain = val
473468
}
474469
}
475470
// only return the detailed version of the filter

Diff for: filter_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -2068,3 +2068,97 @@ func TestFilterU32PoliceAddDel(t *testing.T) {
20682068
t.Fatal("Failed to remove qdisc")
20692069
}
20702070
}
2071+
2072+
func TestFilterChainAddDel(t *testing.T) {
2073+
tearDown := setUpNetlinkTest(t)
2074+
defer tearDown()
2075+
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
2076+
t.Fatal(err)
2077+
}
2078+
if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
2079+
t.Fatal(err)
2080+
}
2081+
link, err := LinkByName("foo")
2082+
if err != nil {
2083+
t.Fatal(err)
2084+
}
2085+
if err := LinkSetUp(link); err != nil {
2086+
t.Fatal(err)
2087+
}
2088+
redir, err := LinkByName("bar")
2089+
if err != nil {
2090+
t.Fatal(err)
2091+
}
2092+
if err := LinkSetUp(redir); err != nil {
2093+
t.Fatal(err)
2094+
}
2095+
qdisc := &Ingress{
2096+
QdiscAttrs: QdiscAttrs{
2097+
LinkIndex: link.Attrs().Index,
2098+
Handle: MakeHandle(0xffff, 0),
2099+
Parent: HANDLE_INGRESS,
2100+
},
2101+
}
2102+
if err := QdiscAdd(qdisc); err != nil {
2103+
t.Fatal(err)
2104+
}
2105+
qdiscs, err := SafeQdiscList(link)
2106+
if err != nil {
2107+
t.Fatal(err)
2108+
}
2109+
if len(qdiscs) != 1 {
2110+
t.Fatal("Failed to add qdisc")
2111+
}
2112+
_, ok := qdiscs[0].(*Ingress)
2113+
if !ok {
2114+
t.Fatal("Qdisc is the wrong type")
2115+
}
2116+
classId := MakeHandle(1, 1)
2117+
chainVal := new(uint32)
2118+
*chainVal = 20
2119+
filter := &U32{
2120+
FilterAttrs: FilterAttrs{
2121+
LinkIndex: link.Attrs().Index,
2122+
Parent: MakeHandle(0xffff, 0),
2123+
Priority: 1,
2124+
Protocol: unix.ETH_P_IP,
2125+
Chain: chainVal,
2126+
},
2127+
RedirIndex: redir.Attrs().Index,
2128+
ClassId: classId,
2129+
}
2130+
if err := FilterAdd(filter); err != nil {
2131+
t.Fatal(err)
2132+
}
2133+
filters, err := FilterList(link, MakeHandle(0xffff, 0))
2134+
if err != nil {
2135+
t.Fatal(err)
2136+
}
2137+
if len(filters) != 1 {
2138+
t.Fatal("Failed to add filter")
2139+
}
2140+
filterChain := filters[0].Attrs().Chain
2141+
if filterChain != nil && *filterChain != *chainVal {
2142+
t.Fatalf("Chain of the filter is the wrong value")
2143+
}
2144+
if err := FilterDel(filter); err != nil {
2145+
t.Fatal(err)
2146+
}
2147+
filters, err = FilterList(link, MakeHandle(0xffff, 0))
2148+
if err != nil {
2149+
t.Fatal(err)
2150+
}
2151+
if len(filters) != 0 {
2152+
t.Fatal("Failed to remove filter")
2153+
}
2154+
if err := QdiscDel(qdisc); err != nil {
2155+
t.Fatal(err)
2156+
}
2157+
qdiscs, err = SafeQdiscList(link)
2158+
if err != nil {
2159+
t.Fatal(err)
2160+
}
2161+
if len(qdiscs) != 0 {
2162+
t.Fatal("Failed to remove qdisc")
2163+
}
2164+
}

0 commit comments

Comments
 (0)