github.com/cilium/cilium@v1.16.2/pkg/maps/cidrmap/cidrmap.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package cidrmap 5 6 import ( 7 "fmt" 8 "net" 9 "path" 10 "unsafe" 11 12 "github.com/cilium/ebpf" 13 "github.com/sirupsen/logrus" 14 15 "github.com/cilium/cilium/pkg/bpf" 16 "github.com/cilium/cilium/pkg/logging" 17 "github.com/cilium/cilium/pkg/logging/logfields" 18 ) 19 20 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-cidr") 21 22 const ( 23 MapName = "cilium_cidr_" 24 MaxEntries = 16384 25 ) 26 27 // CIDRMap refers to an LPM trie map at 'path'. 28 type CIDRMap struct { 29 path string 30 m *ebpf.Map 31 AddrSize int // max prefix length in bytes, 4 for IPv4, 16 for IPv6 32 Prefixlen uint32 33 34 // PrefixIsDynamic determines whether it's valid for entries to have 35 // a prefix length that is not equal to the Prefixlen above 36 PrefixIsDynamic bool 37 } 38 39 const ( 40 LPM_MAP_VALUE_SIZE = 1 41 ) 42 43 type cidrKey struct { 44 Prefixlen uint32 45 46 // v4 LPM maps have 8-byte key sizes even though the 20-byte cidrKey is used 47 // for map ops. This hack relies on passing unsafe.Pointers to the cidrKey so 48 // the kernel only accesses Prefixlen (4 bytes) and the first 4 bytes of Net. 49 // v4 net.IPNets are packed into those 4 first bytes. 50 Net [16]byte 51 } 52 53 func (cm *CIDRMap) cidrKeyInit(cidr net.IPNet) (key cidrKey) { 54 ones, _ := cidr.Mask.Size() 55 key.Prefixlen = uint32(ones) 56 // IPv4 address can be represented by 16 byte slice in 'cidr.IP', 57 // in which case the address is at the end of the slice. 58 copy(key.Net[:], cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)]) 59 return 60 } 61 62 func (cm *CIDRMap) keyCidrInit(key cidrKey) (cidr net.IPNet) { 63 cidr.Mask = net.CIDRMask(int(key.Prefixlen), cm.AddrSize*8) 64 cidr.IP = make(net.IP, cm.AddrSize) 65 copy(cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)], key.Net[:]) 66 return 67 } 68 69 // checkPrefixlen checks whether it's valid to manipulate elements in the map 70 // with the specified key. If it's unsupported, it returns an error. 71 func (cm *CIDRMap) checkPrefixlen(key *cidrKey, operation string) error { 72 if cm.Prefixlen != 0 && 73 ((cm.PrefixIsDynamic && cm.Prefixlen < key.Prefixlen) || 74 (!cm.PrefixIsDynamic && cm.Prefixlen != key.Prefixlen)) { 75 return fmt.Errorf("Unable to %s element with dynamic prefix length cm.Prefixlen=%d key.Prefixlen=%d", 76 operation, cm.Prefixlen, key.Prefixlen) 77 } 78 return nil 79 } 80 81 // InsertCIDR inserts an entry to 'cm' with key 'cidr'. Value is currently not 82 // used. 83 func (cm *CIDRMap) InsertCIDR(cidr net.IPNet) error { 84 key := cm.cidrKeyInit(cidr) 85 entry := [LPM_MAP_VALUE_SIZE]byte{} 86 if err := cm.checkPrefixlen(&key, "update"); err != nil { 87 return err 88 } 89 log.WithField(logfields.Path, cm.path).Debugf("Inserting CIDR entry %s", cidr.String()) 90 return cm.m.Update(unsafe.Pointer(&key), unsafe.Pointer(&entry), ebpf.UpdateAny) 91 } 92 93 // DeleteCIDR deletes an entry from 'cm' with key 'cidr'. 94 func (cm *CIDRMap) DeleteCIDR(cidr net.IPNet) error { 95 key := cm.cidrKeyInit(cidr) 96 if err := cm.checkPrefixlen(&key, "delete"); err != nil { 97 return err 98 } 99 log.WithField(logfields.Path, cm.path).Debugf("Removing CIDR entry %s", cidr.String()) 100 return cm.m.Delete(unsafe.Pointer(&key)) 101 } 102 103 // CIDRExists returns true if 'cidr' exists in map 'cm' 104 func (cm *CIDRMap) CIDRExists(cidr net.IPNet) bool { 105 key := cm.cidrKeyInit(cidr) 106 var entry [LPM_MAP_VALUE_SIZE]byte 107 return cm.m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&entry)) == nil 108 } 109 110 // CIDRNext returns next CIDR entry in map 'cm' 111 func (cm *CIDRMap) CIDRNext(cidr *net.IPNet) *net.IPNet { 112 var key, keyNext cidrKey 113 if cidr != nil { 114 key = cm.cidrKeyInit(*cidr) 115 } 116 if err := cm.m.NextKey(unsafe.Pointer(&key), unsafe.Pointer(&keyNext)); err != nil { 117 return nil 118 } 119 out := cm.keyCidrInit(keyNext) 120 return &out 121 } 122 123 // CIDRDump walks map 'cm' and dumps all CIDR entries 124 func (cm *CIDRMap) CIDRDump(to []string) []string { 125 var key, keyNext *net.IPNet 126 for { 127 keyNext = cm.CIDRNext(key) 128 if keyNext == nil { 129 return to 130 } 131 key = keyNext 132 to = append(to, key.String()) 133 } 134 } 135 136 // String returns the path of the map. 137 func (cm *CIDRMap) String() string { 138 if cm == nil { 139 return "" 140 } 141 return cm.path 142 } 143 144 // Close closes the FD of the given CIDRMap 145 func (cm *CIDRMap) Close() error { 146 if cm == nil { 147 return nil 148 } 149 return cm.m.Close() 150 } 151 152 // OpenMapElems is the same as OpenMap only with defined maxelem as argument. 153 func OpenMapElems(pinPath string, prefixlen int, prefixdyn bool, maxelem uint32) (*CIDRMap, error) { 154 mapType := ebpf.LPMTrie 155 prefix := 0 156 157 if !prefixdyn { 158 mapType = ebpf.Hash 159 prefix = prefixlen 160 } 161 if prefixlen <= 0 { 162 return nil, fmt.Errorf("prefixlen must be > 0") 163 } 164 bytes := (prefixlen-1)/8 + 1 165 m, err := bpf.OpenOrCreateMap(&ebpf.MapSpec{ 166 Name: path.Base(pinPath), 167 Type: mapType, 168 KeySize: uint32(unsafe.Sizeof(uint32(0)) + uintptr(bytes)), 169 ValueSize: uint32(LPM_MAP_VALUE_SIZE), 170 MaxEntries: maxelem, 171 Flags: bpf.BPF_F_NO_PREALLOC, 172 Pinning: ebpf.PinByName, 173 }, path.Dir(pinPath)) 174 175 if err != nil { 176 scopedLog := log.WithError(err).WithField(logfields.Path, pinPath) 177 scopedLog.Warning("Failed to create CIDR map") 178 return nil, err 179 } 180 181 log.WithFields(logrus.Fields{ 182 logfields.Path: pinPath, 183 "fd": m.FD(), 184 "LPM": m.Type() == ebpf.LPMTrie, 185 }).Debug("Created CIDR map") 186 187 return &CIDRMap{ 188 path: pinPath, 189 m: m, 190 AddrSize: bytes, 191 Prefixlen: uint32(prefix), 192 PrefixIsDynamic: prefixdyn, 193 }, nil 194 }