pbrd: dscp interpret standard codepoints

Matching by dscp may now also be specified by its standard codepoint
(provided it has one), such as `cf0` or `af11`.

Signed-off-by: Wesley Coakley <wcoakley@nvidia.com>
This commit is contained in:
Wesley Coakley 2020-06-24 05:37:06 -04:00
parent 01f23affdb
commit 116b86bdb9
4 changed files with 101 additions and 8 deletions

View File

@ -123,11 +123,15 @@ end destination.
on another platform it will be denied. This mark translates to the on another platform it will be denied. This mark translates to the
underlying `ip rule .... fwmark XXXX` command. underlying `ip rule .... fwmark XXXX` command.
.. clicmd:: match dscp (0-63) .. clicmd:: match dscp (DSCP|0-63)
Match packets according to the specified differentiated services code point Match packets according to the specified differentiated services code point
(DSCP) in the IP header; if this value matches then forward the packet (DSCP) in the IP header; if this value matches then forward the packet
according to the nexthop(s) specified. according to the nexthop(s) specified. The passed DSCP value may also be a
standard name for a differentiated service code point like cs0 or af11.
You may only specify one dscp per route map sequence; to match on multiple
dscp values you will need to create several sequences, one for each value.
.. clicmd:: match ecn (0-3) .. clicmd:: match ecn (0-3)

View File

@ -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_sequence *pbrms_get(const char *name, uint32_t seqno)
{ {
struct pbr_map *pbrm; struct pbr_map *pbrm;

View File

@ -169,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, extern void pbr_map_interface_delete(struct pbr_map *pbrm,
struct interface *ifp); struct interface *ifp);
extern uint8_t pbr_map_decode_dscp_enum(const char *name);
/* Update maps installed on interface */ /* Update maps installed on interface */
extern void pbr_map_policy_interface_update(const struct interface *ifp, extern void pbr_map_policy_interface_update(const struct interface *ifp,
bool state_up); bool state_up);

View File

@ -184,21 +184,57 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
} }
DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd, DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
"[no] match dscp (0-63)$dscp", "[no] match dscp DSCP$dscp",
NO_STR NO_STR
"Match the rest of the command\n" "Match the rest of the command\n"
"Match based on IP DSCP field\n" "Match based on IP DSCP field\n"
"Differentiated Service Code Point\n") "DSCP value (below 64) or standard codepoint name\n")
{ {
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); 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 (!no) {
if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == dscp) if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp)
return CMD_SUCCESS; return CMD_SUCCESS;
/* Set the DSCP bits of the DSField */ /* Set the DSCP bits of the DSField */
pbrms->dsfield = pbrms->dsfield =
(pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (dscp << 2); (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2);
} else { } else {
pbrms->dsfield &= ~PBR_DSFIELD_DSCP; pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
} }
@ -614,8 +650,6 @@ static void vty_show_pbrms(struct vty *vty,
if (pbrms->dsfield & PBR_DSFIELD_ECN) if (pbrms->dsfield & PBR_DSFIELD_ECN)
vty_out(vty, " ECN Match: %u\n", vty_out(vty, " ECN Match: %u\n",
pbrms->dsfield & PBR_DSFIELD_ECN); pbrms->dsfield & PBR_DSFIELD_ECN);
if (pbrms->dsfield)
vty_out(vty, " DSField Match: %u\n", (pbrms->dsfield));
if (pbrms->mark) if (pbrms->mark)
vty_out(vty, " MARK Match: %u\n", pbrms->mark); vty_out(vty, " MARK Match: %u\n", pbrms->mark);