Add app source, add vendoring and module support

This commit is contained in:
Mayuresh Gaitonde
2019-10-16 15:57:55 -07:00
parent b49447a374
commit a8fd79c0e1
991 changed files with 505284 additions and 415 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
// Copyright (C) 2018 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 apiutil
import (
"fmt"
proto "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/any"
api "github.com/osrg/gobgp/api"
"github.com/osrg/gobgp/pkg/packet/bgp"
)
func NewMultiProtocolCapability(a *bgp.CapMultiProtocol) *api.MultiProtocolCapability {
afi, safi := bgp.RouteFamilyToAfiSafi(a.CapValue)
return &api.MultiProtocolCapability{
Family: ToApiFamily(afi, safi),
}
}
func NewRouteRefreshCapability(a *bgp.CapRouteRefresh) *api.RouteRefreshCapability {
return &api.RouteRefreshCapability{}
}
func NewCarryingLabelInfoCapability(a *bgp.CapCarryingLabelInfo) *api.CarryingLabelInfoCapability {
return &api.CarryingLabelInfoCapability{}
}
func NewExtendedNexthopCapability(a *bgp.CapExtendedNexthop) *api.ExtendedNexthopCapability {
tuples := make([]*api.ExtendedNexthopCapabilityTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
tuples = append(tuples, &api.ExtendedNexthopCapabilityTuple{
NlriFamily: ToApiFamily(t.NLRIAFI, uint8(t.NLRISAFI)),
NexthopFamily: ToApiFamily(t.NexthopAFI, bgp.SAFI_UNICAST),
})
}
return &api.ExtendedNexthopCapability{
Tuples: tuples,
}
}
func NewGracefulRestartCapability(a *bgp.CapGracefulRestart) *api.GracefulRestartCapability {
tuples := make([]*api.GracefulRestartCapabilityTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
tuples = append(tuples, &api.GracefulRestartCapabilityTuple{
Family: ToApiFamily(t.AFI, t.SAFI),
Flags: uint32(t.Flags),
})
}
return &api.GracefulRestartCapability{
Flags: uint32(a.Flags),
Time: uint32(a.Time),
Tuples: tuples,
}
}
func NewFourOctetASNumberCapability(a *bgp.CapFourOctetASNumber) *api.FourOctetASNumberCapability {
return &api.FourOctetASNumberCapability{
As: a.CapValue,
}
}
func NewAddPathCapability(a *bgp.CapAddPath) *api.AddPathCapability {
tuples := make([]*api.AddPathCapabilityTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
afi, safi := bgp.RouteFamilyToAfiSafi(t.RouteFamily)
tuples = append(tuples, &api.AddPathCapabilityTuple{
Family: ToApiFamily(afi, safi),
Mode: api.AddPathMode(t.Mode),
})
}
return &api.AddPathCapability{
Tuples: tuples,
}
}
func NewEnhancedRouteRefreshCapability(a *bgp.CapEnhancedRouteRefresh) *api.EnhancedRouteRefreshCapability {
return &api.EnhancedRouteRefreshCapability{}
}
func NewLongLivedGracefulRestartCapability(a *bgp.CapLongLivedGracefulRestart) *api.LongLivedGracefulRestartCapability {
tuples := make([]*api.LongLivedGracefulRestartCapabilityTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
tuples = append(tuples, &api.LongLivedGracefulRestartCapabilityTuple{
Family: ToApiFamily(t.AFI, uint8(t.SAFI)),
Flags: uint32(t.Flags),
Time: t.RestartTime,
})
}
return &api.LongLivedGracefulRestartCapability{
Tuples: tuples,
}
}
func NewRouteRefreshCiscoCapability(a *bgp.CapRouteRefreshCisco) *api.RouteRefreshCiscoCapability {
return &api.RouteRefreshCiscoCapability{}
}
func NewUnknownCapability(a *bgp.CapUnknown) *api.UnknownCapability {
return &api.UnknownCapability{
Code: uint32(a.CapCode),
Value: a.CapValue,
}
}
func MarshalCapability(value bgp.ParameterCapabilityInterface) (*any.Any, error) {
var m proto.Message
switch n := value.(type) {
case *bgp.CapMultiProtocol:
m = NewMultiProtocolCapability(n)
case *bgp.CapRouteRefresh:
m = NewRouteRefreshCapability(n)
case *bgp.CapCarryingLabelInfo:
m = NewCarryingLabelInfoCapability(n)
case *bgp.CapExtendedNexthop:
m = NewExtendedNexthopCapability(n)
case *bgp.CapGracefulRestart:
m = NewGracefulRestartCapability(n)
case *bgp.CapFourOctetASNumber:
m = NewFourOctetASNumberCapability(n)
case *bgp.CapAddPath:
m = NewAddPathCapability(n)
case *bgp.CapEnhancedRouteRefresh:
m = NewEnhancedRouteRefreshCapability(n)
case *bgp.CapLongLivedGracefulRestart:
m = NewLongLivedGracefulRestartCapability(n)
case *bgp.CapRouteRefreshCisco:
m = NewRouteRefreshCiscoCapability(n)
case *bgp.CapUnknown:
m = NewUnknownCapability(n)
default:
return nil, fmt.Errorf("invalid capability type to marshal: %+v", value)
}
return ptypes.MarshalAny(m)
}
func MarshalCapabilities(values []bgp.ParameterCapabilityInterface) ([]*any.Any, error) {
caps := make([]*any.Any, 0, len(values))
for _, value := range values {
a, err := MarshalCapability(value)
if err != nil {
return nil, err
}
caps = append(caps, a)
}
return caps, nil
}
func unmarshalCapability(a *any.Any) (bgp.ParameterCapabilityInterface, error) {
var value ptypes.DynamicAny
if err := ptypes.UnmarshalAny(a, &value); err != nil {
return nil, fmt.Errorf("failed to unmarshal capability: %s", err)
}
switch a := value.Message.(type) {
case *api.MultiProtocolCapability:
return bgp.NewCapMultiProtocol(ToRouteFamily(a.Family)), nil
case *api.RouteRefreshCapability:
return bgp.NewCapRouteRefresh(), nil
case *api.CarryingLabelInfoCapability:
return bgp.NewCapCarryingLabelInfo(), nil
case *api.ExtendedNexthopCapability:
tuples := make([]*bgp.CapExtendedNexthopTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
var nhAfi uint16
switch t.NexthopFamily.Afi {
case api.Family_AFI_IP:
nhAfi = bgp.AFI_IP
case api.Family_AFI_IP6:
nhAfi = bgp.AFI_IP6
default:
return nil, fmt.Errorf("invalid address family for nexthop afi in extended nexthop capability: %s", t.NexthopFamily)
}
tuples = append(tuples, bgp.NewCapExtendedNexthopTuple(ToRouteFamily(t.NlriFamily), nhAfi))
}
return bgp.NewCapExtendedNexthop(tuples), nil
case *api.GracefulRestartCapability:
tuples := make([]*bgp.CapGracefulRestartTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
var forward bool
if t.Flags&0x80 > 0 {
forward = true
}
tuples = append(tuples, bgp.NewCapGracefulRestartTuple(ToRouteFamily(t.Family), forward))
}
var restarting bool
if a.Flags&0x08 > 0 {
restarting = true
}
var notification bool
if a.Flags&0x04 > 0 {
notification = true
}
return bgp.NewCapGracefulRestart(restarting, notification, uint16(a.Time), tuples), nil
case *api.FourOctetASNumberCapability:
return bgp.NewCapFourOctetASNumber(a.As), nil
case *api.AddPathCapability:
tuples := make([]*bgp.CapAddPathTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
tuples = append(tuples, bgp.NewCapAddPathTuple(ToRouteFamily(t.Family), bgp.BGPAddPathMode(t.Mode)))
}
return bgp.NewCapAddPath(tuples), nil
case *api.EnhancedRouteRefreshCapability:
return bgp.NewCapEnhancedRouteRefresh(), nil
case *api.LongLivedGracefulRestartCapability:
tuples := make([]*bgp.CapLongLivedGracefulRestartTuple, 0, len(a.Tuples))
for _, t := range a.Tuples {
var forward bool
if t.Flags&0x80 > 0 {
forward = true
}
tuples = append(tuples, bgp.NewCapLongLivedGracefulRestartTuple(ToRouteFamily(t.Family), forward, t.Time))
}
return bgp.NewCapLongLivedGracefulRestart(tuples), nil
case *api.RouteRefreshCiscoCapability:
return bgp.NewCapRouteRefreshCisco(), nil
case *api.UnknownCapability:
return bgp.NewCapUnknown(bgp.BGPCapabilityCode(a.Code), a.Value), nil
}
return nil, fmt.Errorf("invalid capability type to unmarshal: %s", a.TypeUrl)
}
func UnmarshalCapabilities(values []*any.Any) ([]bgp.ParameterCapabilityInterface, error) {
caps := make([]bgp.ParameterCapabilityInterface, 0, len(values))
for _, value := range values {
c, err := unmarshalCapability(value)
if err != nil {
return nil, err
}
caps = append(caps, c)
}
return caps, nil
}

View File

