]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: inject policy route entry from bgp into zebra pbr entries.
authorPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 15 Mar 2018 15:06:59 +0000 (16:06 +0100)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 3 May 2018 13:15:08 +0000 (15:15 +0200)
Once the bgp flowspec entry is validated, then that means that zebra is
able to handle the entries.

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

index 672bb0bd587073d6e022995ad39a5f14a7a6f826..07f7be10b609ff309af05e8abc74f1f93d090e41 100644 (file)
 #include "bgpd/bgp_ecommunity.h"
 #include "bgpd/bgp_route.h"
 #include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_zebra.h"
+
+DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry")
+DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match")
+DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action")
+
+static int bgp_pbr_match_counter_unique;
+static int bgp_pbr_match_entry_counter_unique;
+static int bgp_pbr_action_counter_unique;
+static int bgp_pbr_match_iptable_counter_unique;
 
 static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
                                     const char *prepend)
@@ -194,6 +204,44 @@ static int bgp_pbr_build_and_validate_entry(struct prefix *p,
        return 0;
 }
 
+static void *bgp_pbr_match_alloc_intern(void *arg)
+{
+       struct bgp_pbr_match *bpm, *new;
+
+       bpm = (struct bgp_pbr_match *)arg;
+
+       new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
+       memcpy(new, bpm, sizeof(*bpm));
+
+       return new;
+}
+
+static void *bgp_pbr_action_alloc_intern(void *arg)
+{
+       struct bgp_pbr_action *bpa, *new;
+
+       bpa = (struct bgp_pbr_action *)arg;
+
+       new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
+
+       memcpy(new, bpa, sizeof(*bpa));
+
+       return new;
+}
+
+static void *bgp_pbr_match_entry_alloc_intern(void *arg)
+{
+       struct bgp_pbr_match_entry *bpme, *new;
+
+       bpme = (struct bgp_pbr_match_entry *)arg;
+
+       new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
+
+       memcpy(new, bpme, sizeof(*bpme));
+
+       return new;
+}
+
 uint32_t bgp_pbr_match_hash_key(void *arg)
 {
        struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
@@ -461,6 +509,370 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
        zlog_info("%s", return_string);
 }
 
