]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: zapi PBR common encode/decode
authorG. Paul Ziemba <paulz@labn.net>
Mon, 17 Jul 2023 16:31:06 +0000 (09:31 -0700)
committerG. Paul Ziemba <paulz@labn.net>
Thu, 20 Jul 2023 15:10:45 +0000 (08:10 -0700)
    bgpd, pbrd: use common pbr encoder
    zebra: use common pbr decoder
    tests: pbr_topo1: check more filter fields

    Purpose:
1. Reduce likelihood of zapi format mismatches when adding
   PBR fields due to multiple parallel encoder implementations
2. Encourage common PBR structure usage among various daemons
3. Reduce coding errors via explicit per-field enable flags

Signed-off-by: G. Paul Ziemba <paulz@labn.net>
bgpd/bgp_zebra.c
lib/pbr.h
lib/zclient.c
pbrd/pbr_zebra.c
tests/topotests/pbr_topo1/test_pbr_topo1.py
zebra/zapi_msg.c

index aa7ce6a8fbd3b4751680157dc92b7780ad1f3f9b..035481aaf2a9e882392a07c582e9d28da3abc5fb 100644 (file)
@@ -2712,17 +2712,20 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
                                       struct bgp_pbr_action *pbra,
                                       struct bgp_pbr_rule *pbr)
 {
-       struct prefix pfx;
        uint8_t fam = AF_INET;
-       char ifname[INTERFACE_NAMSIZ];
+       struct pbr_rule r;
 
        if (pbra->nh.type == NEXTHOP_TYPE_IPV6)
                fam = AF_INET6;
-       stream_putl(s, 0); /* seqno unused */
+
+       /*
+        * Convert to canonical form
+        */
+       memset(&r, 0, sizeof(r));
+       /* r.seq unused */
        if (pbr)
-               stream_putl(s, pbr->priority);
-       else
-               stream_putl(s, 0);
+               r.priority = pbr->priority;
+
        /* ruleno unused - priority change
         * ruleno permits distinguishing various FS PBR entries
         * - FS PBR entries based on ipset/iptables
@@ -2730,56 +2733,37 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
         * the latter may contain default routing information injected by FS
         */
        if (pbr)
-               stream_putl(s, pbr->unique);
+               r.unique = pbr->unique;
        else
-               stream_putl(s, pbra->unique);
+               r.unique = pbra->unique;
 
-       stream_putl(s, 0); /* filter_bm placeholder */
 
-       stream_putc(s, 0); /* ip protocol being used */
-       if (pbr && pbr->flags & MATCH_IP_SRC_SET)
-               memcpy(&pfx, &(pbr->src), sizeof(struct prefix));
-       else {
-               memset(&pfx, 0, sizeof(pfx));
-               pfx.family = fam;
-       }
-       stream_putc(s, pfx.family);
-       stream_putc(s, pfx.prefixlen);
-       stream_put(s, &pfx.u.prefix, prefix_blen(&pfx));
-
-       stream_putw(s, 0);  /* src port */
+       /* filter */
 
-       if (pbr && pbr->flags & MATCH_IP_DST_SET)
-               memcpy(&pfx, &(pbr->dst), sizeof(struct prefix));
-       else {
-               memset(&pfx, 0, sizeof(pfx));
-               pfx.family = fam;
+       if (pbr && pbr->flags & MATCH_IP_SRC_SET) {
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_IP);
+               r.filter.src_ip = pbr->src;
+       } else {
+               /* ??? */
+               r.filter.src_ip.family = fam;
+       }
+       if (pbr && pbr->flags & MATCH_IP_DST_SET) {
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_IP);
+               r.filter.dst_ip = pbr->dst;
+       } else {
+               /* ??? */
+               r.filter.dst_ip.family = fam;
+       }
+       /* src_port, dst_port, pcp, dsfield not used */
+       if (!pbr) {
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_FWMARK);
+               r.filter.fwmark = pbra->fwmark;
        }