@@ -0,0 +1,125 @@
// Copyright (C) 2016 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 apiutil
import (
"encoding/json"
"net"
"time"
api "github.com/osrg/gobgp/api"
"github.com/osrg/gobgp/pkg/packet/bgp"
)
// workaround. This for the json format compatibility. Once we update senario tests, we can remove this.
type Path struct {
Nlri bgp.AddrPrefixInterface `json:"nlri"`
Age int64 `json:"age"`
Best bool `json:"best"`
Attrs []bgp.PathAttributeInterface `json:"attrs"`
Stale bool `json:"stale"`
Withdrawal bool `json:"withdrawal,omitempty"`
SourceID net.IP `json:"source-id,omitempty"`
NeighborIP net.IP `json:"neighbor-ip,omitempty"`
}
type Destination struct {
Paths []*Path
}
func (d *Destination) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Paths)
}
func NewDestination(dst *api.Destination) *Destination {
l := make([]*Path, 0, len(dst.Paths))
for _, p := range dst.Paths {
nlri, _ := GetNativeNlri(p)
attrs, _ := GetNativePathAttributes(p)
l = append(l, &Path{
Nlri: nlri,
Age: p.Age,
Best: p.Best,
Attrs: attrs,
Stale: p.Stale,
Withdrawal: p.IsWithdraw,
SourceID: net.ParseIP(p.SourceId),
NeighborIP: net.ParseIP(p.NeighborIp),
})
}
return &Destination{Paths: l}
}
func NewPath(nlri bgp.AddrPrefixInterface, isWithdraw bool, attrs []bgp.PathAttributeInterface, age time.Time) *api.Path {
return &api.Path{
AnyNlri: MarshalNLRI(nlri),
AnyPattrs: MarshalPathAttributes(attrs),
Age: age.Unix(),
IsWithdraw: isWithdraw,
Family: ToApiFamily(nlri.AFI(), nlri.SAFI()),
Identifier: nlri.PathIdentifier(),
}
}
func getNLRI(family bgp.RouteFamily, buf []byte) (bgp.AddrPrefixInterface, error) {
afi, safi := bgp.RouteFamilyToAfiSafi(family)
nlri, err := bgp.NewPrefixFromRouteFamily(afi, safi)
if err != nil {
return nil, err
}
if err := nlri.DecodeFromBytes(buf); err != nil {
return nil, err
}
return nlri, nil
}
func GetNativeNlri(p *api.Path) (bgp.AddrPrefixInterface, error) {
if len(p.Nlri) > 0 {
return getNLRI(ToRouteFamily(p.Family), p.Nlri)
}
return UnmarshalNLRI(ToRouteFamily(p.Family), p.AnyNlri)
}
func GetNativePathAttributes(p *api.Path) ([]bgp.PathAttributeInterface, error) {
pattrsLen := len(p.Pattrs)
if pattrsLen > 0 {
pattrs := make([]bgp.PathAttributeInterface, 0, pattrsLen)
for _, attr := range p.Pattrs {
a, err := bgp.GetPathAttribute(attr)
if err != nil {
return nil, err
}
err = a.DecodeFromBytes(attr)
if err != nil {
return nil, err
}
pattrs = append(pattrs, a)
}
return pattrs, nil
}
return UnmarshalPathAttributes(p.AnyPattrs)
}
func ToRouteFamily(f *api.Family) bgp.RouteFamily {
return bgp.AfiSafiToRouteFamily(uint16(f.Afi), uint8(f.Safi))
}
func ToApiFamily(afi uint16, safi uint8) *api.Family {
return &api.Family{
Afi: api.Family_Afi(afi),
Safi: api.Family_Safi(safi),
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,524 @@
package config
import (
"encoding/binary"
"fmt"
"math"
"net"
"reflect"
"strconv"
"github.com/osrg/gobgp/pkg/packet/bgp"
"github.com/osrg/gobgp/pkg/packet/bmp"
"github.com/osrg/gobgp/pkg/packet/rtr"
"github.com/spf13/viper"
)
const (
DEFAULT_HOLDTIME = 90
DEFAULT_IDLE_HOLDTIME_AFTER_RESET = 30
DEFAULT_CONNECT_RETRY = 120
)
var forcedOverwrittenConfig = []string{
"neighbor.config.peer-as",
"neighbor.timers.config.minimum-advertisement-interval",
}
var configuredFields map[string]interface{}
func RegisterConfiguredFields(addr string, n interface{}) {
if configuredFields == nil {
configuredFields = make(map[string]interface{})
}
configuredFields[addr] = n
}
func defaultAfiSafi(typ AfiSafiType, enable bool) AfiSafi {
return AfiSafi{
Config: AfiSafiConfig{
AfiSafiName: typ,
Enabled: enable,
},
State: AfiSafiState{
AfiSafiName: typ,
Family: bgp.AddressFamilyValueMap[string(typ)],
},
}
}
func SetDefaultNeighborConfigValues(n *Neighbor, pg *PeerGroup, g *Global) error {
// Determines this function is called against the same Neighbor struct,
// and if already called, returns immediately.
if n.State.LocalAs != 0 {
return nil
}
return setDefaultNeighborConfigValuesWithViper(nil, n, g, pg)
}
func setDefaultNeighborConfigValuesWithViper(v *viper.Viper, n *Neighbor, g *Global, pg *PeerGroup) error {
if n == nil {
return fmt.Errorf("neighbor config is nil")
}
if g == nil {
return fmt.Errorf("global config is nil")
}
if v == nil {
v = viper.New()
}
if pg != nil {
if err := OverwriteNeighborConfigWithPeerGroup(n, pg); err != nil {
return err
}
}
if n.Config.LocalAs == 0 {
n.Config.LocalAs = g.Config.As
if !g.Confederation.Config.Enabled || n.IsConfederation(g) {
n.Config.LocalAs = g.Config.As
} else {
n.Config.LocalAs = g.Confederation.Config.Identifier
}
}
n.State.LocalAs = n.Config.LocalAs
if n.Config.PeerAs != n.Config.LocalAs {
n.Config.PeerType = PEER_TYPE_EXTERNAL
n.State.PeerType = PEER_TYPE_EXTERNAL
n.State.RemovePrivateAs = n.Config.RemovePrivateAs
n.AsPathOptions.State.ReplacePeerAs = n.AsPathOptions.Config.ReplacePeerAs
} else {
n.Config.PeerType = PEER_TYPE_INTERNAL
n.State.PeerType = PEER_TYPE_INTERNAL
if string(n.Config.RemovePrivateAs) != "" {
return fmt.Errorf("can't set remove-private-as for iBGP peer")
}
if n.AsPathOptions.Config.ReplacePeerAs {
return fmt.Errorf("can't set replace-peer-as for iBGP peer")
}
}
if n.State.NeighborAddress == "" {
n.State.NeighborAddress = n.Config.NeighborAddress
}
n.State.PeerAs = n.Config.PeerAs
n.AsPathOptions.State.AllowOwnAs = n.AsPathOptions.Config.AllowOwnAs
if !v.IsSet("neighbor.error-handling.config.treat-as-withdraw") {
n.ErrorHandling.Config.TreatAsWithdraw = true
}
if !v.IsSet("neighbor.timers.config.connect-retry") && n.Timers.Config.ConnectRetry == 0 {
n.Timers.Config.ConnectRetry = float64(DEFAULT_CONNECT_RETRY)
}
if !v.IsSet("neighbor.timers.config.hold-time") && n.Timers.Config.HoldTime == 0 {
n.Timers.Config.HoldTime = float64(DEFAULT_HOLDTIME)
}
if !v.IsSet("neighbor.timers.config.keepalive-interval") && n.Timers.Config.KeepaliveInterval == 0 {
n.Timers.Config.KeepaliveInterval = n.Timers.Config.HoldTime / 3
}
if !v.IsSet("neighbor.timers.config.idle-hold-time-after-reset") && n.Timers.Config.IdleHoldTimeAfterReset == 0 {
n.Timers.Config.IdleHoldTimeAfterReset = float64(DEFAULT_IDLE_HOLDTIME_AFTER_RESET)
}
if n.Config.NeighborInterface != "" {
if n.RouteServer.Config.RouteServerClient {
return fmt.Errorf("configuring route server client as unnumbered peer is not supported")
}
addr, err := GetIPv6LinkLocalNeighborAddress(n.Config.NeighborInterface)
if err != nil {
return err
}
n.State.NeighborAddress = addr
}
if n.Transport.Config.LocalAddress == "" {
if n.State.NeighborAddress == "" {
return fmt.Errorf("no neighbor address/interface specified")
}
ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress)
if err != nil {
return err
}
localAddress := "0.0.0.0"
if ipAddr.IP.To4() == nil {
localAddress = "::"
if ipAddr.Zone != "" {
localAddress, err = getIPv6LinkLocalAddress(ipAddr.Zone)
if err != nil {
return err
}
}
}
n.Transport.Config.LocalAddress = localAddress
}
if len(n.AfiSafis) == 0 {
if n.Config.NeighborInterface != "" {
n.AfiSafis = []AfiSafi{
defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true),
defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true),
}
} else if ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress); err != nil {
return fmt.Errorf("invalid neighbor address: %s", n.State.NeighborAddress)
} else if ipAddr.IP.To4() != nil {
n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true)}
} else {
n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true)}
}
for i := range n.AfiSafis {
n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive
n.AfiSafis[i].AddPaths.State.Receive = n.AddPaths.Config.Receive
n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax
n.AfiSafis[i].AddPaths.State.SendMax = n.AddPaths.Config.SendMax
}
} else {
afs, err := extractArray(v.Get("neighbor.afi-safis"))
if err != nil {
return err
}
for i := range n.AfiSafis {
vv := viper.New()
if len(afs) > i {
vv.Set("afi-safi", afs[i])
}
rf, err := bgp.GetRouteFamily(string(n.AfiSafis[i].Config.AfiSafiName))
if err != nil {
return err
}
n.AfiSafis[i].State.Family = rf
n.AfiSafis[i].State.AfiSafiName = n.AfiSafis[i].Config.AfiSafiName
if !vv.IsSet("afi-safi.config.enabled") {
n.AfiSafis[i].Config.Enabled = true
}
n.AfiSafis[i].MpGracefulRestart.State.Enabled = n.AfiSafis[i].MpGracefulRestart.Config.Enabled
if !vv.IsSet("afi-safi.add-paths.config.receive") {
n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive
}
n.AfiSafis[i].AddPaths.State.Receive = n.AfiSafis[i].AddPaths.Config.Receive
if !vv.IsSet("afi-safi.add-paths.config.send-max") {
n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax
}
n.AfiSafis[i].AddPaths.State.SendMax = n.AfiSafis[i].AddPaths.Config.SendMax
}
}
n.State.Description = n.Config.Description
n.State.AdminDown = n.Config.AdminDown
if n.GracefulRestart.Config.Enabled {
if !v.IsSet("neighbor.graceful-restart.config.restart-time") && n.GracefulRestart.Config.RestartTime == 0 {
// RFC 4724 4. Operation
// A suggested default for the Restart Time is a value less than or
// equal to the HOLDTIME carried in the OPEN.
n.GracefulRestart.Config.RestartTime = uint16(n.Timers.Config.HoldTime)
}
if !v.IsSet("neighbor.graceful-restart.config.deferral-time") && n.GracefulRestart.Config.DeferralTime == 0 {
// RFC 4724 4.1. Procedures for the Restarting Speaker
// The value of this timer should be large
// enough, so as to provide all the peers of the Restarting Speaker with
// enough time to send all the routes to the Restarting Speaker
n.GracefulRestart.Config.DeferralTime = uint16(360)
}
}
if n.EbgpMultihop.Config.Enabled {
if n.TtlSecurity.Config.Enabled {
return fmt.Errorf("ebgp-multihop and ttl-security are mututally exclusive")
}
if n.EbgpMultihop.Config.MultihopTtl == 0 {
n.EbgpMultihop.Config.MultihopTtl = 255
}
} else if n.TtlSecurity.Config.Enabled {
if n.TtlSecurity.Config.TtlMin == 0 {
n.TtlSecurity.Config.TtlMin = 255
}
}
if n.RouteReflector.Config.RouteReflectorClient {
if n.RouteReflector.Config.RouteReflectorClusterId == "" {
n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(g.Config.RouterId)
} else {
id := string(n.RouteReflector.Config.RouteReflectorClusterId)
if ip := net.ParseIP(id).To4(); ip != nil {
n.RouteReflector.State.RouteReflectorClusterId = n.RouteReflector.Config.RouteReflectorClusterId
} else if num, err := strconv.ParseUint(id, 10, 32); err == nil {
ip = make(net.IP, 4)
binary.BigEndian.PutUint32(ip, uint32(num))
n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(ip.String())
} else {
return fmt.Errorf("route-reflector-cluster-id should be specified as IPv4 address or 32-bit unsigned integer")
}
}
}
return nil
}
func SetDefaultGlobalConfigValues(g *Global) error {
if len(g.AfiSafis) == 0 {
g.AfiSafis = []AfiSafi{}
for k := range AfiSafiTypeToIntMap {
g.AfiSafis = append(g.AfiSafis, defaultAfiSafi(k, true))
}
}
if g.Config.Port == 0 {
g.Config.Port = bgp.BGP_PORT
}
if len(g.Config.LocalAddressList) == 0 {
g.Config.LocalAddressList = []string{"0.0.0.0", "::"}
}
return nil
}
func setDefaultVrfConfigValues(v *Vrf) error {
if v == nil {
return fmt.Errorf("cannot set default values for nil vrf config")
}
if v.Config.Name == "" {
return fmt.Errorf("specify vrf name")
}
_, err := bgp.ParseRouteDistinguisher(v.Config.Rd)
if err != nil {
return fmt.Errorf("invalid rd for vrf %s: %s", v.Config.Name, v.Config.Rd)
}
if len(v.Config.ImportRtList) == 0 {
v.Config.ImportRtList = v.Config.BothRtList
}
for _, rtString := range v.Config.ImportRtList {
_, err := bgp.ParseRouteTarget(rtString)
if err != nil {
return fmt.Errorf("invalid import rt for vrf %s: %s", v.Config.Name, rtString)
}
}
if len(v.Config.ExportRtList) == 0 {
v.Config.ExportRtList = v.Config.BothRtList
}
for _, rtString := range v.Config.ExportRtList {
_, err := bgp.ParseRouteTarget(rtString)
if err != nil {
return fmt.Errorf("invalid export rt for vrf %s: %s", v.Config.Name, rtString)
}
}
return nil
}
func SetDefaultConfigValues(b *BgpConfigSet) error {
return setDefaultConfigValuesWithViper(nil, b)
}
func setDefaultPolicyConfigValuesWithViper(v *viper.Viper, p *PolicyDefinition) error {
stmts, err := extractArray(v.Get("policy.statements"))
if err != nil {
return err
}
for i := range p.Statements {
vv := viper.New()
if len(stmts) > i {
vv.Set("statement", stmts[i])
}
if !vv.IsSet("statement.actions.route-disposition") {
p.Statements[i].Actions.RouteDisposition = ROUTE_DISPOSITION_NONE
}
}
return nil
}
func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
if v == nil {
v = viper.New()
}
if err := SetDefaultGlobalConfigValues(&b.Global); err != nil {
return err
}
for idx, server := range b.BmpServers {
if server.Config.Port == 0 {
server.Config.Port = bmp.BMP_DEFAULT_PORT
}
if server.Config.RouteMonitoringPolicy == "" {
server.Config.RouteMonitoringPolicy = BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY
}
// statistics-timeout is uint16 value and implicitly less than 65536
if server.Config.StatisticsTimeout != 0 && server.Config.StatisticsTimeout < 15 {
return fmt.Errorf("too small statistics-timeout value: %d", server.Config.StatisticsTimeout)
}
b.BmpServers[idx] = server
}
vrfNames := make(map[string]struct{})
vrfIDs := make(map[uint32]struct{})
for idx, vrf := range b.Vrfs {
if err := setDefaultVrfConfigValues(&vrf); err != nil {
return err
}
if _, ok := vrfNames[vrf.Config.Name]; ok {
return fmt.Errorf("duplicated vrf name: %s", vrf.Config.Name)
}
vrfNames[vrf.Config.Name] = struct{}{}
if vrf.Config.Id != 0 {
if _, ok := vrfIDs[vrf.Config.Id]; ok {
return fmt.Errorf("duplicated vrf id: %d", vrf.Config.Id)
}
vrfIDs[vrf.Config.Id] = struct{}{}
}
b.Vrfs[idx] = vrf
}
// Auto assign VRF identifier
for idx, vrf := range b.Vrfs {
if vrf.Config.Id == 0 {
for id := uint32(1); id < math.MaxUint32; id++ {
if _, ok := vrfIDs[id]; !ok {
vrf.Config.Id = id
vrfIDs[id] = struct{}{}
break
}
}
}
b.Vrfs[idx] = vrf
}
if b.Zebra.Config.Url == "" {
b.Zebra.Config.Url = "unix:/var/run/quagga/zserv.api"
}
if b.Zebra.Config.Version < 2 {
b.Zebra.Config.Version = 2
} else if b.Zebra.Config.Version > 5 {
b.Zebra.Config.Version = 5
}
if !v.IsSet("zebra.config.nexthop-trigger-enable") && !b.Zebra.Config.NexthopTriggerEnable && b.Zebra.Config.Version > 2 {
b.Zebra.Config.NexthopTriggerEnable = true
}
if b.Zebra.Config.NexthopTriggerDelay == 0 {
b.Zebra.Config.NexthopTriggerDelay = 5
}
list, err := extractArray(v.Get("neighbors"))
if err != nil {
return err
}
for idx, n := range b.Neighbors {
vv := viper.New()
if len(list) > idx {
vv.Set("neighbor", list[idx])
}
pg, err := b.getPeerGroup(n.Config.PeerGroup)
if err != nil {
return nil
}
if pg != nil {
identifier := vv.Get("neighbor.config.neighbor-address")
if identifier == nil {
identifier = vv.Get("neighbor.config.neighbor-interface")
}
RegisterConfiguredFields(identifier.(string), list[idx])
}
if err := setDefaultNeighborConfigValuesWithViper(vv, &n, &b.Global, pg); err != nil {
return err
}
b.Neighbors[idx] = n
}
for _, d := range b.DynamicNeighbors {
if err := d.validate(b); err != nil {
return err
}
}
for idx, r := range b.RpkiServers {
if r.Config.Port == 0 {
b.RpkiServers[idx].Config.Port = rtr.RPKI_DEFAULT_PORT
}
}
list, err = extractArray(v.Get("policy-definitions"))
if err != nil {
return err
}
for idx, p := range b.PolicyDefinitions {
vv := viper.New()
if len(list) > idx {
vv.Set("policy", list[idx])
}
if err := setDefaultPolicyConfigValuesWithViper(vv, &p); err != nil {
return err
}
b.PolicyDefinitions[idx] = p
}
return nil
}
func OverwriteNeighborConfigWithPeerGroup(c *Neighbor, pg *PeerGroup) error {
v := viper.New()
val, ok := configuredFields[c.Config.NeighborAddress]
if ok {
v.Set("neighbor", val)
} else {
v.Set("neighbor.config.peer-group", c.Config.PeerGroup)
}
overwriteConfig(&c.Config, &pg.Config, "neighbor.config", v)
overwriteConfig(&c.Timers.Config, &pg.Timers.Config, "neighbor.timers.config", v)
overwriteConfig(&c.Transport.Config, &pg.Transport.Config, "neighbor.transport.config", v)
overwriteConfig(&c.ErrorHandling.Config, &pg.ErrorHandling.Config, "neighbor.error-handling.config", v)
overwriteConfig(&c.LoggingOptions.Config, &pg.LoggingOptions.Config, "neighbor.logging-options.config", v)
overwriteConfig(&c.EbgpMultihop.Config, &pg.EbgpMultihop.Config, "neighbor.ebgp-multihop.config", v)
overwriteConfig(&c.RouteReflector.Config, &pg.RouteReflector.Config, "neighbor.route-reflector.config", v)
overwriteConfig(&c.AsPathOptions.Config, &pg.AsPathOptions.Config, "neighbor.as-path-options.config", v)
overwriteConfig(&c.AddPaths.Config, &pg.AddPaths.Config, "neighbor.add-paths.config", v)
overwriteConfig(&c.GracefulRestart.Config, &pg.GracefulRestart.Config, "neighbor.gradeful-restart.config", v)
overwriteConfig(&c.ApplyPolicy.Config, &pg.ApplyPolicy.Config, "neighbor.apply-policy.config", v)
overwriteConfig(&c.UseMultiplePaths.Config, &pg.UseMultiplePaths.Config, "neighbor.use-multiple-paths.config", v)
overwriteConfig(&c.RouteServer.Config, &pg.RouteServer.Config, "neighbor.route-server.config", v)
overwriteConfig(&c.TtlSecurity.Config, &pg.TtlSecurity.Config, "neighbor.ttl-security.config", v)
if !v.IsSet("neighbor.afi-safis") {
c.AfiSafis = append(c.AfiSafis, pg.AfiSafis...)
}
return nil
}
func overwriteConfig(c, pg interface{}, tagPrefix string, v *viper.Viper) {
nValue := reflect.Indirect(reflect.ValueOf(c))
nType := reflect.Indirect(nValue).Type()
pgValue := reflect.Indirect(reflect.ValueOf(pg))
pgType := reflect.Indirect(pgValue).Type()
for i := 0; i < pgType.NumField(); i++ {
field := pgType.Field(i).Name
tag := tagPrefix + "." + nType.Field(i).Tag.Get("mapstructure")
if func() bool {
for _, t := range forcedOverwrittenConfig {
if t == tag {
return true
}
}
return false
}() || !v.IsSet(tag) {
nValue.FieldByName(field).Set(pgValue.FieldByName(field))
}
}
}

