]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: Handle ORF capability using dynamic capabilities
authorDonatas Abraitis <donatas@opensourcerouting.org>
Tue, 17 Oct 2023 13:01:33 +0000 (16:01 +0300)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Wed, 18 Oct 2023 13:56:02 +0000 (16:56 +0300)
Add an ability to enable/disable ORF capability dynamically without tearing
down the session.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
bgpd/bgp_packet.c
bgpd/bgp_vty.c
bgpd/bgpd.c

index fe39488d567c12f0af78babbdadbeb0c6a071f86..b585591e2f69610bbfafd9c61608f15d302702a8 100644 (file)
@@ -1214,6 +1214,8 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
        uint32_t gr_restart_time;
        uint8_t addpath_afi_safi_count = 0;
        bool adv_addpath_tx = false;
+       unsigned long number_of_orfs_p;
+       uint8_t number_of_orfs = 0;
        const char *capability = lookup_msg(capcode_str, capability_code,
                                            "Unknown");
 
@@ -1458,8 +1460,78 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
                                   iana_safi2str(pkt_safi));
 
                break;
-       case CAPABILITY_CODE_REFRESH:
        case CAPABILITY_CODE_ORF:
+               /* Convert AFI, SAFI to values for packet. */
+               bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+               stream_putc(s, action);
+               stream_putc(s, CAPABILITY_CODE_ORF);
+               cap_len = stream_get_endp(s);
+               stream_putc(s, 0);
+
+               stream_putw(s, pkt_afi); /* Address Family Identifier */
+               stream_putc(s, 0);       /* Reserved */
+               stream_putc(s,
+                           pkt_safi); /* Subsequent Address Family Identifier */
+
+               number_of_orfs_p =
+                       stream_get_endp(s); /* Number of ORFs pointer */
+               stream_putc(s, 0);          /* Number of ORFs */
+
+               /* Address Prefix ORF */
+               if (CHECK_FLAG(peer->af_flags[afi][safi],
+                              PEER_FLAG_ORF_PREFIX_SM) ||
+                   CHECK_FLAG(peer->af_flags[afi][safi],
+                              PEER_FLAG_ORF_PREFIX_RM)) {
+                       stream_putc(s, ORF_TYPE_PREFIX);
+
+                       if (CHECK_FLAG(peer->af_flags[afi][safi],
+                                      PEER_FLAG_ORF_PREFIX_SM) &&
+                           CHECK_FLAG(peer->af_flags[afi][safi],
+                                      PEER_FLAG_ORF_PREFIX_RM)) {
+                               SET_FLAG(peer->af_cap[afi][safi],
+                                        PEER_CAP_ORF_PREFIX_SM_ADV);
+                               SET_FLAG(peer->af_cap[afi][safi],
+                                        PEER_CAP_ORF_PREFIX_RM_ADV);
+                               stream_putc(s, ORF_MODE_BOTH);
+                       } else if (CHECK_FLAG(peer->af_flags[afi][safi],
+                                             PEER_FLAG_ORF_PREFIX_SM)) {
+                               SET_FLAG(peer->af_cap[afi][safi],
+                                        PEER_CAP_ORF_PREFIX_SM_ADV);
+                               UNSET_FLAG(peer->af_cap[afi][safi],
+                                          PEER_CAP_ORF_PREFIX_RM_ADV);
+                               stream_putc(s, ORF_MODE_SEND);
+                       } else {
+                               SET_FLAG(peer->af_cap[afi][safi],
+                                        PEER_CAP_ORF_PREFIX_RM_ADV);
+                               UNSET_FLAG(peer->af_cap[afi][safi],
+                                          PEER_CAP_ORF_PREFIX_SM_ADV);
+                               stream_putc(s, ORF_MODE_RECEIVE);
+                       }
+                       number_of_orfs++;
+               } else {
+                       UNSET_FLAG(peer->af_cap[afi][safi],
+                                  PEER_CAP_ORF_PREFIX_SM_ADV);
+                       UNSET_FLAG(peer->af_cap[afi][safi],
+                                  PEER_CAP_ORF_PREFIX_RM_ADV);
+               }
+
+               /* Total Number of ORFs. */
+               stream_putc_at(s, number_of_orfs_p, number_of_orfs);
+
+               len = stream_get_endp(s) - cap_len - 1;
+               stream_putc_at(s, cap_len, len);
+
+               if (bgp_debug_neighbor_events(peer))
+                       zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s",
+                                  peer,
+                                  action == CAPABILITY_ACTION_SET
+                                          ? "Advertising"
+                                          : "Removing",
+                                  capability, iana_afi2str(pkt_afi),
+                                  iana_safi2str(pkt_safi));
+               break;
+       case CAPABILITY_CODE_REFRESH:
        case CAPABILITY_CODE_AS4:
        case CAPABILITY_CODE_DYNAMIC:
        case CAPABILITY_CODE_ENHANCED_RR:
