Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions api/gobgp.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cmd/gobgp/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const globalRIBName = "global"
const (
cmdGlobal = "global"
cmdNeighbor = "neighbor"
cmdPeerGroup = "peer-group"
cmdPeerGroupShort = "pg"
cmdPolicy = "policy"
cmdRib = "rib"
cmdAdd = "add"
Expand Down
10 changes: 7 additions & 3 deletions cmd/gobgp/neighbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ func makeShowRouteArgs(p *api.Path, idx int, now time.Time, showAge, showBest, s
args = append(args, teid, qfi, endpoint)
}

attrs, _ := apiutil.GetNativePathAttributes(p)
attrs, attrErr := apiutil.GetNativePathAttributes(p)
// Next Hop
nexthop := "fictitious"
if n := getNextHopFromPathAttributes(attrs); n.IsValid() {
Expand All @@ -649,8 +649,12 @@ func makeShowRouteArgs(p *api.Path, idx int, now time.Time, showAge, showBest, s
}

// Path Attributes
pattrstr := getPathAttributeString(nlri, attrs)
args = append(args, pattrstr)
if attrErr == nil {
pattrstr := getPathAttributeString(nlri, attrs)
args = append(args, pattrstr)
} else {
args = append(args, fmt.Sprintf("error: %s", attrErr))
}

if showSendMaxFiltered {
if p.SendMaxFiltered {
Expand Down
128 changes: 128 additions & 0 deletions cmd/gobgp/peer_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"encoding/json"
"fmt"
"io"
"sort"
"strconv"
"strings"

api "github.com/osrg/gobgp/v4/api"
"github.com/osrg/gobgp/v4/pkg/config/oc"
"github.com/spf13/cobra"
)

func newPeerGroupCmd() *cobra.Command {
pgCmdImpl := &cobra.Command{}

policyCmd := &cobra.Command{
Use: cmdPolicy,
Run: func(cmd *cobra.Command, args []string) {
key := oc.NewPeerGroupPolicyAssignmentKeyFromName(args[0])
for _, v := range []string{cmdImport, cmdExport} {
if err := showNeighborPolicy(key, v, 4); err != nil {
exitWithError(err)
}
}
},
}
pgCmdImpl.AddCommand(policyCmd)

pgCmd := &cobra.Command{
Use: cmdPeerGroup,
Aliases: []string{cmdPeerGroupShort},

RunE: func(cmd *cobra.Command, args []string) error {
var err error
if len(args) < 2 {
var pgName string
if len(args) == 1 {
pgName = args[0]
}
showPeerGroups(pgName)
} else {
args = append(args[1:], args[0])
pgCmdImpl.SetArgs(args)
err = pgCmdImpl.Execute()
}
if err != nil {
exitWithError(err)
}
return nil
},
}

return pgCmd
}

func showPeerGroups(name string) {
pgs, err := getPeerGroups(name)
if err != nil {
exitWithError(err)
}

if globalOpts.Json {
j, _ := json.Marshal(pgs)
fmt.Println(string(j))
return
}

sort.Slice(pgs, func(i, j int) bool {
return pgs[i].Conf.PeerGroupName < pgs[j].Conf.PeerGroupName
})

nameColLen := 10
for _, pg := range pgs {
if len(pg.Conf.PeerGroupName) > nameColLen {
nameColLen = len(pg.Conf.PeerGroupName)
}
}

fmtstr := "%-" + strconv.Itoa(nameColLen) + "s %8s %5s %s\n"
fmt.Printf(fmtstr, "PeerGroup", "Type", "AS", "Info")
for _, pg := range pgs {
info := make([]string, 0, 2)
if pg.Info.LocalAsn != pg.Info.PeerAsn {
info = append(info, fmt.Sprintf("local-as %d", pg.Info.LocalAsn))
}

if pg.RouteReflector != nil && pg.RouteReflector.RouteReflectorClient {
info = append(info, fmt.Sprintf("route-reflector-client %s", pg.RouteReflector.RouteReflectorClusterId))
} else if pg.RouteServer != nil && pg.RouteServer.RouteServerClient {
info = append(info, "route-server-client")
}

fmt.Printf(
fmtstr,
pg.Conf.PeerGroupName,
api.PeerType_name[int32(pg.Info.Type)],
fmt.Sprint(pg.Info.PeerAsn),
strings.Join(info, ", "),
)
}
}

func getPeerGroups(name string) ([]*api.PeerGroup, error) {
stream, err := client.ListPeerGroup(ctx, &api.ListPeerGroupRequest{
PeerGroupName: name,
})
if err != nil {
return nil, err
}

l := make([]*api.PeerGroup, 0, 1024)
for {
r, err := stream.Recv()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
l = append(l, r.PeerGroup)
}
if name != "" && len(l) == 0 {
return l, fmt.Errorf("not found peer group %s", name)
}
return l, err
}
3 changes: 2 additions & 1 deletion cmd/gobgp/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ func newRootCmd() *cobra.Command {

globalCmd := newGlobalCmd()
neighborCmd := newNeighborCmd()
pgCmd := newPeerGroupCmd()
vrfCmd := newVrfCmd()
policyCmd := newPolicyCmd()
monitorCmd := newMonitorCmd()
mrtCmd := newMrtCmd()
rpkiCmd := newRPKICmd()
bmpCmd := newBmpCmd()
logLevelCmd := newLogLevelCmd()
rootCmd.AddCommand(globalCmd, neighborCmd, vrfCmd, policyCmd, monitorCmd, mrtCmd, rpkiCmd, bmpCmd, logLevelCmd)
rootCmd.AddCommand(globalCmd, neighborCmd, pgCmd, vrfCmd, policyCmd, monitorCmd, mrtCmd, rpkiCmd, bmpCmd, logLevelCmd)
return rootCmd
}
21 changes: 21 additions & 0 deletions docs/sources/policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Assumed that you finished [Getting Started](getting-started.md).
- [4.2. Attach policy to route-server-client](#42-attach-policy-to-route-server-client)
- [Policy Configuration Example](#policy-configuration-example)
- [Policy and Soft Reset](#policy-and-soft-reset)
- [Shared Policy in Peer Groups](#shared-policy-in-peer-groups)

## Overview

Expand Down Expand Up @@ -996,3 +997,23 @@ When you change an import policy and reset the inbound routing table (aka soft r
The outbound routing table doesn't exist for saving memory usage, it's impossible to know whether the route was actually sent to peer or the route also was rejected by the previous export policies and not sent. GoBGP doesn't send such withdraw rather than possible unwilling leaking information.

Please report if other implementations such as bird work in a different way.

## Shared Policy in Peer Groups

By default each policy is computed individually per each receiver peer. There still an option to select policy applicable statements by using matching based on neighbor sets, but it might be ineffective for large amount of peers route reflector or server might have as each neighbor set requires source IP matching per each path received or propagated. GoBGP offers an option to group policy computation per each group which is enabled by `shared-policy` option in peer-group config and specifying individual `apply-policy` config section for it.

With `shared-policy` option enabled specified policy is computed once per peer-group with a additional small work done on per-peer basis, such as Route Target Constraint checks or Split Horizon check. Note that this option is incompatible with peers with ADD_PATH capability.

If shared policy is enabled, applicable policy for a peer group can be checked using `gobgp peer-group PG_NAME policy` command:

```bash
$ gobgp peer-group control policy
Import policy:
Default: ACCEPT
Name control-import:
...
```

NOTE: shared policy changes behavior of RTC: since it requires for RTC filtering to be computed on per-peer basis,
while policy is computed on per-peer-group basis, RTC starts to account for route targets appended or removed via export
policies.
79 changes: 67 additions & 12 deletions internal/pkg/table/destination.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,44 @@ func (r *BestPathReason) String() string {
return BestPathReasonStringMap[*r]
}

// PeerInfo contains a chunk of peer configuration that is used by table code to determine
// how to handle path attributes. PeerInfo struct can be used both for individual peers
// and peer groups when peer group is in shared policy mode and handles paths in bulk.
//
// It is also used to identify source of path to determine best path, but in that case it
// can only be a peer, not a peer group.
//
// Zero PeerInfo value denotes localSource (paths originated locally) - use localSource
type PeerInfo struct {
AS uint32
LocalAS uint32
ID netip.Addr
LocalID netip.Addr
Address netip.Addr
LocalAddress netip.Addr
RouteReflectorClusterID netip.Addr
// PeerType: INTERNAL for iBGP peers or EXTERNAL for eBGP peers. Computed from
// a state based on AS/LocalAS Comparison
PeerType oc.PeerType

// AS Number, Address and BGP Identifier (not specified for peer groups) of the remote peer
AS uint32
ID netip.Addr
Address netip.Addr

// Local AS Number, Address and BGP Identifier of local router.
//
// AS Number can be overridden using local-as config option on per-peer basis and
// used for AS_PATH prepending for eBGP peers and for path filtering
// FIXME: for now, local-as override is not fully supported for filtering, see handleUpdate() in peer.go
LocalAS uint32
LocalID netip.Addr
LocalAddress netip.Addr

// A view of peer/peer group configuration used to compute path attributes correctly
RouteReflectorClient bool
RouteReflectorClusterID netip.Addr
RouteServerClient bool
MultihopTtl uint8
Confederation bool
RemovePrivateAs oc.RemovePrivateAsOption

// PeerGroup contains name for peer group itself if this is PeerInfo for a peer group
// or the name of peer group this peer belongs to. Used for informational purposes.
PeerGroup string
}

func (lhs *PeerInfo) Equal(rhs *PeerInfo) bool {
Expand All @@ -107,9 +134,11 @@ func (lhs *PeerInfo) Equal(rhs *PeerInfo) bool {
}

func (i *PeerInfo) String() string {
if !i.Address.IsValid() {
const peerTypeUnspecified = oc.PeerType("")
if i.PeerType == peerTypeUnspecified {
return "local"
}

s := bytes.NewBuffer(make([]byte, 0, 64))
fmt.Fprintf(s, "{ %s | ", i.Address)
fmt.Fprintf(s, "as: %d", i.AS)
Expand All @@ -123,16 +152,42 @@ func (i *PeerInfo) String() string {

func NewPeerInfo(g *oc.Global, p *oc.Neighbor, AS, localAS uint32, ID, localID netip.Addr, addr, localAddr netip.Addr) *PeerInfo {
return &PeerInfo{
AS: AS,
LocalAS: localAS,
PeerType: p.State.PeerType,
ID: ID,
LocalID: localID,
AS: p.Config.PeerAs,
Address: addr,
LocalAS: p.Config.LocalAs,
LocalID: localID,
LocalAddress: localAddr,
RouteReflectorClient: p.RouteReflector.Config.RouteReflectorClient,
RouteReflectorClusterID: p.RouteReflector.State.RouteReflectorClusterId,
RouteServerClient: p.RouteServer.Config.RouteServerClient,
MultihopTtl: p.EbgpMultihop.Config.MultihopTtl,
Confederation: g.IsConfederationMember(p.Config.PeerAs),
RemovePrivateAs: p.State.RemovePrivateAs,
PeerGroup: p.Config.PeerGroup,
}
}

func NewPeerGroupInfo(g *oc.Global, p *oc.PeerGroup) *PeerInfo {
localAddr := p.Transport.Config.LocalAddress
if !localAddr.IsValid() {
localAddr = g.Config.RouterId
}

return &PeerInfo{
PeerType: p.State.PeerType,
AS: p.Config.PeerAs,
LocalAS: p.Config.LocalAs,
LocalID: g.Config.RouterId,
LocalAddress: localAddr,
RouteReflectorClient: p.RouteReflector.Config.RouteReflectorClient,
RouteReflectorClusterID: p.RouteReflector.State.RouteReflectorClusterId,
RouteServerClient: p.RouteServer.Config.RouteServerClient,
MultihopTtl: p.EbgpMultihop.Config.MultihopTtl,
Confederation: p.IsConfederationMember(g),
Confederation: g.IsConfederationMember(p.Config.PeerAs),
RemovePrivateAs: p.State.RemovePrivateAs,
PeerGroup: p.Config.PeerGroupName,
}
}

Expand Down
Loading
Loading