// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bgp

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"net"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

const (
	AFI_IP     = 1
	AFI_IP6    = 2
	AFI_L2VPN  = 25
	AFI_OPAQUE = 16397
)

const (
	SAFI_UNICAST                  = 1
	SAFI_MULTICAST                = 2
	SAFI_MPLS_LABEL               = 4
	SAFI_ENCAPSULATION            = 7
	SAFI_VPLS                     = 65
	SAFI_EVPN                     = 70
	SAFI_MPLS_VPN                 = 128
	SAFI_MPLS_VPN_MULTICAST       = 129
	SAFI_ROUTE_TARGET_CONSTRTAINS = 132
	SAFI_FLOW_SPEC_UNICAST        = 133
	SAFI_FLOW_SPEC_VPN            = 134
	SAFI_KEY_VALUE                = 241
)

const (
	BGP_ORIGIN_ATTR_TYPE_IGP        = 0
	BGP_ORIGIN_ATTR_TYPE_EGP        = 1
	BGP_ORIGIN_ATTR_TYPE_INCOMPLETE = 2
)

const (
	BGP_ASPATH_ATTR_TYPE_SET        = 1
	BGP_ASPATH_ATTR_TYPE_SEQ        = 2
	BGP_ASPATH_ATTR_TYPE_CONFED_SEQ = 3
	BGP_ASPATH_ATTR_TYPE_CONFED_SET = 4
)

// RFC7153 5.1. Registries for the "Type" Field
// RANGE	REGISTRACTION PROCEDURES
// 0x00-0x3F	Transitive First Come First Served
// 0x40-0x7F	Non-Transitive First Come First Served
// 0x80-0x8F	Transitive Experimental Use
// 0x90-0xBF	Transitive Standards Action
// 0xC0-0xCF	Non-Transitive Experimental Use
// 0xD0-0xFF	Non-Transitive Standards Action
type ExtendedCommunityAttrType uint8

const (
	EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC      ExtendedCommunityAttrType = 0x00
	EC_TYPE_TRANSITIVE_IP4_SPECIFIC               ExtendedCommunityAttrType = 0x01
	EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC     ExtendedCommunityAttrType = 0x02
	EC_TYPE_TRANSITIVE_OPAQUE                     ExtendedCommunityAttrType = 0x03
	EC_TYPE_TRANSITIVE_QOS_MARKING                ExtendedCommunityAttrType = 0x04
	EC_TYPE_COS_CAPABILITY                        ExtendedCommunityAttrType = 0x05
	EC_TYPE_EVPN                                  ExtendedCommunityAttrType = 0x06
	EC_TYPE_FLOWSPEC_REDIRECT_MIRROR              ExtendedCommunityAttrType = 0x08
	EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC  ExtendedCommunityAttrType = 0x40
	EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC           ExtendedCommunityAttrType = 0x41
	EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x42
	EC_TYPE_NON_TRANSITIVE_OPAQUE                 ExtendedCommunityAttrType = 0x43
	EC_TYPE_NON_TRANSITIVE_QOS_MARKING            ExtendedCommunityAttrType = 0x44
	EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL       ExtendedCommunityAttrType = 0x80
	EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2      ExtendedCommunityAttrType = 0x81 // RFC7674
	EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3      ExtendedCommunityAttrType = 0x82 // RFC7674
)

// RFC7153 5.2. Registraction for the "Sub-Type" Field
// RANGE	REGISTRACTION PROCEDURES
// 0x00-0xBF	First Come First Served
// 0xC0-0xFF	IETF Review
type ExtendedCommunityAttrSubType uint8

const (
	EC_SUBTYPE_ROUTE_TARGET            ExtendedCommunityAttrSubType = 0x02 // EC_TYPE: 0x00, 0x01, 0x02
	EC_SUBTYPE_ROUTE_ORIGIN            ExtendedCommunityAttrSubType = 0x03 // EC_TYPE: 0x00, 0x01, 0x02
	EC_SUBTYPE_LINK_BANDWIDTH          ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x40
	EC_SUBTYPE_GENERIC                 ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x02, 0x42
	EC_SUBTYPE_OSPF_DOMAIN_ID          ExtendedCommunityAttrSubType = 0x05 // EC_TYPE: 0x00, 0x01, 0x02
	EC_SUBTYPE_OSPF_ROUTE_ID           ExtendedCommunityAttrSubType = 0x07 // EC_TYPE: 0x01
	EC_SUBTYPE_BGP_DATA_COLLECTION     ExtendedCommunityAttrSubType = 0x08 // EC_TYPE: 0x00, 0x02
	EC_SUBTYPE_SOURCE_AS               ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x00, 0x02
	EC_SUBTYPE_L2VPN_ID                ExtendedCommunityAttrSubType = 0x0A // EC_TYPE: 0x00, 0x01
	EC_SUBTYPE_VRF_ROUTE_IMPORT        ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x01
	EC_SUBTYPE_CISCO_VPN_DISTINGUISHER ExtendedCommunityAttrSubType = 0x10 // EC_TYPE: 0x00, 0x01, 0x02

	EC_SUBTYPE_OSPF_ROUTE_TYPE ExtendedCommunityAttrSubType = 0x06 // EC_TYPE: 0x03
	EC_SUBTYPE_COLOR           ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x03
	EC_SUBTYPE_ENCAPSULATION   ExtendedCommunityAttrSubType = 0x0C // EC_TYPE: 0x03
	EC_SUBTYPE_DEFAULT_GATEWAY ExtendedCommunityAttrSubType = 0x0D // EC_TYPE: 0x03

	EC_SUBTYPE_ORIGIN_VALIDATION ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x43

	EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE   ExtendedCommunityAttrSubType = 0x06 // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION ExtendedCommunityAttrSubType = 0x07 // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_REDIRECT       ExtendedCommunityAttrSubType = 0x08 // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x80
	EC_SUBTYPE_L2_INFO                 ExtendedCommunityAttrSubType = 0x0A // EC_TYPE: 0x80

	EC_SUBTYPE_MAC_MOBILITY ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x06
	EC_SUBTYPE_ESI_LABEL    ExtendedCommunityAttrSubType = 0x01 // EC_TYPE: 0x06
	EC_SUBTYPE_ES_IMPORT    ExtendedCommunityAttrSubType = 0x02 // EC_TYPE: 0x06

	EC_SUBTYPE_UUID_BASED_RT ExtendedCommunityAttrSubType = 0x11
)

type TunnelType uint16

const (
	TUNNEL_TYPE_L2TP3       TunnelType = 1
	TUNNEL_TYPE_GRE         TunnelType = 2
	TUNNEL_TYPE_IP_IN_IP    TunnelType = 7
	TUNNEL_TYPE_VXLAN       TunnelType = 8
	TUNNEL_TYPE_NVGRE       TunnelType = 9
	TUNNEL_TYPE_MPLS        TunnelType = 10
	TUNNEL_TYPE_MPLS_IN_GRE TunnelType = 11
	TUNNEL_TYPE_VXLAN_GRE   TunnelType = 12
)

type PmsiTunnelType uint8

const (
	PMSI_TUNNEL_TYPE_NO_TUNNEL      PmsiTunnelType = 0
	PMSI_TUNNEL_TYPE_RSVP_TE_P2MP   PmsiTunnelType = 1
	PMSI_TUNNEL_TYPE_MLDP_P2MP      PmsiTunnelType = 2
	PMSI_TUNNEL_TYPE_PIM_SSM_TREE   PmsiTunnelType = 3
	PMSI_TUNNEL_TYPE_PIM_SM_TREE    PmsiTunnelType = 4
	PMSI_TUNNEL_TYPE_BIDIR_PIM_TREE PmsiTunnelType = 5
	PMSI_TUNNEL_TYPE_INGRESS_REPL   PmsiTunnelType = 6
	PMSI_TUNNEL_TYPE_MLDP_MP2MP     PmsiTunnelType = 7
)

func (p PmsiTunnelType) String() string {
	switch p {
	case PMSI_TUNNEL_TYPE_NO_TUNNEL:
		return "no-tunnel"
	case PMSI_TUNNEL_TYPE_RSVP_TE_P2MP:
		return "rsvp-te-p2mp"
	case PMSI_TUNNEL_TYPE_MLDP_P2MP:
		return "mldp-p2mp"
	case PMSI_TUNNEL_TYPE_PIM_SSM_TREE:
		return "pim-ssm-tree"
	case PMSI_TUNNEL_TYPE_PIM_SM_TREE:
		return "pim-sm-tree"
	case PMSI_TUNNEL_TYPE_BIDIR_PIM_TREE:
		return "bidir-pim-tree"
	case PMSI_TUNNEL_TYPE_INGRESS_REPL:
		return "ingress-repl"
	case PMSI_TUNNEL_TYPE_MLDP_MP2MP:
		return "mldp-mp2mp"
	default:
		return fmt.Sprintf("PmsiTunnelType(%d)", uint8(p))
	}
}

type EncapSubTLVType uint8

const (
	ENCAP_SUBTLV_TYPE_ENCAPSULATION EncapSubTLVType = 1
	ENCAP_SUBTLV_TYPE_PROTOCOL      EncapSubTLVType = 2
	ENCAP_SUBTLV_TYPE_COLOR         EncapSubTLVType = 4
)

const (
	_ = iota
	BGP_MSG_OPEN
	BGP_MSG_UPDATE
	BGP_MSG_NOTIFICATION
	BGP_MSG_KEEPALIVE
	BGP_MSG_ROUTE_REFRESH
)

const (
	BGP_OPT_CAPABILITY = 2
)

type BGPCapabilityCode uint8

const (
	BGP_CAP_MULTIPROTOCOL          BGPCapabilityCode = 1
	BGP_CAP_ROUTE_REFRESH          BGPCapabilityCode = 2
	BGP_CAP_CARRYING_LABEL_INFO    BGPCapabilityCode = 4
	BGP_CAP_GRACEFUL_RESTART       BGPCapabilityCode = 64
	BGP_CAP_FOUR_OCTET_AS_NUMBER   BGPCapabilityCode = 65
	BGP_CAP_ADD_PATH               BGPCapabilityCode = 69
	BGP_CAP_ENHANCED_ROUTE_REFRESH BGPCapabilityCode = 70
	BGP_CAP_ROUTE_REFRESH_CISCO    BGPCapabilityCode = 128
)

type ParameterCapabilityInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	Len() int
	Code() BGPCapabilityCode
}

type DefaultParameterCapability struct {
	CapCode  BGPCapabilityCode `json:"code"`
	CapLen   uint8             `json:"-"`
	CapValue []byte            `json:"value,omitempty"`
}

func (c *DefaultParameterCapability) Code() BGPCapabilityCode {
	return c.CapCode
}

func (c *DefaultParameterCapability) DecodeFromBytes(data []byte) error {
	c.CapCode = BGPCapabilityCode(data[0])
	c.CapLen = data[1]
	if len(data) < 2+int(c.CapLen) {
		return fmt.Errorf("Not all OptionParameterCapability bytes available")
	}
	if c.CapLen > 0 {
		c.CapValue = data[2 : 2+c.CapLen]
	}
	return nil
}

func (c *DefaultParameterCapability) Serialize() ([]byte, error) {
	c.CapLen = uint8(len(c.CapValue))
	buf := make([]byte, 2)
	buf[0] = uint8(c.CapCode)
	buf[1] = c.CapLen
	buf = append(buf, c.CapValue...)
	return buf, nil
}

func (c *DefaultParameterCapability) Len() int {
	return int(c.CapLen + 2)
}

type CapMultiProtocol struct {
	DefaultParameterCapability
	CapValue RouteFamily
}

func (c *CapMultiProtocol) DecodeFromBytes(data []byte) error {
	c.DefaultParameterCapability.DecodeFromBytes(data)
	data = data[2:]
	if len(data) < 4 {
		return fmt.Errorf("Not all CapabilityMultiProtocol bytes available")
	}
	c.CapValue = AfiSafiToRouteFamily(binary.BigEndian.Uint16(data[0:2]), data[3])
	return nil
}

func (c *CapMultiProtocol) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	afi, safi := RouteFamilyToAfiSafi(c.CapValue)
	binary.BigEndian.PutUint16(buf[0:], afi)
	buf[3] = safi
	c.DefaultParameterCapability.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapMultiProtocol) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code  BGPCapabilityCode `json:"code"`
		Value RouteFamily       `json:"value"`
	}{
		Code:  c.Code(),
		Value: c.CapValue,
	})
}

func NewCapMultiProtocol(rf RouteFamily) *CapMultiProtocol {
	return &CapMultiProtocol{
		DefaultParameterCapability{
			CapCode: BGP_CAP_MULTIPROTOCOL,
		},
		rf,
	}
}

type CapRouteRefresh struct {
	DefaultParameterCapability
}

func NewCapRouteRefresh() *CapRouteRefresh {
	return &CapRouteRefresh{
		DefaultParameterCapability{
			CapCode: BGP_CAP_ROUTE_REFRESH,
		},
	}
}

type CapCarryingLabelInfo struct {
	DefaultParameterCapability
}

type CapGracefulRestartTuple struct {
	AFI   uint16
	SAFI  uint8
	Flags uint8
}

func (c *CapGracefulRestartTuple) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RouteFamily RouteFamily `json:"route_family"`
		Flags       uint8       `json:"flags"`
	}{
		RouteFamily: AfiSafiToRouteFamily(c.AFI, c.SAFI),
		Flags:       c.Flags,
	})
}

func NewCapGracefulRestartTuple(rf RouteFamily, forward bool) *CapGracefulRestartTuple {
	afi, safi := RouteFamilyToAfiSafi(rf)
	flags := 0
	if forward {
		flags = 0x80
	}
	return &CapGracefulRestartTuple{
		AFI:   afi,
		SAFI:  safi,
		Flags: uint8(flags),
	}
}

type CapGracefulRestart struct {
	DefaultParameterCapability
	Flags  uint8
	Time   uint16
	Tuples []*CapGracefulRestartTuple
}

func (c *CapGracefulRestart) DecodeFromBytes(data []byte) error {
	c.DefaultParameterCapability.DecodeFromBytes(data)
	data = data[2:]
	restart := binary.BigEndian.Uint16(data[0:2])
	c.Flags = uint8(restart >> 12)
	c.Time = restart & 0xfff
	data = data[2:]
	c.Tuples = make([]*CapGracefulRestartTuple, 0, len(data)/4)
	for len(data) >= 4 {
		t := &CapGracefulRestartTuple{binary.BigEndian.Uint16(data[0:2]),
			data[2], data[3]}
		c.Tuples = append(c.Tuples, t)
		data = data[4:]
	}
	return nil
}

func (c *CapGracefulRestart) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	binary.BigEndian.PutUint16(buf[0:], uint16(c.Flags)<<12|c.Time)
	for _, t := range c.Tuples {
		tbuf := make([]byte, 4)
		binary.BigEndian.PutUint16(tbuf[0:2], t.AFI)
		tbuf[2] = t.SAFI
		tbuf[3] = t.Flags
		buf = append(buf, tbuf...)
	}
	c.DefaultParameterCapability.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapGracefulRestart) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code   BGPCapabilityCode          `json:"code"`
		Flags  uint8                      `json:"flags"`
		Time   uint16                     `json:"time"`
		Tuples []*CapGracefulRestartTuple `json:"tuples"`
	}{
		Code:   c.Code(),
		Flags:  c.Flags,
		Time:   c.Time,
		Tuples: c.Tuples,
	})
}

func NewCapGracefulRestart(restarting bool, time uint16, tuples []*CapGracefulRestartTuple) *CapGracefulRestart {
	flags := 0
	if restarting {
		flags = 0x08
	}
	return &CapGracefulRestart{
		DefaultParameterCapability: DefaultParameterCapability{
			CapCode: BGP_CAP_GRACEFUL_RESTART,
		},
		Flags:  uint8(flags),
		Time:   time,
		Tuples: tuples,
	}
}

type CapFourOctetASNumber struct {
	DefaultParameterCapability
	CapValue uint32
}

func (c *CapFourOctetASNumber) DecodeFromBytes(data []byte) error {
	c.DefaultParameterCapability.DecodeFromBytes(data)
	data = data[2:]
	if len(data) < 4 {
		return fmt.Errorf("Not all CapabilityMultiProtocol bytes available")
	}
	c.CapValue = binary.BigEndian.Uint32(data[0:4])
	return nil
}

func (c *CapFourOctetASNumber) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, c.CapValue)
	c.DefaultParameterCapability.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapFourOctetASNumber) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code  BGPCapabilityCode `json:"code"`
		Value uint32            `json:"value"`
	}{
		Code:  c.Code(),
		Value: c.CapValue,
	})
}

func NewCapFourOctetASNumber(asnum uint32) *CapFourOctetASNumber {
	return &CapFourOctetASNumber{
		DefaultParameterCapability{
			CapCode: BGP_CAP_FOUR_OCTET_AS_NUMBER,
		},
		asnum,
	}
}

type BGPAddPathMode uint8

const (
	BGP_ADD_PATH_RECEIVE BGPAddPathMode = 1
	BGP_ADD_PATH_SEND    BGPAddPathMode = 2
	BGP_ADD_PATH_BOTH    BGPAddPathMode = 3
)

func (m BGPAddPathMode) String() string {
	switch m {
	case BGP_ADD_PATH_RECEIVE:
		return "receive"
	case BGP_ADD_PATH_SEND:
		return "send"
	case BGP_ADD_PATH_BOTH:
		return "receive/send"
	default:
		return fmt.Sprintf("unknown(%d)", m)
	}
}

type CapAddPath struct {
	DefaultParameterCapability
	RouteFamily RouteFamily
	Mode        BGPAddPathMode
}

func (c *CapAddPath) DecodeFromBytes(data []byte) error {
	c.DefaultParameterCapability.DecodeFromBytes(data)
	data = data[2:]
	if len(data) < 4 {
		return fmt.Errorf("Not all CapabilityAddPath bytes available")
	}
	c.RouteFamily = AfiSafiToRouteFamily(binary.BigEndian.Uint16(data[:2]), data[2])
	c.Mode = BGPAddPathMode(data[3])
	return nil
}

func (c *CapAddPath) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	afi, safi := RouteFamilyToAfiSafi(c.RouteFamily)
	binary.BigEndian.PutUint16(buf, afi)
	buf[2] = safi
	buf[3] = byte(c.Mode)
	c.DefaultParameterCapability.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapAddPath) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code  BGPCapabilityCode `json:"code"`
		Value RouteFamily       `json:"value"`
		Mode  BGPAddPathMode    `json:"mode"`
	}{
		Code:  c.Code(),
		Value: c.RouteFamily,
		Mode:  c.Mode,
	})
}

func NewCapAddPath(rf RouteFamily, mode BGPAddPathMode) *CapAddPath {
	return &CapAddPath{
		DefaultParameterCapability: DefaultParameterCapability{
			CapCode: BGP_CAP_ADD_PATH,
		},
		RouteFamily: rf,
		Mode:        mode,
	}
}

type CapEnhancedRouteRefresh struct {
	DefaultParameterCapability
}

func NewCapEnhancedRouteRefresh() *CapEnhancedRouteRefresh {
	return &CapEnhancedRouteRefresh{
		DefaultParameterCapability{
			CapCode: BGP_CAP_ENHANCED_ROUTE_REFRESH,
		},
	}
}

type CapRouteRefreshCisco struct {
	DefaultParameterCapability
}

func NewCapRouteRefreshCisco() *CapRouteRefreshCisco {
	return &CapRouteRefreshCisco{
		DefaultParameterCapability{
			CapCode: BGP_CAP_ROUTE_REFRESH_CISCO,
		},
	}
}

type CapUnknown struct {
	DefaultParameterCapability
}

func DecodeCapability(data []byte) (ParameterCapabilityInterface, error) {
	if len(data) < 2 {
		return nil, fmt.Errorf("Not all ParameterCapability bytes available")
	}
	var c ParameterCapabilityInterface
	switch BGPCapabilityCode(data[0]) {
	case BGP_CAP_MULTIPROTOCOL:
		c = &CapMultiProtocol{}
	case BGP_CAP_ROUTE_REFRESH:
		c = &CapRouteRefresh{}
	case BGP_CAP_CARRYING_LABEL_INFO:
		c = &CapCarryingLabelInfo{}
	case BGP_CAP_GRACEFUL_RESTART:
		c = &CapGracefulRestart{}
	case BGP_CAP_FOUR_OCTET_AS_NUMBER:
		c = &CapFourOctetASNumber{}
	case BGP_CAP_ADD_PATH:
		c = &CapAddPath{}
	case BGP_CAP_ENHANCED_ROUTE_REFRESH:
		c = &CapEnhancedRouteRefresh{}
	case BGP_CAP_ROUTE_REFRESH_CISCO:
		c = &CapRouteRefreshCisco{}
	default:
		c = &CapUnknown{}
	}
	err := c.DecodeFromBytes(data)
	return c, err
}

type OptionParameterInterface interface {
	Serialize() ([]byte, error)
}

type OptionParameterCapability struct {
	ParamType  uint8
	ParamLen   uint8
	Capability []ParameterCapabilityInterface
}

func (o *OptionParameterCapability) DecodeFromBytes(data []byte) error {
	if uint8(len(data)) < o.ParamLen {
		return fmt.Errorf("Not all OptionParameterCapability bytes available")
	}
	for len(data) >= 2 {
		c, err := DecodeCapability(data)
		if err != nil {
			return err
		}
		o.Capability = append(o.Capability, c)
		data = data[c.Len():]
	}
	return nil
}

func (o *OptionParameterCapability) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	buf[0] = o.ParamType
	for _, p := range o.Capability {
		pbuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, pbuf...)
	}
	o.ParamLen = uint8(len(buf) - 2)
	buf[1] = o.ParamLen
	return buf, nil
}

func NewOptionParameterCapability(capability []ParameterCapabilityInterface) *OptionParameterCapability {
	return &OptionParameterCapability{
		ParamType:  BGP_OPT_CAPABILITY,
		Capability: capability,
	}
}

type OptionParameterUnknown struct {
	ParamType uint8
	ParamLen  uint8
	Value     []byte
}

func (o *OptionParameterUnknown) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	buf[0] = o.ParamType
	if o.ParamLen == 0 {
		o.ParamLen = uint8(len(o.Value))
	}
	buf[1] = o.ParamLen
	return append(buf, o.Value...), nil
}

type BGPOpen struct {
	Version     uint8
	MyAS        uint16
	HoldTime    uint16
	ID          net.IP
	OptParamLen uint8
	OptParams   []OptionParameterInterface
}

func (msg *BGPOpen) DecodeFromBytes(data []byte) error {
	msg.Version = data[0]
	msg.MyAS = binary.BigEndian.Uint16(data[1:3])
	msg.HoldTime = binary.BigEndian.Uint16(data[3:5])
	msg.ID = net.IP(data[5:9]).To4()
	msg.OptParamLen = data[9]
	data = data[10:]
	if len(data) < int(msg.OptParamLen) {
		return fmt.Errorf("Not all BGP Open message bytes available")
	}

	msg.OptParams = []OptionParameterInterface{}
	for rest := msg.OptParamLen; rest > 0; {
		paramtype := data[0]
		paramlen := data[1]
		if rest < paramlen+2 {
			return fmt.Errorf("Malformed BGP Open message")
		}
		rest -= paramlen + 2

		if paramtype == BGP_OPT_CAPABILITY {
			p := &OptionParameterCapability{}
			p.ParamType = paramtype
			p.ParamLen = paramlen
			p.DecodeFromBytes(data[2 : 2+paramlen])
			msg.OptParams = append(msg.OptParams, p)
		} else {
			p := &OptionParameterUnknown{}
			p.ParamType = paramtype
			p.ParamLen = paramlen
			p.Value = data[2 : 2+paramlen]
			msg.OptParams = append(msg.OptParams, p)
		}
		data = data[2+paramlen:]
	}
	return nil
}

func (msg *BGPOpen) Serialize() ([]byte, error) {
	buf := make([]byte, 10)
	buf[0] = msg.Version
	binary.BigEndian.PutUint16(buf[1:3], msg.MyAS)
	binary.BigEndian.PutUint16(buf[3:5], msg.HoldTime)
	copy(buf[5:9], msg.ID.To4())
	pbuf := make([]byte, 0)
	for _, p := range msg.OptParams {
		onepbuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		pbuf = append(pbuf, onepbuf...)
	}
	msg.OptParamLen = uint8(len(pbuf))
	buf[9] = msg.OptParamLen
	return append(buf, pbuf...), nil
}

func NewBGPOpenMessage(myas uint16, holdtime uint16, id string, optparams []OptionParameterInterface) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_OPEN},
		Body:   &BGPOpen{4, myas, holdtime, net.ParseIP(id).To4(), 0, optparams},
	}
}

type AddrPrefixInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	AFI() uint16
	SAFI() uint8
	Len() int
	String() string
	MarshalJSON() ([]byte, error)
}

type IPAddrPrefixDefault struct {
	Length uint8
	Prefix net.IP
}

func (r *IPAddrPrefixDefault) decodePrefix(data []byte, bitlen uint8, addrlen uint8) error {
	bytelen := (int(bitlen) + 7) / 8
	if len(data) < bytelen {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "network bytes is short")
	}
	b := make([]byte, addrlen)
	copy(b, data[:bytelen])
	r.Prefix = b
	return nil
}

