diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 8aabec7f7c729cd23d47ed6abe517742f2583708..a8df09179d16d6154a6df2e8b0bbfe3ea7abd481 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -133,22 +133,6 @@ type BGPNotification struct {
 	ErrorSubcode uint8
 }
 
-type BGPUpdate struct {
-	WithdrawnRoutesLen uint16
-	WithdrawnRoutes    *NLRI
-	TotalPathAttrLen   uint16
-	PathAttributes     *PathAttribute
-	NLRI               *NLRI
-}
-
-type BGPUpdateAddPath struct {
-	WithdrawnRoutesLen uint16
-	WithdrawnRoutes    *NLRIAddPath
-	TotalPathAttrLen   uint16
-	PathAttributes     *PathAttribute
-	NLRI               *NLRIAddPath
-}
-
 type PathAttribute struct {
 	Length         uint16
 	Optional       bool
@@ -160,19 +144,6 @@ type PathAttribute struct {
 	Next           *PathAttribute
 }
 
-type NLRI struct {
-	IP     uint32
-	Pfxlen uint8
-	Next   *NLRI
-}
-
-type NLRIAddPath struct {
-	PathIdentifier uint32
-	IP             uint32
-	Pfxlen         uint8
-	Next           *NLRIAddPath
-}
-
 type Aggretator struct {
 	Addr uint32
 	ASN  uint16
diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go
index ac9bc4483199b81f4e8ecc25c6b91e3ec8361b9c..122bca972219f246346b263f666554a13f035b04 100644
--- a/protocols/bgp/packet/encoder.go
+++ b/protocols/bgp/packet/encoder.go
@@ -2,7 +2,6 @@ package packet
 
 import (
 	"bytes"
-	"fmt"
 
 	"github.com/taktv6/tflow2/convert"
 )
@@ -62,121 +61,3 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) {
 	buf.Write(convert.Uint16Byte(length))
 	buf.WriteByte(typ)
 }
-
-func (b *BGPUpdateAddPath) SerializeUpdate(opt *Options) ([]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, opt))
-		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(opt *Options) ([]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, opt))
-		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
-}
diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go
index 6de92a6cd59ff167d87efbaf5d197fb46c00aaca..a9ed83b94946ea6415672970668e58fd5c99aa71 100644
--- a/protocols/bgp/packet/nlri.go
+++ b/protocols/bgp/packet/nlri.go
@@ -9,6 +9,13 @@ import (
 	"github.com/taktv6/tflow2/convert"
 )
 
+type NLRI struct {
+	PathIdentifier uint32
+	IP             uint32
+	Pfxlen         uint8
+	Next           *NLRI
+}
+
 func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) {
 	var ret *NLRI
 	var eol *NLRI
@@ -65,7 +72,7 @@ func (n *NLRI) 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)
+	nBytes := BytesInAddr(n.Pfxlen)
 
 	buf.WriteByte(n.Pfxlen)
 	buf.Write(addr[:nBytes])
@@ -73,11 +80,11 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 {
 	return nBytes + 1
 }
 