+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)
+{
+       /* if bpme is null, bpm is also null
+        */
+       if (bpme == NULL)
+               return;
+       /* ipset del entry */
+       if (bpme->installed) {
+               bgp_send_pbr_ipset_entry_match(bpme, false);
+               bpme->installed = false;
+               bpme->backpointer = NULL;
+       }
+       hash_release(bpm->entry_hash, bpme);
+       if (hashcount(bpm->entry_hash) == 0) {
+               /* delete iptable entry first */
+               /* then delete ipset match */
+               if (bpm->installed) {
+                       if (bpm->installed_in_iptable) {
+                               bgp_send_pbr_iptable(bpm->action,
+                                                    bpm, false);
+                               bpm->installed_in_iptable = false;
+                       }
+                       bgp_send_pbr_ipset_match(bpm, false);
+                       bpm->installed = false;
+                       bpm->action = NULL;
+               }
+               hash_release(bgp->pbr_match_hash, bpm);
+               /* XXX release pbr_match_action if not used
+                * note that drop does not need to call send_pbr_action
+                */
+       }
+}
+
+struct bgp_pbr_match_entry_remain {
+       struct bgp_pbr_match_entry *bpme_to_match;
+       struct bgp_pbr_match_entry *bpme_found;
+};
+
+static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
+{
+       struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
+       struct bgp_pbr_match_entry_remain *bpmer =
+               (struct bgp_pbr_match_entry_remain *)arg;
+       struct bgp_pbr_match *bpm_temp;
+       struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
+
+       if (!bpme->backpointer ||
+           bpm == bpme->backpointer ||
+           bpme->backpointer->action == bpm->action)
+               return HASHWALK_CONTINUE;
+       /* ensure bpm other characteristics are equal */
+       bpm_temp = bpme->backpointer;
+       if (bpm_temp->vrf_id != bpm->vrf_id ||
+           bpm_temp->type != bpm->type ||
+           bpm_temp->flags != bpm->flags)
+               return HASHWALK_CONTINUE;
+
+       /* look for remaining bpme */
+       bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
+       if (!bpmer->bpme_found)
+               return HASHWALK_CONTINUE;
+       return HASHWALK_ABORT;
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
+                                                 struct bgp_info *binfo,
+                                                 vrf_id_t vrf_id,
+                                                 struct prefix *src,
+                                                 struct prefix *dst)
+{
+       struct bgp_pbr_match temp;
+       struct bgp_pbr_match_entry temp2;
+       struct bgp_pbr_match *bpm;
+       struct bgp_pbr_match_entry *bpme;
+       struct bgp_pbr_match_entry_remain bpmer;
+
+       /* as we don't know information from EC
+        * look for bpm that have the bpm
+        * with vrf_id characteristics
+        */
+       memset(&temp2, 0, sizeof(temp2));
+       memset(&temp, 0, sizeof(temp));
+       if (src) {
+               temp.flags |= MATCH_IP_SRC_SET;
+               prefix_copy(&temp2.src, src);
+       } else
+               temp2.src.family = AF_INET;
+       if (dst) {
+               temp.flags |= MATCH_IP_DST_SET;
+               prefix_copy(&temp2.dst, dst);
+       } else
+               temp2.dst.family = AF_INET;
+
+       if (src == NULL || dst == NULL)
+               temp.type = IPSET_NET;
+       else
+               temp.type = IPSET_NET_NET;
+       if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
+               temp.vrf_id = 0;
+       else
+               temp.vrf_id = vrf_id;
+       bpme = &temp2;
+       bpm = &temp;
+       bpme->backpointer = bpm;
+       /* right now, a previous entry may already exist
+        * flush previous entry if necessary
+        */
+       bpmer.bpme_to_match = bpme;
+       bpmer.bpme_found = NULL;
+       hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
+       if (bpmer.bpme_found) {
+               static struct bgp_pbr_match *local_bpm;
+               static struct bgp_pbr_action *local_bpa;
+
+               local_bpm = bpmer.bpme_found->backpointer;
+               local_bpa = local_bpm->action;
+               bgp_pbr_flush_entry(bgp, local_bpa,
+                                   local_bpm, bpmer.bpme_found);
+       }
+}
+
+static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
+                                            struct bgp_info *binfo,
+                                            vrf_id_t vrf_id,
+                                            struct prefix *src,
+                                            struct prefix *dst,
+                                            struct nexthop *nh,
+                                            float *rate)
+{
+       struct bgp_pbr_match temp;
+       struct bgp_pbr_match_entry temp2;
+       struct bgp_pbr_match *bpm;
+       struct bgp_pbr_match_entry *bpme = NULL;
+       struct bgp_pbr_action temp3;
+       struct bgp_pbr_action *bpa = NULL;
+       struct bgp_pbr_match_entry_remain bpmer;
+
+       /* look for bpa first */
+       memset(&temp3, 0, sizeof(temp3));
+       if (rate)
+               temp3.rate = *rate;
+       if (nh)
+               memcpy(&temp3.nh, nh, sizeof(struct nexthop));
+       temp3.vrf_id = vrf_id;
+       bpa = hash_get(bgp->pbr_action_hash, &temp3,
+                      bgp_pbr_action_alloc_intern);
+
+       if (bpa->fwmark == 0) {
+               /* TODO: allocate new table ID using zebra */
+               static int fwmark_id;
+
+               /* drop is handled by iptable */
+               if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
+                       bpa->table_id = 0;
+                       bpa->installed = true;
+               } else {
+                       bpa->fwmark = ++fwmark_id;
+                       bpa->table_id = fwmark_id;
+                       bpa->installed = false;
+               }
+               bpa->unique = ++bgp_pbr_action_counter_unique;
+               /* 0 value is forbidden */
+               bpa->install_in_progress = false;
+       }
+
+       /* then look for bpm */
+       memset(&temp, 0, sizeof(temp));
+       if (src == NULL || dst == NULL)
+               temp.type = IPSET_NET;
+       else
+               temp.type = IPSET_NET_NET;
+       temp.vrf_id = vrf_id;
+       if (src)
+               temp.flags |= MATCH_IP_SRC_SET;
+       if (dst)
+               temp.flags |= MATCH_IP_DST_SET;
+       temp.action = bpa;
+       bpm = hash_get(bgp->pbr_match_hash, &temp,
+                      bgp_pbr_match_alloc_intern);
+
+       /* new, then self allocate ipset_name and unique */
+       if (bpm && bpm->unique == 0) {
+               bpm->unique = ++bgp_pbr_match_counter_unique;
+               /* 0 value is forbidden */
+               sprintf(bpm->ipset_name, "match%p", bpm);
+               bpm->entry_hash = hash_create_size(8,
+                                  bgp_pbr_match_entry_hash_key,
+                                  bgp_pbr_match_entry_hash_equal,
+                                  "Match Entry Hash");
+               bpm->installed = false;
+
+               /* unique2 should be updated too */
+               bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
+               bpm->installed_in_iptable = false;
+               bpm->install_in_progress = false;
+               bpm->install_iptable_in_progress = false;
+       }
+
+       memset(&temp2, 0, sizeof(temp2));
+       if (src)
+               prefix_copy(&temp2.src, src);
+       else
+               temp2.src.family = AF_INET;
+       if (dst)
+               prefix_copy(&temp2.dst, dst);
+       else
+               temp2.dst.family = AF_INET;
+       if (bpm)
+               bpme = hash_get(bpm->entry_hash, &temp2,
+                       bgp_pbr_match_entry_alloc_intern);
+       if (bpme && bpme->unique == 0) {
+               bpme->unique = ++bgp_pbr_match_entry_counter_unique;
+               /* 0 value is forbidden */
+               bpme->backpointer = bpm;
+               bpme->installed = false;
+               bpme->install_in_progress = false;
+       }
+
+       /* BGP FS: append entry to zebra
+        * - policies are not routing entries and as such
+        * route replace semantics don't necessarily follow
+        * through to policy entries
+        * - because of that, not all policing information will be stored
+        * into zebra. and non selected policies will be suppressed from zebra
+        * - as consequence, in order to bring consistency
+        * a policy will be added, then ifan ecmp policy exists,
+        * it will be suppressed subsequently
+        */
+       /* ip rule add */
+       if (!bpa->installed)
+               bgp_send_pbr_rule_action(bpa, true);
+
+       /* ipset create */
+       if (bpm && !bpm->installed)
+               bgp_send_pbr_ipset_match(bpm, true);
+       /* ipset add */
+       if (bpme && !bpme->installed)
+               bgp_send_pbr_ipset_entry_match(bpme, true);
+
+       /* iptables */
+       if (bpm && !bpm->installed_in_iptable)
+               bgp_send_pbr_iptable(bpa, bpm, true);
+
+       /* A previous entry may already exist
+        * flush previous entry if necessary
+        */
+       bpmer.bpme_to_match = bpme;
+       bpmer.bpme_found = NULL;
+       hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
+       if (bpmer.bpme_found) {
+               static struct bgp_pbr_match *local_bpm;
+               static struct bgp_pbr_action *local_bpa;
+
+               local_bpm = bpmer.bpme_found->backpointer;
+               local_bpa = local_bpm->action;
+               bgp_pbr_flush_entry(bgp, local_bpa,
+                                   local_bpm, bpmer.bpme_found);
+       }
+
+
+}
+
+static void bgp_pbr_handle_entry(struct bgp *bgp,
+                               struct bgp_info *binfo,
+                               struct bgp_pbr_entry_main *api,
+                               bool add)
+{
+       struct nexthop nh;
+       int i = 0;
+       int continue_loop = 1;
+       float rate = 0;
+       struct prefix *src = NULL, *dst = NULL;
+
+       if (api->match_bitmask & PREFIX_SRC_PRESENT)
+               src = &api->src_prefix;
+       if (api->match_bitmask & PREFIX_DST_PRESENT)
+               dst = &api->dst_prefix;
+       memset(&nh, 0, sizeof(struct nexthop));
+       nh.vrf_id = VRF_UNKNOWN;
+
+       if (!add)
+               return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
+                                            api->vrf_id, src, dst);
+       /* no action for add = true */
+       for (i = 0; i < api->action_num; i++) {
+               switch (api->actions[i].action) {
+               case ACTION_TRAFFICRATE:
+                       /* drop packet */
+                       if (api->actions[i].u.r.rate == 0) {
+                               nh.vrf_id = api->vrf_id;
+                               nh.type = NEXTHOP_TYPE_BLACKHOLE;
+                               bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                   api->vrf_id, src, dst,
+                                                   &nh, &rate);
+                       } else {
+                               /* update rate. can be reentrant */
+                               rate = api->actions[i].u.r.rate;
+                               if (BGP_DEBUG(pbr, PBR))
+                                       bgp_pbr_print_policy_route(api);
+                               zlog_warn("PBR: ignoring Set action rate %f",
+                                         api->actions[i].u.r.rate);
+                       }
+                       break;
+               case ACTION_TRAFFIC_ACTION:
+                       if (api->actions[i].u.za.filter
+                           & TRAFFIC_ACTION_SAMPLE) {
+                               if (BGP_DEBUG(pbr, PBR))
+                                       bgp_pbr_print_policy_route(api);
+                               zlog_warn("PBR: Sample action Ignored");
+                       }
+#if 0
+                       if (api->actions[i].u.za.filter
+                           & TRAFFIC_ACTION_DISTRIBUTE) {
+                               if (BGP_DEBUG(pbr, PBR))
+                                       bgp_pbr_print_policy_route(api);
+                               zlog_warn("PBR: Distribute action Applies");
+                               continue_loop = 0;
+                               /* continue forwarding entry as before
+                                * no action
+                                */
+                       }
+#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
+                       /* terminate action: run other filters
+                        */
+                       break;
+               case ACTION_REDIRECT_IP:
+                       nh.type = NEXTHOP_TYPE_IPV4;
+                       nh.gate.ipv4.s_addr =
+                               api->actions[i].u.zr.redirect_ip_v4.s_addr;
+                       nh.vrf_id = api->vrf_id;
+                       bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                           api->vrf_id,
+                                                           src, dst,
+                                                           &nh, &rate);
+                       /* XXX combination with REDIRECT_VRF
+                        * + REDIRECT_NH_IP not done
+                        */
+                       continue_loop = 0;
+                       break;
+               case ACTION_REDIRECT:
+                       nh.vrf_id = api->actions[i].u.redirect_vrf;
+                       nh.type = NEXTHOP_TYPE_IPV4;
+                       bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                        api->vrf_id,
+                                                        src, dst,
+                                                        &nh, &rate);
+                       continue_loop = 0;
+                       break;
+               case ACTION_MARKING:
+                       if (BGP_DEBUG(pbr, PBR))
+                               bgp_pbr_print_policy_route(api);
+                       zlog_warn("PBR: Set DSCP %u Ignored",
+                                 api->actions[i].u.marking_dscp);
+                       break;
+               default:
+                       break;
+               }
+               if (continue_loop == 0)
+                       break;
+       }
+}
+
 void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
                         struct bgp_info *info, afi_t afi, safi_t safi,
                         bool nlri_update)
@@ -481,6 +893,5 @@ void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
                         __func__);
                return;
        }
-       /* TODO. update prefix and pbr hash contexts */
+       bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
 }
-