bgp_packet_mpattr_end(s, mpattrlen_pos);
}
+ (void)peer_sort(peer);
+
/* Origin attribute. */
stream_putc(s, BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_ORIGIN);
BGP_NOTIFY_OPEN_BAD_PEER_AS,
notify_data_remote_as, 2);
return BGP_Stop;
+ } else if (peer->as_type == AS_AUTO) {
+ if (remote_as == peer->bgp->as) {
+ peer->as = peer->local_as;
+ SET_FLAG(peer->as_type, AS_INTERNAL);
+ } else {
+ peer->as = remote_as;
+ SET_FLAG(peer->as_type, AS_EXTERNAL);
+ }
} else if (peer->as_type == AS_INTERNAL) {
if (remote_as != peer->bgp->as) {
if (bgp_debug_neighbor_events(peer))
key = 0;
- key = jhash_1word(peer->sort, key); /* EBGP or IBGP */
+ /* `remote-as auto` technically uses identical peer->sort.
+ * After OPEN message is parsed, this is updated accordingly, but
+ * we need to call the peer_sort() here also to properly create
+ * separate subgroups.
+ */
+ key = jhash_1word(peer_sort((struct peer *)peer), key);
key = jhash_1word(peer->sub_sort, key); /* OAD */
key = jhash_1word((peer->flags & PEER_UPDGRP_FLAGS), key);
key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key);
} else if (as_str[0] == 'e') {
as = 0;
as_type = AS_EXTERNAL;
+ } else if (as_str[0] == 'a') {
+ as = 0;
+ as_type = AS_AUTO;
} else if (!asn_str2asn(as_str, &as))
as_type = AS_UNSPECIFIED;
DEFUN (neighbor_remote_as,
neighbor_remote_as_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <ASNUM|internal|external>",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <ASNUM|internal|external|auto>",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a BGP neighbor\n"
AS_STR
"Internal BGP peer\n"
- "External BGP peer\n")
+ "External BGP peer\n"
+ "Automatically detect remote ASN\n")
{
int idx_peer = 1;
int idx_remote_as = 3;
as_type = AS_INTERNAL;
} else if (as_str[0] == 'e') {
as_type = AS_EXTERNAL;
+ } else if (as_str[0] == 'a') {
+ as_type = AS_AUTO;
} else {
/* Get AS number. */
if (asn_str2asn(as_str, &as))
DEFUN (neighbor_interface_config_remote_as,
neighbor_interface_config_remote_as_cmd,
- "neighbor WORD interface remote-as <ASNUM|internal|external>",
+ "neighbor WORD interface remote-as <ASNUM|internal|external|auto>",
NEIGHBOR_STR
"Interface name or neighbor tag\n"
"Enable BGP on interface\n"
"Specify a BGP neighbor\n"
AS_STR
"Internal BGP peer\n"
- "External BGP peer\n")
+ "External BGP peer\n"
+ "Automatically detect remote ASN\n")
{
int idx_word = 1;
int idx_remote_as = 4;
DEFUN (neighbor_interface_v6only_config_remote_as,
neighbor_interface_v6only_config_remote_as_cmd,
- "neighbor WORD interface v6only remote-as <ASNUM|internal|external>",
+ "neighbor WORD interface v6only remote-as <ASNUM|internal|external|auto>",
NEIGHBOR_STR
"Interface name or neighbor tag\n"
"Enable BGP with v6 link-local only\n"
"Specify a BGP neighbor\n"
AS_STR
"Internal BGP peer\n"
- "External BGP peer\n")
+ "External BGP peer\n"
+ "Automatically detect remote ASN\n")
{
int idx_word = 1;
int idx_remote_as = 5;
DEFUN (no_neighbor,
no_neighbor_cmd,
- "no neighbor <WORD|<A.B.C.D|X:X::X:X> [remote-as <(1-4294967295)|internal|external>]>",
+ "no neighbor <WORD|<A.B.C.D|X:X::X:X> [remote-as <(1-4294967295)|internal|external|auto>]>",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a BGP neighbor\n"
AS_STR
"Internal BGP peer\n"
- "External BGP peer\n")
+ "External BGP peer\n"
+ "Automatically detect remote ASN\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx_peer = 2;
DEFUN (no_neighbor_interface_config,
no_neighbor_interface_config_cmd,
- "no neighbor WORD interface [v6only] [peer-group PGNAME] [remote-as <(1-4294967295)|internal|external>]",
+ "no neighbor WORD interface [v6only] [peer-group PGNAME] [remote-as <(1-4294967295)|internal|external|auto>]",
NO_STR
NEIGHBOR_STR
"Interface name\n"
"Specify a BGP neighbor\n"
AS_STR
"Internal BGP peer\n"
- "External BGP peer\n")
+ "External BGP peer\n"
+ "Automatically detect remote ASN\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx_word = 2;
DEFUN (no_neighbor_interface_peer_group_remote_as,
no_neighbor_interface_peer_group_remote_as_cmd,
- "no neighbor WORD remote-as <ASNUM|internal|external>",
+ "no neighbor WORD remote-as <ASNUM|internal|external|auto>",
NO_STR
NEIGHBOR_STR
"Interface name or neighbor tag\n"
"Specify a BGP neighbor\n"
AS_STR
"Internal BGP peer\n"
- "External BGP peer\n")
+ "External BGP peer\n"
+ "Automatically detect remote ASN\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx_word = 2;
/* filter remote-as (internal|external) */
if (as_type != AS_UNSPECIFIED) {
if (peer->as_type == AS_SPECIFIED) {
- if (as_type == AS_INTERNAL) {
+ if (CHECK_FLAG(as_type, AS_INTERNAL)) {
if (peer->as != peer->local_as)
return true;
} else if (peer->as == peer->local_as)
as_type = AS_INTERNAL;
else if (argv[idx + 1]->arg[0] == 'e')
as_type = AS_EXTERNAL;
+ else if (argv[idx + 1]->arg[0] == 'a')
+ as_type = AS_AUTO;
else if (!asn_str2asn(argv[idx + 1]->arg, &as)) {
vty_out(vty,
"%% Invalid neighbor remote-as value: %s\n",
json_object_boolean_true_add(json_neigh,
"localAsReplaceAs");
} else {
- if ((p->as_type == AS_SPECIFIED) ||
- (p->as_type == AS_EXTERNAL) ||
- (p->as_type == AS_INTERNAL)) {
+ if (p->as_type == AS_SPECIFIED ||
+ CHECK_FLAG(p->as_type, AS_AUTO) ||
+ CHECK_FLAG(p->as_type, AS_EXTERNAL) ||
+ CHECK_FLAG(p->as_type, AS_INTERNAL)) {
vty_out(vty, "remote AS ");
vty_out(vty, ASN_FORMAT(bgp->asnotation), &p->as);
vty_out(vty, ", ");
: "");
}
/* peer type internal or confed-internal */
- if ((p->as == p->local_as) || (p->as_type == AS_INTERNAL)) {
+ if ((p->as == p->local_as) || (CHECK_FLAG(p->as_type, AS_INTERNAL))) {
if (use_json) {
if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
json_object_boolean_true_add(
&conf->as);
vty_out(vty, "\n");
}
- } else if (conf->as_type == AS_INTERNAL) {
+ } else if (CHECK_FLAG(conf->as_type, AS_INTERNAL)) {
if (json)
asn_asn2json(json, "remoteAs", group->bgp->as,
group->bgp->asnotation);
vty_out(vty, "\nBGP peer-group %s\n", group->name);
}
- if ((group->bgp->as == conf->as) || (conf->as_type == AS_INTERNAL)) {
+ if ((group->bgp->as == conf->as) ||
+ CHECK_FLAG(conf->as_type, AS_INTERNAL)) {
if (json)
json_object_string_add(json_peer_group, "type",
"internal");
} else if (peer->as_type == AS_EXTERNAL) {
vty_out(vty, " remote-as external");
if_ras_printed = true;
+ } else if (CHECK_FLAG(peer->as_type, AS_AUTO)) {
+ vty_out(vty, " remote-as auto");
+ if_ras_printed = true;
}
vty_out(vty, "\n");
vty_out(vty,
" neighbor %s remote-as external\n",
addr);
+ } else if (CHECK_FLAG(peer->as_type, AS_AUTO)) {
+ vty_out(vty, " neighbor %s remote-as auto\n",
+ addr);
}
}
vty_out(vty,
" neighbor %s remote-as external\n",
addr);
+ } else if (CHECK_FLAG(peer->as_type, AS_AUTO)) {
+ vty_out(vty, " neighbor %s remote-as auto\n",
+ addr);
}
}
}
/* Peer-group */
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
- if (peer->as_type == AS_INTERNAL)
+ if (CHECK_FLAG(peer->as_type, AS_INTERNAL))
return BGP_PEER_IBGP;
- else if (peer->as_type == AS_EXTERNAL)
+ if (CHECK_FLAG(peer->as_type, AS_EXTERNAL))
return BGP_PEER_EBGP;
else if (peer->as_type == AS_SPECIFIED && peer->as) {
return BGP_PEER_IBGP;
else
return BGP_PEER_EBGP;
- } else if (peer->group->conf->as_type
- == AS_INTERNAL)
+ } else if (CHECK_FLAG(peer->group->conf->as_type,
+ AS_INTERNAL))
return BGP_PEER_IBGP;
else
return BGP_PEER_EBGP;
}
/* no AS information anywhere, let caller know */
return BGP_PEER_UNSPECIFIED;
- } else if (peer->as_type != AS_SPECIFIED)
- return (peer->as_type == AS_INTERNAL ? BGP_PEER_IBGP
- : BGP_PEER_EBGP);
+ } else if (peer->as_type != AS_SPECIFIED) {
+ if (CHECK_FLAG(peer->as_type, AS_INTERNAL))
+ return BGP_PEER_IBGP;
+ else if (CHECK_FLAG(peer->as_type, AS_EXTERNAL))
+ return BGP_PEER_EBGP;
+ }
return (local_as == 0 ? BGP_PEER_INTERNAL
: local_as == peer->as ? BGP_PEER_IBGP
}
} else {
/* internal/external used, compare as-types */
- if (((peer_sort_type == BGP_PEER_IBGP)
- && (as_type != AS_INTERNAL))
- || ((peer_sort_type == BGP_PEER_EBGP)
- && (as_type != AS_EXTERNAL))) {
+ if (((peer_sort_type == BGP_PEER_IBGP) &&
+ !CHECK_FLAG(as_type, AS_INTERNAL)) ||
+ ((peer_sort_type == BGP_PEER_EBGP) &&
+ !CHECK_FLAG(as_type, AS_EXTERNAL))) {
*as = peer->as;
return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
}
* behavior
* in the system.
*/
-enum { AS_UNSPECIFIED = 0,
- AS_SPECIFIED,
- AS_INTERNAL,
- AS_EXTERNAL,
+enum peer_asn_type {
+ AS_UNSPECIFIED = 1,
+ AS_SPECIFIED = 2,
+ AS_INTERNAL = 4,
+ AS_EXTERNAL = 8,
+ AS_AUTO = 16,
};
/* Zebra Gracaful Restart states */
peers ASN is the same as mine as specified under the :clicmd:`router bgp ASN`
command the connection will be denied.
+.. clicmd:: neighbor PEER remote-as auto
+
+ The neighbor's ASN is detected automatically from the OPEN message.
+
.. clicmd:: neighbor PEER oad
Mark a peer belonging to the One Administrative Domain.
IPv4 session addresses, see the ``neighbor PEER update-source`` command
below.
-.. clicmd:: neighbor PEER interface remote-as <internal|external|ASN>
+.. clicmd:: neighbor PEER interface remote-as <internal|external|auto|ASN>
Configure an unnumbered BGP peer. ``PEER`` should be an interface name. The
session will be established via IPv6 link locals. Use ``internal`` for iBGP
--- /dev/null
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as auto
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ neighbor 192.168.1.3 remote-as auto
+ neighbor 192.168.1.3 timers 1 3
+ neighbor 192.168.1.3 timers connect 1
+ address-family ipv4 unicast
+ network 10.0.0.1/32
+ exit-address-family
+!
--- /dev/null
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as auto
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+!
--- /dev/null
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as auto
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+!
--- /dev/null
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2024 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+import os
+import re
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2", "r3")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_remote_as_auto():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ def _bgp_converge():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast summary json"))
+ expected = {
+ "peers": {
+ "192.168.1.2": {
+ "hostname": "r2",
+ "remoteAs": 65001,
+ "localAs": 65001,
+ "state": "Established",
+ },
+ "192.168.1.3": {
+ "hostname": "r3",
+ "remoteAs": 65003,
+ "localAs": 65001,
+ "state": "Established",
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't see automatic iBGP/eBGP peerings"
+
+ def _bgp_converge_internal():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 10.0.0.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {
+ "string": "Local",
+ },
+ "valid": True,
+ "peer": {
+ "hostname": "r1",
+ "type": "internal",
+ },
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge_internal,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't see automatic iBGP peering"
+
+ def _bgp_converge_external():
+ output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast 10.0.0.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {
+ "string": "65001",
+ },
+ "valid": True,
+ "peer": {
+ "hostname": "r1",
+ "type": "external",
+ },
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge_external,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't see automatic eBGP peering"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))