1package netlink
2
3import (
4	"fmt"
5	"syscall"
6
7	"github.com/vishvananda/netlink/nl"
8)
9
10// FilterDel will delete a filter from the system.
11// Equivalent to: `tc filter del $filter`
12func FilterDel(filter Filter) error {
13	req := nl.NewNetlinkRequest(syscall.RTM_DELTFILTER, syscall.NLM_F_ACK)
14	base := filter.Attrs()
15	msg := &nl.TcMsg{
16		Family:  nl.FAMILY_ALL,
17		Ifindex: int32(base.LinkIndex),
18		Handle:  base.Handle,
19		Parent:  base.Parent,
20		Info:    MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
21	}
22	req.AddData(msg)
23
24	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
25	return err
26}
27
28// FilterAdd will add a filter to the system.
29// Equivalent to: `tc filter add $filter`
30func FilterAdd(filter Filter) error {
31	req := nl.NewNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
32	base := filter.Attrs()
33	msg := &nl.TcMsg{
34		Family:  nl.FAMILY_ALL,
35		Ifindex: int32(base.LinkIndex),
36		Handle:  base.Handle,
37		Parent:  base.Parent,
38		Info:    MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
39	}
40	req.AddData(msg)
41	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
42
43	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
44	if u32, ok := filter.(*U32); ok {
45		// match all
46		sel := nl.TcU32Sel{
47			Nkeys: 1,
48			Flags: nl.TC_U32_TERMINAL,
49		}
50		sel.Keys = append(sel.Keys, nl.TcU32Key{})
51		nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
52		actions := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil)
53		table := nl.NewRtAttrChild(actions, nl.TCA_ACT_TAB, nil)
54		nl.NewRtAttrChild(table, nl.TCA_KIND, nl.ZeroTerminated("mirred"))
55		// redirect to other interface
56		mir := nl.TcMirred{
57			Action:  nl.TC_ACT_STOLEN,
58			Eaction: nl.TCA_EGRESS_REDIR,
59			Ifindex: uint32(u32.RedirIndex),
60		}
61		aopts := nl.NewRtAttrChild(table, nl.TCA_OPTIONS, nil)
62		nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mir.Serialize())
63	}
64	req.AddData(options)
65	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
66	return err
67}
68
69// FilterList gets a list of filters in the system.
70// Equivalent to: `tc filter show`.
71// Generally retunrs nothing if link and parent are not specified.
72func FilterList(link Link, parent uint32) ([]Filter, error) {
73	req := nl.NewNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP)
74	msg := &nl.TcMsg{
75		Family: nl.FAMILY_ALL,
76		Parent: parent,
77	}
78	if link != nil {
79		base := link.Attrs()
80		ensureIndex(base)
81		msg.Ifindex = int32(base.Index)
82	}
83	req.AddData(msg)
84
85	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTFILTER)
86	if err != nil {
87		return nil, err
88	}
89
90	var res []Filter
91	for _, m := range msgs {
92		msg := nl.DeserializeTcMsg(m)
93
94		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
95		if err != nil {
96			return nil, err
97		}
98
99		base := FilterAttrs{
100			LinkIndex: int(msg.Ifindex),
101			Handle:    msg.Handle,
102			Parent:    msg.Parent,
103		}
104		base.Priority, base.Protocol = MajorMinor(msg.Info)
105		base.Protocol = nl.Swap16(base.Protocol)
106
107		var filter Filter
108		filterType := ""
109		detailed := false
110		for _, attr := range attrs {
111			switch attr.Attr.Type {
112			case nl.TCA_KIND:
113				filterType = string(attr.Value[:len(attr.Value)-1])
114				switch filterType {
115				case "u32":
116					filter = &U32{}
117				default:
118					filter = &GenericFilter{FilterType: filterType}
119				}
120			case nl.TCA_OPTIONS:
121				switch filterType {
122				case "u32":
123					data, err := nl.ParseRouteAttr(attr.Value)
124					if err != nil {
125						return nil, err
126					}
127					detailed, err = parseU32Data(filter, data)
128					if err != nil {
129						return nil, err
130					}
131				}
132			}
133		}
134		// only return the detailed version of the filter
135		if detailed {
136			*filter.Attrs() = base
137			res = append(res, filter)
138		}
139	}
140
141	return res, nil
142}
143
144func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
145	native = nl.NativeEndian()
146	u32 := filter.(*U32)
147	detailed := false
148	for _, datum := range data {
149		switch datum.Attr.Type {
150		case nl.TCA_U32_SEL:
151			detailed = true
152			sel := nl.DeserializeTcU32Sel(datum.Value)
153			// only parse if we have a very basic redirect
154			if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 {
155				return detailed, nil
156			}
157		case nl.TCA_U32_ACT:
158			table, err := nl.ParseRouteAttr(datum.Value)
159			if err != nil {
160				return detailed, err
161			}
162			if len(table) != 1 || table[0].Attr.Type != nl.TCA_ACT_TAB {
163				return detailed, fmt.Errorf("Action table not formed properly")
164			}
165			aattrs, err := nl.ParseRouteAttr(table[0].Value)
166			for _, aattr := range aattrs {
167				switch aattr.Attr.Type {
168				case nl.TCA_KIND:
169					actionType := string(aattr.Value[:len(aattr.Value)-1])
170					// only parse if the action is mirred
171					if actionType != "mirred" {
172						return detailed, nil
173					}
174				case nl.TCA_OPTIONS:
175					adata, err := nl.ParseRouteAttr(aattr.Value)
176					if err != nil {
177						return detailed, err
178					}
179					for _, adatum := range adata {
180						switch adatum.Attr.Type {
181						case nl.TCA_MIRRED_PARMS:
182							mir := nl.DeserializeTcMirred(adatum.Value)
183							u32.RedirIndex = int(mir.Ifindex)
184						}
185					}
186				}
187			}
188		}
189	}
190	return detailed, nil
191}
192