func (r *IPAddrPrefixDefault) serializePrefix(bitlen uint8) ([]byte, error) {
	bytelen := (int(bitlen) + 7) / 8
	buf := make([]byte, bytelen)
	copy(buf, r.Prefix)
	// clear trailing bits in the last byte. rfc doesn't require
	// this though.
	if bitlen%8 != 0 {
		mask := 0xff00 >> (bitlen % 8)
		last_byte_value := buf[bytelen-1] & byte(mask)
		buf[bytelen-1] = last_byte_value
	}
	b := make([]byte, len(r.Prefix))
	copy(b, buf)
	copy(r.Prefix, b)
	return buf, nil
}

func (r *IPAddrPrefixDefault) Len() int {
	return 1 + ((int(r.Length) + 7) / 8)
}

func (r *IPAddrPrefixDefault) String() string {
	return fmt.Sprintf("%s/%d", r.Prefix.String(), r.Length)
}

func (r *IPAddrPrefixDefault) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Prefix string `json:"prefix"`
	}{
		Prefix: r.String(),
	})
}

type IPAddrPrefix struct {
	IPAddrPrefixDefault
	addrlen uint8
}

func (r *IPAddrPrefix) DecodeFromBytes(data []byte) error {
	if len(data) < 1 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "prefix misses length field")
	}
	r.Length = data[0]
	if r.addrlen == 0 {
		r.addrlen = 4
	}
	return r.decodePrefix(data[1:], r.Length, r.addrlen)
}

func (r *IPAddrPrefix) Serialize() ([]byte, error) {
	buf := make([]byte, 1)
	buf[0] = r.Length
	pbuf, err := r.serializePrefix(r.Length)
	if err != nil {
		return nil, err
	}
	return append(buf, pbuf...), nil
}

func (r *IPAddrPrefix) AFI() uint16 {
	return AFI_IP
}

func (r *IPAddrPrefix) SAFI() uint8 {
	return SAFI_UNICAST
}

func NewIPAddrPrefix(length uint8, prefix string) *IPAddrPrefix {
	return &IPAddrPrefix{
		IPAddrPrefixDefault{length, net.ParseIP(prefix).To4()},
		4,
	}
}

type IPv6AddrPrefix struct {
	IPAddrPrefix
}

func (r *IPv6AddrPrefix) AFI() uint16 {
	return AFI_IP6
}

func (r *IPv6AddrPrefix) String() string {
	isZero := func(p net.IP) bool {
		for i := 0; i < len(p); i++ {
			if p[i] != 0 {
				return false
			}
		}
		return true
	}(r.Prefix[0:10])
	if isZero && r.Prefix[10] == 0xff && r.Prefix[11] == 0xff {
		return fmt.Sprintf("::ffff:%s/%d", r.Prefix.String(), r.Length)
	}
	return fmt.Sprintf("%s/%d", r.Prefix.String(), r.Length)
}

func NewIPv6AddrPrefix(length uint8, prefix string) *IPv6AddrPrefix {
	return &IPv6AddrPrefix{
		IPAddrPrefix{
			IPAddrPrefixDefault{length, net.ParseIP(prefix)},
			16,
		},
	}
}

const (
	BGP_RD_TWO_OCTET_AS = iota
	BGP_RD_IPV4_ADDRESS
	BGP_RD_FOUR_OCTET_AS
)

type RouteDistinguisherInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	Len() int
	String() string
	MarshalJSON() ([]byte, error)
}

type DefaultRouteDistinguisher struct {
	Type  uint16
	Value []byte
}

func (rd *DefaultRouteDistinguisher) DecodeFromBytes(data []byte) error {
	rd.Type = binary.BigEndian.Uint16(data[0:2])
	rd.Value = data[2:8]
	return nil
}

func (rd *DefaultRouteDistinguisher) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint16(buf, rd.Type)
	copy(buf[2:], rd.Value)
	return buf, nil
}

func (rd *DefaultRouteDistinguisher) String() string {
	return fmt.Sprintf("%v", rd.Value)
}

func (rd *DefaultRouteDistinguisher) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  uint16 `json:"type"`
		Value []byte `json:"value"`
	}{
		Type:  rd.Type,
		Value: rd.Value,
	})
}

func (rd *DefaultRouteDistinguisher) Len() int { return 8 }

type RouteDistinguisherTwoOctetAS struct {
	DefaultRouteDistinguisher
	Admin    uint16
	Assigned uint32
}

func (rd *RouteDistinguisherTwoOctetAS) Serialize() ([]byte, error) {
	buf := make([]byte, 6)
	binary.BigEndian.PutUint16(buf[0:], rd.Admin)
	binary.BigEndian.PutUint32(buf[2:], rd.Assigned)
	rd.Value = buf
	return rd.DefaultRouteDistinguisher.Serialize()
}

func (rd *RouteDistinguisherTwoOctetAS) String() string {
	return fmt.Sprintf("%d:%d", rd.Admin, rd.Assigned)
}

func (rd *RouteDistinguisherTwoOctetAS) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     uint16 `json:"type"`
		Admin    uint16 `json:"admin"`
		Assigned uint32 `json:"assigned"`
	}{
		Type:     rd.Type,
		Admin:    rd.Admin,
		Assigned: rd.Assigned,
	})
}

func NewRouteDistinguisherTwoOctetAS(admin uint16, assigned uint32) *RouteDistinguisherTwoOctetAS {
	return &RouteDistinguisherTwoOctetAS{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: BGP_RD_TWO_OCTET_AS,
		},
		Admin:    admin,
		Assigned: assigned,
	}
}

type RouteDistinguisherIPAddressAS struct {
	DefaultRouteDistinguisher
	Admin    net.IP
	Assigned uint16
}

func (rd *RouteDistinguisherIPAddressAS) Serialize() ([]byte, error) {
	buf := make([]byte, 6)
	copy(buf[0:], rd.Admin.To4())
	binary.BigEndian.PutUint16(buf[4:], rd.Assigned)
	rd.Value = buf
	return rd.DefaultRouteDistinguisher.Serialize()
}

func (rd *RouteDistinguisherIPAddressAS) String() string {
	return fmt.Sprintf("%s:%d", rd.Admin.String(), rd.Assigned)
}

func (rd *RouteDistinguisherIPAddressAS) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     uint16 `json:"type"`
		Admin    string `json:"admin"`
		Assigned uint16 `json:"assigned"`
	}{
		Type:     rd.Type,
		Admin:    rd.Admin.String(),
		Assigned: rd.Assigned,
	})
}

func NewRouteDistinguisherIPAddressAS(admin string, assigned uint16) *RouteDistinguisherIPAddressAS {
	return &RouteDistinguisherIPAddressAS{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: BGP_RD_IPV4_ADDRESS,
		},
		Admin:    net.ParseIP(admin).To4(),
		Assigned: assigned,
	}
}

type RouteDistinguisherFourOctetAS struct {
	DefaultRouteDistinguisher
	Admin    uint32
	Assigned uint16
}

func (rd *RouteDistinguisherFourOctetAS) Serialize() ([]byte, error) {
	buf := make([]byte, 6)
	binary.BigEndian.PutUint32(buf[0:], rd.Admin)
	binary.BigEndian.PutUint16(buf[4:], rd.Assigned)
	rd.Value = buf
	return rd.DefaultRouteDistinguisher.Serialize()
}

func (rd *RouteDistinguisherFourOctetAS) String() string {
	fst := rd.Admin >> 16 & 0xffff
	snd := rd.Admin & 0xffff
	return fmt.Sprintf("%d.%d:%d", fst, snd, rd.Assigned)
}

func (rd *RouteDistinguisherFourOctetAS) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     uint16 `json:"type"`
		Admin    uint32 `json:"admin"`
		Assigned uint16 `json:"assigned"`
	}{
		Type:     rd.Type,
		Admin:    rd.Admin,
		Assigned: rd.Assigned,
	})
}

func NewRouteDistinguisherFourOctetAS(admin uint32, assigned uint16) *RouteDistinguisherFourOctetAS {
	return &RouteDistinguisherFourOctetAS{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: BGP_RD_FOUR_OCTET_AS,
		},
		Admin:    admin,
		Assigned: assigned,
	}
}

type RouteDistinguisherUnknown struct {
	DefaultRouteDistinguisher
}

func GetRouteDistinguisher(data []byte) RouteDistinguisherInterface {
	rdtype := binary.BigEndian.Uint16(data[0:2])
	switch rdtype {
	case BGP_RD_TWO_OCTET_AS:
		return NewRouteDistinguisherTwoOctetAS(binary.BigEndian.Uint16(data[2:4]), binary.BigEndian.Uint32(data[4:8]))
	case BGP_RD_IPV4_ADDRESS:
		return NewRouteDistinguisherIPAddressAS(net.IP(data[2:6]).String(), binary.BigEndian.Uint16(data[6:8]))
	case BGP_RD_FOUR_OCTET_AS:
		return NewRouteDistinguisherFourOctetAS(binary.BigEndian.Uint32(data[2:6]), binary.BigEndian.Uint16(data[6:8]))
	}
	rd := &RouteDistinguisherUnknown{}
	rd.Type = rdtype
	return rd
}

func parseRdAndRt(input string) ([]string, error) {
	exp := regexp.MustCompile("^((\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)|((\\d+)\\.)?(\\d+)):(\\d+)$")
	elems := exp.FindStringSubmatch(input)
	if len(elems) != 10 {
		return nil, fmt.Errorf("failed to parse")
	}
	return elems, nil
}

func ParseRouteDistinguisher(rd string) (RouteDistinguisherInterface, error) {
	elems, err := parseRdAndRt(rd)
	if err != nil {
		return nil, err
	}
	assigned, _ := strconv.Atoi(elems[9])
	ip := net.ParseIP(elems[1])
	switch {
	case ip.To4() != nil:
		return NewRouteDistinguisherIPAddressAS(elems[1], uint16(assigned)), nil
	case elems[6] == "" && elems[7] == "":
		asn, _ := strconv.Atoi(elems[8])
		return NewRouteDistinguisherTwoOctetAS(uint16(asn), uint32(assigned)), nil
	default:
		fst, _ := strconv.Atoi(elems[7])
		snd, _ := strconv.Atoi(elems[8])
		asn := fst<<16 | snd
		return NewRouteDistinguisherFourOctetAS(uint32(asn), uint16(assigned)), nil
	}
}

//
// RFC3107 Carrying Label Information in BGP-4
//
// 3. Carrying Label Mapping Information
//
// b) Label:
//
// The Label field carries one or more labels (that corresponds to
// the stack of labels [MPLS-ENCAPS(RFC3032)]). Each label is encoded as
// 4 octets, where the high-order 20 bits contain the label value, and
// the low order bit contains "Bottom of Stack"
//
// RFC3032 MPLS Label Stack Encoding
//
// 2.1. Encoding the Label Stack
//
//  0       1       2               3
//  0 ... 9 0 ... 9 0 1 2 3 4 ... 9 0 1
// +-----+-+-+---+-+-+-+-+-+-----+-+-+-+
// |     Label     | Exp |S|    TTL    |
// +-----+-+-+---+-+-+-+-+-+-----+-+-+-+
//

// RFC3107 Carrying Label Information in BGP-4
//
// 3. Carrying Label Mapping Information
//
// The label information carried (as part of NLRI) in the Withdrawn
// Routes field should be set to 0x800000.
const WITHDRAW_LABEL = uint32(0x800000)

type MPLSLabelStack struct {
	Labels []uint32
}

func (l *MPLSLabelStack) DecodeFromBytes(data []byte) error {
	labels := []uint32{}
	foundBottom := false
	for len(data) >= 3 {
		label := uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
		if label == WITHDRAW_LABEL {
			l.Labels = []uint32{label}
			return nil
		}
		data = data[3:]
		labels = append(labels, label>>4)
		if label&1 == 1 {
			foundBottom = true
			break
		}
	}
	if foundBottom == false {
		l.Labels = []uint32{}
		return nil
	}
	l.Labels = labels
	return nil
}

func (l *MPLSLabelStack) Serialize() ([]byte, error) {
	buf := make([]byte, len(l.Labels)*3)
	for i, label := range l.Labels {
		if label == WITHDRAW_LABEL {
			return []byte{128, 0, 0}, nil
		}
		label = label << 4
		buf[i*3] = byte((label >> 16) & 0xff)
		buf[i*3+1] = byte((label >> 8) & 0xff)
		buf[i*3+2] = byte(label & 0xff)
	}
	buf[len(buf)-1] |= 1
	return buf, nil
}

func (l *MPLSLabelStack) Len() int { return 3 * len(l.Labels) }

func (l *MPLSLabelStack) String() string {
	if len(l.Labels) == 0 {
		return ""
	}
	s := bytes.NewBuffer(make([]byte, 0, 64))
	s.WriteString("[")
	ss := make([]string, 0, len(l.Labels))
	for _, label := range l.Labels {
		ss = append(ss, fmt.Sprintf("%d", label))
	}
	s.WriteString(strings.Join(ss, ", "))
	s.WriteString("]")
	return s.String()
}

func NewMPLSLabelStack(labels ...uint32) *MPLSLabelStack {
	if len(labels) == 0 {
		labels = []uint32{0}
	}
	return &MPLSLabelStack{labels}
}

func ParseMPLSLabelStack(buf string) (*MPLSLabelStack, error) {
	elems := strings.Split(buf, "/")
	labels := make([]uint32, 0, len(elems))
	if len(elems) == 0 {
		goto ERR
	}
	for _, elem := range elems {
		i, err := strconv.Atoi(elem)
		if err != nil {
			goto ERR
		}
		if i < 0 || i > ((1<<20)-1) {
			goto ERR
		}
		labels = append(labels, uint32(i))
	}
	return NewMPLSLabelStack(labels...), nil
ERR:
	return nil, fmt.Errorf("invalid mpls label stack format")
}

//
// RFC3107 Carrying Label Information in BGP-4
//
// 3. Carrying Label Mapping Information
//
// +----------------------+
// |   Length (1 octet)   |
// +----------------------+
// |   Label (3 octets)   |
// +----------------------+
// .......................
// +----------------------+
// |   Prefix (variable)  |
// +----------------------+
//
// RFC4364 BGP/MPLS IP VPNs
//
// 4.3.4. How VPN-IPv4 NLRI Is Carried in BGP
//
// The labeled VPN-IPv4 NLRI itself is encoded as specified in
// [MPLS-BGP(RFC3107)], where the prefix consists of an 8-byte RD
// followed by an IPv4 prefix.
//

type LabeledVPNIPAddrPrefix struct {
	IPAddrPrefixDefault
	Labels  MPLSLabelStack
	RD      RouteDistinguisherInterface
	addrlen uint8
}

func (l *LabeledVPNIPAddrPrefix) DecodeFromBytes(data []byte) error {
	l.Length = uint8(data[0])
	data = data[1:]
	l.Labels.DecodeFromBytes(data)
	if int(l.Length)-8*(l.Labels.Len()) < 0 {
		l.Labels.Labels = []uint32{}
	}
	data = data[l.Labels.Len():]
	l.RD = GetRouteDistinguisher(data)
	data = data[l.RD.Len():]
	restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len())
	l.decodePrefix(data, uint8(restbits), l.addrlen)
	return nil
}

func (l *LabeledVPNIPAddrPrefix) Serialize() ([]byte, error) {
	buf := make([]byte, 1)
	buf[0] = l.Length
	lbuf, err := l.Labels.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, lbuf...)
	rbuf, err := l.RD.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, rbuf...)
	restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len())
	pbuf, err := l.serializePrefix(uint8(restbits))
	if err != nil {
		return nil, err
	}
	buf = append(buf, pbuf...)
	return buf, nil
}

func (l *LabeledVPNIPAddrPrefix) AFI() uint16 {
	return AFI_IP
}

func (l *LabeledVPNIPAddrPrefix) SAFI() uint8 {
	return SAFI_MPLS_VPN
}

func (l *LabeledVPNIPAddrPrefix) String() string {
	masklen := l.IPAddrPrefixDefault.Length - uint8(8*(l.Labels.Len()+l.RD.Len()))
	return fmt.Sprintf("%s:%s/%d", l.RD, l.IPAddrPrefixDefault.Prefix, masklen)
}

func (l *LabeledVPNIPAddrPrefix) MarshalJSON() ([]byte, error) {
	masklen := l.IPAddrPrefixDefault.Length - uint8(8*(l.Labels.Len()+l.RD.Len()))
	return json.Marshal(struct {
		Prefix string                      `json:"prefix"`
		Labels []uint32                    `json:"labels"`
		RD     RouteDistinguisherInterface `json:"rd"`
	}{
		Prefix: fmt.Sprintf("%s/%d", l.IPAddrPrefixDefault.Prefix, masklen),
		Labels: l.Labels.Labels,
		RD:     l.RD,
	})
}

func NewLabeledVPNIPAddrPrefix(length uint8, prefix string, label MPLSLabelStack, rd RouteDistinguisherInterface) *LabeledVPNIPAddrPrefix {
	rdlen := 0
	if rd != nil {
		rdlen = rd.Len()
	}
	return &LabeledVPNIPAddrPrefix{
		IPAddrPrefixDefault{length + uint8(8*(label.Len()+rdlen)), net.ParseIP(prefix).To4()},
		label,
		rd,
		4,
	}
}

type LabeledVPNIPv6AddrPrefix struct {
	LabeledVPNIPAddrPrefix
}

func (l *LabeledVPNIPv6AddrPrefix) AFI() uint16 {
	return AFI_IP6
}

func NewLabeledVPNIPv6AddrPrefix(length uint8, prefix string, label MPLSLabelStack, rd RouteDistinguisherInterface) *LabeledVPNIPv6AddrPrefix {
	rdlen := 0
	if rd != nil {
		rdlen = rd.Len()
	}
	return &LabeledVPNIPv6AddrPrefix{
		LabeledVPNIPAddrPrefix{
			IPAddrPrefixDefault{length + uint8(8*(label.Len()+rdlen)), net.ParseIP(prefix)},
			label,
			rd,
			16,
		},
	}
}

type LabeledIPAddrPrefix struct {
	IPAddrPrefixDefault
	Labels  MPLSLabelStack
	addrlen uint8
}

func (r *LabeledIPAddrPrefix) AFI() uint16 {
	return AFI_IP
}

func (r *LabeledIPAddrPrefix) SAFI() uint8 {
	return SAFI_MPLS_LABEL
}

func (l *LabeledIPAddrPrefix) DecodeFromBytes(data []byte) error {
	l.Length = uint8(data[0])
	data = data[1:]
	l.Labels.DecodeFromBytes(data)
	if int(l.Length)-8*(l.Labels.Len()) < 0 {
		l.Labels.Labels = []uint32{}
	}
	restbits := int(l.Length) - 8*(l.Labels.Len())
	data = data[l.Labels.Len():]
	l.decodePrefix(data, uint8(restbits), l.addrlen)
	return nil
}

func (l *LabeledIPAddrPrefix) Serialize() ([]byte, error) {
	buf := make([]byte, 1)
	buf[0] = l.Length
	restbits := int(l.Length) - 8*(l.Labels.Len())
	lbuf, err := l.Labels.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, lbuf...)
	pbuf, err := l.serializePrefix(uint8(restbits))
	if err != nil {
		return nil, err
	}
	buf = append(buf, pbuf...)
	return buf, nil
}

func (l *LabeledIPAddrPrefix) String() string {
	return fmt.Sprintf("%s/%d", l.Prefix.String(), int(l.Length)-l.Labels.Len()*8)
}

func NewLabeledIPAddrPrefix(length uint8, prefix string, label MPLSLabelStack) *LabeledIPAddrPrefix {
	return &LabeledIPAddrPrefix{
		IPAddrPrefixDefault{length + uint8(label.Len()*8), net.ParseIP(prefix).To4()},
		label,
		4,
	}
}

type LabeledIPv6AddrPrefix struct {
	LabeledIPAddrPrefix
}

func (l *LabeledIPv6AddrPrefix) AFI() uint16 {
	return AFI_IP6
}

func NewLabeledIPv6AddrPrefix(length uint8, prefix string, label MPLSLabelStack) *LabeledIPv6AddrPrefix {
	return &LabeledIPv6AddrPrefix{
		LabeledIPAddrPrefix{
			IPAddrPrefixDefault{length + uint8(label.Len()*8), net.ParseIP(prefix)},
			label,
			16,
		},
	}
}

type RouteTargetMembershipNLRI struct {
	Length      uint8
	AS          uint32
	RouteTarget ExtendedCommunityInterface
}

func (n *RouteTargetMembershipNLRI) DecodeFromBytes(data []byte) error {
	n.Length = data[0]
	data = data[1:]
	if len(data) == 0 {
		return nil
	} else if len(data) != 12 {
		return fmt.Errorf("Not all RouteTargetMembershipNLRI bytes available")
	}
	n.AS = binary.BigEndian.Uint32(data[0:4])
	rt, err := ParseExtended(data[4:])
	n.RouteTarget = rt
	if err != nil {
		return err
	}
	return nil
}

func (n *RouteTargetMembershipNLRI) Serialize() ([]byte, error) {
	if n.RouteTarget == nil {
		return []byte{0}, nil
	}
	buf := make([]byte, 5)
	buf[0] = 12 * 8
	binary.BigEndian.PutUint32(buf[1:], n.AS)
	ebuf, err := n.RouteTarget.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, ebuf...)
	return buf, nil
}

func (n *RouteTargetMembershipNLRI) AFI() uint16 {
	return AFI_IP
}

func (n *RouteTargetMembershipNLRI) SAFI() uint8 {
	return SAFI_ROUTE_TARGET_CONSTRTAINS
}

func (n *RouteTargetMembershipNLRI) Len() int {
	if n.AS == 0 && n.RouteTarget == nil {
		return 1
	}
	return 13
}

func (n *RouteTargetMembershipNLRI) String() string {
	target := "default"
	if n.RouteTarget != nil {
		target = n.RouteTarget.String()
	}
	return fmt.Sprintf("%d:%s", n.AS, target)
}

func (n *RouteTargetMembershipNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Prefix string `json:"prefix"`
	}{
		Prefix: n.String(),
	})
}

func NewRouteTargetMembershipNLRI(as uint32, target ExtendedCommunityInterface) *RouteTargetMembershipNLRI {
	l := 12 * 8
	if as == 0 && target == nil {
		l = 1
	}
	return &RouteTargetMembershipNLRI{
		Length:      uint8(l),
		AS:          as,
		RouteTarget: target,
	}
}

type ESIType uint8

const (
	ESI_ARBITRARY ESIType = iota
	ESI_LACP
	ESI_MSTP
	ESI_MAC
	ESI_ROUTERID
	ESI_AS
)

type EthernetSegmentIdentifier struct {
	Type  ESIType
	Value []byte
}

func (esi *EthernetSegmentIdentifier) DecodeFromBytes(data []byte) error {
	esi.Type = ESIType(data[0])
	esi.Value = data[1:10]
	switch esi.Type {
	case ESI_LACP, ESI_MSTP, ESI_ROUTERID, ESI_AS:
		if esi.Value[8] != 0x00 {
			return fmt.Errorf("invalid %s. last octet must be 0x00 (0x%02x)", esi.Type.String(), esi.Value[8])
		}
	}
	return nil
}

func (esi *EthernetSegmentIdentifier) Serialize() ([]byte, error) {
	buf := make([]byte, 10)
	buf[0] = uint8(esi.Type)
	copy(buf[1:], esi.Value)
	return buf, nil
}

func isZeroBuf(buf []byte) bool {
	for _, b := range buf {
		if b != 0 {
			return false
		}
	}
	return true
}