@@ -3044,6 +3116,123 @@ static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action,
        }
 }
 
+static void bgp_dynamic_capability_orf(uint8_t *pnt, int action,
+                                      struct capability_header *hdr,
+                                      struct peer *peer)
+{
+       uint8_t *data = pnt + 3;
+       uint8_t *end = data + hdr->length;
+       size_t len = end - data;
+
+       struct capability_mp_data mpc;
+       uint8_t num;
+       iana_afi_t pkt_afi;
+       afi_t afi;
+       iana_safi_t pkt_safi;
+       safi_t safi;
+       uint8_t type;
+       uint8_t mode;
+       uint16_t sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
+       uint16_t rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+       int i;
+
+       if (data + CAPABILITY_CODE_ORF_LEN > end) {
+               flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+                         "ORF: Received invalid length %zu, less than %d", len,
+                         CAPABILITY_CODE_ORF_LEN);
+               return;
+       }
+
+       /* ORF Entry header */
+       memcpy(&mpc, data, sizeof(mpc));
+       data += sizeof(mpc);
+       num = *data++;
+       pkt_afi = ntohs(mpc.afi);
+       pkt_safi = mpc.safi;
+
+       /* Convert AFI, SAFI to internal values, check. */
+       if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+               zlog_info("%pBP Addr-family %d/%d not supported. Ignoring the ORF capability",
+                         peer, pkt_afi, pkt_safi);
+               return;
+       }
+
+       /* validate number field */
+       if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) {
+               zlog_info("%pBP ORF Capability entry length error, Cap length %u, num %u",
+                         peer, hdr->length, num);
+               return;
+       }
+
+       if (action == CAPABILITY_ACTION_UNSET) {
+               UNSET_FLAG(peer->af_cap[afi][safi], sm_cap);
+               UNSET_FLAG(peer->af_cap[afi][safi], rm_cap);
+               return;
+       }
+
+       for (i = 0; i < num; i++) {
+               if (data + 1 > end) {
+                       flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH,
+                                "%pBP ORF Capability entry length (type) error, Cap length %u, num %u",
+                                peer, hdr->length, num);
+                       return;
+               }
+               type = *data++;
+
+               if (data + 1 > end) {
+                       flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH,
+                                "%pBP ORF Capability entry length (mode) error, Cap length %u, num %u",
+                                peer, hdr->length, num);
+                       return;
+               }
+               mode = *data++;
+
+               /* ORF Mode error check */
+               switch (mode) {
+               case ORF_MODE_BOTH:
+               case ORF_MODE_SEND:
+               case ORF_MODE_RECEIVE:
+                       break;
+               default:
+                       if (bgp_debug_neighbor_events(peer))
+                               zlog_debug("%pBP Addr-family %d/%d has ORF type/mode %d/%d not supported",
+                                          peer, afi, safi, type, mode);
+                       continue;
+               }
+
+               if (!((afi == AFI_IP && safi == SAFI_UNICAST) ||
+                     (afi == AFI_IP && safi == SAFI_MULTICAST) ||
+                     (afi == AFI_IP6 && safi == SAFI_UNICAST))) {
+                       if (bgp_debug_neighbor_events(peer))
+                               zlog_debug("%pBP Addr-family %d/%d unsupported AFI/SAFI received",
+                                          peer, afi, safi);
+                       continue;
+               }
+
+               if (bgp_debug_neighbor_events(peer))
+                       zlog_debug("%pBP OPEN has %s ORF capability as %s for afi/safi: %s/%s",
+                                  peer, lookup_msg(orf_type_str, type, NULL),
+                                  lookup_msg(orf_mode_str, mode, NULL),
+                                  iana_afi2str(pkt_afi),
+                                  iana_safi2str(pkt_safi));
+
+               switch (mode) {
+               case ORF_MODE_BOTH:
+                       SET_FLAG(peer->af_cap[afi][safi], sm_cap);
+                       SET_FLAG(peer->af_cap[afi][safi], rm_cap);
+                       break;
+               case ORF_MODE_SEND:
+                       SET_FLAG(peer->af_cap[afi][safi], sm_cap);
+                       UNSET_FLAG(peer->af_cap[afi][safi], rm_cap);
+                       break;
+               case ORF_MODE_RECEIVE:
+                       SET_FLAG(peer->af_cap[afi][safi], rm_cap);
+                       UNSET_FLAG(peer->af_cap[afi][safi], sm_cap);
+                       break;
+               }
+       }
+}
+
 static void bgp_dynamic_capability_llgr(uint8_t *pnt, int action,
                                        struct capability_header *hdr,
                                        struct peer *peer)
