Skip to content

Commit

Permalink
bgpd: Implement neighbor X remote-as auto
Browse files Browse the repository at this point in the history
In some cases (large scale) it's desired to avoid changing configurations, but
let the BGP to automatically handle ASN changes.

`auto` means the peering can be iBGP or eBGP. It will be automatically detected
and adjusted from the OPEN message.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
  • Loading branch information
ton31337 committed Jul 4, 2024
1 parent d4758b3 commit 0dfe256
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 36 deletions.
2 changes: 2 additions & 0 deletions bgpd/bgp_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -4468,6 +4468,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
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);
Expand Down
8 changes: 8 additions & 0 deletions bgpd/bgp_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1977,6 +1977,14 @@ static int bgp_open_receive(struct peer_connection *connection,
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))
Expand Down
7 changes: 6 additions & 1 deletion bgpd/bgp_updgrp.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,12 @@ static unsigned int updgrp_hash_key_make(const void *p)

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);
Expand Down
62 changes: 43 additions & 19 deletions bgpd/bgp_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -4871,6 +4871,9 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str,
} 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;

Expand Down Expand Up @@ -4976,13 +4979,14 @@ ALIAS(no_bgp_shutdown, no_bgp_shutdown_msg_cmd,

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;
Expand Down Expand Up @@ -5054,6 +5058,8 @@ static int peer_conf_interface_get(struct vty *vty, const char *conf_if,
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))
Expand Down Expand Up @@ -5170,14 +5176,15 @@ DEFUN (neighbor_interface_config_v6only,

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;
Expand All @@ -5187,15 +5194,16 @@ DEFUN (neighbor_interface_config_remote_as,

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"
"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 = 5;
Expand Down Expand Up @@ -5232,14 +5240,15 @@ DEFUN (neighbor_peer_group,

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;
Expand Down Expand Up @@ -5310,7 +5319,7 @@ DEFUN (no_neighbor,

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"
Expand All @@ -5321,7 +5330,8 @@ DEFUN (no_neighbor_interface_config,
"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;
Expand Down Expand Up @@ -5378,14 +5388,15 @@ DEFUN (no_neighbor_peer_group,

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;
Expand Down Expand Up @@ -11887,7 +11898,7 @@ static bool bgp_show_summary_is_peer_filtered(struct peer *peer,
/* 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)
Expand Down Expand Up @@ -12879,6 +12890,8 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
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",
Expand Down Expand Up @@ -14002,9 +14015,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
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, ", ");
Expand All @@ -14023,7 +14037,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
: "");
}
/* 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(
Expand Down Expand Up @@ -17011,7 +17025,7 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group,
&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);
Expand All @@ -17023,7 +17037,8 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group,
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");
Expand Down Expand Up @@ -18525,6 +18540,9 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
} 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");
Expand All @@ -18547,6 +18565,9 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
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);
}
}

Expand Down Expand Up @@ -18576,6 +18597,9 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
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);
}
}
}
Expand Down
25 changes: 14 additions & 11 deletions bgpd/bgpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1074,10 +1074,10 @@ static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer)

/* 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) {
Expand Down Expand Up @@ -1132,17 +1132,20 @@ static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer)
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
Expand Down Expand Up @@ -2201,10 +2204,10 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if,
}
} 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;
}
Expand Down
10 changes: 6 additions & 4 deletions bgpd/bgpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ struct bgp_pbr_config;
* 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 */
Expand Down
6 changes: 5 additions & 1 deletion doc/user/bgp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,10 @@ Defining Peers
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.
Expand Down Expand Up @@ -1699,7 +1703,7 @@ Configuring Peers
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
Expand Down
Empty file.
17 changes: 17 additions & 0 deletions tests/topotests/bgp_remote_as_auto/r1/frr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
!
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
!
10 changes: 10 additions & 0 deletions tests/topotests/bgp_remote_as_auto/r2/frr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!
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
!
10 changes: 10 additions & 0 deletions tests/topotests/bgp_remote_as_auto/r3/frr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!
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
!
Loading

0 comments on commit 0dfe256

Please sign in to comment.