-       stream_putc(s, pfx.family);
-       stream_putc(s, pfx.prefixlen);
-       stream_put(s, &pfx.u.prefix, prefix_blen(&pfx));
-
-       stream_putw(s, 0);  /* dst port */
-
-       stream_putc(s, 0); /* filter dsfield */
-       /* if pbr present, fwmark is not used */
-       if (pbr)
-               stream_putl(s, 0);
-       else
-               stream_putl(s, pbra->fwmark); /* filter fwmark */
-
-       stream_putc(s, 0); /* pcp filter */
-       stream_putw(s, 0); /* pcp action  */
-       stream_putw(s, 0); /* vlan_id filter */
-       stream_putw(s, 0); /* vlan_flags filter */
-       stream_putw(s, 0); /* vlan_id action */
-       stream_putw(s, 0); /* vlan_flags action */
-       stream_putl(s, 0); /* queue id action */
 
-       stream_putl(s, pbra->table_id); /* table action */
+       SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */
+       r.action.table = pbra->table_id;
 
-       memset(ifname, 0, sizeof(ifname));
-       stream_put(s, ifname, INTERFACE_NAMSIZ); /* ifname unused */
+       zapi_pbr_rule_encode(s, &r);
 }
 
 static void bgp_encode_pbr_ipset_match(struct stream *s,
@@ -3545,11 +3529,9 @@ void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra,
        zclient_create_header(s,
                              install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
                              VRF_DEFAULT);
-       stream_putl(s, 1); /* send one pbr action */
 
        bgp_encode_pbr_rule_action(s, pbra, pbr);
 
-       stream_putw_at(s, 0, stream_get_endp(s));
        if ((zclient_send_message(zclient) != ZCLIENT_SEND_FAILURE)
            && install) {
                if (!pbr)
index f4b6633812fa7b26a78608b1b8c7d264acb0bc7e..1a3d562ed9448028768f69870586ffc0c3c63597 100644 (file)
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -85,14 +85,23 @@ struct pbr_filter {
  * the user criteria may directly point to a table too.
  */
 struct pbr_action {
+       uint32_t flags;
+
+#define PBR_ACTION_TABLE      (1 << 0)
+#define PBR_ACTION_QUEUE_ID   (1 << 1)
+#define PBR_ACTION_PCP        (1 << 2)
+#define PBR_ACTION_VLAN_ID    (1 << 3)
+#define PBR_ACTION_VLAN_FLAGS (1 << 4)
+
+       uint32_t table;
+       uint32_t queue_id;
+
        /* VLAN */
        uint8_t pcp;
        uint16_t vlan_id;
        uint16_t vlan_flags;
 
-       uint32_t queue_id;
 
-       uint32_t table;
 };
 
 /*
@@ -146,8 +155,8 @@ struct pbr_rule {
 #define MATCH_FLOW_LABEL_SET           (1 << 12)
 #define MATCH_FLOW_LABEL_INVERSE_SET   (1 << 13)
 
-extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
-                               struct pbr_rule *zrule);
+extern int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r);
+extern bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r);
 
 #ifdef __cplusplus
 }
index 929a18a9537205d1ca574cc74e0af120ed148a95..c6d06ee6d6103838131be434237ed32c18cd9f48 100644 (file)
@@ -1611,30 +1611,105 @@ static void zapi_encode_prefix(struct stream *s, struct prefix *p,
        stream_put(s, &p->u.prefix, prefix_blen(p));
 }
 
-int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule)
+static bool zapi_decode_prefix(struct stream *s, struct prefix *p)
 {
-       stream_reset(s);
-       zclient_create_header(s, cmd, zrule->vrf_id);
+       STREAM_GETC(s, p->family);
+       STREAM_GETC(s, p->prefixlen);
+       STREAM_GET(&(p->u.prefix), s, prefix_blen(p));
+       return true;
+
+stream_failure:
+       return false;
+}
+
+/*
+ * Encode filter subsection of pbr_rule
+ */
+static void zapi_pbr_rule_filter_encode(struct stream *s, struct pbr_filter *f)
+{
+       assert(f->src_ip.family == f->dst_ip.family);
+       assert((AF_INET == f->src_ip.family) || (AF_INET6 == f->src_ip.family));
+
+       stream_putl(s, f->filter_bm);
+       stream_putc(s, f->ip_proto);
 
+       /* addresses */
+       zapi_encode_prefix(s, &f->src_ip, f->src_ip.family);
+       zapi_encode_prefix(s, &f->dst_ip, f->dst_ip.family);
+
+       /* port numbers */
+       stream_putw(s, f->src_port);
+       stream_putw(s, f->dst_port);
+
+       /* vlan */
+       stream_putc(s, f->pcp);
+       stream_putw(s, f->vlan_id);
+       stream_putw(s, f->vlan_flags);
+
+       stream_putc(s, f->dsfield);
+       stream_putl(s, f->fwmark);
+}
+
+static bool zapi_pbr_rule_filter_decode(struct stream *s, struct pbr_filter *f)
+{
+       STREAM_GETL(s, f->filter_bm);
+       STREAM_GETC(s, f->ip_proto);
+       if (!zapi_decode_prefix(s, &(f->src_ip)))
+               goto stream_failure;
+       if (!zapi_decode_prefix(s, &(f->dst_ip)))
+               goto stream_failure;
+       STREAM_GETW(s, f->src_port);
+       STREAM_GETW(s, f->dst_port);
+       STREAM_GETC(s, f->pcp);
+       STREAM_GETW(s, f->vlan_id);
+       STREAM_GETW(s, f->vlan_flags);
+       STREAM_GETC(s, f->dsfield);
+       STREAM_GETL(s, f->fwmark);
+       return true;
+
+stream_failure:
+       return false;
+}
+
+static void zapi_pbr_rule_action_encode(struct stream *s, struct pbr_action *a)
+{
+       stream_putl(s, a->flags);
+       stream_putl(s, a->table);
+       stream_putl(s, a->queue_id);
+       stream_putc(s, a->pcp);
+       stream_putw(s, a->vlan_id);
+       stream_putw(s, a->vlan_flags);
+}
+
+static bool zapi_pbr_rule_action_decode(struct stream *s, struct pbr_action *a)
+{
+       STREAM_GETL(s, a->flags);
+       STREAM_GETL(s, a->table);
+       STREAM_GETL(s, a->queue_id);
+       STREAM_GETC(s, a->pcp);
+       STREAM_GETW(s, a->vlan_id);
+       STREAM_GETW(s, a->vlan_flags);
+       return true;
+
+stream_failure:
+       return false;
+}
+
+int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r)
+{
        /*
-        * We are sending one item at a time at the moment
+        * PBR record count is always 1
         */
        stream_putl(s, 1);
 
-       stream_putl(s, zrule->seq);
-       stream_putl(s, zrule->priority);
-       stream_putl(s, zrule->unique);
+       stream_putl(s, r->seq);
+       stream_putl(s, r->priority);
+       stream_putl(s, r->unique);
 
-       zapi_encode_prefix(s, &(zrule->filter.src_ip),
-                          zrule->filter.src_ip.family);
-       stream_putw(s, zrule->filter.src_port); /* src port */
-       zapi_encode_prefix(s, &(zrule->filter.dst_ip),
-                          zrule->filter.src_ip.family);
-       stream_putw(s, zrule->filter.dst_port); /* dst port */
-       stream_putw(s, zrule->filter.fwmark);   /* fwmark */
+       zapi_pbr_rule_filter_encode(s, &(r->filter));
+       zapi_pbr_rule_action_encode(s, &(r->action));
 
-       stream_putl(s, zrule->action.table);
-       stream_put(s, zrule->ifname, INTERFACE_NAMSIZ);
+       stream_put(s, r->ifname, INTERFACE_NAMSIZ);
 
        /* Put length at the first point of the stream. */
        stream_putw_at(s, 0, stream_get_endp(s));
@@ -1642,6 +1717,28 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule)
        return 0;
 }
 
+bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r)
+{
+       /* NB caller has already read 4-byte rule count */
+
+       memset(r, 0, sizeof(*r));
+
+       STREAM_GETL(s, r->seq);
+       STREAM_GETL(s, r->priority);
+       STREAM_GETL(s, r->unique);
+
+       if (!zapi_pbr_rule_filter_decode(s, &(r->filter)))
+               goto stream_failure;
+       if (!zapi_pbr_rule_action_decode(s, &(r->action)))
+               goto stream_failure;
+
+       STREAM_GET(r->ifname, s, INTERFACE_NAMSIZ);
+       return true;
+
+stream_failure:
+       return false;
+}
+
 int zapi_tc_qdisc_encode(uint8_t cmd, struct stream *s, struct tc_qdisc *qdisc)
 {
        stream_reset(s);
index 28d89b0b5c37403c97e761724a8ec9ce956696da..f4bf287c5951be50c0b968bd16f7d636cfe41347 100644 (file)
@@ -483,27 +483,9 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg)
        }
 }
 
-static void pbr_encode_pbr_map_sequence_prefix(struct stream *s,
-                                              struct prefix *p,
-                                              unsigned char  family)
-{
-       struct prefix any;
-
-       if (!p) {
-               memset(&any, 0, sizeof(any));
-               any.family = family;
-               p = &any;
-       }
-
-       stream_putc(s, p->family);
-       stream_putc(s, p->prefixlen);
-       stream_put(s, &p->u.prefix, prefix_blen(p));
-}
 
-static void
-pbr_encode_pbr_map_sequence_vrf(struct stream *s,
-                               const struct pbr_map_sequence *pbrms,
-                               const struct interface *ifp)
+static uint32_t pbr_map_sequence_vrf(const struct pbr_map_sequence *pbrms,
+                                    const struct interface *ifp)
 {
        struct pbr_vrf *pbr_vrf;
 
@@ -514,66 +496,130 @@ pbr_encode_pbr_map_sequence_vrf(struct stream *s,
 
        if (!pbr_vrf) {
                DEBUGD(&pbr_dbg_zebra, "%s: VRF not found", __func__);
-               return;
+               return 0;
        }
 
-       stream_putl(s, pbr_vrf->vrf->data.l.table_id);
+       return pbr_vrf->vrf->data.l.table_id;
+
 }
 
+/*
+ * 230716 gpz note: it would be worthwhile for pbrd to represent
+ * its rules internally using the lib/pbr.h structures to help
+ * move toward a more common structure across pbrd, bgpd, and zebra.
+ */
 static bool pbr_encode_pbr_map_sequence(struct stream *s,
                                        struct pbr_map_sequence *pbrms,
                                        struct interface *ifp)
 {
-       unsigned char family;
 
+       struct pbr_rule r;
+       uint8_t family;
+
+       /*
+        * There seems to be some effort in pbr_vty.c to keep the three
+        * copies of "family" equal. Not sure if the reason goes beyond
+        * ensuring consistency in ZAPI encoding. In any case, it might
+        * be handled better as an internal matter for the encoder (TBD).
+        */
        family = AF_INET;
        if (pbrms->family)
                family = pbrms->family;
 
-       stream_putl(s, pbrms->seqno);
-       stream_putl(s, pbrms->ruleno);
-       stream_putl(s, pbrms->unique);
-
-       stream_putl(s, pbrms->filter_bm);
-
-       stream_putc(s, pbrms->ip_proto); /* The ip_proto */
-       pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
-       stream_putw(s, pbrms->src_prt);
-       pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
-       stream_putw(s, pbrms->dst_prt);
-       stream_putc(s, pbrms->dsfield);
-       stream_putl(s, pbrms->mark);
-       /* PCP */
-       if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP))
-               stream_putc(s, pbrms->match_pcp);
+       if (pbrms->src)
+               assert(family == pbrms->src->family);
+       if (pbrms->dst)
+               assert(family == pbrms->dst->family);
+
+       /*
+        * Convert struct pbr_map_sequence to canonical form
+        */
+       memset(&r, 0, sizeof(r));
+       r.seq = pbrms->seqno;
+       r.priority = pbrms->ruleno;
+       r.unique = pbrms->unique;
+
+       /* filter */
+       r.filter.filter_bm = pbrms->filter_bm;
+       if (pbrms->src)
+               r.filter.src_ip = *pbrms->src;
+       else
+               r.filter.src_ip.family = family;
+       if (pbrms->dst)
+               r.filter.dst_ip = *pbrms->dst;
        else
