]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: conversion from fs to pbr: support for ip rule from/to
authorPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 29 Nov 2018 14:17:36 +0000 (15:17 +0100)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Tue, 29 Jan 2019 13:15:10 +0000 (14:15 +0100)
adding/suppressing flowspec to pbr is supported. the add and the remove
code is being added. now,bgp supports the hash list of ip rule list.
The removal of bgp ip rule is done via search. The search uses the
action field. the reason is that when a pbr rule is added, to replace an
old one, the old one is kept until the new one is installed, so as to
avoid traffic to be cut. This is why at one moment, one can have two
same iprules with different actions. And this is why the algorithm
covers this case.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
bgpd/bgp_pbr.c

index 66c20b1aa59b92186d7105a096214eee3a08bf3f..941bee98b382e06ead0adeb5d45d50b8d8b2cdec 100644 (file)
@@ -201,9 +201,11 @@ struct bgp_pbr_val_mask {
  * so that BGP can create pbr instructions to ZEBRA
  */
 struct bgp_pbr_filter {
+       uint8_t type;
        vrf_id_t vrf_id;
        struct prefix *src;
        struct prefix *dst;
+       uint8_t bitmask_iprule;
        uint8_t protocol;
        struct bgp_pbr_range_port *pkt_len;
        struct bgp_pbr_range_port *src_port;
@@ -1367,6 +1369,32 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
        zlog_info("%s", return_string);
 }
 
+static void bgp_pbr_flush_iprule(struct bgp *bgp, struct bgp_pbr_action *bpa,
+                                 struct bgp_pbr_rule *bpr)
+{
+       /* if bpr is null, do nothing
+        */
+       if (bpr == NULL)
+               return;
+       if (bpr->installed) {
+               bgp_send_pbr_rule_action(bpa, bpr, false);
+               bpr->installed = false;
+               bpr->action->refcnt--;
+               bpr->action = NULL;
+       }
+       hash_release(bgp->pbr_rule_hash, bpr);
+       if (bpa->refcnt == 0) {
+               if (bpa->installed && bpa->table_id != 0) {
+                       bgp_send_pbr_rule_action(bpa, NULL, false);
+                       bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
+                                                  AFI_IP,
+                                                  bpa->table_id,
+                                                  false);
+                       bpa->installed = false;
+               }
+       }
+}
+
 static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
                                struct bgp_pbr_match *bpm,
                                struct bgp_pbr_match_entry *bpme)
@@ -1429,6 +1457,50 @@ struct bgp_pbr_match_entry_remain {
        struct bgp_pbr_match_entry *bpme_found;
 };
 
