]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: handle ICMP type and code from flowspec
authorPhilippe Guibert <philippe.guibert@6wind.com>
Mon, 4 Jun 2018 16:11:04 +0000 (18:11 +0200)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 28 Jun 2018 06:52:15 +0000 (08:52 +0200)
It is possible for flowspec entries containing ICMP rule to insert PBR
entries based on ICMP type and ICMP code.
Flowspec ICMP filtering can either have icmp type or icmp code or both.
Not all combinations are permitted:
- if icmp code is provided, then it is not possible to derive the
  correct icmp value. This will not be installed
- range of ICMP is authorised or list of ICMP, but not both.
- on receiving a list of ICMPtype/code, each ICMP type is attempted to
  be associated to ICMP code. If not found, then ICMPtype is combined
  with all known ICMP code values associated to that ICMP type.
- if a specific ICMP type/code is needed, despite the ICMP code/type
  combination does not exist, then it is possible to do it by forging a
  FS ICMP type/code specific for that.

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

index 5e36f9175048eb6d1a332ea8370ad0fd77576c65..0a54183db7779c6259db2224c7f103e0983b4f20 100644 (file)
@@ -180,6 +180,19 @@ struct bgp_pbr_range_port {
        uint16_t max_port;
 };
 
+static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[],
+                                     int num)
+{
+       int i = 0;
+
+       for (i = 0; i < num; i++) {
+               if (list[i].compare_operator !=
+                   OPERATOR_COMPARE_EQUAL_TO)
+                       return false;
+       }
+       return true;
+}
+
 /* return true if extraction ok
  */
 static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