-               stream_putc(s, 0);
-       stream_putw(s, pbrms->action_pcp);
-       /* VLAN */
-       stream_putw(s, pbrms->match_vlan_id);
-       stream_putw(s, pbrms->match_vlan_flags);
+               r.filter.dst_ip.family = family;
+       r.filter.src_port = pbrms->src_prt;
+       r.filter.dst_port = pbrms->dst_prt;
+       r.filter.pcp = pbrms->match_pcp;
+       r.filter.vlan_id = pbrms->match_vlan_id;
+       r.filter.vlan_flags = pbrms->match_vlan_flags;
+       r.filter.dsfield = pbrms->dsfield;
+       r.filter.fwmark = pbrms->mark;
+       r.filter.ip_proto = pbrms->ip_proto;
 
-       stream_putw(s, pbrms->action_vlan_id);
-       stream_putw(s, pbrms->action_vlan_flags);
-       stream_putl(s, pbrms->action_queue_id);
+       /*
+        * Fix up filter flags for now, since PBRD doesn't maintain
+        * them yet (aside from PBR_FILTER_PCP)
+        */
+       if (!is_default_prefix(&r.filter.src_ip))
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_IP);
+       if (!is_default_prefix(&r.filter.dst_ip))
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_IP);
+       if (r.filter.src_port)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_PORT);
+       if (r.filter.dst_port)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_PORT);
+       if (r.filter.vlan_id)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_VLAN_ID);
+       if (r.filter.vlan_flags)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_VLAN_FLAGS);
+       if (r.filter.dsfield)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_DSFIELD);
+       if (r.filter.fwmark)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_FWMARK);
+       if (r.filter.ip_proto)
+               SET_FLAG(r.filter.filter_bm, PBR_FILTER_IP_PROTOCOL);
+
+       /* actions */
 
