Skip to content
Snippets Groups Projects
Commit 95203374 authored by Oliver Herms's avatar Oliver Herms
Browse files

Added addpath support

parent 699f7bfd
No related branches found
No related tags found
No related merge requests found
Showing
with 374 additions and 53 deletions
......@@ -17,5 +17,5 @@ type Peer struct {
Passive bool
RouterID uint32
AddPathSend routingtable.ClientOptions
AddPathRecv uint
AddPathRecv bool
}
......@@ -37,9 +37,12 @@ func main() {
KeepAlive: 30,
Passive: true,
RouterID: b.RouterID(),
AddPathSend: routingtable.ClientOptions{
MaxPaths: 10,
},
}, rib)
time.Sleep(time.Second * 30)
time.Sleep(time.Second * 15)
b.AddPeer(config.Peer{
AdminEnabled: true,
......@@ -54,7 +57,7 @@ func main() {
AddPathSend: routingtable.ClientOptions{
MaxPaths: 10,
},
AddPathRecv: 1,
AddPathRecv: true,
}, rib)
go func() {
......
......@@ -4,6 +4,7 @@ const (
OctetLen = 8
MaxASNsSegment = 255
BGP4Version = 4
MinOpenLen = 29
MarkerLen = 16
HeaderLen = 19
......@@ -128,6 +129,14 @@ type BGPUpdate struct {
NLRI *NLRI
}
type BGPUpdateAddPath struct {
WithdrawnRoutesLen uint16
WithdrawnRoutes *NLRIAddPath
TotalPathAttrLen uint16
PathAttributes *PathAttribute
NLRI *NLRIAddPath
}
type PathAttribute struct {
Length uint16
Optional bool
......@@ -145,6 +154,13 @@ type NLRI struct {
Next *NLRI
}
type NLRIAddPath struct {
PathIdentifier uint32
IP uint32
Pfxlen uint8
Next *NLRIAddPath
}
type ASPath []ASPathSegment
type ASPathSegment struct {
Type uint8
......
......@@ -130,6 +130,9 @@ func invalidErrCode(n *BGPNotification) (*BGPNotification, error) {
func decodeOpenMsg(buf *bytes.Buffer) (*BGPOpen, error) {
msg, err := _decodeOpenMsg(buf)
if err != nil {
return nil, fmt.Errorf("Unable to decode OPEN message: %v", err)
}
return msg.(*BGPOpen), err
}
......@@ -182,15 +185,18 @@ func decodeOptParams(buf *bytes.Buffer, optParmLen uint8) ([]OptParam, error) {
fmt.Printf("Type: %d\n", o.Type)
switch o.Type {
case CapabilitiesParamType:
cap, err := decodeCapability(buf)
caps, err := decodeCapabilities(buf, o.Length)
if err != nil {
return nil, fmt.Errorf("Unable to decode capability: %v", err)
return nil, fmt.Errorf("Unable to decode capabilites: %v", err)
}
o.Value = cap
o.Value = caps
optParams = append(optParams, o)
read += cap.Length + 2
for _, cap := range caps {
read += cap.Length + 2
}
default:
return nil, fmt.Errorf("Unrecognized option")
return nil, fmt.Errorf("Unrecognized option: %d", o.Type)
}
}
......@@ -198,6 +204,22 @@ func decodeOptParams(buf *bytes.Buffer, optParmLen uint8) ([]OptParam, error) {
return optParams, nil
}
func decodeCapabilities(buf *bytes.Buffer, length uint8) (Capabilities, error) {
ret := make(Capabilities, 0)
read := uint8(0)
for read < length {
cap, err := decodeCapability(buf)
if err != nil {
return nil, fmt.Errorf("Unable to decode capability: %v", err)
}
ret = append(ret, cap)
read += cap.Length + 2
}
return ret, nil
}
func decodeCapability(buf *bytes.Buffer) (Capability, error) {
cap := Capability{}
fields := []interface{}{
......@@ -218,7 +240,12 @@ func decodeCapability(buf *bytes.Buffer) (Capability, error) {
}
cap.Value = addPathCap
default:
return cap, fmt.Errorf("Unknown capability: %d", cap.Code)
for i := uint8(0); i < cap.Length; i++ {
_, err := buf.ReadByte()
if err != nil {
return cap, fmt.Errorf("Read failed: %v", err)
}
}
}
return cap, nil
......
......@@ -26,7 +26,11 @@ func SerializeNotificationMsg(msg *BGPNotification) []byte {
}
func SerializeOpenMsg(msg *BGPOpen) []byte {
openLen := uint16(29)
optParmsBuf := bytes.NewBuffer(make([]byte, 0))
serializeOptParams(optParmsBuf, msg.OptParams)
optParms := optParmsBuf.Bytes()
openLen := uint16(len(optParms) + MinOpenLen)
buf := bytes.NewBuffer(make([]byte, 0, openLen))
serializeHeader(buf, openLen, OpenMsg)
......@@ -35,9 +39,6 @@ func SerializeOpenMsg(msg *BGPOpen) []byte {
buf.Write(convert.Uint16Byte(msg.HoldTime))
buf.Write(convert.Uint32Byte(msg.BGPIdentifier))
optParmsBuf := bytes.NewBuffer(make([]byte, 0))
serializeOptParams(optParmsBuf, msg.OptParams)
optParms := optParmsBuf.Bytes()
buf.WriteByte(uint8(len(optParms)))
buf.Write(optParms)
......@@ -62,6 +63,65 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) {
buf.WriteByte(typ)
}
func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) {
budget := MaxLen - MinLen
buf := bytes.NewBuffer(nil)
withdrawBuf := bytes.NewBuffer(nil)
for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next {
nlriLen := int(withdraw.serialize(withdrawBuf))
budget -= nlriLen
if budget < 0 {
return nil, fmt.Errorf("update too long")
}
}
pathAttributesBuf := bytes.NewBuffer(nil)
for pa := b.PathAttributes; pa != nil; pa = pa.Next {
paLen := int(pa.serialize(pathAttributesBuf))
budget -= paLen
if budget < 0 {
return nil, fmt.Errorf("update too long")
}
}
nlriBuf := bytes.NewBuffer(nil)
for nlri := b.NLRI; nlri != nil; nlri = nlri.Next {
nlriLen := int(nlri.serialize(nlriBuf))
budget -= nlriLen
if budget < 0 {
return nil, fmt.Errorf("update too long")
}
}
withdrawnRoutesLen := withdrawBuf.Len()
if withdrawnRoutesLen > 65535 {
return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen)
}
totalPathAttributesLen := pathAttributesBuf.Len()
if totalPathAttributesLen > 65535 {
return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen)
}
totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19
if totalLength > 4096 {
return nil, fmt.Errorf("Update too long: %d bytes", totalLength)
}
serializeHeader(buf, uint16(totalLength), UpdateMsg)
buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen)))
buf.Write(withdrawBuf.Bytes())
buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen)))
buf.Write(pathAttributesBuf.Bytes())
buf.Write(nlriBuf.Bytes())
return buf.Bytes(), nil
}
func (b *BGPUpdate) SerializeUpdate() ([]byte, error) {
budget := MaxLen - MinLen
buf := bytes.NewBuffer(nil)
......
......@@ -94,7 +94,7 @@ func TestSerializeOpenMsg(t *testing.T) {
}
}
/*func TestSerializeOptParams(t *testing.T) {
func TestSerializeOptParams(t *testing.T) {
tests := []struct {
name string
optParams []OptParam
......@@ -109,28 +109,20 @@ func TestSerializeOpenMsg(t *testing.T) {
name: "1 Option",
optParams: []OptParam{
{
Type: 100,
Length: 2,
Value: []byte{1, 2},
Type: 2,
Length: 6,
Value: Capability{
Code: 69,
Length: 4,
Value: AddPathCapability{
AFI: 1,
SAFI: 1,
SendReceive: 3,
},
},
},
},
expected: []byte{100, 2, 1, 2},
},
{
name: "2 Options",
optParams: []OptParam{
{
Type: 100,
Length: 2,
Value: []byte{1, 2},
},
{
Type: 200,
Length: 2,
Value: []byte{3, 4},
},
},
expected: []byte{100, 2, 1, 2, 200, 2, 3, 4},
expected: []byte{2, 6, 69, 4, 0, 1, 1, 3},
},
}
......@@ -139,7 +131,7 @@ func TestSerializeOpenMsg(t *testing.T) {
serializeOptParams(buf, test.optParams)
assert.Equal(t, test.expected, buf.Bytes())
}
}*/
}
func TestSerializeHeader(t *testing.T) {
tests := []struct {
......
......@@ -73,6 +73,19 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 {
return nBytes + 1
}
func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 {
a := convert.Uint32Byte(n.IP)
addr := [4]byte{a[0], a[1], a[2], a[3]}
nBytes := bytesInAddr(n.Pfxlen)
buf.Write(convert.Uint32Byte(n.PathIdentifier))
buf.WriteByte(n.Pfxlen)
buf.Write(addr[:nBytes])
return nBytes + 1
}
func bytesInAddr(pfxlen uint8) uint8 {
return uint8(math.Ceil(float64(pfxlen) / 8))
}
......@@ -208,3 +208,46 @@ func TestNLRISerialize(t *testing.T) {
assert.Equal(t, test.expected, res)
}
}
func TestNLRIAddPathSerialize(t *testing.T) {
tests := []struct {
name string
nlri *NLRIAddPath
expected []byte
}{
{
name: "Test #1",
nlri: &NLRIAddPath{
PathIdentifier: 100,
IP: strAddr("1.2.3.0"),
Pfxlen: 25,
},
expected: []byte{0, 0, 0, 100, 25, 1, 2, 3, 0},
},
{
name: "Test #2",
nlri: &NLRIAddPath{
PathIdentifier: 100,
IP: strAddr("1.2.3.0"),
Pfxlen: 24,
},
expected: []byte{0, 0, 0, 100, 24, 1, 2, 3},
},
{
name: "Test #3",
nlri: &NLRIAddPath{
PathIdentifier: 100,
IP: strAddr("100.200.128.0"),
Pfxlen: 17,
},
expected: []byte{0, 0, 0, 100, 17, 100, 200, 128},
},
}
for _, test := range tests {
buf := bytes.NewBuffer(nil)
test.nlri.serialize(buf)
res := buf.Bytes()
assert.Equal(t, test.expected, res)
}
}
......@@ -16,12 +16,21 @@ type OptParam struct {
Value Serializable
}
type Capabilities []Capability
type Capability struct {
Code uint8
Length uint8
Value Serializable
}
func (c Capabilities) serialize(buf *bytes.Buffer) {
tmpBuf := bytes.NewBuffer(make([]byte, 0))
for _, cap := range c {
cap.serialize(tmpBuf)
}
}
func (c Capability) serialize(buf *bytes.Buffer) {
tmpBuf := bytes.NewBuffer(make([]byte, 0))
c.Value.serialize(tmpBuf)
......
......@@ -15,6 +15,7 @@ import (
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
"github.com/bio-routing/bio-rd/routingtable/adjRIBOutAddPath"
"github.com/bio-routing/bio-rd/routingtable/locRIB"
log "github.com/sirupsen/logrus"
tomb "gopkg.in/tomb.v2"
......@@ -90,9 +91,12 @@ type FSM struct {
stopMsgRecvCh chan struct{}
adjRIBIn *adjRIBIn.AdjRIBIn
adjRIBOut *adjRIBOut.AdjRIBOut
adjRIBOut routingtable.RouteTableClient
rib *locRIB.LocRIB
updateSender *UpdateSender
updateSender routingtable.RouteTableClient
capAddPathSend bool
capAddPathRecv bool
}
type msgRecvMsg struct {
......@@ -134,7 +138,6 @@ func NewFSM(peer *Peer, c config.Peer, rib *locRIB.LocRIB) *FSM {
rib: rib,
}
fsm.updateSender = newUpdateSender(fsm)
return fsm
}
......@@ -303,8 +306,6 @@ func (fsm *FSM) connect() int {
}
func (fsm *FSM) connectSendOpen() int {
optOpenParams := make([]packet.OptParam, 0)
err := fsm.sendOpen(fsm.con)
if err != nil {
stopTimer(fsm.connectRetryTimer)
......@@ -459,6 +460,9 @@ func (fsm *FSM) openSent() int {
fsm.keepaliveTime = fsm.holdTime / 3
fsm.keepaliveTimer.Reset(time.Second * fsm.keepaliveTime)
}
fsm.processOpenOptions(openMsg.OptParams)
return fsm.changeState(OpenConfirm, "Received OPEN message")
default:
sendNotification(fsm.con, packet.FiniteStateMachineError, 0)
......@@ -485,6 +489,56 @@ func (fsm *FSM) openSent() int {
}
}
func (fsm *FSM) processOpenOptions(optParams []packet.OptParam) {
for _, optParam := range optParams {
if optParam.Type != packet.CapabilitiesParamType {
continue
}
fsm.processCapabilities(optParam.Value.(packet.Capabilities))
}
}
func (fsm *FSM) processCapabilities(caps packet.Capabilities) {
for _, cap := range caps {
fsm.processCapability(cap)
}
}
func (fsm *FSM) processCapability(cap packet.Capability) {
switch cap.Code {
case packet.AddPathCapabilityCode:
fsm.processAddPathCapability(cap.Value.(packet.AddPathCapability))
}
}
func (fsm *FSM) processAddPathCapability(addPathCap packet.AddPathCapability) {
if addPathCap.AFI != 1 {
return
}
if addPathCap.SAFI != 1 {
return
}
switch addPathCap.SendReceive {
case packet.AddPathReceive:
if !fsm.peer.addPathSend.BestOnly {
fsm.capAddPathSend = true
}
case packet.AddPathSend:
if fsm.peer.addPathRecv {
fsm.capAddPathRecv = true
}
case packet.AddPathSendReceive:
if !fsm.peer.addPathSend.BestOnly {
fsm.capAddPathSend = true
}
if fsm.peer.addPathRecv {
fsm.capAddPathRecv = true
}
}
}
func (fsm *FSM) openSentTCPFail(err error) int {
fsm.con.Close()
fsm.resetConnectRetryTimer()
......@@ -607,7 +661,7 @@ func (fsm *FSM) openConfirm() int {
switch msg.Header.Type {
case packet.NotificationMsg:
nMsg := msg.Body.(packet.BGPNotification)
nMsg := msg.Body.(*packet.BGPNotification)
if nMsg.ErrorCode == packet.UnsupportedVersionNumber {
stopTimer(fsm.connectRetryTimer)
fsm.con.Close()
......@@ -669,18 +723,30 @@ func (fsm *FSM) established() int {
Type: route.BGPPathType,
Address: tnet.IPv4ToUint32(fsm.remote),
}
fsm.adjRIBOut = adjRIBOut.New(n)
fsm.adjRIBOut.Register(fsm.updateSender)
fsm.rib.RegisterWithOptions(fsm.adjRIBOut, routingtable.ClientOptions{BestOnly: true})
clientOptions := routingtable.ClientOptions{}
if fsm.capAddPathSend {
fsm.updateSender = newUpdateSenderAddPath(fsm)
fsm.adjRIBOut = adjRIBOutAddPath.New(n)
clientOptions = fsm.peer.addPathSend
} else {
fsm.updateSender = newUpdateSender(fsm)
fsm.adjRIBOut = adjRIBOut.New(n)
}
fsm.adjRIBOut.Register(fsm.updateSender)
fsm.rib.RegisterWithOptions(fsm.adjRIBOut, clientOptions)
go func() {
/*go func() {
for {
if fsm.adjRIBOut == nil {
return
}
fmt.Printf("ADJ-RIB-OUT: %s\n", fsm.remote.String())
fmt.Print(fsm.adjRIBOut.Print())
time.Sleep(time.Second * 11)
}
}()
}()*/
for {
select {
......@@ -756,8 +822,10 @@ func (fsm *FSM) established() int {
fmt.Printf("LPM: Adding prefix %s\n", pfx.String())
path := &route.Path{
Type: route.BGPPathType,
BGPPath: &route.BGPPath{},
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
Source: tnet.IPv4ToUint32(fsm.remote),
},
}
for pa := u.PathAttributes; pa != nil; pa = pa.Next {
......
......@@ -17,7 +17,7 @@ type Peer struct {
rib *locRIB.LocRIB
routerID uint32
addPathSend routingtable.ClientOptions
addPathRecv uint
addPathRecv bool
optOpenParams []packet.OptParam
}
......@@ -35,7 +35,7 @@ func NewPeer(c config.Peer, rib *locRIB.LocRIB) (*Peer, error) {
caps := make([]packet.Capability, 0)
addPath := uint8(0)
if c.AddPathRecv > 1 {
if c.AddPathRecv {
addPath += packet.AddPathReceive
}
if !c.AddPathSend.BestOnly {
......
......@@ -12,6 +12,7 @@ import (
)
type UpdateSender struct {
routingtable.ClientManager
fsm *FSM
}
......
package server
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
)
type UpdateSenderAddPath struct {
routingtable.ClientManager
fsm *FSM
}
func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath {
return &UpdateSenderAddPath{
fsm: fsm,
}
}
func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
fmt.Printf("SENDING AN BGP UPDATE WITH ADD PATH TO %s\n", u.fsm.remote.String())
asPathPA, err := packet.ParseASPathStr(fmt.Sprintf("%d %s", u.fsm.localASN, p.BGPPath.ASPath))
if err != nil {
return fmt.Errorf("Unable to parse AS path: %v", err)
}
update := &packet.BGPUpdateAddPath{
PathAttributes: &packet.PathAttribute{
TypeCode: packet.OriginAttr,
Value: p.BGPPath.Origin,
Next: &packet.PathAttribute{
TypeCode: packet.ASPathAttr,
Value: asPathPA.Value,
Next: &packet.PathAttribute{
TypeCode: packet.NextHopAttr,
Value: p.BGPPath.NextHop,
},
},
},
NLRI: &packet.NLRIAddPath{
PathIdentifier: p.BGPPath.PathIdentifier,
IP: pfx.Addr(),
Pfxlen: pfx.Pfxlen(),
},
}
updateBytes, err := update.SerializeUpdate()
if err != nil {
log.Errorf("Unable to serialize BGP Update: %v", err)
return nil
}
fmt.Printf("Sending Update: %v\n", updateBytes)
_, err = u.fsm.con.Write(updateBytes)
if err != nil {
return fmt.Errorf("Failed sending Update: %v", err)
}
return nil
}
func (u *UpdateSenderAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool {
log.Warningf("BGP Update Sender: RemovePath not implemented")
return false
}
func (u *UpdateSenderAddPath) UpdateNewClient(client routingtable.RouteTableClient) error {
log.Warningf("BGP Update Sender: RemovePath not implemented")
return nil
}
......@@ -7,6 +7,7 @@ import (
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/taktv6/tflow2/convert"
)
// AdjRIBOut represents an Adjacency RIB In as described in RFC4271
......@@ -29,11 +30,13 @@ func New(neighbor *routingtable.Neighbor) *AdjRIBOut {
// UpdateNewClient sends current state to a new client
func (a *AdjRIBOut) UpdateNewClient(client routingtable.RouteTableClient) error {
return fmt.Errorf("Not supported")
return nil
}
// AddPath replaces the path for prefix `pfx`. If the prefix doesn't exist it is added.
func (a *AdjRIBOut) AddPath(pfx net.Prefix, p *route.Path) error {
fmt.Printf("THIS IS ADJ RIB OUT NON ADD PATH FOR %v\n", convert.Uint32Byte(a.neighbor.Address))
if a.isOwnPath(p) {
return nil
}
......
......@@ -7,6 +7,7 @@ import (
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/taktv6/tflow2/convert"
)
// AdjRIBOutAddPath represents an Adjacency RIB Out with BGP add path
......@@ -29,11 +30,13 @@ func New(neighbor *routingtable.Neighbor) *AdjRIBOutAddPath {
// UpdateNewClient sends current state to a new client
func (a *AdjRIBOutAddPath) UpdateNewClient(client routingtable.RouteTableClient) error {
return fmt.Errorf("Not supported")
return nil
}
// AddPath adds path p to prefix `pfx`
func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
fmt.Printf("THIS IS ADD PATH ON ADJ RIB OUT FOR %v", convert.Uint32Byte(a.neighbor.Address))
if a.isOwnPath(p) {
return nil
}
......@@ -41,6 +44,10 @@ func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
a.mu.Lock()
defer a.mu.Unlock()
p.BGPPath.PathIdentifier = 7
a.rt.AddPath(pfx, p)
for _, client := range a.ClientManager.Clients() {
client.AddPath(pfx, p)
}
......
......@@ -10,4 +10,6 @@ type RouteTableClient interface {
AddPath(net.Prefix, *route.Path) error
RemovePath(net.Prefix, *route.Path) bool
UpdateNewClient(RouteTableClient) error
Register(RouteTableClient)
Unregister(RouteTableClient)
}
......@@ -51,7 +51,7 @@ func (a *LocRIB) AddPath(pfx net.Prefix, p *route.Path) error {
oldRoute = r.Copy()
routeExisted = true
}
// FIXME: in AddPath() we assume that the same reference of route (r) is modified (not responsibility of locRIB). If this implementation changes in the future this code will break.
a.rt.AddPath(pfx, p)
if !routeExisted {
......@@ -99,6 +99,7 @@ func (a *LocRIB) propagateChanges(oldRoute *route.Route, newRoute *route.Route)
}
func (a *LocRIB) addPathsToClients(oldRoute *route.Route, newRoute *route.Route) {
fmt.Printf("LocRIB: Updating %d clients\n", len(a.ClientManager.Clients()))
for _, client := range a.ClientManager.Clients() {
opts := a.ClientManager.GetOptions(client)
oldMaxPaths := opts.GetMaxPaths(oldRoute.ECMPPathCount())
......@@ -107,6 +108,9 @@ func (a *LocRIB) addPathsToClients(oldRoute *route.Route, newRoute *route.Route)
oldPathsLimit := int(math.Min(float64(oldMaxPaths), float64(len(oldRoute.Paths()))))
newPathsLimit := int(math.Min(float64(newMaxPaths), float64(len(newRoute.Paths()))))
fmt.Printf("oldPathsLimit: %v\n", oldPathsLimit)
fmt.Printf("newPathsLimit: %v\n", newPathsLimit)
advertise := route.PathsDiff(newRoute.Paths()[0:newPathsLimit], oldRoute.Paths()[0:oldPathsLimit])
fmt.Printf("ADVERTISING PATHS %v TO CLIENTS\n", advertise)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment