]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #3102 from ton31337/feature/match_blackhole_nexthops
authorDavid Lamparter <equinox@diac24.net>
Tue, 2 Oct 2018 09:29:59 +0000 (11:29 +0200)
committerGitHub <noreply@github.com>
Tue, 2 Oct 2018 09:29:59 +0000 (11:29 +0200)
bgpd: Match routes by type under route-maps

1  2 
bgpd/bgp_routemap.c
lib/routemap.c
lib/routemap.h

diff --combined bgpd/bgp_routemap.c
index 8e7c0a69dd4a77d78ffd5f75ad938225a6cd787a,3f29e879dfc70091c90e0a3bdd0b41f62743d783..0a3e4b3c89d0f69abede91175544aab3b8d004d7
@@@ -28,7 -28,6 +28,7 @@@
  #include "plist.h"
  #include "memory.h"
  #include "log.h"
 +#include "lua.h"
  #ifdef HAVE_LIBPCREPOSIX
  #include <pcreposix.h>
  #else
@@@ -331,102 -330,6 +331,102 @@@ struct route_map_rule_cmd route_match_p
                                                  route_match_peer_compile,
                                                  route_match_peer_free};
  
 +#if defined(HAVE_LUA)
 +static route_map_result_t route_match_command(void *rule,
 +                                            const struct prefix *prefix,
 +                                            route_map_object_t type,
 +                                            void *object)
 +{
 +      int status = RMAP_NOMATCH;
 +      u_int32_t locpref = 0;
 +      u_int32_t newlocpref = 0;
 +      enum lua_rm_status lrm_status;
 +      struct bgp_info *info = (struct bgp_info *)object;
 +      lua_State *L = lua_initialize("/etc/frr/lua.scr");
 +
 +      if (L == NULL)
 +              return status;
 +
 +      /*
 +       * Setup the prefix information to pass in
 +       */
 +      lua_setup_prefix_table(L, prefix);
 +
 +      zlog_debug("Set up prefix table");
 +      /*
 +       * Setup the bgp_info information
 +       */
 +      lua_newtable(L);
 +      lua_pushinteger(L, info->attr->med);
 +      lua_setfield(L, -2, "metric");
 +      lua_pushinteger(L, info->attr->nh_ifindex);
 +      lua_setfield(L, -2, "ifindex");
 +      lua_pushstring(L, info->attr->aspath->str);
 +      lua_setfield(L, -2, "aspath");
 +      lua_pushinteger(L, info->attr->local_pref);
 +      lua_setfield(L, -2, "localpref");
 +      zlog_debug("%s %d", info->attr->aspath->str, info->attr->nh_ifindex);
 +      lua_setglobal(L, "nexthop");
 +
 +      zlog_debug("Set up nexthop information");
 +      /*
 +       * Run the rule
 +       */
 +      lrm_status = lua_run_rm_rule(L, rule);
 +      switch (lrm_status) {
 +      case LUA_RM_FAILURE:
 +              zlog_debug("RM_FAILURE");
 +              break;
 +      case LUA_RM_NOMATCH:
 +              zlog_debug("RM_NOMATCH");
 +              break;
 +      case LUA_RM_MATCH_AND_CHANGE:
 +              zlog_debug("MATCH AND CHANGE");
 +              lua_getglobal(L, "nexthop");
 +              info->attr->med = get_integer(L, "metric");
 +              /*
 +               * This needs to be abstraced with the set function
 +               */
 +              if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
 +                      locpref = info->attr->local_pref;
 +              newlocpref = get_integer(L, "localpref");
 +              if (newlocpref != locpref) {
 +                      info->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
 +                      info->attr->local_pref = newlocpref;
 +              }
 +              status = RMAP_MATCH;
 +              break;
 +      case LUA_RM_MATCH:
 +              zlog_debug("MATCH ONLY");
 +              status = RMAP_MATCH;
 +              break;
 +      }
 +      lua_close(L);
 +      return status;
 +}
 +
 +static void *route_match_command_compile(const char *arg)
 +{
 +      char *command;
 +
 +      command = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
 +      return command;
 +}
 +
 +static void
 +route_match_command_free(void *rule)
 +{
 +      XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
 +}
 +
 +struct route_map_rule_cmd route_match_command_cmd = {
 +      "command",
 +      route_match_command,
 +      route_match_command_compile,
 +      route_match_command_free
 +};
 +#endif
 +
  /* `match ip address IP_ACCESS_LIST' */
  
  /* Match function should return 1 if match is success else return
@@@ -645,6 -548,45 +645,45 @@@ struct route_map_rule_cmd route_match_i
        route_match_ip_next_hop_prefix_list_compile,
        route_match_ip_next_hop_prefix_list_free};
  
+ /* `match ip next-hop type <blackhole>' */
+ static route_map_result_t
+ route_match_ip_next_hop_type(void *rule, const struct prefix *prefix,
+                            route_map_object_t type, void *object)
+ {
+       struct bgp_info *bgp_info;
+       if (type == RMAP_BGP && prefix->family == AF_INET) {
+               bgp_info = (struct bgp_info *)object;
+               if (!bgp_info || !bgp_info->attr)
+                       return RMAP_DENYMATCH;
+               /* If nexthop interface's index can't be resolved and nexthop is
+                  set to any address then mark it as type `blackhole`.
+                  This logic works for matching kernel/static routes like:
+                  `ip route add blackhole 10.0.0.1`. */
+               if (bgp_info->attr->nexthop.s_addr == INADDR_ANY
+                   && !bgp_info->attr->nh_ifindex)
+                       return RMAP_MATCH;
+       }
+       return RMAP_NOMATCH;
+ }
+ static void *route_match_ip_next_hop_type_compile(const char *arg)
+ {
+       return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+ }
+ static void route_match_ip_next_hop_type_free(void *rule)
+ {
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+ }
+ static struct route_map_rule_cmd route_match_ip_next_hop_type_cmd = {
+       "ip next-hop type", route_match_ip_next_hop_type,
+       route_match_ip_next_hop_type_compile,
+       route_match_ip_next_hop_type_free};
  /* `match ip route-source prefix-list PREFIX_LIST' */
  
  static route_map_result_t