func (esi *EthernetSegmentIdentifier) String() string {
	s := bytes.NewBuffer(make([]byte, 0, 64))
	s.WriteString(fmt.Sprintf("%s | ", esi.Type.String()))
	switch esi.Type {
	case ESI_ARBITRARY:
		if isZeroBuf(esi.Value) {
			return "single-homed"
		}
		s.WriteString(fmt.Sprintf("%s", esi.Value))
	case ESI_LACP:
		s.WriteString(fmt.Sprintf("system mac %s, ", net.HardwareAddr(esi.Value[:6]).String()))
		s.WriteString(fmt.Sprintf("port key %d", binary.BigEndian.Uint16(esi.Value[6:8])))
	case ESI_MSTP:
		s.WriteString(fmt.Sprintf("bridge mac %s, ", net.HardwareAddr(esi.Value[:6]).String()))
		s.WriteString(fmt.Sprintf("priority %d", binary.BigEndian.Uint16(esi.Value[6:8])))
	case ESI_MAC:
		s.WriteString(fmt.Sprintf("system mac %s, ", net.HardwareAddr(esi.Value[:6]).String()))
		s.WriteString(fmt.Sprintf("local discriminator %d", uint32(esi.Value[6])<<16|uint32(esi.Value[7])<<8|uint32(esi.Value[8])))
	case ESI_ROUTERID:
		s.WriteString(fmt.Sprintf("router id %s, ", net.IP(esi.Value[:4])))
		s.WriteString(fmt.Sprintf("local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8])))
	case ESI_AS:
		s.WriteString(fmt.Sprintf("as %d:%d, ", binary.BigEndian.Uint16(esi.Value[:2]), binary.BigEndian.Uint16(esi.Value[2:4])))
		s.WriteString(fmt.Sprintf("local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8])))
	default:
		s.WriteString(fmt.Sprintf("value %s", esi.Value))
	}
	return s.String()
}

//
// I-D bess-evpn-overlay-01
//
// 5.1.3 Constructing EVPN BGP Routes
//
// For the balance of this memo, the MPLS label field will be
// referred to as the VNI/VSID field. The VNI/VSID field is used for
// both local and global VNIs/VSIDs, and for either case the entire 24-
// bit field is used to encode the VNI/VSID value.
//
// We can't use type MPLSLabelStack for EVPN NLRI, because EVPN NLRI's MPLS
// field can be filled with VXLAN VNI. In that case, we must avoid modifying
// bottom of stack bit.
//

func labelDecode(data []byte) uint32 {
	return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
}

func labelSerialize(label uint32, buf []byte) {
	buf[0] = byte((label >> 16) & 0xff)
	buf[1] = byte((label >> 8) & 0xff)
	buf[2] = byte(label & 0xff)
}

type EVPNEthernetAutoDiscoveryRoute struct {
	RD    RouteDistinguisherInterface
	ESI   EthernetSegmentIdentifier
	ETag  uint32
	Label uint32
}

func (er *EVPNEthernetAutoDiscoveryRoute) DecodeFromBytes(data []byte) error {
	er.RD = GetRouteDistinguisher(data)
	data = data[er.RD.Len():]
	err := er.ESI.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	data = data[10:]
	er.ETag = binary.BigEndian.Uint32(data[0:4])
	data = data[4:]
	er.Label = labelDecode(data)
	return nil
}

func (er *EVPNEthernetAutoDiscoveryRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}
	tbuf, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)

	tbuf = make([]byte, 4)
	binary.BigEndian.PutUint32(tbuf, er.ETag)
	buf = append(buf, tbuf...)

	tbuf = make([]byte, 3)
	labelSerialize(er.Label, tbuf)
	buf = append(buf, tbuf...)

	return buf, nil
}

func (er *EVPNEthernetAutoDiscoveryRoute) String() string {
	return fmt.Sprintf("[type:A-D][rd:%s][esi:%s][etag:%d][label:%d]", er.RD, er.ESI.String(), er.ETag, er.Label)
}

func (er *EVPNEthernetAutoDiscoveryRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD    RouteDistinguisherInterface `json:"rd"`
		ESI   string                      `json:"esi"`
		Etag  uint32                      `json:"etag"`
		Label uint32                      `json:"label"`
	}{
		RD:    er.RD,
		ESI:   er.ESI.String(),
		Etag:  er.ETag,
		Label: er.Label,
	})
}

func (er *EVPNEthernetAutoDiscoveryRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

type EVPNMacIPAdvertisementRoute struct {
	RD               RouteDistinguisherInterface
	ESI              EthernetSegmentIdentifier
	ETag             uint32
	MacAddressLength uint8
	MacAddress       net.HardwareAddr
	IPAddressLength  uint8
	IPAddress        net.IP
	Labels           []uint32
}

func (er *EVPNMacIPAdvertisementRoute) DecodeFromBytes(data []byte) error {
	er.RD = GetRouteDistinguisher(data)
	data = data[er.RD.Len():]
	err := er.ESI.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	data = data[10:]
	er.ETag = binary.BigEndian.Uint32(data[0:4])
	data = data[4:]
	er.MacAddressLength = data[0]
	er.MacAddress = net.HardwareAddr(data[1:7])
	er.IPAddressLength = data[7]
	data = data[8:]
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		er.IPAddress = net.IP(data[0:((er.IPAddressLength) / 8)])
	} else if er.IPAddressLength != 0 {
		return fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
	}
	data = data[(er.IPAddressLength / 8):]
	label1 := labelDecode(data)
	er.Labels = append(er.Labels, label1)
	data = data[3:]
	if len(data) == 3 {
		label2 := labelDecode(data)
		er.Labels = append(er.Labels, label2)

	}
	return nil
}

func (er *EVPNMacIPAdvertisementRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}

	tbuf, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}

	buf = append(buf, tbuf...)
	tbuf = make([]byte, 4)
	binary.BigEndian.PutUint32(tbuf, er.ETag)
	buf = append(buf, tbuf...)
	tbuf = make([]byte, 7)
	tbuf[0] = er.MacAddressLength
	copy(tbuf[1:], er.MacAddress)
	buf = append(buf, tbuf...)

	if er.IPAddressLength == 0 {
		buf = append(buf, 0)
	} else if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		buf = append(buf, er.IPAddressLength)
		if er.IPAddressLength == 32 {
			er.IPAddress = er.IPAddress.To4()
		}
		buf = append(buf, []byte(er.IPAddress)...)
	} else {
		return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
	}

	for _, l := range er.Labels {
		tbuf = make([]byte, 3)
		labelSerialize(l, tbuf)
		buf = append(buf, tbuf...)
	}
	return buf, nil
}

func (er *EVPNMacIPAdvertisementRoute) String() string {
	return fmt.Sprintf("[type:macadv][rd:%s][esi:%s][etag:%d][mac:%s][ip:%s][labels:%v]", er.RD, er.ESI.String(), er.ETag, er.MacAddress, er.IPAddress, er.Labels)
}

func (er *EVPNMacIPAdvertisementRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD         RouteDistinguisherInterface `json:"rd"`
		ESI        string                      `json:"esi"`
		Etag       uint32                      `json:"etag"`
		MacAddress string                      `json:"mac"`
		IPAddress  string                      `json:"ip"`
		Labels     []uint32                    `json:"labels"`
	}{
		RD:         er.RD,
		ESI:        er.ESI.String(),
		Etag:       er.ETag,
		MacAddress: er.MacAddress.String(),
		IPAddress:  er.IPAddress.String(),
		Labels:     er.Labels,
	})
}

func (er *EVPNMacIPAdvertisementRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

type EVPNMulticastEthernetTagRoute struct {
	RD              RouteDistinguisherInterface
	ETag            uint32
	IPAddressLength uint8
	IPAddress       net.IP
}

func (er *EVPNMulticastEthernetTagRoute) DecodeFromBytes(data []byte) error {
	er.RD = GetRouteDistinguisher(data)
	data = data[er.RD.Len():]
	er.ETag = binary.BigEndian.Uint32(data[0:4])
	er.IPAddressLength = data[4]
	data = data[5:]
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		er.IPAddress = net.IP(data[:er.IPAddressLength/8])
	} else {
		return fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
	}
	return nil
}

func (er *EVPNMulticastEthernetTagRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}
	tbuf := make([]byte, 4)
	binary.BigEndian.PutUint32(tbuf, er.ETag)
	buf = append(buf, tbuf...)
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		buf = append(buf, er.IPAddressLength)
		if er.IPAddressLength == 32 {
			er.IPAddress = er.IPAddress.To4()
		}
		buf = append(buf, []byte(er.IPAddress)...)
	} else {
		return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
	}
	if err != nil {
		return nil, err
	}
	return buf, nil
}

func (er *EVPNMulticastEthernetTagRoute) String() string {
	return fmt.Sprintf("[type:multicast][rd:%s][etag:%d][ip:%s]", er.RD, er.ETag, er.IPAddress)
}

func (er *EVPNMulticastEthernetTagRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD        RouteDistinguisherInterface `json:"rd"`
		Etag      uint32                      `json:"etag"`
		IPAddress string                      `json:"ip"`
	}{
		RD:        er.RD,
		Etag:      er.ETag,
		IPAddress: er.IPAddress.String(),
	})
}

func (er *EVPNMulticastEthernetTagRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

type EVPNEthernetSegmentRoute struct {
	RD              RouteDistinguisherInterface
	ESI             EthernetSegmentIdentifier
	IPAddressLength uint8
	IPAddress       net.IP
}

func (er *EVPNEthernetSegmentRoute) DecodeFromBytes(data []byte) error {
	er.RD = GetRouteDistinguisher(data)
	data = data[er.RD.Len():]
	er.ESI.DecodeFromBytes(data)
	data = data[10:]
	er.IPAddressLength = data[0]
	data = data[1:]
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		er.IPAddress = net.IP(data[:er.IPAddressLength/8])
	} else {
		return fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
	}
	return nil
}

func (er *EVPNEthernetSegmentRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}
	tbuf, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)
	buf = append(buf, er.IPAddressLength)
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		if er.IPAddressLength == 32 {
			er.IPAddress = er.IPAddress.To4()
		}
		buf = append(buf, []byte(er.IPAddress)...)
	} else {
		return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
	}
	return buf, nil
}

type EVPNRouteTypeInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	String() string
	rd() RouteDistinguisherInterface
	MarshalJSON() ([]byte, error)
}

func (er *EVPNEthernetSegmentRoute) String() string {
	return fmt.Sprintf("[type:esi][rd:%s][esi:%d][ip:%s]", er.RD, er.ESI, er.IPAddress)
}

func (er *EVPNEthernetSegmentRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD        RouteDistinguisherInterface `json:"rd"`
		ESI       string                      `json:"esi"`
		IPAddress string                      `json:"ip"`
	}{
		RD:        er.RD,
		ESI:       er.ESI.String(),
		IPAddress: er.IPAddress.String(),
	})
}

func (er *EVPNEthernetSegmentRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func getEVPNRouteType(t uint8) (EVPNRouteTypeInterface, error) {
	switch t {
	case EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY:
		return &EVPNEthernetAutoDiscoveryRoute{}, nil
	case EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
		return &EVPNMacIPAdvertisementRoute{}, nil
	case EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
		return &EVPNMulticastEthernetTagRoute{}, nil
	case EVPN_ETHERNET_SEGMENT_ROUTE:
		return &EVPNEthernetSegmentRoute{}, nil
	}
	return nil, fmt.Errorf("Unknown EVPN Route type: %d", t)
}

const (
	EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY = 1
	EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT    = 2
	EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG   = 3
	EVPN_ETHERNET_SEGMENT_ROUTE             = 4
)

type EVPNNLRI struct {
	RouteType     uint8
	Length        uint8
	RouteTypeData EVPNRouteTypeInterface
}

func (n *EVPNNLRI) DecodeFromBytes(data []byte) error {
	if len(data) < 2 {
		return fmt.Errorf("Not all EVPNNLRI bytes available")
	}
	n.RouteType = data[0]
	n.Length = data[1]
	data = data[2:]
	if len(data) < int(n.Length) {
		return fmt.Errorf("Not all EVPNNLRI Route type bytes available")
	}
	r, err := getEVPNRouteType(n.RouteType)
	if err != nil {
		return err
	}
	n.RouteTypeData = r
	return n.RouteTypeData.DecodeFromBytes(data[:n.Length])
}

func (n *EVPNNLRI) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	buf[0] = n.RouteType
	tbuf, err := n.RouteTypeData.Serialize()
	n.Length = uint8(len(tbuf))
	buf[1] = n.Length
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)
	return buf, nil
}

func (n *EVPNNLRI) AFI() uint16 {
	return AFI_L2VPN
}

func (n *EVPNNLRI) SAFI() uint8 {
	return SAFI_EVPN
}

func (n *EVPNNLRI) Len() int {
	return int(n.Length) + 2
}

func (n *EVPNNLRI) String() string {
	if n.RouteTypeData != nil {
		return n.RouteTypeData.String()
	}
	return fmt.Sprintf("%d:%d", n.RouteType, n.Length)
}

func (n *EVPNNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  uint8                  `json:"type"`
		Value EVPNRouteTypeInterface `json:"value"`
	}{
		Type:  n.RouteType,
		Value: n.RouteTypeData,
	})
}

func (n *EVPNNLRI) RD() RouteDistinguisherInterface {
	return n.RouteTypeData.rd()
}

func NewEVPNNLRI(routetype uint8, length uint8, routetypedata EVPNRouteTypeInterface) *EVPNNLRI {
	return &EVPNNLRI{
		routetype,
		length,
		routetypedata,
	}
}

type EncapNLRI struct {
	IPAddrPrefixDefault
	addrlen uint8
}

func (n *EncapNLRI) DecodeFromBytes(data []byte) error {
	if len(data) < 4 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "prefix misses length field")
	}
	n.Length = data[0]
	if n.addrlen == 0 {
		n.addrlen = 4
	}
	return n.decodePrefix(data[1:], n.Length, n.addrlen)
}

func (n *EncapNLRI) Serialize() ([]byte, error) {
	buf := make([]byte, 1)
	buf[0] = n.Length
	pbuf, err := n.serializePrefix(n.Length)
	if err != nil {
		return nil, err
	}
	return append(buf, pbuf...), nil
}

func (n *EncapNLRI) String() string {
	return n.Prefix.String()
}

func (n *EncapNLRI) AFI() uint16 {
	return AFI_IP
}

func (n *EncapNLRI) SAFI() uint8 {
	return SAFI_ENCAPSULATION
}

func NewEncapNLRI(endpoint string) *EncapNLRI {
	return &EncapNLRI{
		IPAddrPrefixDefault{32, net.ParseIP(endpoint).To4()},
		4,
	}
}

type Encapv6NLRI struct {
	EncapNLRI
}

func (n *Encapv6NLRI) AFI() uint16 {
	return AFI_IP6
}

func NewEncapv6NLRI(endpoint string) *Encapv6NLRI {
	return &Encapv6NLRI{
		EncapNLRI{
			IPAddrPrefixDefault{128, net.ParseIP(endpoint)},
			16,
		},
	}
}

type BGPFlowSpecType uint8

const (
	FLOW_SPEC_TYPE_UNKNOWN BGPFlowSpecType = iota
	FLOW_SPEC_TYPE_DST_PREFIX
	FLOW_SPEC_TYPE_SRC_PREFIX
	FLOW_SPEC_TYPE_IP_PROTO
	FLOW_SPEC_TYPE_PORT
	FLOW_SPEC_TYPE_DST_PORT
	FLOW_SPEC_TYPE_SRC_PORT
	FLOW_SPEC_TYPE_ICMP_TYPE
	FLOW_SPEC_TYPE_ICMP_CODE
	FLOW_SPEC_TYPE_TCP_FLAG
	FLOW_SPEC_TYPE_PKT_LEN
	FLOW_SPEC_TYPE_DSCP
	FLOW_SPEC_TYPE_FRAGMENT
	FLOW_SPEC_TYPE_LABEL
	FLOW_SPEC_TYPE_ETHERNET_TYPE // 14
	FLOW_SPEC_TYPE_SRC_MAC
	FLOW_SPEC_TYPE_DST_MAC
	FLOW_SPEC_TYPE_LLC_DSAP
	FLOW_SPEC_TYPE_LLC_SSAP
	FLOW_SPEC_TYPE_LLC_CONTROL
	FLOW_SPEC_TYPE_SNAP
	FLOW_SPEC_TYPE_VID
	FLOW_SPEC_TYPE_COS
	FLOW_SPEC_TYPE_INNER_VID
	FLOW_SPEC_TYPE_INNER_COS
)

var FlowSpecNameMap = map[BGPFlowSpecType]string{
	FLOW_SPEC_TYPE_UNKNOWN:       "unknown",
	FLOW_SPEC_TYPE_DST_PREFIX:    "destination",
	FLOW_SPEC_TYPE_SRC_PREFIX:    "source",
	FLOW_SPEC_TYPE_IP_PROTO:      "protocol",
	FLOW_SPEC_TYPE_PORT:          "port",
	FLOW_SPEC_TYPE_DST_PORT:      "destination-port",
	FLOW_SPEC_TYPE_SRC_PORT:      "source-port",
	FLOW_SPEC_TYPE_ICMP_TYPE:     "icmp-type",
	FLOW_SPEC_TYPE_ICMP_CODE:     "icmp-code",
	FLOW_SPEC_TYPE_TCP_FLAG:      "tcp-flags",
	FLOW_SPEC_TYPE_PKT_LEN:       "packet-length",
	FLOW_SPEC_TYPE_DSCP:          "dscp",
	FLOW_SPEC_TYPE_FRAGMENT:      "fragment",
	FLOW_SPEC_TYPE_LABEL:         "label",
	FLOW_SPEC_TYPE_ETHERNET_TYPE: "ether-type",
	FLOW_SPEC_TYPE_SRC_MAC:       "source-mac",
	FLOW_SPEC_TYPE_DST_MAC:       "destination-mac",
	FLOW_SPEC_TYPE_LLC_DSAP:      "llc-dsap",
	FLOW_SPEC_TYPE_LLC_SSAP:      "llc-ssap",
	FLOW_SPEC_TYPE_LLC_CONTROL:   "llc-control",
	FLOW_SPEC_TYPE_SNAP:          "snap",
	FLOW_SPEC_TYPE_VID:           "vid",
	FLOW_SPEC_TYPE_COS:           "cos",
	FLOW_SPEC_TYPE_INNER_VID:     "inner-vid",
	FLOW_SPEC_TYPE_INNER_COS:     "inner-cos",
}

var FlowSpecValueMap = map[string]BGPFlowSpecType{
	FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]:    FLOW_SPEC_TYPE_DST_PREFIX,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]:    FLOW_SPEC_TYPE_SRC_PREFIX,
	FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO]:      FLOW_SPEC_TYPE_IP_PROTO,
	FlowSpecNameMap[FLOW_SPEC_TYPE_PORT]:          FLOW_SPEC_TYPE_PORT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PORT]:      FLOW_SPEC_TYPE_DST_PORT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PORT]:      FLOW_SPEC_TYPE_SRC_PORT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_TYPE]:     FLOW_SPEC_TYPE_ICMP_TYPE,
	FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_CODE]:     FLOW_SPEC_TYPE_ICMP_CODE,
	FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG]:      FLOW_SPEC_TYPE_TCP_FLAG,
	FlowSpecNameMap[FLOW_SPEC_TYPE_PKT_LEN]:       FLOW_SPEC_TYPE_PKT_LEN,
	FlowSpecNameMap[FLOW_SPEC_TYPE_DSCP]:          FLOW_SPEC_TYPE_DSCP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_FRAGMENT]:      FLOW_SPEC_TYPE_FRAGMENT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LABEL]:         FLOW_SPEC_TYPE_LABEL,
	FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE]: FLOW_SPEC_TYPE_ETHERNET_TYPE,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]:       FLOW_SPEC_TYPE_SRC_MAC,
	FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]:       FLOW_SPEC_TYPE_DST_MAC,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_DSAP]:      FLOW_SPEC_TYPE_LLC_DSAP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_SSAP]:      FLOW_SPEC_TYPE_LLC_SSAP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_CONTROL]:   FLOW_SPEC_TYPE_LLC_CONTROL,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SNAP]:          FLOW_SPEC_TYPE_SNAP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_VID]:           FLOW_SPEC_TYPE_VID,
	FlowSpecNameMap[FLOW_SPEC_TYPE_COS]:           FLOW_SPEC_TYPE_COS,
	FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_VID]:     FLOW_SPEC_TYPE_INNER_VID,
	FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_COS]:     FLOW_SPEC_TYPE_INNER_COS,
}

func flowSpecPrefixParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	if len(args) < 2 {
		return nil, fmt.Errorf("invalid flowspec dst/src prefix")
	}
	typ := args[0]
	ip, net, err := net.ParseCIDR(args[1])
	if err != nil {
		return nil, fmt.Errorf("invalid ip prefix")
	}
	afi, _ := RouteFamilyToAfiSafi(rf)
	if afi == AFI_IP && ip.To4() == nil {
		return nil, fmt.Errorf("invalid ipv4 prefix")
	} else if afi == AFI_IP6 && !strings.Contains(ip.String(), ":") {
		return nil, fmt.Errorf("invalid ipv6 prefix")
	}
	ones, _ := net.Mask.Size()
	var offset uint8
	if len(args) > 2 {
		o, err := strconv.Atoi(args[2])
		offset = uint8(o)
		if err != nil {
			return nil, err
		}
	}

	switch typ {
	case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]:
		switch rf {
		case RF_FS_IPv4_UC:
			return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil
		case RF_FS_IPv6_UC:
			return NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil
		default:
			return nil, fmt.Errorf("invalid type. only RF_FS_IPv4_UC or RF_FS_IPv6_UC is allowed")
		}
	case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]:
		switch rf {
		case RF_FS_IPv4_UC:
			return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil
		case RF_FS_IPv6_UC:
			return NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil
		default:
			return nil, fmt.Errorf("invalid type. only RF_FS_IPv4_UC or RF_FS_IPv6_UC is allowed")
		}
	}
	return nil, fmt.Errorf("invalid type. only destination or source is allowed")
}

func flowSpecIpProtoParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	ss := make([]string, 0, len(ProtocolNameMap))
	for _, v := range ProtocolNameMap {
		ss = append(ss, v)
	}
	protos := strings.Join(ss, "|")
	exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO], protos, protos))
	elems := exp.FindStringSubmatch(strings.Join(args, " "))
	items := make([]*FlowSpecComponentItem, 0)
	eq := 0x1
	if elems[1] != "" {
		for _, v := range strings.Split(elems[1], " ") {
			p, ok := ProtocolValueMap[v]
			if !ok {
				continue
			}
			items = append(items, NewFlowSpecComponentItem(eq, int(p)))
		}
	}
	items = append(items, NewFlowSpecComponentItem(eq, int(ProtocolValueMap[elems[4]])))
	return NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, items), nil
}

func flowSpecTcpFlagParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	ss := make([]string, 0, len(TCPFlagNameMap))
	for _, v := range TCPFlagNameMap {
		ss = append(ss, v)
	}
	protos := strings.Join(ss, "|")
	exp := regexp.MustCompile(fmt.Sprintf("^%s (not )?(match )?((((%s)\\&)*(%s) )*(((%s)\\&)*(%s)))$", FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG], protos, protos, protos, protos))
	elems := exp.FindStringSubmatch(strings.Join(args, " "))
	if len(elems) < 1 {
		return nil, fmt.Errorf("invalid flag format")
	}
	items := make([]*FlowSpecComponentItem, 0)
	op := 0
	if elems[2] != "" {
		op |= 0x1
	}
	if elems[1] != "" {
		op |= 0x2
	}
	for _, v := range strings.Split(elems[3], " ") {
		flag := 0
		for _, e := range strings.Split(v, "&") {
			flag |= int(TCPFlagValueMap[e])
		}
		items = append(items, NewFlowSpecComponentItem(op, flag))
	}
	return NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, items), nil
}

func flowSpecEtherTypeParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	ss := make([]string, 0, len(EthernetTypeNameMap))
	for _, v := range EthernetTypeNameMap {
		ss = append(ss, v)
	}
	protos := strings.Join(ss, "|")
	exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE], protos, protos))
	elems := exp.FindStringSubmatch(strings.Join(args, " "))
	items := make([]*FlowSpecComponentItem, 0)
	eq := 0x1
	if elems[1] != "" {
		for _, v := range strings.Split(elems[1], " ") {
			p, ok := EthernetTypeValueMap[v]
			if !ok {
				continue
			}
			items = append(items, NewFlowSpecComponentItem(eq, int(p)))
		}
	}
	items = append(items, NewFlowSpecComponentItem(eq, int(EthernetTypeValueMap[elems[4]])))
	return NewFlowSpecComponent(FLOW_SPEC_TYPE_ETHERNET_TYPE, items), nil
}

func doFlowSpecNumericParser(rf RouteFamily, args []string, validationFunc func(int) error) (FlowSpecComponentInterface, error) {
	if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP && FlowSpecValueMap[args[0]] == FLOW_SPEC_TYPE_LABEL {
		return nil, fmt.Errorf("flow label spec is only allowed for ipv6")
	}
	exp := regexp.MustCompile("^((<=|>=|[<>=])(\\d+)&)?(<=|>=|[<>=])?(\\d+)$")
	items := make([]*FlowSpecComponentItem, 0)

	f := func(and bool, o, v string) (*FlowSpecComponentItem, error) {
		op := 0
		if and {
			op |= 0x40
		}
		if len(o) == 0 {
			op |= 0x1
		}
		for _, oo := range o {
			switch oo {
			case '>':
				op |= 0x2
			case '<':
				op |= 0x4
			case '=':
				op |= 0x1
			}
		}
		value, err := strconv.Atoi(v)
		if err != nil {
			return nil, err
		}
		err = validationFunc(value)
		if err != nil {
			return nil, err
		}
		return NewFlowSpecComponentItem(op, value), nil
	}

	for _, arg := range args[1:] {
		var and bool
		elems := exp.FindStringSubmatch(arg)
		if len(elems) == 0 {
			return nil, fmt.Errorf("invalid flowspec numeric item")
		}
		if elems[1] != "" {
			and = true
			item, err := f(false, elems[2], elems[3])
			if err != nil {
				return nil, err
			}
			items = append(items, item)
		}
		item, err := f(and, elems[4], elems[5])
		if err != nil {
			return nil, err
		}
		items = append(items, item)
	}

	return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil
}

func flowSpecNumericParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	f := func(i int) error {
		return nil
	}
	return doFlowSpecNumericParser(rf, args, f)
}

func flowSpecPortParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	f := func(i int) error {
		if 0 < i && i < 65536 {
			return nil
		}
		return fmt.Errorf("port range exceeded")
	}
	return doFlowSpecNumericParser(rf, args, f)
}

func flowSpecDscpParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	f := func(i int) error {
		if 0 < i && i < 64 {
			return nil
		}
		return fmt.Errorf("dscp value range exceeded")
	}
	return doFlowSpecNumericParser(rf, args, f)
}

func flowSpecFragmentParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	if len(args) < 2 {
		return nil, fmt.Errorf("invalid flowspec fragment specifier")
	}
	items := make([]*FlowSpecComponentItem, 0)
	for _, a := range args[1:] {
		value := 0
		switch a {
		case "dont-fragment":
			if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP6 {
				return nil, fmt.Errorf("can't specify dont-fragment for ipv6")
			}
			value = 0x1
		case "is-fragment":
			value = 0x2
		case "first-fragment":
			value = 0x4
		case "last-fragment":
			value = 0x8
		case "not-a-fragment":
			value = 0x0
		default:
			return nil, fmt.Errorf("invalid flowspec fragment specifier")
		}
		items = append(items, NewFlowSpecComponentItem(0, value))
	}
	return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil
}

func flowSpecMacParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
	if len(args) < 2 {
		return nil, fmt.Errorf("invalid flowspec dst/src mac")
	}
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("invalid family")
	}
	typ := args[0]
	mac, err := net.ParseMAC(args[1])
	if err != nil {
		return nil, fmt.Errorf("invalid mac")
	}
	switch typ {
	case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]:
		return NewFlowSpecDestinationMac(mac), nil
	case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]:
		return NewFlowSpecSourceMac(mac), nil
	}
	return nil, fmt.Errorf("invalid type. only %s or %s allowed", FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC], FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC])
}

var flowSpecParserMap = map[BGPFlowSpecType]func(RouteFamily, []string) (FlowSpecComponentInterface, error){
	FLOW_SPEC_TYPE_DST_PREFIX:    flowSpecPrefixParser,
	FLOW_SPEC_TYPE_SRC_PREFIX:    flowSpecPrefixParser,
	FLOW_SPEC_TYPE_IP_PROTO:      flowSpecIpProtoParser,
	FLOW_SPEC_TYPE_PORT:          flowSpecPortParser,
	FLOW_SPEC_TYPE_DST_PORT:      flowSpecPortParser,
	FLOW_SPEC_TYPE_SRC_PORT:      flowSpecPortParser,
	FLOW_SPEC_TYPE_ICMP_TYPE:     flowSpecNumericParser,
	FLOW_SPEC_TYPE_ICMP_CODE:     flowSpecNumericParser,
	FLOW_SPEC_TYPE_TCP_FLAG:      flowSpecTcpFlagParser,
	FLOW_SPEC_TYPE_PKT_LEN:       flowSpecNumericParser,
	FLOW_SPEC_TYPE_DSCP:          flowSpecDscpParser,
	FLOW_SPEC_TYPE_FRAGMENT:      flowSpecFragmentParser,
	FLOW_SPEC_TYPE_LABEL:         flowSpecNumericParser,
	FLOW_SPEC_TYPE_ETHERNET_TYPE: flowSpecEtherTypeParser,
	FLOW_SPEC_TYPE_DST_MAC:       flowSpecMacParser,
	FLOW_SPEC_TYPE_SRC_MAC:       flowSpecMacParser,
	FLOW_SPEC_TYPE_LLC_DSAP:      flowSpecNumericParser,
	FLOW_SPEC_TYPE_LLC_SSAP:      flowSpecNumericParser,
	FLOW_SPEC_TYPE_LLC_CONTROL:   flowSpecNumericParser,
	FLOW_SPEC_TYPE_SNAP:          flowSpecNumericParser,
	FLOW_SPEC_TYPE_VID:           flowSpecNumericParser,
	FLOW_SPEC_TYPE_COS:           flowSpecNumericParser,
	FLOW_SPEC_TYPE_INNER_VID:     flowSpecNumericParser,
	FLOW_SPEC_TYPE_INNER_COS:     flowSpecNumericParser,
}

func ParseFlowSpecComponents(rf RouteFamily, input string) ([]FlowSpecComponentInterface, error) {
	idxs := make([]struct {
		t BGPFlowSpecType
		i int
	}, 0, 8)
	args := strings.Split(input, " ")
	for idx, v := range args {
		if t, ok := FlowSpecValueMap[v]; ok {
			idxs = append(idxs, struct {
				t BGPFlowSpecType
				i int
			}{t, idx})
		}
	}
	if len(idxs) == 0 {
		return nil, fmt.Errorf("failed to parse: %s", input)
	}
	cmps := make([]FlowSpecComponentInterface, 0, len(idxs))
	for i, idx := range idxs {
		var a []string
		f := flowSpecParserMap[idx.t]
		if i < len(idxs)-1 {
			a = args[idx.i:idxs[i+1].i]
		} else {
			a = args[idx.i:]
		}
		cmp, err := f(rf, a)
		if err != nil {
			return nil, err
		}
		cmps = append(cmps, cmp)
	}
	return cmps, nil
}

func (t BGPFlowSpecType) String() string {
	name, ok := FlowSpecNameMap[t]
	if !ok {
		return fmt.Sprintf("%s(%d)", FlowSpecNameMap[FLOW_SPEC_TYPE_UNKNOWN], t)
	}
	return name
}

type FlowSpecComponentInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	Len() int
	Type() BGPFlowSpecType
	String() string
}

type flowSpecPrefix struct {
	Prefix AddrPrefixInterface
	type_  BGPFlowSpecType
}

func (p *flowSpecPrefix) DecodeFromBytes(data []byte) error {
	p.type_ = BGPFlowSpecType(data[0])
	return p.Prefix.DecodeFromBytes(data[1:])
}

func (p *flowSpecPrefix) Serialize() ([]byte, error) {
	buf := []byte{byte(p.Type())}
	bbuf, err := p.Prefix.Serialize()
	if err != nil {
		return nil, err
	}
	return append(buf, bbuf...), nil
}

func (p *flowSpecPrefix) Len() int {
	buf, _ := p.Serialize()
	return len(buf)
}

func (p *flowSpecPrefix) Type() BGPFlowSpecType {
	return p.type_
}

func (p *flowSpecPrefix) String() string {
	return fmt.Sprintf("[%s:%s]", p.Type(), p.Prefix.String())
}

func (p *flowSpecPrefix) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType     `json:"type"`
		Value AddrPrefixInterface `json:"value"`
	}{
		Type:  p.Type(),
		Value: p.Prefix,
	})
}

type flowSpecPrefix6 struct {
	Prefix AddrPrefixInterface
	Offset uint8
	type_  BGPFlowSpecType
}

// draft-ietf-idr-flow-spec-v6-06
// <type (1 octet), prefix length (1 octet), prefix offset(1 octet), prefix>
func (p *flowSpecPrefix6) DecodeFromBytes(data []byte) error {
	p.type_ = BGPFlowSpecType(data[0])
	p.Offset = data[2]
	prefix := append([]byte{data[1]}, data[3:]...)
	return p.Prefix.DecodeFromBytes(prefix)
}

func (p *flowSpecPrefix6) Serialize() ([]byte, error) {
	buf := []byte{byte(p.Type())}
	bbuf, err := p.Prefix.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, bbuf[0])
	buf = append(buf, p.Offset)
	return append(buf, bbuf[1:]...), nil
}

func (p *flowSpecPrefix6) Len() int {
	buf, _ := p.Serialize()
	return len(buf)
}

func (p *flowSpecPrefix6) Type() BGPFlowSpecType {
	return p.type_
}

func (p *flowSpecPrefix6) String() string {
	return fmt.Sprintf("[%s:%s/%d]", p.Type(), p.Prefix.String(), p.Offset)
}

func (p *flowSpecPrefix6) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   BGPFlowSpecType     `json:"type"`
		Value  AddrPrefixInterface `json:"value"`
		Offset uint8               `json:"offset"`
	}{
		Type:   p.Type(),
		Value:  p.Prefix,
		Offset: p.Offset,
	})
}

type FlowSpecDestinationPrefix struct {
	flowSpecPrefix
}

func NewFlowSpecDestinationPrefix(prefix AddrPrefixInterface) *FlowSpecDestinationPrefix {
	return &FlowSpecDestinationPrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_DST_PREFIX}}
}

type FlowSpecSourcePrefix struct {
	flowSpecPrefix
}

func NewFlowSpecSourcePrefix(prefix AddrPrefixInterface) *FlowSpecSourcePrefix {
	return &FlowSpecSourcePrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_SRC_PREFIX}}
}

type FlowSpecDestinationPrefix6 struct {
	flowSpecPrefix6
}

func NewFlowSpecDestinationPrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecDestinationPrefix6 {
	return &FlowSpecDestinationPrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_DST_PREFIX}}
}

type FlowSpecSourcePrefix6 struct {
	flowSpecPrefix6
}

func NewFlowSpecSourcePrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecSourcePrefix6 {
	return &FlowSpecSourcePrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_SRC_PREFIX}}
}

type flowSpecMac struct {
	Mac   net.HardwareAddr
	type_ BGPFlowSpecType
}

func (p *flowSpecMac) DecodeFromBytes(data []byte) error {
	if len(data) < 2 || len(data) < 2+int(data[1]) {
		return fmt.Errorf("not all mac bits available")
	}
	p.type_ = BGPFlowSpecType(data[0])
	p.Mac = net.HardwareAddr(data[2 : 2+int(data[1])])
	return nil
}

func (p *flowSpecMac) Serialize() ([]byte, error) {
	if len(p.Mac) == 0 {
		return nil, fmt.Errorf("mac unset")
	}
	buf := []byte{byte(p.Type()), byte(len(p.Mac))}
	return append(buf, []byte(p.Mac)...), nil
}

func (p *flowSpecMac) Len() int {
	return 2 + len(p.Mac)
}

func (p *flowSpecMac) Type() BGPFlowSpecType {
	return p.type_
}

func (p *flowSpecMac) String() string {
	return fmt.Sprintf("[%s:%s]", p.Type(), p.Mac.String())
}

func (p *flowSpecMac) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType `json:"type"`
		Value string          `json:"value"`
	}{
		Type:  p.Type(),
		Value: p.Mac.String(),
	})
}

type FlowSpecSourceMac struct {
	flowSpecMac
}

func NewFlowSpecSourceMac(mac net.HardwareAddr) *FlowSpecSourceMac {
	return &FlowSpecSourceMac{flowSpecMac{Mac: mac, type_: FLOW_SPEC_TYPE_SRC_MAC}}
}

type FlowSpecDestinationMac struct {
	flowSpecMac
}

func NewFlowSpecDestinationMac(mac net.HardwareAddr) *FlowSpecDestinationMac {
	return &FlowSpecDestinationMac{flowSpecMac{Mac: mac, type_: FLOW_SPEC_TYPE_DST_MAC}}
}

type FlowSpecComponentItem struct {
	Op    int `json:"op"`
	Value int `json:"value"`
}

func (v *FlowSpecComponentItem) Len() int {
	return 1 << ((uint32(v.Op) >> 4) & 0x3)
}

func (v *FlowSpecComponentItem) Serialize() ([]byte, error) {
	if v.Value < 0 {
		return nil, fmt.Errorf("invalid value size(too small): %d", v.Value)
	}
	if v.Op < 0 || v.Op > math.MaxUint8 {
		return nil, fmt.Errorf("invalid op size: %d", v.Op)

	}
	order := uint32(math.Log2(float64(v.Len())))
	buf := make([]byte, 1+(1<<order))
	buf[0] = byte(uint32(v.Op) | order<<4)
	switch order {
	case 0:
		buf[1] = byte(v.Value)
	case 1:
		binary.BigEndian.PutUint16(buf[1:], uint16(v.Value))
	case 2:
		binary.BigEndian.PutUint32(buf[1:], uint32(v.Value))
	case 3:
		binary.BigEndian.PutUint64(buf[1:], uint64(v.Value))
	default:
		return nil, fmt.Errorf("invalid value size(too big): %d", v.Value)
	}
	return buf, nil
}

func NewFlowSpecComponentItem(op int, value int) *FlowSpecComponentItem {
	v := &FlowSpecComponentItem{op, value}
	order := uint32(math.Log2(float64(v.Len())))
	// we don't know if not initialized properly or initialized to
	// zero...
	if order == 0 {
		order = func() uint32 {
			for i := 0; i < 3; i++ {
				if v.Value < (1 << ((1 << uint(i)) * 8)) {
					return uint32(i)
				}
			}
			// return invalid order
			return 4
		}()
	}
	if order > 3 {
		return nil
	}
	v.Op = int(uint32(v.Op) | order<<4)
	return v
}

type FlowSpecComponent struct {
	Items []*FlowSpecComponentItem
	type_ BGPFlowSpecType
}

func (p *FlowSpecComponent) DecodeFromBytes(data []byte) error {
	p.type_ = BGPFlowSpecType(data[0])
	data = data[1:]
	p.Items = make([]*FlowSpecComponentItem, 0)
	for {
		if len(data) < 2 {
			return fmt.Errorf("not all flowspec component bytes available")
		}
		op := data[0]
		end := op & 0x80
		l := 1 << ((op >> 4) & 0x3) // (min, max) = (1, 8)
		v := make([]byte, 8)
		copy(v[8-l:], data[1:1+l])
		i := int(binary.BigEndian.Uint64(v))
		item := &FlowSpecComponentItem{int(op), i}
		p.Items = append(p.Items, item)
		if end > 0 {
			break
		}
		data = data[1+l:]
	}
	return nil
}

func (p *FlowSpecComponent) Serialize() ([]byte, error) {
	buf := []byte{byte(p.Type())}
	for i, v := range p.Items {
		//set end-of-list bit
		if i == (len(p.Items) - 1) {
			v.Op |= 0x80
		} else {
			v.Op &^= 0x80
		}
		bbuf, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	return buf, nil
}

func (p *FlowSpecComponent) Len() int {
	l := 1
	for _, item := range p.Items {
		l += (item.Len() + 1)
	}
	return l
}

func (p *FlowSpecComponent) Type() BGPFlowSpecType {
	return p.type_
}

func formatRaw(op int, value int) string {
	return fmt.Sprintf("op: %b, value: %d", op, value)
}

func formatNumericOp(op int) string {
	var opstr string
	if op&0x40 > 0 {
		opstr = "&"
	} else {
		opstr = " "
	}
	if op&0x2 > 0 {
		opstr += ">"
	}
	if op&0x4 > 0 {
		opstr += "<"
	}
	if op&0x1 > 0 {
		opstr += "="
	}
	return opstr
}

func formatNumeric(op int, value int) string {
	return fmt.Sprintf("%s%d", formatNumericOp(op), value)
}

func formatProto(op int, value int) string {
	return fmt.Sprintf(" %s", Protocol(value).String())
}

func formatFlag(op int, value int) string {
	and := " "
	ss := make([]string, 0, 2)
	if op&0x40 > 0 {
		and = "&"
	}
	if op&0x1 > 0 {
		ss = append(ss, "match")
	}
	if op&0x2 > 0 {
		ss = append(ss, "not")
	}
	if len(ss) > 0 {
		return fmt.Sprintf("%s(%s)%s", and, strings.Join(ss, "|"), TCPFlag(value).String())
	}
	return fmt.Sprintf("%s%s", and, TCPFlag(value).String())
}

func formatFragment(op int, value int) string {
	ss := make([]string, 0)
	if value == 0 {
		ss = append(ss, "not-a-fragment")
	}
	if value&0x1 > 0 {
		ss = append(ss, "dont-fragment")
	}
	if value&0x2 > 0 {
		ss = append(ss, "is-fragment")
	}
	if value&0x4 > 0 {
		ss = append(ss, "first-fragment")
	}
	if value&0x8 > 0 {
		ss = append(ss, "last-fragment")
	}
	if len(ss) > 1 {
		return fmt.Sprintf("%s(%s)", formatNumericOp(op), strings.Join(ss, "|"))
	}
	return fmt.Sprintf("%s%s", formatNumericOp(op), ss[0])
}

func formatEtherType(op int, value int) string {
	return fmt.Sprintf(" %s", EthernetType(value).String())
}

var flowSpecFormatMap = map[BGPFlowSpecType]func(op int, value int) string{
	FLOW_SPEC_TYPE_UNKNOWN:       formatRaw,
	FLOW_SPEC_TYPE_IP_PROTO:      formatProto,
	FLOW_SPEC_TYPE_PORT:          formatNumeric,
	FLOW_SPEC_TYPE_DST_PORT:      formatNumeric,
	FLOW_SPEC_TYPE_SRC_PORT:      formatNumeric,
	FLOW_SPEC_TYPE_ICMP_TYPE:     formatNumeric,
	FLOW_SPEC_TYPE_ICMP_CODE:     formatNumeric,
	FLOW_SPEC_TYPE_TCP_FLAG:      formatFlag,
	FLOW_SPEC_TYPE_PKT_LEN:       formatNumeric,
	FLOW_SPEC_TYPE_DSCP:          formatNumeric,
	FLOW_SPEC_TYPE_FRAGMENT:      formatFragment,
	FLOW_SPEC_TYPE_LABEL:         formatNumeric,
	FLOW_SPEC_TYPE_ETHERNET_TYPE: formatEtherType,
	FLOW_SPEC_TYPE_LLC_DSAP:      formatNumeric,
	FLOW_SPEC_TYPE_LLC_SSAP:      formatNumeric,
	FLOW_SPEC_TYPE_LLC_CONTROL:   formatNumeric,
	FLOW_SPEC_TYPE_SNAP:          formatNumeric,
	FLOW_SPEC_TYPE_VID:           formatNumeric,
	FLOW_SPEC_TYPE_COS:           formatNumeric,
	FLOW_SPEC_TYPE_INNER_VID:     formatNumeric,
	FLOW_SPEC_TYPE_INNER_COS:     formatNumeric,
}

func (p *FlowSpecComponent) String() string {
	f := flowSpecFormatMap[FLOW_SPEC_TYPE_UNKNOWN]
	if _, ok := flowSpecFormatMap[p.Type()]; ok {
		f = flowSpecFormatMap[p.Type()]
	}
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	for _, i := range p.Items {
		buf.WriteString(f(i.Op, i.Value))
	}
	return fmt.Sprintf("[%s:%s]", p.type_, buf.String())
}

func (p *FlowSpecComponent) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType          `json:"type"`
		Value []*FlowSpecComponentItem `json:"value"`
	}{
		Type:  p.Type(),
		Value: p.Items,
	})
}

func NewFlowSpecComponent(type_ BGPFlowSpecType, items []*FlowSpecComponentItem) *FlowSpecComponent {
	return &FlowSpecComponent{
		Items: items,
		type_: type_,
	}
}

type FlowSpecUnknown struct {
	Value []byte
}

func (p *FlowSpecUnknown) DecodeFromBytes(data []byte) error {
	p.Value = data
	return nil
}

func (p *FlowSpecUnknown) Serialize() ([]byte, error) {
	return p.Value, nil
}

func (p *FlowSpecUnknown) Len() int {
	return len(p.Value)
}

func (p *FlowSpecUnknown) Type() BGPFlowSpecType {
	if len(p.Value) > 0 {
		return BGPFlowSpecType(p.Value[0])
	}
	return FLOW_SPEC_TYPE_UNKNOWN
}

func (p *FlowSpecUnknown) String() string {
	return fmt.Sprintf("[unknown:%v]", p.Value)
}

type FlowSpecNLRI struct {
	Value []FlowSpecComponentInterface
	rf    RouteFamily
}

func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte) error {
	var length int
	if (data[0]>>4) == 0xf && len(data) > 2 {
		length = int(binary.BigEndian.Uint16(data[0:2]))
		data = data[2:]
	} else if len(data) > 1 {
		length = int(data[0])
		data = data[1:]
	} else {
		return fmt.Errorf("not all flowspec component bytes available")
	}

	n.rf = rf

	for l := length; l > 0; {
		if len(data) == 0 {
			return fmt.Errorf("not all flowspec component bytes available")
		}
		t := BGPFlowSpecType(data[0])
		var i FlowSpecComponentInterface
		switch t {
		case FLOW_SPEC_TYPE_DST_PREFIX:
			switch rf {
			case RF_FS_IPv4_UC:
				i = NewFlowSpecDestinationPrefix(NewIPAddrPrefix(0, ""))
			case RF_FS_IPv4_VPN:
				i = NewFlowSpecDestinationPrefix(NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil))
			case RF_FS_IPv6_UC:
				i = NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(0, ""), 0)
			case RF_FS_IPv6_VPN:
				i = NewFlowSpecDestinationPrefix6(NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil), 0)
			default:
				return fmt.Errorf("Invalid RF: %v", rf)
			}
		case FLOW_SPEC_TYPE_SRC_PREFIX:
			switch rf {
			case RF_FS_IPv4_UC:
				i = NewFlowSpecSourcePrefix(NewIPAddrPrefix(0, ""))
			case RF_FS_IPv4_VPN:
				i = NewFlowSpecSourcePrefix(NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil))
			case RF_FS_IPv6_UC:
				i = NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(0, ""), 0)
			case RF_FS_IPv6_VPN:
				i = NewFlowSpecSourcePrefix6(NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil), 0)
			default:
				return fmt.Errorf("Invalid RF: %v", rf)
			}
		case FLOW_SPEC_TYPE_SRC_MAC:
			switch rf {
			case RF_FS_L2_VPN:
				i = NewFlowSpecSourceMac(nil)
			default:
				return fmt.Errorf("invalid family: %v", rf)
			}
		case FLOW_SPEC_TYPE_DST_MAC:
			switch rf {
			case RF_FS_L2_VPN:
				i = NewFlowSpecDestinationMac(nil)
			default:
				return fmt.Errorf("invalid family: %v", rf)
			}
		case FLOW_SPEC_TYPE_IP_PROTO, FLOW_SPEC_TYPE_PORT, FLOW_SPEC_TYPE_DST_PORT, FLOW_SPEC_TYPE_SRC_PORT,
			FLOW_SPEC_TYPE_ICMP_TYPE, FLOW_SPEC_TYPE_ICMP_CODE, FLOW_SPEC_TYPE_TCP_FLAG, FLOW_SPEC_TYPE_PKT_LEN,
			FLOW_SPEC_TYPE_DSCP, FLOW_SPEC_TYPE_FRAGMENT, FLOW_SPEC_TYPE_LABEL, FLOW_SPEC_TYPE_ETHERNET_TYPE,
			FLOW_SPEC_TYPE_LLC_DSAP, FLOW_SPEC_TYPE_LLC_SSAP, FLOW_SPEC_TYPE_LLC_CONTROL, FLOW_SPEC_TYPE_SNAP,
			FLOW_SPEC_TYPE_VID, FLOW_SPEC_TYPE_COS, FLOW_SPEC_TYPE_INNER_VID, FLOW_SPEC_TYPE_INNER_COS:
			i = NewFlowSpecComponent(t, nil)
		default:
			i = &FlowSpecUnknown{}
		}

		err := i.DecodeFromBytes(data)
		if err != nil {
			i = &FlowSpecUnknown{data}
		}
		l -= i.Len()
		data = data[i.Len():]
		n.Value = append(n.Value, i)
	}

	return nil
}

func (n *FlowSpecNLRI) Serialize() ([]byte, error) {
	buf := make([]byte, 0, 32)
	for _, v := range n.Value {
		b, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, b...)
	}
	length := n.Len()
	if length > 0xfff {
		return nil, fmt.Errorf("Too large: %d", length)
	} else if length < 0xf0 {
		length -= 1
		buf = append([]byte{byte(length)}, buf...)
	} else {
		length -= 2
		b := make([]byte, 2)
		binary.BigEndian.PutUint16(buf, uint16(length))
		buf = append(b, buf...)
	}

	return buf, nil
}

func (n *FlowSpecNLRI) Len() int {
	l := 0
	for _, v := range n.Value {
		l += v.Len()
	}
	if l < 0xf0 {
		return l + 1
	} else {
		return l + 2
	}
}

func (n *FlowSpecNLRI) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	for _, v := range n.Value {
		buf.WriteString(v.String())
	}
	return buf.String()
}

func (n *FlowSpecNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Value []FlowSpecComponentInterface `json:"value"`
	}{
		Value: n.Value,
	})
}

type FlowSpecIPv4Unicast struct {
	FlowSpecNLRI
}

func (n *FlowSpecIPv4Unicast) DecodeFromBytes(data []byte) error {
	return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
}

func (n *FlowSpecIPv4Unicast) AFI() uint16 {
	return AFI_IP
}

func (n *FlowSpecIPv4Unicast) SAFI() uint8 {
	return SAFI_FLOW_SPEC_UNICAST
}

func NewFlowSpecIPv4Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv4Unicast {
	return &FlowSpecIPv4Unicast{FlowSpecNLRI{value, RF_FS_IPv4_UC}}
}

type FlowSpecIPv4VPN struct {
	FlowSpecNLRI
}

func (n *FlowSpecIPv4VPN) DecodeFromBytes(data []byte) error {
	return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
}

func (n *FlowSpecIPv4VPN) AFI() uint16 {
	return AFI_IP
}

func (n *FlowSpecIPv4VPN) SAFI() uint8 {
	return SAFI_FLOW_SPEC_VPN
}

func NewFlowSpecIPv4VPN(value []FlowSpecComponentInterface) *FlowSpecIPv4VPN {
	return &FlowSpecIPv4VPN{FlowSpecNLRI{value, RF_FS_IPv4_VPN}}
}

type FlowSpecIPv6Unicast struct {
	FlowSpecNLRI
}

func (n *FlowSpecIPv6Unicast) DecodeFromBytes(data []byte) error {
	return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
}

func (n *FlowSpecIPv6Unicast) AFI() uint16 {
	return AFI_IP6
}

func (n *FlowSpecIPv6Unicast) SAFI() uint8 {
	return SAFI_FLOW_SPEC_UNICAST
}

