From 38fd9448c9f3582924fdc5414bb01074dad35656 Mon Sep 17 00:00:00 2001 From: Maxence Younsi Date: Fri, 12 Apr 2024 18:39:48 +0200 Subject: [PATCH] add source-interface parameter to peer --- src/exabgp/bgp/neighbor.py | 3 +++ src/exabgp/configuration/neighbor/__init__.py | 3 +++ src/exabgp/configuration/neighbor/parser.py | 8 +++++++- src/exabgp/reactor/network/outgoing.py | 5 +++-- src/exabgp/reactor/network/tcp.py | 11 ++++++++++- src/exabgp/reactor/protocol.py | 3 ++- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/exabgp/bgp/neighbor.py b/src/exabgp/bgp/neighbor.py index 31608871e..b2eafe6b3 100644 --- a/src/exabgp/bgp/neighbor.py +++ b/src/exabgp/bgp/neighbor.py @@ -66,6 +66,7 @@ class Capability(dict): 'description': '', 'router-id': None, 'local-address': None, + 'source-interface': None, 'peer-address': None, 'local-as': None, 'peer-as': None, @@ -397,6 +398,7 @@ def string(self, with_changes=True): '\thost-name %s;\n' '\tdomain-name %s;\n' '\tlocal-address %s;\n' + '\tsource-interface %s;\n' '\tlocal-as %s;\n' '\tpeer-as %s;\n' '\thold-time %s;\n' @@ -421,6 +423,7 @@ def string(self, with_changes=True): self['host-name'], self['domain-name'], self['local-address'] if not self.auto_discovery else 'auto', + self['source-interface'], self['local-as'], self['peer-as'], self['hold-time'], diff --git a/src/exabgp/configuration/neighbor/__init__.py b/src/exabgp/configuration/neighbor/__init__.py index 2ab352687..1329111b9 100644 --- a/src/exabgp/configuration/neighbor/__init__.py +++ b/src/exabgp/configuration/neighbor/__init__.py @@ -38,6 +38,7 @@ from exabgp.configuration.neighbor.parser import hold_time from exabgp.configuration.neighbor.parser import router_id from exabgp.configuration.neighbor.parser import local_address +from exabgp.configuration.neighbor.parser import source_interface from exabgp.configuration.neighbor.parser import hostname from exabgp.configuration.neighbor.parser import domainname from exabgp.configuration.neighbor.parser import description @@ -63,6 +64,7 @@ class ParseNeighbor(Section): 'hold-time': hold_time, 'rate-limit': rate_limit, 'local-address': local_address, + 'source-interface': source_interface, 'peer-address': peer_ip, 'local-as': auto_asn, 'peer-as': auto_asn, @@ -90,6 +92,7 @@ class ParseNeighbor(Section): 'hold-time': 'set-command', 'rate-limit': 'set-command', 'local-address': 'set-command', + 'source-interface': 'set-command', 'peer-address': 'set-command', 'local-as': 'set-command', 'peer-as': 'set-command', diff --git a/src/exabgp/configuration/neighbor/parser.py b/src/exabgp/configuration/neighbor/parser.py index 18e44462a..e63f7cbcb 100644 --- a/src/exabgp/configuration/neighbor/parser.py +++ b/src/exabgp/configuration/neighbor/parser.py @@ -101,6 +101,12 @@ def local_address(tokeniser): except (IndexError, ValueError, socket.error): raise ValueError('"%s" is an invalid IP address' % value) +def source_interface(tokeniser): + try: + return string(tokeniser) + except Exception: + raise ValueError('bad source interface') + def router_id(tokeniser): value = tokeniser() @@ -175,4 +181,4 @@ def rate_limit(tokeniser): raise ValueError('"%s" is an invalid rate-limit' % value) if rate <= 0: raise ValueError('rate must be zero or at 1 (per second)') - return rate + return rate \ No newline at end of file diff --git a/src/exabgp/reactor/network/outgoing.py b/src/exabgp/reactor/network/outgoing.py index 894ad6880..b1873f43d 100644 --- a/src/exabgp/reactor/network/outgoing.py +++ b/src/exabgp/reactor/network/outgoing.py @@ -19,7 +19,7 @@ class Outgoing(Connection): direction = 'outgoing' - def __init__(self, afi, peer, local, port=179, md5='', md5_base64=False, ttl=None): + def __init__(self, afi, peer, local, port=179, md5='', md5_base64=False, ttl=None, itf=None): Connection.__init__(self, afi, peer, local) self.ttl = ttl @@ -27,10 +27,11 @@ def __init__(self, afi, peer, local, port=179, md5='', md5_base64=False, ttl=Non self.md5 = md5 self.md5_base64 = md5_base64 self.port = port + self.interface = itf def _setup(self): try: - self.io = create(self.afi) + self.io = create(self.afi, self.interface) MD5(self.io, self.peer, self.port, self.md5, self.md5_base64) if self.afi == AFI.ipv4: TTL(self.io, self.peer, self.ttl) diff --git a/src/exabgp/reactor/network/tcp.py b/src/exabgp/reactor/network/tcp.py index ace5e50bc..22d991f90 100644 --- a/src/exabgp/reactor/network/tcp.py +++ b/src/exabgp/reactor/network/tcp.py @@ -32,7 +32,7 @@ from exabgp.logger import log -def create(afi): +def create(afi, interface=None): try: if afi == AFI.ipv4: io = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) @@ -46,6 +46,15 @@ def create(afi): io.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) # pylint: disable=E1101 except (socket.error, AttributeError): pass + + if interface is not None: + try: + if not hasattr(socket,'SO_BINDTODEVICE') : + socket.SO_BINDTODEVICE = 25 + + io.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, str(interface + '\0').encode("utf-8")) + except socket.error: + raise NotConnected(f'Could not bind to device {interface}') except socket.error: raise NotConnected('Could not create socket') return io diff --git a/src/exabgp/reactor/protocol.py b/src/exabgp/reactor/protocol.py index 73a623e4c..bc82b9805 100644 --- a/src/exabgp/reactor/protocol.py +++ b/src/exabgp/reactor/protocol.py @@ -103,7 +103,8 @@ def connect(self): md5 = self.neighbor['md5-password'] md5_base64 = self.neighbor['md5-base64'] ttl_out = self.neighbor['outgoing-ttl'] - self.connection = Outgoing(afi, peer, local, self.port, md5, md5_base64, ttl_out) + itf = self.neighbor['source-interface'] + self.connection = Outgoing(afi, peer, local, self.port, md5, md5_base64, ttl_out, itf) for connected in self.connection.establish(): yield False