diff options
author | Russ White <russ@riw.us> | 2020-07-23 18:08:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-23 18:08:38 +0200 |
commit | a90f46738a6a8b0450b269b7e28090677ca27920 (patch) | |
tree | c511340f026dbfdc6376707cd97153699a61cb59 /pbrd | |
parent | Merge pull request #6243 from pguibert6WIND/flowspec_some_regression_seen (diff) | |
parent | tests: expand pbr-topo1 to cover DSCP & ECN (diff) | |
download | frr-a90f46738a6a8b0450b269b7e28090677ca27920.tar.xz frr-a90f46738a6a8b0450b269b7e28090677ca27920.zip |
Merge pull request #6730 from wesleycoakley/pbrd-dscp-ecn
DSCP / ECN-based PBR Matching
Diffstat (limited to 'pbrd')
-rw-r--r-- | pbrd/pbr_map.c | 55 | ||||
-rw-r--r-- | pbrd/pbr_map.h | 3 | ||||
-rw-r--r-- | pbrd/pbr_vty.c | 107 | ||||
-rw-r--r-- | pbrd/pbr_zebra.c | 1 |
4 files changed, 165 insertions, 1 deletions
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 0ef8432e5..10a75a9f5 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -444,6 +444,59 @@ static void pbr_map_add_interfaces(struct pbr_map *pbrm) } } +/* Decodes a standardized DSCP into its representative value */ +uint8_t pbr_map_decode_dscp_enum(const char *name) +{ + /* Standard Differentiated Services Field Codepoints */ + if (!strcmp(name, "cs0")) + return 0; + if (!strcmp(name, "cs1")) + return 8; + if (!strcmp(name, "cs2")) + return 16; + if (!strcmp(name, "cs3")) + return 24; + if (!strcmp(name, "cs4")) + return 32; + if (!strcmp(name, "cs5")) + return 40; + if (!strcmp(name, "cs6")) + return 48; + if (!strcmp(name, "cs7")) + return 56; + if (!strcmp(name, "af11")) + return 10; + if (!strcmp(name, "af12")) + return 12; + if (!strcmp(name, "af13")) + return 14; + if (!strcmp(name, "af21")) + return 18; + if (!strcmp(name, "af22")) + return 20; + if (!strcmp(name, "af23")) + return 22; + if (!strcmp(name, "af31")) + return 26; + if (!strcmp(name, "af32")) + return 28; + if (!strcmp(name, "af33")) + return 30; + if (!strcmp(name, "af41")) + return 34; + if (!strcmp(name, "af42")) + return 36; + if (!strcmp(name, "af43")) + return 38; + if (!strcmp(name, "ef")) + return 46; + if (!strcmp(name, "voice-admit")) + return 44; + + /* No match? Error out */ + return -1; +} + struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) { struct pbr_map *pbrm; @@ -547,7 +600,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; } diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 704f1a036..64c090d2e 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -89,6 +89,7 @@ struct pbr_map_sequence { */ struct prefix *src; struct prefix *dst; + uint8_t dsfield; uint32_t mark; /* @@ -168,6 +169,8 @@ extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp); extern void pbr_map_interface_delete(struct pbr_map *pbrm, struct interface *ifp); +extern uint8_t pbr_map_decode_dscp_enum(const char *name); + /* Update maps installed on interface */ extern void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index cd9096cbc..a73d885ea 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -183,6 +183,91 @@ 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 DSCP$dscp", + NO_STR + "Match the rest of the command\n" + "Match based on IP DSCP field\n" + "DSCP value (below 64) or standard codepoint name\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + char dscpname[100]; + uint8_t rawDscp; + + /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */ + bool isANumber = true; + for (int i = 0; i < (int)strlen(dscp); i++) { + /* Letters are not numbers */ + if (!isdigit(dscp[i])) + isANumber = false; + + /* Lowercase the dscp enum (if needed) */ + if (isupper(dscp[i])) + dscpname[i] = tolower(dscp[i]); + else + dscpname[i] = dscp[i]; + } + dscpname[strlen(dscp)] = '\0'; + + if (isANumber) { + /* dscp passed is a regular number */ + long dscpAsNum = strtol(dscp, NULL, 0); + + if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) { + /* Refuse to install on overflow */ + vty_out(vty, "dscp (%s) must be less than 64\n", dscp); + return CMD_WARNING_CONFIG_FAILED; + } + rawDscp = dscpAsNum; + } else { + /* check dscp if it is an enum like cs0 */ + rawDscp = pbr_map_decode_dscp_enum(dscpname); + if (rawDscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s\n", dscpname); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (!no) { + if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp) + return CMD_SUCCESS; + + /* Set the DSCP bits of the DSField */ + pbrms->dsfield = + (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 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 +644,12 @@ 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->mark) vty_out(vty, " MARK Match: %u\n", pbrms->mark); @@ -653,6 +744,12 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, prefix2str(pbrms->dst, buf, sizeof(buf))); if (pbrms->mark) json_object_int_add(jpbrm, "matchMark", pbrms->mark); + if (pbrms->dsfield & PBR_DSFIELD_DSCP) + json_object_int_add(jpbrm, "matchDscp", + (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); + if (pbrms->dsfield & PBR_DSFIELD_ECN) + json_object_int_add(jpbrm, "matchEcn", + pbrms->dsfield & PBR_DSFIELD_ECN); json_object_array_add(j, jpbrm); } @@ -946,6 +1043,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 +1131,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); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index de2a99e26..d0099a46e 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -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) |