@@@ -2469,6 -2411,53 +2508,53 @@@ struct route_map_rule_cmd route_match_i
        route_match_ipv6_address_prefix_list_compile,
        route_match_ipv6_address_prefix_list_free};
  
+ /* `match ipv6 next-hop type <TYPE>' */
+ static route_map_result_t
+ route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix,
+                             route_map_object_t type, void *object)
+ {
+       struct bgp_info *bgp_info;
+       struct in6_addr *addr = rule;
+       if (type == RMAP_BGP && prefix->family == AF_INET6) {
+               bgp_info = (struct bgp_info *)object;
+               if (!bgp_info || !bgp_info->attr)
+                       return RMAP_DENYMATCH;
+               if (IPV6_ADDR_SAME(&bgp_info->attr->mp_nexthop_global, addr)
+                   && !bgp_info->attr->nh_ifindex)
+                       return RMAP_MATCH;
+       }
+       return RMAP_NOMATCH;
+ }
+ static void *route_match_ipv6_next_hop_type_compile(const char *arg)
+ {
+       struct in6_addr *address;
+       int ret;
+       address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+       ret = inet_pton(AF_INET6, "::0", address);
+       if (!ret) {
+               XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+               return NULL;
+       }
+       return address;
+ }
+ static void route_match_ipv6_next_hop_type_free(void *rule)
+ {
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+ }
+ struct route_map_rule_cmd route_match_ipv6_next_hop_type_cmd = {
+       "ipv6 next-hop type", route_match_ipv6_next_hop_type,
+       route_match_ipv6_next_hop_type_compile,
+       route_match_ipv6_next_hop_type_free};
  /* `set ipv6 nexthop global IP_ADDRESS' */
  
  /* Set nexthop to object.  ojbect must be pointer to struct attr. */
