├── README.md ├── LICENSE └── ethernet ├── frame_test.go ├── ethertypes.go └── frame.go /README.md: -------------------------------------------------------------------------------- 1 | # packets 2 | Libraries for parsing and constructing common network packets. 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Song Gao 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of packets nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /ethernet/frame_test.go: -------------------------------------------------------------------------------- 1 | package ethernet 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | "testing" 7 | ) 8 | 9 | func panics(f func()) (didPanic bool) { 10 | defer func() { 11 | if r := recover(); r != nil { 12 | didPanic = true 13 | } 14 | }() 15 | f() 16 | return 17 | } 18 | 19 | func mustParseMAC(str string) (addr net.HardwareAddr) { 20 | var err error 21 | addr, err = net.ParseMAC(str) 22 | if err != nil { 23 | panic(err) 24 | } 25 | return 26 | } 27 | 28 | func TestPrepare(t *testing.T) { 29 | var frame Frame 30 | dst := mustParseMAC("ff:ff:ff:ff:ff:ff") 31 | src := mustParseMAC("12:34:56:78:9a:bc") 32 | (&frame).Prepare(dst, src, NotTagged, IPv6, 1024) 33 | if len(frame.Payload()) != 1024 { 34 | t.Fatalf("frame payload does not have correct length. expected %d; got %d\n", 1024, len(frame.Payload())) 35 | } 36 | expectedLength := 6 + 6 + int(NotTagged) + 2 + 1024 37 | if len(frame) != expectedLength { 38 | t.Fatalf("frame does not have correct length. expected %d; got %d\n", expectedLength, len(frame)) 39 | } 40 | if !bytes.Equal([]byte(frame.Source()), []byte(src)) { 41 | t.Fatalf("frame source address is incorrect. expected %s; got %s\n", src.String(), frame.Source().String()) 42 | } 43 | if !bytes.Equal([]byte(frame.Destination()), []byte(dst)) { 44 | t.Fatalf("frame destination address is incorrect. expected %s; got %s\n", dst.String(), frame.Destination().String()) 45 | } 46 | if frame.Tagging() != NotTagged { 47 | t.Fatalf("frame tagging is incorrect. expected %d; got %d\n", NotTagged, frame.Tagging()) 48 | } 49 | if frame.Ethertype() != IPv6 { 50 | t.Fatalf("frame ethertype is incorrect. expected %v; got %v\n", IPv6, frame.Ethertype()) 51 | } 52 | } 53 | 54 | func TestResize(t *testing.T) { 55 | var frame Frame 56 | (&frame).Resize(8) 57 | expectedLength := 6 + 6 + int(NotTagged) + 2 + 8 58 | if len(frame) != expectedLength { 59 | t.Fatalf("frame does not have correct length. expected %d; got %d\n", expectedLength, len(frame)) 60 | } 61 | frame.Payload()[0] = 42 62 | (&frame).Resize(1024) 63 | if frame.Payload()[0] != 42 { 64 | t.Fatalf("expanded frame does not have same content\n") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ethernet/ethertypes.go: -------------------------------------------------------------------------------- 1 | package ethernet 2 | 3 | // Ethertype is a type used represent the ethertype of an ethernet frame. 4 | // Defined as a 2-byte array, variables of this type are intended to be used as 5 | // immutable values. 6 | type Ethertype [2]byte 7 | 8 | // Common ethertype values 9 | var ( 10 | IPv4 = Ethertype{0x08, 0x00} 11 | ARP = Ethertype{0x08, 0x06} 12 | WakeOnLAN = Ethertype{0x08, 0x42} 13 | TRILL = Ethertype{0x22, 0xF3} 14 | DECnetPhase4 = Ethertype{0x60, 0x03} 15 | RARP = Ethertype{0x80, 0x35} 16 | AppleTalk = Ethertype{0x80, 0x9B} 17 | AARP = Ethertype{0x80, 0xF3} 18 | IPX1 = Ethertype{0x81, 0x37} 19 | IPX2 = Ethertype{0x81, 0x38} 20 | QNXQnet = Ethertype{0x82, 0x04} 21 | IPv6 = Ethertype{0x86, 0xDD} 22 | EthernetFlowControl = Ethertype{0x88, 0x08} 23 | IEEE802_3 = Ethertype{0x88, 0x09} 24 | CobraNet = Ethertype{0x88, 0x19} 25 | MPLSUnicast = Ethertype{0x88, 0x47} 26 | MPLSMulticast = Ethertype{0x88, 0x48} 27 | PPPoEDiscovery = Ethertype{0x88, 0x63} 28 | PPPoESession = Ethertype{0x88, 0x64} 29 | JumboFrames = Ethertype{0x88, 0x70} 30 | HomePlug1_0MME = Ethertype{0x88, 0x7B} 31 | IEEE802_1X = Ethertype{0x88, 0x8E} 32 | PROFINET = Ethertype{0x88, 0x92} 33 | HyperSCSI = Ethertype{0x88, 0x9A} 34 | AoE = Ethertype{0x88, 0xA2} 35 | EtherCAT = Ethertype{0x88, 0xA4} 36 | EthernetPowerlink = Ethertype{0x88, 0xAB} 37 | LLDP = Ethertype{0x88, 0xCC} 38 | SERCOS3 = Ethertype{0x88, 0xCD} 39 | WSMP = Ethertype{0x88, 0xDC} 40 | HomePlugAVMME = Ethertype{0x88, 0xE1} 41 | MRP = Ethertype{0x88, 0xE3} 42 | IEEE802_1AE = Ethertype{0x88, 0xE5} 43 | IEEE1588 = Ethertype{0x88, 0xF7} 44 | IEEE802_1ag = Ethertype{0x89, 0x02} 45 | FCoE = Ethertype{0x89, 0x06} 46 | FCoEInit = Ethertype{0x89, 0x14} 47 | RoCE = Ethertype{0x89, 0x15} 48 | CTP = Ethertype{0x90, 0x00} 49 | VeritasLLT = Ethertype{0xCA, 0xFE} 50 | ) 51 | -------------------------------------------------------------------------------- /ethernet/frame.go: -------------------------------------------------------------------------------- 1 | package ethernet 2 | 3 | import "net" 4 | 5 | // Frame represents an ethernet frame. The length of the underlying slice of a 6 | // Frame should always reflect the ethernet frame length. 7 | type Frame []byte 8 | 9 | // Tagging is a type used to indicate whether/how a frame is tagged. The value 10 | // is number of bytes taken by tagging. 11 | type Tagging byte 12 | 13 | // Const values for different taggings 14 | const ( 15 | NotTagged Tagging = 0 16 | Tagged Tagging = 4 17 | DoubleTagged Tagging = 8 18 | ) 19 | 20 | // Destination returns the destination address field of the frame. The address 21 | // references a slice on the frame. 22 | // 23 | // It is not safe to use this method if f is nil or an invalid ethernet frame. 24 | func (f Frame) Destination() net.HardwareAddr { 25 | return net.HardwareAddr(f[:6:6]) 26 | } 27 | 28 | // Source returns the source address field of the frame. The address references 29 | // a slice on the frame. 30 | // 31 | // It is not safe to use this method if f is nil or an invalid ethernet frame. 32 | func (f Frame) Source() net.HardwareAddr { 33 | return net.HardwareAddr(f[6:12:12]) 34 | } 35 | 36 | // Tagging returns whether/how the frame has 802.1Q tag(s). 37 | // 38 | // It is not safe to use this method if f is nil or an invalid ethernet frame. 39 | func (f Frame) Tagging() Tagging { 40 | if f[12] == 0x81 && f[13] == 0x00 { 41 | return Tagged 42 | } else if f[12] == 0x88 && f[13] == 0xa8 { 43 | return DoubleTagged 44 | } 45 | return NotTagged 46 | } 47 | 48 | // Tag returns a slice holding the tag part of the frame, if any. Note that 49 | // this includes the Tag Protocol Identifier (TPID), e.g. 0x8100 or 0x88a8. 50 | // Upper layer should use the returned slice for both reading and writing. 51 | // 52 | // It is not safe to use this method if f is nil or an invalid ethernet frame. 53 | func (f Frame) Tags() []byte { 54 | tagging := f.Tagging() 55 | return f[12 : 12+tagging : 12+tagging] 56 | } 57 | 58 | // Ethertype returns the ethertype field of the frame. 59 | // 60 | // It is not safe to use this method if f is nil or an invalid ethernet frame. 61 | func (f Frame) Ethertype() Ethertype { 62 | ethertypePos := 12 + f.Tagging() 63 | return Ethertype{f[ethertypePos], f[ethertypePos+1]} 64 | } 65 | 66 | // Payload returns a slice holding the payload part of the frame. Upper layer 67 | // should use the returned slice for both reading and writing purposes. 68 | // 69 | // It is not safe to use this method if f is nil or an invalid ethernet frame. 70 | func (f Frame) Payload() []byte { 71 | return f[12+f.Tagging()+2:] 72 | } 73 | 74 | // Resize re-slices (*f) so that len(*f) holds exactly payloadSize bytes of 75 | // payload. If cap(*f) is not large enough, a new slice is made and content 76 | // from old slice is copied to the new one. 77 | // 78 | // If len(*f) is less than 14 bytes, it is assumed to be not tagged. 79 | // 80 | // It is safe to call Resize on a pointer to a nil Frame. 81 | func (f *Frame) Resize(payloadSize int) { 82 | tagging := NotTagged 83 | if len(*f) > 6+6+2 { 84 | tagging = f.Tagging() 85 | } 86 | f.resize(6 + 6 + int(tagging) + 2 + payloadSize) 87 | } 88 | 89 | // Prepare prepares *f to be used, by filling in dst/src address, setting up 90 | // proper tagging and ethertype, and resizing it to proper length. 91 | // 92 | // It is safe to call Prepare on a pointer to a nil Frame or invalid Frame. 93 | func (f *Frame) Prepare(dst net.HardwareAddr, src net.HardwareAddr, tagging Tagging, ethertype Ethertype, payloadSize int) { 94 | f.resize(6 + 6 + int(tagging) + 2 + payloadSize) 95 | copy((*f)[0:6:6], dst) 96 | copy((*f)[6:12:12], src) 97 | if tagging == Tagged { 98 | (*f)[12] = 0x81 99 | (*f)[13] = 0x00 100 | } else if tagging == DoubleTagged { 101 | (*f)[12] = 0x88 102 | (*f)[13] = 0xa8 103 | } 104 | (*f)[12+tagging] = ethertype[0] 105 | (*f)[12+tagging+1] = ethertype[1] 106 | return 107 | } 108 | 109 | func (f *Frame) resize(length int) { 110 | if cap(*f) < length { 111 | old := *f 112 | *f = make(Frame, length, length) 113 | copy(*f, old) 114 | } else { 115 | *f = (*f)[:length] 116 | } 117 | } 118 | --------------------------------------------------------------------------------