]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: pbr support for port redirecting
authorPhilippe Guibert <philippe.guibert@6wind.com>
Fri, 30 Mar 2018 10:25:03 +0000 (12:25 +0200)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 31 May 2018 15:44:40 +0000 (17:44 +0200)
Ability for BGP FS to convert some rules containining at least one
address and one port information into a pbr_match_entry rule.

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

index 04d6314fd7282681e88a1443b8b1e77b83b92d37..de94c0e03e551ba827582d262bcbe7de528c7f1a 100644 (file)
@@ -169,7 +169,60 @@ static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
                _cnt++; \
        } while (0)
 
-/* return 1 if OK, 0 if validation should stop) */
+struct bgp_pbr_range_port {
+       uint16_t min_port;
+       uint16_t max_port;
+};
+
+/* return true if extraction ok
+ */
+static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
+                           int num,
+                           struct bgp_pbr_range_port *range)
+{
+       int i = 0;
+       bool exact_match = false;
+
+       if (range)
+               memset(range, 0, sizeof(struct bgp_pbr_range_port));
+
+       if (num > 2)
+               return false;
+       for (i = 0; i < num; i++) {
+               if (i != 0 && (list[i].compare_operator ==
+                              OPERATOR_COMPARE_EQUAL_TO))
+                       return false;
+               if (i == 0 && (list[i].compare_operator ==
+                              OPERATOR_COMPARE_EQUAL_TO)) {
+                       if (range)
+                               range->min_port = list[i].value;
+                       exact_match = true;
+               }
+               if (exact_match == true && i > 0)
+                       return false;
+               if (list[i].compare_operator ==
+                   (OPERATOR_COMPARE_GREATER_THAN +
+                    OPERATOR_COMPARE_EQUAL_TO)) {
+                       if (range)
+                               range->min_port = list[i].value;
+               } else if (list[i].compare_operator ==
+                          (OPERATOR_COMPARE_LESS_THAN +
+                           OPERATOR_COMPARE_EQUAL_TO)) {
+                       if (range)
+                               range->max_port = list[i].value;
+               } else if (list[i].compare_operator ==
+                          OPERATOR_COMPARE_LESS_THAN) {
+                       if (range)
+                               range->max_port = list[i].value - 1;
+               } else if (list[i].compare_operator ==
+                          OPERATOR_COMPARE_GREATER_THAN) {
+                       if (range)
+                               range->min_port = list[i].value + 1;
+               }
+       }
+       return true;
+}
+
 static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
 {
        /* because bgp pbr entry may contain unsupported
@@ -179,18 +232,63 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
         * - combination src/dst => redirect nexthop [ + rate]
         * - combination src/dst => redirect VRF [ + rate]
         * - combination src/dst => drop
+        * - combination srcport + @IP
         */
-       if (api->match_src_port_num || api->match_dst_port_num
-           || api->match_port_num || api->match_protocol_num
-           || api->match_icmp_type_num || api->match_icmp_type_num
+       if (api->match_icmp_type_num || api->match_icmp_type_num
            || api->match_packet_length_num || api->match_dscp_num
            || api->match_tcpflags_num) {
                if (BGP_DEBUG(pbr, PBR)) {
                        bgp_pbr_print_policy_route(api);
                        zlog_debug("BGP: some SET actions not supported by Zebra. ignoring.");
+                       zlog_debug("BGP: case icmp or length or dscp or tcp flags");
                }
                return 0;
        }
+
+       if (api->match_protocol_num > 1) {
+               if (BGP_DEBUG(pbr, PBR))
+                       zlog_debug("BGP: match protocol operations:"
+                                "multiple protocols ( %d). ignoring.",
+                                api->match_protocol_num);
+               return 0;
+       }
+       if (api->match_protocol_num == 1 &&
+           api->protocol[0].value != PROTOCOL_UDP &&
+           api->protocol[0].value != PROTOCOL_TCP) {
+               if (BGP_DEBUG(pbr, PBR))
+                       zlog_debug("BGP: match protocol operations:"
+                                  "protocol (%d) not supported. ignoring",
+                                  api->match_protocol_num);
+               return 0;
+       }
+       if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) {
+               if (BGP_DEBUG(pbr, PBR))
+                       zlog_debug("BGP: match src port operations:"
+                                  "too complex. ignoring.");
+               return 0;
+       }
+       if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) {
+               if (BGP_DEBUG(pbr, PBR))
+                       zlog_debug("BGP: match dst port operations:"
+                                  "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:"
+                                "too complex. ignoring.");
+               return 0;
+       }
+       /* no combinations with both src_port and dst_port
+        * or port with src_port and dst_port
+        */
+       if (api->match_src_port_num + api->match_dst_port_num +
+           api->match_port_num > 3) {
+               if (BGP_DEBUG(pbr, PBR))
+                       zlog_debug("BGP: match multiple port operations:"
+                                " too complex. ignoring.");
+               return 0;
+       }
        if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
            !(api->match_bitmask & PREFIX_DST_PRESENT)) {
                if (BGP_DEBUG(pbr, PBR)) {
@@ -447,6 +545,11 @@ uint32_t bgp_pbr_match_entry_hash_key(void *arg)
        pbme = (struct bgp_pbr_match_entry *)arg;
        key = prefix_hash_key(&pbme->src);
        key = jhash_1word(prefix_hash_key(&pbme->dst), key);
+       key = jhash(&pbme->dst_port_min, 2, key);
+       key = jhash(&pbme->src_port_min, 2, key);
+       key = jhash(&pbme->dst_port_max, 2, key);
+       key = jhash(&pbme->src_port_max, 2, key);
+       key = jhash(&pbme->proto, 1, key);
 
        return key;
 }
@@ -474,6 +577,21 @@ int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
        if (!prefix_same(&r1->dst, &r2->dst))
                return 0;
 
+       if (r1->src_port_min != r2->src_port_min)
+               return 0;
+
+       if (r1->dst_port_min != r2->dst_port_min)
+               return 0;
+
+       if (r1->src_port_max != r2->src_port_max)
+               return 0;
+
+       if (r1->dst_port_max != r2->dst_port_max)
+               return 0;
+
+       if (r1->proto != r2->proto)
+               return 0;
+
        return 1;
 }
 