View File

@@ -0,0 +1,72 @@
// Copyright (C) 2016 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.
// +build linux
package config
import (
"fmt"
"net"
"github.com/vishvananda/netlink"
)
func GetIPv6LinkLocalNeighborAddress(ifname string) (string, error) {
ifi, err := net.InterfaceByName(ifname)
if err != nil {
return "", err
}
neighs, err := netlink.NeighList(ifi.Index, netlink.FAMILY_V6)
if err != nil {
return "", err
}
cnt := 0
var addr net.IP
for _, neigh := range neighs {
local, err := isLocalLinkLocalAddress(ifi.Index, neigh.IP)
if err != nil {
return "", err
}
if neigh.State&netlink.NUD_FAILED == 0 && neigh.IP.IsLinkLocalUnicast() && !local {
addr = neigh.IP
cnt++
}
}
if cnt == 0 {
return "", fmt.Errorf("no ipv6 link-local neighbor found")
} else if cnt > 1 {
return "", fmt.Errorf("found %d link-local neighbors. only support p2p link", cnt)
}
return fmt.Sprintf("%s%%%s", addr, ifname), nil
}
func isLocalLinkLocalAddress(ifindex int, addr net.IP) (bool, error) {
ifi, err := net.InterfaceByIndex(ifindex)
if err != nil {
return false, err
}
addrs, err := ifi.Addrs()
if err != nil {
return false, err
}
for _, a := range addrs {
if ip, _, _ := net.ParseCIDR(a.String()); addr.Equal(ip) {
return true, nil
}
}
return false, nil
}

View File

@@ -0,0 +1,25 @@
// Copyright (C) 2016 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.
// +build !linux
package config
import (
"fmt"
)
func GetIPv6LinkLocalNeighborAddress(ifname string) (string, error) {
return "", fmt.Errorf("unnumbered peering is not supported")
}

View File

@@ -0,0 +1,159 @@
package config
import (
"os"
"os/signal"
"syscall"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
type BgpConfigSet struct {
Global Global `mapstructure:"global"`
Neighbors []Neighbor `mapstructure:"neighbors"`
PeerGroups []PeerGroup `mapstructure:"peer-groups"`
RpkiServers []RpkiServer `mapstructure:"rpki-servers"`
BmpServers []BmpServer `mapstructure:"bmp-servers"`
Vrfs []Vrf `mapstructure:"vrfs"`
MrtDump []Mrt `mapstructure:"mrt-dump"`
Zebra Zebra `mapstructure:"zebra"`
Collector Collector `mapstructure:"collector"`
DefinedSets DefinedSets `mapstructure:"defined-sets"`
PolicyDefinitions []PolicyDefinition `mapstructure:"policy-definitions"`
DynamicNeighbors []DynamicNeighbor `mapstructure:"dynamic-neighbors"`
}
func ReadConfigfileServe(path, format string, configCh chan *BgpConfigSet) {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGHUP)
// Update config file type, if detectable
format = detectConfigFileType(path, format)
cnt := 0
for {
c := &BgpConfigSet{}
v := viper.New()
v.SetConfigFile(path)
v.SetConfigType(format)
var err error
if err = v.ReadInConfig(); err != nil {
goto ERROR
}
if err = v.UnmarshalExact(c); err != nil {
goto ERROR
}
if err = setDefaultConfigValuesWithViper(v, c); err != nil {
goto ERROR
}
if cnt == 0 {
log.WithFields(log.Fields{
"Topic": "Config",
}).Info("Finished reading the config file")
}
cnt++
configCh <- c
goto NEXT
ERROR:
if cnt == 0 {
log.WithFields(log.Fields{
"Topic": "Config",
"Error": err,
}).Fatalf("Can't read config file %s", path)
} else {
log.WithFields(log.Fields{
"Topic": "Config",
"Error": err,
}).Warningf("Can't read config file %s", path)
}
NEXT:
<-sigCh
log.WithFields(log.Fields{
"Topic": "Config",
}).Info("Reload the config file")
}
}
func ConfigSetToRoutingPolicy(c *BgpConfigSet) *RoutingPolicy {
return &RoutingPolicy{
DefinedSets: c.DefinedSets,
PolicyDefinitions: c.PolicyDefinitions,
}
}
func UpdatePeerGroupConfig(curC, newC *BgpConfigSet) ([]PeerGroup, []PeerGroup, []PeerGroup) {
addedPg := []PeerGroup{}
deletedPg := []PeerGroup{}
updatedPg := []PeerGroup{}
for _, n := range newC.PeerGroups {
if idx := existPeerGroup(n.Config.PeerGroupName, curC.PeerGroups); idx < 0 {
addedPg = append(addedPg, n)
} else if !n.Equal(&curC.PeerGroups[idx]) {
log.WithFields(log.Fields{
"Topic": "Config",
}).Debugf("Current peer-group config:%v", curC.PeerGroups[idx])
log.WithFields(log.Fields{
"Topic": "Config",
}).Debugf("New peer-group config:%v", n)
updatedPg = append(updatedPg, n)
}
}
for _, n := range curC.PeerGroups {
if existPeerGroup(n.Config.PeerGroupName, newC.PeerGroups) < 0 {
deletedPg = append(deletedPg, n)
}
}
return addedPg, deletedPg, updatedPg
}
func UpdateNeighborConfig(curC, newC *BgpConfigSet) ([]Neighbor, []Neighbor, []Neighbor) {
added := []Neighbor{}
deleted := []Neighbor{}
updated := []Neighbor{}
for _, n := range newC.Neighbors {
if idx := inSlice(n, curC.Neighbors); idx < 0 {
added = append(added, n)
} else if !n.Equal(&curC.Neighbors[idx]) {
log.WithFields(log.Fields{
"Topic": "Config",
}).Debugf("Current neighbor config:%v", curC.Neighbors[idx])
log.WithFields(log.Fields{
"Topic": "Config",
}).Debugf("New neighbor config:%v", n)
updated = append(updated, n)
}
}
for _, n := range curC.Neighbors {
if inSlice(n, newC.Neighbors) < 0 {
deleted = append(deleted, n)
}
}
return added, deleted, updated
}
func CheckPolicyDifference(currentPolicy *RoutingPolicy, newPolicy *RoutingPolicy) bool {
log.WithFields(log.Fields{
"Topic": "Config",
}).Debugf("Current policy:%v", currentPolicy)
log.WithFields(log.Fields{
"Topic": "Config",
}).Debugf("New policy:%v", newPolicy)
var result bool
if currentPolicy == nil && newPolicy == nil {
result = false
} else {
if currentPolicy != nil && newPolicy != nil {
result = !currentPolicy.Equal(newPolicy)
} else {
result = true
}
}
return result
}

View File

@@ -0,0 +1,264 @@
// Copyright (C) 2015 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 config
import (
"fmt"
"net"
"path/filepath"
"regexp"
"strconv"
"github.com/osrg/gobgp/pkg/packet/bgp"
)
// Returns config file type by retrieving extension from the given path.
// If no corresponding type found, returns the given def as the default value.
func detectConfigFileType(path, def string) string {
switch ext := filepath.Ext(path); ext {
case ".toml":
return "toml"
case ".yaml", ".yml":
return "yaml"
case ".json":
return "json"
default:
return def
}
}
// yaml is decoded as []interface{}
// but toml is decoded as []map[string]interface{}.
// currently, viper can't hide this difference.
// handle the difference here.
func extractArray(intf interface{}) ([]interface{}, error) {
if intf != nil {
list, ok := intf.([]interface{})
if ok {
return list, nil
}
l, ok := intf.([]map[string]interface{})
if !ok {
return nil, fmt.Errorf("invalid configuration: neither []interface{} nor []map[string]interface{}")
}
list = make([]interface{}, 0, len(l))
for _, m := range l {
list = append(list, m)
}
return list, nil
}
return nil, nil
}
func getIPv6LinkLocalAddress(ifname string) (string, error) {
ifi, err := net.InterfaceByName(ifname)
if err != nil {
return "", err
}
addrs, err := ifi.Addrs()
if err != nil {
return "", err
}
for _, addr := range addrs {
ip := addr.(*net.IPNet).IP
if ip.To4() == nil && ip.IsLinkLocalUnicast() {
return fmt.Sprintf("%s%%%s", ip.String(), ifname), nil
}
}
return "", fmt.Errorf("no ipv6 link local address for %s", ifname)
}
func (b *BgpConfigSet) getPeerGroup(n string) (*PeerGroup, error) {
if n == "" {
return nil, nil
}
for _, pg := range b.PeerGroups {
if n == pg.Config.PeerGroupName {
return &pg, nil
}
}
return nil, fmt.Errorf("no such peer-group: %s", n)
}
func (d *DynamicNeighbor) validate(b *BgpConfigSet) error {
if d.Config.PeerGroup == "" {
return fmt.Errorf("dynamic neighbor requires the peer group config")
}
if _, err := b.getPeerGroup(d.Config.PeerGroup); err != nil {
return err
}
if _, _, err := net.ParseCIDR(d.Config.Prefix); err != nil {
return fmt.Errorf("invalid dynamic neighbor prefix %s", d.Config.Prefix)
}
return nil
}
func (n *Neighbor) IsConfederationMember(g *Global) bool {
for _, member := range g.Confederation.Config.MemberAsList {
if member == n.Config.PeerAs {
return true
}
}
return false
}
func (n *Neighbor) IsConfederation(g *Global) bool {
if n.Config.PeerAs == g.Config.As {
return true
}
return n.IsConfederationMember(g)
}
func (n *Neighbor) IsEBGPPeer(g *Global) bool {
return n.Config.PeerAs != g.Config.As
}
func (n *Neighbor) CreateRfMap() map[bgp.RouteFamily]bgp.BGPAddPathMode {
rfMap := make(map[bgp.RouteFamily]bgp.BGPAddPathMode)
for _, af := range n.AfiSafis {
mode := bgp.BGP_ADD_PATH_NONE
if af.AddPaths.State.Receive {
mode |= bgp.BGP_ADD_PATH_RECEIVE
}
if af.AddPaths.State.SendMax > 0 {
mode |= bgp.BGP_ADD_PATH_SEND
}
rfMap[af.State.Family] = mode
}
return rfMap
}
func (n *Neighbor) GetAfiSafi(family bgp.RouteFamily) *AfiSafi {
for _, a := range n.AfiSafis {
if string(a.Config.AfiSafiName) == family.String() {
return &a
}
}
return nil
}
func (n *Neighbor) ExtractNeighborAddress() (string, error) {
addr := n.State.NeighborAddress
if addr == "" {
addr = n.Config.NeighborAddress
if addr == "" {
return "", fmt.Errorf("NeighborAddress is not configured")
}
}
return addr, nil
}
func (n *Neighbor) IsAddPathReceiveEnabled(family bgp.RouteFamily) bool {
for _, af := range n.AfiSafis {
if af.State.Family == family {
return af.AddPaths.State.Receive
}
}
return false
}
type AfiSafis []AfiSafi
func (c AfiSafis) ToRfList() ([]bgp.RouteFamily, error) {
rfs := make([]bgp.RouteFamily, 0, len(c))
for _, af := range c {
rfs = append(rfs, af.State.Family)
}
return rfs, nil
}
func inSlice(n Neighbor, b []Neighbor) int {
for i, nb := range b {
if nb.State.NeighborAddress == n.State.NeighborAddress {
return i
}
}
return -1
}
func existPeerGroup(n string, b []PeerGroup) int {
for i, nb := range b {
if nb.Config.PeerGroupName == n {
return i
}
}
return -1
}
func isAfiSafiChanged(x, y []AfiSafi) bool {
if len(x) != len(y) {
return true
}
m := make(map[string]bool)
for _, e := range x {
m[string(e.Config.AfiSafiName)] = true
}
for _, e := range y {
if !m[string(e.Config.AfiSafiName)] {
return true
}
}
return false
}
func (n *Neighbor) NeedsResendOpenMessage(new *Neighbor) bool {
return !n.Config.Equal(&new.Config) ||
!n.Transport.Config.Equal(&new.Transport.Config) ||
!n.AddPaths.Config.Equal(&new.AddPaths.Config) ||
!n.GracefulRestart.Config.Equal(&new.GracefulRestart.Config) ||
isAfiSafiChanged(n.AfiSafis, new.AfiSafis)
}
// TODO: these regexp are duplicated in api
var _regexpPrefixMaskLengthRange = regexp.MustCompile(`(\d+)\.\.(\d+)`)
func ParseMaskLength(prefix, mask string) (int, int, error) {
_, ipNet, err := net.ParseCIDR(prefix)
if err != nil {
return 0, 0, fmt.Errorf("invalid prefix: %s", prefix)
}
if mask == "" {
l, _ := ipNet.Mask.Size()
return l, l, nil
}
elems := _regexpPrefixMaskLengthRange.FindStringSubmatch(mask)
if len(elems) != 3 {
return 0, 0, fmt.Errorf("invalid mask length range: %s", mask)
}
// we've already checked the range is sane by regexp
min, _ := strconv.ParseUint(elems[1], 10, 8)
max, _ := strconv.ParseUint(elems[2], 10, 8)
if min > max {
return 0, 0, fmt.Errorf("invalid mask length range: %s", mask)
}
if ipv4 := ipNet.IP.To4(); ipv4 != nil {
f := func(i uint64) bool {
return i <= 32
}
if !f(min) || !f(max) {
return 0, 0, fmt.Errorf("ipv4 mask length range outside scope :%s", mask)
}
} else {
f := func(i uint64) bool {
return i <= 128
}
if !f(min) || !f(max) {
return 0, 0, fmt.Errorf("ipv6 mask length range outside scope :%s", mask)
}
}
return int(min), int(max), nil
}