-       /* if the user does not use the command "set vrf name |unchanged"
-        * then pbr_encode_pbr_map_sequence_vrf will not be called
+       /*
+        * PBR should maintain its own set of action flags that we
+        * can copy here instead of trying to infer from magic values.
         */
+       SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */
+       if (PBR_MAP_UNDEFINED_QUEUE_ID != pbrms->action_queue_id)
+               SET_FLAG(r.action.flags, PBR_ACTION_QUEUE_ID);
+       if (0 != pbrms->action_pcp)
+               SET_FLAG(r.action.flags, PBR_ACTION_PCP);
+       if (0 != pbrms->action_vlan_id)
+               SET_FLAG(r.action.flags, PBR_ACTION_VLAN_ID);
+       if (0 != pbrms->action_vlan_flags)
+               SET_FLAG(r.action.flags, PBR_ACTION_VLAN_FLAGS);
 
-       /* these statement get a table id */
+       /*
+        * if the user does not use the command "set vrf name unchanged"
+        * then pbr_encode_pbr_map_sequence_vrf will not be called
+        */
        if (pbrms->vrf_unchanged || pbrms->vrf_lookup)
-               pbr_encode_pbr_map_sequence_vrf(s, pbrms, ifp);
+               r.action.table = pbr_map_sequence_vrf(pbrms, ifp);
        else if (pbrms->nhgrp_name)
