Browse Source

add ethernet

Song Gao 9 years ago
parent
commit
6b909109c3
3 changed files with 225 additions and 0 deletions
  1. 50 0
      ethernet/ethertypes.go
  2. 114 0
      ethernet/frame.go
  3. 61 0
      ethernet/frame_test.go

+ 50 - 0
ethernet/ethertypes.go

@@ -0,0 +1,50 @@
+package ethernet
+
+// Ethertype is a type used represent the ethertype of an ethernet frame.
+// Defined as a 2-byte array, variables of this type are intended to be used as
+// immutable values.
+type Ethertype [2]byte
+
+// Common ethertype values
+var (
+	IPv4                = Ethertype{0x08, 0x00}
+	ARP                 = Ethertype{0x08, 0x06}
+	WakeOnLAN           = Ethertype{0x08, 0x42}
+	TRILL               = Ethertype{0x22, 0xF3}
+	DECnetPhase4        = Ethertype{0x60, 0x03}
+	RARP                = Ethertype{0x80, 0x35}
+	AppleTalk           = Ethertype{0x80, 0x9B}
+	AARP                = Ethertype{0x80, 0xF3}
+	IPX1                = Ethertype{0x81, 0x37}
+	IPX2                = Ethertype{0x81, 0x38}
+	QNXQnet             = Ethertype{0x82, 0x04}
+	IPv6                = Ethertype{0x86, 0xDD}
+	EthernetFlowControl = Ethertype{0x88, 0x08}
+	IEEE802_3           = Ethertype{0x88, 0x09}
+	CobraNet            = Ethertype{0x88, 0x19}
+	MPLSUnicast         = Ethertype{0x88, 0x47}
+	MPLSMulticast       = Ethertype{0x88, 0x48}
+	PPPoEDiscovery      = Ethertype{0x88, 0x63}
+	PPPoESession        = Ethertype{0x88, 0x64}
+	JumboFrames         = Ethertype{0x88, 0x70}
+	HomePlug1_0MME      = Ethertype{0x88, 0x7B}
+	IEEE802_1X          = Ethertype{0x88, 0x8E}
+	PROFINET            = Ethertype{0x88, 0x92}
+	HyperSCSI           = Ethertype{0x88, 0x9A}
+	AoE                 = Ethertype{0x88, 0xA2}
+	EtherCAT            = Ethertype{0x88, 0xA4}
+	EthernetPowerlink   = Ethertype{0x88, 0xAB}
+	LLDP                = Ethertype{0x88, 0xCC}
+	SERCOS3             = Ethertype{0x88, 0xCD}
+	WSMP                = Ethertype{0x88, 0xDC}
+	HomePlugAVMME       = Ethertype{0x88, 0xE1}
+	MRP                 = Ethertype{0x88, 0xE3}
+	IEEE802_1AE         = Ethertype{0x88, 0xE5}
+	IEEE1588            = Ethertype{0x88, 0xF7}
+	IEEE802_1ag         = Ethertype{0x89, 0x02}
+	FCoE                = Ethertype{0x89, 0x06}
+	FCoEInit            = Ethertype{0x89, 0x14}
+	RoCE                = Ethertype{0x89, 0x15}
+	CTP                 = Ethertype{0x90, 0x00}
+	VeritasLLT          = Ethertype{0xCA, 0xFE}
+)

+ 114 - 0
ethernet/frame.go