186
vendor/github.com/osrg/gobgp/internal/pkg/table/adj.go generated vendored Normal file
View File

@@ -0,0 +1,186 @@
// Copyright (C) 2015 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 table
import (
"fmt"
"github.com/osrg/gobgp/pkg/packet/bgp"
)
type AdjRib struct {
accepted map[bgp.RouteFamily]int
table map[bgp.RouteFamily]map[string]*Path
}
func NewAdjRib(rfList []bgp.RouteFamily) *AdjRib {
table := make(map[bgp.RouteFamily]map[string]*Path)
for _, rf := range rfList {
table[rf] = make(map[string]*Path)
}
return &AdjRib{
table: table,
accepted: make(map[bgp.RouteFamily]int),
}
}
func (adj *AdjRib) Update(pathList []*Path) {
for _, path := range pathList {
if path == nil || path.IsEOR() {
continue
}
rf := path.GetRouteFamily()
key := fmt.Sprintf("%d:%s", path.GetNlri().PathIdentifier(), path.getPrefix())
old, found := adj.table[rf][key]
if path.IsWithdraw {
if found {
delete(adj.table[rf], key)
if !old.IsAsLooped() {
adj.accepted[rf]--
}
}
} else {
if found {
if old.IsAsLooped() && !path.IsAsLooped() {
adj.accepted[rf]++
} else if !old.IsAsLooped() && path.IsAsLooped() {
adj.accepted[rf]--
}
} else {
if !path.IsAsLooped() {
adj.accepted[rf]++
}
}
if found && old.Equal(path) {
path.setTimestamp(old.GetTimestamp())
}
adj.table[rf][key] = path
}
}
}
func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path {
pathList := make([]*Path, 0, adj.Count(rfList))
for _, rf := range rfList {
for _, rr := range adj.table[rf] {
if accepted && rr.IsAsLooped() {
continue
}
pathList = append(pathList, rr)
}
}
return pathList
}
func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int {
count := 0
for _, rf := range rfList {
if table, ok := adj.table[rf]; ok {
count += len(table)
}
}
return count
}
func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int {
count := 0
for _, rf := range rfList {
if n, ok := adj.accepted[rf]; ok {
count += n
}
}
return count
}
func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) {
for _, rf := range rfList {
if _, ok := adj.table[rf]; ok {
adj.table[rf] = make(map[string]*Path)
adj.accepted[rf] = 0
}
}
}
func (adj *AdjRib) DropStale(rfList []bgp.RouteFamily) []*Path {
pathList := make([]*Path, 0, adj.Count(rfList))
for _, rf := range rfList {
if table, ok := adj.table[rf]; ok {
for k, p := range table {
if p.IsStale() {
delete(table, k)
if !p.IsAsLooped() {
adj.accepted[rf]--
}
pathList = append(pathList, p.Clone(true))
}
}
}
}
return pathList
}
func (adj *AdjRib) StaleAll(rfList []bgp.RouteFamily) []*Path {
pathList := make([]*Path, 0)
for _, rf := range rfList {
if table, ok := adj.table[rf]; ok {
l := make([]*Path, 0, len(table))
for k, p := range table {
n := p.Clone(false)
n.MarkStale(true)
table[k] = n
l = append(l, n)
}
if len(l) > 0 {
pathList = append(pathList, l...)
}
}
}
return pathList
}
func (adj *AdjRib) Select(family bgp.RouteFamily, accepted bool, option ...TableSelectOption) (*Table, error) {
m := make(map[string][]*Path)
pl := adj.PathList([]bgp.RouteFamily{family}, accepted)
for _, path := range pl {
key := path.GetNlri().String()
if _, y := m[key]; y {
m[key] = append(m[key], path)
} else {
m[key] = []*Path{path}
}
}
d := make([]*Destination, 0, len(pl))
for _, l := range m {
d = append(d, NewDestination(l[0].GetNlri(), 0, l...))
}
tbl := NewTable(family, d...)
option = append(option, TableSelectOption{adj: true})
return tbl.Select(option...)
}
func (adj *AdjRib) TableInfo(family bgp.RouteFamily) (*TableInfo, error) {
if _, ok := adj.table[family]; !ok {
return nil, fmt.Errorf("%s unsupported", family)
}
c := adj.Count([]bgp.RouteFamily{family})
a := adj.Accepted([]bgp.RouteFamily{family})
return &TableInfo{
NumDestination: c,
NumPath: c,
NumAccepted: a,
}, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,502 @@
// 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 table
import (
"bytes"
"reflect"
"github.com/osrg/gobgp/pkg/packet/bgp"
log "github.com/sirupsen/logrus"
)
func UpdatePathAttrs2ByteAs(msg *bgp.BGPUpdate) error {
ps := msg.PathAttributes
msg.PathAttributes = make([]bgp.PathAttributeInterface, len(ps))
copy(msg.PathAttributes, ps)
var asAttr *bgp.PathAttributeAsPath
idx := 0
for i, attr := range msg.PathAttributes {
if a, ok := attr.(*bgp.PathAttributeAsPath); ok {
asAttr = a
idx = i
break
}
}
if asAttr == nil {
return nil
}
as4Params := make([]*bgp.As4PathParam, 0, len(asAttr.Value))
as2Params := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
mkAs4 := false
for _, param := range asAttr.Value {
segType := param.GetType()
asList := param.GetAS()
as2Path := make([]uint16, 0, len(asList))
for _, as := range asList {
if as > (1<<16)-1 {
mkAs4 = true
as2Path = append(as2Path, bgp.AS_TRANS)
} else {
as2Path = append(as2Path, uint16(as))
}
}
as2Params = append(as2Params, bgp.NewAsPathParam(segType, as2Path))
// RFC 6793 4.2.2 Generating Updates
//
// Whenever the AS path information contains the AS_CONFED_SEQUENCE or
// AS_CONFED_SET path segment, the NEW BGP speaker MUST exclude such
// path segments from the AS4_PATH attribute being constructed.
switch segType {
case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
// pass
default:
if as4param, ok := param.(*bgp.As4PathParam); ok {
as4Params = append(as4Params, as4param)
}
}
}
msg.PathAttributes[idx] = bgp.NewPathAttributeAsPath(as2Params)
if mkAs4 {
msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Path(as4Params))
}
return nil
}
func UpdatePathAttrs4ByteAs(msg *bgp.BGPUpdate) error {
var asAttr *bgp.PathAttributeAsPath
var as4Attr *bgp.PathAttributeAs4Path
asAttrPos := 0
as4AttrPos := 0
for i, attr := range msg.PathAttributes {
switch attr.(type) {
case *bgp.PathAttributeAsPath:
asAttr = attr.(*bgp.PathAttributeAsPath)
for j, param := range asAttr.Value {
as2Param, ok := param.(*bgp.AsPathParam)
if ok {
asPath := make([]uint32, 0, len(as2Param.AS))
for _, as := range as2Param.AS {
asPath = append(asPath, uint32(as))
}
as4Param := bgp.NewAs4PathParam(as2Param.Type, asPath)
asAttr.Value[j] = as4Param
}
}
asAttrPos = i
msg.PathAttributes[i] = asAttr
case *bgp.PathAttributeAs4Path:
as4AttrPos = i
as4Attr = attr.(*bgp.PathAttributeAs4Path)
}
}
if as4Attr != nil {
msg.PathAttributes = append(msg.PathAttributes[:as4AttrPos], msg.PathAttributes[as4AttrPos+1:]...)
}
if asAttr == nil || as4Attr == nil {
return nil
}
asLen := 0
asConfedLen := 0
asParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
for _, param := range asAttr.Value {
asLen += param.ASLen()
switch param.GetType() {
case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
asConfedLen++
case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
asConfedLen += len(param.GetAS())
}
asParams = append(asParams, param)
}
as4Len := 0
as4Params := make([]bgp.AsPathParamInterface, 0, len(as4Attr.Value))
if as4Attr != nil {
for _, p := range as4Attr.Value {
// RFC 6793 6. Error Handling
//
// the path segment types AS_CONFED_SEQUENCE and AS_CONFED_SET [RFC5065]
// MUST NOT be carried in the AS4_PATH attribute of an UPDATE message.
// A NEW BGP speaker that receives these path segment types in the AS4_PATH
// attribute of an UPDATE message from an OLD BGP speaker MUST discard
// these path segments, adjust the relevant attribute fields accordingly,
// and continue processing the UPDATE message.
// This case SHOULD be logged locally for analysis.
switch p.Type {
case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
typ := "CONFED_SEQ"
if p.Type == bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET {
typ = "CONFED_SET"
}
log.WithFields(log.Fields{
"Topic": "Table",
}).Warnf("AS4_PATH contains %s segment %s. ignore", typ, p.String())
continue
}
as4Len += p.ASLen()
as4Params = append(as4Params, p)
}
}
if asLen+asConfedLen < as4Len {
log.WithFields(log.Fields{
"Topic": "Table",
}).Warn("AS4_PATH is longer than AS_PATH. ignore AS4_PATH")
return nil
}
keepNum := asLen + asConfedLen - as4Len
newParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
for _, param := range asParams {
if keepNum-param.ASLen() >= 0 {
newParams = append(newParams, param)
keepNum -= param.ASLen()
} else {
// only SEQ param reaches here
newParams = append(newParams, bgp.NewAs4PathParam(param.GetType(), param.GetAS()[:keepNum]))
keepNum = 0
}
if keepNum <= 0 {
break
}
}
for _, param := range as4Params {
lastParam := newParams[len(newParams)-1]
lastParamAS := lastParam.GetAS()
paramType := param.GetType()
paramAS := param.GetAS()
if paramType == lastParam.GetType() && paramType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ {
if len(lastParamAS)+len(paramAS) > 255 {
newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS[:255-len(lastParamAS)]...))
newParams = append(newParams, bgp.NewAs4PathParam(paramType, paramAS[255-len(lastParamAS):]))
} else {
newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS...))
}
} else {
newParams = append(newParams, param)
}
}
newIntfParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
newIntfParams = append(newIntfParams, newParams...)
msg.PathAttributes[asAttrPos] = bgp.NewPathAttributeAsPath(newIntfParams)
return nil
}
func UpdatePathAggregator2ByteAs(msg *bgp.BGPUpdate) {
as := uint32(0)
var addr string
for i, attr := range msg.PathAttributes {
switch attr.(type) {
case *bgp.PathAttributeAggregator:
agg := attr.(*bgp.PathAttributeAggregator)
addr = agg.Value.Address.String()
if agg.Value.AS > (1<<16)-1 {
as = agg.Value.AS
msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(bgp.AS_TRANS), addr)
} else {
msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(agg.Value.AS), addr)
}
}
}
if as != 0 {
msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Aggregator(as, addr))
}
}
func UpdatePathAggregator4ByteAs(msg *bgp.BGPUpdate) error {
var aggAttr *bgp.PathAttributeAggregator
var agg4Attr *bgp.PathAttributeAs4Aggregator
agg4AttrPos := 0
for i, attr := range msg.PathAttributes {
switch attr.(type) {
case *bgp.PathAttributeAggregator:
attr := attr.(*bgp.PathAttributeAggregator)
if attr.Value.Askind == reflect.Uint16 {
aggAttr = attr
aggAttr.Value.Askind = reflect.Uint32
}
case *bgp.PathAttributeAs4Aggregator:
agg4Attr = attr.(*bgp.PathAttributeAs4Aggregator)
agg4AttrPos = i
}
}
if aggAttr == nil && agg4Attr == nil {
return nil
}
if aggAttr == nil && agg4Attr != nil {
return bgp.NewMessageError(bgp.BGP_ERROR_UPDATE_MESSAGE_ERROR, bgp.BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "AS4 AGGREGATOR attribute exists, but AGGREGATOR doesn't")
}
if agg4Attr != nil {
msg.PathAttributes = append(msg.PathAttributes[:agg4AttrPos], msg.PathAttributes[agg4AttrPos+1:]...)
aggAttr.Value.AS = agg4Attr.Value.AS
}
return nil
}
type cage struct {
attrsBytes []byte
paths []*Path
}
func newCage(b []byte, path *Path) *cage {
return &cage{
attrsBytes: b,
paths: []*Path{path},
}
}
type packerInterface interface {
add(*Path)
pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage
}
type packer struct {
eof bool
family bgp.RouteFamily
total uint32
}
type packerMP struct {
packer
paths []*Path
withdrawals []*Path
}
func (p *packerMP) add(path *Path) {
p.packer.total++
if path.IsEOR() {
p.packer.eof = true
return
}
if path.IsWithdraw {
p.withdrawals = append(p.withdrawals, path)
return
}
p.paths = append(p.paths, path)
}
func createMPReachMessage(path *Path) *bgp.BGPMessage {
oattrs := path.GetPathAttrs()
attrs := make([]bgp.PathAttributeInterface, 0, len(oattrs))
for _, a := range oattrs {
if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI {
attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(path.GetNexthop().String(), []bgp.AddrPrefixInterface{path.GetNlri()}))
} else {
attrs = append(attrs, a)
}
}
return bgp.NewBGPUpdateMessage(nil, attrs, nil)
}
func (p *packerMP) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
msgs := make([]*bgp.BGPMessage, 0, p.packer.total)
for _, path := range p.withdrawals {
nlris := []bgp.AddrPrefixInterface{path.GetNlri()}
msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeMpUnreachNLRI(nlris)}, nil))
}
for _, path := range p.paths {
msgs = append(msgs, createMPReachMessage(path))
}
if p.eof {
msgs = append(msgs, bgp.NewEndOfRib(p.family))
}
return msgs
}
func newPackerMP(f bgp.RouteFamily) *packerMP {
return &packerMP{
packer: packer{
family: f,
},
withdrawals: make([]*Path, 0),
paths: make([]*Path, 0),
}
}
type packerV4 struct {
packer
hashmap map[uint32][]*cage
mpPaths []*Path
withdrawals []*Path
}
func (p *packerV4) add(path *Path) {
p.packer.total++
if path.IsEOR() {
p.packer.eof = true
return
}
if path.IsWithdraw {
p.withdrawals = append(p.withdrawals, path)
return
}
if path.GetNexthop().To4() == nil {
// RFC 5549
p.mpPaths = append(p.mpPaths, path)
return
}
key := path.GetHash()
attrsB := bytes.NewBuffer(make([]byte, 0))
for _, v := range path.GetPathAttrs() {
b, _ := v.Serialize()
attrsB.Write(b)
}
if cages, y := p.hashmap[key]; y {
added := false
for _, c := range cages {
if bytes.Equal(c.attrsBytes, attrsB.Bytes()) {
c.paths = append(c.paths, path)
added = true
break
}
}
if !added {
p.hashmap[key] = append(p.hashmap[key], newCage(attrsB.Bytes(), path))
}
} else {
p.hashmap[key] = []*cage{newCage(attrsB.Bytes(), path)}
}
}
func (p *packerV4) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
split := func(max int, paths []*Path) ([]*bgp.IPAddrPrefix, []*Path) {
nlris := make([]*bgp.IPAddrPrefix, 0, max)
i := 0
if max > len(paths) {
max = len(paths)
}
for ; i < max; i++ {
nlris = append(nlris, paths[i].GetNlri().(*bgp.IPAddrPrefix))
}
return nlris, paths[i:]
}
addpathNLRILen := 0
if bgp.IsAddPathEnabled(false, p.packer.family, options) {
addpathNLRILen = 4
}
// Header + Update (WithdrawnRoutesLen +
// TotalPathAttributeLen + attributes + maxlen of NLRI).
// the max size of NLRI is 5bytes (plus 4bytes with addpath enabled)
maxNLRIs := func(attrsLen int) int {
return (bgp.BGP_MAX_MESSAGE_LENGTH - (19 + 2 + 2 + attrsLen)) / (5 + addpathNLRILen)
}
loop := func(attrsLen int, paths []*Path, cb func([]*bgp.IPAddrPrefix)) {
max := maxNLRIs(attrsLen)
var nlris []*bgp.IPAddrPrefix
for {
nlris, paths = split(max, paths)
if len(nlris) == 0 {
break
}
cb(nlris)
}
}
msgs := make([]*bgp.BGPMessage, 0, p.packer.total)
loop(0, p.withdrawals, func(nlris []*bgp.IPAddrPrefix) {
msgs = append(msgs, bgp.NewBGPUpdateMessage(nlris, nil, nil))
})
for _, cages := range p.hashmap {
for _, c := range cages {
paths := c.paths
attrs := paths[0].GetPathAttrs()
attrsLen := 0
for _, a := range attrs {
attrsLen += a.Len()
}
loop(attrsLen, paths, func(nlris []*bgp.IPAddrPrefix) {
msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, attrs, nlris))
})
}
}
for _, path := range p.mpPaths {
msgs = append(msgs, createMPReachMessage(path))
}
if p.eof {
msgs = append(msgs, bgp.NewEndOfRib(p.family))
}
return msgs
}
func newPackerV4(f bgp.RouteFamily) *packerV4 {
return &packerV4{
packer: packer{
family: f,
},
hashmap: make(map[uint32][]*cage),
withdrawals: make([]*Path, 0),
mpPaths: make([]*Path, 0),
}
}
func newPacker(f bgp.RouteFamily) packerInterface {
switch f {
case bgp.RF_IPv4_UC:
return newPackerV4(bgp.RF_IPv4_UC)
default:
return newPackerMP(f)
}
}
func CreateUpdateMsgFromPaths(pathList []*Path, options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
msgs := make([]*bgp.BGPMessage, 0, len(pathList))
m := make(map[bgp.RouteFamily]packerInterface)
for _, path := range pathList {
f := path.GetRouteFamily()
if _, y := m[f]; !y {
m[f] = newPacker(f)
}
m[f].add(path)
}
for _, p := range m {
msgs = append(msgs, p.pack(options...)...)
}
return msgs
}

