blob: 9a40e2d9124e348ac507dc9ac300c7028bcf7f0c [file] [log] [blame]
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
type PPPoEOpt uint16
// Constants for PPPoE Options
const (
PPPoEOptEndOfList PPPoEOpt = 0x0000
PPPoEOptServiceName PPPoEOpt = 0x0101
PPPoEOptAcName PPPoEOpt = 0x0102
PPPoEOptHostUniq PPPoEOpt = 0x0103
PPPoEOptAcCookie PPPoEOpt = 0x0104
PPPoEOptVendorSpecific PPPoEOpt = 0x0105
PPPoEOptRelaySessionId PPPoEOpt = 0x0110
PPPoEOptServiceErrorName PPPoEOpt = 0x0201
PPPoEOptAcSystemError PPPoEOpt = 0x0202
PPPoEOptGenericError PPPoEOpt = 0x0203
)
type PPPoEOption struct {
Type PPPoEOpt
Length uint16
Data []byte
}
func (o PPPoEOption) encode(b []byte) (uint16, error) {
binary.BigEndian.PutUint16(b[0:2], uint16(o.Type))
binary.BigEndian.PutUint16(b[2:4], uint16(o.Length))
copy(b[4:], o.Data)
return (4 + o.Length), nil
}
func (o PPPoEOption) decode(b []byte) error {
if len(b) < 4 {
return DecOptionNotEnoughData
}
o.Type = PPPoEOpt(binary.BigEndian.Uint16(b[0:2]))
o.Length = binary.BigEndian.Uint16(b[2:4])
if int(o.Length) > len(b[4:]) {
return DecOptionNotEnoughData
}
o.Data = b[4 : 4+int(o.Length)]
return nil
}
func NewPPPoEOption(opt PPPoEOpt, data []byte) PPPoEOption {
length := (uint16)(len(data))
option := PPPoEOption{Type: opt, Length: length, Data: data}
return option
}
func (o PPPoEOption) String() string {
return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
}
func (o PPPoEOption) length() int {
return 4 + int(o.Length)
}
type PPPoEOptions []PPPoEOption
// String returns a string version of the options list.
func (o PPPoEOptions) String() string {
buf := &bytes.Buffer{}
buf.WriteByte('[')
for i, opt := range o {
buf.WriteString(opt.String())
if i+1 != len(o) {
buf.WriteString(", ")
}
}
buf.WriteByte(']')
return buf.String()
}
func (o PPPoEOptions) length() int {
length := 0
for _, op := range o {
length = length + op.length()
}
return length
}
// String returns a string version of a PPPoEOpt.
func (o PPPoEOpt) String() string {
switch o {
case PPPoEOptEndOfList:
return "EndOfList"
case PPPoEOptServiceName:
return "ServiceName"
case PPPoEOptAcName:
return "AcName"
case PPPoEOptHostUniq:
return "HostUniq"
case PPPoEOptAcCookie:
return "AcCookie"
case PPPoEOptVendorSpecific:
return "VendorSpecific"
case PPPoEOptRelaySessionId:
return "RelaySessionId"
case PPPoEOptServiceErrorName:
return "ServiceErrorName"
case PPPoEOptAcSystemError:
return "AcSystemError"
case PPPoEOptGenericError:
return "GenericError"
default:
return "Unknown"
}
}
// PPPoE is the layer for PPPoE encapsulation headers.
type PPPoE struct {
BaseLayer
Version uint8
Type uint8
Code PPPoECode
SessionId uint16
Length uint16
Options PPPoEOptions
}
// LayerType returns gopacket.LayerTypePPPoE.
func (p *PPPoE) LayerType() gopacket.LayerType {
return LayerTypePPPoE
}
func (p *PPPoE) DecodeOptions(data []byte, len uint16) {
start := uint16(0)
stop := len
for start < stop {
pppoeOpt := &PPPoEOption{}
pppoeOpt.Type = PPPoEOpt(binary.BigEndian.Uint16(data[start : start+2]))
pppoeOpt.Length = binary.BigEndian.Uint16(data[start+2 : start+4])
if pppoeOpt.Length != 0 {
pppoeOpt.Data = data[start+4 : start+4+pppoeOpt.Length]
}
start = start + 4 + pppoeOpt.Length
p.Options = append(p.Options, *pppoeOpt)
}
}
// decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516).
func decodePPPoE(data []byte, p gopacket.PacketBuilder) error {
pppoe := &PPPoE{
Version: data[0] >> 4,
Type: data[0] & 0x0F,
Code: PPPoECode(data[1]),
SessionId: binary.BigEndian.Uint16(data[2:4]),
Length: binary.BigEndian.Uint16(data[4:6]),
}
pppoe.DecodeOptions(data[6:], pppoe.Length)
pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]}
p.AddLayer(pppoe)
return p.NextDecoder(pppoe.Code)
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
payload := b.Bytes()
//Pkt length with Options
length := 6
optLength := 0
for _, o := range p.Options {
optLength = optLength + o.length()
}
bytes, err := b.PrependBytes(length + optLength)
if err != nil {
return err
}
bytes[0] = (p.Version << 4) | p.Type
bytes[1] = byte(p.Code)
binary.BigEndian.PutUint16(bytes[2:], p.SessionId)
if opts.FixLengths {
p.Length = uint16(len(payload) + optLength)
}
binary.BigEndian.PutUint16(bytes[4:], p.Length)
//Encoding the options
start := uint16(6)
for _, option := range p.Options {
optLen, _ := option.encode(bytes[start:])
start = start + optLen
}
return nil
}