@@ -231,6 +244,8 @@ static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
 
 static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
 {
+       bool enumerate_icmp = false;
+
        /* because bgp pbr entry may contain unsupported
         * combinations, a message will be displayed here if
         * not supported.
@@ -240,7 +255,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
         * - combination src/dst => drop
         * - combination srcport + @IP
         */
-       if (api->match_icmp_type_num || api->match_packet_length_num
+       if (api->match_packet_length_num
            || api->match_dscp_num || api->match_tcpflags_num) {
                if (BGP_DEBUG(pbr, PBR)) {
                        bgp_pbr_print_policy_route(api);
@@ -259,6 +274,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
        }
        if (api->match_protocol_num == 1 &&
            api->protocol[0].value != PROTOCOL_UDP &&
+           api->protocol[0].value != PROTOCOL_ICMP &&
            api->protocol[0].value != PROTOCOL_TCP) {
                if (BGP_DEBUG(pbr, PBR))
                        zlog_debug("BGP: match protocol operations:"
@@ -278,6 +294,32 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
                                   "too complex. ignoring.");
                return 0;
        }
+       if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) {
+               if (!bgp_pbr_extract_enumerate(api->icmp_type,
+                                              api->match_icmp_type_num)) {
+                       if (BGP_DEBUG(pbr, PBR))
+                               zlog_debug("BGP: match icmp type operations:"
+                                          "too complex. ignoring.");
+                       return 0;
+               }
+               enumerate_icmp = true;
+       }
+       if (!bgp_pbr_extract(api->icmp_code, api->match_icmp_code_num, NULL)) {
+               if (!bgp_pbr_extract_enumerate(api->icmp_code,
+                                         api->match_icmp_code_num)) {
+                       if (BGP_DEBUG(pbr, PBR))
+                               zlog_debug("BGP: match icmp code operations:"
+                                          "too complex. ignoring.");
+                       return 0;
+               } else if (api->match_icmp_type_num > 1 &&
+                          enumerate_icmp == false) {
+                       if (BGP_DEBUG(pbr, PBR))
+                               zlog_debug("BGP: match icmp code is enumerate"
+                                          ", and icmp type is not."
+                                          " too complex. ignoring.");
+                       return 0;
+               }
+       }
        if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) {
                if (BGP_DEBUG(pbr, PBR))
                        zlog_debug("BGP: match port operations:"
@@ -294,6 +336,14 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
                                 " too complex. ignoring.");
                return 0;
        }
+       if ((api->match_src_port_num || api->match_dst_port_num
+            || api->match_port_num) && (api->match_icmp_type_num
+                                        || api->match_icmp_code_num)) {
+               if (BGP_DEBUG(pbr, PBR))
+                       zlog_debug("BGP: match multiple port/imcp operations:"
+                                " too complex. ignoring.");
+               return 0;
+       }
        if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
            !(api->match_bitmask & PREFIX_DST_PRESENT)) {
                if (BGP_DEBUG(pbr, PBR)) {
@@ -984,7 +1034,7 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
                prefix_copy(&temp2.dst, dst);
        } else
                temp2.dst.family = AF_INET;
-       if (src_port) {
+       if (src_port && src_port->min_port) {
                temp.flags |= MATCH_PORT_SRC_SET;
                temp2.src_port_min = src_port->min_port;
                if (src_port->max_port) {
@@ -992,7 +1042,7 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
                        temp2.src_port_max = src_port->max_port;
                }
        }
-       if (dst_port) {
+       if (dst_port && dst_port->min_port) {
                temp.flags |= MATCH_PORT_DST_SET;
                temp2.dst_port_min = dst_port->min_port;
                if (dst_port->max_port) {
@@ -1207,6 +1257,183 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
 
 }
 
+static const struct message icmp_code_unreach_str[] = {
+       { 9, "communication-prohibited-by-filtering"},
+       { 10, "destination-host-prohibited"},
+       { 7, "destination-host-unknown"},
+       { 6, "destination-network-unknown"},
+       { 4, "fragmentation-needed"},
+       { 14, "host-precedence-violation"},
+       { 0, "network-unreachable"},
+       { 12, "network-unreachable-for-tos"},
+       { 3, "port-unreachable"},
+       { 8, "source-host-isolated"},
+       { 5, "source-route-failed"},
+       {0}
+};
+
+static const struct message icmp_code_redirect_str[] = {
+       { 1, "redirect-for-host"},
+       { 0, "redirect-for-network"},
+       { 3, "redirect-for-tos-and-host"},
+       { 2, "redirect-for-tos-and-net"},
+       {0}
+};
+
+static const struct message icmp_code_exceed_str[] = {
+       { 1, "ttl-eq-zero-during-reassembly"},
+       { 0, "ttl-eq-zero-during-transit"},
+       {0}
+};
+
+static const struct message icmp_code_problem_str[] = {
+       { 1, "required-option-missing"},
+       { 2, "ip-header-bad"},
+       {0}
+};
+static void  bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[],
+                                    int src_num,
+                                    struct bgp_pbr_match_val dst[],
+                                    int dst_num,
+                                    struct prefix *src_address,
+                                    struct prefix *dst_address,
+                                    uint8_t proto,
+                                    struct bgp *bgp,
+                                    struct bgp_info *binfo,
+                                    bool add,
+                                    vrf_id_t vrf_id,
+                                    struct nexthop *nh,
+                                    float *rate)
+{
+       int i = 0, j;
+       struct bgp_pbr_range_port srcp, dstp;
+
+       if (proto != IPPROTO_ICMP)
+               return;
+       /* combinatory forced. ignore icmp type / code combinatory */
+       if (src_num == 1 && dst_num == 1) {
+               srcp.max_port = 0;
+               dstp.max_port = 0;
+               srcp.min_port = src[0].value;
+               dstp.min_port = dst[0].value;
+               if (add)
+                       bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                        vrf_id, src_address,
+                                                        dst_address,
+                                                        nh, rate, proto,
+                                                        &srcp, &dstp);
+               else
+                       bgp_pbr_policyroute_remove_from_zebra(
+                                                             bgp,
+                                                             binfo,
+                                                             vrf_id,
+                                                             src_address,
+                                                             dst_address,
+                                                             proto,
+                                                             &srcp, &dstp);
+               return;
+       }
+       /* parse icmp type and lookup appropriate icmp code
+        * if no icmp code found, create as many entryes as
+        * there are listed icmp codes for that icmp type
+        */
+       for (i = 0; i < src_num; i++) {
+               const struct message *pnt;
+               const struct message *pnt_code = NULL;
+               static struct message nt = {0};
+               bool icmp_typecode_configured = false;
+
+               srcp.min_port = src[i].value;
+               srcp.max_port = 0;
+               dstp.max_port = 0;
+               if (src[i].value == 3)
+                       pnt_code = icmp_code_unreach_str;
+               else if (src[i].value == 5)
+                       pnt_code = icmp_code_redirect_str;
+               else if (src[i].value == 11)
+                       pnt_code = icmp_code_exceed_str;
+               else if (src[i].value == 12)
+                       pnt_code = icmp_code_problem_str;
+               switch (src[i].value) {
+               case 3:
+               case 5:
+               case 11:
+               case 12:
+                       for (j = 0; j < dst_num; j++) {
+                               for (pnt = pnt_code;
+                                    pnt && memcmp(pnt, &nt, sizeof(struct message));
+                                    pnt++) {
+                                       if (dst[i].value != pnt->key)
+                                               continue;
+                                       dstp.min_port = dst[i].value;
+                                       icmp_typecode_configured = true;
+                                       if (add)
+                                               bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                                                vrf_id, src_address,
+                                                                                dst_address,
+                                                                                nh, rate, proto,
+                                                                                &srcp, &dstp);
+                                       else
+                                               bgp_pbr_policyroute_remove_from_zebra(
+                                                                                     bgp,
+                                                                                     binfo,
+                                                                                     vrf_id,
+                                                                                     src_address,
+                                                                                     dst_address,
+                                                                                     proto,
+                                                                                     &srcp, &dstp);
+                               }
+                       }
+                       /* create a list of ICMP type/code combinatories */
+                       if (!icmp_typecode_configured) {
+                               for (pnt = pnt_code;
+                                    pnt && memcmp(pnt, &nt, sizeof(struct message));
+                                    pnt++) {
+                                       dstp.min_port = pnt->key;
+                                       if (add)
+                                               bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                                                vrf_id, src_address,
+                                                                                dst_address,
+                                                                                nh, rate, proto,
+                                                                                &srcp, &dstp);
+                                       else
+                                               bgp_pbr_policyroute_remove_from_zebra(
+                                                                                     bgp,
+                                                                                     binfo,
+                                                                                     vrf_id,
+                                                                                     src_address,
+                                                                                     dst_address,
+                                                                                     proto,
+                                                                                     &srcp, &dstp);
+                               }
+
+                       }
+                       break;
+               default:
+                       /* icmp type is not one of the above
+                        * forge an entry only based on the icmp type
+                        */
+                       dstp.min_port = 0;
+                       if (add)
+                               bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                                vrf_id, src_address,
+                                                                dst_address,
+                                                                nh, rate, proto,
+                                                                &srcp, &dstp);
+                       else
+                               bgp_pbr_policyroute_remove_from_zebra(
+                                                                     bgp,
+                                                                     binfo,
+                                                                     vrf_id,
+                                                                     src_address,
+                                                                     dst_address,
+                                                                     proto,
+                                                                     &srcp, &dstp);
+                       break;
+               }
+       }
+}
+
 static void bgp_pbr_handle_entry(struct bgp *bgp,
                                struct bgp_info *binfo,
                                struct bgp_pbr_entry_main *api,
@@ -1219,7 +1446,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
        struct prefix *src = NULL, *dst = NULL;
        uint8_t proto = 0;
        struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL;
-       struct bgp_pbr_range_port range;
+       struct bgp_pbr_range_port range, range_icmp_code;
+       bool enum_icmp = false;
 
        memset(&nh, 0, sizeof(struct nexthop));
        if (api->match_bitmask & PREFIX_SRC_PRESENT)
@@ -1251,10 +1479,48 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                dstp = &range;
                srcp = NULL;
        }
