...

Source file src/edge-infra.dev/pkg/lib/kernel/netlink/tc/u32.go

Documentation: edge-infra.dev/pkg/lib/kernel/netlink/tc

     1  //go:build linux
     2  
     3  package tc
     4  
     5  import (
     6  	"fmt"
     7  	"net"
     8  
     9  	"github.com/vishvananda/netlink"
    10  
    11  	"edge-infra.dev/pkg/lib/kernel/netlink/ip"
    12  )
    13  
    14  type MatchIP struct {
    15  	ip   *net.IP
    16  	mask *net.IP
    17  	*ip.IPv4HeaderBitOffset
    18  }
    19  
    20  func NewMatchIP() *MatchIP {
    21  	return &MatchIP{}
    22  }
    23  
    24  func (m *MatchIP) WithIP(ip *net.IP) *MatchIP {
    25  	m.ip = ip
    26  	return m
    27  }
    28  
    29  func (m *MatchIP) WithMask(mask *net.IP) *MatchIP {
    30  	m.mask = mask
    31  	return m
    32  }
    33  
    34  func (m *MatchIP) WithIPNet(ipnet *net.IPNet) *MatchIP {
    35  	m.ip = &ipnet.IP
    36  	mask := net.IP(ipnet.Mask)
    37  	m.mask = &mask
    38  	return m
    39  }
    40  
    41  func (m *MatchIP) WithIPv4HeaderOffset(offset ip.IPv4HeaderBitOffset) *MatchIP {
    42  	m.IPv4HeaderBitOffset = &offset
    43  	return m
    44  }
    45  
    46  type MatchPort struct {
    47  	port uint32
    48  	mask uint32
    49  }
    50  
    51  func NewMatchPort() *MatchPort {
    52  	return &MatchPort{}
    53  }
    54  
    55  // overrides any existing match port set
    56  func (m *MatchPort) WithDstPort(dstPort uint32) *MatchPort {
    57  	m.port = dstPort
    58  	m.mask = ip.DstPortMask
    59  	return m
    60  }
    61  
    62  // overrides any existing match port set
    63  func (m *MatchPort) WithSrcPort(srcPort uint32) *MatchPort {
    64  	m.port = srcPort
    65  	m.mask = ip.SrcPortMask
    66  	return m
    67  }
    68  
    69  type U32Filter struct {
    70  	matchIP   []*MatchIP
    71  	matchPort []*MatchPort
    72  	attrs     netlink.FilterAttrs
    73  	classid   uint32
    74  	actions   []netlink.Action
    75  	divisor   uint32
    76  	hashtable uint32
    77  }
    78  
    79  func NewU32Filter() *U32Filter {
    80  	return &U32Filter{}
    81  }
    82  
    83  func (f *U32Filter) WithHashTable(hashtable uint32) *U32Filter {
    84  	f.hashtable = hashtable
    85  	return f
    86  }
    87  
    88  func (f *U32Filter) WithDivisor(divisor uint32) *U32Filter {
    89  	f.divisor = divisor
    90  	return f
    91  }
    92  
    93  func (f *U32Filter) WithMatchIP(m *MatchIP) *U32Filter {
    94  	f.matchIP = append(f.matchIP, m)
    95  	return f
    96  }
    97  
    98  func (f *U32Filter) WithMatchPort(m *MatchPort) *U32Filter {
    99  	f.matchPort = append(f.matchPort, m)
   100  	return f
   101  }
   102  
   103  func (f *U32Filter) WithAttrs(attrs netlink.FilterAttrs) *U32Filter {
   104  	f.attrs = attrs
   105  	return f
   106  }
   107  
   108  func (f *U32Filter) WithAction(action netlink.Action) *U32Filter {
   109  	f.actions = append(f.actions, action)
   110  	return f
   111  }
   112  
   113  func (f *U32Filter) WithClassID(classid uint32) *U32Filter {
   114  	f.classid = classid
   115  	return f
   116  }
   117  
   118  func (f *U32Filter) WithMakeClassID(major, minor uint16) *U32Filter {
   119  	f.classid = netlink.MakeHandle(major, minor)
   120  	return f
   121  }
   122  
   123  // atomic filter replace/change
   124  // upstream bug with netlink.FilterReplace https://github.com/vishvananda/netlink/issues/914
   125  func (f *U32Filter) Replace() error {
   126  	filter, err := f.Build()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	link, err := netlink.LinkByIndex(filter.Attrs().LinkIndex)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	if err := netlink.FilterDel(filter); err != nil && !errorIsNotFound(err) {
   135  		return fmt.Errorf(
   136  			"error replacing u32 filter with parent %s and handle %s (dev %s): %w",
   137  			netlink.HandleStr(filter.Attrs().Parent),
   138  			netlink.HandleStr(filter.Attrs().Handle),
   139  			link.Attrs().Name,
   140  			err,
   141  		)
   142  	}
   143  	if err := netlink.FilterAdd(filter); err != nil {
   144  		return fmt.Errorf(
   145  			"error replacing u32 filter with parent %s and handle %s (dev %s): %w",
   146  			netlink.HandleStr(filter.Attrs().Parent),
   147  			netlink.HandleStr(filter.Attrs().Handle),
   148  			link.Attrs().Name,
   149  			err,
   150  		)
   151  	}
   152  	return nil
   153  }
   154  
   155  func (f *U32Filter) Add() error {
   156  	filter, err := f.Build()
   157  	if err != nil {
   158  		return err
   159  	}
   160  	if err := netlink.FilterAdd(filter); err != nil && !errorIsFileExists(err) {
   161  		return formatTcError("error creating u32 filter", filter.Attrs().LinkIndex, filter.Attrs().Parent, filter.Attrs().Handle, err)
   162  	}
   163  	return nil
   164  }
   165  
   166  func (f *U32Filter) Delete() error {
   167  	filter, err := f.Build()
   168  	if err != nil {
   169  		return err
   170  	}
   171  	if err := netlink.FilterDel(filter); err != nil && !errorIsNotFound(err) {
   172  		return err
   173  	}
   174  	return nil
   175  }
   176  
   177  func (f *U32Filter) Build() (netlink.Filter, error) {
   178  	keys := []netlink.TcU32Key{}
   179  
   180  	for _, matchIP := range f.matchIP {
   181  		keys = append(keys, getIPSelKey(matchIP))
   182  	}
   183  
   184  	for _, matchPort := range f.matchPort {
   185  		keys = append(keys, getPortSelKey(matchPort))
   186  	}
   187  
   188  	filter := netlink.U32{}
   189  	sel := netlink.TcU32Sel{
   190  		Flags: netlink.TC_U32_TERMINAL, // avoid printing *flowid https://lore.kernel.org/netdev/20221013084344.0fe8f3de@hermes.local/T/
   191  		Keys:  keys,
   192  	}
   193  	filter.Sel = &sel
   194  	filter.FilterAttrs = f.attrs
   195  	filter.ClassId = f.classid
   196  	filter.Actions = f.actions
   197  	filter.Divisor = f.divisor
   198  	filter.Hash = f.hashtable
   199  	return &filter, nil
   200  }
   201  
   202  func getIPSelKey(m *MatchIP) netlink.TcU32Key {
   203  	if m.ip == nil || m.mask == nil || m.IPv4HeaderBitOffset == nil {
   204  		return netlink.TcU32Key{}
   205  	}
   206  
   207  	ipAddr := ip.IPv4ToUInt32(*m.ip)
   208  	mask := ip.IPv4ToUInt32(*m.mask)
   209  
   210  	offset := int32(*m.IPv4HeaderBitOffset) /* #nosec G115 */
   211  
   212  	return netlink.TcU32Key{
   213  		Mask:    mask,
   214  		Val:     ipAddr,
   215  		Off:     offset,
   216  		OffMask: 0,
   217  	}
   218  }
   219  
   220  func getPortSelKey(m *MatchPort) netlink.TcU32Key {
   221  	return netlink.TcU32Key{
   222  		Mask:    m.mask,
   223  		Val:     m.port,
   224  		Off:     int32(ip.OptionsBitOffset),
   225  		OffMask: 0,
   226  	}
   227  }
   228  

View as plain text