1179
vendor/github.com/osrg/gobgp/internal/pkg/table/path.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

3895
vendor/github.com/osrg/gobgp/internal/pkg/table/policy.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

60
vendor/github.com/osrg/gobgp/internal/pkg/table/roa.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
// Copyright (C) 2016 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 table
import (
"fmt"
"net"
)
type IPPrefix struct {
Prefix net.IP
Length uint8
}
func (p *IPPrefix) String() string {
return fmt.Sprintf("%s/%d", p.Prefix, p.Length)
}
type ROA struct {
Family int
Prefix *IPPrefix
MaxLen uint8
AS uint32
Src string
}
func NewROA(family int, prefixByte []byte, prefixLen uint8, maxLen uint8, as uint32, src string) *ROA {
p := make([]byte, len(prefixByte))
copy(p, prefixByte)
return &ROA{
Family: family,
Prefix: &IPPrefix{
Prefix: p,
Length: prefixLen,
},
MaxLen: maxLen,
AS: as,
Src: src,
}
}
func (r *ROA) Equal(roa *ROA) bool {
if r.MaxLen == roa.MaxLen && r.Src == roa.Src && r.AS == roa.AS {
return true
}
return false
}

View File

@@ -0,0 +1,451 @@
// 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 table
import (
"fmt"
"net"
"strings"
"unsafe"
"github.com/armon/go-radix"
"github.com/osrg/gobgp/pkg/packet/bgp"
log "github.com/sirupsen/logrus"
)
type LookupOption uint8
const (
LOOKUP_EXACT LookupOption = iota
LOOKUP_LONGER
LOOKUP_SHORTER
)
type LookupPrefix struct {
Prefix string
LookupOption
}
type TableSelectOption struct {
ID string
AS uint32
LookupPrefixes []*LookupPrefix
VRF *Vrf
adj bool
Best bool
MultiPath bool
}
type Table struct {
routeFamily bgp.RouteFamily
destinations map[string]*Destination
}
func NewTable(rf bgp.RouteFamily, dsts ...*Destination) *Table {
t := &Table{
routeFamily: rf,
destinations: make(map[string]*Destination),
}
for _, dst := range dsts {
t.setDestination(dst)
}
return t
}
func (t *Table) GetRoutefamily() bgp.RouteFamily {
return t.routeFamily
}
func (t *Table) deletePathsByVrf(vrf *Vrf) []*Path {
pathList := make([]*Path, 0)
for _, dest := range t.destinations {
for _, p := range dest.knownPathList {
var rd bgp.RouteDistinguisherInterface
nlri := p.GetNlri()
switch nlri.(type) {
case *bgp.LabeledVPNIPAddrPrefix:
rd = nlri.(*bgp.LabeledVPNIPAddrPrefix).RD
case *bgp.LabeledVPNIPv6AddrPrefix:
rd = nlri.(*bgp.LabeledVPNIPv6AddrPrefix).RD
case *bgp.EVPNNLRI:
rd = nlri.(*bgp.EVPNNLRI).RD()
default:
return pathList
}
if p.IsLocal() && vrf.Rd.String() == rd.String() {
pathList = append(pathList, p.Clone(true))
break
}
}
}
return pathList
}
func (t *Table) deleteRTCPathsByVrf(vrf *Vrf, vrfs map[string]*Vrf) []*Path {
pathList := make([]*Path, 0)
if t.routeFamily != bgp.RF_RTC_UC {
return pathList
}
for _, target := range vrf.ImportRt {
lhs := target.String()
for _, dest := range t.destinations {
nlri := dest.GetNlri().(*bgp.RouteTargetMembershipNLRI)
rhs := nlri.RouteTarget.String()
if lhs == rhs && isLastTargetUser(vrfs, target) {
for _, p := range dest.knownPathList {
if p.IsLocal() {
pathList = append(pathList, p.Clone(true))
break
}
}
}
}
}
return pathList
}
func (t *Table) deleteDestByNlri(nlri bgp.AddrPrefixInterface) *Destination {
if dst := t.GetDestination(nlri); dst != nil {
t.deleteDest(dst)
return dst
}
return nil
}
func (t *Table) deleteDest(dest *Destination) {
destinations := t.GetDestinations()
delete(destinations, t.tableKey(dest.GetNlri()))
if len(destinations) == 0 {
t.destinations = make(map[string]*Destination)
}
}
func (t *Table) validatePath(path *Path) {
if path == nil {
log.WithFields(log.Fields{
"Topic": "Table",
"Key": t.routeFamily,
}).Error("path is nil")
}
if path.GetRouteFamily() != t.routeFamily {
log.WithFields(log.Fields{
"Topic": "Table",
"Key": t.routeFamily,
"Prefix": path.GetNlri().String(),
"ReceivedRf": path.GetRouteFamily().String(),
}).Error("Invalid path. RouteFamily mismatch")
}
if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH); attr != nil {
pathParam := attr.(*bgp.PathAttributeAsPath).Value
for _, as := range pathParam {
_, y := as.(*bgp.As4PathParam)
if !y {
log.WithFields(log.Fields{
"Topic": "Table",
"Key": t.routeFamily,
"As": as,
}).Fatal("AsPathParam must be converted to As4PathParam")
}
}
}
if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS4_PATH); attr != nil {
log.WithFields(log.Fields{
"Topic": "Table",
"Key": t.routeFamily,
}).Fatal("AS4_PATH must be converted to AS_PATH")
}
if path.GetNlri() == nil {
log.WithFields(log.Fields{
"Topic": "Table",
"Key": t.routeFamily,
}).Fatal("path's nlri is nil")
}
}
func (t *Table) getOrCreateDest(nlri bgp.AddrPrefixInterface) *Destination {
dest := t.GetDestination(nlri)
// If destination for given prefix does not exist we create it.
if dest == nil {
log.WithFields(log.Fields{
"Topic": "Table",
"Nlri": nlri,
}).Debugf("create Destination")
dest = NewDestination(nlri, 64)
t.setDestination(dest)
}
return dest
}
func (t *Table) GetDestinations() map[string]*Destination {
return t.destinations
}
func (t *Table) setDestinations(destinations map[string]*Destination) {
t.destinations = destinations
}
func (t *Table) GetDestination(nlri bgp.AddrPrefixInterface) *Destination {
dest, ok := t.destinations[t.tableKey(nlri)]
if ok {
return dest
} else {
return nil
}
}
func (t *Table) GetLongerPrefixDestinations(key string) ([]*Destination, error) {
results := make([]*Destination, 0, len(t.GetDestinations()))
switch t.routeFamily {
case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC, bgp.RF_IPv4_MPLS, bgp.RF_IPv6_MPLS:
_, prefix, err := net.ParseCIDR(key)
if err != nil {
return nil, err
}
k := CidrToRadixkey(prefix.String())
r := radix.New()
for _, dst := range t.GetDestinations() {
r.Insert(AddrToRadixkey(dst.nlri), dst)
}
r.WalkPrefix(k, func(s string, v interface{}) bool {
results = append(results, v.(*Destination))
return false
})
default:
for _, dst := range t.GetDestinations() {
results = append(results, dst)
}
}
return results, nil
}
func (t *Table) GetEvpnDestinationsWithRouteType(typ string) ([]*Destination, error) {
var routeType uint8
switch strings.ToLower(typ) {
case "a-d":
routeType = bgp.EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY
case "macadv":
routeType = bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT
case "multicast":
routeType = bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG
case "esi":
routeType = bgp.EVPN_ETHERNET_SEGMENT_ROUTE
case "prefix":
routeType = bgp.EVPN_IP_PREFIX
default:
return nil, fmt.Errorf("unsupported evpn route type: %s", typ)
}
destinations := t.GetDestinations()
results := make([]*Destination, 0, len(destinations))
switch t.routeFamily {
case bgp.RF_EVPN:
for _, dst := range destinations {
if nlri, ok := dst.nlri.(*bgp.EVPNNLRI); !ok {
return nil, fmt.Errorf("invalid evpn nlri type detected: %T", dst.nlri)
} else if nlri.RouteType == routeType {
results = append(results, dst)
}
}
default:
for _, dst := range destinations {
results = append(results, dst)
}
}
return results, nil
}
func (t *Table) setDestination(dst *Destination) {
t.destinations[t.tableKey(dst.nlri)] = dst
}
func (t *Table) tableKey(nlri bgp.AddrPrefixInterface) string {
switch T := nlri.(type) {
case *bgp.IPAddrPrefix:
b := make([]byte, 5)
copy(b, T.Prefix.To4())
b[4] = T.Length
return *(*string)(unsafe.Pointer(&b))
case *bgp.IPv6AddrPrefix:
b := make([]byte, 17)
copy(b, T.Prefix.To16())
b[16] = T.Length
return *(*string)(unsafe.Pointer(&b))
}
return nlri.String()
}
func (t *Table) Bests(id string, as uint32) []*Path {
paths := make([]*Path, 0, len(t.destinations))
for _, dst := range t.destinations {
path := dst.GetBestPath(id, as)
if path != nil {
paths = append(paths, path)
}
}
return paths
}
func (t *Table) MultiBests(id string) [][]*Path {
paths := make([][]*Path, 0, len(t.destinations))
for _, dst := range t.destinations {
path := dst.GetMultiBestPath(id)
if path != nil {
paths = append(paths, path)
}
}
return paths
}
func (t *Table) GetKnownPathList(id string, as uint32) []*Path {
paths := make([]*Path, 0, len(t.destinations))
for _, dst := range t.destinations {
paths = append(paths, dst.GetKnownPathList(id, as)...)
}
return paths
}
func (t *Table) Select(option ...TableSelectOption) (*Table, error) {
id := GLOBAL_RIB_NAME
var vrf *Vrf
adj := false
prefixes := make([]*LookupPrefix, 0, len(option))
best := false
mp := false
as := uint32(0)
for _, o := range option {
if o.ID != "" {
id = o.ID
}
if o.VRF != nil {
vrf = o.VRF
}
adj = o.adj
prefixes = append(prefixes, o.LookupPrefixes...)
best = o.Best
mp = o.MultiPath
as = o.AS
}
dOption := DestinationSelectOption{ID: id, AS: as, VRF: vrf, adj: adj, Best: best, MultiPath: mp}
r := &Table{
routeFamily: t.routeFamily,
destinations: make(map[string]*Destination),
}
if len(prefixes) != 0 {
switch t.routeFamily {
case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC:
f := func(prefixStr string) bool {
var nlri bgp.AddrPrefixInterface
if t.routeFamily == bgp.RF_IPv4_UC {
nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP, bgp.SAFI_UNICAST, prefixStr)
} else {
nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP6, bgp.SAFI_UNICAST, prefixStr)
}
if dst := t.GetDestination(nlri); dst != nil {
if d := dst.Select(dOption); d != nil {
r.setDestination(d)
return true
}
}
return false
}
for _, p := range prefixes {
key := p.Prefix
switch p.LookupOption {
case LOOKUP_LONGER:
ds, err := t.GetLongerPrefixDestinations(key)
if err != nil {
return nil, err
}
for _, dst := range ds {
if d := dst.Select(dOption); d != nil {
r.setDestination(d)
}
}
case LOOKUP_SHORTER:
addr, prefix, err := net.ParseCIDR(key)
if err != nil {
return nil, err
}
ones, _ := prefix.Mask.Size()
for i := ones; i >= 0; i-- {
_, prefix, _ := net.ParseCIDR(fmt.Sprintf("%s/%d", addr.String(), i))
f(prefix.String())
}
default:
if host := net.ParseIP(key); host != nil {
masklen := 32
if t.routeFamily == bgp.RF_IPv6_UC {
masklen = 128
}
for i := masklen; i >= 0; i-- {
_, prefix, err := net.ParseCIDR(fmt.Sprintf("%s/%d", key, i))
if err != nil {
return nil, err
}
if f(prefix.String()) {
break
}
}
} else {
f(key)
}
}
}
case bgp.RF_EVPN:
for _, p := range prefixes {
// Uses LookupPrefix.Prefix as EVPN Route Type string
ds, err := t.GetEvpnDestinationsWithRouteType(p.Prefix)
if err != nil {
return nil, err
}
for _, dst := range ds {
if d := dst.Select(dOption); d != nil {
r.setDestination(d)
}
}
}
default:
return nil, fmt.Errorf("route filtering is not supported for this family")
}
} else {
for _, dst := range t.GetDestinations() {
if d := dst.Select(dOption); d != nil {
r.setDestination(d)
}
}
}
return r, nil
}
type TableInfo struct {
NumDestination int
NumPath int
NumAccepted int
}
func (t *Table) Info(id string, as uint32) *TableInfo {
var numD, numP int
for _, d := range t.destinations {
ps := d.GetKnownPathList(id, as)
if len(ps) > 0 {
numD += 1
numP += len(ps)
}
}
return &TableInfo{
NumDestination: numD,
NumPath: numP,
}
}

