diff --git a/etc/exabgp/api-nexthop-self.conf b/etc/exabgp/api-nexthop-self.conf index 268c31001..34896015c 100644 --- a/etc/exabgp/api-nexthop-self.conf +++ b/etc/exabgp/api-nexthop-self.conf @@ -18,9 +18,13 @@ neighbor 127.0.0.1 { processes [ announce-routes ]; } static { - route 10.0.0.1 next-hop self; route 10.0.0.2 { next-hop self; } } + announce { + ipv4 { + unicast 10.0.0.1 next-hop self; + } + } } diff --git a/lib/exabgp/bgp/message/open/capability/negotiated.py b/lib/exabgp/bgp/message/open/capability/negotiated.py index 8d134f427..9d2a3add5 100644 --- a/lib/exabgp/bgp/message/open/capability/negotiated.py +++ b/lib/exabgp/bgp/message/open/capability/negotiated.py @@ -172,17 +172,7 @@ def validate(self, neighbor): return None def nexthopself(self, afi): - if afi == self.neighbor.local_address.afi: - return self.neighbor.local_address - - # attempting to not barf for next-hop self when the peer is IPv6 - if afi == AFI.ipv4: - return self.neighbor.router_id - - raise TypeError( - 'use of "next-hop self": the route (%s) does not have the same family as the BGP tcp session (%s)' - % (afi, self.neighbor.local_address.afi) - ) + return self.neighbor.ip_self(afi) # =================================================================== RequirePath diff --git a/lib/exabgp/bgp/neighbor.py b/lib/exabgp/bgp/neighbor.py index c4868f281..29a32643c 100644 --- a/lib/exabgp/bgp/neighbor.py +++ b/lib/exabgp/bgp/neighbor.py @@ -21,8 +21,8 @@ from exabgp.protocol.family import AFI from exabgp.bgp.message import Message -from exabgp.bgp.message.open.capability import NextHop -from exabgp.bgp.message.open.capability import AddPath +from exabgp.bgp.message.update.attribute import NextHop +from exabgp.bgp.message.update.attribute import Attribute from exabgp.rib import RIB @@ -405,5 +405,28 @@ def string(self, with_changes=True): # '\t\tsend {\n%s\t\t}\n' % send if send else '', return returned.replace('\t', ' ') + def ip_self(self, afi): + if afi == self.local_address.afi: + return self.local_address + + # attempting to not barf for next-hop self when the peer is IPv6 + if afi == AFI.ipv4: + return self.router_id + + raise TypeError( + 'use of "next-hop self": the route (%s) does not have the same family as the BGP tcp session (%s)' + % (afi, self.local_address.afi) + ) + + # NOTE: this may very well modify the change object passed to the function + def remove_self(self, change): + if not change.nlri.nexthop.SELF: + return change + neighbor_self = self.ip_self(change.nlri.afi) + change.nlri.nexthop = neighbor_self + if Attribute.CODE.NEXT_HOP in change.attributes: + change.attributes[Attribute.CODE.NEXT_HOP] = NextHop(str(neighbor_self),neighbor_self.pack()) + return change + def __str__(self): return self.string(False) diff --git a/lib/exabgp/configuration/configuration.py b/lib/exabgp/configuration/configuration.py index 4bf36cdae..ee4ae8c0c 100644 --- a/lib/exabgp/configuration/configuration.py +++ b/lib/exabgp/configuration/configuration.py @@ -68,10 +68,11 @@ def __init__(self): def inject_change(self, peers, change): result = True - for neighbor in self.neighbors: - if neighbor in peers: - if change.nlri.family() in self.neighbors[neighbor].families(): - self.neighbors[neighbor].rib.outgoing.add_to_rib(change) + for neighbor_name in self.neighbors: + if neighbor_name in peers: + neighbor = self.neighbors[neighbor_name] + if change.nlri.family() in neighbor.families(): + neighbor.rib.outgoing.add_to_rib(neighbor.remove_self(change)) else: self.logger.error('the route family is not configured on neighbor', 'configuration') result = False diff --git a/lib/exabgp/configuration/neighbor/__init__.py b/lib/exabgp/configuration/neighbor/__init__.py index d96c415f1..071fc7fc7 100644 --- a/lib/exabgp/configuration/neighbor/__init__.py +++ b/lib/exabgp/configuration/neighbor/__init__.py @@ -246,19 +246,23 @@ def post(self): neighbor.add_nexthop(afi, safi, nhafi) neighbor.changes = [] - neighbor.changes.extend(self.scope.pop_routes()) + for change in self.scope.pop_routes(): + # remove_self may well have side effects on change + neighbor.changes.append(neighbor.remove_self(change)) # old format for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE - neighbor.changes.extend(routes) + # remove_self may well have side effects on change + neighbor.changes.append(neighbor.remove_self(route)) routes = local.get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE - neighbor.changes.extend(routes) + # remove_self may well have side effects on change + neighbor.changes.append(neighbor.remove_self(route)) messages = local.get('operational', {}).get('routes', []) diff --git a/lib/exabgp/protocol/ip/__init__.py b/lib/exabgp/protocol/ip/__init__.py index b422ce14c..02a1caf5d 100644 --- a/lib/exabgp/protocol/ip/__init__.py +++ b/lib/exabgp/protocol/ip/__init__.py @@ -25,6 +25,8 @@ class IPSelf(object): + SELF = True + def __init__(self, afi): self.afi = afi @@ -45,6 +47,8 @@ def index(self): class IP(object): + SELF = False + afi = None # here for the API, changed in init which does not change this _known = dict() @@ -214,6 +218,8 @@ def __repr__(self): class _NoNextHop(object): + SELF = False + packed = '' def pack(self, data, negotiated=None):