osrg/gobgp

How to use `ListPath` to get OpenConfig BGP RIB information?

wenovus opened this issue · 7 comments

TLDR: The API behaviour of ListPath is hard to understand. After experimentation I can't get the correct information to populate OpenConfig BGP RIB schema. Specifically, ListPath may be incorrectly populating path attributes for post-policy ADJ_IN.

Ask: Requesting guidance on how to use or improve this API to get pre/post-policy ADJ_IN/ADJ_OUT RIB information.

Background

I'm using ListPath with ADJ_IN and ADJ_OUT to get the BGP RIB for the following types of RIBs:

  • ADJ_IN routes before applying import policies. (adj-rib-in-pre)
  • ADJ_IN routes after applying import policies. (adj-rib-in-post)
  • ADJ_OUT routes before applying export policies. (adj-rib-out-pre)
  • ADJ_OUT routes after applying export policies. (adj-rib-out-post)

The use case is to populate the BGP RIBs defined in OpenConfig.

Problem

At first, the EnableFiltered parameter seems to do what I want: it applies the policies, and marks each route as either "filtered", (or rejected).

Case/Attempt 1

So I did this:

  • ADJ_IN routes before applying import policies. (adj-rib-in-pre)
    • Call ListPath with EnableFiltered=false, and use all routes
  • ADJ_IN routes after applying import policies. (adj-rib-in-post)
    • Call ListPath with EnableFiltered=true, and use routes where route.Filtered=false
  • ADJ_OUT routes before applying export policies. (adj-rib-out-pre)
    • Call ListPath with EnableFiltered=false, and use all routes
  • ADJ_OUT routes after applying export policies. (adj-rib-out-post)
    • Call ListPath with EnableFiltered=true, and use routes where route.Filtered=false

Observations

  • ACCEPT/REJECT actions were wrong for adj-rib-out-pre (i.e. EXPORT policy was being applied).
  • community path attributes were wrong for all but adj-rib-in-pre.
  • community path attributes seems to be reversed between adj-rib-out-post and adj-rib-out-pre -- they seem to have what each other's should have.

Case/Attempt 2

So now I tried flipping EnableFiltered for adj-rib-out-*:

  • ADJ_IN routes before applying import policies. (adj-rib-in-pre)
    • Call ListPath with EnableFiltered=false, and use all routes.
  • ADJ_IN routes after applying import policies. (adj-rib-in-post)
    • Call ListPath with EnableFiltered=true, and use routes where route.Filtered=false.
  • ADJ_OUT routes before applying export policies. (adj-rib-out-pre)
    • Call ListPath with EnableFiltered=true, and use all routes.
  • ADJ_OUT routes after applying export policies. (adj-rib-out-post)
    • Call ListPath with EnableFiltered=false, and use routes where route.Filtered=false.

Observations

  • ACCEPT/REJECT actions were correct.
  • community path attributes were correct for all but adj-rib-in-post (in my test, the community REPLACE by the import policy was not applied).

Case 2 is tested here: #2764, which shows the incorrect adj-rib-in-post results (last added test case)

Case/Attempt 3

So now I tried flipping EnableFiltered for adj-rib-in-* as well:

  • ADJ_IN routes before applying import policies. (adj-rib-in-pre)
    • Call ListPath with EnableFiltered=true, and use all routes.
  • ADJ_IN routes after applying import policies. (adj-rib-in-post)
    • Call ListPath with EnableFiltered=false, and use routes where route.Filtered=false.
  • ADJ_OUT routes before applying export policies. (adj-rib-out-pre)
    • Call ListPath with EnableFiltered=true, and use all routes.
  • ADJ_OUT routes after applying export policies. (adj-rib-out-post)
    • Call ListPath with EnableFiltered=false, and use routes where route.Filtered=false.

Observations

  • ACCEPT/REJECT actions were correct.
  • adj-rib-in-post now seems to be what adj-rib-out-pre should be --- IMPORT policy is no longer applied.

Based on the above Case 2 was the closest the behaviour I expected., but for ADJ_IN, the community attribute of the IMPORT policy was not being applied.

I tried to understand the code at

gobgp/pkg/server/server.go

Lines 2614 to 2645 in 8fdda5d

if in {
adjRib = peer.adjRibIn
if enableFiltered {
for _, path := range peer.adjRibIn.PathList([]bgp.RouteFamily{family}, true) {
options := &table.PolicyOptions{
Validate: s.roaTable.Validate,
}
if s.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_IMPORT, path, options) == nil {
filtered[path.GetNlri().String()] = path
}
}
}
} else {
adjRib = table.NewAdjRib(s.logger, peer.configuredRFlist())
if enableFiltered {
for _, path := range s.getPossibleBest(peer, family) {
path, options, stop := s.prePolicyFilterpath(peer, path, nil)
if stop {
continue
}
options.Validate = s.roaTable.Validate
p := peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options)
if p == nil {
filtered[path.GetNlri().String()] = path
}
adjRib.UpdateAdjRibOut([]*table.Path{path})
}
} else {
accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist())
adjRib.UpdateAdjRibOut(accepted)
}
}
but I could not make sense of the code to the above behaviours.

Requesting guidance on how to use or improve this API to get pre/post-policy ADJ_IN/ADJ_OUT RIB information.

fujita commented

In the case of the global rib (not route server case),

adj-in -> in-filter -> the global rib -> out-filter -> adj-out

ListPath with ADJ_IN and EnableFiltered=false gets adj-in
ListPath with ADJ_IN and EnableFiltered=true gets routes between in-filter and the global rib
ListPath with ADJ_OUT and EnableFiltered=false gets routes between the global rib and out-filter
ListPath with ADJ_OUT and EnableFiltered=true gets routes adj-out

In the case of the global rib (not route server case),

adj-in -> in-filter -> the global rib -> out-filter -> adj-out

ListPath with ADJ_IN and EnableFiltered=false gets adj-in ListPath with ADJ_IN and EnableFiltered=true gets routes between in-filter and the global rib ListPath with ADJ_OUT and EnableFiltered=false gets routes between the global rib and out-filter ListPath with ADJ_OUT and EnableFiltered=true gets routes adj-out

Hi Fujita, this is also what I first assumed and tried in "Case/Attempt 1" above, but I observed that it didn't work.

See the test failures here: #2764 -- I tried all the permutations of EnableFiltered=false/true and the closest I got was "Case/Attempt 2" -- there was no way of getting the "routes between in-filter and the global rib" with the correct community attributes populated.

Very sorry about the delay.
I was wrong. EnableFiltered sets path.Filtered if a path is dropped by a policy. The current API doesn't give the information that you need. I think that we needs a new option like AppliedPolicies.

Very sorry about the delay. I was wrong. EnableFiltered sets path.Filtered if a path is dropped by a policy. The current API doesn't give the information that you need. I think that we needs a new option like AppliedPolicies.

What you just described is actually the behaviour I want -- the issue is that I'm not seeing the same thing. In particular the community attributes at adj-rib-in-post cannot be populated by any flag permutation.

The current API doesn't provide routes between in-filter and the global rib because the CLI doesn't need to provide such.
You could get routes from the global rib and filter these routes from a particular peer.

The current API doesn't provide routes between in-filter and the global rib because the CLI doesn't need to provide such. You could get routes from the global rib and filter these routes from a particular peer.

I still think the API was intended to get what I wanted, but because the CLI was the primary use case that's why the community attributes weren't populated correctly.

Opened #2784 to fix this and make the behaviour conform to "Case/Attempt 2" described above.

Even though the semantics is still confusing, "Case/Attempt 2" is now working so the requested behaviour is now possible. I'll consider this closed.