]> git.puffer.fish Git - mirror/frr.git/commitdiff
pbrd, zebra, lib: DSCP / ECN-based PBR Matching
authorWesley Coakley <wcoakley@nvidia.com>
Thu, 18 Jun 2020 00:20:05 +0000 (20:20 -0400)
committerWesley Coakley <wcoakley@nvidia.com>
Wed, 15 Jul 2020 16:59:36 +0000 (12:59 -0400)
Extend PBR maps to discriminate by Differentiated Services Code Point and / or
Explicit Congestion Notification fields. These fields are used in the IP header
for classifying network traffic.

      0     1     2     3     4     5     6     7
    +-----+-----+-----+-----+-----+-----+-----+-----+
    |          DS FIELD, DSCP           | ECN FIELD |
    +-----+-----+-----+-----+-----+-----+-----+-----+
      DSCP: differentiated services codepoint
      ECN:  Explicit Congestion Notification

Signed-off-by: Wesley Coakley <wcoakley@nvidia.com>
Signed-off-by: Saurav Kumar Paul <saurav@cumulusnetworks.com>
doc/user/pbr.rst
lib/pbr.h
pbrd/pbr_map.c
pbrd/pbr_map.h
pbrd/pbr_vty.c
pbrd/pbr_zebra.c
zebra/rule_netlink.c
zebra/zapi_msg.c
zebra/zebra_dplane.c
zebra/zebra_dplane.h
zebra/zebra_pbr.h

index 149949e863f0cca78da649bf94f0f52c4df4247e..a483f17c79fb20a1bd4808ef323d7cc53a5487b5 100644 (file)
@@ -123,6 +123,18 @@ end destination.
    on another platform it will be denied.  This mark translates to the
    underlying `ip rule .... fwmark XXXX` command.
 
+.. clicmd:: match dscp (0-63)
+
+   Match packets according to the specified differentiated services code point
+   (DSCP) in the IP header; if this value matches then forward the packet
+   according to the nexthop(s) specified.
+
+.. clicmd:: match ecn (0-3)
+
+   Match packets according to the specified explicit congestion notification
+   (ECN) field in the IP header; if this value matches then forward the packet
+   according to the nexthop(s) specified.
+
 .. clicmd:: set nexthop-group NAME
 
    Use the nexthop-group NAME as the place to forward packets when the match
index cf6ac41d326c7a091ef520741f5963c824412179..fd183d7115ac022a4836d729555006c9a4edf87d 100644 (file)
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -49,6 +49,10 @@ struct pbr_filter {
 #define PBR_FILTER_PROTO               (1 << 5)
 #define PBR_FILTER_SRC_PORT_RANGE      (1 << 6)
 #define PBR_FILTER_DST_PORT_RANGE      (1 << 7)
+#define PBR_FILTER_DSFIELD                     (1 << 8)
+
+#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
+#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
 
        /* Source and Destination IP address with masks. */
        struct prefix src_ip;
@@ -58,6 +62,9 @@ struct pbr_filter {
        uint16_t src_port;
        uint16_t dst_port;
 
+       /* Filter by Differentiated Services field  */
+       uint8_t dsfield; /* DSCP (6 bits) & ECN (2 bits) */
+
        /* Filter with fwmark */
        uint32_t fwmark;
 };
index edc3f1d8daf605d836b3c9f41045b0ddea66d5f0..3a3bea349f725290c5ca55f5f6f04725d61a0acf 100644 (file)
@@ -547,7 +547,7 @@ pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)
 
 static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms)
 {
-       if (!pbrms->src && !pbrms->dst && !pbrms->mark)
+       if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield)
                pbrms->reason |= PBR_MAP_INVALID_EMPTY;
 }
 