@@ -3401,8 +3590,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
                case CAPABILITY_CODE_ADDPATH:
                        bgp_dynamic_capability_addpath(pnt, action, hdr, peer);
                        break;
-               case CAPABILITY_CODE_REFRESH:
                case CAPABILITY_CODE_ORF:
+                       bgp_dynamic_capability_orf(pnt, action, hdr, peer);
+                       break;
+               case CAPABILITY_CODE_REFRESH:
                case CAPABILITY_CODE_AS4:
                case CAPABILITY_CODE_DYNAMIC:
                case CAPABILITY_CODE_ENHANCED_RR:
index 8144d6e7b3bc2b3fda85ad9ce8db64b0528086c2..fca9e2ad8e3c926d2d4548115beb44324be20108 100644 (file)
@@ -5831,24 +5831,37 @@ DEFUN (neighbor_capability_orf_prefix,
        struct peer *peer;
        afi_t afi = bgp_node_afi(vty);
        safi_t safi = bgp_node_safi(vty);
+       int ret;
 
        peer = peer_and_group_lookup_vty(vty, peer_str);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       if (strmatch(argv[idx_send_recv]->text, "send"))
-               return peer_af_flag_set_vty(vty, peer_str, afi, safi,
-                                           PEER_FLAG_ORF_PREFIX_SM);
+       if (strmatch(argv[idx_send_recv]->text, "send")) {
+               ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
+                                          PEER_FLAG_ORF_PREFIX_SM);
+               bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
+                                   CAPABILITY_ACTION_SET);
+               return ret;
+       }
 
-       if (strmatch(argv[idx_send_recv]->text, "receive"))
-               return peer_af_flag_set_vty(vty, peer_str, afi, safi,
-                                           PEER_FLAG_ORF_PREFIX_RM);
+       if (strmatch(argv[idx_send_recv]->text, "receive")) {
+               ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
+                                          PEER_FLAG_ORF_PREFIX_RM);
+               bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
+                                   CAPABILITY_ACTION_SET);
+               return ret;
+       }
 