-func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 {
+func (n *NLRI) serializeAddPath(buf *bytes.Buffer) uint8 {
 	a := convert.Uint32Byte(n.IP)
 
 	addr := [4]byte{a[0], a[1], a[2], a[3]}
-	nBytes := bytesInAddr(n.Pfxlen)
+	nBytes := BytesInAddr(n.Pfxlen)
 
 	buf.Write(convert.Uint32Byte(n.PathIdentifier))
 	buf.WriteByte(n.Pfxlen)
@@ -86,6 +93,7 @@ func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 {
 	return nBytes + 1
 }
 
-func bytesInAddr(pfxlen uint8) uint8 {
+// BytesInAddr gets the amount of bytes needed to encode an NLRI of prefix length pfxlen
+func BytesInAddr(pfxlen uint8) uint8 {
 	return uint8(math.Ceil(float64(pfxlen) / 8))
 }
diff --git a/protocols/bgp/packet/options.go b/protocols/bgp/packet/options.go
index b2d35bc60cbe17490f94314c28da26de9a19e40a..612e68935c59108d84915dcd78d505bf5e42efb5 100644
--- a/protocols/bgp/packet/options.go
+++ b/protocols/bgp/packet/options.go
@@ -2,4 +2,5 @@ package packet
 
 type Options struct {
 	Supports4OctetASN bool
+	AddPathRX         bool
 }
diff --git a/protocols/bgp/packet/update.go b/protocols/bgp/packet/update.go
new file mode 100644
index 0000000000000000000000000000000000000000..001f2c1cdba2227d8bdb179789a9f0bebc03bb23
--- /dev/null
+++ b/protocols/bgp/packet/update.go
@@ -0,0 +1,145 @@
+package packet
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/taktv6/tflow2/convert"
+)
+
+type BGPUpdate struct {
+	WithdrawnRoutesLen uint16
+	WithdrawnRoutes    *NLRI
+	TotalPathAttrLen   uint16
+	PathAttributes     *PathAttribute
+	NLRI               *NLRI
+}
+
+func (b *BGPUpdate) SerializeUpdate(opt *Options) ([]byte, error) {
+	budget := MaxLen - MinLen
+	nlriLen := 0
+	buf := bytes.NewBuffer(nil)
+
+	withdrawBuf := bytes.NewBuffer(nil)
+	for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next {
+		if opt.AddPathRX {
+			nlriLen = int(withdraw.serializeAddPath(withdrawBuf))
+		} else {
+			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, opt))
+		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 {
+		if opt.AddPathRX {
+			nlriLen = int(nlri.serializeAddPath(withdrawBuf))
+		} else {
+			nlriLen = int(nlri.serialize(withdrawBuf))
+		}
+
+		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) SerializeUpdateAddPath(opt *Options) ([]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, opt))
+		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
+}
diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index cf970b40483511c2ecafc1d36597f66117de06ef..94612c231adca5737f1c28537c428e8152808b0b 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -54,9 +54,6 @@ type FSM struct {
 	msgRecvFailCh chan error
 	stopMsgRecvCh chan struct{}
 
-	capAddPathSend bool
-	capAddPathRecv bool
-
 	options *packet.Options
 
 	local net.IP
@@ -65,7 +62,7 @@ type FSM struct {
 	adjRIBIn        routingtable.RouteTableClient
 	adjRIBOut       routingtable.RouteTableClient
 	rib             *locRIB.LocRIB
-	updateSender    routingtable.RouteTableClient
+	updateSender    *UpdateSender
 
 	neighborID uint32
 	state      state
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index fdb6ea30a1463ad06e0cb44507205eed71d7c75b..3111055d9c0757f329c72acf27c66a1942cc34a4 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -77,20 +77,21 @@ func (s *establishedState) init() error {
 		LocalASN:          s.fsm.peer.localASN,
 		RouteServerClient: s.fsm.peer.routeServerClient,
 		LocalAddress:      bnet.IPv4ToUint32(hostIP),
-		CapAddPathRX:      s.fsm.capAddPathSend,
+		CapAddPathRX:      s.fsm.options.AddPathRX,
 	}
 
 	s.fsm.adjRIBOut = adjRIBOut.New(n, s.fsm.peer.exportFilter)
 	clientOptions := routingtable.ClientOptions{
 		BestOnly: true,
 	}
-	if s.fsm.capAddPathSend {
+	if s.fsm.options.AddPathRX {
 		s.fsm.updateSender = newUpdateSenderAddPath(s.fsm)
 		clientOptions = s.fsm.peer.addPathSend
 	} else {
 		s.fsm.updateSender = newUpdateSender(s.fsm)
 	}
 
+	s.fsm.updateSender.Start()
 	s.fsm.adjRIBOut.Register(s.fsm.updateSender)
 	s.fsm.rib.RegisterWithOptions(s.fsm.adjRIBOut, clientOptions)
 
@@ -103,6 +104,7 @@ func (s *establishedState) uninit() {
 	s.fsm.adjRIBIn.Unregister(s.fsm.rib)
 	s.fsm.rib.Unregister(s.fsm.adjRIBOut)
 	s.fsm.adjRIBOut.Unregister(s.fsm.updateSender)
+	s.fsm.updateSender.Destroy()
 
 	s.fsm.adjRIBIn = nil
 	s.fsm.adjRIBOut = nil
diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go
index 121ea883a1c4d2de0e9384e275c394608b8665b7..928ed450b7b2a4cb230acaca5db418af80923c11 100644
--- a/protocols/bgp/server/fsm_open_sent.go
+++ b/protocols/bgp/server/fsm_open_sent.go
@@ -183,18 +183,18 @@ func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapabi
 	switch addPathCap.SendReceive {
 	case packet.AddPathReceive:
 		if !s.fsm.peer.addPathSend.BestOnly {
-			s.fsm.capAddPathSend = true
+			s.fsm.options.AddPathRX = true
 		}
 	case packet.AddPathSend:
 		if s.fsm.peer.addPathRecv {
-			s.fsm.capAddPathRecv = true
+			s.fsm.options.AddPathRX = true
 		}
 	case packet.AddPathSendReceive:
 		if !s.fsm.peer.addPathSend.BestOnly {
-			s.fsm.capAddPathSend = true
+			s.fsm.options.AddPathRX = true
 		}
 		if s.fsm.peer.addPathRecv {
-			s.fsm.capAddPathRecv = true
+			s.fsm.options.AddPathRX = true
 		}
 	}
 }
diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go
index 1fe4fdbaf05d6e483047712861874365a3af74a3..38070219394100e9006e660f75f2212b1a2b5668 100644
--- a/protocols/bgp/server/update_helper.go
+++ b/protocols/bgp/server/update_helper.go
@@ -65,11 +65,7 @@ func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) *pack
 	return current
 }
 
-type serializeAbleUpdate interface {
-	SerializeUpdate(opt *packet.Options) ([]byte, error)
-}
-
-func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *packet.Options) error {
+func serializeAndSendUpdate(out io.Writer, update *packet.BGPUpdate, opt *packet.Options) error {
 	updateBytes, err := update.SerializeUpdate(opt)
 	if err != nil {
 		log.Errorf("Unable to serialize BGP Update: %v", err)
diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go
index 23da78c532952a70bce63b8cc9570dba2d9e503e..3ac1e6e9349d5e31caf29b139088b20a235daacc 100644
--- a/protocols/bgp/server/update_sender.go
+++ b/protocols/bgp/server/update_sender.go
@@ -1,12 +1,12 @@
 package server
 
 import (
-	"fmt"
-	"strings"
+	"sync"
+	"time"
 
 	log "github.com/sirupsen/logrus"
 
-	"github.com/bio-routing/bio-rd/net"
+	bnet "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"
@@ -15,59 +15,151 @@ import (
 // UpdateSender converts table changes into BGP update messages
 type UpdateSender struct {
 	routingtable.ClientManager
-	fsm  *FSM
-	iBGP bool
+	fsm       *FSM
+	iBGP      bool
+	addPath   bool
+	toSendMu  sync.Mutex
+	toSend    map[string]*pathPfxs
+	destroyCh chan struct{}
+}
+
+type pathPfxs struct {
+	path *route.Path
+	pfxs []bnet.Prefix
+}
+
+func newUpdateSenderAddPath(fsm *FSM) *UpdateSender {
+	u := newUpdateSender(fsm)
+	u.addPath = true
+	return u
 }
 
 func newUpdateSender(fsm *FSM) *UpdateSender {
 	return &UpdateSender{
-		fsm:  fsm,
-		iBGP: fsm.peer.localASN == fsm.peer.peerASN,
+		fsm:       fsm,
+		iBGP:      fsm.peer.localASN == fsm.peer.peerASN,
+		destroyCh: make(chan struct{}),
 	}
 }
 
-// AddPath serializes a new path and sends out a BGP update message
-func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error {
-	pathAttrs, err := pathAttribues(p)
-	if err != nil {
-		log.Errorf("Unable to create BGP Update: %v", err)
+// Start starts the update sender
+func (u *UpdateSender) Start() {
+	go u.sender()
+}
+
+// Destroy destroys everything (with greetings to Hatebreed)
+func (u *UpdateSender) Destroy() {
+	u.destroyCh <- struct{}{}
+}
+
+// AddPath adds path p for pfx to toSend queue
+func (u *UpdateSender) AddPath(pfx bnet.Prefix, p *route.Path) error {
+	u.toSendMu.Lock()
+
+	hash := p.BGPPath.ComputeHash()
+	if _, exists := u.toSend[hash]; exists {
+		u.toSend[hash].pfxs = append(u.toSend[hash].pfxs, pfx)
+		u.toSendMu.Unlock()
 		return nil
 	}
 
-	update := &packet.BGPUpdate{
-		PathAttributes: pathAttrs,
-		NLRI: &packet.NLRI{
-			IP:     pfx.Addr(),
-			Pfxlen: pfx.Pfxlen(),
+	u.toSend[p.BGPPath.ComputeHash()] = &pathPfxs{
+		path: p,
+		pfxs: []bnet.Prefix{
+			pfx,
 		},
 	}
 
-	return serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
+	u.toSendMu.Unlock()
+	return nil
+}
+
+// sender serializes BGP update messages
+func (u *UpdateSender) sender() {
+	ticker := time.NewTicker(time.Millisecond * 5)
+	var err error
+	var pathAttrs *packet.PathAttribute
+	var budget int
+	var nlri *packet.NLRI
+
+	for {
+		select {
+		case <-u.destroyCh:
+			return
+		case <-ticker.C:
+		}
+
+		u.toSendMu.Lock()
+
+		for key, pathNLRIs := range u.toSend {
+			budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length())
+			pathAttrs, err = pathAttribues(pathNLRIs.path)
+			if err != nil {
+				log.Errorf("Unable to get path attributes: %v", err)
+				continue
+			}
+
+			updatesPrefixes := make([][]bnet.Prefix, 1)
+			prefixes := make([]bnet.Prefix, 1)
+			for _, pfx := range pathNLRIs.pfxs {
+				budget -= int(packet.BytesInAddr(nlri.Pfxlen)) - 5
+				if budget < 0 {
+					updatesPrefixes = append(updatesPrefixes, prefixes)
+					prefixes = make([]bnet.Prefix, 1)
+				}
+
+				prefixes = append(prefixes, pfx)
+			}
+
+			delete(u.toSend, key)
+			u.toSendMu.Unlock()
+
+			u.sendUpdates(pathAttrs, updatesPrefixes, pathNLRIs.path.BGPPath.PathIdentifier)
+			u.toSendMu.Lock()
+		}
+	}
+}
+
+func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) {
+	var nlri *packet.NLRI
+	var err error
+
+	for _, updatePrefix := range updatePrefixes {
+		update := &packet.BGPUpdate{
+			PathAttributes: pathAttrs,
+		}
+
+		for _, pfx := range updatePrefix {
+			nlri = &packet.NLRI{
+				PathIdentifier: pathID,
+				IP:             pfx.Addr(),
+				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)
+		}
+	}
 }
 
 // RemovePath withdraws prefix `pfx` from a peer
-func (u *UpdateSender) RemovePath(pfx net.Prefix, p *route.Path) bool {
-	err := withDrawPrefixes(u.fsm.con, u.fsm.options, pfx)
+func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
+	err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
 	return err == nil
 }
 
 // UpdateNewClient does nothing
 func (u *UpdateSender) UpdateNewClient(client routingtable.RouteTableClient) error {
-	log.Warningf("BGP Update Sender: UpdateNewClient() not supported")
+	log.Warningf("BGP Update Sender: UpdateNewClient not implemented")
 	return nil
 }
 
-// RouteCount does nothing
+// RouteCount returns the number of stored routes
 func (u *UpdateSender) RouteCount() int64 {
-	log.Warningf("BGP Update Sender: RouteCount() not supported")
+	log.Warningf("BGP Update Sender: RouteCount not implemented")
 	return 0
 }
-
-func asPathString(iBGP bool, localASN uint16, asPath string) string {
-	ret := ""
-	if iBGP {
-		ret = ret + fmt.Sprintf("%d ", localASN)
-	}
-	ret = ret + asPath
-	return strings.TrimRight(ret, " ")
-}
diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go
deleted file mode 100644
index 9d4820a3f3d7ed8a57ea821d3714f192053fe03e..0000000000000000000000000000000000000000
--- a/protocols/bgp/server/update_sender_add_path.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package server
-
-import (
-	"sync"
-	"time"
-
-	log "github.com/sirupsen/logrus"
-
-	bnet "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"
-)
-
-// UpdateSenderAddPath converts table changes into BGP update messages with add path
-type UpdateSenderAddPath struct {
-	routingtable.ClientManager
-	fsm      *FSM
-	iBGP     bool
-	buffer   chan *route.Route
-	toSendMu sync.Mutex
-	toSend   map[string]struct {
-		path *route.Path
-		pfxs []bnet.Prefix
-	}
-}
-
-func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath {
-	return &UpdateSenderAddPath{
-		fsm:    fsm,
-		iBGP:   fsm.peer.localASN == fsm.peer.peerASN,
-		buffer: make(chan *route.Route, 1000),
-	}
-}
-
-func (u *UpdateSenderAddPath) AddPath(pfx bnet.Prefix, p *route.Path) error {
-	u.buffer <- route.NewRoute(pfx, p)
-	return nil
-}
-
-// sender serializes BGP update messages
-func (u *UpdateSenderAddPath) sender() {
-	ticker := time.NewTicker(time.Millisecond * 5)
-	var r *route.Route
-	var p *route.Path
-	var pfx bnet.Prefix
-	var err error
-	var pathAttrs *packet.PathAttribute
-	var update *packet.BGPUpdateAddPath
-	var lastNLRI *packet.NLRIAddPath
-	var budget int64
-
-	for {
-		<-ticker.C
-		u.toSendMu.Lock()
-
-		for _, pathNLRIs := range u.toSend {
-			budget = packet.MaxLen
-
-			pathAttrs, err = pathAttribues(pathNLRIs.path)
-			if err != nil {
-				log.Errorf("Unable to get path attributes: %v", err)
-				continue
-			}
-
-			update = &packet.BGPUpdateAddPath{
-				PathAttributes: pathAttrs,
-				/*NLRI: &packet.NLRIAddPath{
-					PathIdentifier: p.BGPPath.PathIdentifier,
-					IP:             pfx.Addr(),
-					Pfxlen:         pfx.Pfxlen(),
-				},*/
-			}
-
-			for _, pfx := range pathNLRIs.pfxs {
-				nlri = &packet.NLRIAddPath{
-					PathIdentifier: pathNLRIs.path.BGPPath.PathIdentifier,
-					IP:             pfx.Addr(),
-					Pfxlen:         pfx.Pfxlen(),
-				}
-			}
-		}
-
-		u.toSendMu.Unlock()
-
-		p = r.Paths()[0]
-		pfx = r.Prefix()
-		pathAttrs, err = pathAttribues(p)
-
-		if err != nil {
-			log.Errorf("Unable to create BGP Update: %v", err)
-			continue
-		}
-		update = &packet.BGPUpdateAddPath{
-			PathAttributes: pathAttrs,
-			NLRI: &packet.NLRIAddPath{
-				PathIdentifier: p.BGPPath.PathIdentifier,
-				IP:             pfx.Addr(),
-				Pfxlen:         pfx.Pfxlen(),
-			},
-		}
-
-		err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
-		if err != nil {
-			log.Errorf("Failed to serialize and send: %v", err)
-		}
-	}
-
-}
-
-// RemovePath withdraws prefix `pfx` from a peer
-func (u *UpdateSenderAddPath) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
-	err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
-	return err == nil
-}
-
-// UpdateNewClient does nothing
-func (u *UpdateSenderAddPath) UpdateNewClient(client routingtable.RouteTableClient) error {
-	log.Warningf("BGP Update Sender: UpdateNewClient not implemented")
-	return nil
-}
-
-// RouteCount returns the number of stored routes
-func (u *UpdateSenderAddPath) RouteCount() int64 {
-	log.Warningf("BGP Update Sender: RouteCount not implemented")
-	return 0
-}
diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go
index 089f9f2c0f7a0e25d1c5d1f2e9f292cdeec74a3c..a66fade667558a45b4a2f92d4a569516c57c7dd1 100644
--- a/protocols/bgp/server/withdraw.go
+++ b/protocols/bgp/server/withdraw.go
@@ -48,8 +48,8 @@ func withDrawPrefixesAddPath(out io.Writer, opt *packet.Options, pfx net.Prefix,
 	if p.BGPPath == nil {
 		return errors.New("got nil BGPPath")
 	}
-	update := &packet.BGPUpdateAddPath{
-		WithdrawnRoutes: &packet.NLRIAddPath{
+	update := &packet.BGPUpdate{
+		WithdrawnRoutes: &packet.NLRI{
 			PathIdentifier: p.BGPPath.PathIdentifier,
 			IP:             pfx.Addr(),
 			Pfxlen:         pfx.Pfxlen(),
diff --git a/route/bgp_path.go b/route/bgp_path.go
index 175d7bf4c8d45d1a24edc7d6bb0e98b0aafc16d8..31837161b2a5a0ba91cb9b302b9a3b8ef3c975a9 100644
--- a/route/bgp_path.go
+++ b/route/bgp_path.go
@@ -26,15 +26,27 @@ type BGPPath struct {
 	UnknownAttributes *packet.PathAttribute
 }
 
-// Legth get's the length of serialized path
-func (b *BGPPath) Legth() uint16 {
+// Length get's the length of serialized path
+func (b *BGPPath) Length() uint16 {
 	asPathLen := uint16(3)
 	for _, segment := range b.ASPath {
 		asPathLen++
 		asPathLen += uint16(4 * len(segment.ASNs))
 	}
 
-	return uint16(len(b.Communities))*7 + uint16(len(b.LargeCommunities))*15 + 4*7 + 4 + asPathLen
+	return uint16(len(b.Communities))*7 + uint16(len(b.LargeCommunities))*15 + 4*7 + 4 + asPathLen + b.unknownAttributesLength()
+}
+
+// unknownAttributesLength calculates the length of unknown attributes
+func (b *BGPPath) unknownAttributesLength() (length uint16) {
+	for a := b.UnknownAttributes; a != nil; a = a.Next {
+		length += a.Length + 3
+		if a.ExtendedLength {
+			length++
+		}
+	}
+
+	return length
 }
 
 // ECMP determines if routes b and c are euqal in terms of ECMP