@@ -813,10 +931,13 @@ static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
 }
 
 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_info *binfo,
+                                         vrf_id_t vrf_id,
+                                         struct prefix *src,
+                                         struct prefix *dst,
+                                         uint8_t protocol,
+                                         struct bgp_pbr_range_port *src_port,
+                                         struct bgp_pbr_range_port *dst_port)
 {
        struct bgp_pbr_match temp;
        struct bgp_pbr_match_entry temp2;
@@ -840,11 +961,35 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
                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 (src_port) {
+               temp.flags |= MATCH_PORT_SRC_SET;
+               temp2.src_port_min = src_port->min_port;
+               if (src_port->max_port) {
+                       temp.flags |= MATCH_PORT_SRC_RANGE_SET;
+                       temp2.src_port_max = src_port->max_port;
+               }
+       }
+       if (dst_port) {
+               temp.flags |= MATCH_PORT_DST_SET;
+               temp2.dst_port_min = dst_port->min_port;
+               if (dst_port->max_port) {
+                       temp.flags |= MATCH_PORT_DST_RANGE_SET;
+                       temp2.dst_port_max = dst_port->max_port;
+               }
+       }
+       temp2.proto = protocol;
+
+       if (src == NULL || dst == NULL) {
+               if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+                       temp.type = IPSET_NET_PORT;
+               else
+                       temp.type = IPSET_NET;
+       } else {
+               if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+                       temp.type = IPSET_NET_PORT_NET;
+               else
+                       temp.type = IPSET_NET_NET;
+       }
        if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
                temp.vrf_id = 0;
        else
@@ -870,12 +1015,15 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
 }
 
 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_info *binfo,
