Skip to content
Snippets Groups Projects
Unverified Commit 86f17a1f authored by takt's avatar takt Committed by GitHub
Browse files

Merge pull request #90 from bio-routing/feature/ipv6_export

Exporting IPv6 routes (including withdraws)
parents bf930b3a 2677986f
No related branches found
No related tags found
No related merge requests found
Showing
with 807 additions and 102 deletions
......@@ -45,4 +45,23 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
ExportFilter: filter.NewDrainFilter(),
IPv6: true,
}, rib)
b.AddPeer(config.Peer{
AdminEnabled: true,
LocalAS: 65200,
PeerAS: 65400,
PeerAddress: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xcafe, 0, 0, 0, 5),
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.NewDrainFilter(),
ExportFilter: filter.NewAcceptAllFilter(),
IPv6: true,
}, rib)
}
......@@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"helper.go",
"ip.go",
"prefix.go",
],
......@@ -14,7 +13,6 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"helper_test.go",
"ip_test.go",
"prefix_test.go",
],
......
package net
import "net"
// IPv4ToUint32 converts an `net.IP` to an uint32 interpretation
func IPv4ToUint32(ip net.IP) uint32 {
ip = ip.To4()
return uint32(ip[3]) + uint32(ip[2])<<8 + uint32(ip[1])<<16 + uint32(ip[0])<<24
}
package net
import (
"testing"
"net"
"github.com/stretchr/testify/assert"
)
func TestIPv4ToUint32(t *testing.T) {
tests := []struct {
input []byte
expected uint32
}{
{
input: []byte{192, 168, 1, 5},
expected: 3232235781,
},
{
input: []byte{10, 0, 0, 0},
expected: 167772160,
},
{
input: []byte{172, 24, 5, 1},
expected: 2887255297,
},
{
input: net.ParseIP("172.24.5.1"),
expected: 2887255297,
},
}
for _, test := range tests {
res := IPv4ToUint32(test.input)
assert.Equal(t, test.expected, res)
}
}
......@@ -133,6 +133,20 @@ func (ip IP) bytesIPv4() []byte {
}
}
// IsIPv4 returns if the `IP` is of address family IPv4
func (ip IP) IsIPv4() bool {
return ip.ipVersion == 4
}
// SizeBytes returns the number of bytes required to represent the `IP`
func (ip IP) SizeBytes() uint8 {
if ip.ipVersion == 4 {
return 4
}
return 16
}
// ToUint32 return the rightmost 32 bits of an 'IP'
func (ip IP) ToUint32() uint32 {
return uint32(^uint64(0) >> 32 & ip.lower)
......
......@@ -12,8 +12,12 @@ const (
MaxLen = 4096
MinUpdateLen = 4
NLRIMaxLen = 5
AFILen = 2
SAFILen = 1
CommunityLen = 4
LargeCommunityLen = 12
IPv4Len = 4
IPv6Len = 16
ClusterIDLen = 4
OpenMsg = 1
......
......@@ -2,7 +2,6 @@ package packet
import (
"fmt"
"math"
bnet "github.com/bio-routing/bio-rd/net"
)
......@@ -12,7 +11,7 @@ func serializePrefix(pfx bnet.Prefix) []byte {
return []byte{}
}
numBytes := numberOfBytesForPrefixLength(pfx.Pfxlen())
numBytes := BytesInAddr(pfx.Pfxlen())
b := make([]byte, numBytes+1)
b[0] = pfx.Pfxlen()
......@@ -22,7 +21,7 @@ func serializePrefix(pfx bnet.Prefix) []byte {
}
func deserializePrefix(b []byte, pfxLen uint8, afi uint16) (bnet.Prefix, error) {
numBytes := numberOfBytesForPrefixLength(pfxLen)
numBytes := BytesInAddr(pfxLen)
if numBytes != uint8(len(b)) {
return bnet.Prefix{}, fmt.Errorf("could not parse prefix of length %d. Expected %d bytes, got %d", pfxLen, numBytes, len(b))
......@@ -38,7 +37,3 @@ 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))
}
......@@ -17,7 +17,7 @@ type MultiProtocolReachNLRI struct {
Prefixes []bnet.Prefix
}
func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 {
func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint16 {
nextHop := n.NextHop.Bytes()
tempBuf := bytes.NewBuffer(nil)
......@@ -32,7 +32,7 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 {
buf.Write(tempBuf.Bytes())
return uint8(tempBuf.Len())
return uint16(tempBuf.Len())
}
func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) {
......@@ -67,7 +67,7 @@ func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error)
idx := uint16(0)
for idx < uint16(len(variable)) {
pfxLen := variable[idx]
numBytes := uint16(numberOfBytesForPrefixLength(pfxLen))
numBytes := uint16(BytesInAddr(pfxLen))
idx++
r := uint16(len(variable)) - idx
......
......@@ -15,7 +15,7 @@ type MultiProtocolUnreachNLRI struct {
Prefixes []bnet.Prefix
}
func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint8 {
func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint16 {
tempBuf := bytes.NewBuffer(nil)
tempBuf.Write(convert.Uint16Byte(n.AFI))
tempBuf.WriteByte(n.SAFI)
......@@ -25,7 +25,7 @@ func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint8 {
buf.Write(tempBuf.Bytes())
return uint8(tempBuf.Len())
return uint16(tempBuf.Len())
}
func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, error) {
......@@ -49,7 +49,7 @@ func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, er
idx := uint16(0)
for idx < uint16(len(prefix)) {
pfxLen := prefix[idx]
numBytes := uint16(numberOfBytesForPrefixLength(pfxLen))
numBytes := uint16(BytesInAddr(pfxLen))
idx++
r := uint16(len(prefix)) - idx
......
......@@ -3,6 +3,7 @@ package packet
import (
"bytes"
"fmt"
"math"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/types"
......@@ -466,6 +467,10 @@ func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *types.Options) uint16
pathAttrLen = uint16(pa.serializeCommunities(buf))
case LargeCommunitiesAttr:
pathAttrLen = uint16(pa.serializeLargeCommunities(buf))
case MultiProtocolReachNLRICode:
pathAttrLen = pa.serializeMultiProtocolReachNLRI(buf)
case MultiProtocolUnreachNLRICode:
pathAttrLen = pa.serializeMultiProtocolUnreachNLRI(buf)
case OriginatorIDAttr:
pathAttrLen = uint16(pa.serializeOriginatorID(buf))
case ClusterListAttr:
......@@ -690,6 +695,62 @@ func (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint16 {
return uint16(len(b) + 2)
}
func (pa *PathAttribute) serializeMultiProtocolReachNLRI(buf *bytes.Buffer) uint16 {
v := pa.Value.(MultiProtocolReachNLRI)
pa.Optional = true
tempBuf := bytes.NewBuffer(nil)
v.serialize(tempBuf)
return pa.serializeGeneric(tempBuf.Bytes(), buf)
}
func (pa *PathAttribute) serializeMultiProtocolUnreachNLRI(buf *bytes.Buffer) uint16 {
v := pa.Value.(MultiProtocolUnreachNLRI)
pa.Optional = true
tempBuf := bytes.NewBuffer(nil)
v.serialize(tempBuf)
return pa.serializeGeneric(tempBuf.Bytes(), buf)
}
func (pa *PathAttribute) serializeGeneric(b []byte, buf *bytes.Buffer) uint16 {
attrFlags := uint8(0)
if pa.Optional {
attrFlags = setOptional(attrFlags)
}
if pa.Transitive {
attrFlags = setTransitive(attrFlags)
}
if len(b) > math.MaxUint8 {
pa.ExtendedLength = true
}
if pa.ExtendedLength {
attrFlags = setExtendedLength(attrFlags)
}
if pa.Transitive {
attrFlags = setTransitive(attrFlags)
}
buf.WriteByte(attrFlags)
buf.WriteByte(pa.TypeCode)
if pa.ExtendedLength {
l := len(b)
buf.WriteByte(uint8(l >> 8))
buf.WriteByte(uint8(l & 0x0000FFFF))
} else {
buf.WriteByte(uint8(len(b)))
}
buf.Write(b)
return uint16(len(b) + 2)
}
func fourBytesToUint32(address [4]byte) uint32 {
return uint32(address[0])<<24 + uint32(address[1])<<16 + uint32(address[2])<<8 + uint32(address[3])
}
......@@ -765,7 +826,7 @@ func PathAttributes(p *route.Path, iBGP bool, rrClient bool) (*PathAttribute, er
if p.BGPPath.Aggregator != nil {
aggregator := &PathAttribute{
TypeCode: AggregatorAttr,
Value: p.BGPPath.Aggregator,
Value: *p.BGPPath.Aggregator,
}
last.Next = aggregator
last = aggregator
......
......@@ -259,6 +259,8 @@ func (s *establishedState) newRoutePath() *route.Path {
}
func (s *establishedState) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) {
path.BGPPath.NextHop = nlri.NextHop
for _, pfx := range nlri.Prefixes {
s.fsm.adjRIBIn.AddPath(pfx, path)
}
......
......@@ -86,8 +86,11 @@ func (u *UpdateSender) sender(aggrTime time.Duration) {
u.toSendMu.Lock()
overhead := u.updateOverhead()
for key, pathNLRIs := range u.toSend {
budget = packet.MaxLen - packet.HeaderLen - packet.MinUpdateLen - int(pathNLRIs.path.BGPPath.Length())
budget = packet.MaxLen - packet.HeaderLen - packet.MinUpdateLen - int(pathNLRIs.path.BGPPath.Length()) - overhead
pathAttrs, err = packet.PathAttributes(pathNLRIs.path, u.iBGP, u.rrClient)
if err != nil {
log.Errorf("Unable to get path attributes: %v", err)
......@@ -98,10 +101,11 @@ func (u *UpdateSender) sender(aggrTime time.Duration) {
prefixes := make([]bnet.Prefix, 0, 1)
for _, pfx := range pathNLRIs.pfxs {
budget -= int(packet.BytesInAddr(pfx.Pfxlen())) + 1
if budget < 0 {
updatesPrefixes = append(updatesPrefixes, prefixes)
prefixes = make([]bnet.Prefix, 0, 1)
budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length())
budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length()) - overhead
}
prefixes = append(prefixes, pfx)
......@@ -120,35 +124,97 @@ func (u *UpdateSender) sender(aggrTime time.Duration) {
}
}
func (u *UpdateSender) updateOverhead() int {
// TODO: for multi RIB support we need the AFI/SAFI combination to determine overhead. For now: MultiProtocol = IPv6
if u.fsm.options.SupportsMultiProtocol {
// since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length
return packet.AFILen + packet.SAFILen + 1 + packet.IPv6Len - packet.IPv4Len + 1
}
return 0
}
func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) {
var nlri *packet.NLRI
var err error
for _, prefixes := range updatePrefixes {
update := u.updateMessageForPrefixes(prefixes, pathAttrs, pathID)
for _, updatePrefix := range updatePrefixes {
update := &packet.BGPUpdate{
PathAttributes: pathAttrs,
err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
if err != nil {
log.Errorf("Failed to serialize and send: %v", err)
}
}
}
for _, pfx := range updatePrefix {
nlri = &packet.NLRI{
PathIdentifier: pathID,
IP: pfx.Addr().ToUint32(),
Pfxlen: pfx.Pfxlen(),
Next: update.NLRI,
}
update.NLRI = nlri
func (u *UpdateSender) updateMessageForPrefixes(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
if u.fsm.options.SupportsMultiProtocol {
return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
}
return u.bgpUpdate(pfxs, pa, pathID)
}
func (u *UpdateSender) bgpUpdate(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
update := &packet.BGPUpdate{
PathAttributes: pa,
}
var nlri *packet.NLRI
for _, pfx := range pfxs {
nlri = &packet.NLRI{
PathIdentifier: pathID,
IP: pfx.Addr().ToUint32(),
Pfxlen: pfx.Pfxlen(),
Next: update.NLRI,
}
update.NLRI = nlri
}
err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
if err != nil {
log.Errorf("Failed to serialize and send: %v", err)
return update
}
func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
pa, nextHop := u.copyAttributesWithoutNextHop(pa)
attrs := &packet.PathAttribute{
TypeCode: packet.MultiProtocolReachNLRICode,
Value: packet.MultiProtocolReachNLRI{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
NextHop: nextHop,
Prefixes: pfxs,
},
}
attrs.Next = pa
return &packet.BGPUpdate{
PathAttributes: attrs,
}
}
func (u *UpdateSender) copyAttributesWithoutNextHop(pa *packet.PathAttribute) (attrs *packet.PathAttribute, nextHop bnet.IP) {
var curCopy, lastCopy *packet.PathAttribute
for cur := pa; cur != nil; cur = cur.Next {
if cur.TypeCode == packet.NextHopAttr {
nextHop = cur.Value.(bnet.IP)
} else {
curCopy = cur.Copy()
if lastCopy == nil {
attrs = curCopy
} else {
lastCopy.Next = curCopy
}
lastCopy = curCopy
}
}
return attrs, nextHop
}
// RemovePath withdraws prefix `pfx` from a peer
func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
err := u.withdrawPrefix(pfx, p)
if err != nil {
log.Errorf("Unable to withdraw prefix: %v", err)
return false
......@@ -156,6 +222,14 @@ func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
return true
}
func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error {
if u.fsm.options.SupportsMultiProtocol {
return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx)
}
return withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
}
// UpdateNewClient does nothing
func (u *UpdateSender) UpdateNewClient(client routingtable.RouteTableClient) error {
log.Warningf("BGP Update Sender: UpdateNewClient not implemented")
......
This diff is collapsed.
......@@ -58,3 +58,17 @@ func withDrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix,
}
return serializeAndSendUpdate(out, update, opt)
}
func withDrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix) error {
update := &packet.BGPUpdate{
PathAttributes: &packet.PathAttribute{
TypeCode: packet.MultiProtocolUnreachNLRICode,
Value: packet.MultiProtocolUnreachNLRI{
AFI: packet.IPv6AFI,
SAFI: packet.UnicastSAFI,
Prefixes: []net.Prefix{pfx},
},
},
}
return serializeAndSendUpdate(out, update, opt)
}
......@@ -61,6 +61,46 @@ func TestWithDrawPrefixes(t *testing.T) {
}
}
func TestWithDrawPrefixesMultiProtocol(t *testing.T) {
tests := []struct {
Name string
Prefix net.Prefix
Expected []byte
}{
{
Name: "IPv6 MP_UNREACH_NLRI",
Prefix: net.NewPfx(net.IPv6FromBlocks(0x2804, 0x148c, 0, 0, 0, 0, 0, 0), 32),
Expected: []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
0x00, 0x22, // BGP Message Length
0x02, // BGP Message Type == Update
0x00, 0x00, // WithDraw Octet length
0x00, 0x0b, // Length
0x80, // Flags
0x0f, // Attribute Code
0x08, // Attribute length
0x00, 0x02, // AFI
0x01, // SAFI
0x20, 0x28, 0x04, 0x14, 0x8c, // Prefix
},
},
}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
opt := &types.Options{
AddPathRX: false,
}
err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
assert.Equal(t, test.Expected, buf.Bytes())
})
}
}
func TestWithDrawPrefixesAddPath(t *testing.T) {
testcases := []struct {
Name string
......
......@@ -5,9 +5,10 @@ import (
"fmt"
"strings"
"github.com/taktv6/tflow2/convert"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/types"
"github.com/taktv6/tflow2/convert"
)
// BGPPath represents a set of BGP path attributes
......@@ -253,7 +254,7 @@ func (b *BGPPath) Print() string {
ret += fmt.Sprintf("\t\tLargeCommunities: %v\n", b.LargeCommunities)
if b.OriginatorID != 0 {
oid := uint32To4Byte(b.OriginatorID)
oid := convert.Uint32Byte(b.OriginatorID)
ret += fmt.Sprintf("\t\tOriginatorID: %d.%d.%d.%d\n", oid[0], oid[1], oid[2], oid[3])
}
if b.ClusterList != nil {
......@@ -331,7 +332,7 @@ func (b *BGPPath) Copy() *BGPPath {
// ComputeHash computes an hash over all attributes of the path
func (b *BGPPath) ComputeHash() string {
s := fmt.Sprintf("%s\t%d\t%v\t%d\t%d\t%v\t%d\t%s\t%v\t%v\t%d",
s := fmt.Sprintf("%s\t%d\t%v\t%d\t%d\t%v\t%d\t%s\t%v\t%v\t%d\t%d\t%v",
b.NextHop,
b.LocalPref,
b.ASPath,
......@@ -341,10 +342,10 @@ func (b *BGPPath) ComputeHash() string {
b.BGPIdentifier,
b.Source,
b.Communities,
b.OriginatorID,
b.ClusterList,
b.LargeCommunities,
b.PathIdentifier)
b.PathIdentifier,
b.OriginatorID,
b.ClusterList)
return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
}
......@@ -363,7 +364,7 @@ func (b *BGPPath) CommunitiesString() string {
func (b *BGPPath) ClusterListString() string {
str := ""
for _, cid := range b.ClusterList {
octes := uint32To4Byte(cid)
octes := convert.Uint32Byte(cid)
str += fmt.Sprintf("%d.%d.%d.%d ", octes[0], octes[1], octes[2], octes[3])
}
......@@ -379,14 +380,3 @@ func (b *BGPPath) LargeCommunitiesString() string {
return strings.TrimRight(str, " ")
}
func uint32To4Byte(addr uint32) [4]byte {
slice := convert.Uint32Byte(addr)
ret := [4]byte{
slice[0],
slice[1],
slice[2],
slice[3],
}
return ret
}
......@@ -36,9 +36,9 @@ func TestComputeHash(t *testing.T) {
Source: bnet.IPv4(4),
}
assert.Equal(t, "1058916ff3e6a51c7d8a47945d13fc3fcd8ee578a6d376505f46d58979b30fae", p.ComputeHash())
assert.Equal(t, "5907ed8960ccc14eed8f1a34a8eb3e6c82a8dd947d6cbf67eb58ca292f4588d5", p.ComputeHash())
p.LocalPref = 150
assert.NotEqual(t, "1058916ff3e6a51c7d8a47945d13fc3fcd8ee578a6d376505f46d58979b30fae", p.ComputeHash())
assert.NotEqual(t, "5907ed8960ccc14eed8f1a34a8eb3e6c82a8dd947d6cbf67eb58ca292f4588d5", p.ComputeHash())
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment