Files
moby/libnetwork/drivers/overlay/bpf.go
Cory Snider c399963243 libn/d/overlay: make VNI matcher IPv6-compatible
Use Linux BPF extensions to locate the offset of the VXLAN header within
the packet so that the same BPF program works with VXLAN packets
received over either IPv4 or IPv6.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2023-04-24 14:20:29 -04:00

64 lines
2.4 KiB
Go

package overlay
import (
"fmt"
"strconv"
"strings"
"golang.org/x/net/bpf"
)
// vniMatchBPF returns a BPF program suitable for passing to the iptables and
// ip6tables bpf match which matches on the VXAN Network ID of encapsulated
// packets. The program assumes that it will be used in a rule which only
// matches UDP datagrams.
func vniMatchBPF(vni uint32) []bpf.RawInstruction {
asm, err := bpf.Assemble([]bpf.Instruction{
// Load offset of UDP payload into X.
bpf.LoadExtension{Num: bpf.ExtPayloadOffset}, // ld poff
bpf.TAX{}, // tax
bpf.LoadIndirect{Off: 4, Size: 4}, // ld [x + 4] ; Load VXLAN ID into top 24 bits of A
bpf.ALUOpConstant{Op: bpf.ALUOpShiftRight, Val: 8}, // rsh #8 ; A >>= 8
bpf.JumpIf{Cond: bpf.JumpEqual, Val: vni, SkipTrue: 1}, // jeq $vni, match
bpf.RetConstant{Val: 0}, // ret #0
bpf.RetConstant{Val: ^uint32(0)}, // match: ret #-1
})
// bpf.Assemble() only errors if an instruction is invalid. As the only variable
// part of the program is an instruction value for which the entire range is
// valid, whether the program can be successfully assembled is independent of
// the input. Given that the only recourse is to fix this function and
// recompile, there's little value in bubbling the error up to the caller.
if err != nil {
panic(err)
}
return asm
}
// marshalXTBPF marshals a BPF program into the "decimal" byte code format
// which is suitable for passing to the [iptables bpf match].
//
// iptables -m bpf --bytecode
//
// [iptables bpf match]: https://ipset.netfilter.org/iptables-extensions.man.html#lbAH
func marshalXTBPF(prog []bpf.RawInstruction) string { //nolint:unused
var b strings.Builder
fmt.Fprintf(&b, "%d", len(prog))
for _, ins := range prog {
fmt.Fprintf(&b, ",%d %d %d %d", ins.Op, ins.Jt, ins.Jf, ins.K)
}
return b.String()
}
// matchVXLAN returns an iptables rule fragment which matches VXLAN datagrams
// with the given destination port and VXLAN Network ID utilizing the xt_bpf
// netfilter kernel module. The returned slice's backing array is guaranteed not
// to alias any other slice's.
func matchVXLAN(port, vni uint32) []string {
dport := strconv.FormatUint(uint64(port), 10)
vniMatch := marshalXTBPF(vniMatchBPF(vni))
// https://ipset.netfilter.org/iptables-extensions.man.html#lbAH
return []string{"-p", "udp", "--dport", dport, "-m", "bpf", "--bytecode", vniMatch}
}