-       if (!add)
-               return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
-                                            api->vrf_id, src, dst,
-                                            proto, srcp, dstp);
+       if (api->match_icmp_type_num >= 1) {
+               proto = IPPROTO_ICMP;
+               if (bgp_pbr_extract_enumerate(api->icmp_type,
+                                             api->match_icmp_type_num))
+                       enum_icmp = true;
+               else {
+                       bgp_pbr_extract(api->icmp_type,
+                                       api->match_icmp_type_num,
+                                       &range);
+                       srcp = &range;
+               }
+       }
+       if (api->match_icmp_code_num >= 1) {
+               proto = IPPROTO_ICMP;
+               if (bgp_pbr_extract_enumerate(api->icmp_code,
+                                             api->match_icmp_code_num))
+                       enum_icmp = true;
+               else {
+                       bgp_pbr_extract(api->icmp_code,
+                                       api->match_icmp_code_num,
+                                       &range_icmp_code);
+                       dstp = &range_icmp_code;
+               }
+       }
+
+       if (!add) {
+               if (enum_icmp) {
+                       return bgp_pbr_enumerate_action_src_dst(api->icmp_type,
+                                               api->match_icmp_type_num,
+                                               api->icmp_code,
+                                               api->match_icmp_code_num,
+                                               src, dst, proto,
+                                               bgp, binfo, add,
+                                               api->vrf_id, NULL, NULL);
+               }
+               return bgp_pbr_policyroute_remove_from_zebra(bgp,
+                                                            binfo,
+                                                            api->vrf_id,
+                                                            src, dst,
+                                                            proto,
+                                                            srcp, dstp);
+       }
        /* no action for add = true */
        for (i = 0; i < api->action_num; i++) {
                switch (api->actions[i].action) {
@@ -1263,10 +1529,19 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                        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, proto,
-                                                   srcp, dstp);
+                               if (enum_icmp)
+                                       bgp_pbr_enumerate_action_src_dst(api->icmp_type,
+                                                                        api->match_icmp_type_num,
+                                                                        api->icmp_code,
+                                                                        api->match_icmp_code_num,
+                                                                        src, dst, proto,
+                                                                        bgp, binfo, add,
+                                                                        api->vrf_id, &nh, &rate);
+                               else
+                                       bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                                        api->vrf_id, src, dst,
+                                                                        &nh, &rate, proto,
+                                                                        srcp, dstp);
                        } else {
                                /* update rate. can be reentrant */
                                rate = api->actions[i].u.r.rate;
@@ -1306,11 +1581,20 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                        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, proto,
-                                                           srcp, dstp);
+                       if (enum_icmp)
+                               bgp_pbr_enumerate_action_src_dst(api->icmp_type,
+                                                                api->match_icmp_type_num,
+                                                                api->icmp_code,
+                                                                api->match_icmp_code_num,
+                                                                src, dst, proto,
+                                                                bgp, binfo, add,
+                                                                api->vrf_id, &nh, &rate);
+                       else
+                               bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+                                                                api->vrf_id,
+                                                                src, dst,
+                                                                &nh, &rate, proto,
+                                                                srcp, dstp);
                        /* XXX combination with REDIRECT_VRF
                         * + REDIRECT_NH_IP not done
                         */
@@ -1319,7 +1603,16 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                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,
+                       if (enum_icmp)
+                               bgp_pbr_enumerate_action_src_dst(api->icmp_type,
+                                                                api->match_icmp_type_num,
+                                                                api->icmp_code,
+                                                                api->match_icmp_code_num,
+                                                                src, dst, proto,
+                                                                bgp, binfo, add,
+                                                                api->vrf_id, &nh, &rate);
+                       else
+                               bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
                                                         api->vrf_id,
                                                         src, dst,
                                                         &nh, &rate, proto,
index 20edaf30b8a567fa5bf70a6f73e73375ef6cb276..e869d0106d6f6d24c76506d7057fef4af6c171ff 100644 (file)
@@ -125,6 +125,7 @@ struct bgp_pbr_entry_main {
 
 #define PROTOCOL_UDP 17
 #define PROTOCOL_TCP 6
+#define PROTOCOL_ICMP 1
        struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX];
        struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX];
        struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX];