1package netlink
2
3import (
4	"bytes"
5	"encoding/binary"
6	"encoding/hex"
7	"errors"
8	"fmt"
9	"net"
10	"syscall"
11
12	"github.com/vishvananda/netlink/nl"
13	"golang.org/x/sys/unix"
14)
15
16// Constants used in TcU32Sel.Flags.
17const (
18	TC_U32_TERMINAL  = nl.TC_U32_TERMINAL
19	TC_U32_OFFSET    = nl.TC_U32_OFFSET
20	TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
21	TC_U32_EAT       = nl.TC_U32_EAT
22)
23
24// Sel of the U32 filters that contains multiple TcU32Key. This is the type
25// alias and the frontend representation of nl.TcU32Sel. It is serialized into
26// canonical nl.TcU32Sel with the appropriate endianness.
27type TcU32Sel = nl.TcU32Sel
28
29// TcU32Key contained of Sel in the U32 filters. This is the type alias and the
30// frontend representation of nl.TcU32Key. It is serialized into chanonical
31// nl.TcU32Sel with the appropriate endianness.
32type TcU32Key = nl.TcU32Key
33
34// U32 filters on many packet related properties
35type U32 struct {
36	FilterAttrs
37	ClassId    uint32
38	Divisor    uint32 // Divisor MUST be power of 2.
39	Hash       uint32
40	RedirIndex int
41	Sel        *TcU32Sel
42	Actions    []Action
43}
44
45func (filter *U32) Attrs() *FilterAttrs {
46	return &filter.FilterAttrs
47}
48
49func (filter *U32) Type() string {
50	return "u32"
51}
52
53// Fw filter filters on firewall marks
54// NOTE: this is in filter_linux because it refers to nl.TcPolice which
55//       is defined in nl/tc_linux.go
56type Fw struct {
57	FilterAttrs
58	ClassId uint32
59	// TODO remove nl type from interface
60	Police nl.TcPolice
61	InDev  string
62	// TODO Action
63	Mask   uint32
64	AvRate uint32
65	Rtab   [256]uint32
66	Ptab   [256]uint32
67}
68
69func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) {
70	var rtab [256]uint32
71	var ptab [256]uint32
72	rcellLog := -1
73	pcellLog := -1
74	avrate := fattrs.AvRate / 8
75	police := nl.TcPolice{}
76	police.Rate.Rate = fattrs.Rate / 8
77	police.PeakRate.Rate = fattrs.PeakRate / 8
78	buffer := fattrs.Buffer
79	linklayer := nl.LINKLAYER_ETHERNET
80
81	if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC {
82		linklayer = fattrs.LinkLayer
83	}
84
85	police.Action = int32(fattrs.Action)
86	if police.Rate.Rate != 0 {
87		police.Rate.Mpu = fattrs.Mpu
88		police.Rate.Overhead = fattrs.Overhead
89		if CalcRtable(&police.Rate, rtab[:], rcellLog, fattrs.Mtu, linklayer) < 0 {
90			return nil, errors.New("TBF: failed to calculate rate table")
91		}
92		police.Burst = uint32(Xmittime(uint64(police.Rate.Rate), uint32(buffer)))
93	}
94	police.Mtu = fattrs.Mtu
95	if police.PeakRate.Rate != 0 {
96		police.PeakRate.Mpu = fattrs.Mpu
97		police.PeakRate.Overhead = fattrs.Overhead
98		if CalcRtable(&police.PeakRate, ptab[:], pcellLog, fattrs.Mtu, linklayer) < 0 {
99			return nil, errors.New("POLICE: failed to calculate peak rate table")
100		}
101	}
102
103	return &Fw{
104		FilterAttrs: attrs,
105		ClassId:     fattrs.ClassId,
106		InDev:       fattrs.InDev,
107		Mask:        fattrs.Mask,
108		Police:      police,
109		AvRate:      avrate,
110		Rtab:        rtab,
111		Ptab:        ptab,
112	}, nil
113}
114
115func (filter *Fw) Attrs() *FilterAttrs {
116	return &filter.FilterAttrs
117}
118
119func (filter *Fw) Type() string {
120	return "fw"
121}
122
123// FilterDel will delete a filter from the system.
124// Equivalent to: `tc filter del $filter`
125func FilterDel(filter Filter) error {
126	return pkgHandle.FilterDel(filter)
127}
128
129// FilterDel will delete a filter from the system.
130// Equivalent to: `tc filter del $filter`
131func (h *Handle) FilterDel(filter Filter) error {
132	req := h.newNetlinkRequest(unix.RTM_DELTFILTER, unix.NLM_F_ACK)
133	base := filter.Attrs()
134	msg := &nl.TcMsg{
135		Family:  nl.FAMILY_ALL,
136		Ifindex: int32(base.LinkIndex),
137		Handle:  base.Handle,
138		Parent:  base.Parent,
139		Info:    MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
140	}
141	req.AddData(msg)
142
143	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
144	return err
145}
146
147// FilterAdd will add a filter to the system.
148// Equivalent to: `tc filter add $filter`
149func FilterAdd(filter Filter) error {
150	return pkgHandle.FilterAdd(filter)
151}
152
153// FilterAdd will add a filter to the system.
154// Equivalent to: `tc filter add $filter`
155func (h *Handle) FilterAdd(filter Filter) error {
156	return h.filterModify(filter, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
157}
158
159// FilterReplace will replace a filter.
160// Equivalent to: `tc filter replace $filter`
161func FilterReplace(filter Filter) error {
162	return pkgHandle.FilterReplace(filter)
163}
164
165// FilterReplace will replace a filter.
166// Equivalent to: `tc filter replace $filter`
167func (h *Handle) FilterReplace(filter Filter) error {
168	return h.filterModify(filter, unix.NLM_F_CREATE)
169}
170
171func (h *Handle) filterModify(filter Filter, flags int) error {
172	native = nl.NativeEndian()
173	req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK)
174	base := filter.Attrs()
175	msg := &nl.TcMsg{
176		Family:  nl.FAMILY_ALL,
177		Ifindex: int32(base.LinkIndex),
178		Handle:  base.Handle,
179		Parent:  base.Parent,
180		Info:    MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
181	}
182	req.AddData(msg)
183	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
184
185	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
186
187	switch filter := filter.(type) {
188	case *U32:
189		sel := filter.Sel
190		if sel == nil {
191			// match all
192			sel = &nl.TcU32Sel{
193				Nkeys: 1,
194				Flags: nl.TC_U32_TERMINAL,
195			}
196			sel.Keys = append(sel.Keys, nl.TcU32Key{})
197		}
198
199		if native != networkOrder {
200			// Copy TcU32Sel.
201			cSel := *sel
202			keys := make([]nl.TcU32Key, cap(sel.Keys))
203			copy(keys, sel.Keys)
204			cSel.Keys = keys
205			sel = &cSel
206
207			// Handle the endianness of attributes
208			sel.Offmask = native.Uint16(htons(sel.Offmask))
209			sel.Hmask = native.Uint32(htonl(sel.Hmask))
210			for i, key := range sel.Keys {
211				sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
212				sel.Keys[i].Val = native.Uint32(htonl(key.Val))
213			}
214		}
215		sel.Nkeys = uint8(len(sel.Keys))
216		options.AddRtAttr(nl.TCA_U32_SEL, sel.Serialize())
217		if filter.ClassId != 0 {
218			options.AddRtAttr(nl.TCA_U32_CLASSID, nl.Uint32Attr(filter.ClassId))
219		}
220		if filter.Divisor != 0 {
221			if (filter.Divisor-1)&filter.Divisor != 0 {
222				return fmt.Errorf("illegal divisor %d. Must be a power of 2", filter.Divisor)
223			}
224			options.AddRtAttr(nl.TCA_U32_DIVISOR, nl.Uint32Attr(filter.Divisor))
225		}
226		if filter.Hash != 0 {
227			options.AddRtAttr(nl.TCA_U32_HASH, nl.Uint32Attr(filter.Hash))
228		}
229		actionsAttr := options.AddRtAttr(nl.TCA_U32_ACT, nil)
230		// backwards compatibility
231		if filter.RedirIndex != 0 {
232			filter.Actions = append([]Action{NewMirredAction(filter.RedirIndex)}, filter.Actions...)
233		}
234		if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
235			return err
236		}
237	case *Fw:
238		if filter.Mask != 0 {
239			b := make([]byte, 4)
240			native.PutUint32(b, filter.Mask)
241			options.AddRtAttr(nl.TCA_FW_MASK, b)
242		}
243		if filter.InDev != "" {
244			options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev))
245		}
246		if (filter.Police != nl.TcPolice{}) {
247
248			police := options.AddRtAttr(nl.TCA_FW_POLICE, nil)
249			police.AddRtAttr(nl.TCA_POLICE_TBF, filter.Police.Serialize())
250			if (filter.Police.Rate != nl.TcRateSpec{}) {
251				payload := SerializeRtab(filter.Rtab)
252				police.AddRtAttr(nl.TCA_POLICE_RATE, payload)
253			}
254			if (filter.Police.PeakRate != nl.TcRateSpec{}) {
255				payload := SerializeRtab(filter.Ptab)
256				police.AddRtAttr(nl.TCA_POLICE_PEAKRATE, payload)
257			}
258		}
259		if filter.ClassId != 0 {
260			b := make([]byte, 4)
261			native.PutUint32(b, filter.ClassId)
262			options.AddRtAttr(nl.TCA_FW_CLASSID, b)
263		}
264	case *BpfFilter:
265		var bpfFlags uint32
266		if filter.ClassId != 0 {
267			options.AddRtAttr(nl.TCA_BPF_CLASSID, nl.Uint32Attr(filter.ClassId))
268		}
269		if filter.Fd >= 0 {
270			options.AddRtAttr(nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd))))
271		}
272		if filter.Name != "" {
273			options.AddRtAttr(nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name))
274		}
275		if filter.DirectAction {
276			bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT
277		}
278		options.AddRtAttr(nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags))
279	case *MatchAll:
280		actionsAttr := options.AddRtAttr(nl.TCA_MATCHALL_ACT, nil)
281		if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
282			return err
283		}
284		if filter.ClassId != 0 {
285			options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId))
286		}
287	}
288
289	req.AddData(options)
290	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
291	return err
292}
293
294// FilterList gets a list of filters in the system.
295// Equivalent to: `tc filter show`.
296// Generally returns nothing if link and parent are not specified.
297func FilterList(link Link, parent uint32) ([]Filter, error) {
298	return pkgHandle.FilterList(link, parent)
299}
300
301// FilterList gets a list of filters in the system.
302// Equivalent to: `tc filter show`.
303// Generally returns nothing if link and parent are not specified.
304func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
305	req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP)
306	msg := &nl.TcMsg{
307		Family: nl.FAMILY_ALL,
308		Parent: parent,
309	}
310	if link != nil {
311		base := link.Attrs()
312		h.ensureIndex(base)
313		msg.Ifindex = int32(base.Index)
314	}
315	req.AddData(msg)
316
317	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
318	if err != nil {
319		return nil, err
320	}
321
322	var res []Filter
323	for _, m := range msgs {
324		msg := nl.DeserializeTcMsg(m)
325
326		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
327		if err != nil {
328			return nil, err
329		}
330
331		base := FilterAttrs{
332			LinkIndex: int(msg.Ifindex),
333			Handle:    msg.Handle,
334			Parent:    msg.Parent,
335		}
336		base.Priority, base.Protocol = MajorMinor(msg.Info)
337		base.Protocol = nl.Swap16(base.Protocol)
338
339		var filter Filter
340		filterType := ""
341		detailed := false
342		for _, attr := range attrs {
343			switch attr.Attr.Type {
344			case nl.TCA_KIND:
345				filterType = string(attr.Value[:len(attr.Value)-1])
346				switch filterType {
347				case "u32":
348					filter = &U32{}
349				case "fw":
350					filter = &Fw{}
351				case "bpf":
352					filter = &BpfFilter{}
353				case "matchall":
354					filter = &MatchAll{}
355				default:
356					filter = &GenericFilter{FilterType: filterType}
357				}
358			case nl.TCA_OPTIONS:
359				data, err := nl.ParseRouteAttr(attr.Value)
360				if err != nil {
361					return nil, err
362				}
363				switch filterType {
364				case "u32":
365					detailed, err = parseU32Data(filter, data)
366					if err != nil {
367						return nil, err
368					}
369				case "fw":
370					detailed, err = parseFwData(filter, data)
371					if err != nil {
372						return nil, err
373					}
374				case "bpf":
375					detailed, err = parseBpfData(filter, data)
376					if err != nil {
377						return nil, err
378					}
379				case "matchall":
380					detailed, err = parseMatchAllData(filter, data)
381					if err != nil {
382						return nil, err
383					}
384				default:
385					detailed = true
386				}
387			}
388		}
389		// only return the detailed version of the filter
390		if detailed {
391			*filter.Attrs() = base
392			res = append(res, filter)
393		}
394	}
395
396	return res, nil
397}
398
399func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) {
400	tcgen.Index = uint32(attrs.Index)
401	tcgen.Capab = uint32(attrs.Capab)
402	tcgen.Action = int32(attrs.Action)
403	tcgen.Refcnt = int32(attrs.Refcnt)
404	tcgen.Bindcnt = int32(attrs.Bindcnt)
405}
406
407func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
408	attrs.Index = int(tcgen.Index)
409	attrs.Capab = int(tcgen.Capab)
410	attrs.Action = TcAct(tcgen.Action)
411	attrs.Refcnt = int(tcgen.Refcnt)
412	attrs.Bindcnt = int(tcgen.Bindcnt)
413}
414
415func EncodeActions(attr *nl.RtAttr, actions []Action) error {
416	tabIndex := int(nl.TCA_ACT_TAB)
417
418	for _, action := range actions {
419		switch action := action.(type) {
420		default:
421			return fmt.Errorf("unknown action type %s", action.Type())
422		case *MirredAction:
423			table := attr.AddRtAttr(tabIndex, nil)
424			tabIndex++
425			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred"))
426			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
427			mirred := nl.TcMirred{
428				Eaction: int32(action.MirredAction),
429				Ifindex: uint32(action.Ifindex),
430			}
431			toTcGen(action.Attrs(), &mirred.TcGen)
432			aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize())
433		case *TunnelKeyAction:
434			table := attr.AddRtAttr(tabIndex, nil)
435			tabIndex++
436			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("tunnel_key"))
437			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
438			tun := nl.TcTunnelKey{
439				Action: int32(action.Action),
440			}
441			toTcGen(action.Attrs(), &tun.TcGen)
442			aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_PARMS, tun.Serialize())
443			if action.Action == TCA_TUNNEL_KEY_SET {
444				aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_KEY_ID, htonl(action.KeyID))
445				if v4 := action.SrcAddr.To4(); v4 != nil {
446					aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC, v4[:])
447				} else if v6 := action.SrcAddr.To16(); v6 != nil {
448					aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, v6[:])
449				} else {
450					return fmt.Errorf("invalid src addr %s for tunnel_key action", action.SrcAddr)
451				}
452				if v4 := action.DstAddr.To4(); v4 != nil {
453					aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_DST, v4[:])
454				} else if v6 := action.DstAddr.To16(); v6 != nil {
455					aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, v6[:])
456				} else {
457					return fmt.Errorf("invalid dst addr %s for tunnel_key action", action.DstAddr)
458				}
459			}
460		case *SkbEditAction:
461			table := attr.AddRtAttr(tabIndex, nil)
462			tabIndex++
463			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("skbedit"))
464			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
465			skbedit := nl.TcSkbEdit{}
466			toTcGen(action.Attrs(), &skbedit.TcGen)
467			aopts.AddRtAttr(nl.TCA_SKBEDIT_PARMS, skbedit.Serialize())
468			if action.QueueMapping != nil {
469				aopts.AddRtAttr(nl.TCA_SKBEDIT_QUEUE_MAPPING, nl.Uint16Attr(*action.QueueMapping))
470			}
471			if action.Priority != nil {
472				aopts.AddRtAttr(nl.TCA_SKBEDIT_PRIORITY, nl.Uint32Attr(*action.Priority))
473			}
474			if action.PType != nil {
475				aopts.AddRtAttr(nl.TCA_SKBEDIT_PTYPE, nl.Uint16Attr(*action.PType))
476			}
477			if action.Mark != nil {
478				aopts.AddRtAttr(nl.TCA_SKBEDIT_MARK, nl.Uint32Attr(*action.Mark))
479			}
480		case *ConnmarkAction:
481			table := attr.AddRtAttr(tabIndex, nil)
482			tabIndex++
483			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("connmark"))
484			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
485			connmark := nl.TcConnmark{
486				Zone: action.Zone,
487			}
488			toTcGen(action.Attrs(), &connmark.TcGen)
489			aopts.AddRtAttr(nl.TCA_CONNMARK_PARMS, connmark.Serialize())
490		case *BpfAction:
491			table := attr.AddRtAttr(tabIndex, nil)
492			tabIndex++
493			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf"))
494			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
495			gen := nl.TcGen{}
496			toTcGen(action.Attrs(), &gen)
497			aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize())
498			aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
499			aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
500		case *GenericAction:
501			table := attr.AddRtAttr(tabIndex, nil)
502			tabIndex++
503			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("gact"))
504			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
505			gen := nl.TcGen{}
506			toTcGen(action.Attrs(), &gen)
507			aopts.AddRtAttr(nl.TCA_GACT_PARMS, gen.Serialize())
508		}
509	}
510	return nil
511}
512
513func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
514	var actions []Action
515	for _, table := range tables {
516		var action Action
517		var actionType string
518		aattrs, err := nl.ParseRouteAttr(table.Value)
519		if err != nil {
520			return nil, err
521		}
522	nextattr:
523		for _, aattr := range aattrs {
524			switch aattr.Attr.Type {
525			case nl.TCA_KIND:
526				actionType = string(aattr.Value[:len(aattr.Value)-1])
527				// only parse if the action is mirred or bpf
528				switch actionType {
529				case "mirred":
530					action = &MirredAction{}
531				case "bpf":
532					action = &BpfAction{}
533				case "connmark":
534					action = &ConnmarkAction{}
535				case "gact":
536					action = &GenericAction{}
537				case "tunnel_key":
538					action = &TunnelKeyAction{}
539				case "skbedit":
540					action = &SkbEditAction{}
541				default:
542					break nextattr
543				}
544			case nl.TCA_OPTIONS:
545				adata, err := nl.ParseRouteAttr(aattr.Value)
546				if err != nil {
547					return nil, err
548				}
549				for _, adatum := range adata {
550					switch actionType {
551					case "mirred":
552						switch adatum.Attr.Type {
553						case nl.TCA_MIRRED_PARMS:
554							mirred := *nl.DeserializeTcMirred(adatum.Value)
555							action.(*MirredAction).ActionAttrs = ActionAttrs{}
556							toAttrs(&mirred.TcGen, action.Attrs())
557							action.(*MirredAction).Ifindex = int(mirred.Ifindex)
558							action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction)
559						}
560					case "tunnel_key":
561						switch adatum.Attr.Type {
562						case nl.TCA_TUNNEL_KEY_PARMS:
563							tun := *nl.DeserializeTunnelKey(adatum.Value)
564							action.(*TunnelKeyAction).ActionAttrs = ActionAttrs{}
565							toAttrs(&tun.TcGen, action.Attrs())
566							action.(*TunnelKeyAction).Action = TunnelKeyAct(tun.Action)
567						case nl.TCA_TUNNEL_KEY_ENC_KEY_ID:
568							action.(*TunnelKeyAction).KeyID = networkOrder.Uint32(adatum.Value[0:4])
569						case nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC:
570						case nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC:
571							action.(*TunnelKeyAction).SrcAddr = net.IP(adatum.Value[:])
572						case nl.TCA_TUNNEL_KEY_ENC_IPV6_DST:
573						case nl.TCA_TUNNEL_KEY_ENC_IPV4_DST:
574							action.(*TunnelKeyAction).DstAddr = net.IP(adatum.Value[:])
575						}
576					case "skbedit":
577						switch adatum.Attr.Type {
578						case nl.TCA_SKBEDIT_PARMS:
579							skbedit := *nl.DeserializeSkbEdit(adatum.Value)
580							action.(*SkbEditAction).ActionAttrs = ActionAttrs{}
581							toAttrs(&skbedit.TcGen, action.Attrs())
582						case nl.TCA_SKBEDIT_MARK:
583							mark := native.Uint32(adatum.Value[0:4])
584							action.(*SkbEditAction).Mark = &mark
585						case nl.TCA_SKBEDIT_PRIORITY:
586							priority := native.Uint32(adatum.Value[0:4])
587							action.(*SkbEditAction).Priority = &priority
588						case nl.TCA_SKBEDIT_PTYPE:
589							ptype := native.Uint16(adatum.Value[0:2])
590							action.(*SkbEditAction).PType = &ptype
591						case nl.TCA_SKBEDIT_QUEUE_MAPPING:
592							mapping := native.Uint16(adatum.Value[0:2])
593							action.(*SkbEditAction).QueueMapping = &mapping
594						}
595					case "bpf":
596						switch adatum.Attr.Type {
597						case nl.TCA_ACT_BPF_PARMS:
598							gen := *nl.DeserializeTcGen(adatum.Value)
599							toAttrs(&gen, action.Attrs())
600						case nl.TCA_ACT_BPF_FD:
601							action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4]))
602						case nl.TCA_ACT_BPF_NAME:
603							action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1])
604						}
605					case "connmark":
606						switch adatum.Attr.Type {
607						case nl.TCA_CONNMARK_PARMS:
608							connmark := *nl.DeserializeTcConnmark(adatum.Value)
609							action.(*ConnmarkAction).ActionAttrs = ActionAttrs{}
610							toAttrs(&connmark.TcGen, action.Attrs())
611							action.(*ConnmarkAction).Zone = connmark.Zone
612						}
613					case "gact":
614						switch adatum.Attr.Type {
615						case nl.TCA_GACT_PARMS:
616							gen := *nl.DeserializeTcGen(adatum.Value)
617							toAttrs(&gen, action.Attrs())
618						}
619					}
620				}
621			}
622		}
623		actions = append(actions, action)
624	}
625	return actions, nil
626}
627
628func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
629	native = nl.NativeEndian()
630	u32 := filter.(*U32)
631	detailed := false
632	for _, datum := range data {
633		switch datum.Attr.Type {
634		case nl.TCA_U32_SEL:
635			detailed = true
636			sel := nl.DeserializeTcU32Sel(datum.Value)
637			u32.Sel = sel
638			if native != networkOrder {
639				// Handle the endianness of attributes
640				u32.Sel.Offmask = native.Uint16(htons(sel.Offmask))
641				u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask))
642				for i, key := range u32.Sel.Keys {
643					u32.Sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
644					u32.Sel.Keys[i].Val = native.Uint32(htonl(key.Val))
645				}
646			}
647		case nl.TCA_U32_ACT:
648			tables, err := nl.ParseRouteAttr(datum.Value)
649			if err != nil {
650				return detailed, err
651			}
652			u32.Actions, err = parseActions(tables)
653			if err != nil {
654				return detailed, err
655			}
656			for _, action := range u32.Actions {
657				if action, ok := action.(*MirredAction); ok {
658					u32.RedirIndex = int(action.Ifindex)
659				}
660			}
661		case nl.TCA_U32_CLASSID:
662			u32.ClassId = native.Uint32(datum.Value)
663		case nl.TCA_U32_DIVISOR:
664			u32.Divisor = native.Uint32(datum.Value)
665		case nl.TCA_U32_HASH:
666			u32.Hash = native.Uint32(datum.Value)
667		}
668	}
669	return detailed, nil
670}
671
672func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
673	native = nl.NativeEndian()
674	fw := filter.(*Fw)
675	detailed := true
676	for _, datum := range data {
677		switch datum.Attr.Type {
678		case nl.TCA_FW_MASK:
679			fw.Mask = native.Uint32(datum.Value[0:4])
680		case nl.TCA_FW_CLASSID:
681			fw.ClassId = native.Uint32(datum.Value[0:4])
682		case nl.TCA_FW_INDEV:
683			fw.InDev = string(datum.Value[:len(datum.Value)-1])
684		case nl.TCA_FW_POLICE:
685			adata, _ := nl.ParseRouteAttr(datum.Value)
686			for _, aattr := range adata {
687				switch aattr.Attr.Type {
688				case nl.TCA_POLICE_TBF:
689					fw.Police = *nl.DeserializeTcPolice(aattr.Value)
690				case nl.TCA_POLICE_RATE:
691					fw.Rtab = DeserializeRtab(aattr.Value)
692				case nl.TCA_POLICE_PEAKRATE:
693					fw.Ptab = DeserializeRtab(aattr.Value)
694				}
695			}
696		}
697	}
698	return detailed, nil
699}
700
701func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
702	native = nl.NativeEndian()
703	bpf := filter.(*BpfFilter)
704	detailed := true
705	for _, datum := range data {
706		switch datum.Attr.Type {
707		case nl.TCA_BPF_FD:
708			bpf.Fd = int(native.Uint32(datum.Value[0:4]))
709		case nl.TCA_BPF_NAME:
710			bpf.Name = string(datum.Value[:len(datum.Value)-1])
711		case nl.TCA_BPF_CLASSID:
712			bpf.ClassId = native.Uint32(datum.Value[0:4])
713		case nl.TCA_BPF_FLAGS:
714			flags := native.Uint32(datum.Value[0:4])
715			if (flags & nl.TCA_BPF_FLAG_ACT_DIRECT) != 0 {
716				bpf.DirectAction = true
717			}
718		case nl.TCA_BPF_ID:
719			bpf.Id = int(native.Uint32(datum.Value[0:4]))
720		case nl.TCA_BPF_TAG:
721			bpf.Tag = hex.EncodeToString(datum.Value[:len(datum.Value)-1])
722		}
723	}
724	return detailed, nil
725}
726
727func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
728	native = nl.NativeEndian()
729	matchall := filter.(*MatchAll)
730	detailed := true
731	for _, datum := range data {
732		switch datum.Attr.Type {
733		case nl.TCA_MATCHALL_CLASSID:
734			matchall.ClassId = native.Uint32(datum.Value[0:4])
735		case nl.TCA_MATCHALL_ACT:
736			tables, err := nl.ParseRouteAttr(datum.Value)
737			if err != nil {
738				return detailed, err
739			}
740			matchall.Actions, err = parseActions(tables)
741			if err != nil {
742				return detailed, err
743			}
744		}
745	}
746	return detailed, nil
747}
748
749func AlignToAtm(size uint) uint {
750	var linksize, cells int
751	cells = int(size / nl.ATM_CELL_PAYLOAD)
752	if (size % nl.ATM_CELL_PAYLOAD) > 0 {
753		cells++
754	}
755	linksize = cells * nl.ATM_CELL_SIZE
756	return uint(linksize)
757}
758
759func AdjustSize(sz uint, mpu uint, linklayer int) uint {
760	if sz < mpu {
761		sz = mpu
762	}
763	switch linklayer {
764	case nl.LINKLAYER_ATM:
765		return AlignToAtm(sz)
766	default:
767		return sz
768	}
769}
770
771func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int {
772	bps := rate.Rate
773	mpu := rate.Mpu
774	var sz uint
775	if mtu == 0 {
776		mtu = 2047
777	}
778	if cellLog < 0 {
779		cellLog = 0
780		for (mtu >> uint(cellLog)) > 255 {
781			cellLog++
782		}
783	}
784	for i := 0; i < 256; i++ {
785		sz = AdjustSize(uint((i+1)<<uint32(cellLog)), uint(mpu), linklayer)
786		rtab[i] = uint32(Xmittime(uint64(bps), uint32(sz)))
787	}
788	rate.CellAlign = -1
789	rate.CellLog = uint8(cellLog)
790	rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK)
791	return cellLog
792}
793
794func DeserializeRtab(b []byte) [256]uint32 {
795	var rtab [256]uint32
796	native := nl.NativeEndian()
797	r := bytes.NewReader(b)
798	_ = binary.Read(r, native, &rtab)
799	return rtab
800}
801
802func SerializeRtab(rtab [256]uint32) []byte {
803	native := nl.NativeEndian()
804	var w bytes.Buffer
805	_ = binary.Write(&w, native, rtab)
806	return w.Bytes()
807}
808