diff --git a/route/bgp.go b/route/bgp.go index 47f7db4fc15980936e174ffa540fd3c09bb04a7e..e56b140b4bea79f00125a020c5f05b61aceb7279 100644 --- a/route/bgp.go +++ b/route/bgp.go @@ -2,6 +2,8 @@ package route import ( "fmt" + "strconv" + "strings" "github.com/taktv6/tflow2/convert" ) @@ -154,6 +156,32 @@ func (b *BGPPath) Print() string { return ret } +func (b *BGPPath) Prepend(asn uint32, times uint16) { + if times == 0 { + return + } + + asnStr := strconv.FormatUint(uint64(asn), 10) + + path := make([]string, times+1) + for i := 0; uint16(i) < times; i++ { + path[i] = asnStr + } + path[times] = b.ASPath + + b.ASPath = strings.TrimSuffix(strings.Join(path, " "), " ") + b.ASPathLen = b.ASPathLen + times +} + +func (p *BGPPath) Copy() *BGPPath { + if p == nil { + return nil + } + + cp := *p + return &cp +} + func uint32To4Byte(addr uint32) [4]byte { slice := convert.Uint32Byte(addr) ret := [4]byte{ diff --git a/route/path.go b/route/path.go index 544b83b485c106870b8ff7a251539187c85a06d6..ef2e881798212232fc6e116d2df2764e7d6207d3 100644 --- a/route/path.go +++ b/route/path.go @@ -100,3 +100,15 @@ func (p *Path) Print() string { return ret } + +func (p *Path) Copy() *Path { + if p == nil { + return nil + } + + cp := *p + cp.BGPPath = cp.BGPPath.Copy() + cp.StaticPath = cp.StaticPath.Copy() + + return &cp +} diff --git a/route/static.go b/route/static.go index 483138f04874ff7f50185ba3fcfb04fb007c6029..0ab6b26e3dc01c58b756efca28935b2e8e4e9d5a 100644 --- a/route/static.go +++ b/route/static.go @@ -23,3 +23,12 @@ func (s *StaticPath) Compare(t *StaticPath) int8 { func (s *StaticPath) ECMP(t *StaticPath) bool { return true } + +func (s *StaticPath) Copy() *StaticPath { + if s == nil { + return nil + } + + cp := *s + return &cp +} diff --git a/routingtable/filter/actions/as_path_prepend_action.go b/routingtable/filter/actions/as_path_prepend_action.go new file mode 100644 index 0000000000000000000000000000000000000000..30d63ca783fd91a9530f336f3311106c57ac5968 --- /dev/null +++ b/routingtable/filter/actions/as_path_prepend_action.go @@ -0,0 +1,29 @@ +package actions + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" +) + +type ASPathPrependAction struct { + asn uint32 + times uint16 +} + +func NewASPathPrependAction(asn uint32, times uint16) *ASPathPrependAction { + return &ASPathPrependAction{ + asn: asn, + times: times, + } +} + +func (a *ASPathPrependAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { + if pa.BGPPath == nil { + return pa, false + } + + modified := pa.Copy() + modified.BGPPath.Prepend(a.asn, a.times) + + return modified, false +} diff --git a/routingtable/filter/actions/as_path_prepend_action_test.go b/routingtable/filter/actions/as_path_prepend_action_test.go new file mode 100644 index 0000000000000000000000000000000000000000..348116ccb33b7e067e7e50effa438b9eb0d42c3a --- /dev/null +++ b/routingtable/filter/actions/as_path_prepend_action_test.go @@ -0,0 +1,59 @@ +package actions + +import ( + "testing" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestAppendPath(t *testing.T) { + tests := []struct { + name string + times uint16 + bgpPath *route.BGPPath + expectedPath string + expectedLength uint16 + }{ + { + name: "BGPPath is nil", + }, + { + name: "append 0", + times: 0, + bgpPath: &route.BGPPath{ + ASPath: "12345 12345", + ASPathLen: 2, + }, + expectedPath: "12345 12345", + expectedLength: 2, + }, + { + name: "append 3", + times: 3, + bgpPath: &route.BGPPath{ + ASPath: "12345 15169", + ASPathLen: 2, + }, + expectedPath: "12345 12345 12345 12345 15169", + expectedLength: 5, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + a := NewASPathPrependAction(12345, test.times) + p, _ := a.Do(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + BGPPath: test.bgpPath, + }) + + if test.bgpPath == nil { + return + } + + assert.Equal(te, test.expectedPath, p.BGPPath.ASPath, "ASPath") + assert.Equal(te, test.expectedLength, p.BGPPath.ASPathLen, "ASPathLen") + }) + } +} diff --git a/routingtable/filter/actions/filter_action.go b/routingtable/filter/actions/filter_action.go new file mode 100644 index 0000000000000000000000000000000000000000..7a2665146922719766b880f9093eb1049c6ae018 --- /dev/null +++ b/routingtable/filter/actions/filter_action.go @@ -0,0 +1,10 @@ +package actions + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" +) + +type FilterAction interface { + Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) +} diff --git a/routingtable/filter/actions/set_local_pref_action.go b/routingtable/filter/actions/set_local_pref_action.go new file mode 100644 index 0000000000000000000000000000000000000000..e60b6faa0ab5e4e986f785d7b8322db2c6492e34 --- /dev/null +++ b/routingtable/filter/actions/set_local_pref_action.go @@ -0,0 +1,27 @@ +package actions + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" +) + +type SetLocalPrefAction struct { + pref uint32 +} + +func NewSetLocalPrefAction(pref uint32) *SetLocalPrefAction { + return &SetLocalPrefAction{ + pref: pref, + } +} + +func (a *SetLocalPrefAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { + if pa.BGPPath == nil { + return pa, false + } + + modified := *pa + modified.BGPPath.LocalPref = a.pref + + return &modified, false +} diff --git a/routingtable/filter/actions/set_local_pref_action_test.go b/routingtable/filter/actions/set_local_pref_action_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0f21a637379bf7c0018db89a0fab0a46deadd5b0 --- /dev/null +++ b/routingtable/filter/actions/set_local_pref_action_test.go @@ -0,0 +1,46 @@ +package actions + +import ( + "testing" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestSetLocalPref(t *testing.T) { + tests := []struct { + name string + bgpPath *route.BGPPath + expectedLocalPref uint32 + }{ + { + name: "BGPPath is nil", + }, + { + name: "modify path", + bgpPath: &route.BGPPath{ + LocalPref: 100, + }, + expectedLocalPref: 150, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + a := NewSetLocalPrefAction(150) + p, _ := a.Do(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + BGPPath: test.bgpPath, + }) + + if test.expectedLocalPref > 0 { + assert.Equal(te, test.expectedLocalPref, p.BGPPath.LocalPref) + } + }) + } +} + +func strAddr(s string) uint32 { + ret, _ := net.StrToAddr(s) + return ret +} diff --git a/routingtable/filter/actions/set_nexthop_action.go b/routingtable/filter/actions/set_nexthop_action.go new file mode 100644 index 0000000000000000000000000000000000000000..f178f61802882157856fc1f4bf15c8d9f0b08dc6 --- /dev/null +++ b/routingtable/filter/actions/set_nexthop_action.go @@ -0,0 +1,27 @@ +package actions + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" +) + +type SetNextHopAction struct { + addr uint32 +} + +func NewSetNextHopAction(addr uint32) *SetNextHopAction { + return &SetNextHopAction{ + addr: addr, + } +} + +func (a *SetNextHopAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { + if pa.BGPPath == nil { + return pa, false + } + + modified := pa.Copy() + modified.BGPPath.NextHop = a.addr + + return modified, false +} diff --git a/routingtable/filter/actions/set_nexthop_action_test.go b/routingtable/filter/actions/set_nexthop_action_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7501e6bdcc388c65577cea8ce0a425acf8b7d3e3 --- /dev/null +++ b/routingtable/filter/actions/set_nexthop_action_test.go @@ -0,0 +1,41 @@ +package actions + +import ( + "testing" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestSetNextHopTest(t *testing.T) { + tests := []struct { + name string + bgpPath *route.BGPPath + expected uint32 + }{ + { + name: "BGPPath is nil", + }, + { + name: "modify path", + bgpPath: &route.BGPPath{ + NextHop: strAddr("100.64.2.1"), + }, + expected: strAddr("100.64.2.1"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + a := NewSetNextHopAction(strAddr("100.64.2.1")) + p, _ := a.Do(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + BGPPath: test.bgpPath, + }) + + if test.expected > 0 { + assert.Equal(te, test.expected, p.BGPPath.NextHop) + } + }) + } +}