View File

@@ -0,0 +1,370 @@
// 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 table
import (
"bytes"
"fmt"
"net"
"time"
farm "github.com/dgryski/go-farm"
log "github.com/sirupsen/logrus"
"github.com/osrg/gobgp/pkg/packet/bgp"
)
const (
GLOBAL_RIB_NAME = "global"
)
func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time) []*Path {
update := m.Body.(*bgp.BGPUpdate)
if y, f := update.IsEndOfRib(); y {
// this message has no normal updates or withdrawals.
return []*Path{NewEOR(f)}
}
adds := make([]bgp.AddrPrefixInterface, 0, len(update.NLRI))
for _, nlri := range update.NLRI {
adds = append(adds, nlri)
}
dels := make([]bgp.AddrPrefixInterface, 0, len(update.WithdrawnRoutes))
for _, nlri := range update.WithdrawnRoutes {
dels = append(dels, nlri)
}
attrs := make([]bgp.PathAttributeInterface, 0, len(update.PathAttributes))
var reach *bgp.PathAttributeMpReachNLRI
for _, attr := range update.PathAttributes {
switch a := attr.(type) {
case *bgp.PathAttributeMpReachNLRI:
reach = a
case *bgp.PathAttributeMpUnreachNLRI:
l := make([]bgp.AddrPrefixInterface, 0, len(a.Value))
l = append(l, a.Value...)
dels = append(dels, l...)
default:
attrs = append(attrs, attr)
}
}
listLen := len(adds) + len(dels)
if reach != nil {
listLen += len(reach.Value)
}
var hash uint32
if len(adds) > 0 || reach != nil {
total := bytes.NewBuffer(make([]byte, 0))
for _, a := range attrs {
b, _ := a.Serialize()
total.Write(b)
}
hash = farm.Hash32(total.Bytes())
}
pathList := make([]*Path, 0, listLen)
for _, nlri := range adds {
p := NewPath(peerInfo, nlri, false, attrs, timestamp, false)
p.SetHash(hash)
pathList = append(pathList, p)
}
if reach != nil {
reachAttrs := make([]bgp.PathAttributeInterface, len(attrs)+1)
copy(reachAttrs, attrs)
// we sort attributes when creating a bgp message from paths
reachAttrs[len(reachAttrs)-1] = reach
for _, nlri := range reach.Value {
p := NewPath(peerInfo, nlri, false, reachAttrs, timestamp, false)
p.SetHash(hash)
pathList = append(pathList, p)
}
}
for _, nlri := range dels {
p := NewPath(peerInfo, nlri, true, []bgp.PathAttributeInterface{}, timestamp, false)
pathList = append(pathList, p)
}
return pathList
}
type TableManager struct {
Tables map[bgp.RouteFamily]*Table
Vrfs map[string]*Vrf
rfList []bgp.RouteFamily
}
func NewTableManager(rfList []bgp.RouteFamily) *TableManager {
t := &TableManager{
Tables: make(map[bgp.RouteFamily]*Table),
Vrfs: make(map[string]*Vrf),
rfList: rfList,
}
for _, rf := range rfList {
t.Tables[rf] = NewTable(rf)
}
return t
}
func (manager *TableManager) GetRFlist() []bgp.RouteFamily {
return manager.rfList
}
func (manager *TableManager) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInterface, importRt, exportRt []bgp.ExtendedCommunityInterface, info *PeerInfo) ([]*Path, error) {
if _, ok := manager.Vrfs[name]; ok {
return nil, fmt.Errorf("vrf %s already exists", name)
}
log.WithFields(log.Fields{
"Topic": "Vrf",
"Key": name,
"Rd": rd,
"ImportRt": importRt,
"ExportRt": exportRt,
}).Debugf("add vrf")
manager.Vrfs[name] = &Vrf{
Name: name,
Id: id,
Rd: rd,
ImportRt: importRt,
ExportRt: exportRt,
}
msgs := make([]*Path, 0, len(importRt))
nexthop := "0.0.0.0"
for _, target := range importRt {
nlri := bgp.NewRouteTargetMembershipNLRI(info.AS, target)
pattr := make([]bgp.PathAttributeInterface, 0, 2)
pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP))
pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
msgs = append(msgs, NewPath(info, nlri, false, pattr, time.Now(), false))
}
return msgs, nil
}
func (manager *TableManager) DeleteVrf(name string) ([]*Path, error) {
if _, ok := manager.Vrfs[name]; !ok {
return nil, fmt.Errorf("vrf %s not found", name)
}
msgs := make([]*Path, 0)
vrf := manager.Vrfs[name]
for _, t := range manager.Tables {
msgs = append(msgs, t.deletePathsByVrf(vrf)...)
}
log.WithFields(log.Fields{
"Topic": "Vrf",
"Key": vrf.Name,
"Rd": vrf.Rd,
"ImportRt": vrf.ImportRt,
"ExportRt": vrf.ExportRt,
}).Debugf("delete vrf")
delete(manager.Vrfs, name)
rtcTable := manager.Tables[bgp.RF_RTC_UC]
msgs = append(msgs, rtcTable.deleteRTCPathsByVrf(vrf, manager.Vrfs)...)
return msgs, nil
}
func (tm *TableManager) update(newPath *Path) *Update {
t := tm.Tables[newPath.GetRouteFamily()]
t.validatePath(newPath)
dst := t.getOrCreateDest(newPath.GetNlri())
u := dst.Calculate(newPath)
if len(dst.knownPathList) == 0 {
t.deleteDest(dst)
}
return u
}
func (manager *TableManager) GetPathListByPeer(info *PeerInfo, rf bgp.RouteFamily) []*Path {
if t, ok := manager.Tables[rf]; ok {
pathList := make([]*Path, 0, len(t.destinations))
for _, dst := range t.destinations {
for _, p := range dst.knownPathList {
if p.GetSource().Equal(info) {
pathList = append(pathList, p)
}
}
}
return pathList
}
return nil
}
func (manager *TableManager) Update(newPath *Path) []*Update {
if newPath == nil || newPath.IsEOR() {
return nil
}
// Except for a special case with EVPN, we'll have one destination.
updates := make([]*Update, 0, 1)
family := newPath.GetRouteFamily()
if _, ok := manager.Tables[family]; ok {
updates = append(updates, manager.update(newPath))
if family == bgp.RF_EVPN {
for _, p := range manager.handleMacMobility(newPath) {
updates = append(updates, manager.update(p))
}
}
}
return updates
}
// EVPN MAC MOBILITY HANDLING
//
// RFC7432 15. MAC Mobility
//
// A PE receiving a MAC/IP Advertisement route for a MAC address with a
// different Ethernet segment identifier and a higher sequence number
// than that which it had previously advertised withdraws its MAC/IP
// Advertisement route.
func (manager *TableManager) handleMacMobility(path *Path) []*Path {
pathList := make([]*Path, 0)
nlri := path.GetNlri().(*bgp.EVPNNLRI)
if path.IsWithdraw || path.IsLocal() || nlri.RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
return nil
}
for _, path2 := range manager.GetPathList(GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}) {
if !path2.IsLocal() || path2.GetNlri().(*bgp.EVPNNLRI).RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
continue
}
f := func(p *Path) (bgp.EthernetSegmentIdentifier, net.HardwareAddr, int) {
nlri := p.GetNlri().(*bgp.EVPNNLRI)
d := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
ecs := p.GetExtCommunities()
seq := -1
for _, ec := range ecs {
if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY {
seq = int(ec.(*bgp.MacMobilityExtended).Sequence)
break
}
}
return d.ESI, d.MacAddress, seq
}
e1, m1, s1 := f(path)
e2, m2, s2 := f(path2)
if bytes.Equal(m1, m2) && !bytes.Equal(e1.Value, e2.Value) && s1 > s2 {
pathList = append(pathList, path2.Clone(true))
}
}
return pathList
}
func (manager *TableManager) tables(list ...bgp.RouteFamily) []*Table {
l := make([]*Table, 0, len(manager.Tables))
if len(list) == 0 {
for _, v := range manager.Tables {
l = append(l, v)
}
return l
}
for _, f := range list {
if t, ok := manager.Tables[f]; ok {
l = append(l, t)
}
}
return l
}
func (manager *TableManager) getDestinationCount(rfList []bgp.RouteFamily) int {
count := 0
for _, t := range manager.tables(rfList...) {
count += len(t.GetDestinations())
}
return count
}
func (manager *TableManager) GetBestPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path {
if SelectionOptions.DisableBestPathSelection {
// Note: If best path selection disabled, there is no best path.
return nil
}
paths := make([]*Path, 0, manager.getDestinationCount(rfList))
for _, t := range manager.tables(rfList...) {
paths = append(paths, t.Bests(id, as)...)
}
return paths
}
func (manager *TableManager) GetBestMultiPathList(id string, rfList []bgp.RouteFamily) [][]*Path {
if !UseMultiplePaths.Enabled || SelectionOptions.DisableBestPathSelection {
// Note: If multi path not enabled or best path selection disabled,
// there is no best multi path.
return nil
}
paths := make([][]*Path, 0, manager.getDestinationCount(rfList))
for _, t := range manager.tables(rfList...) {
paths = append(paths, t.MultiBests(id)...)
}
return paths
}
func (manager *TableManager) GetPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path {
paths := make([]*Path, 0, manager.getDestinationCount(rfList))
for _, t := range manager.tables(rfList...) {
paths = append(paths, t.GetKnownPathList(id, as)...)
}
return paths
}
func (manager *TableManager) GetPathListWithNexthop(id string, rfList []bgp.RouteFamily, nexthop net.IP) []*Path {
paths := make([]*Path, 0, manager.getDestinationCount(rfList))
for _, rf := range rfList {
if t, ok := manager.Tables[rf]; ok {
for _, path := range t.GetKnownPathList(id, 0) {
if path.GetNexthop().Equal(nexthop) {
paths = append(paths, path)
}
}
}
}
return paths
}
func (manager *TableManager) GetPathListWithSource(id string, rfList []bgp.RouteFamily, source *PeerInfo) []*Path {
paths := make([]*Path, 0, manager.getDestinationCount(rfList))
for _, rf := range rfList {
if t, ok := manager.Tables[rf]; ok {
for _, path := range t.GetKnownPathList(id, 0) {
if path.GetSource().Equal(source) {
paths = append(paths, path)
}
}
}
}
return paths
}
func (manager *TableManager) GetDestination(path *Path) *Destination {
if path == nil {
return nil
}
family := path.GetRouteFamily()
t, ok := manager.Tables[family]
if !ok {
return nil
}
return t.GetDestination(path.GetNlri())
}
func (manager *TableManager) TableInfo(id string, as uint32, family bgp.RouteFamily) (*TableInfo, error) {
t, ok := manager.Tables[family]
if !ok {
return nil, fmt.Errorf("address family %s is not configured", family)
}
return t.Info(id, as), nil
}