index 41f1703954fc149a7ff63d71612a687d693b5b5a..fb8c6bf032bd5cf009b06299ba9aea8ab0ed6c5d 100644 (file)
@@ -89,6 +89,7 @@ struct pbr_map_sequence {
         */
        struct prefix *src;
        struct prefix *dst;
+       uint8_t dsfield;
        uint32_t mark;
 
        /*
index cd9096cbc88620e5d54c5c5bcdb9a1ffffc084c3..9472503238ce47113a326c4751bad10aea4b74ee 100644 (file)
@@ -183,6 +183,55 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
        return CMD_SUCCESS;
 }
 
+DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
+      "[no] match dscp (0-63)$dscp",
+      NO_STR
+      "Match the rest of the command\n"
+      "Match based on IP DSCP field\n"
+      "Differentiated Service Code Point\n")
+{
+       struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+       if (!no) {
+               if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == dscp)
+                       return CMD_SUCCESS;
+
+               /* Set the DSCP bits of the DSField */
+               pbrms->dsfield =
+                       (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (dscp << 2);
+       } else {
+               pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
+       }
+
+       pbr_map_check(pbrms, true);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd,
+      "[no] match ecn (0-3)$ecn",
+      NO_STR
+      "Match the rest of the command\n"
+      "Match based on IP ECN field\n"
+      "Explicit Congestion Notification\n")
+{
+       struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+       if (!no) {
+               if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)
+                       return CMD_SUCCESS;
+
+               /* Set the ECN bits of the DSField */
+               pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
+       } else {
+               pbrms->dsfield &= ~PBR_DSFIELD_ECN;
+       }
+
+       pbr_map_check(pbrms, true);
+
+       return CMD_SUCCESS;
+}
+
 DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
        "[no] match mark (1-4294967295)$mark",
        NO_STR
@@ -559,6 +608,14 @@ static void vty_show_pbrms(struct vty *vty,
        if (pbrms->dst)
                vty_out(vty, "        DST Match: %s\n",
                        prefix2str(pbrms->dst, buf, sizeof(buf)));
+       if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+               vty_out(vty, "        DSCP Match: %u\n",
+                       (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+       if (pbrms->dsfield & PBR_DSFIELD_ECN)
+               vty_out(vty, "        ECN Match: %u\n",
+                       pbrms->dsfield & PBR_DSFIELD_ECN);
+       if (pbrms->dsfield)
+               vty_out(vty, "        DSField Match: %u\n", (pbrms->dsfield));
        if (pbrms->mark)
                vty_out(vty, "        MARK Match: %u\n", pbrms->mark);
 
@@ -946,6 +1003,14 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
                vty_out(vty, " match dst-ip %s\n",
                        prefix2str(pbrms->dst, buff, sizeof(buff)));
 
+       if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+               vty_out(vty, " match dscp %u\n",
+                       (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+
+       if (pbrms->dsfield & PBR_DSFIELD_ECN)
+               vty_out(vty, " match ecn %u\n",
+                       pbrms->dsfield & PBR_DSFIELD_ECN);
+
        if (pbrms->mark)
                vty_out(vty, " match mark %u\n", pbrms->mark);
 
@@ -1026,6 +1091,8 @@ void pbr_vty_init(void)
        install_element(INTERFACE_NODE, &pbr_policy_cmd);
        install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
        install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
+       install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
+       install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd);
        install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
        install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
        install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
index de2a99e2696fb0de507c6e8a0f10424238787e74..d0099a46e3244685f80434ad3706b67cdd72f194 100644 (file)
@@ -536,6 +536,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
        stream_putw(s, 0);  /* src port */
        pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
        stream_putw(s, 0);  /* dst port */
+       stream_putc(s, pbrms->dsfield);
        stream_putl(s, pbrms->mark);
 
        if (pbrms->vrf_unchanged || pbrms->vrf_lookup)
index b7be3985069fcb51e59289b5ce6f4b5bab5b8319..f0cfe64b0f6a1d4f45fc78979fde9c7169370ee9 100644 (file)
  * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
  * or the number of bytes written to buf.
  */
-static ssize_t netlink_rule_msg_encode(
-       int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
-       uint32_t priority, uint32_t table, const struct prefix *src_ip,
-       const struct prefix *dst_ip, uint32_t fwmark, void *buf, size_t buflen)
+static ssize_t
+netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
+                       uint32_t filter_bm, uint32_t priority, uint32_t table,
+                       const struct prefix *src_ip,
+                       const struct prefix *dst_ip, uint32_t fwmark,
+                       uint8_t dsfield, void *buf, size_t buflen)
 {
        uint8_t protocol = RTPROT_ZEBRA;
        int family;
@@ -122,6 +124,10 @@ static ssize_t netlink_rule_msg_encode(
                        return 0;
        }
 
+       /* dsfield, if specified */
+       if (filter_bm & PBR_FILTER_DSFIELD)
+               req->frh.tos = dsfield;
+
        /* Route table to use to forward, if filter criteria matches. */
        if (table < 256)
                req->frh.table = table;
@@ -145,16 +151,15 @@ static ssize_t netlink_rule_msg_encode(
 /* Install or uninstall specified rule for a specific interface.
  * Form netlink message and ship it.
  */
-static int
-netlink_rule_update_internal(int cmd, const struct zebra_dplane_ctx *ctx,
-                            uint32_t filter_bm, uint32_t priority,
-                            uint32_t table, const struct prefix *src_ip,
-                            const struct prefix *dst_ip, uint32_t fwmark)
+static int netlink_rule_update_internal(
+       int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
+       uint32_t priority, uint32_t table, const struct prefix *src_ip,
+       const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield)
 {
        char buf[NL_PKT_BUF_SIZE];
 
        netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip,
-                               dst_ip, fwmark, buf, sizeof(buf));
+                               dst_ip, fwmark, dsfield, buf, sizeof(buf));
        return netlink_talk_info(netlink_talk_filter, (void *)&buf,
                                 dplane_ctx_get_ns(ctx), 0);
 }
@@ -188,7 +193,8 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
                dplane_ctx_rule_get_priority(ctx),
                dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
                dplane_ctx_rule_get_dst_ip(ctx),
-               dplane_ctx_rule_get_fwmark(ctx));
+               dplane_ctx_rule_get_fwmark(ctx),
+               dplane_ctx_rule_get_dsfield(ctx));
 
        /**
         * Delete the old one.
@@ -203,7 +209,8 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
                        dplane_ctx_rule_get_old_table(ctx),
                        dplane_ctx_rule_get_old_src_ip(ctx),
                        dplane_ctx_rule_get_old_dst_ip(ctx),
-                       dplane_ctx_rule_get_old_fwmark(ctx));
+                       dplane_ctx_rule_get_old_fwmark(ctx),
+                       dplane_ctx_rule_get_old_dsfield(ctx));
 
 
        return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
index dc7c595d26133c7db9031753ae9bd6418b768f71..daf08e96a0a08873261581fa6aaa302c6dff6f0f 100644 (file)
@@ -2526,6 +2526,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
                STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s,
                           prefix_blen(&zpr.rule.filter.dst_ip));
                STREAM_GETW(s, zpr.rule.filter.dst_port);
+               STREAM_GETC(s, zpr.rule.filter.dsfield);
                STREAM_GETL(s, zpr.rule.filter.fwmark);
                STREAM_GETL(s, zpr.rule.action.table);
                STREAM_GETL(s, zpr.rule.ifindex);
@@ -2556,6 +2557,9 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
                if (zpr.rule.filter.dst_port)
                        zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT;
 
+               if (zpr.rule.filter.dsfield)
+                       zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD;
+
                if (zpr.rule.filter.fwmark)
                        zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
 
index e34b6f23ffeee8e92e210106d8dd6eed65242208..7657ae8dc549feeba8eaf2bf346c64ca55ff8c52 100644 (file)
@@ -204,6 +204,7 @@ struct dplane_ctx_rule {
        /* Filter criteria */
        uint32_t filter_bm;
        uint32_t fwmark;
+       uint8_t dsfield;
        struct prefix src_ip;
        struct prefix dst_ip;
 };
