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