53
vendor/github.com/osrg/gobgp/internal/pkg/table/vrf.go generated vendored Normal file
View File

@@ -0,0 +1,53 @@
// Copyright (C) 2014-2016 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 table
import (
"github.com/osrg/gobgp/pkg/packet/bgp"
)
type Vrf struct {
Name string
Id uint32
Rd bgp.RouteDistinguisherInterface
ImportRt []bgp.ExtendedCommunityInterface
ExportRt []bgp.ExtendedCommunityInterface
}
func (v *Vrf) Clone() *Vrf {
f := func(rt []bgp.ExtendedCommunityInterface) []bgp.ExtendedCommunityInterface {
l := make([]bgp.ExtendedCommunityInterface, 0, len(rt))
return append(l, rt...)
}
return &Vrf{
Name: v.Name,
Id: v.Id,
Rd: v.Rd,
ImportRt: f(v.ImportRt),
ExportRt: f(v.ExportRt),
}
}
func isLastTargetUser(vrfs map[string]*Vrf, target bgp.ExtendedCommunityInterface) bool {
for _, vrf := range vrfs {
for _, rt := range vrf.ImportRt {
if target.String() == rt.String() {
return false
}
}
}
return true
}

View File

@@ -0,0 +1,17 @@
// Code generated by "stringer -type=AFI"; DO NOT EDIT.
package zebra
import "strconv"
const _AFI_name = "AFI_IPAFI_IP6AFI_ETHERAFI_MAX"
var _AFI_index = [...]uint8{0, 6, 13, 22, 29}
func (i AFI) String() string {
i -= 1
if i >= AFI(len(_AFI_index)-1) {
return "AFI(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _AFI_name[_AFI_index[i]:_AFI_index[i+1]]
}

View File

@@ -0,0 +1,16 @@
// Code generated by "stringer -type=API_TYPE"; DO NOT EDIT.
package zebra
import "strconv"
const _API_TYPE_name = "FRR_ZAPI5_INTERFACE_ADDFRR_ZAPI5_INTERFACE_DELETEFRR_ZAPI5_INTERFACE_ADDRESS_ADDFRR_ZAPI5_INTERFACE_ADDRESS_DELETEFRR_ZAPI5_INTERFACE_UPFRR_ZAPI5_INTERFACE_DOWNFRR_ZAPI5_INTERFACE_SET_MASTERFRR_ZAPI5_ROUTE_ADDFRR_ZAPI5_ROUTE_DELETEFRR_ZAPI5_ROUTE_NOTIFY_OWNERFRR_ZAPI5_IPV4_ROUTE_ADDFRR_ZAPI5_IPV4_ROUTE_DELETEFRR_ZAPI5_IPV6_ROUTE_ADDFRR_ZAPI5_IPV6_ROUTE_DELETEFRR_ZAPI5_REDISTRIBUTE_ADDFRR_ZAPI5_REDISTRIBUTE_DELETEFRR_ZAPI5_REDISTRIBUTE_DEFAULT_ADDFRR_ZAPI5_REDISTRIBUTE_DEFAULT_DELETEFRR_ZAPI5_ROUTER_ID_ADDFRR_ZAPI5_ROUTER_ID_DELETEFRR_ZAPI5_ROUTER_ID_UPDATEFRR_ZAPI5_HELLOFRR_ZAPI5_CAPABILITIESFRR_ZAPI5_NEXTHOP_REGISTERFRR_ZAPI5_NEXTHOP_UNREGISTERFRR_ZAPI5_NEXTHOP_UPDATEFRR_ZAPI5_INTERFACE_NBR_ADDRESS_ADDFRR_ZAPI5_INTERFACE_NBR_ADDRESS_DELETEFRR_ZAPI5_INTERFACE_BFD_DEST_UPDATEFRR_ZAPI5_IMPORT_ROUTE_REGISTERFRR_ZAPI5_IMPORT_ROUTE_UNREGISTERFRR_ZAPI5_IMPORT_CHECK_UPDATEFRR_ZAPI5_IPV4_ROUTE_IPV6_NEXTHOP_ADDFRR_ZAPI5_BFD_DEST_REGISTERFRR_ZAPI5_BFD_DEST_DEREGISTERFRR_ZAPI5_BFD_DEST_UPDATEFRR_ZAPI5_BFD_DEST_REPLAYFRR_ZAPI5_REDISTRIBUTE_ROUTE_ADDFRR_ZAPI5_REDISTRIBUTE_ROUTE_DELFRR_ZAPI5_VRF_UNREGISTERFRR_ZAPI5_VRF_ADDFRR_ZAPI5_VRF_DELETEFRR_ZAPI5_VRF_LABELFRR_ZAPI5_INTERFACE_VRF_UPDATEFRR_ZAPI5_BFD_CLIENT_REGISTERFRR_ZAPI5_INTERFACE_ENABLE_RADVFRR_ZAPI5_INTERFACE_DISABLE_RADVFRR_ZAPI5_IPV4_NEXTHOP_LOOKUP_MRIBFRR_ZAPI5_INTERFACE_LINK_PARAMSFRR_ZAPI5_MPLS_LABELS_ADDFRR_ZAPI5_MPLS_LABELS_DELETEFRR_ZAPI5_IPMR_ROUTE_STATSFRR_ZAPI5_LABEL_MANAGER_CONNECTFRR_ZAPI5_GET_LABEL_CHUNKFRR_ZAPI5_RELEASE_LABEL_CHUNKFRR_ZAPI5_FEC_REGISTERFRR_ZAPI5_FEC_UNREGISTERFRR_ZAPI5_FEC_UPDATEFRR_ZAPI5_ADVERTISE_DEFAULT_GWFRR_ZAPI5_ADVERTISE_SUBNETFRR_ZAPI5_ADVERTISE_ALL_VNIFRR_ZAPI5_VNI_ADDFRR_ZAPI5_VNI_DELFRR_ZAPI5_L3VNI_ADDFRR_ZAPI5_L3VNI_DELFRR_ZAPI5_REMOTE_VTEP_ADDFRR_ZAPI5_REMOTE_VTEP_DELFRR_ZAPI5_MACIP_ADDFRR_ZAPI5_MACIP_DELFRR_ZAPI5_IP_PREFIX_ROUTE_ADDFRR_ZAPI5_IP_PREFIX_ROUTE_DELFRR_ZAPI5_REMOTE_MACIP_ADDFRR_ZAPI5_REMOTE_MACIP_DELFRR_ZAPI5_PW_ADDFRR_ZAPI5_PW_DELETEFRR_ZAPI5_PW_SETFRR_ZAPI5_PW_UNSETFRR_ZAPI5_PW_STATUS_UPDATEFRR_ZAPI5_RULE_ADDFRR_ZAPI5_RULE_DELETEFRR_ZAPI5_RULE_NOTIFY_OWNERFRR_ZAPI5_TABLE_MANAGER_CONNECTFRR_ZAPI5_GET_TABLE_CHUNKFRR_ZAPI5_RELEASE_TABLE_CHUNKFRR_ZAPI5_IPSET_CREATEFRR_ZAPI5_IPSET_DESTROYFRR_ZAPI5_IPSET_ENTRY_ADDFRR_ZAPI5_IPSET_ENTRY_DELETEFRR_ZAPI5_IPSET_NOTIFY_OWNERFRR_ZAPI5_IPSET_ENTRY_NOTIFY_OWNERFRR_ZAPI5_IPTABLE_ADDFRR_ZAPI5_IPTABLE_DELETEFRR_ZAPI5_IPTABLE_NOTIFY_OWNER"
var _API_TYPE_index = [...]uint16{0, 23, 49, 80, 114, 136, 160, 190, 209, 231, 259, 283, 310, 334, 361, 387, 416, 450, 487, 510, 536, 562, 577, 599, 625, 653, 677, 712, 750, 785, 816, 849, 878, 915, 942, 971, 996, 1021, 1053, 1085, 1109, 1126, 1146, 1165, 1195, 1224, 1255, 1287, 1321, 1352, 1377, 1405, 1431, 1462, 1487, 1516, 1538, 1562, 1582, 1612, 1638, 1665, 1682, 1699, 1718, 1737, 1762, 1787, 1806, 1825, 1854, 1883, 1909, 1935, 1951, 1970, 1986, 2004, 2030, 2048, 2069, 2096, 2127, 2152, 2181, 2203, 2226, 2251, 2279, 2307, 2341, 2362, 2386, 2416}
func (i API_TYPE) String() string {
if i >= API_TYPE(len(_API_TYPE_index)-1) {
return "API_TYPE(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _API_TYPE_name[_API_TYPE_index[i]:_API_TYPE_index[i+1]]
}

View File

@@ -0,0 +1,16 @@
// Code generated by "stringer -type=LINK_TYPE"; DO NOT EDIT.
package zebra
import "strconv"
const _LINK_TYPE_name = "LINK_TYPE_UNKNOWNLINK_TYPE_ETHERLINK_TYPE_EETHERLINK_TYPE_AX25LINK_TYPE_PRONETLINK_TYPE_IEEE802LINK_TYPE_ARCNETLINK_TYPE_APPLETLKLINK_TYPE_DLCILINK_TYPE_ATMLINK_TYPE_METRICOMLINK_TYPE_IEEE1394LINK_TYPE_EUI64LINK_TYPE_INFINIBANDLINK_TYPE_SLIPLINK_TYPE_CSLIPLINK_TYPE_SLIP6LINK_TYPE_CSLIP6LINK_TYPE_RSRVDLINK_TYPE_ADAPTLINK_TYPE_ROSELINK_TYPE_X25LINK_TYPE_PPPLINK_TYPE_CHDLCLINK_TYPE_LAPBLINK_TYPE_RAWHDLCLINK_TYPE_IPIPLINK_TYPE_IPIP6LINK_TYPE_FRADLINK_TYPE_SKIPLINK_TYPE_LOOPBACKLINK_TYPE_LOCALTLKLINK_TYPE_FDDILINK_TYPE_SITLINK_TYPE_IPDDPLINK_TYPE_IPGRELINK_TYPE_IP6GRELINK_TYPE_PIMREGLINK_TYPE_HIPPILINK_TYPE_ECONETLINK_TYPE_IRDALINK_TYPE_FCPPLINK_TYPE_FCALLINK_TYPE_FCPLLINK_TYPE_FCFABRICLINK_TYPE_IEEE802_TRLINK_TYPE_IEEE80211LINK_TYPE_IEEE80211_RADIOTAPLINK_TYPE_IEEE802154LINK_TYPE_IEEE802154_PHY"
var _LINK_TYPE_index = [...]uint16{0, 17, 32, 48, 62, 78, 95, 111, 129, 143, 156, 174, 192, 207, 227, 241, 256, 271, 287, 302, 317, 331, 344, 357, 372, 386, 403, 417, 432, 446, 460, 478, 496, 510, 523, 538, 553, 569, 585, 600, 616, 630, 644, 658, 672, 690, 710, 729, 757, 777, 801}
func (i LINK_TYPE) String() string {
if i >= LINK_TYPE(len(_LINK_TYPE_index)-1) {
return "LINK_TYPE(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _LINK_TYPE_name[_LINK_TYPE_index[i]:_LINK_TYPE_index[i+1]]
}

View File

@@ -0,0 +1,41 @@
// Code generated by "stringer -type=NEXTHOP_FLAG"; DO NOT EDIT.
package zebra
import "strconv"
const (
_NEXTHOP_FLAG_name_0 = "NEXTHOP_FLAG_ACTIVENEXTHOP_FLAG_FIB"
_NEXTHOP_FLAG_name_1 = "NEXTHOP_FLAG_RECURSIVE"
_NEXTHOP_FLAG_name_2 = "NEXTHOP_FLAG_ONLINK"
_NEXTHOP_FLAG_name_3 = "NEXTHOP_FLAG_MATCHED"
_NEXTHOP_FLAG_name_4 = "NEXTHOP_FLAG_FILTERED"
_NEXTHOP_FLAG_name_5 = "NEXTHOP_FLAG_DUPLICATE"
_NEXTHOP_FLAG_name_6 = "NEXTHOP_FLAG_EVPN_RVTEP"
)
var (
_NEXTHOP_FLAG_index_0 = [...]uint8{0, 19, 35}
)
func (i NEXTHOP_FLAG) String() string {
switch {
case 1 <= i && i <= 2:
i -= 1
return _NEXTHOP_FLAG_name_0[_NEXTHOP_FLAG_index_0[i]:_NEXTHOP_FLAG_index_0[i+1]]
case i == 4:
return _NEXTHOP_FLAG_name_1
case i == 8:
return _NEXTHOP_FLAG_name_2
case i == 16:
return _NEXTHOP_FLAG_name_3
case i == 32:
return _NEXTHOP_FLAG_name_4
case i == 64:
return _NEXTHOP_FLAG_name_5
case i == 128:
return _NEXTHOP_FLAG_name_6
default:
return "NEXTHOP_FLAG(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

@@ -0,0 +1,17 @@
// Code generated by "stringer -type=NEXTHOP_TYPE"; DO NOT EDIT.
package zebra
import "strconv"
const _NEXTHOP_TYPE_name = "FRR_NEXTHOP_TYPE_IFINDEXFRR_NEXTHOP_TYPE_IPV4FRR_NEXTHOP_TYPE_IPV4_IFINDEXFRR_NEXTHOP_TYPE_IPV6FRR_NEXTHOP_TYPE_IPV6_IFINDEXFRR_NEXTHOP_TYPE_BLACKHOLENEXTHOP_TYPE_IPV6_IFINDEXNEXTHOP_TYPE_IPV6_IFNAMENEXTHOP_TYPE_BLACKHOLE"
var _NEXTHOP_TYPE_index = [...]uint8{0, 24, 45, 74, 95, 124, 150, 175, 199, 221}
func (i NEXTHOP_TYPE) String() string {
i -= 1
if i >= NEXTHOP_TYPE(len(_NEXTHOP_TYPE_index)-1) {
return "NEXTHOP_TYPE(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _NEXTHOP_TYPE_name[_NEXTHOP_TYPE_index[i]:_NEXTHOP_TYPE_index[i+1]]
}

View File

@@ -0,0 +1,16 @@
// Code generated by "stringer -type=PTM_ENABLE"; DO NOT EDIT.
package zebra
import "strconv"
const _PTM_ENABLE_name = "PTM_ENABLE_OFFPTM_ENABLE_ONPTM_ENABLE_UNSPEC"
var _PTM_ENABLE_index = [...]uint8{0, 14, 27, 44}
func (i PTM_ENABLE) String() string {
if i >= PTM_ENABLE(len(_PTM_ENABLE_index)-1) {
return "PTM_ENABLE(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _PTM_ENABLE_name[_PTM_ENABLE_index[i]:_PTM_ENABLE_index[i+1]]
}

View File

@@ -0,0 +1,16 @@
// Code generated by "stringer -type=PTM_STATUS"; DO NOT EDIT.
package zebra
import "strconv"
const _PTM_STATUS_name = "PTM_STATUS_DOWNPTM_STATUS_UPPTM_STATUS_UNKNOWN"
var _PTM_STATUS_index = [...]uint8{0, 15, 28, 46}
func (i PTM_STATUS) String() string {
if i >= PTM_STATUS(len(_PTM_STATUS_index)-1) {
return "PTM_STATUS(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _PTM_STATUS_name[_PTM_STATUS_index[i]:_PTM_STATUS_index[i+1]]
}

View File

@@ -0,0 +1,16 @@
// Code generated by "stringer -type=ROUTE_TYPE"; DO NOT EDIT.
package zebra
import "strconv"
const _ROUTE_TYPE_name = "FRR_ZAPI5_ROUTE_SYSTEMFRR_ZAPI5_ROUTE_KERNELFRR_ZAPI5_ROUTE_CONNECTFRR_ZAPI5_ROUTE_STATICFRR_ZAPI5_ROUTE_RIPFRR_ZAPI5_ROUTE_RIPNGFRR_ZAPI5_ROUTE_OSPFFRR_ZAPI5_ROUTE_OSPF6FRR_ZAPI5_ROUTE_ISISFRR_ZAPI5_ROUTE_BGPFRR_ZAPI5_ROUTE_PIMFRR_ZAPI5_ROUTE_EIGRPFRR_ZAPI5_ROUTE_NHRPFRR_ZAPI5_ROUTE_HSLSFRR_ZAPI5_ROUTE_OLSRFRR_ZAPI5_ROUTE_TABLEFRR_ZAPI5_ROUTE_LDPFRR_ZAPI5_ROUTE_VNCFRR_ZAPI5_ROUTE_VNC_DIRECTFRR_ZAPI5_ROUTE_VNC_DIRECT_RHFRR_ZAPI5_ROUTE_BGP_DIRECTFRR_ZAPI5_ROUTE_BGP_DIRECT_EXTFRR_ZAPI5_ROUTE_BABELFRR_ZAPI5_ROUTE_SHARPFRR_ZAPI5_ROUTE_PBRFRR_ZAPI5_ROUTE_ALLFRR_ZAPI5_ROUTE_MAX"
var _ROUTE_TYPE_index = [...]uint16{0, 22, 44, 67, 89, 108, 129, 149, 170, 190, 209, 228, 249, 269, 289, 309, 330, 349, 368, 394, 423, 449, 479, 500, 521, 540, 559, 578}
func (i ROUTE_TYPE) String() string {
if i >= ROUTE_TYPE(len(_ROUTE_TYPE_index)-1) {
return "ROUTE_TYPE(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ROUTE_TYPE_name[_ROUTE_TYPE_index[i]:_ROUTE_TYPE_index[i+1]]
}

View File

@@ -0,0 +1,17 @@
// Code generated by "stringer -type=SAFI"; DO NOT EDIT.
package zebra
import "strconv"
const _SAFI_name = "SAFI_UNICASTSAFI_MULTICASTSAFI_RESERVED_3SAFI_MPLS_VPNSAFI_MAX"
var _SAFI_index = [...]uint8{0, 12, 26, 41, 54, 62}
func (i SAFI) String() string {
i -= 1
if i >= SAFI(len(_SAFI_index)-1) {
return "SAFI(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _SAFI_name[_SAFI_index[i]:_SAFI_index[i+1]]
}

2534
vendor/github.com/osrg/gobgp/internal/pkg/zebra/zapi.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
// Copyright (C) 2014, 2015 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.
// +build freebsd netbsd openbsd
package zebra
import (
"strings"
"syscall"
)
func intfflag2string(flag uint64) string {
ss := make([]string, 0, 10)
if flag&syscall.IFF_UP > 0 {
ss = append(ss, "UP")
}
if flag&syscall.IFF_BROADCAST > 0 {
ss = append(ss, "BROADCAST")
}
if flag&syscall.IFF_DEBUG > 0 {
ss = append(ss, "DEBUG")
}
if flag&syscall.IFF_LOOPBACK > 0 {
ss = append(ss, "LOOPBACK")
}
if flag&syscall.IFF_POINTOPOINT > 0 {
ss = append(ss, "POINTOPOINT")
}
if flag&syscall.IFF_RUNNING > 0 {
ss = append(ss, "RUNNING")
}
if flag&syscall.IFF_NOARP > 0 {
ss = append(ss, "NOARP")
}
if flag&syscall.IFF_PROMISC > 0 {
ss = append(ss, "PROMISC")
}
if flag&syscall.IFF_ALLMULTI > 0 {
ss = append(ss, "ALLMULTI")
}
if flag&syscall.IFF_MULTICAST > 0 {
ss = append(ss, "MULTICAST")
}
return strings.Join(ss, " | ")
}

View File

@@ -0,0 +1,59 @@
// Copyright (C) 2014, 2015 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 zebra
import (
"strings"
"syscall"
)
func intfflag2string(flag uint64) string {
ss := make([]string, 0, 10)
if flag&syscall.IFF_UP > 0 {
ss = append(ss, "UP")
}
if flag&syscall.IFF_BROADCAST > 0 {
ss = append(ss, "BROADCAST")
}
if flag&syscall.IFF_DEBUG > 0 {
ss = append(ss, "DEBUG")
}
if flag&syscall.IFF_LOOPBACK > 0 {
ss = append(ss, "LOOPBACK")
}
if flag&syscall.IFF_POINTOPOINT > 0 {
ss = append(ss, "POINTOPOINT")
}
if flag&syscall.IFF_NOTRAILERS > 0 {
ss = append(ss, "NOTRAILERS")
}
if flag&syscall.IFF_RUNNING > 0 {
ss = append(ss, "RUNNING")
}
if flag&syscall.IFF_NOARP > 0 {
ss = append(ss, "NOARP")
}
if flag&syscall.IFF_PROMISC > 0 {
ss = append(ss, "PROMISC")
}
if flag&syscall.IFF_ALLMULTI > 0 {
ss = append(ss, "ALLMULTI")
}
if flag&syscall.IFF_MULTICAST > 0 {
ss = append(ss, "MULTICAST")
}
return strings.Join(ss, " | ")
}

View File

@@ -0,0 +1,83 @@
// Copyright (C) 2014, 2015 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 zebra
import (
"strings"
"syscall"
)
func intfflag2string(flag uint64) string {
ss := make([]string, 0, 10)
if flag&syscall.IFF_UP > 0 {
ss = append(ss, "UP")
}
if flag&syscall.IFF_BROADCAST > 0 {
ss = append(ss, "BROADCAST")
}
if flag&syscall.IFF_DEBUG > 0 {
ss = append(ss, "DEBUG")
}
if flag&syscall.IFF_LOOPBACK > 0 {
ss = append(ss, "LOOPBACK")
}
if flag&syscall.IFF_POINTOPOINT > 0 {
ss = append(ss, "POINTOPOINT")
}
if flag&syscall.IFF_NOTRAILERS > 0 {
ss = append(ss, "NOTRAILERS")
}
if flag&syscall.IFF_RUNNING > 0 {
ss = append(ss, "RUNNING")
}
if flag&syscall.IFF_NOARP > 0 {
ss = append(ss, "NOARP")
}
if flag&syscall.IFF_PROMISC > 0 {
ss = append(ss, "PROMISC")
}
if flag&syscall.IFF_ALLMULTI > 0 {
ss = append(ss, "ALLMULTI")
}
if flag&syscall.IFF_MASTER > 0 {
ss = append(ss, "MASTER")
}
if flag&syscall.IFF_SLAVE > 0 {
ss = append(ss, "SLAVE")
}
if flag&syscall.IFF_MULTICAST > 0 {
ss = append(ss, "MULTICAST")
}
if flag&syscall.IFF_PORTSEL > 0 {
ss = append(ss, "PORTSEL")
}
if flag&syscall.IFF_AUTOMEDIA > 0 {
ss = append(ss, "AUTOMEDIA")
}
if flag&syscall.IFF_DYNAMIC > 0 {
ss = append(ss, "DYNAMIC")
}
// if flag&syscall.IFF_LOWER_UP > 0 {
// ss = append(ss, "LOWER_UP")
// }
// if flag&syscall.IFF_DORMANT > 0 {
// ss = append(ss, "DORMANT")
// }
// if flag&syscall.IFF_ECHO > 0 {
// ss = append(ss, "ECHO")
// }
return strings.Join(ss, " | ")
}

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2014, 2015 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 zebra
import (
"strings"
"syscall"
)
func intfflag2string(flag uint64) string {
ss := make([]string, 0, 10)
if flag&syscall.IFF_UP > 0 {
ss = append(ss, "UP")
}
if flag&syscall.IFF_BROADCAST > 0 {
ss = append(ss, "BROADCAST")
}
if flag&syscall.IFF_LOOPBACK > 0 {
ss = append(ss, "LOOPBACK")
}
if flag&syscall.IFF_MULTICAST > 0 {
ss = append(ss, "MULTICAST")
}
return strings.Join(ss, " | ")
}