func NewFlowSpecIPv6Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv6Unicast {
	return &FlowSpecIPv6Unicast{FlowSpecNLRI{
		Value: value,
		rf:    RF_FS_IPv6_UC,
	}}
}

type FlowSpecIPv6VPN struct {
	FlowSpecNLRI
}

func (n *FlowSpecIPv6VPN) DecodeFromBytes(data []byte) error {
	return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
}

func (n *FlowSpecIPv6VPN) AFI() uint16 {
	return AFI_IP6
}

func (n *FlowSpecIPv6VPN) SAFI() uint8 {
	return SAFI_FLOW_SPEC_VPN
}

func NewFlowSpecIPv6VPN(value []FlowSpecComponentInterface) *FlowSpecIPv6VPN {
	return &FlowSpecIPv6VPN{FlowSpecNLRI{
		Value: value,
		rf:    RF_FS_IPv6_VPN,
	}}
}

type FlowSpecL2VPN struct {
	FlowSpecNLRI
}

func (n *FlowSpecL2VPN) DecodeFromBytes(data []byte) error {
	return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
}

func (n *FlowSpecL2VPN) AFI() uint16 {
	return AFI_L2VPN
}

func (n *FlowSpecL2VPN) SAFI() uint8 {
	return SAFI_FLOW_SPEC_VPN
}

func NewFlowSpecL2VPN(value []FlowSpecComponentInterface) *FlowSpecL2VPN {
	return &FlowSpecL2VPN{FlowSpecNLRI{
		Value: value,
		rf:    RF_FS_L2_VPN,
	}}
}

type OpaqueNLRI struct {
	Length uint8
	Key    []byte
}

func (n *OpaqueNLRI) DecodeFromBytes(data []byte) error {
	n.Length = data[0]
	if len(data)-1 < int(n.Length) {
		return fmt.Errorf("Not all OpaqueNLRI bytes available")
	}
	n.Key = data[1 : 1+n.Length]
	return nil
}

func (n *OpaqueNLRI) Serialize() ([]byte, error) {
	if len(n.Key) > math.MaxUint8 {
		return nil, fmt.Errorf("Key length too big")
	}
	return append([]byte{byte(len(n.Key))}, n.Key...), nil
}

func (n *OpaqueNLRI) AFI() uint16 {
	return AFI_OPAQUE
}

func (n *OpaqueNLRI) SAFI() uint8 {
	return SAFI_KEY_VALUE
}

func (n *OpaqueNLRI) Len() int {
	return 1 + len(n.Key)
}

func (n *OpaqueNLRI) String() string {
	return string(n.Key)
}

func (n *OpaqueNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Key string `json:"key"`
	}{
		Key: n.String(),
	})
}

func NewOpaqueNLRI(key []byte) *OpaqueNLRI {
	return &OpaqueNLRI{
		Key: key,
	}
}

func AfiSafiToRouteFamily(afi uint16, safi uint8) RouteFamily {
	return RouteFamily(int(afi)<<16 | int(safi))
}

func RouteFamilyToAfiSafi(rf RouteFamily) (uint16, uint8) {
	return uint16(int(rf) >> 16), uint8(int(rf) & 0xff)
}

type RouteFamily int

func (f RouteFamily) String() string {
	if n, y := AddressFamilyNameMap[f]; y {
		return n
	}
	return fmt.Sprintf("UnknownFamily(%d)", f)
}

const (
	RF_IPv4_UC     RouteFamily = AFI_IP<<16 | SAFI_UNICAST
	RF_IPv6_UC     RouteFamily = AFI_IP6<<16 | SAFI_UNICAST
	RF_IPv4_MC     RouteFamily = AFI_IP<<16 | SAFI_MULTICAST
	RF_IPv6_MC     RouteFamily = AFI_IP6<<16 | SAFI_MULTICAST
	RF_IPv4_VPN    RouteFamily = AFI_IP<<16 | SAFI_MPLS_VPN
	RF_IPv6_VPN    RouteFamily = AFI_IP6<<16 | SAFI_MPLS_VPN
	RF_IPv4_VPN_MC RouteFamily = AFI_IP<<16 | SAFI_MPLS_VPN_MULTICAST
	RF_IPv6_VPN_MC RouteFamily = AFI_IP6<<16 | SAFI_MPLS_VPN_MULTICAST
	RF_IPv4_MPLS   RouteFamily = AFI_IP<<16 | SAFI_MPLS_LABEL
	RF_IPv6_MPLS   RouteFamily = AFI_IP6<<16 | SAFI_MPLS_LABEL
	RF_VPLS        RouteFamily = AFI_L2VPN<<16 | SAFI_VPLS
	RF_EVPN        RouteFamily = AFI_L2VPN<<16 | SAFI_EVPN
	RF_RTC_UC      RouteFamily = AFI_IP<<16 | SAFI_ROUTE_TARGET_CONSTRTAINS
	RF_IPv4_ENCAP  RouteFamily = AFI_IP<<16 | SAFI_ENCAPSULATION
	RF_IPv6_ENCAP  RouteFamily = AFI_IP6<<16 | SAFI_ENCAPSULATION
	RF_FS_IPv4_UC  RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_UNICAST
	RF_FS_IPv4_VPN RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_VPN
	RF_FS_IPv6_UC  RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_UNICAST
	RF_FS_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_VPN
	RF_FS_L2_VPN   RouteFamily = AFI_L2VPN<<16 | SAFI_FLOW_SPEC_VPN
	RF_OPAQUE      RouteFamily = AFI_OPAQUE<<16 | SAFI_KEY_VALUE
)

var AddressFamilyNameMap = map[RouteFamily]string{
	RF_IPv4_UC:     "ipv4-unicast",
	RF_IPv6_UC:     "ipv6-unicast",
	RF_IPv4_MC:     "ipv4-multicast",
	RF_IPv6_MC:     "ipv6-multicast",
	RF_IPv4_MPLS:   "ipv4-labelled-unicast",
	RF_IPv6_MPLS:   "ipv6-labelled-unicast",
	RF_IPv4_VPN:    "l3vpn-ipv4-unicast",
	RF_IPv6_VPN:    "l3vpn-ipv6-unicast",
	RF_IPv4_VPN_MC: "l3vpn-ipv4-multicast",
	RF_IPv6_VPN_MC: "l3vpn-ipv6-multicast",
	RF_VPLS:        "l2vpn-vpls",
	RF_EVPN:        "l2vpn-evpn",
	RF_RTC_UC:      "rtc",
	RF_IPv4_ENCAP:  "ipv4-encap",
	RF_IPv6_ENCAP:  "ipv6-encap",
	RF_FS_IPv4_UC:  "ipv4-flowspec",
	RF_FS_IPv4_VPN: "l3vpn-ipv4-flowspec",
	RF_FS_IPv6_UC:  "ipv6-flowspec",
	RF_FS_IPv6_VPN: "l3vpn-ipv6-flowspec",
	RF_FS_L2_VPN:   "l2vpn-flowspec",
	RF_OPAQUE:      "opaque",
}

var AddressFamilyValueMap = map[string]RouteFamily{
	AddressFamilyNameMap[RF_IPv4_UC]:     RF_IPv4_UC,
	AddressFamilyNameMap[RF_IPv6_UC]:     RF_IPv6_UC,
	AddressFamilyNameMap[RF_IPv4_MC]:     RF_IPv4_MC,
	AddressFamilyNameMap[RF_IPv6_MC]:     RF_IPv6_MC,
	AddressFamilyNameMap[RF_IPv4_MPLS]:   RF_IPv4_MPLS,
	AddressFamilyNameMap[RF_IPv6_MPLS]:   RF_IPv6_MPLS,
	AddressFamilyNameMap[RF_IPv4_VPN]:    RF_IPv4_VPN,
	AddressFamilyNameMap[RF_IPv6_VPN]:    RF_IPv6_VPN,
	AddressFamilyNameMap[RF_IPv4_VPN_MC]: RF_IPv4_VPN_MC,
	AddressFamilyNameMap[RF_IPv6_VPN_MC]: RF_IPv6_VPN_MC,
	AddressFamilyNameMap[RF_VPLS]:        RF_VPLS,
	AddressFamilyNameMap[RF_EVPN]:        RF_EVPN,
	AddressFamilyNameMap[RF_RTC_UC]:      RF_RTC_UC,
	AddressFamilyNameMap[RF_IPv4_ENCAP]:  RF_IPv4_ENCAP,
	AddressFamilyNameMap[RF_IPv6_ENCAP]:  RF_IPv6_ENCAP,
	AddressFamilyNameMap[RF_FS_IPv4_UC]:  RF_FS_IPv4_UC,
	AddressFamilyNameMap[RF_FS_IPv4_VPN]: RF_FS_IPv4_VPN,
	AddressFamilyNameMap[RF_FS_IPv6_UC]:  RF_FS_IPv6_UC,
	AddressFamilyNameMap[RF_FS_IPv6_VPN]: RF_FS_IPv6_VPN,
	AddressFamilyNameMap[RF_FS_L2_VPN]:   RF_FS_L2_VPN,
	AddressFamilyNameMap[RF_OPAQUE]:      RF_OPAQUE,
}

func GetRouteFamily(name string) (RouteFamily, error) {
	if v, ok := AddressFamilyValueMap[name]; ok {
		return v, nil
	}
	return RouteFamily(0), fmt.Errorf("%s isn't a valid route family name", name)
}

func NewPrefixFromRouteFamily(afi uint16, safi uint8) (prefix AddrPrefixInterface, err error) {
	switch AfiSafiToRouteFamily(afi, safi) {
	case RF_IPv4_UC, RF_IPv4_MC:
		prefix = NewIPAddrPrefix(0, "")
	case RF_IPv6_UC, RF_IPv6_MC:
		prefix = NewIPv6AddrPrefix(0, "")
	case RF_IPv4_VPN:
		prefix = NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil)
	case RF_IPv6_VPN:
		prefix = NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil)
	case RF_IPv4_MPLS:
		prefix = NewLabeledIPAddrPrefix(0, "", *NewMPLSLabelStack())
	case RF_IPv6_MPLS:
		prefix = NewLabeledIPv6AddrPrefix(0, "", *NewMPLSLabelStack())
	case RF_EVPN:
		prefix = NewEVPNNLRI(0, 0, nil)
	case RF_RTC_UC:
		prefix = &RouteTargetMembershipNLRI{}
	case RF_IPv4_ENCAP:
		prefix = NewEncapNLRI("")
	case RF_IPv6_ENCAP:
		prefix = NewEncapv6NLRI("")
	case RF_FS_IPv4_UC:
		prefix = &FlowSpecIPv4Unicast{}
	case RF_FS_IPv4_VPN:
		prefix = &FlowSpecIPv4VPN{}
	case RF_FS_IPv6_UC:
		prefix = &FlowSpecIPv6Unicast{}
	case RF_FS_IPv6_VPN:
		prefix = &FlowSpecIPv6VPN{}
	case RF_FS_L2_VPN:
		prefix = &FlowSpecL2VPN{}
	case RF_OPAQUE:
		prefix = &OpaqueNLRI{}
	default:
		return nil, fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", afi, safi)
	}
	return prefix, nil
}

type BGPAttrFlag uint8

const (
	BGP_ATTR_FLAG_EXTENDED_LENGTH BGPAttrFlag = 1 << 4
	BGP_ATTR_FLAG_PARTIAL         BGPAttrFlag = 1 << 5
	BGP_ATTR_FLAG_TRANSITIVE      BGPAttrFlag = 1 << 6
	BGP_ATTR_FLAG_OPTIONAL        BGPAttrFlag = 1 << 7
)

func (f BGPAttrFlag) String() string {
	strs := make([]string, 0, 4)
	if f&BGP_ATTR_FLAG_EXTENDED_LENGTH > 0 {
		strs = append(strs, "EXTENDED_LENGTH")
	}
	if f&BGP_ATTR_FLAG_PARTIAL > 0 {
		strs = append(strs, "PARTIAL")
	}
	if f&BGP_ATTR_FLAG_TRANSITIVE > 0 {
		strs = append(strs, "TRANSITIVE")
	}
	if f&BGP_ATTR_FLAG_OPTIONAL > 0 {
		strs = append(strs, "OPTIONAL")
	}
	return strings.Join(strs, "|")
}

type BGPAttrType uint8

const (
	_ BGPAttrType = iota
	BGP_ATTR_TYPE_ORIGIN
	BGP_ATTR_TYPE_AS_PATH
	BGP_ATTR_TYPE_NEXT_HOP
	BGP_ATTR_TYPE_MULTI_EXIT_DISC
	BGP_ATTR_TYPE_LOCAL_PREF
	BGP_ATTR_TYPE_ATOMIC_AGGREGATE
	BGP_ATTR_TYPE_AGGREGATOR
	BGP_ATTR_TYPE_COMMUNITIES
	BGP_ATTR_TYPE_ORIGINATOR_ID
	BGP_ATTR_TYPE_CLUSTER_LIST
	_
	_
	_
	BGP_ATTR_TYPE_MP_REACH_NLRI // = 14
	BGP_ATTR_TYPE_MP_UNREACH_NLRI
	BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
	BGP_ATTR_TYPE_AS4_PATH
	BGP_ATTR_TYPE_AS4_AGGREGATOR
	_
	_
	_
	BGP_ATTR_TYPE_PMSI_TUNNEL // = 22
	BGP_ATTR_TYPE_TUNNEL_ENCAP
	_
	_
	BGP_ATTR_TYPE_AIGP                     // = 26
	BGP_ATTR_TYPE_OPAQUE_VALUE BGPAttrType = 41
)

// NOTIFICATION Error Code  RFC 4271 4.5.
const (
	_ = iota
	BGP_ERROR_MESSAGE_HEADER_ERROR
	BGP_ERROR_OPEN_MESSAGE_ERROR
	BGP_ERROR_UPDATE_MESSAGE_ERROR
	BGP_ERROR_HOLD_TIMER_EXPIRED
	BGP_ERROR_FSM_ERROR
	BGP_ERROR_CEASE
	BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR
)

// NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED
	BGP_ERROR_SUB_BAD_MESSAGE_LENGTH
	BGP_ERROR_SUB_BAD_MESSAGE_TYPE
)

// NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER
	BGP_ERROR_SUB_BAD_PEER_AS
	BGP_ERROR_SUB_BAD_BGP_IDENTIFIER
	BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER
	BGP_ERROR_SUB_DEPRECATED_AUTHENTICATION_FAILURE
	BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
	BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY
)

// NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST
	BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
	BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE
	BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR
	BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR
	BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE
	BGP_ERROR_SUB_DEPRECATED_ROUTING_LOOP
	BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE
	BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR
	BGP_ERROR_SUB_INVALID_NETWORK_FIELD
	BGP_ERROR_SUB_MALFORMED_AS_PATH
)

// NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED
const (
	_ = iota
	BGP_ERROR_SUB_HOLD_TIMER_EXPIRED
)

// NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE
	BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE
	BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE
)

// NOTIFICATION Error Subcode for BGP_ERROR_CEASE  (RFC 4486)
const (
	_ = iota
	BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED
	BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
	BGP_ERROR_SUB_PEER_DECONFIGURED
	BGP_ERROR_SUB_ADMINISTRATIVE_RESET
	BGP_ERROR_SUB_CONNECTION_REJECTED
	BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
	BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
	BGP_ERROR_SUB_OUT_OF_RESOURCES
)

// NOTIFICATION Error Subcode for BGP_ERROR_ROUTE_REFRESH
const (
	_ = iota
	BGP_ERROR_SUB_INVALID_MESSAGE_LENGTH
)

type NotificationErrorCode uint16

func (c NotificationErrorCode) String() string {
	code := uint8(uint16(c) >> 8)
	subcode := uint8(uint16(c) & 0xff)
	UNDEFINED := "undefined"
	codeStr := UNDEFINED
	subcodeList := []string{}
	switch code {
	case BGP_ERROR_MESSAGE_HEADER_ERROR:
		codeStr = "header"
		subcodeList = []string{
			UNDEFINED,
			"connection not synchronized",
			"bad message length",
			"bad message type"}
	case BGP_ERROR_OPEN_MESSAGE_ERROR:
		codeStr = "open"
		subcodeList = []string{
			UNDEFINED,
			"unsupported version number",
			"bad peer as",
			"bad bgp identifier",
			"unsupported optional parameter",
			"deprecated authentication failure",
			"unacceptable hold time",
			"unsupported capability"}
	case BGP_ERROR_UPDATE_MESSAGE_ERROR:
		codeStr = "update"
		subcodeList = []string{
			UNDEFINED,
			"malformed attribute list",
			"unrecognized well known attribute",
			"missing well known attribute",
			"attribute flags error",
			"attribute length error",
			"invalid origin attribute",
			"deprecated routing loop",
			"invalid next hop attribute",
			"optional attribute error",
			"invalid network field",
			"sub malformed as path"}
	case BGP_ERROR_HOLD_TIMER_EXPIRED:
		codeStr = "hold timer expired"
		subcodeList = []string{
			UNDEFINED,
			"hold timer expired"}
	case BGP_ERROR_FSM_ERROR:
		codeStr = "fsm"
		subcodeList = []string{
			UNDEFINED,
			"receive unexpected message in opensent state",
			"receive unexpected message in openconfirm state",
			"receive unexpected message in established state"}
	case BGP_ERROR_CEASE:
		codeStr = "cease"
		subcodeList = []string{
			UNDEFINED,
			"maximum number of prefixes reached",
			"administrative shutdown",
			"peer deconfigured",
			"administrative reset",
			"connection rejected",
			"other configuration change",
			"connection collision resolution",
			"out of resources"}
	case BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR:
		codeStr = "route refresh"
		subcodeList = []string{"invalid message length"}
	}
	subcodeStr := func(idx uint8, l []string) string {
		if len(l) == 0 || int(idx) > len(l)-1 {
			return UNDEFINED
		}
		return l[idx]
	}(subcode, subcodeList)
	return fmt.Sprintf("code %v(%v) subcode %v(%v)", code, codeStr, subcode, subcodeStr)
}

func NewNotificationErrorCode(code, subcode uint8) NotificationErrorCode {
	return NotificationErrorCode(uint16(code)<<8 | uint16(subcode))
}

var pathAttrFlags map[BGPAttrType]BGPAttrFlag = map[BGPAttrType]BGPAttrFlag{
	BGP_ATTR_TYPE_ORIGIN:               BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_AS_PATH:              BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_NEXT_HOP:             BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_MULTI_EXIT_DISC:      BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_LOCAL_PREF:           BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_ATOMIC_AGGREGATE:     BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_AGGREGATOR:           BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_COMMUNITIES:          BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_ORIGINATOR_ID:        BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_CLUSTER_LIST:         BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_MP_REACH_NLRI:        BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_MP_UNREACH_NLRI:      BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_EXTENDED_COMMUNITIES: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_AS4_PATH:             BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_AS4_AGGREGATOR:       BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_PMSI_TUNNEL:          BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_TUNNEL_ENCAP:         BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_AIGP:                 BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_OPAQUE_VALUE:         BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
}

type PathAttributeInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	Len() int
	getFlags() BGPAttrFlag
	GetType() BGPAttrType
	String() string
	MarshalJSON() ([]byte, error)
}

type PathAttribute struct {
	Flags  BGPAttrFlag
	Type   BGPAttrType
	Length uint16
	Value  []byte
}

func (p *PathAttribute) Len() int {
	if p.Length == 0 {
		p.Length = uint16(len(p.Value))
	}
	l := 2 + p.Length
	if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
		l += 2
	} else {
		l += 1
	}
	return int(l)
}

func (p *PathAttribute) getFlags() BGPAttrFlag {
	return p.Flags
}

func (p *PathAttribute) GetType() BGPAttrType {
	return p.Type
}

func (p *PathAttribute) DecodeFromBytes(data []byte) error {
	odata := data
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, data, "attribute header length is short")
	}
	p.Flags = BGPAttrFlag(data[0])
	p.Type = BGPAttrType(data[1])

	if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
		if len(data) < 4 {
			return NewMessageError(eCode, eSubCode, data, "attribute header length is short")
		}
		p.Length = binary.BigEndian.Uint16(data[2:4])
		data = data[4:]
	} else {
		if len(data) < 3 {
			return NewMessageError(eCode, eSubCode, data, "attribute header length is short")
		}
		p.Length = uint16(data[2])
		data = data[3:]
	}
	if len(data) < int(p.Length) {
		return NewMessageError(eCode, eSubCode, data, "attribute value length is short")
	}
	if len(data[:p.Length]) > 0 {
		p.Value = data[:p.Length]
	}

	ok, eMsg := ValidateFlags(p.Type, p.Flags)
	if !ok {
		return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, odata[:p.Len()], eMsg)
	}
	return nil
}

func (p *PathAttribute) Serialize() ([]byte, error) {
	p.Length = uint16(len(p.Value))
	if p.Length > 255 {
		p.Flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
	} else {
		p.Flags &^= BGP_ATTR_FLAG_EXTENDED_LENGTH
	}
	buf := make([]byte, p.Len())
	buf[0] = uint8(p.Flags)
	buf[1] = uint8(p.Type)
	if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
		binary.BigEndian.PutUint16(buf[2:4], p.Length)
		copy(buf[4:], p.Value)
	} else {
		buf[2] = byte(p.Length)
		copy(buf[3:], p.Value)
	}
	return buf, nil
}

func (p *PathAttribute) String() string {
	return fmt.Sprintf("%s %s %s", p.Type.String(), p.Flags, []byte(p.Value))
}

func (p *PathAttribute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value []byte      `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

type PathAttributeOrigin struct {
	PathAttribute
}

func (p *PathAttributeOrigin) String() string {
	typ := "-"
	switch p.Value[0] {
	case BGP_ORIGIN_ATTR_TYPE_IGP:
		typ = "i"
	case BGP_ORIGIN_ATTR_TYPE_EGP:
		typ = "e"
	case BGP_ORIGIN_ATTR_TYPE_INCOMPLETE:
		typ = "?"
	}
	return fmt.Sprintf("{Origin: %s}", typ)
}

func (p *PathAttributeOrigin) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value uint8       `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value[0],
	})
}

func NewPathAttributeOrigin(value uint8) *PathAttributeOrigin {
	t := BGP_ATTR_TYPE_ORIGIN
	return &PathAttributeOrigin{

		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
			Value: []byte{byte(value)},
		},
	}
}

type AsPathParamFormat struct {
	start     string
	end       string
	separator string
}

var asPathParamFormatMap = map[uint8]*AsPathParamFormat{
	BGP_ASPATH_ATTR_TYPE_SET:        &AsPathParamFormat{"{", "}", ","},
	BGP_ASPATH_ATTR_TYPE_SEQ:        &AsPathParamFormat{"", "", " "},
	BGP_ASPATH_ATTR_TYPE_CONFED_SET: &AsPathParamFormat{"(", ")", " "},
	BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: &AsPathParamFormat{"[", "]", ","},
}

type AsPathParamInterface interface {
	Serialize() ([]byte, error)
	DecodeFromBytes([]byte) error
	Len() int
	ASLen() int
	MarshalJSON() ([]byte, error)
	String() string
}

type AsPathParam struct {
	Type uint8
	Num  uint8
	AS   []uint16
}

func (a *AsPathParam) Serialize() ([]byte, error) {
	buf := make([]byte, 2+len(a.AS)*2)
	buf[0] = uint8(a.Type)
	buf[1] = a.Num
	for j, as := range a.AS {
		binary.BigEndian.PutUint16(buf[2+j*2:], as)
	}
	return buf, nil
}

func (a *AsPathParam) DecodeFromBytes(data []byte) error {
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "AS param header length is short")
	}
	a.Type = data[0]
	a.Num = data[1]
	data = data[2:]
	if len(data) < int(a.Num*2) {
		return NewMessageError(eCode, eSubCode, nil, "AS param data length is short")
	}
	for i := 0; i < int(a.Num); i++ {
		a.AS = append(a.AS, binary.BigEndian.Uint16(data))
		data = data[2:]
	}
	return nil
}

func (a *AsPathParam) Len() int {
	return 2 + len(a.AS)*2
}

func (a *AsPathParam) ASLen() int {
	switch a.Type {
	case BGP_ASPATH_ATTR_TYPE_SEQ:
		return len(a.AS)
	case BGP_ASPATH_ATTR_TYPE_SET:
		return 1
	case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
		return 0
	}
	return 0
}

func (a *AsPathParam) String() string {
	format, ok := asPathParamFormatMap[a.Type]
	if !ok {
		return fmt.Sprintf("%v", a.AS)
	}
	aspath := make([]string, 0, len(a.AS))
	for _, asn := range a.AS {
		aspath = append(aspath, fmt.Sprintf("%d", asn))
	}
	s := bytes.NewBuffer(make([]byte, 0, 32))
	s.WriteString(format.start)
	s.WriteString(strings.Join(aspath, format.separator))
	s.WriteString(format.end)
	return s.String()
}

func (a *AsPathParam) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type uint8    `json:"segment_type"`
		Num  uint8    `json:"num"`
		AS   []uint16 `json:"asns"`
	}{
		Type: a.Type,
		Num:  a.Num,
		AS:   a.AS,
	})
}

func NewAsPathParam(segType uint8, as []uint16) *AsPathParam {
	return &AsPathParam{
		Type: segType,
		Num:  uint8(len(as)),
		AS:   as,
	}
}

type As4PathParam struct {
	Type uint8
	Num  uint8
	AS   []uint32
}

