From 54c415719b5d26961dead88c5ddd3094dde25ba0 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk <daniel@dan-nrw.de> Date: Sun, 1 Jul 2018 22:53:19 +0200 Subject: [PATCH] bio receives ipv6 routes, tag for ipv6 testbed --- config/peer.go | 1 + main.go | 55 +-------------- main_ipv4.go | 70 ++++++++++++++++++++ main_ipv6.go | 48 ++++++++++++++ protocols/bgp/packet/helper.go | 10 ++- protocols/bgp/packet/mp_reach_nlri.go | 29 +++++--- protocols/bgp/packet/mp_reach_nlri_test.go | 4 +- protocols/bgp/packet/mp_unreach_nlri.go | 25 +++++-- protocols/bgp/packet/mp_unreach_nlri_test.go | 8 ++- protocols/bgp/packet/path_attributes_test.go | 12 ++-- protocols/bgp/server/fsm_established.go | 41 +++++++++++- protocols/bgp/server/fsm_open_sent.go | 6 ++ protocols/bgp/server/peer.go | 14 ++++ protocols/bgp/types/options.go | 5 +- 14 files changed, 245 insertions(+), 83 deletions(-) create mode 100644 main_ipv4.go create mode 100644 main_ipv6.go diff --git a/config/peer.go b/config/peer.go index 5f151fe5..a192e317 100644 --- a/config/peer.go +++ b/config/peer.go @@ -25,4 +25,5 @@ type Peer struct { ImportFilter *filter.Filter ExportFilter *filter.Filter RouteServerClient bool + IPv6 bool } diff --git a/main.go b/main.go index 6b924601..11c2a689 100644 --- a/main.go +++ b/main.go @@ -2,15 +2,11 @@ package main import ( "fmt" - "net" "time" "github.com/sirupsen/logrus" - "github.com/bio-routing/bio-rd/config" "github.com/bio-routing/bio-rd/protocols/bgp/server" - "github.com/bio-routing/bio-rd/routingtable" - "github.com/bio-routing/bio-rd/routingtable/filter" "github.com/bio-routing/bio-rd/routingtable/locRIB" bnet "github.com/bio-routing/bio-rd/net" @@ -26,56 +22,7 @@ func main() { rib := locRIB.New() b := server.NewBgpServer() - - err := b.Start(&config.Global{ - Listen: true, - LocalAddressList: []net.IP{ - net.IPv4(169, 254, 100, 1), - net.IPv4(169, 254, 200, 0), - }, - }) - if err != nil { - logrus.Fatalf("Unable to start BGP server: %v", err) - } - - b.AddPeer(config.Peer{ - AdminEnabled: true, - LocalAS: 65200, - PeerAS: 65300, - PeerAddress: bnet.IPv4FromOctets(172, 17, 0, 3), - LocalAddress: bnet.IPv4FromOctets(169, 254, 200, 0), - ReconnectInterval: time.Second * 15, - HoldTime: time.Second * 90, - KeepAlive: time.Second * 30, - Passive: true, - RouterID: b.RouterID(), - AddPathSend: routingtable.ClientOptions{ - MaxPaths: 10, - }, - ImportFilter: filter.NewAcceptAllFilter(), - ExportFilter: filter.NewAcceptAllFilter(), - RouteServerClient: true, - }, rib) - - b.AddPeer(config.Peer{ - AdminEnabled: true, - LocalAS: 65200, - PeerAS: 65100, - PeerAddress: bnet.IPv4FromOctets(172, 17, 0, 2), - LocalAddress: bnet.IPv4FromOctets(169, 254, 100, 1), - ReconnectInterval: time.Second * 15, - HoldTime: time.Second * 90, - KeepAlive: time.Second * 30, - Passive: true, - RouterID: b.RouterID(), - AddPathSend: routingtable.ClientOptions{ - MaxPaths: 10, - }, - AddPathRecv: true, - ImportFilter: filter.NewAcceptAllFilter(), - ExportFilter: filter.NewAcceptAllFilter(), - RouteServerClient: true, - }, rib) + startServer(b, rib) go func() { for { diff --git a/main_ipv4.go b/main_ipv4.go new file mode 100644 index 00000000..65293f07 --- /dev/null +++ b/main_ipv4.go @@ -0,0 +1,70 @@ +// +build !ipv6 + +package main + +import ( + "net" + "time" + + "github.com/bio-routing/bio-rd/routingtable/locRIB" + + "github.com/bio-routing/bio-rd/config" + "github.com/bio-routing/bio-rd/protocols/bgp/server" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/sirupsen/logrus" + + bnet "github.com/bio-routing/bio-rd/net" +) + +func startServer(b server.BGPServer, rib *locRIB.LocRIB) { + err := b.Start(&config.Global{ + Listen: true, + LocalAddressList: []net.IP{ + net.IPv4(169, 254, 100, 1), + net.IPv4(169, 254, 200, 0), + }, + }) + if err != nil { + logrus.Fatalf("Unable to start BGP server: %v", err) + } + + b.AddPeer(config.Peer{ + AdminEnabled: true, + LocalAS: 65200, + PeerAS: 65300, + PeerAddress: bnet.IPv4FromOctets(172, 17, 0, 3), + LocalAddress: bnet.IPv4FromOctets(169, 254, 200, 0), + ReconnectInterval: time.Second * 15, + HoldTime: time.Second * 90, + KeepAlive: time.Second * 30, + Passive: true, + RouterID: b.RouterID(), + AddPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + ImportFilter: filter.NewAcceptAllFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + RouteServerClient: true, + }, rib) + + b.AddPeer(config.Peer{ + AdminEnabled: true, + LocalAS: 65200, + PeerAS: 65100, + PeerAddress: bnet.IPv4FromOctets(172, 17, 0, 2), + LocalAddress: bnet.IPv4FromOctets(169, 254, 100, 1), + ReconnectInterval: time.Second * 15, + HoldTime: time.Second * 90, + KeepAlive: time.Second * 30, + Passive: true, + RouterID: b.RouterID(), + AddPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + AddPathRecv: true, + ImportFilter: filter.NewAcceptAllFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + RouteServerClient: true, + }, rib) +} diff --git a/main_ipv6.go b/main_ipv6.go new file mode 100644 index 00000000..9afc7e90 --- /dev/null +++ b/main_ipv6.go @@ -0,0 +1,48 @@ +// +build ipv6 + +package main + +import ( + "net" + "time" + + "github.com/bio-routing/bio-rd/config" + "github.com/bio-routing/bio-rd/protocols/bgp/server" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/bio-routing/bio-rd/routingtable/locRIB" + "github.com/sirupsen/logrus" + + bnet "github.com/bio-routing/bio-rd/net" +) + +func startServer(b server.BGPServer, rib *locRIB.LocRIB) { + err := b.Start(&config.Global{ + Listen: true, + LocalAddressList: []net.IP{ + net.IP{0x20, 0x01, 0x6, 0x78, 0x1, 0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0xca, 0xfe}, + }, + }) + if err != nil { + logrus.Fatalf("Unable to start BGP server: %v", err) + } + + b.AddPeer(config.Peer{ + AdminEnabled: true, + LocalAS: 65200, + PeerAS: 202739, + PeerAddress: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 1), + LocalAddress: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0xcafe), + ReconnectInterval: time.Second * 15, + HoldTime: time.Second * 90, + KeepAlive: time.Second * 30, + Passive: true, + RouterID: b.RouterID(), + AddPathSend: routingtable.ClientOptions{ + BestOnly: true, + }, + ImportFilter: filter.NewAcceptAllFilter(), + ExportFilter: filter.NewDrainFilter(), + IPv6: true, + }, rib) +} diff --git a/protocols/bgp/packet/helper.go b/protocols/bgp/packet/helper.go index 18176cf1..a4a0226b 100644 --- a/protocols/bgp/packet/helper.go +++ b/protocols/bgp/packet/helper.go @@ -12,7 +12,7 @@ func serializePrefix(pfx bnet.Prefix) []byte { return []byte{} } - numBytes := uint8(math.Ceil(float64(pfx.Pfxlen()) / 8)) + numBytes := numberOfBytesForPrefixLength(pfx.Pfxlen()) b := make([]byte, numBytes+1) b[0] = pfx.Pfxlen() @@ -22,9 +22,9 @@ func serializePrefix(pfx bnet.Prefix) []byte { } func deserializePrefix(b []byte, pfxLen uint8, afi uint16) (bnet.Prefix, error) { - numBytes := int(math.Ceil(float64(pfxLen) / 8)) + numBytes := numberOfBytesForPrefixLength(pfxLen) - if numBytes != len(b) { + if numBytes != uint8(len(b)) { return bnet.Prefix{}, fmt.Errorf("could not parse prefix of legth %d. Expected %d bytes, got %d", pfxLen, numBytes, len(b)) } @@ -38,3 +38,7 @@ func deserializePrefix(b []byte, pfxLen uint8, afi uint16) (bnet.Prefix, error) return bnet.NewPfx(ip, pfxLen), nil } + +func numberOfBytesForPrefixLength(pfxLen uint8) uint8 { + return uint8(math.Ceil(float64(pfxLen) / 8)) +} diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go index e886f27c..e6ec075e 100644 --- a/protocols/bgp/packet/mp_reach_nlri.go +++ b/protocols/bgp/packet/mp_reach_nlri.go @@ -11,10 +11,10 @@ import ( // MultiProtocolReachNLRI represents network layer reachability information for one prefix of an IP address family (rfc4760) type MultiProtocolReachNLRI struct { - AFI uint16 - SAFI uint8 - NextHop bnet.IP - Prefix bnet.Prefix + AFI uint16 + SAFI uint8 + NextHop bnet.IP + Prefixes []bnet.Prefix } func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 { @@ -26,7 +26,9 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 { tempBuf.WriteByte(uint8(len(nextHop))) // NextHop length tempBuf.Write(nextHop) tempBuf.WriteByte(0) // RESERVED - tempBuf.Write(serializePrefix(n.Prefix)) + for _, pfx := range n.Prefixes { + tempBuf.Write(serializePrefix(pfx)) + } buf.Write(tempBuf.Bytes()) @@ -54,9 +56,20 @@ func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) return MultiProtocolReachNLRI{}, fmt.Errorf("Failed to decode next hop IP: %v", err) } - n.Prefix, err = deserializePrefix(variable[nextHopLength+2:], variable[nextHopLength+1], n.AFI) - if err != nil { - return MultiProtocolReachNLRI{}, err + variable = variable[1+nextHopLength:] + + idx := uint8(0) + n.Prefixes = make([]bnet.Prefix, 0) + for idx < uint8(len(variable)) { + l := numberOfBytesForPrefixLength(variable[idx]) + + pfx, err := deserializePrefix(variable[idx+1:idx+1+l], variable[idx], n.AFI) + if err != nil { + return MultiProtocolReachNLRI{}, err + } + n.Prefixes = append(n.Prefixes, pfx) + + idx = idx + l + 1 } return n, nil diff --git a/protocols/bgp/packet/mp_reach_nlri_test.go b/protocols/bgp/packet/mp_reach_nlri_test.go index 2bbb9d2e..f16ac3c4 100644 --- a/protocols/bgp/packet/mp_reach_nlri_test.go +++ b/protocols/bgp/packet/mp_reach_nlri_test.go @@ -20,7 +20,9 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) { AFI: IPv6AFI, SAFI: UnicastSAFI, NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2), - Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48), + Prefixes: []bnet.Prefix{ + bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48), + }, }, expected: []byte{ 0x00, 0x02, // AFI diff --git a/protocols/bgp/packet/mp_unreach_nlri.go b/protocols/bgp/packet/mp_unreach_nlri.go index e3e43822..35f3cadf 100644 --- a/protocols/bgp/packet/mp_unreach_nlri.go +++ b/protocols/bgp/packet/mp_unreach_nlri.go @@ -9,16 +9,18 @@ import ( // MultiProtocolUnreachNLRI represents network layer withdraw information for one prefix of an IP address family (rfc4760) type MultiProtocolUnreachNLRI struct { - AFI uint16 - SAFI uint8 - Prefix bnet.Prefix + AFI uint16 + SAFI uint8 + Prefixes []bnet.Prefix } func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint8 { tempBuf := bytes.NewBuffer(nil) tempBuf.Write(convert.Uint16Byte(n.AFI)) tempBuf.WriteByte(n.SAFI) - tempBuf.Write(serializePrefix(n.Prefix)) + for _, pfx := range n.Prefixes { + tempBuf.Write(serializePrefix(pfx)) + } buf.Write(tempBuf.Bytes()) @@ -39,9 +41,18 @@ func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, er return MultiProtocolUnreachNLRI{}, err } - n.Prefix, err = deserializePrefix(prefix[1:], prefix[0], n.AFI) - if err != nil { - return MultiProtocolUnreachNLRI{}, err + idx := uint8(0) + n.Prefixes = make([]bnet.Prefix, 0) + for idx < uint8(len(prefix)) { + l := numberOfBytesForPrefixLength(prefix[idx]) + + pfx, err := deserializePrefix(prefix[idx+1:idx+1+l], prefix[idx], n.AFI) + if err != nil { + return MultiProtocolUnreachNLRI{}, err + } + n.Prefixes = append(n.Prefixes, pfx) + + idx = idx + l + 1 } return n, nil diff --git a/protocols/bgp/packet/mp_unreach_nlri_test.go b/protocols/bgp/packet/mp_unreach_nlri_test.go index 7e132acc..02e1b00f 100644 --- a/protocols/bgp/packet/mp_unreach_nlri_test.go +++ b/protocols/bgp/packet/mp_unreach_nlri_test.go @@ -17,9 +17,11 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) { { name: "Simple IPv6 prefix", nlri: MultiProtocolUnreachNLRI{ - AFI: IPv6AFI, - SAFI: UnicastSAFI, - Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + AFI: IPv6AFI, + SAFI: UnicastSAFI, + Prefixes: []bnet.Prefix{ + bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + }, }, expected: []byte{ 0x00, 0x02, // AFI diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 0048f900..3bd22598 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -775,7 +775,9 @@ func TestDecodeMultiProtocolReachNLRI(t *testing.T) { AFI: IPv6AFI, SAFI: UnicastSAFI, NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2), - Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48), + Prefixes: []bnet.Prefix{ + bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48), + }, }, }, }, @@ -835,9 +837,11 @@ func TestDecodeMultiProtocolUnreachNLRI(t *testing.T) { expected: &PathAttribute{ Length: 10, Value: MultiProtocolUnreachNLRI{ - AFI: IPv6AFI, - SAFI: UnicastSAFI, - Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + AFI: IPv6AFI, + SAFI: UnicastSAFI, + Prefixes: []bnet.Prefix{ + bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44), + }, }, }, }, diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index b5740c57..9c14c322 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -69,6 +69,10 @@ func (s *establishedState) init() error { } hostIP := net.ParseIP(host) if hostIP == nil { + return fmt.Errorf("Unable to parse address") + } + localAddr, err := bnet.IPFromBytes(hostIP) + if err != nil { return fmt.Errorf("Unable to parse address: %v", err) } @@ -78,7 +82,7 @@ func (s *establishedState) init() error { IBGP: s.fsm.peer.localASN == s.fsm.peer.peerASN, LocalASN: s.fsm.peer.localASN, RouteServerClient: s.fsm.peer.routeServerClient, - LocalAddress: bnet.IPv4(bnet.IPv4ToUint32(hostIP)), + LocalAddress: localAddr, CapAddPathRX: s.fsm.options.AddPathRX, } @@ -163,6 +167,7 @@ func (s *establishedState) keepaliveTimerExpired() (state, string) { func (s *establishedState) msgReceived(data []byte) (state, string) { msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.options) if err != nil { + fmt.Println(err) switch bgperr := err.(type) { case packet.BGPError: s.fsm.sendNotification(bgperr.ErrorCode, bgperr.ErrorSubCode) @@ -201,6 +206,7 @@ func (s *establishedState) update(msg *packet.BGPMessage) (state, string) { u := msg.Body.(*packet.BGPUpdate) s.withdraws(u) s.updates(u) + s.multiProtocolUpdates(u) return newEstablishedState(s.fsm), s.fsm.reason } @@ -230,6 +236,39 @@ func (s *establishedState) updates(u *packet.BGPUpdate) { } } +func (s *establishedState) multiProtocolUpdates(u *packet.BGPUpdate) { + path := &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Source: s.fsm.peer.addr, + EBGP: s.fsm.peer.localASN != s.fsm.peer.peerASN, + }, + } + + s.processAttributes(u.PathAttributes, path) + + for pa := u.PathAttributes; pa != nil; pa = pa.Next { + switch pa.TypeCode { + case packet.MultiProtocolReachNLRICode: + s.multiProtocolUpdate(path, pa.Value.(packet.MultiProtocolReachNLRI)) + case packet.MultiProtocolUnreachNLRICode: + s.multiProtocolWithdraw(path, pa.Value.(packet.MultiProtocolUnreachNLRI)) + } + } +} + +func (s *establishedState) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) { + for _, pfx := range nlri.Prefixes { + s.fsm.adjRIBIn.AddPath(pfx, path) + } +} + +func (s *establishedState) multiProtocolWithdraw(path *route.Path, nlri packet.MultiProtocolUnreachNLRI) { + for _, pfx := range nlri.Prefixes { + s.fsm.adjRIBIn.RemovePath(pfx, path) + } +} + func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path *route.Path) { for pa := attrs; pa != nil; pa = pa.Next { switch pa.TypeCode { diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index 928ed450..3ece0447 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -169,9 +169,15 @@ func (s *openSentState) processCapability(cap packet.Capability) { s.processAddPathCapability(cap.Value.(packet.AddPathCapability)) case packet.ASN4CapabilityCode: s.processASN4Capability(cap.Value.(packet.ASN4Capability)) + case packet.MultiProtocolCapabilityCode: + s.processMultiProtocolCapability(cap.Value.(packet.MultiProtocolCapability)) } } +func (s *openSentState) processMultiProtocolCapability(cap packet.MultiProtocolCapability) { + s.fsm.options.SupportsMultiProtocol = true +} + func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapability) { if addPathCap.AFI != 1 { return diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 7a7587f8..638d8b03 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -133,6 +133,10 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error caps = append(caps, asn4Capability(c)) + if c.IPv6 { + caps = append(caps, multiProtocolCapability(packet.IPv6AFI)) + } + p.optOpenParams = append(p.optOpenParams, packet.OptParam{ Type: packet.CapabilitiesParamType, Value: caps, @@ -150,6 +154,16 @@ func asn4Capability(c config.Peer) packet.Capability { } } +func multiProtocolCapability(afi uint16) packet.Capability { + return packet.Capability{ + Code: packet.MultiProtocolCapabilityCode, + Value: packet.MultiProtocolCapability{ + AFI: afi, + SAFI: packet.UnicastSAFI, + }, + } +} + func handleAddPathCapability(c config.Peer) (bool, packet.Capability) { addPath := uint8(0) if c.AddPathRecv { diff --git a/protocols/bgp/types/options.go b/protocols/bgp/types/options.go index 19b580d2..8b96d669 100644 --- a/protocols/bgp/types/options.go +++ b/protocols/bgp/types/options.go @@ -2,6 +2,7 @@ package types // Options represents options to the update sender, decoder and encoder type Options struct { - Supports4OctetASN bool - AddPathRX bool + Supports4OctetASN bool + SupportsMultiProtocol bool + AddPathRX bool } -- GitLab