-       if (strmatch(argv[idx_send_recv]->text, "both"))
-               return peer_af_flag_set_vty(vty, peer_str, afi, safi,
-                                           PEER_FLAG_ORF_PREFIX_SM)
-                      | peer_af_flag_set_vty(vty, peer_str, afi, safi,
-                                             PEER_FLAG_ORF_PREFIX_RM);
+       if (strmatch(argv[idx_send_recv]->text, "both")) {
+               ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
+                                          PEER_FLAG_ORF_PREFIX_SM) |
+                     peer_af_flag_set_vty(vty, peer_str, afi, safi,
+                                          PEER_FLAG_ORF_PREFIX_RM);
+               bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
+                                   CAPABILITY_ACTION_SET);
+               return ret;
+       }
 
        return CMD_WARNING_CONFIG_FAILED;
 }
@@ -5883,24 +5896,37 @@ DEFUN (no_neighbor_capability_orf_prefix,
        struct peer *peer;
        afi_t afi = bgp_node_afi(vty);
        safi_t safi = bgp_node_safi(vty);
+       int ret;
 
        peer = peer_and_group_lookup_vty(vty, peer_str);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       if (strmatch(argv[idx_send_recv]->text, "send"))
-               return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
-                                             PEER_FLAG_ORF_PREFIX_SM);
+       if (strmatch(argv[idx_send_recv]->text, "send")) {
+               ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+                                            PEER_FLAG_ORF_PREFIX_SM);
+               bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
+                                   CAPABILITY_ACTION_UNSET);
+               return ret;
+       }
 
-       if (strmatch(argv[idx_send_recv]->text, "receive"))
-               return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
-                                             PEER_FLAG_ORF_PREFIX_RM);
+       if (strmatch(argv[idx_send_recv]->text, "receive")) {
+               ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+                                            PEER_FLAG_ORF_PREFIX_RM);
+               bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
+                                   CAPABILITY_ACTION_UNSET);
+               return ret;
+       }
 
-       if (strmatch(argv[idx_send_recv]->text, "both"))
-               return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
-                                             PEER_FLAG_ORF_PREFIX_SM)
-                      | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
-                                               PEER_FLAG_ORF_PREFIX_RM);
+       if (strmatch(argv[idx_send_recv]->text, "both")) {
+               ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+                                            PEER_FLAG_ORF_PREFIX_SM) |
+                     peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+                                            PEER_FLAG_ORF_PREFIX_RM);
+               bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
+                                   CAPABILITY_ACTION_UNSET);
+               return ret;
+       }
 
        return CMD_WARNING_CONFIG_FAILED;
 }
index 12e462a8ca739e111a20edbd0f2c88aa0216a592..04c06b94d8ac41536a32bc9d2023806bd93e7a58 100644 (file)
@@ -4979,6 +4979,16 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
                        else if (flag == PEER_FLAG_ORF_PREFIX_RM)
                                peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
 
+                       /* We should not reset the session if
+                        * dynamic capability is enabled and we
+                        * are changing the ORF prefix flags.
+                        */
+                       if ((CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
+                            CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) &&
+                           (flag == PEER_FLAG_ORF_PREFIX_RM ||
+                            flag == PEER_FLAG_ORF_PREFIX_SM))
+                               action.type = peer_change_none;
+
                        peer_change_action(peer, afi, safi, action.type);
                }
        }
@@ -5039,6 +5049,18 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
                                                member->last_reset =
                                                        PEER_DOWN_CAPABILITY_CHANGE;
 
+                                       /* We should not reset the session if
+                                        * dynamic capability is enabled and we
+                                        * are changing the ORF prefix flags.
+                                        */
+                                       if ((CHECK_FLAG(peer->cap,
+                                                       PEER_CAP_DYNAMIC_RCV) &&
+                                            CHECK_FLAG(peer->cap,
+                                                       PEER_CAP_DYNAMIC_ADV)) &&
+                                           (flag == PEER_FLAG_ORF_PREFIX_RM ||
+                                            flag == PEER_FLAG_ORF_PREFIX_SM))
+                                               action.type = peer_change_none;
+
                                        peer_change_action(member, afi, safi,
                                                           action.type);
                                }