func (a *As4PathParam) Serialize() ([]byte, error) {
	buf := make([]byte, 2+len(a.AS)*4)
	buf[0] = a.Type
	buf[1] = a.Num
	for j, as := range a.AS {
		binary.BigEndian.PutUint32(buf[2+j*4:], as)
	}
	return buf, nil
}

func (a *As4PathParam) DecodeFromBytes(data []byte) error {
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "AS4 param header length is short")
	}
	a.Type = data[0]
	a.Num = data[1]
	data = data[2:]
	if len(data) < int(a.Num)*4 {
		return NewMessageError(eCode, eSubCode, nil, "AS4 param data length is short")
	}
	for i := 0; i < int(a.Num); i++ {
		a.AS = append(a.AS, binary.BigEndian.Uint32(data))
		data = data[4:]
	}
	return nil
}

func (a *As4PathParam) Len() int {
	return 2 + len(a.AS)*4
}

func (a *As4PathParam) ASLen() int {
	switch a.Type {
	case BGP_ASPATH_ATTR_TYPE_SEQ:
		return len(a.AS)
	case BGP_ASPATH_ATTR_TYPE_SET:
		return 1
	case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
		return 0
	}
	return 0
}

func (a *As4PathParam) String() string {
	format, ok := asPathParamFormatMap[a.Type]
	if !ok {
		return fmt.Sprintf("%v", a.AS)
	}
	aspath := make([]string, 0, len(a.AS))
	for _, asn := range a.AS {
		aspath = append(aspath, fmt.Sprintf("%d", asn))
	}
	s := bytes.NewBuffer(make([]byte, 0, 32))
	s.WriteString(format.start)
	s.WriteString(strings.Join(aspath, format.separator))
	s.WriteString(format.end)
	return s.String()
}

func (a *As4PathParam) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type uint8    `json:"segment_type"`
		Num  uint8    `json:"num"`
		AS   []uint32 `json:"asns"`
	}{
		Type: a.Type,
		Num:  a.Num,
		AS:   a.AS,
	})
}

func NewAs4PathParam(segType uint8, as []uint32) *As4PathParam {
	return &As4PathParam{
		Type: segType,
		Num:  uint8(len(as)),
		AS:   as,
	}
}

type DefaultAsPath struct {
}

func (p *DefaultAsPath) isValidAspath(data []byte) (bool, error) {
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
	if len(data)%2 != 0 {
		return false, NewMessageError(eCode, eSubCode, nil, "AS PATH length is not odd")
	}

	tryParse := func(data []byte, use4byte bool) (bool, error) {
		for len(data) > 0 {
			if len(data) < 2 {
				return false, NewMessageError(eCode, eSubCode, nil, "AS PATH header is short")
			}
			segType := data[0]
			if segType == 0 || segType > 4 {
				return false, NewMessageError(eCode, eSubCode, nil, "unknown AS_PATH seg type")
			}
			asNum := data[1]
			data = data[2:]
			if asNum == 0 || int(asNum) > math.MaxUint8 {
				return false, NewMessageError(eCode, eSubCode, nil, "AS PATH the number of AS is incorrect")
			}
			segLength := int(asNum)
			if use4byte == true {
				segLength *= 4
			} else {
				segLength *= 2
			}
			if int(segLength) > len(data) {
				return false, NewMessageError(eCode, eSubCode, nil, "seg length is short")
			}
			data = data[segLength:]
		}
		return true, nil
	}
	_, err := tryParse(data, true)
	if err == nil {
		return true, nil
	}

	_, err = tryParse(data, false)
	if err == nil {
		return false, nil
	}
	return false, NewMessageError(eCode, eSubCode, nil, "can't parse AS_PATH")
}

type PathAttributeAsPath struct {
	DefaultAsPath
	PathAttribute
	Value []AsPathParamInterface
}

func (p *PathAttributeAsPath) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if p.PathAttribute.Length == 0 {
		// ibgp or something
		return nil
	}
	as4Bytes, err := p.DefaultAsPath.isValidAspath(p.PathAttribute.Value)
	if err != nil {
		err.(*MessageError).Data = data[:p.Len()]
		return err
	}
	v := p.PathAttribute.Value
	for len(v) > 0 {
		var tuple AsPathParamInterface
		if as4Bytes == true {
			tuple = &As4PathParam{}
		} else {
			tuple = &AsPathParam{}
		}
		err := tuple.DecodeFromBytes(v)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, tuple)
		if tuple.Len() > len(v) {

		}
		v = v[tuple.Len():]
	}
	return nil
}

func (p *PathAttributeAsPath) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	for _, v := range p.Value {
		vbuf, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, vbuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeAsPath) String() string {
	params := make([]string, 0, len(p.Value))
	for _, param := range p.Value {
		params = append(params, param.String())
	}
	return strings.Join(params, " ")
}

func (p *PathAttributeAsPath) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType            `json:"type"`
		Value []AsPathParamInterface `json:"as_paths"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeAsPath(value []AsPathParamInterface) *PathAttributeAsPath {
	t := BGP_ATTR_TYPE_AS_PATH
	return &PathAttributeAsPath{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: value,
	}
}

type PathAttributeNextHop struct {
	PathAttribute
	Value net.IP
}

func (p *PathAttributeNextHop) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) != 4 && len(p.PathAttribute.Value) != 16 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "nexthop length isn't correct")
	}
	p.Value = p.PathAttribute.Value
	return nil
}

func (p *PathAttributeNextHop) Serialize() ([]byte, error) {
	p.PathAttribute.Value = p.Value
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeNextHop) String() string {
	return fmt.Sprintf("{Nexthop: %s}", p.Value)
}

func (p *PathAttributeNextHop) MarshalJSON() ([]byte, error) {
	value := "0.0.0.0"
	if p.Value != nil {
		value = p.Value.String()
	}
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value string      `json:"nexthop"`
	}{
		Type:  p.GetType(),
		Value: value,
	})
}

func NewPathAttributeNextHop(value string) *PathAttributeNextHop {
	t := BGP_ATTR_TYPE_NEXT_HOP
	return &PathAttributeNextHop{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: net.ParseIP(value).To4(),
	}
}

type PathAttributeMultiExitDisc struct {
	PathAttribute
	Value uint32
}

func (p *PathAttributeMultiExitDisc) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) != 4 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "med length isn't correct")
	}
	p.Value = binary.BigEndian.Uint32(p.PathAttribute.Value)
	return nil
}

func (p *PathAttributeMultiExitDisc) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, p.Value)
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeMultiExitDisc) String() string {
	return fmt.Sprintf("{Med: %d}", p.Value)
}

func (p *PathAttributeMultiExitDisc) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value uint32      `json:"metric"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeMultiExitDisc(value uint32) *PathAttributeMultiExitDisc {
	t := BGP_ATTR_TYPE_MULTI_EXIT_DISC
	return &PathAttributeMultiExitDisc{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: value,
	}
}

type PathAttributeLocalPref struct {
	PathAttribute
	Value uint32
}

func (p *PathAttributeLocalPref) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) != 4 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "local pref length isn't correct")
	}
	p.Value = binary.BigEndian.Uint32(p.PathAttribute.Value)
	return nil
}

func (p *PathAttributeLocalPref) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, p.Value)
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeLocalPref) String() string {
	return fmt.Sprintf("{LocalPref: %d}", p.Value)
}

func (p *PathAttributeLocalPref) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value uint32      `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeLocalPref(value uint32) *PathAttributeLocalPref {
	t := BGP_ATTR_TYPE_LOCAL_PREF
	return &PathAttributeLocalPref{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: value,
	}
}

type PathAttributeAtomicAggregate struct {
	PathAttribute
}

func (p *PathAttributeAtomicAggregate) String() string {
	return "{AtomicAggregate}"
}

func (p *PathAttributeAtomicAggregate) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type BGPAttrType `json:"type"`
	}{
		Type: p.GetType(),
	})
}

func NewPathAttributeAtomicAggregate() *PathAttributeAtomicAggregate {
	t := BGP_ATTR_TYPE_ATOMIC_AGGREGATE
	return &PathAttributeAtomicAggregate{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
	}
}

type PathAttributeAggregatorParam struct {
	AS      uint32
	askind  reflect.Kind
	Address net.IP
}

type PathAttributeAggregator struct {
	PathAttribute
	Value PathAttributeAggregatorParam
}

func (p *PathAttributeAggregator) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) != 6 && len(p.PathAttribute.Value) != 8 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "aggregator length isn't correct")
	}
	if len(p.PathAttribute.Value) == 6 {
		p.Value.AS = uint32(binary.BigEndian.Uint16(p.PathAttribute.Value[0:2]))
		p.Value.Address = p.PathAttribute.Value[2:]
		p.Value.askind = reflect.Uint16
	} else {
		p.Value.AS = binary.BigEndian.Uint32(p.PathAttribute.Value[0:4])
		p.Value.Address = p.PathAttribute.Value[4:]
		p.Value.askind = reflect.Uint32
	}
	return nil
}

func (p *PathAttributeAggregator) Serialize() ([]byte, error) {
	var buf []byte
	switch p.Value.askind {
	case reflect.Uint16:
		buf = make([]byte, 6)
		binary.BigEndian.PutUint16(buf, uint16(p.Value.AS))
		copy(buf[2:], p.Value.Address)
	case reflect.Uint32:
		buf = make([]byte, 8)
		binary.BigEndian.PutUint32(buf, p.Value.AS)
		copy(buf[4:], p.Value.Address)
	}

	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeAggregator) String() string {
	return fmt.Sprintf("{Aggregate: {AS: %d, Address: %s}}", p.Value.AS, p.Value.Address)
}

func (p *PathAttributeAggregator) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type    BGPAttrType `json:"type"`
		AS      uint32      `json:"as"`
		Address string      `json:"address"`
	}{
		Type:    p.GetType(),
		AS:      p.Value.AS,
		Address: p.Value.Address.String(),
	})
}

func NewPathAttributeAggregator(as interface{}, address string) *PathAttributeAggregator {
	v := reflect.ValueOf(as)
	t := BGP_ATTR_TYPE_AGGREGATOR
	return &PathAttributeAggregator{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: PathAttributeAggregatorParam{
			AS:      uint32(v.Uint()),
			askind:  v.Kind(),
			Address: net.ParseIP(address).To4(),
		},
	}
}

type PathAttributeCommunities struct {
	PathAttribute
	Value []uint32
}

func (p *PathAttributeCommunities) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value)%4 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "communities length isn't correct")
	}
	value := p.PathAttribute.Value
	for len(value) >= 4 {
		p.Value = append(p.Value, binary.BigEndian.Uint32(value))
		value = value[4:]
	}
	return nil
}

func (p *PathAttributeCommunities) Serialize() ([]byte, error) {
	buf := make([]byte, len(p.Value)*4)
	for i, v := range p.Value {
		binary.BigEndian.PutUint32(buf[i*4:], v)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

type WellKnownCommunity uint32

const (
	COMMUNITY_INTERNET                   WellKnownCommunity = 0x00000000
	COMMUNITY_PLANNED_SHUT                                  = 0xffff0000
	COMMUNITY_ACCEPT_OWN                                    = 0xffff0001
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v4                    = 0xffff0002
	COMMUNITY_ROUTE_FILTER_v4                               = 0xffff0003
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v6                    = 0xffff0004
	COMMUNITY_ROUTE_FILTER_v6                               = 0xffff0005
	COMMUNITY_LLGR_STALE                                    = 0xffff0006
	COMMUNITY_NO_LLGR                                       = 0xffff0007
	COMMUNITY_NO_EXPORT                                     = 0xffffff01
	COMMUNITY_NO_ADVERTISE                                  = 0xffffff02
	COMMUNITY_NO_EXPORT_SUBCONFED                           = 0xffffff03
	COMMUNITY_NO_PEER                                       = 0xffffff04
)

var WellKnownCommunityNameMap = map[WellKnownCommunity]string{
	COMMUNITY_INTERNET:                   "internet",
	COMMUNITY_PLANNED_SHUT:               "planned-shut",
	COMMUNITY_ACCEPT_OWN:                 "accept-own",
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: "route-filter-translated-v4",
	COMMUNITY_ROUTE_FILTER_v4:            "route-filter-v4",
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: "route-filter-translated-v6",
	COMMUNITY_ROUTE_FILTER_v6:            "route-filter-v6",
	COMMUNITY_LLGR_STALE:                 "llgr-stale",
	COMMUNITY_NO_LLGR:                    "no-llgr",
	COMMUNITY_NO_EXPORT:                  "no-export",
	COMMUNITY_NO_ADVERTISE:               "no-advertise",
	COMMUNITY_NO_EXPORT_SUBCONFED:        "no-export-subconfed",
	COMMUNITY_NO_PEER:                    "no-peer",
}

var WellKnownCommunityValueMap = map[string]WellKnownCommunity{
	WellKnownCommunityNameMap[COMMUNITY_INTERNET]:                   COMMUNITY_INTERNET,
	WellKnownCommunityNameMap[COMMUNITY_PLANNED_SHUT]:               COMMUNITY_PLANNED_SHUT,
	WellKnownCommunityNameMap[COMMUNITY_ACCEPT_OWN]:                 COMMUNITY_ACCEPT_OWN,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_TRANSLATED_v4]: COMMUNITY_ROUTE_FILTER_TRANSLATED_v4,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_v4]:            COMMUNITY_ROUTE_FILTER_v4,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_TRANSLATED_v6]: COMMUNITY_ROUTE_FILTER_TRANSLATED_v6,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_v6]:            COMMUNITY_ROUTE_FILTER_v6,
	WellKnownCommunityNameMap[COMMUNITY_LLGR_STALE]:                 COMMUNITY_LLGR_STALE,
	WellKnownCommunityNameMap[COMMUNITY_NO_LLGR]:                    COMMUNITY_NO_LLGR,
	WellKnownCommunityNameMap[COMMUNITY_NO_EXPORT]:                  COMMUNITY_NO_EXPORT,
	WellKnownCommunityNameMap[COMMUNITY_NO_ADVERTISE]:               COMMUNITY_NO_ADVERTISE,
	WellKnownCommunityNameMap[COMMUNITY_NO_EXPORT_SUBCONFED]:        COMMUNITY_NO_EXPORT_SUBCONFED,
	WellKnownCommunityNameMap[COMMUNITY_NO_PEER]:                    COMMUNITY_NO_PEER,
}

func (p *PathAttributeCommunities) String() string {
	l := []string{}
	for _, v := range p.Value {
		n, ok := WellKnownCommunityNameMap[WellKnownCommunity(v)]
		if ok {
			l = append(l, n)
		} else {
			l = append(l, fmt.Sprintf("%d:%d", (0xffff0000&v)>>16, 0xffff&v))
		}
	}
	return fmt.Sprintf("{Communities: %s}", strings.Join(l, ", "))
}

func (p *PathAttributeCommunities) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value []uint32    `json:"communities"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeCommunities(value []uint32) *PathAttributeCommunities {
	t := BGP_ATTR_TYPE_COMMUNITIES
	return &PathAttributeCommunities{
		PathAttribute{
			Flags:  pathAttrFlags[t],
			Type:   t,
			Length: 0,
			Value:  nil},
		value,
	}
}

type PathAttributeOriginatorId struct {
	PathAttribute
	Value net.IP
}

func (p *PathAttributeOriginatorId) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) != 4 && len(p.PathAttribute.Value) != 16 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "originatorid length isn't correct")
	}
	p.Value = p.PathAttribute.Value
	return nil
}

func (p *PathAttributeOriginatorId) String() string {
	return fmt.Sprintf("{Originator: %s}", p.Value)
}

func (p *PathAttributeOriginatorId) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value string      `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value.String(),
	})
}

func (p *PathAttributeOriginatorId) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	copy(buf, p.Value)
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func NewPathAttributeOriginatorId(value string) *PathAttributeOriginatorId {
	t := BGP_ATTR_TYPE_ORIGINATOR_ID
	return &PathAttributeOriginatorId{
		PathAttribute{
			Flags:  pathAttrFlags[t],
			Type:   t,
			Length: 0,
			Value:  nil},
		net.ParseIP(value).To4(),
	}
}

type PathAttributeClusterList struct {
	PathAttribute
	Value []net.IP
}

func (p *PathAttributeClusterList) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	value := p.PathAttribute.Value
	if len(p.PathAttribute.Value)%4 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "clusterlist length isn't correct")
	}
	for len(value) >= 4 {
		p.Value = append(p.Value, value[:4])
		value = value[4:]
	}
	return nil
}

func (p *PathAttributeClusterList) Serialize() ([]byte, error) {
	buf := make([]byte, len(p.Value)*4)
	for i, v := range p.Value {
		copy(buf[i*4:], v)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeClusterList) String() string {
	return fmt.Sprintf("{ClusterList: %v}", p.Value)
}

func (p *PathAttributeClusterList) MarshalJSON() ([]byte, error) {
	value := make([]string, 0, len(p.Value))
	for _, v := range p.Value {
		value = append(value, v.String())
	}
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value []string    `json:"value"`
	}{
		Type:  p.GetType(),
		Value: value,
	})
}

func NewPathAttributeClusterList(value []string) *PathAttributeClusterList {
	l := make([]net.IP, len(value))
	for i, v := range value {
		l[i] = net.ParseIP(v).To4()
	}
	t := BGP_ATTR_TYPE_CLUSTER_LIST
	return &PathAttributeClusterList{
		PathAttribute{
			Flags:  pathAttrFlags[t],
			Type:   t,
			Length: 0,
			Value:  nil},
		l,
	}
}

type PathAttributeMpReachNLRI struct {
	PathAttribute
	Nexthop          net.IP
	LinkLocalNexthop net.IP
	AFI              uint16
	SAFI             uint8
	Value            []AddrPrefixInterface
}

func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)

	value := p.PathAttribute.Value
	if len(value) < 3 {
		return NewMessageError(eCode, eSubCode, value, "mpreach header length is short")
	}
	afi := binary.BigEndian.Uint16(value[0:2])
	safi := value[2]
	p.AFI = afi
	p.SAFI = safi
	_, err = NewPrefixFromRouteFamily(afi, safi)
	if err != nil {
		return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
	}
	nexthopLen := value[3]
	if len(value) < 4+int(nexthopLen) {
		return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is short")
	}
	nexthopbin := value[4 : 4+nexthopLen]
	value = value[4+nexthopLen:]
	if nexthopLen > 0 {
		offset := 0
		if safi == SAFI_MPLS_VPN {
			offset = 8
		}
		addrlen := 4
		hasLinkLocal := false

		if afi == AFI_IP6 {
			addrlen = 16
			hasLinkLocal = len(nexthopbin) == offset+2*addrlen
		}

		isValid := len(nexthopbin) == offset+addrlen || hasLinkLocal

		if !isValid {
			return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is incorrect")
		}
		p.Nexthop = nexthopbin[offset : +offset+addrlen]
		if hasLinkLocal {
			p.LinkLocalNexthop = nexthopbin[offset+addrlen : offset+2*addrlen]
		}
	}
	// skip reserved
	if len(value) == 0 {
		return NewMessageError(eCode, eSubCode, value, "no skip byte")
	}
	value = value[1:]
	for len(value) > 0 {
		prefix, err := NewPrefixFromRouteFamily(afi, safi)
		if err != nil {
			return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
		}
		err = prefix.DecodeFromBytes(value)
		if err != nil {
			return err
		}
		if prefix.Len() > len(value) {
			return NewMessageError(eCode, eSubCode, value, "prefix length is incorrect")
		}
		value = value[prefix.Len():]
		p.Value = append(p.Value, prefix)
	}
	return nil
}

func (p *PathAttributeMpReachNLRI) Serialize() ([]byte, error) {
	afi := p.AFI
	safi := p.SAFI
	nexthoplen := 4
	if afi == AFI_IP6 {
		nexthoplen = 16
		if p.LinkLocalNexthop != nil {
			nexthoplen += 16
		}
	}
	offset := 0
	switch safi {
	case SAFI_MPLS_VPN:
		offset = 8
		nexthoplen += 8
	case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST:
		nexthoplen = 0
	}
	buf := make([]byte, 4+nexthoplen)
	binary.BigEndian.PutUint16(buf[0:], afi)
	buf[2] = safi
	buf[3] = uint8(nexthoplen)
	copy(buf[4+offset:], p.Nexthop)
	if p.LinkLocalNexthop != nil {
		copy(buf[4+offset+len(p.Nexthop):], p.LinkLocalNexthop)
	}
	buf = append(buf, make([]byte, 1)...)
	for _, prefix := range p.Value {
		pbuf, err := prefix.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, pbuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeMpReachNLRI) MarshalJSON() ([]byte, error) {
	nexthop := p.Nexthop.String()
	if p.Nexthop == nil {
		switch p.AFI {
		case AFI_IP:
			nexthop = "0.0.0.0"
		case AFI_IP6:
			nexthop = "::"
		default:
			nexthop = "fictitious"
		}
	}
	return json.Marshal(struct {
		Type    BGPAttrType           `json:"type"`
		Nexthop string                `json:"nexthop"`
		AFI     uint16                `json:"afi"`
		SAFI    uint8                 `json:"safi"`
		Value   []AddrPrefixInterface `json:"value"`
	}{
		Type:    p.GetType(),
		Nexthop: nexthop,
		AFI:     p.AFI,
		SAFI:    p.SAFI,
		Value:   p.Value,
	})
}

func (p *PathAttributeMpReachNLRI) String() string {
	return fmt.Sprintf("{MpReach(%s): {Nexthop: %s, NLRIs: %s}}", AfiSafiToRouteFamily(p.AFI, p.SAFI), p.Nexthop, p.Value)
}

func NewPathAttributeMpReachNLRI(nexthop string, nlri []AddrPrefixInterface) *PathAttributeMpReachNLRI {
	t := BGP_ATTR_TYPE_MP_REACH_NLRI
	ip := net.ParseIP(nexthop)
	if ip.To4() != nil {
		ip = ip.To4()
	}
	p := &PathAttributeMpReachNLRI{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Nexthop: ip,
		Value:   nlri,
	}
	if len(nlri) > 0 {
		p.AFI = nlri[0].AFI()
		p.SAFI = nlri[0].SAFI()
	}
	return p
}

type PathAttributeMpUnreachNLRI struct {
	PathAttribute
	AFI   uint16
	SAFI  uint8
	Value []AddrPrefixInterface
}

func (p *PathAttributeMpUnreachNLRI) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)

	value := p.PathAttribute.Value
	if len(value) < 3 {
		return NewMessageError(eCode, eSubCode, value, "unreach header length is incorrect")
	}
	afi := binary.BigEndian.Uint16(value[0:2])
	safi := value[2]
	_, err = NewPrefixFromRouteFamily(afi, safi)
	if err != nil {
		return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
	}
	value = value[3:]
	p.AFI = afi
	p.SAFI = safi
	for len(value) > 0 {
		prefix, err := NewPrefixFromRouteFamily(afi, safi)
		if err != nil {
			return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
		}
		err = prefix.DecodeFromBytes(value)
		if err != nil {
			return err
		}
		if prefix.Len() > len(value) {
			return NewMessageError(eCode, eSubCode, data[:p.PathAttribute.Len()], "prefix length is incorrect")
		}
		value = value[prefix.Len():]
		p.Value = append(p.Value, prefix)
	}
	return nil
}

func (p *PathAttributeMpUnreachNLRI) Serialize() ([]byte, error) {
	buf := make([]byte, 3)
	binary.BigEndian.PutUint16(buf, p.AFI)
	buf[2] = p.SAFI
	for _, prefix := range p.Value {
		pbuf, err := prefix.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, pbuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeMpUnreachNLRI) String() string {
	if len(p.Value) > 0 {
		return fmt.Sprintf("{MpUnreach(%s): {NLRIs: %s}}", AfiSafiToRouteFamily(p.AFI, p.SAFI), p.Value)
	}
	return fmt.Sprintf("{MpUnreach(%s): End-of-Rib}", AfiSafiToRouteFamily(p.AFI, p.SAFI))
}

func NewPathAttributeMpUnreachNLRI(nlri []AddrPrefixInterface) *PathAttributeMpUnreachNLRI {
	t := BGP_ATTR_TYPE_MP_UNREACH_NLRI
	p := &PathAttributeMpUnreachNLRI{
		PathAttribute: PathAttribute{
			Flags:  pathAttrFlags[t],
			Type:   t,
			Length: 0,
		},
		Value: nlri,
	}
	if len(nlri) > 0 {
		p.AFI = nlri[0].AFI()
		p.SAFI = nlri[0].SAFI()
	}
	return p
}

type ExtendedCommunityInterface interface {
	Serialize() ([]byte, error)
	String() string
	GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType)
	MarshalJSON() ([]byte, error)
}

type TwoOctetAsSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	AS           uint16
	LocalAdmin   uint32
	IsTransitive bool
}

func (e *TwoOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	binary.BigEndian.PutUint16(buf[2:], e.AS)
	binary.BigEndian.PutUint32(buf[4:], e.LocalAdmin)
	return buf, nil
}

func (e *TwoOctetAsSpecificExtended) String() string {
	return fmt.Sprintf("%d:%d", e.AS, e.LocalAdmin)
}

func (e *TwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *TwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC
	}
	return t, e.SubType
}

func NewTwoOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint16, localAdmin uint32, isTransitive bool) *TwoOctetAsSpecificExtended {
	return &TwoOctetAsSpecificExtended{
		SubType:      subtype,
		AS:           as,
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}
}

type IPv4AddressSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	IPv4         net.IP
	LocalAdmin   uint16
	IsTransitive bool
}

func (e *IPv4AddressSpecificExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_IP4_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	copy(buf[2:6], e.IPv4)
	binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin)
	return buf, nil
}

func (e *IPv4AddressSpecificExtended) String() string {
	return fmt.Sprintf("%s:%d", e.IPv4.String(), e.LocalAdmin)
}

func (e *IPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *IPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_IP4_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC
	}
	return t, e.SubType
}

func NewIPv4AddressSpecificExtended(subtype ExtendedCommunityAttrSubType, ip string, localAdmin uint16, isTransitive bool) *IPv4AddressSpecificExtended {
	ipv4 := net.ParseIP(ip)
	if ipv4.To4() == nil {
		return nil
	}
	return &IPv4AddressSpecificExtended{
		SubType:      subtype,
		IPv4:         ipv4.To4(),
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}
}

type FourOctetAsSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	AS           uint32
	LocalAdmin   uint16
	IsTransitive bool
}

func (e *FourOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	binary.BigEndian.PutUint32(buf[2:], e.AS)
	binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin)
	return buf, nil
}

func (e *FourOctetAsSpecificExtended) String() string {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, e.AS)
	asUpper := binary.BigEndian.Uint16(buf[0:2])
	asLower := binary.BigEndian.Uint16(buf[2:])
	return fmt.Sprintf("%d.%d:%d", asUpper, asLower, e.LocalAdmin)
}

func (e *FourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *FourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC
	}
	return t, e.SubType
}

func NewFourOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint32, localAdmin uint16, isTransitive bool) *FourOctetAsSpecificExtended {
	return &FourOctetAsSpecificExtended{
		SubType:      subtype,
		AS:           as,
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}
}

func ParseExtendedCommunity(subtype ExtendedCommunityAttrSubType, com string) (ExtendedCommunityInterface, error) {
	if subtype == EC_SUBTYPE_ORIGIN_VALIDATION {
		var value ValidationState
		switch com {
		case VALIDATION_STATE_VALID.String():
			value = VALIDATION_STATE_VALID
		case VALIDATION_STATE_NOT_FOUND.String():
			value = VALIDATION_STATE_NOT_FOUND
		case VALIDATION_STATE_INVALID.String():
			value = VALIDATION_STATE_INVALID
		default:
			return nil, fmt.Errorf("invalid validation state")
		}
		return &OpaqueExtended{
			SubType: EC_SUBTYPE_ORIGIN_VALIDATION,
			Value: &ValidationExtended{
				Value: value,
			},
		}, nil
	}
	elems, err := parseRdAndRt(com)
	if err != nil {
		return nil, err
	}
	localAdmin, _ := strconv.Atoi(elems[9])
	ip := net.ParseIP(elems[1])
	isTransitive := true
	switch {
	case ip.To4() != nil:
		return NewIPv4AddressSpecificExtended(subtype, elems[1], uint16(localAdmin), isTransitive), nil
	case elems[6] == "" && elems[7] == "":
		asn, _ := strconv.Atoi(elems[8])
		return NewTwoOctetAsSpecificExtended(subtype, uint16(asn), uint32(localAdmin), isTransitive), nil
	default:
		fst, _ := strconv.Atoi(elems[7])
		snd, _ := strconv.Atoi(elems[8])
		asn := fst<<16 | snd
		return NewFourOctetAsSpecificExtended(subtype, uint32(asn), uint16(localAdmin), isTransitive), nil
	}
}

func ParseRouteTarget(rt string) (ExtendedCommunityInterface, error) {
	return ParseExtendedCommunity(EC_SUBTYPE_ROUTE_TARGET, rt)
}

type OpaqueExtendedValueInterface interface {
	Serialize() ([]byte, error)
	String() string
}

type DefaultOpaqueExtendedValue struct {
	Value []byte
}

func (v *DefaultOpaqueExtendedValue) Serialize() ([]byte, error) {
	v.Value = v.Value[:7]
	return v.Value[:7], nil
}

func (v *DefaultOpaqueExtendedValue) String() string {
	buf := make([]byte, 8)
	copy(buf[1:], v.Value)
	d := binary.BigEndian.Uint64(buf)
	return fmt.Sprintf("%d", d)
}

type ValidationState uint8

const (
	VALIDATION_STATE_VALID     ValidationState = 0
	VALIDATION_STATE_NOT_FOUND ValidationState = 1
	VALIDATION_STATE_INVALID   ValidationState = 2
)

func (s ValidationState) String() string {
	switch s {
	case VALIDATION_STATE_VALID:
		return "valid"
	case VALIDATION_STATE_NOT_FOUND:
		return "not-found"
	case VALIDATION_STATE_INVALID:
		return "invalid"
	}
	return fmt.Sprintf("unknown validatation state(%d)", s)
}

type ValidationExtended struct {
	Value ValidationState
}

func (e *ValidationExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 7)
	buf[0] = byte(EC_SUBTYPE_ORIGIN_VALIDATION)
	buf[6] = byte(e.Value)
	return buf, nil
}

func (e *ValidationExtended) String() string {
	return e.Value.String()
}

type ColorExtended struct {
	Value uint32
}

func (e *ColorExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 7)
	buf[0] = byte(EC_SUBTYPE_COLOR)
	binary.BigEndian.PutUint32(buf[3:], uint32(e.Value))
	return buf, nil
}

func (e *ColorExtended) String() string {
	return fmt.Sprintf("%d", e.Value)
}

type EncapExtended struct {
	TunnelType TunnelType
}

func (e *EncapExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 7)
	buf[0] = byte(EC_SUBTYPE_ENCAPSULATION)
	binary.BigEndian.PutUint16(buf[5:], uint16(e.TunnelType))
	return buf, nil
}

func (e *EncapExtended) String() string {
	switch e.TunnelType {
	case TUNNEL_TYPE_L2TP3:
		return "L2TPv3 over IP"
	case TUNNEL_TYPE_GRE:
		return "GRE"
	case TUNNEL_TYPE_IP_IN_IP:
		return "IP in IP"
	case TUNNEL_TYPE_VXLAN:
		return "VXLAN"
	case TUNNEL_TYPE_NVGRE:
		return "NVGRE"
	case TUNNEL_TYPE_MPLS:
		return "MPLS"
	case TUNNEL_TYPE_MPLS_IN_GRE:
		return "MPLS in GRE"
	case TUNNEL_TYPE_VXLAN_GRE:
		return "VXLAN GRE"
	default:
		return fmt.Sprintf("tunnel: %d", e.TunnelType)
	}
}

type OpaqueExtended struct {
	IsTransitive bool
	Value        OpaqueExtendedValueInterface
	SubType      ExtendedCommunityAttrSubType
}

func (e *OpaqueExtended) DecodeFromBytes(data []byte) error {
	if len(data) != 7 {
		return fmt.Errorf("Invalid OpaqueExtended bytes len: %d", len(data))
	}
	e.SubType = ExtendedCommunityAttrSubType(data[0])

	if e.IsTransitive {
		switch e.SubType {
		case EC_SUBTYPE_COLOR:
			v := binary.BigEndian.Uint32(data[3:7])
			e.Value = &ColorExtended{
				Value: v,
			}
		case EC_SUBTYPE_ENCAPSULATION:
			t := TunnelType(binary.BigEndian.Uint16(data[5:7]))
			e.Value = &EncapExtended{
				TunnelType: t,
			}
		default:
			e.Value = &DefaultOpaqueExtendedValue{
				Value: data, //7byte
			}
		}
	} else {
		switch e.SubType {
		case EC_SUBTYPE_ORIGIN_VALIDATION:
			e.Value = &ValidationExtended{
				Value: ValidationState(data[6]),
			}
		default:
			e.Value = &DefaultOpaqueExtendedValue{
				Value: data, //7byte
			}
		}
	}
	return nil
}

func (e *OpaqueExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 1, 7)
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_OPAQUE)
	}
	bbuf, err := e.Value.Serialize()
	e.SubType = ExtendedCommunityAttrSubType(bbuf[0])
	if err != nil {
		return nil, err
	}
	buf = append(buf, bbuf...)
	return buf, nil
}

func (e *OpaqueExtended) String() string {
	return e.Value.String()
}

func (e *OpaqueExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   OpaqueExtendedValueInterface `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.Value,
	})
}

