Exemple #1
0
// testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting
// packets into a UDP listener with a BPF program attached to it.
func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) {
	l, err := net.ListenPacket("udp4", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to open OS VM UDP listener: %v", err)
	}

	prog, err := bpf.Assemble(filter)
	if err != nil {
		t.Fatalf("failed to compile BPF program: %v", err)
	}

	p := ipv4.NewPacketConn(l)
	if err = p.SetBPF(prog); err != nil {
		t.Fatalf("failed to attach BPF program to listener: %v", err)
	}

	s, err := net.Dial("udp4", l.LocalAddr().String())
	if err != nil {
		t.Fatalf("failed to dial connection to listener: %v", err)
	}

	done := func() {
		_ = s.Close()
		_ = l.Close()
	}

	return &osVirtualMachine{
		l: l,
		s: s,
	}, done
}
Exemple #2
0
// SetBPF attaches an assembled BPF program to a raw net.PacketConn.
func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error {
	// Base filter filters traffic based on EtherType
	base, err := bpf.Assemble(baseFilter(p.proto))
	if err != nil {
		return err
	}

	// Append user filter to base filter, translate to raw format,
	// and apply to BPF device
	return syscall.SetBpf(p.fd, assembleBpfInsn(append(base, filter...)))
}
Exemple #3
0
// configureBPF configures a BPF device with the specified file descriptor to
// use the specified network and interface and protocol.
func configureBPF(fd int, ifi *net.Interface, proto Protocol) (int, error) {
	// Use specified interface with BPF device
	if err := syscall.SetBpfInterface(fd, ifi.Name); err != nil {
		return 0, err
	}

	// Inform BPF to send us its data immediately
	if err := syscall.SetBpfImmediate(fd, 1); err != nil {
		return 0, err
	}

	// Check buffer size of BPF device
	buflen, err := syscall.BpfBuflen(fd)
	if err != nil {
		return 0, err
	}

	// Do not automatically complete source address in ethernet headers
	if err := syscall.SetBpfHeadercmpl(fd, 0); err != nil {
		return 0, err
	}

	// Only retrieve incoming traffic using BPF device
	if err := setBPFDirection(fd, bpfDIn); err != nil {
		return 0, err
	}

	// Build and apply base BPF filter which checks for correct EtherType
	// on incoming packets
	prog, err := bpf.Assemble(baseInterfaceFilter(proto, ifi.MTU))
	if err != nil {
		return 0, err
	}
	if err := syscall.SetBpf(fd, assembleBpfInsn(prog)); err != nil {
		return 0, err
	}

	// Flush any packets currently in the BPF device's buffer
	if err := syscall.FlushBpf(fd); err != nil {
		return 0, err
	}

	return buflen, nil
}
Exemple #4
0
// mustAssembleBPF assembles a BPF program to filter out packets not bound
// for this server.
func (s *Server) mustAssembleBPF(mtu int) []bpf.RawInstruction {
	// This BPF program filters out packets that are not bound for this server
	// by checking against both the AoE broadcast addresses and this server's
	// major/minor address combination.  The structure of the incoming ethernet
	// frame and AoE header is as follows:
	//
	// Offset | Length | Comment
	// -------------------------
	//   00   |   06   | Ethernet destination MAC address
	//   06   |   06   | Ethernet source MAC address
	//   12   |   02   | Ethernet EtherType
	// -------------------------
	//   14   |   01   | AoE version + flags
	//   15   |   01   | AoE error
	//   16   |   02   | AoE major address
	//   18   |   01   | AoE minor address
	//
	// Thus, our BPF program needs to check for:
	//  - major address: offset 16, length 2
	//  - minor address: offset 18, length 1
	const (
		majorOffset = 16
		majorLen    = 2
		minorOffset = 18
		minorLen    = 1
	)

	// TODO(mdlayher): this routine likely belongs in package AoE, once the server
	// component is more complete.

	prog, err := bpf.Assemble([]bpf.Instruction{
		// Load major address value from AoE header
		bpf.LoadAbsolute{
			Off:  majorOffset,
			Size: majorLen,
		},
		// If major address is equal to broadcast address, jump to minor address
		// filtering
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(aoe.BroadcastMajor),
			SkipTrue: 2,
		},
		// If major address is equal to our server's, jump to minor address
		// filtering
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(s.major),
			SkipTrue: 1,
		},
		// Major address is not our server's or broadcast address
		bpf.RetConstant{
			Val: 0,
		},
		// Load minor address value from AoE header
		bpf.LoadAbsolute{
			Off:  minorOffset,
			Size: minorLen,
		},
		// If minor address is equal to broadcast address, jump to accept packet
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(aoe.BroadcastMinor),
			SkipTrue: 2,
		},
		// If minor address is equal to our server's, jump to accept packet
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(s.minor),
			SkipTrue: 1,
		},
		// Minor address is not our server's or broadcast address
		bpf.RetConstant{
			Val: 0,
		},
		// Accept the packet bytes up to the interface's MTU
		bpf.RetConstant{
			Val: uint32(mtu),
		},
	})
	if err != nil {
		panic(fmt.Sprintf("failed to assemble BPF program: %v", err))
	}

	return prog
}
Exemple #5
0
func TestBPF(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skipf("not supported on %s", runtime.GOOS)
	}

	l, err := net.ListenPacket("udp4", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()

	p := ipv4.NewPacketConn(l)

	// This filter accepts UDP packets whose first payload byte is
	// even.
	prog, err := bpf.Assemble([]bpf.Instruction{
		// Load the first byte of the payload (skipping UDP header).
		bpf.LoadAbsolute{Off: 8, Size: 1},
		// Select LSB of the byte.
		bpf.ALUOpConstant{Op: bpf.ALUOpAnd, Val: 1},
		// Byte is even?
		bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0, SkipFalse: 1},
		// Accept.
		bpf.RetConstant{Val: 4096},
		// Ignore.
		bpf.RetConstant{Val: 0},
	})
	if err != nil {
		t.Fatalf("compiling BPF: %s", err)
	}

	if err = p.SetBPF(prog); err != nil {
		t.Fatalf("attaching filter to Conn: %s", err)
	}

	s, err := net.Dial("udp4", l.LocalAddr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer s.Close()
	go func() {
		for i := byte(0); i < 10; i++ {
			s.Write([]byte{i})
		}
	}()

	l.SetDeadline(time.Now().Add(2 * time.Second))
	seen := make([]bool, 5)
	for {
		var b [512]byte
		n, _, err := l.ReadFrom(b[:])
		if err != nil {
			t.Fatalf("reading from listener: %s", err)
		}
		if n != 1 {
			t.Fatalf("unexpected packet length, want 1, got %d", n)
		}
		if b[0] >= 10 {
			t.Fatalf("unexpected byte, want 0-9, got %d", b[0])
		}
		if b[0]%2 != 0 {
			t.Fatalf("got odd byte %d, wanted only even bytes", b[0])
		}
		seen[b[0]/2] = true

		seenAll := true
		for _, v := range seen {
			if !v {
				seenAll = false
				break
			}
		}
		if seenAll {
			break
		}
	}
}