+                                    vrf_id_t vrf_id,
+                                    struct prefix *src,
+                                    struct prefix *dst,
+                                    struct nexthop *nh,
+                                    float *rate,
+                                    uint8_t protocol,
+                                    struct bgp_pbr_range_port *src_port,
+                                    struct bgp_pbr_range_port *dst_port)
 {
        struct bgp_pbr_match temp;
        struct bgp_pbr_match_entry temp2;
@@ -913,15 +1061,33 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
 
        /* then look for bpm */
        memset(&temp, 0, sizeof(temp));
-       if (src == NULL || dst == NULL)
-               temp.type = IPSET_NET;
-       else
-               temp.type = IPSET_NET_NET;
+       if (src == NULL || dst == NULL) {
+               if ((src_port && src_port->min_port) ||
+                   (dst_port && dst_port->min_port))
+                       temp.type = IPSET_NET_PORT;
+               else
+                       temp.type = IPSET_NET;
+       } else {
+               if ((src_port && src_port->min_port) ||
+                   (dst_port && dst_port->min_port))
+                       temp.type = IPSET_NET_PORT_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;
+
+       if (src_port && src_port->min_port)
+               temp.flags |= MATCH_PORT_SRC_SET;
+       if (dst_port && dst_port->min_port)
+               temp.flags |= MATCH_PORT_DST_SET;
+       if (src_port && src_port->max_port)
+               temp.flags |= MATCH_PORT_SRC_RANGE_SET;
+       if (dst_port && dst_port->max_port)
+               temp.flags |= MATCH_PORT_DST_RANGE_SET;
        temp.action = bpa;
        bpm = hash_get(bgp->pbr_match_hash, &temp,
                       bgp_pbr_match_alloc_intern);
@@ -953,9 +1119,14 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
                prefix_copy(&temp2.dst, dst);
        else
                temp2.dst.family = AF_INET;
+       temp2.src_port_min = src_port ? src_port->min_port : 0;
+       temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
+       temp2.src_port_max = src_port ? src_port->max_port : 0;
+       temp2.dst_port_max = dst_port ? dst_port->max_port : 0;
+       temp2.proto = protocol;
        if (bpm)
                bpme = hash_get(bpm->entry_hash, &temp2,
-                       bgp_pbr_match_entry_alloc_intern);
+                               bgp_pbr_match_entry_alloc_intern);
        if (bpme && bpme->unique == 0) {
                bpme->unique = ++bgp_pbr_match_entry_counter_unique;
                /* 0 value is forbidden */
@@ -1021,6 +1192,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
        int continue_loop = 1;
        float rate = 0;
        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;
 
        if (api->match_bitmask & PREFIX_SRC_PRESENT)
                src = &api->src_prefix;
@@ -1028,10 +1202,33 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                dst = &api->dst_prefix;
        memset(&nh, 0, sizeof(struct nexthop));
        nh.vrf_id = VRF_UNKNOWN;
-
+       if (api->match_protocol_num)
+               proto = (uint8_t)api->protocol[0].value;
+       /* if match_port is selected, then either src or dst port will be parsed
+        * but not both at the same time
+        */
+       if (api->match_port_num >= 1) {
+               bgp_pbr_extract(api->port,
+                               api->match_port_num,
+                               &range);
+               srcp = dstp = &range;
+       } else if (api->match_src_port_num >= 1) {
+               bgp_pbr_extract(api->src_port,
+                               api->match_src_port_num,
+                               &range);
+               srcp = &range;
+               dstp = NULL;
+       } else if (api->match_dst_port_num >= 1) {
+               bgp_pbr_extract(api->dst_port,
+                               api->match_dst_port_num,
+                               &range);
+               dstp = &range;
+               srcp = NULL;
+       }
        if (!add)
                return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
-                                            api->vrf_id, src, dst);
+                                            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) {
@@ -1042,7 +1239,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                                nh.type = NEXTHOP_TYPE_BLACKHOLE;
                                bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
                                                    api->vrf_id, src, dst,
-                                                   &nh, &rate);
+                                                   &nh, &rate, proto,
+                                                   srcp, dstp);
                        } else {
                                /* update rate. can be reentrant */
                                rate = api->actions[i].u.r.rate;
@@ -1085,7 +1283,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                        bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
                                                            api->vrf_id,
                                                            src, dst,
-                                                           &nh, &rate);
+                                                           &nh, &rate, proto,
+                                                           srcp, dstp);
                        /* XXX combination with REDIRECT_VRF
                         * + REDIRECT_NH_IP not done
                         */
@@ -1097,7 +1296,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
                        bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
                                                         api->vrf_id,
                                                         src, dst,
-                                                        &nh, &rate);
+                                                        &nh, &rate, proto,
+                                                        srcp, dstp);
                        continue_loop = 0;
                        break;
                case ACTION_MARKING:
index fd47ad6fbb34bb9df7645e6c827968a48f6879f0..212249295996183b0e7e932326b0d4c6b88f8662 100644 (file)
@@ -123,6 +123,8 @@ struct bgp_pbr_entry_main {
        struct prefix src_prefix;
        struct prefix dst_prefix;
 
+#define PROTOCOL_UDP 17
+#define PROTOCOL_TCP 6
        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];
@@ -157,6 +159,10 @@ struct bgp_pbr_match {
 
 #define MATCH_IP_SRC_SET               (1 << 0)
 #define MATCH_IP_DST_SET               (1 << 1)
+#define MATCH_PORT_SRC_SET             (1 << 2)
+#define MATCH_PORT_DST_SET             (1 << 3)
+#define MATCH_PORT_SRC_RANGE_SET       (1 << 4)
+#define MATCH_PORT_DST_RANGE_SET       (1 << 5)
        uint32_t flags;
 
        vrf_id_t vrf_id;