func (e *OpaqueExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_OPAQUE
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_OPAQUE
	}
	return t, e.SubType
}

func NewOpaqueExtended(isTransitive bool) *OpaqueExtended {
	return &OpaqueExtended{
		IsTransitive: isTransitive,
	}
}

type ESILabelExtended struct {
	Label          uint32
	IsSingleActive bool
}

func (e *ESILabelExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_ESI_LABEL)
	if e.IsSingleActive {
		buf[2] = byte(1)
	}
	buf[3] = 0
	buf[4] = 0
	buf[5] = byte((e.Label >> 16) & 0xff)
	buf[6] = byte((e.Label >> 8) & 0xff)
	buf[7] = byte(e.Label & 0xff)
	return buf, nil
}

func (e *ESILabelExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString(fmt.Sprintf("esi-label: %d", e.Label))
	if e.IsSingleActive {
		buf.WriteString(", single-active")
	}
	return buf.String()
}

func (e *ESILabelExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type           ExtendedCommunityAttrType    `json:"type"`
		Subtype        ExtendedCommunityAttrSubType `json:"subtype"`
		Label          uint32                       `json:"label"`
		IsSingleActive bool                         `json:"is_single_active"`
	}{
		Type:           t,
		Subtype:        s,
		Label:          e.Label,
		IsSingleActive: e.IsSingleActive,
	})
}

func (e *ESILabelExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_ESI_LABEL
}

func NewESILabelExtended(label uint32, isSingleActive bool) *ESILabelExtended {
	return &ESILabelExtended{
		Label:          label,
		IsSingleActive: isSingleActive,
	}
}

type ESImportRouteTarget struct {
	ESImport net.HardwareAddr
}

func (e *ESImportRouteTarget) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_ES_IMPORT)
	copy(buf[2:], e.ESImport)
	return buf, nil
}

func (e *ESImportRouteTarget) String() string {
	return fmt.Sprintf("es-import rt: %s", e.ESImport.String())
}

func (e *ESImportRouteTarget) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.ESImport.String(),
	})
}

func (e *ESImportRouteTarget) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_ES_IMPORT
}

func NewESImportRouteTarget(mac string) *ESImportRouteTarget {
	esImport, err := net.ParseMAC(mac)
	if err != nil {
		return nil
	}
	return &ESImportRouteTarget{
		ESImport: esImport,
	}
}

type MacMobilityExtended struct {
	Sequence uint32
	IsSticky bool
}

func (e *MacMobilityExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_MAC_MOBILITY)
	if e.IsSticky {
		buf[2] = byte(1)
	}
	binary.BigEndian.PutUint32(buf[4:], e.Sequence)
	return buf, nil
}

func (e *MacMobilityExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString(fmt.Sprintf("mac-mobility: %d", e.Sequence))
	if e.IsSticky {
		buf.WriteString(", sticky")
	}
	return buf.String()
}

func (e *MacMobilityExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type     ExtendedCommunityAttrType    `json:"type"`
		Subtype  ExtendedCommunityAttrSubType `json:"subtype"`
		Sequence uint32                       `json:"sequence"`
		IsSticky bool                         `json:"is_sticky"`
	}{
		Type:     t,
		Subtype:  s,
		Sequence: e.Sequence,
		IsSticky: e.IsSticky,
	})
}

func (e *MacMobilityExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_MAC_MOBILITY
}

func NewMacMobilityExtended(seq uint32, isSticky bool) *MacMobilityExtended {
	return &MacMobilityExtended{
		Sequence: seq,
		IsSticky: isSticky,
	}
}

func parseEvpnExtended(data []byte) (ExtendedCommunityInterface, error) {
	if ExtendedCommunityAttrType(data[0]) != EC_TYPE_EVPN {
		return nil, fmt.Errorf("ext comm type is not EC_TYPE_EVPN: %d", data[0])
	}
	subType := ExtendedCommunityAttrSubType(data[1])
	switch subType {
	case EC_SUBTYPE_ESI_LABEL:
		var isSingleActive bool
		if data[2] > 0 {
			isSingleActive = true
		}
		label := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
		return &ESILabelExtended{
			IsSingleActive: isSingleActive,
			Label:          label,
		}, nil
	case EC_SUBTYPE_ES_IMPORT:
		return &ESImportRouteTarget{
			ESImport: net.HardwareAddr(data[2:8]),
		}, nil
	case EC_SUBTYPE_MAC_MOBILITY:
		var isSticky bool
		if data[2] > 0 {
			isSticky = true
		}
		seq := binary.BigEndian.Uint32(data[4:8])
		return &MacMobilityExtended{
			Sequence: seq,
			IsSticky: isSticky,
		}, nil
	}
	return nil, fmt.Errorf("unknown evpn subtype: %d", subType)
}

type TrafficRateExtended struct {
	AS   uint16
	Rate float32
}

func (e *TrafficRateExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE)
	binary.BigEndian.PutUint16(buf[2:4], e.AS)
	binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(e.Rate))
	return buf, nil
}

func (e *TrafficRateExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	if e.Rate == 0 {
		buf.WriteString("discard")
	} else {
		buf.WriteString(fmt.Sprintf("rate: %f", e.Rate))
	}
	if e.AS != 0 {
		buf.WriteString(fmt.Sprintf("(as: %d)", e.AS))
	}
	return buf.String()
}

func (e *TrafficRateExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		As      uint16                       `json:"as"`
		Rate    float32                      `json:"rate"`
	}{t, s, e.AS, e.Rate})
}

func (e *TrafficRateExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE
}

func NewTrafficRateExtended(as uint16, rate float32) *TrafficRateExtended {
	return &TrafficRateExtended{as, rate}
}

type TrafficActionExtended struct {
	Terminal bool
	Sample   bool
}

func (e *TrafficActionExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION)
	if e.Terminal {
		buf[7] = 0x01
	}
	if e.Sample {
		buf[7] = buf[7] | 0x2
	}
	return buf, nil
}

func (e *TrafficActionExtended) String() string {
	ss := make([]string, 0, 2)
	if e.Terminal {
		ss = append(ss, "terminal")
	}
	if e.Sample {
		ss = append(ss, "sample")
	}
	return fmt.Sprintf("action: %s", strings.Join(ss, "-"))
}

func (e *TrafficActionExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type     ExtendedCommunityAttrType    `json:"type"`
		Subtype  ExtendedCommunityAttrSubType `json:"subtype"`
		Terminal bool                         `json:"terminal"`
		Sample   bool                         `json:"sample"`
	}{t, s, e.Terminal, e.Sample})
}

func (e *TrafficActionExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION
}

func NewTrafficActionExtended(terminal bool, sample bool) *TrafficActionExtended {
	return &TrafficActionExtended{terminal, sample}
}

type RedirectTwoOctetAsSpecificExtended struct {
	TwoOctetAsSpecificExtended
}

func (e *RedirectTwoOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.TwoOctetAsSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT)
	return buf, err
}

func (e *RedirectTwoOctetAsSpecificExtended) String() string {
	return fmt.Sprintf("redirect: %s", e.TwoOctetAsSpecificExtended.String())
}

func (e *RedirectTwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.TwoOctetAsSpecificExtended.String()})
}

func (e *RedirectTwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_REDIRECT
}

func NewRedirectTwoOctetAsSpecificExtended(as uint16, localAdmin uint32) *RedirectTwoOctetAsSpecificExtended {
	return &RedirectTwoOctetAsSpecificExtended{*NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)}
}

type RedirectIPv4AddressSpecificExtended struct {
	IPv4AddressSpecificExtended
}

func (e *RedirectIPv4AddressSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.IPv4AddressSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT)
	return buf, err
}

func (e *RedirectIPv4AddressSpecificExtended) String() string {
	return fmt.Sprintf("redirect: %s", e.IPv4AddressSpecificExtended.String())
}

func (e *RedirectIPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.IPv4AddressSpecificExtended.String()})
}

func (e *RedirectIPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_SUBTYPE_FLOWSPEC_REDIRECT
}

func NewRedirectIPv4AddressSpecificExtended(ipv4 string, localAdmin uint16) *RedirectIPv4AddressSpecificExtended {
	return &RedirectIPv4AddressSpecificExtended{*NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv4, localAdmin, false)}
}

type RedirectFourOctetAsSpecificExtended struct {
	FourOctetAsSpecificExtended
}

func (e *RedirectFourOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.FourOctetAsSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT)
	return buf, err
}

func (e *RedirectFourOctetAsSpecificExtended) String() string {
	return fmt.Sprintf("redirect: %s", e.FourOctetAsSpecificExtended.String())
}

func (e *RedirectFourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.FourOctetAsSpecificExtended.String()})
}

func (e *RedirectFourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3, EC_SUBTYPE_FLOWSPEC_REDIRECT
}

func NewRedirectFourOctetAsSpecificExtended(as uint32, localAdmin uint16) *RedirectFourOctetAsSpecificExtended {
	return &RedirectFourOctetAsSpecificExtended{*NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)}
}

type TrafficRemarkExtended struct {
	DSCP uint8
}

func (e *TrafficRemarkExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK)
	buf[7] = byte(e.DSCP)
	return buf, nil
}

func (e *TrafficRemarkExtended) String() string {
	return fmt.Sprintf("remark: %d", e.DSCP)
}

func (e *TrafficRemarkExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   uint8                        `json:"value"`
	}{t, s, e.DSCP})
}

func (e *TrafficRemarkExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK
}

func NewTrafficRemarkExtended(dscp uint8) *TrafficRemarkExtended {
	return &TrafficRemarkExtended{dscp}
}

func parseFlowSpecExtended(data []byte) (ExtendedCommunityInterface, error) {
	typ := ExtendedCommunityAttrType(data[0])
	if typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 {
		return nil, fmt.Errorf("ext comm type is not EC_TYPE_FLOWSPEC: %d", data[0])
	}
	subType := ExtendedCommunityAttrSubType(data[1])
	switch subType {
	case EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE:
		asn := binary.BigEndian.Uint16(data[2:4])
		bits := binary.BigEndian.Uint32(data[4:8])
		rate := math.Float32frombits(bits)
		return NewTrafficRateExtended(asn, rate), nil
	case EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION:
		terminal := data[7]&0x1 == 1
		sample := (data[7]>>1)&0x1 == 1
		return NewTrafficActionExtended(terminal, sample), nil
	case EC_SUBTYPE_FLOWSPEC_REDIRECT:
		// RFC7674
		switch typ {
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL:
			as := binary.BigEndian.Uint16(data[2:4])
			localAdmin := binary.BigEndian.Uint32(data[4:8])
			return NewRedirectTwoOctetAsSpecificExtended(as, localAdmin), nil
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2:
			ipv4 := net.IP(data[2:6]).String()
			localAdmin := binary.BigEndian.Uint16(data[6:8])
			return NewRedirectIPv4AddressSpecificExtended(ipv4, localAdmin), nil
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3:
			as := binary.BigEndian.Uint32(data[2:6])
			localAdmin := binary.BigEndian.Uint16(data[6:8])
			return NewRedirectFourOctetAsSpecificExtended(as, localAdmin), nil
		}
	case EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK:
		dscp := data[7]
		return NewTrafficRemarkExtended(dscp), nil
	}
	return &UnknownExtended{
		Type:  ExtendedCommunityAttrType(data[0]),
		Value: data[1:8],
	}, nil
}

type UnknownExtended struct {
	Type  ExtendedCommunityAttrType
	Value []byte
}

func (e *UnknownExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = uint8(e.Type)
	copy(buf[1:], e.Value)
	e.Value = buf[1:]
	return buf, nil
}

func (e *UnknownExtended) String() string {
	buf := make([]byte, 8)
	copy(buf[1:], e.Value)
	v := binary.BigEndian.Uint64(buf)
	return fmt.Sprintf("%d", v)
}

func (e *UnknownExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   []byte                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.Value,
	})
}

func (e *UnknownExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return ExtendedCommunityAttrType(0xFF), ExtendedCommunityAttrSubType(0xFF)
}

type PathAttributeExtendedCommunities struct {
	PathAttribute
	Value []ExtendedCommunityInterface
}

func ParseExtended(data []byte) (ExtendedCommunityInterface, error) {
	if len(data) < 8 {
		return nil, fmt.Errorf("not all extended community bytes are available")
	}
	attrType := ExtendedCommunityAttrType(data[0])
	subtype := ExtendedCommunityAttrSubType(data[1])
	transitive := false
	switch attrType {
	case EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC:
		as := binary.BigEndian.Uint16(data[2:4])
		localAdmin := binary.BigEndian.Uint32(data[4:8])
		return NewTwoOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil
	case EC_TYPE_TRANSITIVE_IP4_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC:
		ipv4 := net.IP(data[2:6]).String()
		localAdmin := binary.BigEndian.Uint16(data[6:8])
		return NewIPv4AddressSpecificExtended(subtype, ipv4, localAdmin, transitive), nil
	case EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC:
		as := binary.BigEndian.Uint32(data[2:6])
		localAdmin := binary.BigEndian.Uint16(data[6:8])
		return NewFourOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil
	case EC_TYPE_TRANSITIVE_OPAQUE:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_OPAQUE:
		e := NewOpaqueExtended(transitive)
		err := e.DecodeFromBytes(data[1:8])
		return e, err
	case EC_TYPE_EVPN:
		return parseEvpnExtended(data)
	case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3:
		return parseFlowSpecExtended(data)
	default:
		return &UnknownExtended{
			Type:  ExtendedCommunityAttrType(data[0]),
			Value: data[1:8],
		}, nil
	}
}

func (p *PathAttributeExtendedCommunities) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value)%8 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "extendedcommunities length isn't correct")
	}
	value := p.PathAttribute.Value
	for len(value) >= 8 {
		e, err := ParseExtended(value)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, e)
		value = value[8:]
	}
	return nil
}

func (p *PathAttributeExtendedCommunities) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	for _, p := range p.Value {
		ebuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, ebuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeExtendedCommunities) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	for idx, v := range p.Value {
		buf.WriteString("[")
		buf.WriteString(v.String())
		buf.WriteString("]")
		if idx < len(p.Value)-1 {
			buf.WriteString(", ")
		}
	}
	return fmt.Sprintf("{Extcomms: %s}", buf.String())
}