+struct bgp_pbr_rule_remain {
+       struct bgp_pbr_rule *bpr_to_match;
+       struct bgp_pbr_rule *bpr_found;
+};
+
+static int bgp_pbr_get_same_rule(struct hash_backet *backet, void *arg)
+{
+       struct bgp_pbr_rule *r1 = (struct bgp_pbr_rule *)backet->data;
+       struct bgp_pbr_rule_remain *ctxt =
+               (struct bgp_pbr_rule_remain *)arg;
+       struct bgp_pbr_rule *r2;
+
+       r2 = ctxt->bpr_to_match;
+
+       if (r1->vrf_id != r2->vrf_id)
+               return HASHWALK_CONTINUE;
+
+       if (r1->flags != r2->flags)
+               return HASHWALK_CONTINUE;
+
+       if ((r1->flags & MATCH_IP_SRC_SET) &&
+           !prefix_same(&r1->src, &r2->src))
+               return HASHWALK_CONTINUE;
+
+       if ((r1->flags & MATCH_IP_DST_SET) &&
+           !prefix_same(&r1->dst, &r2->dst))
+               return HASHWALK_CONTINUE;
+
+       /* this function is used for two cases:
+        * - remove an entry upon withdraw request
+        * (case r2->action is null)
+        * - replace an old iprule with different action
+        * (case r2->action is != null)
+        * the old one is removed after the new one
+        * this is to avoid disruption in traffic
+        */
+       if (r2->action == NULL ||
+           r1->action != r2->action) {
+               ctxt->bpr_found = r1;
+               return HASHWALK_ABORT;
+       }
+       return HASHWALK_CONTINUE;
+}
+
 static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
 {
        struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
@@ -1466,12 +1538,15 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
 {
        struct bgp_pbr_match temp;
        struct bgp_pbr_match_entry temp2;
+       struct bgp_pbr_rule pbr_rule;
+       struct bgp_pbr_rule *bpr;
        struct bgp_pbr_match *bpm;
        struct bgp_pbr_match_entry *bpme;
        struct bgp_pbr_match_entry_remain bpmer;
        struct bgp_pbr_range_port *src_port;
        struct bgp_pbr_range_port *dst_port;
        struct bgp_pbr_range_port *pkt_len;
+       struct bgp_pbr_rule_remain bprr;
 
        if (!bpf)
                return;
@@ -1488,6 +1563,37 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
         */
        memset(&temp2, 0, sizeof(temp2));
        memset(&temp, 0, sizeof(temp));
+
+       if (bpf->type == BGP_PBR_IPRULE) {
+               memset(&pbr_rule, 0, sizeof(pbr_rule));
+               pbr_rule.vrf_id = bpf->vrf_id;
+               if (bpf->src) {
+                       prefix_copy(&pbr_rule.src, bpf->src);
+                       pbr_rule.flags |= MATCH_IP_SRC_SET;
+               }
+               if (bpf->dst) {
+                       prefix_copy(&pbr_rule.dst, bpf->dst);
+                       pbr_rule.flags |= MATCH_IP_DST_SET;
+               }
+               bpr = &pbr_rule;
+               /* A previous entry may already exist
+                * flush previous entry if necessary
+                */
+               bprr.bpr_to_match = bpr;
+               bprr.bpr_found = NULL;
+               hash_walk(bgp->pbr_rule_hash, bgp_pbr_get_same_rule, &bprr);
+               if (bprr.bpr_found) {
+                       static struct bgp_pbr_rule *local_bpr;
+                       static struct bgp_pbr_action *local_bpa;
+
+                       local_bpr = bprr.bpr_found;
+                       local_bpa = local_bpr->action;
+                       bgp_pbr_flush_iprule(bgp, local_bpa,
+                                            local_bpr);
+               }
+               return;
+       }
+
        if (bpf->src) {
                temp.flags |= MATCH_IP_SRC_SET;
                prefix_copy(&temp2.src, bpf->src);
@@ -1846,9 +1952,12 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
        struct bgp_pbr_action temp3;
        struct bgp_pbr_action *bpa = NULL;
        struct bgp_pbr_match_entry_remain bpmer;
+       struct bgp_pbr_rule_remain bprr;
        struct bgp_pbr_range_port *src_port;
        struct bgp_pbr_range_port *dst_port;
        struct bgp_pbr_range_port *pkt_len;
+       struct bgp_pbr_rule pbr_rule;
+       struct bgp_pbr_rule *bpr;
        bool bpme_found = false;
 
        if (!bpf)
@@ -1885,7 +1994,51 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
                /* 0 value is forbidden */
                bpa->install_in_progress = false;
        }
+       if (bpf->type == BGP_PBR_IPRULE) {
+               memset(&pbr_rule, 0, sizeof(pbr_rule));
+               pbr_rule.vrf_id = bpf->vrf_id;
+               if (bpf->src) {
+                       pbr_rule.flags |= MATCH_IP_SRC_SET;
+                       prefix_copy(&pbr_rule.src, bpf->src);
+               }
+               if (bpf->dst) {
+                       pbr_rule.flags |= MATCH_IP_DST_SET;
+                       prefix_copy(&pbr_rule.dst, bpf->dst);
+               }
+               pbr_rule.action = bpa;
+               bpr = hash_get(bgp->pbr_rule_hash, &pbr_rule,
+                              bgp_pbr_rule_alloc_intern);
+               if (bpr && bpr->unique == 0) {
+                       bpr->unique = ++bgp_pbr_action_counter_unique;
+                       bpr->installed = false;
+                       bpr->install_in_progress = false;
+               }
+               if (!bpa->installed && !bpa->install_in_progress) {
+                       bgp_send_pbr_rule_action(bpa, NULL, true);
+                       bgp_zebra_announce_default(bgp, nh,
+                                                  AFI_IP, bpa->table_id, true);
+               }
+               /* ip rule add */
+               if (bpr && !bpr->installed)
+                       bgp_send_pbr_rule_action(bpa, bpr, true);
 
+               /* A previous entry may already exist
+                * flush previous entry if necessary
+                */
+               bprr.bpr_to_match = bpr;
+               bprr.bpr_found = NULL;
+               hash_walk(bgp->pbr_rule_hash, bgp_pbr_get_same_rule, &bprr);
+               if (bprr.bpr_found) {
+                       static struct bgp_pbr_rule *local_bpr;
+                       static struct bgp_pbr_action *local_bpa;
+
+                       local_bpr = bprr.bpr_found;
+                       local_bpa = local_bpr->action;
+                       bgp_pbr_flush_iprule(bgp, local_bpa,
+                                            local_bpr);
+               }
+               return;
+       }
        /* then look for bpm */
        memset(&temp, 0, sizeof(temp));
        temp.vrf_id = bpf->vrf_id;
@@ -2162,10 +2315,16 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
        memset(&nh, 0, sizeof(struct nexthop));
        memset(&bpf, 0, sizeof(struct bgp_pbr_filter));
        memset(&bpof, 0, sizeof(struct bgp_pbr_or_filter));
-       if (api->match_bitmask & PREFIX_SRC_PRESENT)
+       if (api->match_bitmask & PREFIX_SRC_PRESENT ||
+           (api->type == BGP_PBR_IPRULE &&
+            api->match_bitmask_iprule & PREFIX_SRC_PRESENT))
                src = &api->src_prefix;
-       if (api->match_bitmask & PREFIX_DST_PRESENT)
+       if (api->match_bitmask & PREFIX_DST_PRESENT ||
+           (api->type == BGP_PBR_IPRULE &&
+            api->match_bitmask_iprule & PREFIX_DST_PRESENT))
                dst = &api->dst_prefix;
+       if (api->type == BGP_PBR_IPRULE)
+               bpf.type = api->type;
        memset(&nh, 0, sizeof(struct nexthop));
        nh.vrf_id = VRF_UNKNOWN;
        if (api->match_protocol_num)