@@@ -3453,30 -3442,6 +3539,30 @@@ DEFUN (no_match_peer
                                      RMAP_EVENT_MATCH_DELETED);
  }
  
 +#if defined(HAVE_LUA)
 +DEFUN (match_command,
 +       match_command_cmd,
 +       "match command WORD",
 +       MATCH_STR
 +       "Run a command to match\n"
 +       "The command to run\n")
 +{
 +      return bgp_route_match_add(vty, "command", argv[2]->arg,
 +                                 RMAP_EVENT_FILTER_ADDED);
 +}
 +
 +DEFUN (no_match_command,
 +       no_match_command_cmd,
 +       "no match command WORD",
 +       NO_STR
 +       MATCH_STR
 +       "Run a command to match\n"
 +       "The command to run\n")
 +{
 +      return bgp_route_match_delete(vty, "command", argv[3]->arg,
 +                                    RMAP_EVENT_FILTER_DELETED);
 +}
 +#endif
  
  /* match probability */
  DEFUN (match_probability,
@@@ -4766,12 -4731,18 +4852,18 @@@ void bgp_route_map_init(void
        route_map_match_ip_next_hop_prefix_list_hook(generic_match_add);
        route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete);
  
+       route_map_match_ip_next_hop_type_hook(generic_match_add);
+       route_map_no_match_ip_next_hop_type_hook(generic_match_delete);
        route_map_match_ipv6_address_hook(generic_match_add);
        route_map_no_match_ipv6_address_hook(generic_match_delete);
  
        route_map_match_ipv6_address_prefix_list_hook(generic_match_add);
        route_map_no_match_ipv6_address_prefix_list_hook(generic_match_delete);
  
+       route_map_match_ipv6_next_hop_type_hook(generic_match_add);
+       route_map_no_match_ipv6_next_hop_type_hook(generic_match_delete);
        route_map_match_metric_hook(generic_match_add);
        route_map_no_match_metric_hook(generic_match_delete);
  
  
        route_map_install_match(&route_match_peer_cmd);
        route_map_install_match(&route_match_local_pref_cmd);
 +#if defined(HAVE_LUA)
 +      route_map_install_match(&route_match_command_cmd);
 +#endif
        route_map_install_match(&route_match_ip_address_cmd);
        route_map_install_match(&route_match_ip_next_hop_cmd);
        route_map_install_match(&route_match_ip_route_source_cmd);
        route_map_install_match(&route_match_ip_address_prefix_list_cmd);
        route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd);
+       route_map_install_match(&route_match_ip_next_hop_type_cmd);
        route_map_install_match(&route_match_ip_route_source_prefix_list_cmd);
        route_map_install_match(&route_match_aspath_cmd);
        route_map_install_match(&route_match_community_cmd);
        route_map_install_match(&route_match_ipv6_address_cmd);
        route_map_install_match(&route_match_ipv6_next_hop_cmd);
        route_map_install_match(&route_match_ipv6_address_prefix_list_cmd);
+       route_map_install_match(&route_match_ipv6_next_hop_type_cmd);
        route_map_install_set(&route_set_ipv6_nexthop_global_cmd);
        route_map_install_set(&route_set_ipv6_nexthop_prefer_global_cmd);
        route_map_install_set(&route_set_ipv6_nexthop_local_cmd);
        install_element(RMAP_NODE, &no_set_ipv6_nexthop_prefer_global_cmd);
        install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
        install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
 +#if defined(HAVE_LUA)
 +      install_element(RMAP_NODE, &match_command_cmd);
 +      install_element(RMAP_NODE, &no_match_command_cmd);
 +#endif
  }
  
  void bgp_route_map_terminate(void)
diff --combined lib/routemap.c
index 028351f6c6b37c5d833cd555078fd77e00c267eb,2111a4f4da262e7037d71fb1f05c16e38cca34ea..1d958fa592e0beda7596e2624a1bac4ed36c085a
@@@ -111,6 -111,20 +111,20 @@@ struct route_map_match_set_hooks 
                                                const char *arg,
                                                route_map_event_t type);
  
+       /* match ip next hop type */
+       int (*match_ip_next_hop_type)(struct vty *vty,
+                                            struct route_map_index *index,
+                                            const char *command,
+                                            const char *arg,
+                                            route_map_event_t type);
+       /* no match ip next hop type */
+       int (*no_match_ip_next_hop_type)(struct vty *vty,
+                                               struct route_map_index *index,
+                                               const char *command,
+                                               const char *arg,
+                                               route_map_event_t type);
        /* match ipv6 address */
        int (*match_ipv6_address)(struct vty *vty,
                                  struct route_map_index *index,
                                                 const char *arg,
                                                 route_map_event_t type);
  
+       /* match ipv6 next-hop type */
+       int (*match_ipv6_next_hop_type)(struct vty *vty,
+                                             struct route_map_index *index,
+                                             const char *command,
+                                             const char *arg,
+                                             route_map_event_t type);
+       /* no match ipv6next-hop type */
+       int (*no_match_ipv6_next_hop_type)(struct vty *vty,
+                                          struct route_map_index *index,
+                                          const char *command, const char *arg,
+                                          route_map_event_t type);
        /* match metric */
        int (*match_metric)(struct vty *vty, struct route_map_index *index,
                            const char *command, const char *arg,
@@@ -275,6 -302,22 +302,22 @@@ void route_map_no_match_ip_next_hop_pre
        rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func;
  }
  