func (p *PathAttributeExtendedCommunities) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType                  `json:"type"`
		Value []ExtendedCommunityInterface `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeExtendedCommunities(value []ExtendedCommunityInterface) *PathAttributeExtendedCommunities {
	t := BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
	return &PathAttributeExtendedCommunities{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: value,
	}
}

type PathAttributeAs4Path struct {
	PathAttribute
	Value []*As4PathParam
	DefaultAsPath
}

func (p *PathAttributeAs4Path) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
	v := p.PathAttribute.Value
	as4Bytes, err := p.DefaultAsPath.isValidAspath(p.PathAttribute.Value)
	if err != nil {
		return err
	}
	if as4Bytes == false {
		return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed")
	}
	for len(v) > 0 {
		tuple := &As4PathParam{}
		tuple.DecodeFromBytes(v)
		p.Value = append(p.Value, tuple)
		if len(v) < tuple.Len() {
			return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed")
		}
		v = v[tuple.Len():]
	}
	return nil
}

func (p *PathAttributeAs4Path) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	for _, v := range p.Value {
		vbuf, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, vbuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeAs4Path) String() string {
	params := make([]string, 0, len(p.Value))
	for _, param := range p.Value {
		params = append(params, param.String())
	}
	return strings.Join(params, " ")
}

func NewPathAttributeAs4Path(value []*As4PathParam) *PathAttributeAs4Path {
	t := BGP_ATTR_TYPE_AS4_PATH
	return &PathAttributeAs4Path{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: value,
	}
}

type PathAttributeAs4Aggregator struct {
	PathAttribute
	Value PathAttributeAggregatorParam
}

func (p *PathAttributeAs4Aggregator) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) != 8 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "AS4 Aggregator length is incorrect")
	}
	p.Value.AS = binary.BigEndian.Uint32(p.PathAttribute.Value[0:4])
	p.Value.Address = p.PathAttribute.Value[4:]
	return nil
}

func (p *PathAttributeAs4Aggregator) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint32(buf[0:], p.Value.AS)
	copy(buf[4:], p.Value.Address.To4())
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func NewPathAttributeAs4Aggregator(as uint32, address string) *PathAttributeAs4Aggregator {
	t := BGP_ATTR_TYPE_AS4_AGGREGATOR
	return &PathAttributeAs4Aggregator{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: PathAttributeAggregatorParam{
			AS:      as,
			Address: net.ParseIP(address).To4(),
		},
	}
}

type TunnelEncapSubTLVValue interface {
	Serialize() ([]byte, error)
}

type TunnelEncapSubTLVDefault struct {
	Value []byte
}

func (t *TunnelEncapSubTLVDefault) Serialize() ([]byte, error) {
	return t.Value, nil
}

type TunnelEncapSubTLVEncapuslation struct {
	Key    uint32 // this represent both SessionID for L2TPv3 case and GRE-key for GRE case (RFC5512 4.)
	Cookie []byte
}

func (t *TunnelEncapSubTLVEncapuslation) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, t.Key)
	return append(buf, t.Cookie...), nil
}

type TunnelEncapSubTLVProtocol struct {
	Protocol uint16
}

func (t *TunnelEncapSubTLVProtocol) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	binary.BigEndian.PutUint16(buf, t.Protocol)
	return buf, nil
}

type TunnelEncapSubTLVColor struct {
	Color uint32
}

func (t *TunnelEncapSubTLVColor) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
	buf[1] = byte(EC_SUBTYPE_COLOR)
	binary.BigEndian.PutUint32(buf[4:], t.Color)
	return buf, nil
}

type TunnelEncapSubTLV struct {
	Type  EncapSubTLVType
	Len   int
	Value TunnelEncapSubTLVValue
}

func (p *TunnelEncapSubTLV) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	bbuf, err := p.Value.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, bbuf...)
	buf[0] = byte(p.Type)
	p.Len = len(buf) - 2
	buf[1] = byte(p.Len)
	return buf, nil
}

func (p *TunnelEncapSubTLV) DecodeFromBytes(data []byte) error {
	switch p.Type {
	case ENCAP_SUBTLV_TYPE_ENCAPSULATION:
		if len(data) < 4 {
			return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
		}
		key := binary.BigEndian.Uint32(data[:4])
		p.Value = &TunnelEncapSubTLVEncapuslation{
			Key:    key,
			Cookie: data[4:],
		}
	case ENCAP_SUBTLV_TYPE_PROTOCOL:
		if len(data) < 2 {
			return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
		}
		protocol := binary.BigEndian.Uint16(data[:2])
		p.Value = &TunnelEncapSubTLVProtocol{protocol}
	case ENCAP_SUBTLV_TYPE_COLOR:
		if len(data) < 8 {
			return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
		}
		color := binary.BigEndian.Uint32(data[4:])
		p.Value = &TunnelEncapSubTLVColor{color}
	default:
		p.Value = &TunnelEncapSubTLVDefault{data}
	}
	return nil
}

type TunnelEncapTLV struct {
	Type  TunnelType
	Len   int
	Value []*TunnelEncapSubTLV
}

func (t *TunnelEncapTLV) DecodeFromBytes(data []byte) error {
	curr := 0
	for {
		if len(data) < curr+2 {
			break
		}
		subType := EncapSubTLVType(data[curr])
		l := int(data[curr+1])
		if len(data) < curr+2+l {
			return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
		}
		v := data[curr+2 : curr+2+l]
		subTlv := &TunnelEncapSubTLV{
			Type: subType,
		}
		err := subTlv.DecodeFromBytes(v)
		if err != nil {
			return err
		}
		t.Value = append(t.Value, subTlv)
		curr += 2 + l
	}
	return nil
}

func (p *TunnelEncapTLV) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	for _, s := range p.Value {
		bbuf, err := s.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	binary.BigEndian.PutUint16(buf, uint16(p.Type))
	p.Len = len(buf) - 4
	binary.BigEndian.PutUint16(buf[2:], uint16(p.Len))
	return buf, nil
}

type PathAttributeTunnelEncap struct {
	PathAttribute
	Value []*TunnelEncapTLV
}

func (p *PathAttributeTunnelEncap) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	curr := 0
	for {
		if len(p.PathAttribute.Value) < curr+4 {
			break
		}
		t := binary.BigEndian.Uint16(p.PathAttribute.Value[curr : curr+2])
		tunnelType := TunnelType(t)
		l := int(binary.BigEndian.Uint16(p.PathAttribute.Value[curr+2 : curr+4]))
		if len(p.PathAttribute.Value) < curr+4+l {
			return fmt.Errorf("Not all TunnelEncapTLV bytes available. %d < %d", len(p.PathAttribute.Value), curr+4+l)
		}
		v := p.PathAttribute.Value[curr+4 : curr+4+l]
		tlv := &TunnelEncapTLV{
			Type: tunnelType,
			Len:  l,
		}
		err = tlv.DecodeFromBytes(v)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, tlv)
		curr += 4 + l
	}
	return nil
}

func (p *PathAttributeTunnelEncap) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	for _, t := range p.Value {
		bbuf, err := t.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func NewPathAttributeTunnelEncap(value []*TunnelEncapTLV) *PathAttributeTunnelEncap {
	t := BGP_ATTR_TYPE_TUNNEL_ENCAP
	return &PathAttributeTunnelEncap{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Value: value,
	}
}

type PmsiTunnelIDInterface interface {
	Serialize() ([]byte, error)
	String() string
}

type DefaultPmsiTunnelID struct {
	Value []byte
}

func (i *DefaultPmsiTunnelID) Serialize() ([]byte, error) {
	return i.Value, nil
}

func (i *DefaultPmsiTunnelID) String() string {
	return string(i.Value)
}

type IngressReplTunnelID struct {
	Value net.IP
}

func (i *IngressReplTunnelID) Serialize() ([]byte, error) {
	if i.Value.To4() != nil {
		return []byte(i.Value.To4()), nil
	}
	return []byte(i.Value), nil
}

func (i *IngressReplTunnelID) String() string {
	return i.Value.String()
}

type PathAttributePmsiTunnel struct {
	PathAttribute
	IsLeafInfoRequired bool
	TunnelType         PmsiTunnelType
	Label              uint32
	TunnelID           PmsiTunnelIDInterface
}

func (p *PathAttributePmsiTunnel) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(p.PathAttribute.Value) < 5 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "PMSI Tunnel length is incorrect")
	}

	if (p.PathAttribute.Value[0] & 0x01) > 0 {
		p.IsLeafInfoRequired = true
	}
	p.TunnelType = PmsiTunnelType(p.PathAttribute.Value[1])
	p.Label = labelDecode(p.PathAttribute.Value[2:5])

	switch p.TunnelType {
	case PMSI_TUNNEL_TYPE_INGRESS_REPL:
		p.TunnelID = &IngressReplTunnelID{net.IP(p.PathAttribute.Value[5:])}
	default:
		p.TunnelID = &DefaultPmsiTunnelID{p.PathAttribute.Value[5:]}
	}
	return nil
}

func (p *PathAttributePmsiTunnel) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	if p.IsLeafInfoRequired {
		buf[0] = 0x01
	}
	buf[1] = byte(p.TunnelType)
	lbuf := make([]byte, 3)
	labelSerialize(p.Label, lbuf)
	buf = append(buf, lbuf...)
	ibuf, err := p.TunnelID.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, ibuf...)
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributePmsiTunnel) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString(fmt.Sprintf("{Pmsi: type: %s,", p.TunnelType))
	if p.IsLeafInfoRequired {
		buf.WriteString(" leaf-info-required,")
	}
	buf.WriteString(fmt.Sprintf(" label: %d, tunnel-id: %s}", p.Label, p.TunnelID))
	return buf.String()
}

func (p *PathAttributePmsiTunnel) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type               BGPAttrType `json:"type"`
		IsLeafInfoRequired bool        `json:"is-leaf-info-required"`
		TunnelType         uint8       `json:"tunnel-type"`
		Label              uint32      `json:"label"`
		TunnelID           string      `json:"tunnel-id"`
	}{
		Type:               p.Type,
		IsLeafInfoRequired: p.IsLeafInfoRequired,
		TunnelType:         uint8(p.TunnelType),
		Label:              p.Label,
		TunnelID:           p.TunnelID.String(),
	})
}

func NewPathAttributePmsiTunnel(typ PmsiTunnelType, isLeafInfoRequired bool, label uint32, id PmsiTunnelIDInterface) *PathAttributePmsiTunnel {
	t := BGP_ATTR_TYPE_PMSI_TUNNEL
	return &PathAttributePmsiTunnel{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		IsLeafInfoRequired: isLeafInfoRequired,
		TunnelType:         typ,
		Label:              label,
		TunnelID:           id,
	}
}

type AigpTLVType uint8

const (
	AIGP_TLV_UNKNOWN AigpTLVType = iota
	AIGP_TLV_IGP_METRIC
)

type AigpTLV interface {
	Serialize() ([]byte, error)
	String() string
	MarshalJSON() ([]byte, error)
	Type() AigpTLVType
}

type AigpTLVDefault struct {
	typ   AigpTLVType
	Value []byte
}

func (t *AigpTLVDefault) Serialize() ([]byte, error) {
	buf := make([]byte, 3+len(t.Value))
	buf[0] = uint8(t.Type())
	binary.BigEndian.PutUint16(buf[1:], uint16(3+len(t.Value)))
	copy(buf[3:], t.Value)
	return buf, nil
}

func (t *AigpTLVDefault) String() string {
	return fmt.Sprintf("{Type: %d, Value: %v}", t.Type(), t.Value)
}

func (t *AigpTLVDefault) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  AigpTLVType `json:"type"`
		Value []byte      `json:"value"`
	}{
		Type:  t.Type(),
		Value: t.Value,
	})
}

func (t *AigpTLVDefault) Type() AigpTLVType {
	return t.typ
}

type AigpTLVIgpMetric struct {
	Metric uint64
}

func (t *AigpTLVIgpMetric) Serialize() ([]byte, error) {
	buf := make([]byte, 11)
	buf[0] = uint8(AIGP_TLV_IGP_METRIC)
	binary.BigEndian.PutUint16(buf[1:], uint16(11))
	binary.BigEndian.PutUint64(buf[3:], t.Metric)
	return buf, nil
}

func (t *AigpTLVIgpMetric) String() string {
	return fmt.Sprintf("{Metric: %d}", t.Metric)
}

func (t *AigpTLVIgpMetric) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   AigpTLVType `json:"type"`
		Metric uint64      `json:"metric"`
	}{
		Type:   AIGP_TLV_IGP_METRIC,
		Metric: t.Metric,
	})
}

func NewAigpTLVIgpMetric(metric uint64) *AigpTLVIgpMetric {
	return &AigpTLVIgpMetric{
		Metric: metric,
	}
}

func (t *AigpTLVIgpMetric) Type() AigpTLVType {
	return AIGP_TLV_IGP_METRIC
}

type PathAttributeAigp struct {
	PathAttribute
	Values []AigpTLV
}

func (p *PathAttributeAigp) DecodeFromBytes(data []byte) error {
	err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	rest := p.PathAttribute.Value
	values := make([]AigpTLV, 0)

	for {
		if len(rest) < 3 {
			break
		}
		typ := rest[0]
		length := binary.BigEndian.Uint16(rest[1:3])
		if len(rest) < int(length) {
			break
		}
		v := rest[3:length]
		switch AigpTLVType(typ) {
		case AIGP_TLV_IGP_METRIC:
			if len(v) < 8 {
				break
			}
			metric := binary.BigEndian.Uint64(v)
			values = append(values, NewAigpTLVIgpMetric(metric))
		default:
			values = append(values, &AigpTLVDefault{AigpTLVType(typ), v})
		}
		rest = rest[length:]
		if len(rest) == 0 {
			p.Values = values
			return nil
		}
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
	return NewMessageError(eCode, eSubCode, nil, "Aigp length is incorrect")
}

func (p *PathAttributeAigp) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	for _, t := range p.Values {
		bbuf, err := t.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	p.PathAttribute.Value = buf
	return p.PathAttribute.Serialize()
}

func (p *PathAttributeAigp) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("{Aigp: [")
	for _, v := range p.Values {
		buf.WriteString(v.String())
	}
	buf.WriteString("]}")
	return buf.String()
}

func (p *PathAttributeAigp) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value []AigpTLV   `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Values,
	})
}

func NewPathAttributeAigp(values []AigpTLV) *PathAttributeAigp {
	t := BGP_ATTR_TYPE_AIGP
	return &PathAttributeAigp{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
		},
		Values: values,
	}
}

type PathAttributeOpaqueValue struct {
	PathAttribute
}

func (p *PathAttributeOpaqueValue) String() string {
	return fmt.Sprintf("{Value: %s}", string(p.Value))
}

func (p *PathAttributeOpaqueValue) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value string      `json:"value"`
	}{
		Type:  p.GetType(),
		Value: string(p.Value),
	})
}

func NewPathAttributeOpaqueValue(value []byte) *PathAttributeOpaqueValue {
	t := BGP_ATTR_TYPE_OPAQUE_VALUE
	return &PathAttributeOpaqueValue{
		PathAttribute: PathAttribute{
			Flags: pathAttrFlags[t],
			Type:  t,
			Value: value,
		},
	}
}

type PathAttributeUnknown struct {
	PathAttribute
}

func GetPathAttribute(data []byte) (PathAttributeInterface, error) {
	if len(data) < 2 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return nil, NewMessageError(eCode, eSubCode, data, "attribute type length is short")
	}
	switch BGPAttrType(data[1]) {
	case BGP_ATTR_TYPE_ORIGIN:
		return &PathAttributeOrigin{}, nil
	case BGP_ATTR_TYPE_AS_PATH:
		return &PathAttributeAsPath{}, nil
	case BGP_ATTR_TYPE_NEXT_HOP:
		return &PathAttributeNextHop{}, nil
	case BGP_ATTR_TYPE_MULTI_EXIT_DISC:
		return &PathAttributeMultiExitDisc{}, nil
	case BGP_ATTR_TYPE_LOCAL_PREF:
		return &PathAttributeLocalPref{}, nil
	case BGP_ATTR_TYPE_ATOMIC_AGGREGATE:
		return &PathAttributeAtomicAggregate{}, nil
	case BGP_ATTR_TYPE_AGGREGATOR:
		return &PathAttributeAggregator{}, nil
	case BGP_ATTR_TYPE_COMMUNITIES:
		return &PathAttributeCommunities{}, nil
	case BGP_ATTR_TYPE_ORIGINATOR_ID:
		return &PathAttributeOriginatorId{}, nil
	case BGP_ATTR_TYPE_CLUSTER_LIST:
		return &PathAttributeClusterList{}, nil
	case BGP_ATTR_TYPE_MP_REACH_NLRI:
		return &PathAttributeMpReachNLRI{}, nil
	case BGP_ATTR_TYPE_MP_UNREACH_NLRI:
		return &PathAttributeMpUnreachNLRI{}, nil
	case BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:
		return &PathAttributeExtendedCommunities{}, nil
	case BGP_ATTR_TYPE_AS4_PATH:
		return &PathAttributeAs4Path{}, nil
	case BGP_ATTR_TYPE_AS4_AGGREGATOR:
		return &PathAttributeAs4Aggregator{}, nil
	case BGP_ATTR_TYPE_TUNNEL_ENCAP:
		return &PathAttributeTunnelEncap{}, nil
	case BGP_ATTR_TYPE_PMSI_TUNNEL:
		return &PathAttributePmsiTunnel{}, nil
	case BGP_ATTR_TYPE_AIGP:
		return &PathAttributeAigp{}, nil
	case BGP_ATTR_TYPE_OPAQUE_VALUE:
		return &PathAttributeOpaqueValue{}, nil
	}
	return &PathAttributeUnknown{}, nil
}

type BGPUpdate struct {
	WithdrawnRoutesLen    uint16
	WithdrawnRoutes       []*IPAddrPrefix
	TotalPathAttributeLen uint16
	PathAttributes        []PathAttributeInterface
	NLRI                  []*IPAddrPrefix
}

func (msg *BGPUpdate) DecodeFromBytes(data []byte) error {

	// cache error codes
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)

	// check withdrawn route length
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "message length isn't enough for withdrawn route length")
	}

	msg.WithdrawnRoutesLen = binary.BigEndian.Uint16(data[0:2])
	data = data[2:]

	// check withdrawn route
	if len(data) < int(msg.WithdrawnRoutesLen) {
		return NewMessageError(eCode, eSubCode, nil, "withdrawn route length exceeds message length")
	}

	msg.WithdrawnRoutes = make([]*IPAddrPrefix, 0, msg.WithdrawnRoutesLen)
	for routelen := msg.WithdrawnRoutesLen; routelen > 0; {
		w := &IPAddrPrefix{}
		err := w.DecodeFromBytes(data)
		if err != nil {
			return err
		}
		routelen -= uint16(w.Len())
		if len(data) < w.Len() {
			return NewMessageError(eCode, eSubCode, nil, "Withdrawn route length is short")
		}
		data = data[w.Len():]
		msg.WithdrawnRoutes = append(msg.WithdrawnRoutes, w)
	}

	// check path total attribute length
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "message length isn't enough for path total attribute length")
	}

	msg.TotalPathAttributeLen = binary.BigEndian.Uint16(data[0:2])
	data = data[2:]

	// check path attribute
	if len(data) < int(msg.TotalPathAttributeLen) {
		return NewMessageError(eCode, eSubCode, nil, "path total attribute length exceeds message length")
	}

	msg.PathAttributes = []PathAttributeInterface{}
	for pathlen := msg.TotalPathAttributeLen; pathlen > 0; {
		p, err := GetPathAttribute(data)
		if err != nil {
			return err
		}
		err = p.DecodeFromBytes(data)
		if err != nil {
			return err
		}
		pathlen -= uint16(p.Len())
		if len(data) < p.Len() {
			return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR, data, "attribute length is short")
		}
		data = data[p.Len():]
		msg.PathAttributes = append(msg.PathAttributes, p)
	}

	msg.NLRI = make([]*IPAddrPrefix, 0)
	for restlen := len(data); restlen > 0; {
		n := &IPAddrPrefix{}
		err := n.DecodeFromBytes(data)
		if err != nil {
			return err
		}
		restlen -= n.Len()
		if len(data) < n.Len() {
			return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "NLRI length is short")
		}
		data = data[n.Len():]
		msg.NLRI = append(msg.NLRI, n)
	}

	return nil
}

func (msg *BGPUpdate) Serialize() ([]byte, error) {
	wbuf := make([]byte, 2)
	for _, w := range msg.WithdrawnRoutes {
		onewbuf, err := w.Serialize()
		if err != nil {
			return nil, err
		}
		wbuf = append(wbuf, onewbuf...)
	}
	msg.WithdrawnRoutesLen = uint16(len(wbuf) - 2)
	binary.BigEndian.PutUint16(wbuf, msg.WithdrawnRoutesLen)

	pbuf := make([]byte, 2)
	for _, p := range msg.PathAttributes {
		onepbuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		pbuf = append(pbuf, onepbuf...)
	}
	msg.TotalPathAttributeLen = uint16(len(pbuf) - 2)
	binary.BigEndian.PutUint16(pbuf, msg.TotalPathAttributeLen)

	buf := append(wbuf, pbuf...)
	for _, n := range msg.NLRI {
		nbuf, err := n.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, nbuf...)
	}
	return buf, nil
}

func (msg *BGPUpdate) IsEndOfRib() (bool, RouteFamily) {
	if len(msg.WithdrawnRoutes) == 0 && len(msg.NLRI) == 0 {
		if len(msg.PathAttributes) == 0 {
			return true, RF_IPv4_UC
		} else if len(msg.PathAttributes) == 1 && msg.PathAttributes[0].GetType() == BGP_ATTR_TYPE_MP_UNREACH_NLRI {
			unreach := msg.PathAttributes[0].(*PathAttributeMpUnreachNLRI)
			return true, AfiSafiToRouteFamily(unreach.AFI, unreach.SAFI)
		}
	}
	return false, RouteFamily(0)
}

func NewBGPUpdateMessage(withdrawnRoutes []*IPAddrPrefix, pathattrs []PathAttributeInterface, nlri []*IPAddrPrefix) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_UPDATE},
		Body:   &BGPUpdate{0, withdrawnRoutes, 0, pathattrs, nlri},
	}
}

func NewEndOfRib(family RouteFamily) *BGPMessage {
	if family == RF_IPv4_UC {
		return NewBGPUpdateMessage(nil, nil, nil)
	} else {
		afi, safi := RouteFamilyToAfiSafi(family)
		t := BGP_ATTR_TYPE_MP_UNREACH_NLRI
		unreach := &PathAttributeMpUnreachNLRI{
			PathAttribute: PathAttribute{
				Flags:  pathAttrFlags[t],
				Type:   t,
				Length: 0,
			},
			AFI:  afi,
			SAFI: safi,
		}
		return NewBGPUpdateMessage(nil, []PathAttributeInterface{unreach}, nil)
	}
}

type BGPNotification struct {
	ErrorCode    uint8
	ErrorSubcode uint8
	Data         []byte
}

func (msg *BGPNotification) DecodeFromBytes(data []byte) error {
	if len(data) < 2 {
		return fmt.Errorf("Not all Notificaiton bytes available")
	}
	msg.ErrorCode = data[0]
	msg.ErrorSubcode = data[1]
	if len(data) > 2 {
		msg.Data = data[2:]
	}
	return nil
}

func (msg *BGPNotification) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	buf[0] = msg.ErrorCode
	buf[1] = msg.ErrorSubcode
	buf = append(buf, msg.Data...)
	return buf, nil
}

func NewBGPNotificationMessage(errcode uint8, errsubcode uint8, data []byte) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_NOTIFICATION},
		Body:   &BGPNotification{errcode, errsubcode, data},
	}
}

type BGPKeepAlive struct {
}

func (msg *BGPKeepAlive) DecodeFromBytes(data []byte) error {
	return nil
}

func (msg *BGPKeepAlive) Serialize() ([]byte, error) {
	return nil, nil
}

func NewBGPKeepAliveMessage() *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Len: 19, Type: BGP_MSG_KEEPALIVE},
		Body:   &BGPKeepAlive{},
	}
}

type BGPRouteRefresh struct {
	AFI         uint16
	Demarcation uint8
	SAFI        uint8
}

func (msg *BGPRouteRefresh) DecodeFromBytes(data []byte) error {
	if len(data) < 4 {
		return fmt.Errorf("Not all RouteRefresh bytes available")
	}
	msg.AFI = binary.BigEndian.Uint16(data[0:2])
	msg.Demarcation = data[2]
	msg.SAFI = data[3]
	return nil
}

func (msg *BGPRouteRefresh) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint16(buf[0:2], msg.AFI)
	buf[2] = msg.Demarcation
	buf[3] = msg.SAFI
	return buf, nil
}

func NewBGPRouteRefreshMessage(afi uint16, demarcation uint8, safi uint8) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_ROUTE_REFRESH},
		Body:   &BGPRouteRefresh{afi, demarcation, safi},
	}
}

type BGPBody interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
}

const (
	BGP_HEADER_LENGTH      = 19
	BGP_MAX_MESSAGE_LENGTH = 4096
)

type BGPHeader struct {
	Marker []byte
	Len    uint16
	Type   uint8
}

func (msg *BGPHeader) DecodeFromBytes(data []byte) error {
	// minimum BGP message length
	if uint16(len(data)) < BGP_HEADER_LENGTH {
		return fmt.Errorf("Not all BGP message header")
	}
	msg.Len = binary.BigEndian.Uint16(data[16:18])
	if int(msg.Len) < BGP_HEADER_LENGTH {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "unknown message type")
	}
	msg.Type = data[18]
	return nil
}

func (msg *BGPHeader) Serialize() ([]byte, error) {
	buf := make([]byte, 19)
	for i, _ := range buf[:16] {
		buf[i] = 0xff
	}
	binary.BigEndian.PutUint16(buf[16:18], msg.Len)
	buf[18] = msg.Type
	return buf, nil
}

type BGPMessage struct {
	Header BGPHeader
	Body   BGPBody
}

func parseBody(h *BGPHeader, data []byte) (*BGPMessage, error) {
	if len(data) < int(h.Len)-BGP_HEADER_LENGTH {
		return nil, fmt.Errorf("Not all BGP message bytes available")
	}
	msg := &BGPMessage{Header: *h}

	switch msg.Header.Type {
	case BGP_MSG_OPEN:
		msg.Body = &BGPOpen{}
	case BGP_MSG_UPDATE:
		msg.Body = &BGPUpdate{}
	case BGP_MSG_NOTIFICATION:
		msg.Body = &BGPNotification{}
	case BGP_MSG_KEEPALIVE:
		msg.Body = &BGPKeepAlive{}
	case BGP_MSG_ROUTE_REFRESH:
		msg.Body = &BGPRouteRefresh{}
	default:
		return nil, NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_TYPE, nil, "unknown message type")
	}
	err := msg.Body.DecodeFromBytes(data)
	if err != nil {
		return nil, err
	}
	return msg, nil
}

func ParseBGPMessage(data []byte) (*BGPMessage, error) {
	h := &BGPHeader{}
	err := h.DecodeFromBytes(data)
	if err != nil {
		return nil, err
	}
	return parseBody(h, data[19:h.Len])
}

func ParseBGPBody(h *BGPHeader, data []byte) (*BGPMessage, error) {
	return parseBody(h, data)
}

func (msg *BGPMessage) Serialize() ([]byte, error) {
	b, err := msg.Body.Serialize()
	if err != nil {
		return nil, err
	}
	if msg.Header.Len == 0 {
		if 19+len(b) > BGP_MAX_MESSAGE_LENGTH {
			return nil, NewMessageError(0, 0, nil, fmt.Sprintf("too long message length %d", 19+len(b)))
		}
		msg.Header.Len = 19 + uint16(len(b))
	}
	h, err := msg.Header.Serialize()
	if err != nil {
		return nil, err
	}
	return append(h, b...), nil
}

type MessageError struct {
	TypeCode    uint8
	SubTypeCode uint8
	Data        []byte
	Message     string
}

func NewMessageError(typeCode, subTypeCode uint8, data []byte, msg string) error {
	return &MessageError{
		TypeCode:    typeCode,
		SubTypeCode: subTypeCode,
		Data:        data,
		Message:     msg,
	}
}

func (e *MessageError) Error() string {
	return e.Message
}