1package ebpf 2 3import ( 4 "errors" 5 "fmt" 6 "math" 7 "reflect" 8 "strings" 9 10 "github.com/cilium/ebpf/asm" 11 "github.com/cilium/ebpf/internal" 12 "github.com/cilium/ebpf/internal/btf" 13) 14 15// CollectionOptions control loading a collection into the kernel. 16// 17// Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions. 18type CollectionOptions struct { 19 Maps MapOptions 20 Programs ProgramOptions 21} 22 23// CollectionSpec describes a collection. 24type CollectionSpec struct { 25 Maps map[string]*MapSpec 26 Programs map[string]*ProgramSpec 27} 28 29// Copy returns a recursive copy of the spec. 30func (cs *CollectionSpec) Copy() *CollectionSpec { 31 if cs == nil { 32 return nil 33 } 34 35 cpy := CollectionSpec{ 36 Maps: make(map[string]*MapSpec, len(cs.Maps)), 37 Programs: make(map[string]*ProgramSpec, len(cs.Programs)), 38 } 39 40 for name, spec := range cs.Maps { 41 cpy.Maps[name] = spec.Copy() 42 } 43 44 for name, spec := range cs.Programs { 45 cpy.Programs[name] = spec.Copy() 46 } 47 48 return &cpy 49} 50 51// RewriteMaps replaces all references to specific maps. 52// 53// Use this function to use pre-existing maps instead of creating new ones 54// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps. 55// 56// Returns an error if a named map isn't used in at least one program. 57func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { 58 for symbol, m := range maps { 59 // have we seen a program that uses this symbol / map 60 seen := false 61 fd := m.FD() 62 for progName, progSpec := range cs.Programs { 63 err := progSpec.Instructions.RewriteMapPtr(symbol, fd) 64 65 switch { 66 case err == nil: 67 seen = true 68 69 case asm.IsUnreferencedSymbol(err): 70 // Not all programs need to use the map 71 72 default: 73 return fmt.Errorf("program %s: %w", progName, err) 74 } 75 } 76 77 if !seen { 78 return fmt.Errorf("map %s not referenced by any programs", symbol) 79 } 80 81 // Prevent NewCollection from creating rewritten maps 82 delete(cs.Maps, symbol) 83 } 84 85 return nil 86} 87 88// RewriteConstants replaces the value of multiple constants. 89// 90// The constant must be defined like so in the C program: 91// 92// static volatile const type foobar; 93// static volatile const type foobar = default; 94// 95// Replacement values must be of the same length as the C sizeof(type). 96// If necessary, they are marshalled according to the same rules as 97// map values. 98// 99// From Linux 5.5 the verifier will use constants to eliminate dead code. 100// 101// Returns an error if a constant doesn't exist. 102func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { 103 rodata := cs.Maps[".rodata"] 104 if rodata == nil { 105 return errors.New("missing .rodata section") 106 } 107 108 if rodata.BTF == nil { 109 return errors.New(".rodata section has no BTF") 110 } 111 112 if n := len(rodata.Contents); n != 1 { 113 return fmt.Errorf("expected one key in .rodata, found %d", n) 114 } 115 116 kv := rodata.Contents[0] 117 value, ok := kv.Value.([]byte) 118 if !ok { 119 return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value) 120 } 121 122 buf := make([]byte, len(value)) 123 copy(buf, value) 124 125 err := patchValue(buf, btf.MapValue(rodata.BTF), consts) 126 if err != nil { 127 return err 128 } 129 130 rodata.Contents[0] = MapKV{kv.Key, buf} 131 return nil 132} 133 134// Assign the contents of a collection spec to a struct. 135// 136// This function is a short-cut to manually checking the presence 137// of maps and programs in a collection spec. 138// 139// The argument to must be a pointer to a struct. A field of the 140// struct is updated with values from Programs or Maps if it 141// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. 142// The tag gives the name of the program or map as found in 143// the CollectionSpec. 144// 145// struct { 146// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` 147// Bar *ebpf.MapSpec `ebpf:"bar_map"` 148// Ignored int 149// } 150// 151// Returns an error if any of the fields can't be found, or 152// if the same map or program is assigned multiple times. 153func (cs *CollectionSpec) Assign(to interface{}) error { 154 valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { 155 switch typ { 156 case reflect.TypeOf((*ProgramSpec)(nil)): 157 p := cs.Programs[name] 158 if p == nil { 159 return reflect.Value{}, fmt.Errorf("missing program %q", name) 160 } 161 return reflect.ValueOf(p), nil 162 case reflect.TypeOf((*MapSpec)(nil)): 163 m := cs.Maps[name] 164 if m == nil { 165 return reflect.Value{}, fmt.Errorf("missing map %q", name) 166 } 167 return reflect.ValueOf(m), nil 168 default: 169 return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) 170 } 171 } 172 173 return assignValues(to, valueOf) 174} 175 176// LoadAndAssign creates a collection from a spec, and assigns it to a struct. 177// 178// See Collection.Assign for details. 179func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { 180 if opts == nil { 181 opts = &CollectionOptions{} 182 } 183 184 coll, err := NewCollectionWithOptions(cs, *opts) 185 if err != nil { 186 return err 187 } 188 defer coll.Close() 189 190 return coll.Assign(to) 191} 192 193// Collection is a collection of Programs and Maps associated 194// with their symbols 195type Collection struct { 196 Programs map[string]*Program 197 Maps map[string]*Map 198} 199 200// NewCollection creates a Collection from a specification. 201// 202// Only maps referenced by at least one of the programs are initialized. 203func NewCollection(spec *CollectionSpec) (*Collection, error) { 204 return NewCollectionWithOptions(spec, CollectionOptions{}) 205} 206 207// NewCollectionWithOptions creates a Collection from a specification. 208// 209// Only maps referenced by at least one of the programs are initialized. 210func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (coll *Collection, err error) { 211 var ( 212 maps = make(map[string]*Map) 213 progs = make(map[string]*Program) 214 btfs = make(map[*btf.Spec]*btf.Handle) 215 ) 216 217 defer func() { 218 for _, btf := range btfs { 219 btf.Close() 220 } 221 222 if err == nil { 223 return 224 } 225 226 for _, m := range maps { 227 m.Close() 228 } 229 230 for _, p := range progs { 231 p.Close() 232 } 233 }() 234 235 loadBTF := func(spec *btf.Spec) (*btf.Handle, error) { 236 if btfs[spec] != nil { 237 return btfs[spec], nil 238 } 239 240 handle, err := btf.NewHandle(spec) 241 if err != nil { 242 return nil, err 243 } 244 245 btfs[spec] = handle 246 return handle, nil 247 } 248 249 for mapName, mapSpec := range spec.Maps { 250 var handle *btf.Handle 251 if mapSpec.BTF != nil { 252 handle, err = loadBTF(btf.MapSpec(mapSpec.BTF)) 253 if err != nil && !errors.Is(err, btf.ErrNotSupported) { 254 return nil, err 255 } 256 } 257 258 m, err := newMapWithBTF(mapSpec, handle, opts.Maps) 259 if err != nil { 260 return nil, fmt.Errorf("map %s: %w", mapName, err) 261 } 262 maps[mapName] = m 263 } 264 265 for progName, origProgSpec := range spec.Programs { 266 progSpec := origProgSpec.Copy() 267 268 // Rewrite any reference to a valid map. 269 for i := range progSpec.Instructions { 270 ins := &progSpec.Instructions[i] 271 272 if ins.OpCode != asm.LoadImmOp(asm.DWord) || ins.Reference == "" { 273 continue 274 } 275 276 if uint32(ins.Constant) != math.MaxUint32 { 277 // Don't overwrite maps already rewritten, users can 278 // rewrite programs in the spec themselves 279 continue 280 } 281 282 m := maps[ins.Reference] 283 if m == nil { 284 return nil, fmt.Errorf("program %s: missing map %s", progName, ins.Reference) 285 } 286 287 fd := m.FD() 288 if fd < 0 { 289 return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) 290 } 291 if err := ins.RewriteMapPtr(m.FD()); err != nil { 292 return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) 293 } 294 } 295 296 var handle *btf.Handle 297 if progSpec.BTF != nil { 298 handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF)) 299 if err != nil && !errors.Is(err, btf.ErrNotSupported) { 300 return nil, err 301 } 302 } 303 304 prog, err := newProgramWithBTF(progSpec, handle, opts.Programs) 305 if err != nil { 306 return nil, fmt.Errorf("program %s: %w", progName, err) 307 } 308 progs[progName] = prog 309 } 310 311 return &Collection{ 312 progs, 313 maps, 314 }, nil 315} 316 317// LoadCollection parses an object file and converts it to a collection. 318func LoadCollection(file string) (*Collection, error) { 319 spec, err := LoadCollectionSpec(file) 320 if err != nil { 321 return nil, err 322 } 323 return NewCollection(spec) 324} 325 326// Close frees all maps and programs associated with the collection. 327// 328// The collection mustn't be used afterwards. 329func (coll *Collection) Close() { 330 for _, prog := range coll.Programs { 331 prog.Close() 332 } 333 for _, m := range coll.Maps { 334 m.Close() 335 } 336} 337 338// DetachMap removes the named map from the Collection. 339// 340// This means that a later call to Close() will not affect this map. 341// 342// Returns nil if no map of that name exists. 343func (coll *Collection) DetachMap(name string) *Map { 344 m := coll.Maps[name] 345 delete(coll.Maps, name) 346 return m 347} 348 349// DetachProgram removes the named program from the Collection. 350// 351// This means that a later call to Close() will not affect this program. 352// 353// Returns nil if no program of that name exists. 354func (coll *Collection) DetachProgram(name string) *Program { 355 p := coll.Programs[name] 356 delete(coll.Programs, name) 357 return p 358} 359 360// Assign the contents of a collection to a struct. 361// 362// `to` must be a pointer to a struct like the following: 363// 364// struct { 365// Foo *ebpf.Program `ebpf:"xdp_foo"` 366// Bar *ebpf.Map `ebpf:"bar_map"` 367// Ignored int 368// } 369// 370// See CollectionSpec.Assign for the semantics of this function. 371// 372// DetachMap and DetachProgram is invoked for all assigned elements 373// if the function is successful. 374func (coll *Collection) Assign(to interface{}) error { 375 assignedMaps := make(map[string]struct{}) 376 assignedPrograms := make(map[string]struct{}) 377 valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { 378 switch typ { 379 case reflect.TypeOf((*Program)(nil)): 380 p := coll.Programs[name] 381 if p == nil { 382 return reflect.Value{}, fmt.Errorf("missing program %q", name) 383 } 384 assignedPrograms[name] = struct{}{} 385 return reflect.ValueOf(p), nil 386 case reflect.TypeOf((*Map)(nil)): 387 m := coll.Maps[name] 388 if m == nil { 389 return reflect.Value{}, fmt.Errorf("missing map %q", name) 390 } 391 assignedMaps[name] = struct{}{} 392 return reflect.ValueOf(m), nil 393 default: 394 return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) 395 } 396 } 397 398 if err := assignValues(to, valueOf); err != nil { 399 return err 400 } 401 402 for name := range assignedPrograms { 403 coll.DetachProgram(name) 404 } 405 406 for name := range assignedMaps { 407 coll.DetachMap(name) 408 } 409 410 return nil 411} 412 413func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error { 414 v := reflect.ValueOf(to) 415 if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { 416 return fmt.Errorf("%T is not a pointer to a struct", to) 417 } 418 419 type elem struct { 420 typ reflect.Type 421 name string 422 } 423 424 var ( 425 s = v.Elem() 426 sT = s.Type() 427 assignedTo = make(map[elem]string) 428 ) 429 for i := 0; i < sT.NumField(); i++ { 430 field := sT.Field(i) 431 432 name := field.Tag.Get("ebpf") 433 if name == "" { 434 continue 435 } 436 if strings.Contains(name, ",") { 437 return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) 438 } 439 440 e := elem{field.Type, name} 441 if assignedField := assignedTo[e]; assignedField != "" { 442 return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField) 443 } 444 445 value, err := valueOf(field.Type, name) 446 if err != nil { 447 return fmt.Errorf("field %s: %w", field.Name, err) 448 } 449 450 fieldValue := s.Field(i) 451 if !fieldValue.CanSet() { 452 return fmt.Errorf("can't set value of field %s", field.Name) 453 } 454 455 fieldValue.Set(value) 456 assignedTo[e] = field.Name 457 } 458 459 return nil 460} 461