+ /* match ip next hop type */
+ void route_map_match_ip_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type))
+ {
+       rmap_match_set_hook.match_ip_next_hop_type = func;
+ }
+ /* no match ip next hop type */
+ void route_map_no_match_ip_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type))
+ {
+       rmap_match_set_hook.no_match_ip_next_hop_type = func;
+ }
  /* match ipv6 address */
  void route_map_match_ipv6_address_hook(int (*func)(
        struct vty *vty, struct route_map_index *index, const char *command,
@@@ -308,6 -351,22 +351,22 @@@ void route_map_no_match_ipv6_address_pr
        rmap_match_set_hook.no_match_ipv6_address_prefix_list = func;
  }
  
+ /* match ipv6 next-hop type */
+ void route_map_match_ipv6_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type))
+ {
+       rmap_match_set_hook.match_ipv6_next_hop_type = func;
+ }
+ /* no match ipv6 next-hop type */
+ void route_map_no_match_ipv6_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type))
+ {
+       rmap_match_set_hook.no_match_ipv6_next_hop_type = func;
+ }
  /* match metric */
  void route_map_match_metric_hook(int (*func)(
        struct vty *vty, struct route_map_index *index, const char *command,
@@@ -854,13 -913,11 +913,13 @@@ static void vty_show_route_map_entry(st
        struct route_map_index *index;
        struct route_map_rule *rule;
  
 -      vty_out(vty, "%s:\n", frr_protonameinst);
 +      vty_out(vty, "route-map: %s Invoked: %" PRIu64 "\n",
 +              map->name, map->applied);
  
        for (index = map->head; index; index = index->next) {
 -              vty_out(vty, "route-map %s, %s, sequence %d\n", map->name,
 -                      route_map_type_str(index->type), index->pref);
 +              vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n",
 +                      route_map_type_str(index->type), index->pref,
 +                      index->applied);
  
                /* Description */
                if (index->description)
@@@ -906,8 -963,6 +965,8 @@@ static int vty_show_route_map(struct vt
  {
        struct route_map *map;
  
 +      vty_out(vty, "%s:\n", frr_protonameinst);
 +
        if (name) {
                map = route_map_lookup_by_name(name);
  
@@@ -1461,10 -1516,8 +1520,10 @@@ route_map_result_t route_map_apply(stru
        if (map == NULL)
                return RMAP_DENYMATCH;
  
 +      map->applied++;
        for (index = map->head; index; index = index->next) {
                /* Apply this index. */
 +              index->applied++;
                ret = route_map_apply_match(&index->match_list, prefix, type,
                                            object);
  
@@@ -2034,6 -2087,45 +2093,45 @@@ DEFUN (no_match_ip_next_hop_prefix_list
        return CMD_SUCCESS;
  }
  
+ DEFUN(match_ip_next_hop_type, match_ip_next_hop_type_cmd,
+       "match ip next-hop type <blackhole>",
+       MATCH_STR IP_STR
+       "Match next-hop address of route\n"
+       "Match entries by type\n"
+       "Blackhole\n")
+ {
+       int idx_word = 4;
+       VTY_DECLVAR_CONTEXT(route_map_index, index);
+       if (rmap_match_set_hook.match_ip_next_hop_type)
+               return rmap_match_set_hook.match_ip_next_hop_type(
+                       vty, index, "ip next-hop type", argv[idx_word]->arg,
+                       RMAP_EVENT_MATCH_ADDED);
+       return CMD_SUCCESS;
+ }
+ DEFUN(no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
+       "no match ip next-hop type [<blackhole>]",
+       NO_STR MATCH_STR IP_STR
+       "Match next-hop address of route\n"
+       "Match entries by type\n"
+       "Blackhole\n")
+ {
+       int idx_word = 5;
+       VTY_DECLVAR_CONTEXT(route_map_index, index);
+       if (rmap_match_set_hook.no_match_ip_next_hop) {
+               if (argc <= idx_word)
+                       return rmap_match_set_hook.no_match_ip_next_hop(
+                               vty, index, "ip next-hop type", NULL,
+                               RMAP_EVENT_MATCH_DELETED);
+               return rmap_match_set_hook.no_match_ip_next_hop(
+                       vty, index, "ip next-hop type", argv[idx_word]->arg,
+                       RMAP_EVENT_MATCH_DELETED);
+       }
+       return CMD_SUCCESS;
+ }
  
  DEFUN (match_ipv6_address,
         match_ipv6_address_cmd,
@@@ -2112,6 -2204,39 +2210,39 @@@ DEFUN (no_match_ipv6_address_prefix_lis
        return CMD_SUCCESS;
  }
  
+ DEFUN(match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
+       "match ipv6 next-hop type <blackhole>",
+       MATCH_STR IPV6_STR
+       "Match address of route\n"
+       "Match entries by type\n"
+       "Blackhole\n")
+ {
+       int idx_word = 4;
+       VTY_DECLVAR_CONTEXT(route_map_index, index);
+       if (rmap_match_set_hook.match_ipv6_next_hop_type)
+               return rmap_match_set_hook.match_ipv6_next_hop_type(
+                       vty, index, "ipv6 next-hop type", argv[idx_word]->arg,
+                       RMAP_EVENT_MATCH_ADDED);
+       return CMD_SUCCESS;
+ }
+ DEFUN(no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
+       "no match ipv6 next-hop type [<blackhole>]",
+       NO_STR MATCH_STR IPV6_STR
+       "Match address of route\n"
+       "Match entries by type\n"
+       "Blackhole\n")
+ {
+       int idx_word = 5;
+       VTY_DECLVAR_CONTEXT(route_map_index, index);
+       if (rmap_match_set_hook.no_match_ipv6_next_hop_type)
+               return rmap_match_set_hook.no_match_ipv6_next_hop_type(
+                       vty, index, "ipv6 next-hop type", argv[idx_word]->arg,
+                       RMAP_EVENT_MATCH_DELETED);
+       return CMD_SUCCESS;
+ }
  
  DEFUN (match_metric,
         match_metric_cmd,
@@@ -2879,12 -3004,18 +3010,18 @@@ void route_map_init(void
        install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
        install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
  
+       install_element(RMAP_NODE, &match_ip_next_hop_type_cmd);
+       install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd);
        install_element(RMAP_NODE, &match_ipv6_address_cmd);
        install_element(RMAP_NODE, &no_match_ipv6_address_cmd);
  
        install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
        install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
  
+       install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd);
+       install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd);
        install_element(RMAP_NODE, &match_metric_cmd);
        install_element(RMAP_NODE, &no_match_metric_cmd);
  
diff --combined lib/routemap.h
index 191456368740cc4b213b12bff545e64aab9443ad,c654806b44932d0c0d8a037f4ab4ffa90a86863d..481b8c4a9a34091b44b55931c8b0953671709aec
@@@ -142,9 -142,6 +142,9 @@@ struct route_map_index 
        struct route_map_index *next;
        struct route_map_index *prev;
  
 +      /* Keep track how many times we've try to apply */
 +      uint64_t applied;
 +
        QOBJ_FIELDS
  };
  DECLARE_QOBJ_TYPE(route_map_index)
@@@ -166,9 -163,6 +166,9 @@@ struct route_map 
        bool to_be_processed; /* True if modification isn't acted on yet */
        bool deleted;         /* If 1, then this node will be deleted */
  
 +      /* How many times have we applied this route-map */
 +      uint64_t applied;
 +
        QOBJ_FIELDS
  };
  DECLARE_QOBJ_TYPE(route_map)
@@@ -289,6 -283,14 +289,14 @@@ extern void route_map_match_ip_next_hop
  extern void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)(
        struct vty *vty, struct route_map_index *index, const char *command,
        const char *arg, route_map_event_t type));
+ /* match ip next hop type */
+ extern void route_map_match_ip_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type));
+ /* no match ip next hop type */
+ extern void route_map_no_match_ip_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type));
  /* match ipv6 address */
  extern void route_map_match_ipv6_address_hook(int (*func)(
        struct vty *vty, struct route_map_index *index, const char *command,
@@@ -305,6 -307,14 +313,14 @@@ extern void route_map_match_ipv6_addres
  extern void route_map_no_match_ipv6_address_prefix_list_hook(int (*func)(
        struct vty *vty, struct route_map_index *index, const char *command,
        const char *arg, route_map_event_t type));
+ /* match ipv6 next-hop type */
+ extern void route_map_match_ipv6_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type));
+ /* no match ipv6 next-hop type */
+ extern void route_map_no_match_ipv6_next_hop_type_hook(int (*func)(
+       struct vty *vty, struct route_map_index *index, const char *command,
+       const char *arg, route_map_event_t type));
  /* match metric */
  extern void route_map_match_metric_hook(int (*func)(
        struct vty *vty, struct route_map_index *index, const char *command,