@@ -0,0 +1,114 @@
+package ethernet
+
+import "net"
+
+// Frame represents an ethernet frame. The length of the underlying slice of a
+// Frame should always reflect the ethernet frame length.
+type Frame []byte
+
+// Tagging is a type used to indicate whether/how a frame is tagged. The value
+// is number of bytes taken by tagging.
+type Tagging byte
+
+// Const values for different taggings
+const (
+	NotTagged    Tagging = 0
+	Tagged       Tagging = 4
+	DoubleTagged Tagging = 8
+)
+
+// Destination returns the destination address field of the frame. The address
+// references a slice on the frame.
+//
+// It is not safe to use this method if f is nil or an invalid ethernet frame.
+func (f Frame) Destination() net.HardwareAddr {
+	return net.HardwareAddr(f[:6:6])
+}
+
+// Source returns the source address field of the frame. The address references
+// a slice on the frame.
+//
+// It is not safe to use this method if f is nil or an invalid ethernet frame.
+func (f Frame) Source() net.HardwareAddr {
+	return net.HardwareAddr(f[6:12:12])
+}
+
+// Tagging returns whether/how the frame has 802.1Q tag(s).
+//
+// It is not safe to use this method if f is nil or an invalid ethernet frame.
+func (f Frame) Tagging() Tagging {
+	if f[12] == 0x81 && f[13] == 0x00 {
+		return Tagged
+	} else if f[12] == 0x88 && f[13] == 0xa8 {
+		return DoubleTagged
+	}
+	return NotTagged
+}
+
+// Tag returns a slice holding the tag part of the frame, if any. Note taht
+// this includes the Tag Protocol Identifier (TPID), e.g. 0x8100 or 0x88a8.
+// Upper layer should use the returned slice for both reading and writing.
+//
+// It is not safe to use this method if f is nil or an invalid ethernet frame.
+func (f Frame) Tags() []byte {
+	tagging := f.Tagging()
+	return f[12 : 12+tagging : 12+tagging]
+}
+
+// Ethertype returns the ethertype field of the frame.
+//
+// It is not safe to use this method if f is nil or an invalid ethernet frame.
+func (f Frame) Ethertype() Ethertype {
+	ethertypePos := 12 + f.Tagging()
+	return Ethertype{f[ethertypePos], f[ethertypePos+1]}
+}
+
+// Payload returns a slice holding the payload part of the frame. Upper layer
+// should use the returned slice for both reading and writing purposes.
+//
+// It is not safe to use this method if f is nil or an invalid ethernet frame.
+func (f Frame) Payload() []byte {
+	return f[12+f.Tagging()+2:]
+}
+
+// Resize re-slices (*f) so that len(*f) holds exactly payloadSize bytes of
+// payload. If cap(*f) is not large enough, a new slice is made.
+//
+// If len(*f) is less than 14 bytes, it is assumed to be not tagged.
+//
+// It is safe to call Resize on a pointer to a nil Frame.
+func (f *Frame) Resize(payloadSize int) {
+	tagging := NotTagged
+	if len(*f) > 6+6+2 {
+		tagging = f.Tagging()
+	}
+	f.resize(6 + 6 + int(tagging) + 2 + payloadSize)
+}
+
+// Prepare prepares *f to be used, by filling in dst/src address, setting up
+// proper tagging and ethertype, and resizing it to proper length.
+//
+// It is safe to call Prepare on a pointer to a nil Frame or invalid Frame.
+func (f *Frame) Prepare(dst net.HardwareAddr, src net.HardwareAddr, tagging Tagging, ethertype Ethertype, payloadSize int) {
+	f.resize(6 + 6 + int(tagging) + 2 + payloadSize)
+	copy((*f)[0:6:6], dst)
+	copy((*f)[6:12:12], src)
+	if tagging == Tagged {
+		(*f)[12] = 0x81
+		(*f)[13] = 0x00
+	} else if tagging == DoubleTagged {
+		(*f)[12] = 0x88
+		(*f)[13] = 0xa8
+	}
+	(*f)[12+tagging] = ethertype[0]
+	(*f)[12+tagging+1] = ethertype[1]
+	return
+}
+
+func (f *Frame) resize(length int) {
+	if cap(*f) < length {
+		*f = make(Frame, length, length)
+	} else {
+		*f = (*f)[:length]
+	}
+}

+ 61 - 0
ethernet/frame_test.go

@@ -0,0 +1,61 @@
+package ethernet
+
+import (
+	"bytes"
+	"net"
+	"testing"
+)
+
+func panics(f func()) (didPanic bool) {
+	defer func() {
+		if r := recover(); r != nil {
+			didPanic = true
+		}
+	}()
+	f()
+	return
+}
+
+func mustParseMAC(str string) (addr net.HardwareAddr) {
+	var err error
+	addr, err = net.ParseMAC(str)
+	if err != nil {
+		panic(err)
+	}
+	return
+}
+
+func TestPrepare(t *testing.T) {
+	var frame Frame
+	dst := mustParseMAC("ff:ff:ff:ff:ff:ff")
+	src := mustParseMAC("12:34:56:78:9a:bc")
+	(&frame).Prepare(dst, src, NotTagged, IPv6, 1024)
+	if len(frame.Payload()) != 1024 {
+		t.Fatalf("frame payload does not have correct length. expected %d; got %d\n", 1024, len(frame.Payload()))
+	}
+	expectedLength := 6 + 6 + int(NotTagged) + 2 + 1024
+	if len(frame) != expectedLength {
+		t.Fatalf("frame does not have correct length. expected %d; got %d\n", expectedLength, len(frame))
+	}
+	if !bytes.Equal([]byte(frame.Source()), []byte(src)) {
+		t.Fatalf("frame source address is incorrect. expected %s; got %s\n", src.String(), frame.Source().String())
+	}
+	if !bytes.Equal([]byte(frame.Destination()), []byte(dst)) {
+		t.Fatalf("frame destination address is incorrect. expected %s; got %s\n", dst.String(), frame.Destination().String())
+	}
+	if frame.Tagging() != NotTagged {
+		t.Fatalf("frame tagging is incorrect. expected %d; got %d\n", NotTagged, frame.Tagging())
+	}
+	if frame.Ethertype() != IPv6 {
+		t.Fatalf("frame ethertype is incorrect. expected %v; got %v\n", IPv6, frame.Ethertype())
+	}
+}
+
+func TestResize(t *testing.T) {
+	var frame Frame
+	(&frame).Resize(1024)
+	expectedLength := 6 + 6 + int(NotTagged) + 2 + 1024
+	if len(frame) != expectedLength {
+		t.Fatalf("frame does not have correct length. expected %d; got %d\n", expectedLength, len(frame))
+	}
+}