@@ -1676,6 +1677,20 @@ uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx)
        return ctx->u.rule.old.fwmark;
 }
 
+uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.rule.new.dsfield;
+}
+
+uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.rule.old.dsfield;
+}
+
 const struct prefix *
 dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx)
 {
@@ -2129,6 +2144,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
 
        dplane_rule->filter_bm = rule->rule.filter.filter_bm;
        dplane_rule->fwmark = rule->rule.filter.fwmark;
+       dplane_rule->dsfield = rule->rule.filter.dsfield;
        prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
        prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
 }
index 8e873886df42bc61f6da97bbb6d8927b7cc6b7de..0fa21f620d42ae750ebd4ad191ceb99913eb1870 100644 (file)
@@ -412,6 +412,8 @@ uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx);
 uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx);
 uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx);
 uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx);
 const struct prefix *
 dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx);
 const struct prefix *
index 4bc0f400376d7ec9d67d007e0b06e5cca8e09411..dd2d7a190d87b4a2cb1eee867369f9e5b32fc3eb 100644 (file)
@@ -54,6 +54,8 @@ struct zebra_pbr_rule {
        (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT)
 #define IS_RULE_FILTERING_ON_DST_PORT(r) \
        (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT)
+#define IS_RULE_FILTERING_ON_DSFIELD(r) \
+       (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD)
 #define IS_RULE_FILTERING_ON_FWMARK(r) \
        (r->rule.filter.filter_bm & PBR_FILTER_FWMARK)