Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 1 | // Copyright 2012 Google, Inc. All rights reserved. |
| 2 | // |
| 3 | // Use of this source code is governed by a BSD-style license |
| 4 | // that can be found in the LICENSE file in the root of the source |
| 5 | // tree. |
| 6 | |
| 7 | package layers |
| 8 | |
| 9 | import ( |
| 10 | "bytes" |
| 11 | "encoding/binary" |
| 12 | "fmt" |
| 13 | |
| 14 | "github.com/google/gopacket" |
| 15 | ) |
| 16 | |
| 17 | type PPPoEOpt uint16 |
| 18 | |
| 19 | // Constants for PPPoE Options |
| 20 | const ( |
| 21 | PPPoEOptEndOfList PPPoEOpt = 0x0000 |
| 22 | PPPoEOptServiceName PPPoEOpt = 0x0101 |
| 23 | PPPoEOptAcName PPPoEOpt = 0x0102 |
| 24 | PPPoEOptHostUniq PPPoEOpt = 0x0103 |
| 25 | PPPoEOptAcCookie PPPoEOpt = 0x0104 |
| 26 | PPPoEOptVendorSpecific PPPoEOpt = 0x0105 |
| 27 | PPPoEOptRelaySessionId PPPoEOpt = 0x0110 |
| 28 | PPPoEOptServiceErrorName PPPoEOpt = 0x0201 |
| 29 | PPPoEOptAcSystemError PPPoEOpt = 0x0202 |
| 30 | PPPoEOptGenericError PPPoEOpt = 0x0203 |
| 31 | ) |
| 32 | |
| 33 | type PPPoEOption struct { |
| 34 | Type PPPoEOpt |
| 35 | Length uint16 |
| 36 | Data []byte |
| 37 | } |
| 38 | |
| 39 | func (o PPPoEOption) encode(b []byte) (uint16, error) { |
| 40 | binary.BigEndian.PutUint16(b[0:2], uint16(o.Type)) |
| 41 | binary.BigEndian.PutUint16(b[2:4], uint16(o.Length)) |
| 42 | copy(b[4:], o.Data) |
| 43 | return (4 + o.Length), nil |
| 44 | } |
| 45 | |
| 46 | func (o PPPoEOption) decode(b []byte) error { |
| 47 | if len(b) < 4 { |
| 48 | return DecOptionNotEnoughData |
| 49 | } |
| 50 | o.Type = PPPoEOpt(binary.BigEndian.Uint16(b[0:2])) |
| 51 | o.Length = binary.BigEndian.Uint16(b[2:4]) |
| 52 | if int(o.Length) > len(b[4:]) { |
| 53 | return DecOptionNotEnoughData |
| 54 | } |
| 55 | o.Data = b[4 : 4+int(o.Length)] |
| 56 | return nil |
| 57 | } |
| 58 | |
| 59 | func NewPPPoEOption(opt PPPoEOpt, data []byte) PPPoEOption { |
| 60 | length := (uint16)(len(data)) |
| 61 | option := PPPoEOption{Type: opt, Length: length, Data: data} |
| 62 | return option |
| 63 | } |
| 64 | |
| 65 | func (o PPPoEOption) String() string { |
| 66 | return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data) |
| 67 | } |
| 68 | |
| 69 | func (o PPPoEOption) length() int { |
| 70 | return 4 + int(o.Length) |
| 71 | } |
| 72 | |
| 73 | type PPPoEOptions []PPPoEOption |
| 74 | |
| 75 | // String returns a string version of the options list. |
| 76 | func (o PPPoEOptions) String() string { |
| 77 | buf := &bytes.Buffer{} |
| 78 | buf.WriteByte('[') |
| 79 | for i, opt := range o { |
| 80 | buf.WriteString(opt.String()) |
| 81 | if i+1 != len(o) { |
| 82 | buf.WriteString(", ") |
| 83 | } |
| 84 | } |
| 85 | buf.WriteByte(']') |
| 86 | return buf.String() |
| 87 | } |
| 88 | |
| 89 | func (o PPPoEOptions) length() int { |
| 90 | length := 0 |
| 91 | for _, op := range o { |
| 92 | length = length + op.length() |
| 93 | } |
| 94 | return length |
| 95 | } |
| 96 | |
| 97 | // String returns a string version of a PPPoEOpt. |
| 98 | func (o PPPoEOpt) String() string { |
| 99 | switch o { |
| 100 | case PPPoEOptEndOfList: |
| 101 | return "EndOfList" |
| 102 | case PPPoEOptServiceName: |
| 103 | return "ServiceName" |
| 104 | case PPPoEOptAcName: |
| 105 | return "AcName" |
| 106 | case PPPoEOptHostUniq: |
| 107 | return "HostUniq" |
| 108 | case PPPoEOptAcCookie: |
| 109 | return "AcCookie" |
| 110 | case PPPoEOptVendorSpecific: |
| 111 | return "VendorSpecific" |
| 112 | case PPPoEOptRelaySessionId: |
| 113 | return "RelaySessionId" |
| 114 | case PPPoEOptServiceErrorName: |
| 115 | return "ServiceErrorName" |
| 116 | case PPPoEOptAcSystemError: |
| 117 | return "AcSystemError" |
| 118 | case PPPoEOptGenericError: |
| 119 | return "GenericError" |
| 120 | default: |
| 121 | return "Unknown" |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // PPPoE is the layer for PPPoE encapsulation headers. |
| 126 | type PPPoE struct { |
| 127 | BaseLayer |
| 128 | Version uint8 |
| 129 | Type uint8 |
| 130 | Code PPPoECode |
| 131 | SessionId uint16 |
| 132 | Length uint16 |
| 133 | Options PPPoEOptions |
| 134 | } |
| 135 | |
| 136 | // LayerType returns gopacket.LayerTypePPPoE. |
| 137 | func (p *PPPoE) LayerType() gopacket.LayerType { |
| 138 | return LayerTypePPPoE |
| 139 | } |
| 140 | |
| 141 | func (p *PPPoE) DecodeOptions(data []byte, len uint16) { |
| 142 | |
| 143 | start := uint16(0) |
| 144 | stop := len |
| 145 | for start < stop { |
| 146 | pppoeOpt := &PPPoEOption{} |
| 147 | pppoeOpt.Type = PPPoEOpt(binary.BigEndian.Uint16(data[start : start+2])) |
| 148 | pppoeOpt.Length = binary.BigEndian.Uint16(data[start+2 : start+4]) |
| 149 | if pppoeOpt.Length != 0 { |
| 150 | pppoeOpt.Data = data[start+4 : start+4+pppoeOpt.Length] |
| 151 | } |
| 152 | start = start + 4 + pppoeOpt.Length |
| 153 | p.Options = append(p.Options, *pppoeOpt) |
| 154 | } |
| 155 | |
| 156 | } |
| 157 | |
| 158 | // decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516). |
| 159 | func decodePPPoE(data []byte, p gopacket.PacketBuilder) error { |
| 160 | |
| 161 | pppoe := &PPPoE{ |
| 162 | Version: data[0] >> 4, |
| 163 | Type: data[0] & 0x0F, |
| 164 | Code: PPPoECode(data[1]), |
| 165 | SessionId: binary.BigEndian.Uint16(data[2:4]), |
| 166 | Length: binary.BigEndian.Uint16(data[4:6]), |
| 167 | } |
| 168 | pppoe.DecodeOptions(data[6:], pppoe.Length) |
| 169 | pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]} |
| 170 | p.AddLayer(pppoe) |
| 171 | return p.NextDecoder(pppoe.Code) |
| 172 | } |
| 173 | |
| 174 | // SerializeTo writes the serialized form of this layer into the |
| 175 | // SerializationBuffer, implementing gopacket.SerializableLayer. |
| 176 | // See the docs for gopacket.SerializableLayer for more info. |
| 177 | func (p *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { |
| 178 | payload := b.Bytes() |
| 179 | |
| 180 | //Pkt length with Options |
| 181 | length := 6 |
| 182 | optLength := 0 |
| 183 | for _, o := range p.Options { |
| 184 | optLength = optLength + o.length() |
| 185 | } |
| 186 | |
| 187 | bytes, err := b.PrependBytes(length + optLength) |
| 188 | if err != nil { |
| 189 | return err |
| 190 | } |
| 191 | bytes[0] = (p.Version << 4) | p.Type |
| 192 | bytes[1] = byte(p.Code) |
| 193 | binary.BigEndian.PutUint16(bytes[2:], p.SessionId) |
| 194 | if opts.FixLengths { |
| 195 | p.Length = uint16(len(payload) + optLength) |
| 196 | } |
| 197 | binary.BigEndian.PutUint16(bytes[4:], p.Length) |
| 198 | |
| 199 | //Encoding the options |
| 200 | start := uint16(6) |
| 201 | for _, option := range p.Options { |
| 202 | optLen, _ := option.encode(bytes[start:]) |
| 203 | start = start + optLen |
| 204 | } |
| 205 | return nil |
| 206 | } |