-               stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name));
+               r.action.table = pbr_nht_get_table(pbrms->nhgrp_name);
        else if (pbrms->nhg)
-               stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name));
+               r.action.table = pbr_nht_get_table(pbrms->internal_nhg_name);
        else {
                /* Not valid for install without table */
                return false;
        }
 
-       stream_put(s, ifp->name, INTERFACE_NAMSIZ);
+       r.action.queue_id = pbrms->action_queue_id;
+       r.action.pcp = pbrms->action_pcp;
+       r.action.vlan_id = pbrms->action_vlan_id;
+       r.action.vlan_flags = pbrms->action_vlan_flags;
+
+       strlcpy(r.ifname, ifp->name, sizeof(r.ifname));
+
+       zapi_pbr_rule_encode(s, &r);
 
        return true;
 }
@@ -607,11 +653,6 @@ bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
                              install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
                              VRF_DEFAULT);
 
-       /*
-        * We are sending one item at a time at the moment
-        */
-       stream_putl(s, 1);
-
        DEBUGD(&pbr_dbg_zebra, "%s:    %s %s seq %u %d %s %u", __func__,
               install ? "Installing" : "Deleting", pbrm->name, pbrms->seqno,
               install, pmi->ifp->name, pmi->delete);
index 699e4489a38f97596903ed07e3779360fa92b136..ad096fa14eae041ef45d580aa0a56f6cf8c7e510 100644 (file)
@@ -181,8 +181,10 @@ def test_pbr_data():
 #
 
 #
-# tm: must-match pattern
-# tN: must Not match pattern
+# c:   command
+# cDN: omit default destination IP address (special case)
+# tm:  must-match pattern
+# tN:  must Not match pattern
 #
 # Note we are searching amid a bunch of other rules, so these elements
 # should be unique.
@@ -199,6 +201,27 @@ ftest = [
     {"c": "match vlan untagged", "tm": r"VLAN Flags Match: untagged$"},
     {"c": "match vlan untagged-or-zero", "tm": r"VLAN Flags Match: untagged-or-zero$"},
     {"c": "no match vlan tagged", "tN": r"VLAN Flags Match:"},
+
+    {"c": "match src-ip 37.49.22.0/24", "tm": r"SRC IP Match: 37.49.22.0/24$"},
+    {"c": "no match src-ip 37.49.22.0/24", "tN": r"SRC IP Match: 37.49.22.0/24$"},
+
+    {"c": "match dst-ip 38.41.29.0/25", "cDN": "foo", "tm": r"DST IP Match: 38.41.29.0/25$"},
+    {"c": "no match dst-ip 38.41.29.0/25", "tN": r"DST IP Match: 38.41.29.0/25$"},
+
+    {"c": "match src-port 117", "tm": r"SRC Port Match: 117$"},
+    {"c": "no match src-port 117", "tN": r"SRC Port Match: 117$"},
+
+    {"c": "match dst-port 119", "tm": r"DST Port Match: 119$"},
+    {"c": "no match dst-port 119", "tN": r"DST Port Match: 119$"},
+
+    {"c": "match dscp cs3", "tm": r"DSCP Match: 24$"},
+    {"c": "no match dscp cs3", "tN": r"DSCP Match: 24$"},
+
+    {"c": "match ecn 2", "tm": r"ECN Match: 2$"},
+    {"c": "no match ecn 2", "tN": r"ECN Match: 2$"},
+
+    {"c": "match mark 337", "tm": r"MARK Match: 337$"},
+    {"c": "no match mark 337", "tN": r"MARK Match: 337$"},
 ]
 
 
@@ -233,6 +256,9 @@ def test_pbr_fields():
 
     logger.info("Verifying PBR rule fields")
 
+    # uncomment for manual interaction
+    # tgen.cli()
+
     tag = "field"
 
     router_list = tgen.routers().values()
@@ -240,8 +266,12 @@ def test_pbr_fields():
         for t in ftest:
             # send field-setting command
             # always have a match dst-ip to satisfy rule non-empty check
