github.com/vishvananda/netlink@v1.3.0/filter_linux.go (about) 1 package netlink 2 3 import ( 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. 17 const ( 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. 27 type 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. 32 type TcU32Key = nl.TcU32Key 33 34 // U32 filters on many packet related properties 35 type U32 struct { 36 FilterAttrs 37 ClassId uint32 38 Divisor uint32 // Divisor MUST be power of 2. 39 Hash uint32 40 Link uint32 41 RedirIndex int 42 Sel *TcU32Sel 43 Actions []Action 44 Police *PoliceAction 45 } 46 47 func (filter *U32) Attrs() *FilterAttrs { 48 return &filter.FilterAttrs 49 } 50 51 func (filter *U32) Type() string { 52 return "u32" 53 } 54 55 type Flower struct { 56 FilterAttrs 57 DestIP net.IP 58 DestIPMask net.IPMask 59 SrcIP net.IP 60 SrcIPMask net.IPMask 61 EthType uint16 62 EncDestIP net.IP 63 EncDestIPMask net.IPMask 64 EncSrcIP net.IP 65 EncSrcIPMask net.IPMask 66 EncDestPort uint16 67 EncKeyId uint32 68 SkipHw bool 69 SkipSw bool 70 IPProto *nl.IPProto 71 DestPort uint16 72 SrcPort uint16 73 74 Actions []Action 75 } 76 77 func (filter *Flower) Attrs() *FilterAttrs { 78 return &filter.FilterAttrs 79 } 80 81 func (filter *Flower) Type() string { 82 return "flower" 83 } 84 85 func (filter *Flower) encodeIP(parent *nl.RtAttr, ip net.IP, mask net.IPMask, v4Type, v6Type int, v4MaskType, v6MaskType int) { 86 ipType := v4Type 87 maskType := v4MaskType 88 89 encodeMask := mask 90 if mask == nil { 91 encodeMask = net.CIDRMask(32, 32) 92 } 93 v4IP := ip.To4() 94 if v4IP == nil { 95 ipType = v6Type 96 maskType = v6MaskType 97 if mask == nil { 98 encodeMask = net.CIDRMask(128, 128) 99 } 100 } else { 101 ip = v4IP 102 } 103 104 parent.AddRtAttr(ipType, ip) 105 parent.AddRtAttr(maskType, encodeMask) 106 } 107 108 func (filter *Flower) encode(parent *nl.RtAttr) error { 109 if filter.EthType != 0 { 110 parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_TYPE, htons(filter.EthType)) 111 } 112 if filter.SrcIP != nil { 113 filter.encodeIP(parent, filter.SrcIP, filter.SrcIPMask, 114 nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC, 115 nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK) 116 } 117 if filter.DestIP != nil { 118 filter.encodeIP(parent, filter.DestIP, filter.DestIPMask, 119 nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST, 120 nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK) 121 } 122 if filter.EncSrcIP != nil { 123 filter.encodeIP(parent, filter.EncSrcIP, filter.EncSrcIPMask, 124 nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC, 125 nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK) 126 } 127 if filter.EncDestIP != nil { 128 filter.encodeIP(parent, filter.EncDestIP, filter.EncSrcIPMask, 129 nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST, 130 nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK) 131 } 132 if filter.EncDestPort != 0 { 133 parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT, htons(filter.EncDestPort)) 134 } 135 if filter.EncKeyId != 0 { 136 parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId)) 137 } 138 if filter.IPProto != nil { 139 ipproto := *filter.IPProto 140 parent.AddRtAttr(nl.TCA_FLOWER_KEY_IP_PROTO, ipproto.Serialize()) 141 if filter.SrcPort != 0 { 142 switch ipproto { 143 case nl.IPPROTO_TCP: 144 parent.AddRtAttr(nl.TCA_FLOWER_KEY_TCP_SRC, htons(filter.SrcPort)) 145 case nl.IPPROTO_UDP: 146 parent.AddRtAttr(nl.TCA_FLOWER_KEY_UDP_SRC, htons(filter.SrcPort)) 147 case nl.IPPROTO_SCTP: 148 parent.AddRtAttr(nl.TCA_FLOWER_KEY_SCTP_SRC, htons(filter.SrcPort)) 149 } 150 } 151 if filter.DestPort != 0 { 152 switch ipproto { 153 case nl.IPPROTO_TCP: 154 parent.AddRtAttr(nl.TCA_FLOWER_KEY_TCP_DST, htons(filter.DestPort)) 155 case nl.IPPROTO_UDP: 156 parent.AddRtAttr(nl.TCA_FLOWER_KEY_UDP_DST, htons(filter.DestPort)) 157 case nl.IPPROTO_SCTP: 158 parent.AddRtAttr(nl.TCA_FLOWER_KEY_SCTP_DST, htons(filter.DestPort)) 159 } 160 } 161 } 162 163 var flags uint32 = 0 164 if filter.SkipHw { 165 flags |= nl.TCA_CLS_FLAGS_SKIP_HW 166 } 167 if filter.SkipSw { 168 flags |= nl.TCA_CLS_FLAGS_SKIP_SW 169 } 170 parent.AddRtAttr(nl.TCA_FLOWER_FLAGS, htonl(flags)) 171 172 actionsAttr := parent.AddRtAttr(nl.TCA_FLOWER_ACT, nil) 173 if err := EncodeActions(actionsAttr, filter.Actions); err != nil { 174 return err 175 } 176 return nil 177 } 178 179 func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error { 180 for _, datum := range data { 181 switch datum.Attr.Type { 182 case nl.TCA_FLOWER_KEY_ETH_TYPE: 183 filter.EthType = ntohs(datum.Value) 184 case nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC: 185 filter.SrcIP = datum.Value 186 case nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK: 187 filter.SrcIPMask = datum.Value 188 case nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST: 189 filter.DestIP = datum.Value 190 case nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK: 191 filter.DestIPMask = datum.Value 192 case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC: 193 filter.EncSrcIP = datum.Value 194 case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK: 195 filter.EncSrcIPMask = datum.Value 196 case nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST: 197 filter.EncDestIP = datum.Value 198 case nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK: 199 filter.EncDestIPMask = datum.Value 200 case nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT: 201 filter.EncDestPort = ntohs(datum.Value) 202 case nl.TCA_FLOWER_KEY_ENC_KEY_ID: 203 filter.EncKeyId = ntohl(datum.Value) 204 case nl.TCA_FLOWER_KEY_IP_PROTO: 205 val := new(nl.IPProto) 206 *val = nl.IPProto(datum.Value[0]) 207 filter.IPProto = val 208 case nl.TCA_FLOWER_KEY_TCP_SRC, nl.TCA_FLOWER_KEY_UDP_SRC, nl.TCA_FLOWER_KEY_SCTP_SRC: 209 filter.SrcPort = ntohs(datum.Value) 210 case nl.TCA_FLOWER_KEY_TCP_DST, nl.TCA_FLOWER_KEY_UDP_DST, nl.TCA_FLOWER_KEY_SCTP_DST: 211 filter.DestPort = ntohs(datum.Value) 212 case nl.TCA_FLOWER_ACT: 213 tables, err := nl.ParseRouteAttr(datum.Value) 214 if err != nil { 215 return err 216 } 217 filter.Actions, err = parseActions(tables) 218 if err != nil { 219 return err 220 } 221 case nl.TCA_FLOWER_FLAGS: 222 attr := nl.DeserializeUint32Bitfield(datum.Value) 223 skipSw := attr.Value & nl.TCA_CLS_FLAGS_SKIP_HW 224 skipHw := attr.Value & nl.TCA_CLS_FLAGS_SKIP_SW 225 if skipSw != 0 { 226 filter.SkipSw = true 227 } 228 if skipHw != 0 { 229 filter.SkipHw = true 230 } 231 } 232 } 233 return nil 234 } 235 236 // FilterDel will delete a filter from the system. 237 // Equivalent to: `tc filter del $filter` 238 func FilterDel(filter Filter) error { 239 return pkgHandle.FilterDel(filter) 240 } 241 242 // FilterDel will delete a filter from the system. 243 // Equivalent to: `tc filter del $filter` 244 func (h *Handle) FilterDel(filter Filter) error { 245 return h.filterModify(filter, unix.RTM_DELTFILTER, 0) 246 } 247 248 // FilterAdd will add a filter to the system. 249 // Equivalent to: `tc filter add $filter` 250 func FilterAdd(filter Filter) error { 251 return pkgHandle.FilterAdd(filter) 252 } 253 254 // FilterAdd will add a filter to the system. 255 // Equivalent to: `tc filter add $filter` 256 func (h *Handle) FilterAdd(filter Filter) error { 257 return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE|unix.NLM_F_EXCL) 258 } 259 260 // FilterReplace will replace a filter. 261 // Equivalent to: `tc filter replace $filter` 262 func FilterReplace(filter Filter) error { 263 return pkgHandle.FilterReplace(filter) 264 } 265 266 // FilterReplace will replace a filter. 267 // Equivalent to: `tc filter replace $filter` 268 func (h *Handle) FilterReplace(filter Filter) error { 269 return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE) 270 } 271 272 func (h *Handle) filterModify(filter Filter, proto, flags int) error { 273 req := h.newNetlinkRequest(proto, flags|unix.NLM_F_ACK) 274 base := filter.Attrs() 275 msg := &nl.TcMsg{ 276 Family: nl.FAMILY_ALL, 277 Ifindex: int32(base.LinkIndex), 278 Handle: base.Handle, 279 Parent: base.Parent, 280 Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)), 281 } 282 req.AddData(msg) 283 if filter.Attrs().Chain != nil { 284 req.AddData(nl.NewRtAttr(nl.TCA_CHAIN, nl.Uint32Attr(*filter.Attrs().Chain))) 285 } 286 req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type()))) 287 288 options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) 289 290 switch filter := filter.(type) { 291 case *U32: 292 sel := filter.Sel 293 if sel == nil { 294 // match all 295 sel = &nl.TcU32Sel{ 296 Nkeys: 1, 297 Flags: nl.TC_U32_TERMINAL, 298 } 299 sel.Keys = append(sel.Keys, nl.TcU32Key{}) 300 } 301 302 if native != networkOrder { 303 // Copy TcU32Sel. 304 cSel := *sel 305 keys := make([]nl.TcU32Key, cap(sel.Keys)) 306 copy(keys, sel.Keys) 307 cSel.Keys = keys 308 sel = &cSel 309 310 // Handle the endianness of attributes 311 sel.Offmask = native.Uint16(htons(sel.Offmask)) 312 sel.Hmask = native.Uint32(htonl(sel.Hmask)) 313 for i, key := range sel.Keys { 314 sel.Keys[i].Mask = native.Uint32(htonl(key.Mask)) 315 sel.Keys[i].Val = native.Uint32(htonl(key.Val)) 316 } 317 } 318 sel.Nkeys = uint8(len(sel.Keys)) 319 options.AddRtAttr(nl.TCA_U32_SEL, sel.Serialize()) 320 if filter.ClassId != 0 { 321 options.AddRtAttr(nl.TCA_U32_CLASSID, nl.Uint32Attr(filter.ClassId)) 322 } 323 if filter.Divisor != 0 { 324 if (filter.Divisor-1)&filter.Divisor != 0 { 325 return fmt.Errorf("illegal divisor %d. Must be a power of 2", filter.Divisor) 326 } 327 options.AddRtAttr(nl.TCA_U32_DIVISOR, nl.Uint32Attr(filter.Divisor)) 328 } 329 if filter.Hash != 0 { 330 options.AddRtAttr(nl.TCA_U32_HASH, nl.Uint32Attr(filter.Hash)) 331 } 332 if filter.Link != 0 { 333 options.AddRtAttr(nl.TCA_U32_LINK, nl.Uint32Attr(filter.Link)) 334 } 335 if filter.Police != nil { 336 police := options.AddRtAttr(nl.TCA_U32_POLICE, nil) 337 if err := encodePolice(police, filter.Police); err != nil { 338 return err 339 } 340 } 341 actionsAttr := options.AddRtAttr(nl.TCA_U32_ACT, nil) 342 // backwards compatibility 343 if filter.RedirIndex != 0 { 344 filter.Actions = append([]Action{NewMirredAction(filter.RedirIndex)}, filter.Actions...) 345 } 346 if err := EncodeActions(actionsAttr, filter.Actions); err != nil { 347 return err 348 } 349 case *FwFilter: 350 if filter.Mask != 0 { 351 b := make([]byte, 4) 352 native.PutUint32(b, filter.Mask) 353 options.AddRtAttr(nl.TCA_FW_MASK, b) 354 } 355 if filter.InDev != "" { 356 options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev)) 357 } 358 if filter.Police != nil { 359 police := options.AddRtAttr(nl.TCA_FW_POLICE, nil) 360 if err := encodePolice(police, filter.Police); err != nil { 361 return err 362 } 363 } 364 if filter.ClassId != 0 { 365 b := make([]byte, 4) 366 native.PutUint32(b, filter.ClassId) 367 options.AddRtAttr(nl.TCA_FW_CLASSID, b) 368 } 369 actionsAttr := options.AddRtAttr(nl.TCA_FW_ACT, nil) 370 if err := EncodeActions(actionsAttr, filter.Actions); err != nil { 371 return err 372 } 373 case *BpfFilter: 374 var bpfFlags uint32 375 if filter.ClassId != 0 { 376 options.AddRtAttr(nl.TCA_BPF_CLASSID, nl.Uint32Attr(filter.ClassId)) 377 } 378 if filter.Fd >= 0 { 379 options.AddRtAttr(nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd)))) 380 } 381 if filter.Name != "" { 382 options.AddRtAttr(nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name)) 383 } 384 if filter.DirectAction { 385 bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT 386 } 387 options.AddRtAttr(nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags)) 388 case *MatchAll: 389 actionsAttr := options.AddRtAttr(nl.TCA_MATCHALL_ACT, nil) 390 if err := EncodeActions(actionsAttr, filter.Actions); err != nil { 391 return err 392 } 393 if filter.ClassId != 0 { 394 options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId)) 395 } 396 case *Flower: 397 if err := filter.encode(options); err != nil { 398 return err 399 } 400 } 401 req.AddData(options) 402 _, err := req.Execute(unix.NETLINK_ROUTE, 0) 403 return err 404 } 405 406 // FilterList gets a list of filters in the system. 407 // Equivalent to: `tc filter show`. 408 // Generally returns nothing if link and parent are not specified. 409 func FilterList(link Link, parent uint32) ([]Filter, error) { 410 return pkgHandle.FilterList(link, parent) 411 } 412 413 // FilterList gets a list of filters in the system. 414 // Equivalent to: `tc filter show`. 415 // Generally returns nothing if link and parent are not specified. 416 func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) { 417 req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP) 418 msg := &nl.TcMsg{ 419 Family: nl.FAMILY_ALL, 420 Parent: parent, 421 } 422 if link != nil { 423 base := link.Attrs() 424 h.ensureIndex(base) 425 msg.Ifindex = int32(base.Index) 426 } 427 req.AddData(msg) 428 429 msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER) 430 if err != nil { 431 return nil, err 432 } 433 434 var res []Filter 435 for _, m := range msgs { 436 msg := nl.DeserializeTcMsg(m) 437 438 attrs, err := nl.ParseRouteAttr(m[msg.Len():]) 439 if err != nil { 440 return nil, err 441 } 442 443 base := FilterAttrs{ 444 LinkIndex: int(msg.Ifindex), 445 Handle: msg.Handle, 446 Parent: msg.Parent, 447 } 448 base.Priority, base.Protocol = MajorMinor(msg.Info) 449 base.Protocol = nl.Swap16(base.Protocol) 450 451 var filter Filter 452 filterType := "" 453 detailed := false 454 for _, attr := range attrs { 455 switch attr.Attr.Type { 456 case nl.TCA_KIND: 457 filterType = string(attr.Value[:len(attr.Value)-1]) 458 switch filterType { 459 case "u32": 460 filter = &U32{} 461 case "fw": 462 filter = &FwFilter{} 463 case "bpf": 464 filter = &BpfFilter{} 465 case "matchall": 466 filter = &MatchAll{} 467 case "flower": 468 filter = &Flower{} 469 default: 470 filter = &GenericFilter{FilterType: filterType} 471 } 472 case nl.TCA_OPTIONS: 473 data, err := nl.ParseRouteAttr(attr.Value) 474 if err != nil { 475 return nil, err 476 } 477 switch filterType { 478 case "u32": 479 detailed, err = parseU32Data(filter, data) 480 if err != nil { 481 return nil, err 482 } 483 case "fw": 484 detailed, err = parseFwData(filter, data) 485 if err != nil { 486 return nil, err 487 } 488 case "bpf": 489 detailed, err = parseBpfData(filter, data) 490 if err != nil { 491 return nil, err 492 } 493 case "matchall": 494 detailed, err = parseMatchAllData(filter, data) 495 if err != nil { 496 return nil, err 497 } 498 case "flower": 499 detailed, err = parseFlowerData(filter, data) 500 if err != nil { 501 return nil, err 502 } 503 default: 504 detailed = true 505 } 506 case nl.TCA_CHAIN: 507 val := new(uint32) 508 *val = native.Uint32(attr.Value) 509 base.Chain = val 510 } 511 } 512 // only return the detailed version of the filter 513 if detailed { 514 *filter.Attrs() = base 515 res = append(res, filter) 516 } 517 } 518 519 return res, nil 520 } 521 522 func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) { 523 tcgen.Index = uint32(attrs.Index) 524 tcgen.Capab = uint32(attrs.Capab) 525 tcgen.Action = int32(attrs.Action) 526 tcgen.Refcnt = int32(attrs.Refcnt) 527 tcgen.Bindcnt = int32(attrs.Bindcnt) 528 } 529 530 func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) { 531 attrs.Index = int(tcgen.Index) 532 attrs.Capab = int(tcgen.Capab) 533 attrs.Action = TcAct(tcgen.Action) 534 attrs.Refcnt = int(tcgen.Refcnt) 535 attrs.Bindcnt = int(tcgen.Bindcnt) 536 } 537 538 func toTimeStamp(tcf *nl.Tcf) *ActionTimestamp { 539 return &ActionTimestamp{ 540 Installed: tcf.Install, 541 LastUsed: tcf.LastUse, 542 Expires: tcf.Expires, 543 FirstUsed: tcf.FirstUse} 544 } 545 546 func encodePolice(attr *nl.RtAttr, action *PoliceAction) error { 547 var rtab [256]uint32 548 var ptab [256]uint32 549 police := nl.TcPolice{} 550 police.Index = uint32(action.Attrs().Index) 551 police.Bindcnt = int32(action.Attrs().Bindcnt) 552 police.Capab = uint32(action.Attrs().Capab) 553 police.Refcnt = int32(action.Attrs().Refcnt) 554 police.Rate.Rate = action.Rate 555 police.PeakRate.Rate = action.PeakRate 556 police.Action = int32(action.ExceedAction) 557 558 if police.Rate.Rate != 0 { 559 police.Rate.Mpu = action.Mpu 560 police.Rate.Overhead = action.Overhead 561 if CalcRtable(&police.Rate, rtab[:], action.RCellLog, action.Mtu, action.LinkLayer) < 0 { 562 return errors.New("TBF: failed to calculate rate table") 563 } 564 police.Burst = Xmittime(uint64(police.Rate.Rate), action.Burst) 565 } 566 567 police.Mtu = action.Mtu 568 if police.PeakRate.Rate != 0 { 569 police.PeakRate.Mpu = action.Mpu 570 police.PeakRate.Overhead = action.Overhead 571 if CalcRtable(&police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 { 572 return errors.New("POLICE: failed to calculate peak rate table") 573 } 574 } 575 576 attr.AddRtAttr(nl.TCA_POLICE_TBF, police.Serialize()) 577 if police.Rate.Rate != 0 { 578 attr.AddRtAttr(nl.TCA_POLICE_RATE, SerializeRtab(rtab)) 579 } 580 if police.PeakRate.Rate != 0 { 581 attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE, SerializeRtab(ptab)) 582 } 583 if action.AvRate != 0 { 584 attr.AddRtAttr(nl.TCA_POLICE_AVRATE, nl.Uint32Attr(action.AvRate)) 585 } 586 if action.NotExceedAction != 0 { 587 attr.AddRtAttr(nl.TCA_POLICE_RESULT, nl.Uint32Attr(uint32(action.NotExceedAction))) 588 } 589 590 return nil 591 } 592 593 func EncodeActions(attr *nl.RtAttr, actions []Action) error { 594 tabIndex := int(nl.TCA_ACT_TAB) 595 596 for _, action := range actions { 597 switch action := action.(type) { 598 default: 599 return fmt.Errorf("unknown action type %s", action.Type()) 600 case *PoliceAction: 601 table := attr.AddRtAttr(tabIndex, nil) 602 tabIndex++ 603 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("police")) 604 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 605 if err := encodePolice(aopts, action); err != nil { 606 return err 607 } 608 case *MirredAction: 609 table := attr.AddRtAttr(tabIndex, nil) 610 tabIndex++ 611 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred")) 612 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 613 mirred := nl.TcMirred{ 614 Eaction: int32(action.MirredAction), 615 Ifindex: uint32(action.Ifindex), 616 } 617 toTcGen(action.Attrs(), &mirred.TcGen) 618 aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize()) 619 case *TunnelKeyAction: 620 table := attr.AddRtAttr(tabIndex, nil) 621 tabIndex++ 622 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("tunnel_key")) 623 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 624 tun := nl.TcTunnelKey{ 625 Action: int32(action.Action), 626 } 627 toTcGen(action.Attrs(), &tun.TcGen) 628 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_PARMS, tun.Serialize()) 629 if action.Action == TCA_TUNNEL_KEY_SET { 630 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_KEY_ID, htonl(action.KeyID)) 631 if v4 := action.SrcAddr.To4(); v4 != nil { 632 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC, v4[:]) 633 } else if v6 := action.SrcAddr.To16(); v6 != nil { 634 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, v6[:]) 635 } else { 636 return fmt.Errorf("invalid src addr %s for tunnel_key action", action.SrcAddr) 637 } 638 if v4 := action.DstAddr.To4(); v4 != nil { 639 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_DST, v4[:]) 640 } else if v6 := action.DstAddr.To16(); v6 != nil { 641 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, v6[:]) 642 } else { 643 return fmt.Errorf("invalid dst addr %s for tunnel_key action", action.DstAddr) 644 } 645 if action.DestPort != 0 { 646 aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_DST_PORT, htons(action.DestPort)) 647 } 648 } 649 case *SkbEditAction: 650 table := attr.AddRtAttr(tabIndex, nil) 651 tabIndex++ 652 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("skbedit")) 653 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 654 skbedit := nl.TcSkbEdit{} 655 toTcGen(action.Attrs(), &skbedit.TcGen) 656 aopts.AddRtAttr(nl.TCA_SKBEDIT_PARMS, skbedit.Serialize()) 657 if action.QueueMapping != nil { 658 aopts.AddRtAttr(nl.TCA_SKBEDIT_QUEUE_MAPPING, nl.Uint16Attr(*action.QueueMapping)) 659 } 660 if action.Priority != nil { 661 aopts.AddRtAttr(nl.TCA_SKBEDIT_PRIORITY, nl.Uint32Attr(*action.Priority)) 662 } 663 if action.PType != nil { 664 aopts.AddRtAttr(nl.TCA_SKBEDIT_PTYPE, nl.Uint16Attr(*action.PType)) 665 } 666 if action.Mark != nil { 667 aopts.AddRtAttr(nl.TCA_SKBEDIT_MARK, nl.Uint32Attr(*action.Mark)) 668 } 669 if action.Mask != nil { 670 aopts.AddRtAttr(nl.TCA_SKBEDIT_MASK, nl.Uint32Attr(*action.Mask)) 671 } 672 case *ConnmarkAction: 673 table := attr.AddRtAttr(tabIndex, nil) 674 tabIndex++ 675 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("connmark")) 676 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 677 connmark := nl.TcConnmark{ 678 Zone: action.Zone, 679 } 680 toTcGen(action.Attrs(), &connmark.TcGen) 681 aopts.AddRtAttr(nl.TCA_CONNMARK_PARMS, connmark.Serialize()) 682 case *CsumAction: 683 table := attr.AddRtAttr(tabIndex, nil) 684 tabIndex++ 685 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("csum")) 686 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 687 csum := nl.TcCsum{ 688 UpdateFlags: uint32(action.UpdateFlags), 689 } 690 toTcGen(action.Attrs(), &csum.TcGen) 691 aopts.AddRtAttr(nl.TCA_CSUM_PARMS, csum.Serialize()) 692 case *BpfAction: 693 table := attr.AddRtAttr(tabIndex, nil) 694 tabIndex++ 695 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf")) 696 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 697 gen := nl.TcGen{} 698 toTcGen(action.Attrs(), &gen) 699 aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize()) 700 aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd))) 701 aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name)) 702 case *GenericAction: 703 table := attr.AddRtAttr(tabIndex, nil) 704 tabIndex++ 705 table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("gact")) 706 aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) 707 gen := nl.TcGen{} 708 toTcGen(action.Attrs(), &gen) 709 aopts.AddRtAttr(nl.TCA_GACT_PARMS, gen.Serialize()) 710 case *PeditAction: 711 table := attr.AddRtAttr(tabIndex, nil) 712 tabIndex++ 713 pedit := nl.TcPedit{} 714 if action.SrcMacAddr != nil { 715 pedit.SetEthSrc(action.SrcMacAddr) 716 } 717 if action.DstMacAddr != nil { 718 pedit.SetEthDst(action.DstMacAddr) 719 } 720 if action.SrcIP != nil { 721 pedit.SetSrcIP(action.SrcIP) 722 } 723 if action.DstIP != nil { 724 pedit.SetDstIP(action.DstIP) 725 } 726 if action.SrcPort != 0 { 727 pedit.SetSrcPort(action.SrcPort, action.Proto) 728 } 729 if action.DstPort != 0 { 730 pedit.SetDstPort(action.DstPort, action.Proto) 731 } 732 pedit.Encode(table) 733 } 734 } 735 return nil 736 } 737 738 func parsePolice(data syscall.NetlinkRouteAttr, police *PoliceAction) { 739 switch data.Attr.Type { 740 case nl.TCA_POLICE_RESULT: 741 police.NotExceedAction = TcPolAct(native.Uint32(data.Value[0:4])) 742 case nl.TCA_POLICE_AVRATE: 743 police.AvRate = native.Uint32(data.Value[0:4]) 744 case nl.TCA_POLICE_TBF: 745 p := *nl.DeserializeTcPolice(data.Value) 746 police.ActionAttrs = ActionAttrs{} 747 police.Attrs().Index = int(p.Index) 748 police.Attrs().Bindcnt = int(p.Bindcnt) 749 police.Attrs().Capab = int(p.Capab) 750 police.Attrs().Refcnt = int(p.Refcnt) 751 police.ExceedAction = TcPolAct(p.Action) 752 police.Rate = p.Rate.Rate 753 police.PeakRate = p.PeakRate.Rate 754 police.Burst = Xmitsize(uint64(p.Rate.Rate), p.Burst) 755 police.Mtu = p.Mtu 756 police.LinkLayer = int(p.Rate.Linklayer) & nl.TC_LINKLAYER_MASK 757 police.Overhead = p.Rate.Overhead 758 } 759 } 760 761 func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { 762 var actions []Action 763 for _, table := range tables { 764 var action Action 765 var actionType string 766 var actionnStatistic *ActionStatistic 767 var actionTimestamp *ActionTimestamp 768 aattrs, err := nl.ParseRouteAttr(table.Value) 769 if err != nil { 770 return nil, err 771 } 772 nextattr: 773 for _, aattr := range aattrs { 774 switch aattr.Attr.Type { 775 case nl.TCA_KIND: 776 actionType = string(aattr.Value[:len(aattr.Value)-1]) 777 // only parse if the action is mirred or bpf 778 switch actionType { 779 case "mirred": 780 action = &MirredAction{} 781 case "bpf": 782 action = &BpfAction{} 783 case "connmark": 784 action = &ConnmarkAction{} 785 case "csum": 786 action = &CsumAction{} 787 case "gact": 788 action = &GenericAction{} 789 case "tunnel_key": 790 action = &TunnelKeyAction{} 791 case "skbedit": 792 action = &SkbEditAction{} 793 case "police": 794 action = &PoliceAction{} 795 case "pedit": 796 action = &PeditAction{} 797 default: 798 break nextattr 799 } 800 case nl.TCA_OPTIONS: 801 adata, err := nl.ParseRouteAttr(aattr.Value) 802 if err != nil { 803 return nil, err 804 } 805 for _, adatum := range adata { 806 switch actionType { 807 case "mirred": 808 switch adatum.Attr.Type { 809 case nl.TCA_MIRRED_PARMS: 810 mirred := *nl.DeserializeTcMirred(adatum.Value) 811 action.(*MirredAction).ActionAttrs = ActionAttrs{} 812 toAttrs(&mirred.TcGen, action.Attrs()) 813 action.(*MirredAction).Ifindex = int(mirred.Ifindex) 814 action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction) 815 case nl.TCA_MIRRED_TM: 816 tcTs := nl.DeserializeTcf(adatum.Value) 817 actionTimestamp = toTimeStamp(tcTs) 818 } 819 820 case "tunnel_key": 821 switch adatum.Attr.Type { 822 case nl.TCA_TUNNEL_KEY_PARMS: 823 tun := *nl.DeserializeTunnelKey(adatum.Value) 824 action.(*TunnelKeyAction).ActionAttrs = ActionAttrs{} 825 toAttrs(&tun.TcGen, action.Attrs()) 826 action.(*TunnelKeyAction).Action = TunnelKeyAct(tun.Action) 827 case nl.TCA_TUNNEL_KEY_ENC_KEY_ID: 828 action.(*TunnelKeyAction).KeyID = networkOrder.Uint32(adatum.Value[0:4]) 829 case nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC: 830 action.(*TunnelKeyAction).SrcAddr = adatum.Value[:] 831 case nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, nl.TCA_TUNNEL_KEY_ENC_IPV4_DST: 832 action.(*TunnelKeyAction).DstAddr = adatum.Value[:] 833 case nl.TCA_TUNNEL_KEY_ENC_DST_PORT: 834 action.(*TunnelKeyAction).DestPort = ntohs(adatum.Value) 835 case nl.TCA_TUNNEL_KEY_TM: 836 tcTs := nl.DeserializeTcf(adatum.Value) 837 actionTimestamp = toTimeStamp(tcTs) 838 } 839 case "skbedit": 840 switch adatum.Attr.Type { 841 case nl.TCA_SKBEDIT_PARMS: 842 skbedit := *nl.DeserializeSkbEdit(adatum.Value) 843 action.(*SkbEditAction).ActionAttrs = ActionAttrs{} 844 toAttrs(&skbedit.TcGen, action.Attrs()) 845 case nl.TCA_SKBEDIT_MARK: 846 mark := native.Uint32(adatum.Value[0:4]) 847 action.(*SkbEditAction).Mark = &mark 848 case nl.TCA_SKBEDIT_MASK: 849 mask := native.Uint32(adatum.Value[0:4]) 850 action.(*SkbEditAction).Mask = &mask 851 case nl.TCA_SKBEDIT_PRIORITY: 852 priority := native.Uint32(adatum.Value[0:4]) 853 action.(*SkbEditAction).Priority = &priority 854 case nl.TCA_SKBEDIT_PTYPE: 855 ptype := native.Uint16(adatum.Value[0:2]) 856 action.(*SkbEditAction).PType = &ptype 857 case nl.TCA_SKBEDIT_QUEUE_MAPPING: 858 mapping := native.Uint16(adatum.Value[0:2]) 859 action.(*SkbEditAction).QueueMapping = &mapping 860 case nl.TCA_SKBEDIT_TM: 861 tcTs := nl.DeserializeTcf(adatum.Value) 862 actionTimestamp = toTimeStamp(tcTs) 863 } 864 case "bpf": 865 switch adatum.Attr.Type { 866 case nl.TCA_ACT_BPF_PARMS: 867 gen := *nl.DeserializeTcGen(adatum.Value) 868 toAttrs(&gen, action.Attrs()) 869 case nl.TCA_ACT_BPF_FD: 870 action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4])) 871 case nl.TCA_ACT_BPF_NAME: 872 action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1]) 873 case nl.TCA_ACT_BPF_TM: 874 tcTs := nl.DeserializeTcf(adatum.Value) 875 actionTimestamp = toTimeStamp(tcTs) 876 } 877 case "connmark": 878 switch adatum.Attr.Type { 879 case nl.TCA_CONNMARK_PARMS: 880 connmark := *nl.DeserializeTcConnmark(adatum.Value) 881 action.(*ConnmarkAction).ActionAttrs = ActionAttrs{} 882 toAttrs(&connmark.TcGen, action.Attrs()) 883 action.(*ConnmarkAction).Zone = connmark.Zone 884 case nl.TCA_CONNMARK_TM: 885 tcTs := nl.DeserializeTcf(adatum.Value) 886 actionTimestamp = toTimeStamp(tcTs) 887 } 888 case "csum": 889 switch adatum.Attr.Type { 890 case nl.TCA_CSUM_PARMS: 891 csum := *nl.DeserializeTcCsum(adatum.Value) 892 action.(*CsumAction).ActionAttrs = ActionAttrs{} 893 toAttrs(&csum.TcGen, action.Attrs()) 894 action.(*CsumAction).UpdateFlags = CsumUpdateFlags(csum.UpdateFlags) 895 case nl.TCA_CSUM_TM: 896 tcTs := nl.DeserializeTcf(adatum.Value) 897 actionTimestamp = toTimeStamp(tcTs) 898 } 899 case "gact": 900 switch adatum.Attr.Type { 901 case nl.TCA_GACT_PARMS: 902 gen := *nl.DeserializeTcGen(adatum.Value) 903 toAttrs(&gen, action.Attrs()) 904 if action.Attrs().Action.String() == "goto" { 905 action.(*GenericAction).Chain = TC_ACT_EXT_VAL_MASK & gen.Action 906 } 907 case nl.TCA_GACT_TM: 908 tcTs := nl.DeserializeTcf(adatum.Value) 909 actionTimestamp = toTimeStamp(tcTs) 910 } 911 case "police": 912 parsePolice(adatum, action.(*PoliceAction)) 913 } 914 } 915 case nl.TCA_ACT_STATS: 916 s, err := parseTcStats2(aattr.Value) 917 if err != nil { 918 return nil, err 919 } 920 actionnStatistic = (*ActionStatistic)(s) 921 } 922 } 923 action.Attrs().Statistics = actionnStatistic 924 action.Attrs().Timestamp = actionTimestamp 925 actions = append(actions, action) 926 } 927 return actions, nil 928 } 929 930 func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { 931 u32 := filter.(*U32) 932 detailed := false 933 for _, datum := range data { 934 switch datum.Attr.Type { 935 case nl.TCA_U32_SEL: 936 detailed = true 937 sel := nl.DeserializeTcU32Sel(datum.Value) 938 u32.Sel = sel 939 if native != networkOrder { 940 // Handle the endianness of attributes 941 u32.Sel.Offmask = native.Uint16(htons(sel.Offmask)) 942 u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask)) 943 for i, key := range u32.Sel.Keys { 944 u32.Sel.Keys[i].Mask = native.Uint32(htonl(key.Mask)) 945 u32.Sel.Keys[i].Val = native.Uint32(htonl(key.Val)) 946 } 947 } 948 case nl.TCA_U32_ACT: 949 tables, err := nl.ParseRouteAttr(datum.Value) 950 if err != nil { 951 return detailed, err 952 } 953 u32.Actions, err = parseActions(tables) 954 if err != nil { 955 return detailed, err 956 } 957 for _, action := range u32.Actions { 958 if action, ok := action.(*MirredAction); ok { 959 u32.RedirIndex = int(action.Ifindex) 960 } 961 } 962 case nl.TCA_U32_POLICE: 963 var police PoliceAction 964 adata, _ := nl.ParseRouteAttr(datum.Value) 965 for _, aattr := range adata { 966 parsePolice(aattr, &police) 967 } 968 u32.Police = &police 969 case nl.TCA_U32_CLASSID: 970 u32.ClassId = native.Uint32(datum.Value) 971 case nl.TCA_U32_DIVISOR: 972 u32.Divisor = native.Uint32(datum.Value) 973 case nl.TCA_U32_HASH: 974 u32.Hash = native.Uint32(datum.Value) 975 case nl.TCA_U32_LINK: 976 u32.Link = native.Uint32(datum.Value) 977 } 978 } 979 return detailed, nil 980 } 981 982 func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { 983 fw := filter.(*FwFilter) 984 detailed := true 985 for _, datum := range data { 986 switch datum.Attr.Type { 987 case nl.TCA_FW_MASK: 988 fw.Mask = native.Uint32(datum.Value[0:4]) 989 case nl.TCA_FW_CLASSID: 990 fw.ClassId = native.Uint32(datum.Value[0:4]) 991 case nl.TCA_FW_INDEV: 992 fw.InDev = string(datum.Value[:len(datum.Value)-1]) 993 case nl.TCA_FW_POLICE: 994 var police PoliceAction 995 adata, _ := nl.ParseRouteAttr(datum.Value) 996 for _, aattr := range adata { 997 parsePolice(aattr, &police) 998 } 999 fw.Police = &police 1000 case nl.TCA_FW_ACT: 1001 tables, err := nl.ParseRouteAttr(datum.Value) 1002 if err != nil { 1003 return detailed, err 1004 } 1005 fw.Actions, err = parseActions(tables) 1006 if err != nil { 1007 return detailed, err 1008 } 1009 } 1010 } 1011 return detailed, nil 1012 } 1013 1014 func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { 1015 bpf := filter.(*BpfFilter) 1016 detailed := true 1017 for _, datum := range data { 1018 switch datum.Attr.Type { 1019 case nl.TCA_BPF_FD: 1020 bpf.Fd = int(native.Uint32(datum.Value[0:4])) 1021 case nl.TCA_BPF_NAME: 1022 bpf.Name = string(datum.Value[:len(datum.Value)-1]) 1023 case nl.TCA_BPF_CLASSID: 1024 bpf.ClassId = native.Uint32(datum.Value[0:4]) 1025 case nl.TCA_BPF_FLAGS: 1026 flags := native.Uint32(datum.Value[0:4]) 1027 if (flags & nl.TCA_BPF_FLAG_ACT_DIRECT) != 0 { 1028 bpf.DirectAction = true 1029 } 1030 case nl.TCA_BPF_ID: 1031 bpf.Id = int(native.Uint32(datum.Value[0:4])) 1032 case nl.TCA_BPF_TAG: 1033 bpf.Tag = hex.EncodeToString(datum.Value) 1034 } 1035 } 1036 return detailed, nil 1037 } 1038 1039 func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { 1040 matchall := filter.(*MatchAll) 1041 detailed := true 1042 for _, datum := range data { 1043 switch datum.Attr.Type { 1044 case nl.TCA_MATCHALL_CLASSID: 1045 matchall.ClassId = native.Uint32(datum.Value[0:4]) 1046 case nl.TCA_MATCHALL_ACT: 1047 tables, err := nl.ParseRouteAttr(datum.Value) 1048 if err != nil { 1049 return detailed, err 1050 } 1051 matchall.Actions, err = parseActions(tables) 1052 if err != nil { 1053 return detailed, err 1054 } 1055 } 1056 } 1057 return detailed, nil 1058 } 1059 1060 func parseFlowerData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { 1061 return true, filter.(*Flower).decode(data) 1062 } 1063 1064 func AlignToAtm(size uint) uint { 1065 var linksize, cells int 1066 cells = int(size / nl.ATM_CELL_PAYLOAD) 1067 if (size % nl.ATM_CELL_PAYLOAD) > 0 { 1068 cells++ 1069 } 1070 linksize = cells * nl.ATM_CELL_SIZE 1071 return uint(linksize) 1072 } 1073 1074 func AdjustSize(sz uint, mpu uint, linklayer int) uint { 1075 if sz < mpu { 1076 sz = mpu 1077 } 1078 switch linklayer { 1079 case nl.LINKLAYER_ATM: 1080 return AlignToAtm(sz) 1081 default: 1082 return sz 1083 } 1084 } 1085 1086 func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int { 1087 bps := rate.Rate 1088 mpu := rate.Mpu 1089 var sz uint 1090 if mtu == 0 { 1091 mtu = 2047 1092 } 1093 if cellLog < 0 { 1094 cellLog = 0 1095 for (mtu >> uint(cellLog)) > 255 { 1096 cellLog++ 1097 } 1098 } 1099 for i := 0; i < 256; i++ { 1100 sz = AdjustSize(uint((i+1)<<uint32(cellLog)), uint(mpu), linklayer) 1101 rtab[i] = Xmittime(uint64(bps), uint32(sz)) 1102 } 1103 rate.CellAlign = -1 1104 rate.CellLog = uint8(cellLog) 1105 rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK) 1106 return cellLog 1107 } 1108 1109 func DeserializeRtab(b []byte) [256]uint32 { 1110 var rtab [256]uint32 1111 r := bytes.NewReader(b) 1112 _ = binary.Read(r, native, &rtab) 1113 return rtab 1114 } 1115 1116 func SerializeRtab(rtab [256]uint32) []byte { 1117 var w bytes.Buffer 1118 _ = binary.Write(&w, native, rtab) 1119 return w.Bytes() 1120 }