bpf.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. package overlay
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "golang.org/x/net/bpf"
  7. )
  8. // vniMatchBPF returns a BPF program suitable for passing to the iptables and
  9. // ip6tables bpf match which matches on the VXAN Network ID of encapsulated
  10. // packets. The program assumes that it will be used in a rule which only
  11. // matches UDP datagrams.
  12. func vniMatchBPF(vni uint32) []bpf.RawInstruction {
  13. asm, err := bpf.Assemble([]bpf.Instruction{
  14. // Load offset of UDP payload into X.
  15. bpf.LoadExtension{Num: bpf.ExtPayloadOffset}, // ld poff
  16. bpf.TAX{}, // tax
  17. bpf.LoadIndirect{Off: 4, Size: 4}, // ld [x + 4] ; Load VXLAN ID into top 24 bits of A
  18. bpf.ALUOpConstant{Op: bpf.ALUOpShiftRight, Val: 8}, // rsh #8 ; A >>= 8
  19. bpf.JumpIf{Cond: bpf.JumpEqual, Val: vni, SkipTrue: 1}, // jeq $vni, match
  20. bpf.RetConstant{Val: 0}, // ret #0
  21. bpf.RetConstant{Val: ^uint32(0)}, // match: ret #-1
  22. })
  23. // bpf.Assemble() only errors if an instruction is invalid. As the only variable
  24. // part of the program is an instruction value for which the entire range is
  25. // valid, whether the program can be successfully assembled is independent of
  26. // the input. Given that the only recourse is to fix this function and
  27. // recompile, there's little value in bubbling the error up to the caller.
  28. if err != nil {
  29. panic(err)
  30. }
  31. return asm
  32. }
  33. // marshalXTBPF marshals a BPF program into the "decimal" byte code format
  34. // which is suitable for passing to the [iptables bpf match].
  35. //
  36. // iptables -m bpf --bytecode
  37. //
  38. // [iptables bpf match]: https://ipset.netfilter.org/iptables-extensions.man.html#lbAH
  39. func marshalXTBPF(prog []bpf.RawInstruction) string { //nolint:unused
  40. var b strings.Builder
  41. fmt.Fprintf(&b, "%d", len(prog))
  42. for _, ins := range prog {
  43. fmt.Fprintf(&b, ",%d %d %d %d", ins.Op, ins.Jt, ins.Jf, ins.K)
  44. }
  45. return b.String()
  46. }
  47. // matchVXLAN returns an iptables rule fragment which matches VXLAN datagrams
  48. // with the given destination port and VXLAN Network ID utilizing the xt_bpf
  49. // netfilter kernel module. The returned slice's backing array is guaranteed not
  50. // to alias any other slice's.
  51. func matchVXLAN(port, vni uint32) []string {
  52. dport := strconv.FormatUint(uint64(port), 10)
  53. vniMatch := marshalXTBPF(vniMatchBPF(vni))
  54. // https://ipset.netfilter.org/iptables-extensions.man.html#lbAH
  55. return []string{"-p", "udp", "--dport", dport, "-m", "bpf", "--bytecode", vniMatch}
  56. }