-            vcmd = "c t\npbr-map ASAKUSA seq 100\n{}\nmatch dst-ip 9.9.9.9/32\nset nexthop-group A\nend\nend".format(
-                t["c"]
+            if "cDN" in t:
+                match_dstip = ""
+            else:
+                match_dstip = "match dst-ip 9.9.9.9/32\n"
+            vcmd = "c t\npbr-map ASAKUSA seq 100\n{}\n{}set nexthop-group A\nend\nend".format(
+                t["c"], match_dstip
             )
             router.vtysh_multicmd(vcmd)
 
index 928cb3f9b79bba2882bdf3d1defbee4f57a8a14f..a136fcf8aea3ea8097528d09ab76caac10d1cb85 100644 (file)
@@ -3184,7 +3184,6 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
        struct zebra_pbr_rule zpr;
        struct stream *s;
        uint32_t total, i;
-       char ifname[INTERFACE_NAMSIZ + 1] = {};
 
        s = msg;
        STREAM_GETL(s, total);
@@ -3194,68 +3193,11 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
 
                zpr.sock = client->sock;
                zpr.rule.vrf_id = hdr->vrf_id;
-               STREAM_GETL(s, zpr.rule.seq);
-               STREAM_GETL(s, zpr.rule.priority);
-               STREAM_GETL(s, zpr.rule.unique);
 
-               STREAM_GETL(s, zpr.rule.filter.filter_bm);
-
-               STREAM_GETC(s, zpr.rule.filter.ip_proto);
-               STREAM_GETC(s, zpr.rule.filter.src_ip.family);
-               STREAM_GETC(s, zpr.rule.filter.src_ip.prefixlen);
-               STREAM_GET(&zpr.rule.filter.src_ip.u.prefix, s,
-                          prefix_blen(&zpr.rule.filter.src_ip));
-               STREAM_GETW(s, zpr.rule.filter.src_port);
-               STREAM_GETC(s, zpr.rule.filter.dst_ip.family);
-               STREAM_GETC(s, zpr.rule.filter.dst_ip.prefixlen);
-               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_GETC(s, zpr.rule.filter.pcp);
-               STREAM_GETW(s, zpr.rule.action.pcp);
-               STREAM_GETW(s, zpr.rule.filter.vlan_id);
-               STREAM_GETW(s, zpr.rule.filter.vlan_flags);
-               STREAM_GETW(s, zpr.rule.action.vlan_id);
-               STREAM_GETW(s, zpr.rule.action.vlan_flags);
-               STREAM_GETL(s, zpr.rule.action.queue_id);
-
-               STREAM_GETL(s, zpr.rule.action.table);
-               STREAM_GET(ifname, s, INTERFACE_NAMSIZ);
-
-               strlcpy(zpr.ifname, ifname, sizeof(zpr.ifname));
-               strlcpy(zpr.rule.ifname, ifname, sizeof(zpr.rule.ifname));
-
-               if (!is_default_prefix(&zpr.rule.filter.src_ip))
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
-
-               if (!is_default_prefix(&zpr.rule.filter.dst_ip))
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
-
-               if (zpr.rule.filter.src_port)
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_PORT;
-
-               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.ip_proto)
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_IP_PROTOCOL;
-
-               if (zpr.rule.filter.fwmark)
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
-
-               /* NB PBR_FILTER_PCP should already be set by sender */
-
-               if (zpr.rule.filter.vlan_flags)
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_VLAN_FLAGS;
+               if (!zapi_pbr_rule_decode(s, &zpr.rule))
+                       goto stream_failure;
 
-               if (zpr.rule.filter.vlan_id)
-                       zpr.rule.filter.filter_bm |= PBR_FILTER_VLAN_ID;
+               strlcpy(zpr.ifname, zpr.rule.ifname, sizeof(zpr.ifname));
 
                if (!(zpr.rule.filter.src_ip.family == AF_INET
                      || zpr.rule.filter.src_ip.family == AF_INET6)) {