diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index ea7168f749..21ccbebe67 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2760,6 +2760,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, else r.unique = pbra->unique; + r.family = fam; /* filter */ diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 17f3504898..d0513018e3 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -161,6 +161,26 @@ specified in the rule are also applied to the packet. VLAN-matching facilities, so this field will be ignored unless other dataplane providers are used. +.. clicmd:: set nexthop-group NAME + + Action: + forward the packet using nexthop-group NAME. + +.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX|blackhole] [interface] [nexthop-vrf NAME] + + Action: + forward the packet using the specified single nexthop. + If `blackhole`, packets will be sent to a blackhole route and dropped. + +.. clicmd:: set vrf unchanged|NAME + + Action: + If set to ``unchanged``, the rule will use the vrf table the interface + is in as its lookup. + If set to NAME, the rule will use that vrf table as its lookup. + + Not supported with NETNS VRF backend. + .. clicmd:: set queue-id (1-65535) Action: @@ -195,24 +215,57 @@ specified in the rule are also applied to the packet. so this field will be ignored unless another dataplane provider is used. It is invalid to specify both a `strip` and `set vlan` action. -.. clicmd:: set nexthop-group NAME +.. clicmd:: set src-ip [A.B.C.D/M|X:X::X:X/M] Action: - forward the packet using nexthop-group NAME. + Set the source IP address of matched packets, possibly using a mask `M`. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. -.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] +.. clicmd:: set dst-ip [A.B.C.D/M|X:X::X:X/M] Action: - forward the packet using the specified single nexthop. + set the destination IP address of matched packets, possibly using a mask + `M`. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. -.. clicmd:: set vrf unchanged|NAME +.. clicmd:: set src-port (1-65535) Action: - If set to ``unchanged``, the rule will use the vrf table the interface - is in as its lookup. - If set to NAME, the rule will use that vrf table as its lookup. + set the source port of matched packets. Note that this action only makes + sense with layer 4 protocols that use ports, such as TCP, UDP, and SCTP. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. - Not supported with NETNS VRF backend. +.. clicmd:: set dst-port (1-65535) + + Action: + set the destination port of matched packets. Note that this action only + makes sense with layer 4 protocols that use ports, such as TCP, UDP, and + SCTP. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set dscp DSCP + + Action: + set the differentiated services code point (DSCP) of matched packets. + The Linux Kernel dataplane provider does not currently support + this action, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set ecn (0-3) + + Action: + set the explicit congestion notification (ECN) of matched packets. + The Linux Kernel dataplane provider does not currently support + this action, + so this field will be ignored unless another dataplane provider is used. .. clicmd:: show pbr map [NAME] [detail|json] diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 0613fc6736..25370eba48 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -1047,6 +1047,7 @@ void nexthop_group_write_nexthop_simple(struct vty *vty, vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname); break; case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, "%s", "drop"); break; } } diff --git a/lib/pbr.h b/lib/pbr.h index 1a3d562ed9..c514cc2a65 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -34,17 +34,18 @@ struct pbr_filter { #define PBR_FILTER_SRC_PORT (1 << 2) #define PBR_FILTER_DST_PORT (1 << 3) #define PBR_FILTER_FWMARK (1 << 4) -#define PBR_FILTER_PROTO (1 << 5) +#define PBR_FILTER_IP_PROTOCOL (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_FILTER_IP_PROTOCOL (1 << 9) +#define PBR_FILTER_DSCP (1 << 8) +#define PBR_FILTER_ECN (1 << 9) #define PBR_FILTER_PCP (1 << 10) #define PBR_FILTER_VLAN_FLAGS (1 << 11) #define PBR_FILTER_VLAN_ID (1 << 12) #define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */ #define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */ + #define PBR_PCP (0x07) /* 3-bit value 0..7 for prioritization*/ #define PBR_VLAN_FLAGS_NO_WILD 0 @@ -56,7 +57,7 @@ struct pbr_filter { struct prefix src_ip; struct prefix dst_ip; - /* Source and Destination higher-layer (TCP/UDP) port numbers */ + /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */ uint16_t src_port; uint16_t dst_port; @@ -87,11 +88,17 @@ struct pbr_filter { 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) +#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_STRIP_INNER_ANY (1 << 4) +#define PBR_ACTION_SRC_IP (1 << 5) +#define PBR_ACTION_DST_IP (1 << 6) +#define PBR_ACTION_SRC_PORT (1 << 7) +#define PBR_ACTION_DST_PORT (1 << 8) +#define PBR_ACTION_DSCP (1 << 9) +#define PBR_ACTION_ECN (1 << 10) uint32_t table; uint32_t queue_id; @@ -99,9 +106,18 @@ struct pbr_action { /* VLAN */ uint8_t pcp; uint16_t vlan_id; - uint16_t vlan_flags; + /* Source and Destination IP addresses */ + union sockunion src_ip; + union sockunion dst_ip; + /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */ + uint32_t src_port; + uint32_t dst_port; + + /* Differentiated Services field */ + uint8_t dscp; /* stored here already shifted to upper 6 bits */ + uint8_t ecn; /* stored here as lower 2 bits */ }; /* @@ -113,6 +129,7 @@ struct pbr_action { */ struct pbr_rule { vrf_id_t vrf_id; + uint8_t family; /* netlink: select which rule database */ uint32_t seq; uint32_t priority; diff --git a/lib/zclient.c b/lib/zclient.c index 294a78feb0..e40725826a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1622,6 +1622,47 @@ stream_failure: return false; } +static void zapi_encode_sockunion(struct stream *s, const union sockunion *su) +{ + int family = sockunion_family(su); + size_t addrlen = family2addrsize(family); + + /* + * Must know length to encode + */ + assert(addrlen); + + stream_putc(s, (uint8_t)family); + + stream_write(s, sockunion_get_addr(su), addrlen); +} + +static bool zapi_decode_sockunion(struct stream *s, union sockunion *su) +{ + uint8_t family; + size_t addrlen; + uint8_t buf[sizeof(union sockunion)]; + + memset(su, 0, sizeof(*su)); + + STREAM_GETC(s, family); + sockunion_family(su) = family; + + addrlen = family2addrsize(family); + if (!addrlen) + return false; + + if (addrlen > sizeof(buf)) + return false; + + STREAM_GET(buf, s, addrlen); + sockunion_set(su, family, buf, addrlen); + return true; + +stream_failure: + return false; +} + /* * Encode filter subsection of pbr_rule */ @@ -1631,40 +1672,79 @@ static void zapi_pbr_rule_filter_encode(struct stream *s, struct pbr_filter *f) assert((f->src_ip.family == AF_INET) || (f->src_ip.family == AF_INET6)); stream_putl(s, f->filter_bm); - stream_putc(s, f->ip_proto); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL)) + 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); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP)) + zapi_encode_prefix(s, &f->src_ip, f->src_ip.family); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP)) + 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); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT)) + stream_putw(s, f->src_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT)) + stream_putw(s, f->dst_port); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP)) + stream_putc(s, f->dsfield & PBR_DSFIELD_DSCP); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN)) + stream_putc(s, f->dsfield & PBR_DSFIELD_ECN); /* vlan */ - stream_putc(s, f->pcp); - stream_putw(s, f->vlan_id); - stream_putw(s, f->vlan_flags); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP)) + stream_putc(s, f->pcp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID)) + stream_putw(s, f->vlan_id); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS)) + stream_putw(s, f->vlan_flags); - stream_putc(s, f->dsfield); - stream_putl(s, f->fwmark); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK)) + stream_putl(s, f->fwmark); } static bool zapi_pbr_rule_filter_decode(struct stream *s, struct pbr_filter *f) { + uint8_t dscp = 0; + uint8_t ecn = 0; + 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); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL)) + STREAM_GETC(s, f->ip_proto); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP)) + if (!zapi_decode_prefix(s, &(f->src_ip))) + goto stream_failure; + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP)) + if (!zapi_decode_prefix(s, &(f->dst_ip))) + goto stream_failure; + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT)) + STREAM_GETW(s, f->src_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT)) + STREAM_GETW(s, f->dst_port); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP)) + STREAM_GETC(s, dscp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN)) + STREAM_GETC(s, ecn); + f->dsfield = (dscp & PBR_DSFIELD_DSCP) | (ecn & PBR_DSFIELD_ECN); + + /* vlan */ + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP)) + STREAM_GETC(s, f->pcp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID)) + STREAM_GETW(s, f->vlan_id); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS)) + STREAM_GETW(s, f->vlan_flags); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK)) + STREAM_GETL(s, f->fwmark); + return true; stream_failure: @@ -1674,21 +1754,72 @@ stream_failure: 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); + + if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE)) + stream_putl(s, a->table); + if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) + stream_putl(s, a->queue_id); + + /* L3 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) + zapi_encode_sockunion(s, &a->src_ip); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP)) + zapi_encode_sockunion(s, &a->dst_ip); + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT)) + stream_putw(s, a->src_port); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT)) + stream_putw(s, a->dst_port); + + if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) + stream_putc(s, a->dscp & PBR_DSFIELD_DSCP); + if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) + stream_putc(s, a->ecn & PBR_DSFIELD_ECN); + + /* L2 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) + stream_putc(s, a->pcp); + if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID)) + stream_putw(s, a->vlan_id); } 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); + + if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE)) + STREAM_GETL(s, a->table); + if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) + STREAM_GETL(s, a->queue_id); + + /* L3 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) { + if (!zapi_decode_sockunion(s, &(a->src_ip))) + goto stream_failure; + } + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP)) + if (!zapi_decode_sockunion(s, &(a->dst_ip))) + goto stream_failure; + + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT)) + STREAM_GETW(s, a->src_port); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT)) + STREAM_GETW(s, a->dst_port); + + if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) { + STREAM_GETC(s, a->dscp); + a->dscp &= PBR_DSFIELD_DSCP; + } + if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) { + STREAM_GETC(s, a->ecn); + a->ecn &= PBR_DSFIELD_ECN; + } + + /* L2 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) + STREAM_GETC(s, a->pcp); + if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID)) + STREAM_GETW(s, a->vlan_id); + return true; stream_failure: @@ -1702,6 +1833,7 @@ int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r) */ stream_putl(s, 1); + stream_putc(s, r->family); stream_putl(s, r->seq); stream_putl(s, r->priority); stream_putl(s, r->unique); @@ -1723,6 +1855,7 @@ bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r) memset(r, 0, sizeof(*r)); + STREAM_GETC(s, r->family); STREAM_GETL(s, r->seq); STREAM_GETL(s, r->priority); STREAM_GETL(s, r->unique); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 758e08b565..6b81e1058e 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -105,38 +105,6 @@ static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms, return false; } -void pbr_set_match_clause_for_pcp(struct pbr_map_sequence *pbrms, bool set, - uint8_t pcp) -{ - bool changed = false; - - if (set) { - if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) || - (pcp != pbrms->match_pcp)) { - SET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP); - pbrms->match_pcp = pcp; - changed = true; - } - } else { - if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) { - UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP); - changed = true; - } - } - if (changed) - pbr_map_check(pbrms, true); -} - -void pbr_set_match_clause_for_vlan(struct pbr_map_sequence *pbrms, - uint16_t vlan_id, uint16_t vlan_flags) -{ - if (pbrms) { - pbrms->match_vlan_id = vlan_id; - pbrms->match_vlan_flags = vlan_flags; - pbr_map_check(pbrms, true); - } -} - /* If any sequence is installed on the interface, assume installed */ static bool pbr_map_interface_is_installed(const struct pbr_map *pbrm, @@ -562,14 +530,6 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) pbrms->ruleno = pbr_nht_get_next_rule(seqno); pbrms->parent = pbrm; - pbrms->match_vlan_id = 0; - pbrms->match_vlan_flags = 0; - pbrms->match_pcp = 0; - - pbrms->action_vlan_id = 0; - pbrms->action_vlan_flags = 0; - pbrms->action_pcp = 0; - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; pbrms->reason = @@ -634,13 +594,42 @@ 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 && !pbrms->dsfield && - !CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) && - !pbrms->action_pcp && !pbrms->match_vlan_id && - !pbrms->match_vlan_flags && !pbrms->action_vlan_id && - !pbrms->action_vlan_flags && - pbrms->action_queue_id == PBR_MAP_UNDEFINED_QUEUE_ID) + /* clang-format off */ + if ( + !CHECK_FLAG(pbrms->filter_bm, ( + PBR_FILTER_SRC_IP | + PBR_FILTER_DST_IP | + PBR_FILTER_SRC_PORT | + PBR_FILTER_DST_PORT | + + PBR_FILTER_IP_PROTOCOL | + PBR_FILTER_DSCP | + PBR_FILTER_ECN | + + PBR_FILTER_FWMARK | + PBR_FILTER_PCP | + PBR_FILTER_VLAN_ID | + PBR_FILTER_VLAN_FLAGS + )) && + !CHECK_FLAG(pbrms->action_bm, ( + PBR_ACTION_SRC_IP | + PBR_ACTION_DST_IP | + PBR_ACTION_SRC_PORT | + PBR_ACTION_DST_PORT | + + PBR_ACTION_DSCP | + PBR_ACTION_ECN | + + PBR_ACTION_PCP | + PBR_ACTION_VLAN_ID | + PBR_ACTION_VLAN_STRIP_INNER_ANY | + + PBR_ACTION_QUEUE_ID + )) + ) { pbrms->reason |= PBR_MAP_INVALID_EMPTY; + } + /* clang-format on */ } static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms) @@ -653,7 +642,8 @@ static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms) * The strip vlan action removes any inner tag, so it is invalid to * specify both a set and strip action. */ - if ((pbrms->action_vlan_id != 0) && (pbrms->action_vlan_flags != 0)) + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) && + (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))) pbrms->reason |= PBR_MAP_INVALID_SET_STRIP_VLAN; } diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 3afb199565..61577e4076 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -3,7 +3,9 @@ * PBR-map Header * Copyright (C) 2018 Cumulus Networks, Inc. * Donald Sharp - * Copyright (c) 2023 LabN Consulting, L.L.C. + * Portions: + * Copyright (c) 2023 LabN Consulting, L.L.C. + * Copyright (c) 2021 The MITRE Corporation */ #ifndef __PBR_MAP_H__ #define __PBR_MAP_H__ @@ -54,6 +56,14 @@ struct pbr_map_interface { bool delete; }; +enum pbr_forwarding_type { + PBR_FT_UNSPEC = 0, + PBR_FT_VRF_UNCHANGED, + PBR_FT_SETVRF, + PBR_FT_NEXTHOP_GROUP, + PBR_FT_NEXTHOP_SINGLE, +}; + struct pbr_map_sequence { struct pbr_map *parent; @@ -109,14 +119,28 @@ struct pbr_map_sequence { * Action fields *****************************************************************/ + /* + * same bit definitions as in lib/pbr.h + */ + uint32_t action_bm; + + union sockunion action_src; + union sockunion action_dst; + + uint16_t action_src_port; + uint16_t action_dst_port; + + uint8_t action_dscp; + uint8_t action_ecn; + uint8_t action_pcp; uint8_t action_vlan_id; -#define PBR_MAP_STRIP_INNER_ANY (1 << 0) - uint8_t action_vlan_flags; #define PBR_MAP_UNDEFINED_QUEUE_ID 0 uint32_t action_queue_id; + enum pbr_forwarding_type forwarding_type; + /* * Use interface's vrf. */ @@ -233,9 +257,4 @@ extern void pbr_map_check_vrf_nh_group_change(const char *nh_group, extern void pbr_map_check_interface_nh_group_change(const char *nh_group, struct interface *ifp, ifindex_t oldifindex); -extern void pbr_set_match_clause_for_vlan(struct pbr_map_sequence *pbrms, - uint16_t vlan_id, - uint16_t vlan_flags); -extern void pbr_set_match_clause_for_pcp(struct pbr_map_sequence *pbrms, - bool set, uint8_t pcp); #endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 405a2d6588..4f7882fb22 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -549,6 +549,7 @@ void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms, const char *name) return; pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); + pbrms->forwarding_type = PBR_FT_NEXTHOP_GROUP; nhgc = nhgc_find(name); if (!nhgc) @@ -572,6 +573,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, MTYPE_TMP, pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, pbrms->seqno, buf)); + pbrms->forwarding_type = PBR_FT_NEXTHOP_SINGLE; nh = nexthop_new(); memcpy(nh, nhop, sizeof(*nh)); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 8e9673482e..0d6e1afd5b 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -29,89 +29,60 @@ #include "pbrd/pbr_vty_clippy.c" /* clang-format off */ -DEFPY(pbr_map_match_pcp, pbr_map_match_pcp_cmd, "[no] match pcp <(0-7)$pcp>", - NO_STR - "Match spec follows\n" - "Match based on 802.1p Priority Code Point (PCP) value\n" - "PCP value to match\n") +DEFPY (pbr_set_table_range, + pbr_set_table_range_cmd, + "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", + PBR_STR + "Set table ID range\n" + "Set table ID range\n" + "Lower bound for table ID range\n" + "Upper bound for table ID range\n") { /* clang-format on */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + /* upper bound is 2^32 - 2^10 */ + int ret = CMD_WARNING; + const int minrange = 1000; - if (pbrms) - pbr_set_match_clause_for_pcp(pbrms, !no, pcp); + /* validate given bounds */ + if (lb > ub) + vty_out(vty, "%% Lower bound must be less than upper bound\n"); + else if (ub - lb < minrange) + vty_out(vty, "%% Range breadth must be at least %d\n", minrange); + else { + ret = CMD_SUCCESS; + pbr_nht_set_tableid_range((uint32_t)lb, (uint32_t)ub); + } + return ret; +} + +/* clang-format off */ +DEFPY (no_pbr_set_table_range, + no_pbr_set_table_range_cmd, + "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]", + NO_STR + PBR_STR + "Set table ID range\n" + "Set table ID range\n" + "Lower bound for table ID range\n" + "Upper bound for table ID range\n") +{ + /* clang-format on */ + pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, + PBR_NHT_DEFAULT_HIGH_TABLEID); return CMD_SUCCESS; } /* clang-format off */ -DEFPY(pbr_map_match_vlan_id, pbr_map_match_vlan_id_cmd, - "[no] match vlan <(1-4094)$vlan_id>", - NO_STR - "Match spec follows\n" - "Match based on VLAN ID\n" - "VLAN ID to match\n") -{ - /* clang-format on */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (pbrms) { - if (!no) { - pbr_set_match_clause_for_vlan(pbrms, vlan_id, 0); - } else { - /* if the user previously set a vlan_id value */ - if (pbrms->match_vlan_id != 0) { - if (vlan_id == pbrms->match_vlan_id) { - pbr_set_match_clause_for_vlan(pbrms, 0, - 0); - } - } - } - } - return CMD_SUCCESS; -} - -/* clang-format off */ -DEFPY(pbr_map_match_vlan_tag, pbr_map_match_vlan_tag_cmd, - "[no] match vlan ![$tag_type]", - NO_STR - "Match the rest of the command\n" - "Match based on VLAN tagging\n" - "Match all tagged frames\n" - "Match all untagged frames\n" - "Match untagged frames, or tagged frames with id zero\n") -{ - /* clang-format on */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING; - - if (!no) { - assert(tag_type); - if (strmatch(tag_type, "tagged")) { - pbr_set_match_clause_for_vlan(pbrms, 0, - PBR_VLAN_FLAGS_TAGGED); - } else if (strmatch(tag_type, "untagged")) { - pbr_set_match_clause_for_vlan(pbrms, 0, - PBR_VLAN_FLAGS_UNTAGGED); - } else if (strmatch(tag_type, "untagged-or-zero")) { - pbr_set_match_clause_for_vlan(pbrms, 0, - PBR_VLAN_FLAGS_UNTAGGED_0); - } - } else { - pbr_set_match_clause_for_vlan(pbrms, 0, PBR_VLAN_FLAGS_NO_WILD); - } - - return CMD_SUCCESS; -} - -DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", +DEFUN_NOSH(pbr_map, + pbr_map_cmd, + "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert in existing pbr-map entry\n" "Sequence number\n") { + /* clang-format on */ const char *pbrm_name = argv[1]->arg; uint32_t seqno = atoi(argv[3]->arg); struct pbr_map_sequence *pbrms; @@ -122,13 +93,17 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", return CMD_SUCCESS; } -DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", +/* clang-format off */ +DEFUN_NOSH(no_pbr_map, + no_pbr_map_cmd, + "no pbr-map PBRMAP [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" "Sequence to delete from existing pbr-map entry\n" "Sequence number\n") { + /* clang-format on */ const char *pbrm_name = argv[2]->arg; uint32_t seqno = 0; struct pbr_map *pbrm = pbrm_find(pbrm_name); @@ -153,301 +128,533 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", return CMD_SUCCESS; } -DEFPY(pbr_set_table_range, - pbr_set_table_range_cmd, - "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", - PBR_STR - "Set table ID range\n" - "Set table ID range\n" - "Lower bound for table ID range\n" - "Upper bound for table ID range\n") -{ - /* upper bound is 2^32 - 2^10 */ - int ret = CMD_WARNING; - const int minrange = 1000; +/*********************************************************************** + * pbrms/rule Match L3 Fields + ***********************************************************************/ - /* validate given bounds */ - if (lb > ub) - vty_out(vty, "%% Lower bound must be less than upper bound\n"); - else if (ub - lb < minrange) - vty_out(vty, "%% Range breadth must be at least %d\n", minrange); - else { - ret = CMD_SUCCESS; - pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub); +/* + * Address Family Matters + * + * Linux Kernel constraints + * ------------------------ + * The underlying linux kernel dataplane requires that rules be + * installed into an IPv4-specific or an IPv6-specific database. + * + * Not only do we need to designate an address-family for rule + * installation, but we ALSO must have the same address-family + * available to be able to delete the rule from the correct kernel + * database. + * + * Determining the address-family + * ------------------------------ + * In the current code, we do our best to infer the correct family + * from any configured IP-address match or set clauses in a rule. + * Absent any of those fields, the NHT code also tries to glean the + * address family from resolved nexthops or nexthop-groups. All of + * those opportunistic address-family determinations are stored in + * the "family" field of struct pbr_map_sequence. + * + * This "family" field value is needed particularly when deleting + * a rule piece-by-piece because at the end, the match/set fields + * will be empty. Maybe it would be possible to handle this issue + * as an internal zebra matter in the future. + * + * We also attempt to maintain address-family consistency among the + * various configured fields in a rule. So far, these fields are + * src/dst IP-address match/set values. + * + * It is probably possible to perform the same address-family check in + * the CLI for single nexthops (set nexthop A.B.C.D|X:X::X:X) but the + * address-family is not immediately available for nexthop-groups. + * In both the single-nexthop and nexthop-group, the NHT resolution code + * sets the "family" field of struct pbr_map_sequence asynchronously. + * + * There isn't currently any flagging of rules that have a consistent + * set of src/dst IP-address match/set values but an asynchronously-resolved + * nexthop-group that has a different address-family. + * + * The match/set IP-address handlers below blindly set "family"; it's + * probably possible to wrongly set "family" to, e.g., IPv4 this way after + * a v6 NHG has been resolved and break rule removal. It's not clear + * how to best address this potential issue. + */ +static bool pbr_family_consistent(struct pbr_map_sequence *pbrms, + uint8_t family, uint32_t skip_filter_bm, + uint32_t skip_action_bm, const char **msg) +{ + uint32_t filter_bm = pbrms->filter_bm & ~skip_filter_bm; + uint32_t action_bm = pbrms->action_bm & ~skip_action_bm; + + if (CHECK_FLAG(filter_bm, PBR_FILTER_SRC_IP) && + (family != pbrms->src->family)) { + if (msg) + *msg = "match src-ip"; + return false; } - - return ret; + if (CHECK_FLAG(filter_bm, PBR_FILTER_DST_IP) && + (family != pbrms->dst->family)) { + if (msg) + *msg = "match dst-ip"; + return false; + } + if (CHECK_FLAG(action_bm, PBR_ACTION_SRC_IP) && + (family != sockunion_family(&pbrms->action_src))) { + if (msg) + *msg = "set src-ip"; + return false; + } + if (CHECK_FLAG(filter_bm, PBR_ACTION_DST_IP) && + (family != sockunion_family(&pbrms->action_dst))) { + if (msg) + *msg = "set dst-ip"; + return false; + } + return true; } -DEFPY(no_pbr_set_table_range, no_pbr_set_table_range_cmd, - "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]", - NO_STR - PBR_STR - "Set table ID range\n" - "Set table ID range\n" - "Lower bound for table ID range\n" - "Upper bound for table ID range\n") -{ - pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, - PBR_NHT_DEFAULT_HIGH_TABLEID); - return CMD_SUCCESS; -} -DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, - "[no] match src-ip $prefix", +/* clang-format off */ +DEFPY (pbr_map_match_src, + pbr_map_match_src_cmd, + "[no] match src-ip ![$prefix]", NO_STR "Match the rest of the command\n" - "Choose the src ip or ipv6 prefix to use\n" + "Choose the src ipv4 or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + const char *fmsg = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (pbrms->dst && prefix->family != pbrms->dst->family) { - vty_out(vty, "Cannot mismatch families within match src/dst\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - pbrms->family = prefix->family; - - if (!no) { - if (pbrms->src) { - if (prefix_same(pbrms->src, prefix)) - return CMD_SUCCESS; - } else - pbrms->src = prefix_new(); - - prefix_copy(pbrms->src, prefix); - } else + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) + return CMD_SUCCESS; prefix_free(&pbrms->src); + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP); + goto check; + } + assert(prefix); + if (!pbr_family_consistent(pbrms, prefix->family, PBR_FILTER_SRC_IP, 0, + &fmsg)) { + vty_out(vty, "Address family mismatch (%s)\n", fmsg); + return CMD_WARNING_CONFIG_FAILED; + } + pbrms->family = prefix->family; + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) { + if (prefix_same(pbrms->src, prefix)) + return CMD_SUCCESS; + } else + pbrms->src = prefix_new(); + + prefix_copy(pbrms->src, prefix); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP); + +check: pbr_map_check(pbrms, true); return CMD_SUCCESS; } -DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, - "[no] match dst-ip $prefix", +/* clang-format off */ +DEFPY (pbr_map_match_dst, + pbr_map_match_dst_cmd, + "[no] match dst-ip ![$prefix]", NO_STR "Match the rest of the command\n" - "Choose the dst ip or ipv6 prefix to use\n" + "Choose the dst ipv4 or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + const char *fmsg = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (pbrms->src && prefix->family != pbrms->src->family) { - vty_out(vty, "Cannot mismatch families within match src/dst\n"); - return CMD_WARNING_CONFIG_FAILED; + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) + return CMD_SUCCESS; + prefix_free(&pbrms->dst); + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP); + goto check; } + assert(prefix); + if (!pbr_family_consistent(pbrms, prefix->family, PBR_FILTER_DST_IP, 0, + &fmsg)) { + vty_out(vty, "Address family mismatch (%s)\n", fmsg); + return CMD_WARNING_CONFIG_FAILED; + } pbrms->family = prefix->family; - if (!no) { - if (pbrms->dst) { - if (prefix_same(pbrms->dst, prefix)) - return CMD_SUCCESS; - } else - pbrms->dst = prefix_new(); - - prefix_copy(pbrms->dst, prefix); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) { + if (prefix_same(pbrms->dst, prefix)) + return CMD_SUCCESS; } else - prefix_free(&pbrms->dst); + pbrms->dst = prefix_new(); + prefix_copy(pbrms->dst, prefix); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP); + +check: pbr_map_check(pbrms, true); return CMD_SUCCESS; } -DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd, - "[no] match ip-protocol PROTO$ip_proto", - NO_STR - "Match the rest of the command\n" - "Choose an ip-protocol\n" - "Protocol name\n") +/* clang-format off */ +DEFPY (pbr_map_match_ip_proto, + pbr_map_match_ip_proto_cmd, + "[no] match ip-protocol ![PROTO$ip_proto]", + NO_STR + "Match the rest of the command\n" + "Choose an ip-protocol\n" + "Protocol name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - struct protoent *p; + struct protoent *p = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if (!ip_proto) { - vty_out(vty, "Unable to convert (null) to proto id\n"); - return CMD_WARNING; - } + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL); + goto check; + } + if (ip_proto) p = getprotobyname(ip_proto); - if (!p) { - vty_out(vty, "Unable to convert %s to proto id\n", - ip_proto); - return CMD_WARNING; - } - pbrms->ip_proto = p->p_proto; - } else - pbrms->ip_proto = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd, - "[no] match src-port (1-65535)$port", - NO_STR - "Match the rest of the command\n" - "Choose the source port to use\n" - "The Source Port\n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) + if (!ip_proto || !p) { + vty_out(vty, "Unable to convert %s to proto id\n", + (ip_proto ? ip_proto : "(null)")); return CMD_WARNING_CONFIG_FAILED; - - if (!no) { - if (pbrms->src_prt == port) - return CMD_SUCCESS; - else - pbrms->src_prt = port; - } else - pbrms->src_prt = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd, - "[no] match dst-port (1-65535)$port", - NO_STR - "Match the rest of the command\n" - "Choose the destination port to use\n" - "The Destination Port\n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) { - if (pbrms->dst_prt == port) - return CMD_SUCCESS; - else - pbrms->dst_prt = port; - } else - pbrms->dst_prt = 0; - - pbr_map_check(pbrms, true); - - 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; - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - /* 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; + pbrms->ip_proto = p->p_proto; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL); - /* Set the DSCP bits of the DSField */ - pbrms->dsfield = - (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2); - } else { +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_match_src_port, + pbr_map_match_src_port_cmd, + "[no] match src-port ![(1-65535)$port]", + NO_STR + "Match the rest of the command\n" + "Choose the source port to use\n" + "The Source Port\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT); + goto check; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT) && + (pbrms->src_prt == port)) { + return CMD_SUCCESS; + } + pbrms->src_prt = port; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_match_dst_port, + pbr_map_match_dst_port_cmd, + "[no] match dst-port ![(1-65535)$port]", + NO_STR + "Match the rest of the command\n" + "Choose the destination port to use\n" + "The Destination Port\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT); + goto check; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT) && + (pbrms->dst_prt == port)) { + return CMD_SUCCESS; + } + pbrms->dst_prt = port; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +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") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); pbrms->dsfield &= ~PBR_DSFIELD_DSCP; + goto check; } - pbr_map_check(pbrms, true); + unsigned long ul_dscp; + char *pend; + uint8_t raw_dscp; + assert(dscp); + ul_dscp = strtol(dscp, &pend, 0); + if (*pend) + raw_dscp = pbr_map_decode_dscp_enum(dscp); + else + raw_dscp = ul_dscp << 2; + if (raw_dscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s%s\n", dscp, + (pend ? "" : " (numeric value must be in range 0-63)")); + return CMD_WARNING_CONFIG_FAILED; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP) && + (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == raw_dscp)) { + return CMD_SUCCESS; + } + + /* Set the DSCP bits of the DSField */ + pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (raw_dscp << 2); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); + +check: + 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") +/* clang-format off */ +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") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn) + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN)) return CMD_SUCCESS; - - /* Set the ECN bits of the DSField */ - pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn; - } else { + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN); pbrms->dsfield &= ~PBR_DSFIELD_ECN; + goto check; } - pbr_map_check(pbrms, true); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN) && + ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)) { + return CMD_SUCCESS; + } + /* Set the ECN bits of the DSField */ + pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN); + +check: + pbr_map_check(pbrms, true); return CMD_SUCCESS; } -DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, - "[no] match mark (1-4294967295)$mark", +/*********************************************************************** + * pbrms/rule Match L2 fields + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_match_pcp, + pbr_map_match_pcp_cmd, + "[no] match pcp ![(0-7)$pcp]", + NO_STR + "Match spec follows\n" + "Match based on 802.1p Priority Code Point (PCP) value\n" + "PCP value to match\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP); + goto check; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) && + (pbrms->match_pcp == pcp)) { + return CMD_SUCCESS; + } + + pbrms->match_pcp = pcp; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_match_vlan_id, + pbr_map_match_vlan_id_cmd, + "[no] match vlan ![(1-4094)$vlan_id]", + NO_STR + "Match spec follows\n" + "Match based on VLAN ID\n" + "VLAN ID to match\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID); + goto check; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID) && + (pbrms->match_vlan_id == vlan_id)) { + return CMD_SUCCESS; + } + + /* + * Maintaining previous behavior: setting a vlan_id match + * automatically clears any vlan_flags matching. + */ + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID); + pbrms->match_vlan_id = vlan_id; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_match_vlan_tag, + pbr_map_match_vlan_tag_cmd, + "[no] match vlan ![$tag_type]", + NO_STR + "Match the rest of the command\n" + "Match based on VLAN tagging\n" + "Match all tagged frames\n" + "Match all untagged frames\n" + "Match untagged frames, or tagged frames with id zero\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + uint16_t vlan_flags; + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS); + goto check; + } + + assert(tag_type); + if (strmatch(tag_type, "tagged")) + vlan_flags = PBR_VLAN_FLAGS_TAGGED; + else if (strmatch(tag_type, "untagged")) + vlan_flags = PBR_VLAN_FLAGS_UNTAGGED; + else if (strmatch(tag_type, "untagged-or-zero")) + vlan_flags = PBR_VLAN_FLAGS_UNTAGGED_0; + else { + vty_out(vty, "unknown vlan flag\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS) && + (pbrms->match_vlan_flags == vlan_flags)) { + return CMD_SUCCESS; + } + + /* + * Maintaining previous behavior: setting a vlan_flags match + * automatically clears any vlan_id matching. + */ + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS); + pbrms->match_vlan_flags = vlan_flags; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/*********************************************************************** + * pbrms/rule Match meta + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_match_mark, + pbr_map_match_mark_cmd, + "[no] match mark ![(1-4294967295)$mark]", NO_STR "Match the rest of the command\n" "Choose the mark value to use\n" "mark\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) @@ -458,20 +665,415 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, return CMD_WARNING_CONFIG_FAILED; #endif - if (!no) { - if (pbrms->mark) - if (pbrms->mark == (uint32_t)mark) - return CMD_SUCCESS; + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK); + goto check; + } - pbrms->mark = (uint32_t)mark; - } else - pbrms->mark = 0; + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK) && + (pbrms->mark == (uint32_t)mark)) { + return CMD_SUCCESS; + } + pbrms->mark = (uint32_t)mark; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK); + +check: pbr_map_check(pbrms, true); return CMD_SUCCESS; } +/*********************************************************************** + * pbrms/rule Action Set L3 Fields + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_action_src, + pbr_map_action_src_cmd, + "[no] set src-ip ![$su]", + NO_STR + "Set command\n" + "Set the src ipv4 or ipv6 prefix\n" + "v4 Prefix\n" + "v6 Prefix\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + const char *fmsg = NULL; + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP); + goto check; + } + + assert(su); + if (!pbr_family_consistent(pbrms, sockunion_family(su), + PBR_ACTION_SRC_IP, 0, &fmsg)) { + vty_out(vty, "Address family mismatch (%s)\n", fmsg); + return CMD_WARNING_CONFIG_FAILED; + } + pbrms->family = sockunion_family(su); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP) && + (sockunion_same(&pbrms->action_src, su))) { + return CMD_SUCCESS; + } + pbrms->action_src = *su; + SET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_dst, + pbr_map_action_dst_cmd, + "[no] set dst-ip ![$su]", + NO_STR + "Set command\n" + "Set the dst ipv4 or ipv6 prefix\n" + "v4 Prefix\n" + "v6 Prefix\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + const char *fmsg = NULL; + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP); + goto check; + } + + assert(su); + if (!pbr_family_consistent(pbrms, sockunion_family(su), + PBR_ACTION_DST_IP, 0, &fmsg)) { + vty_out(vty, "Address family mismatch (%s)\n", fmsg); + return CMD_WARNING_CONFIG_FAILED; + } + pbrms->family = sockunion_family(su); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP) && + (sockunion_same(&pbrms->action_dst, su))) { + return CMD_SUCCESS; + } + pbrms->action_dst = *su; + SET_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_src_port, + pbr_map_action_src_port_cmd, + "[no] set src-port ![(1-65535)$port]", + NO_STR + "Set command\n" + "Set Source Port\n" + "The Source Port\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT); + goto check; + } + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT) && + (pbrms->action_src_port == port)) + return CMD_SUCCESS; + + pbrms->action_src_port = port; + SET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_dst_port, + pbr_map_action_dst_port_cmd, + "[no] set dst-port ![(1-65535)$port]", + NO_STR + "Set command\n" + "Set Destination Port\n" + "The Destination Port\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT); + goto check; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT) && + (pbrms->action_dst_port == port)) + return CMD_SUCCESS; + + SET_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT); + pbrms->action_dst_port = port; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_dscp, + pbr_map_action_dscp_cmd, + "[no] set dscp ![DSCP$dscp]", + NO_STR + "Set command\n" + "Set IP DSCP field\n" + "DSCP numeric value (0-63) or standard codepoint name\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP); + goto check; + } + + unsigned long ul_dscp; + char *pend; + uint8_t raw_dscp; + + assert(dscp); + ul_dscp = strtol(dscp, &pend, 0); + if (*pend) + raw_dscp = pbr_map_decode_dscp_enum(dscp); + else + raw_dscp = ul_dscp << 2; + + if (raw_dscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s%s\n", dscp, + (pend ? "" : " (numeric value must be in range 0-63)")); + return CMD_WARNING_CONFIG_FAILED; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP) && + (pbrms->action_dscp == raw_dscp)) { + return CMD_SUCCESS; + } + SET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP); + pbrms->action_dscp = raw_dscp; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_ecn, + pbr_map_action_ecn_cmd, + "[no] set ecn ![(0-3)$ecn]", + NO_STR + "Set command\n" + "Set IP ECN field\n" + "Explicit Congestion Notification value\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_ECN); + goto check; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN) && + (pbrms->action_ecn == ecn)) { + return CMD_SUCCESS; + } + SET_FLAG(pbrms->action_bm, PBR_ACTION_ECN); + pbrms->action_ecn = ecn; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + + +/*********************************************************************** + * pbrms/rule Action Set Meta + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_action_queue_id, + pbr_map_action_queue_id_cmd, + "[no] set queue-id ![(1-65535)$queue_id]", + NO_STR + "Set the rest of the command\n" + "Set based on egress port queue id\n" + "A valid value in range 1..65535 \n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID); + goto check; + } + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID) && + (pbrms->action_queue_id == (uint32_t)queue_id)) { + return CMD_SUCCESS; + } + pbrms->action_queue_id = (uint32_t)queue_id; + SET_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + + +/*********************************************************************** + * pbrms/rule Action Set L2 Fields + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_action_pcp, + pbr_map_action_pcp_cmd, + "[no] set pcp ![(0-7)$pcp]", + NO_STR + "Set the rest of the command\n" + "Set based on 802.1p Priority Code Point (PCP) value\n" + "A valid value in range 0..7\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_PCP); + goto check; + } + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP) && + pbrms->action_pcp == pcp) { + return CMD_SUCCESS; + } + + pbrms->action_pcp = pcp; + SET_FLAG(pbrms->action_bm, PBR_ACTION_PCP); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_vlan_id, + pbr_map_action_vlan_id_cmd, + "[no] set vlan ![(1-4094)$vlan_id]", + NO_STR + "Set the rest of the command\n" + "Set action for VLAN tagging\n" + "A valid value in range 1..4094\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID); + goto check; + } + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) && + (pbrms->action_vlan_id == vlan_id)) { + return CMD_SUCCESS; + } + + /* + * Setting a vlan_id action automatically clears any strip-inner action + */ + pbrms->action_vlan_id = vlan_id; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY); + SET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_strip_vlan, + pbr_map_action_strip_vlan_cmd, + "[no] strip vlan", + NO_STR + "Strip the vlan tags from frame\n" + "Strip any inner vlan tag\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!pbrms) + return CMD_WARNING_CONFIG_FAILED; + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, + PBR_ACTION_VLAN_STRIP_INNER_ANY)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY); + goto check; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + return CMD_SUCCESS; + + /* + * Setting a strip-inner action automatically clears any vlan_id action + */ + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID); + SET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + + +/*********************************************************************** + * pbrms/rule Action Forwarding + ***********************************************************************/ + static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms) { if (pbrms->vrf_lookup || pbrms->vrf_unchanged) { @@ -501,95 +1103,11 @@ static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms) pbrms_clear_set_nexthop_config(pbrms); pbrms->nhs_installed = false; + + pbrms->forwarding_type = PBR_FT_UNSPEC; } -DEFPY(pbr_map_action_queue_id, pbr_map_action_queue_id_cmd, - "[no] set queue-id <(1-65535)$queue_id>", - NO_STR - "Set the rest of the command\n" - "Set based on egress port queue id\n" - "A valid value in range 1..65535 \n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_queue_id = queue_id; - else if ((uint32_t)queue_id == pbrms->action_queue_id) - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_action_pcp, pbr_map_action_pcp_cmd, "[no] set pcp <(0-7)$pcp>", - NO_STR - "Set the rest of the command\n" - "Set based on 802.1p Priority Code Point (PCP) value\n" - "A valid value in range 0..7\n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_pcp = pcp; - else if (pcp == pbrms->action_pcp) - pbrms->action_pcp = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_action_vlan_id, pbr_map_action_vlan_id_cmd, - "[no] set vlan <(1-4094)$vlan_id>", - NO_STR - "Set the rest of the command\n" - "Set action for VLAN tagging\n" - "A valid value in range 1..4094\n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_vlan_id = vlan_id; - else if (pbrms->action_vlan_id == vlan_id) - pbrms->action_vlan_id = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_action_strip_vlan, pbr_map_action_strip_vlan_cmd, - "[no] strip vlan", - NO_STR - "Strip the vlan tags from frame\n" - "Strip any inner vlan tag \n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY; - else - pbrms->action_vlan_flags = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, "set nexthop-group NHGNAME$name", @@ -641,22 +1159,27 @@ DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, - "set nexthop\ +/* clang-format off */ +DEFPY (pbr_map_nexthop, + pbr_map_nexthop_cmd, + "set nexthop\ <\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ + |blackhole$bh\ >\ [nexthop-vrf NAME$vrf_name]", - "Set for the PBR-MAP\n" - "Specify one of the nexthops in this map\n" - "v4 Address\n" - "v6 Address\n" - "Interface to use\n" - "Interface to use\n" - "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + "Set for the PBR-MAP\n" + "Specify one of the nexthops in this map\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "Interface to use\n" + "Blackhole route\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); struct vrf *vrf; struct nexthop nhop; @@ -738,8 +1261,11 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, nhop.type = NEXTHOP_TYPE_IPV6; } } - } else + } else if (bh) { + nhop.type = NEXTHOP_TYPE_BLACKHOLE; + } else { nhop.type = NEXTHOP_TYPE_IFINDEX; + } if (pbrms->nhg) nh = nexthop_exists(pbrms->nhg, &nhop); @@ -768,23 +1294,28 @@ done: return CMD_SUCCESS; } -DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd, - "no set nexthop\ +/* clang-format off */ +DEFPY (no_pbr_map_nexthop, + no_pbr_map_nexthop_cmd, + "no set nexthop\ [<\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ + |blackhole$bh\ >\ [nexthop-vrf NAME$vrf_name]]", - NO_STR - "Set for the PBR-MAP\n" - "Specify one of the nexthops in this map\n" - "v4 Address\n" - "v6 Address\n" - "Interface to use\n" - "Interface to use\n" - "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + NO_STR + "Set for the PBR-MAP\n" + "Specify one of the nexthops in this map\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "Interface to use\n" + "Blackhole route\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) @@ -810,10 +1341,11 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, /* * If an equivalent set vrf * exists, just return success. */ - if (vrf_name && pbrms->vrf_lookup - && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) + if ((pbrms->forwarding_type == PBR_FT_SETVRF) && + strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) return CMD_SUCCESS; - else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */ + else if (!vrf_name && (pbrms->forwarding_type == PBR_FT_VRF_UNCHANGED)) + /* Unchanged already set */ return CMD_SUCCESS; if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) { @@ -826,9 +1358,12 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, if (vrf_name) { pbrms->vrf_lookup = true; + pbrms->forwarding_type = PBR_FT_SETVRF; strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)); - } else + } else { + pbrms->forwarding_type = PBR_FT_VRF_UNCHANGED; pbrms->vrf_unchanged = true; + } pbr_map_check(pbrms, true); @@ -853,13 +1388,19 @@ DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd, return CMD_SUCCESS; } -DEFPY (pbr_policy, +/*********************************************************************** + * Policy + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_policy, pbr_policy_cmd, "[no] pbr-policy PBRMAP$mapname", NO_STR "Policy to use\n" "Name of the pbr-map to apply\n") { + /* clang-format on */ VTY_DECLVAR_CONTEXT(interface, ifp); struct pbr_map *pbrm, *old_pbrm; struct pbr_interface *pbr_ifp = ifp->info; @@ -966,56 +1507,93 @@ static void vty_show_pbrms(struct vty *vty, pbrms->installed ? "yes" : "no", pbrms->reason ? rbuf : "Valid"); - if (pbrms->ip_proto) { + /* match clauses first */ + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) { struct protoent *p; p = getprotobynumber(pbrms->ip_proto); vty_out(vty, " IP Protocol Match: %s\n", p->p_name); } - if (pbrms->src) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) vty_out(vty, " SRC IP Match: %pFX\n", pbrms->src); - if (pbrms->dst) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) vty_out(vty, " DST IP Match: %pFX\n", pbrms->dst); - if (pbrms->src_prt) + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt); - if (pbrms->dst_prt) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt); - if (pbrms->dsfield & PBR_DSFIELD_DSCP) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) vty_out(vty, " DSCP Match: %u\n", (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); - if (pbrms->dsfield & PBR_DSFIELD_ECN) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN)) vty_out(vty, " ECN Match: %u\n", pbrms->dsfield & PBR_DSFIELD_ECN); - if (pbrms->mark) + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) vty_out(vty, " MARK Match: %u\n", pbrms->mark); if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) vty_out(vty, " PCP Match: %d\n", pbrms->match_pcp); - if (pbrms->match_vlan_id != 0) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) vty_out(vty, " Match VLAN ID: %u\n", pbrms->match_vlan_id); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) - vty_out(vty, " Match VLAN tagged frames\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) - vty_out(vty, " Match VLAN untagged frames\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) - vty_out(vty, " Match VLAN untagged or ID 0\n"); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) { + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) + vty_out(vty, " Match VLAN tagged frames\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) + vty_out(vty, " Match VLAN untagged frames\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) + vty_out(vty, " Match VLAN untagged or ID 0\n"); + } - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) + /* set actions */ + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP)) + vty_out(vty, " Set SRC IP: %pSU\n", &pbrms->action_src); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP)) + vty_out(vty, " Set DST IP: %pSU\n", &pbrms->action_dst); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT)) + vty_out(vty, " Set Src port: %u\n", + pbrms->action_src_port); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT)) + vty_out(vty, " Set Dst port: %u\n", + pbrms->action_dst_port); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP)) + vty_out(vty, " Set DSCP: %u\n", (pbrms->action_dscp) >> 2); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN)) + vty_out(vty, " Set ECN: %u\n", pbrms->action_ecn); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID)) + vty_out(vty, " Set VLAN ID %u\n", pbrms->action_vlan_id); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + vty_out(vty, " Strip VLAN ID\n"); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP)) + vty_out(vty, " Set PCP %u\n", pbrms->action_pcp); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID)) vty_out(vty, " Set Queue ID: %u\n", pbrms->action_queue_id); - if (pbrms->action_vlan_id != 0) - vty_out(vty, " Set VLAN ID %u\n", pbrms->action_vlan_id); - if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY) - vty_out(vty, " Strip VLAN ID\n"); - if (pbrms->action_pcp) - vty_out(vty, " Set PCP %u\n", pbrms->action_pcp); - - if (pbrms->nhgrp_name) { + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + break; + case PBR_FT_VRF_UNCHANGED: + vty_out(vty, " VRF Unchanged (use interface vrf)\n"); + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); + vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); + break; + case PBR_FT_NEXTHOP_GROUP: + assert(pbrms->nhgrp_name); vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name); if (detail) @@ -1029,8 +1607,9 @@ static void vty_show_pbrms(struct vty *vty, pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes" : "no", pbr_nht_get_table(pbrms->nhgrp_name)); - - } else if (pbrms->nhg) { + break; + case PBR_FT_NEXTHOP_SINGLE: + assert(pbrms->internal_nhg_name); vty_out(vty, " "); pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); if (detail) @@ -1045,13 +1624,7 @@ static void vty_show_pbrms(struct vty *vty, ? "yes" : "no", pbr_nht_get_table(pbrms->internal_nhg_name)); - - } else if (pbrms->vrf_unchanged) { - vty_out(vty, " VRF Unchanged (use interface vrf)\n"); - } else if (pbrms->vrf_lookup) { - vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); - } else { - vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + break; } } @@ -1078,7 +1651,18 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, json_object_string_add(jpbrm, "installedReason", pbrms->reason ? rbuf : "Valid"); - if (nhg_name) { + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + break; + case PBR_FT_VRF_UNCHANGED: + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); + json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); + break; + case PBR_FT_NEXTHOP_GROUP: + case PBR_FT_NEXTHOP_SINGLE: + assert(nhg_name); nexthop_group = json_object_new_object(); json_object_int_add(nexthop_group, "tableId", @@ -1090,24 +1674,93 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, pbrms->nhs_installed); json_object_object_add(jpbrm, "nexthopGroup", nexthop_group); + break; } - if (pbrms->vrf_lookup) - json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); - if (pbrms->src) + /* + * Match clauses + */ + + /* IP Header */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) + json_object_int_add(jpbrm, "matchIpProtocol", pbrms->ip_proto); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) json_object_string_addf(jpbrm, "matchSrc", "%pFX", pbrms->src); - if (pbrms->dst) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) json_object_string_addf(jpbrm, "matchDst", "%pFX", pbrms->dst); - if (pbrms->mark) - json_object_int_add(jpbrm, "matchMark", pbrms->mark); - if (pbrms->dsfield & PBR_DSFIELD_DSCP) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) + json_object_int_add(jpbrm, "matchSrcPort", pbrms->src_prt); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) + json_object_int_add(jpbrm, "matchDstPort", pbrms->dst_prt); + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) json_object_int_add(jpbrm, "matchDscp", (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); - if (pbrms->dsfield & PBR_DSFIELD_ECN) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN)) json_object_int_add(jpbrm, "matchEcn", pbrms->dsfield & PBR_DSFIELD_ECN); + /* L2 headers */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) + json_object_int_add(jpbrm, "matchPcp", pbrms->match_pcp); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) + json_object_int_add(jpbrm, "matchVlanId", pbrms->match_vlan_id); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) { + const char *p = "?"; + + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) + p = "tagged"; + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) + p = "untagged"; + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) + p = "untagged-or-0"; + + json_object_string_addf(jpbrm, "matchVlanFlags", "%s", p); + } + + /* meta */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + json_object_int_add(jpbrm, "matchMark", pbrms->mark); + + /* + * action clauses + */ + + /* IP header fields */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP)) + json_object_string_addf(jpbrm, "actionSetSrcIpAddr", "%pSU", + &pbrms->action_src); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP)) + json_object_string_addf(jpbrm, "actionSetDstIpAddr", "%pSU", + &pbrms->action_dst); + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT)) + json_object_int_add(jpbrm, "actionSetSrcPort", + pbrms->action_src_port); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT)) + json_object_int_add(jpbrm, "actionSetDstPort", + pbrms->action_dst_port); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP)) + json_object_int_add(jpbrm, "actionSetDscp", + pbrms->action_dscp >> 2); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN)) + json_object_int_add(jpbrm, "actionSetEcn", pbrms->action_ecn); + + /* L2 header fields */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + json_object_boolean_true_add(jpbrm, "actionVlanStripInnerAny"); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID)) + json_object_int_add(jpbrm, "actionSetVlanId", + pbrms->action_vlan_id); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP)) + json_object_int_add(jpbrm, "actionSetPcp", pbrms->action_pcp); + + /* meta */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID)) + json_object_int_add(jpbrm, "actionSetQueueId", + pbrms->action_queue_id); + json_object_array_add(j, jpbrm); } @@ -1373,71 +2026,99 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, { vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno); - if (pbrms->src) - vty_out(vty, " match src-ip %pFX\n", pbrms->src); - - if (pbrms->dst) - vty_out(vty, " match dst-ip %pFX\n", pbrms->dst); - - if (pbrms->src_prt) - vty_out(vty, " match src-port %u\n", pbrms->src_prt); - if (pbrms->dst_prt) - vty_out(vty, " match dst-port %u\n", pbrms->dst_prt); - - if (pbrms->ip_proto) { + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) { struct protoent *p; p = getprotobynumber(pbrms->ip_proto); vty_out(vty, " match ip-protocol %s\n", p->p_name); } - if (pbrms->dsfield & PBR_DSFIELD_DSCP) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) + vty_out(vty, " match src-ip %pFX\n", pbrms->src); + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) + vty_out(vty, " match dst-ip %pFX\n", pbrms->dst); + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) + vty_out(vty, " match src-port %u\n", pbrms->src_prt); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) + vty_out(vty, " match dst-port %u\n", pbrms->dst_prt); + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) vty_out(vty, " match dscp %u\n", (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); - if (pbrms->dsfield & PBR_DSFIELD_ECN) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_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); if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) vty_out(vty, " match pcp %d\n", pbrms->match_pcp); - if ((pbrms->match_vlan_id) && - (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_NO_WILD)) + /* L2 headers */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) vty_out(vty, " match vlan %u\n", pbrms->match_vlan_id); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) - vty_out(vty, " match vlan tagged\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) - vty_out(vty, " match vlan untagged\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) - vty_out(vty, " match vlan untagged-or-zero\n"); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) { + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) + vty_out(vty, " match vlan tagged\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) + vty_out(vty, " match vlan untagged\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) + vty_out(vty, " match vlan untagged-or-zero\n"); + } - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id); + /* meta */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + vty_out(vty, " match mark %u\n", pbrms->mark); - if (pbrms->action_pcp) + /* + * action clauses + */ + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP)) + vty_out(vty, " set src-ip %pSU\n", &pbrms->action_src); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP)) + vty_out(vty, " set dst-ip %pSU\n", &pbrms->action_dst); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT)) + vty_out(vty, " set src-port %d\n", pbrms->action_src_port); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT)) + vty_out(vty, " set dst-port %d\n", pbrms->action_dst_port); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP)) + vty_out(vty, " set dscp %u\n", (pbrms->action_dscp) >> 2); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN)) + vty_out(vty, " set ecn %u\n", pbrms->action_ecn); + + /* L2 header fields */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + vty_out(vty, " strip vlan any\n"); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID)) + vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP)) vty_out(vty, " set pcp %d\n", pbrms->action_pcp); - if (pbrms->action_vlan_id) - vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id); + /* meta */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID)) + vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id); - if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY) - vty_out(vty, " strip vlan any\n"); - - if (pbrms->vrf_unchanged) + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + break; + case PBR_FT_VRF_UNCHANGED: vty_out(vty, " set vrf unchanged\n"); - - if (pbrms->vrf_lookup) + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); vty_out(vty, " set vrf %s\n", pbrms->vrf_name); - - if (pbrms->nhgrp_name) + break; + case PBR_FT_NEXTHOP_GROUP: + assert(pbrms->nhgrp_name); vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); - - if (pbrms->nhg) { + break; + case PBR_FT_NEXTHOP_SINGLE: + assert(pbrms->nhg); vty_out(vty, " set "); pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); + break; } vty_out(vty, "exit\n"); @@ -1505,6 +2186,7 @@ void pbr_vty_init(void) install_element(CONFIG_NODE, &pbr_set_table_range_cmd); install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd); install_element(INTERFACE_NODE, &pbr_policy_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_ip_proto_cmd); install_element(PBRMAP_NODE, &pbr_map_match_src_port_cmd); install_element(PBRMAP_NODE, &pbr_map_match_dst_port_cmd); @@ -1516,10 +2198,18 @@ void pbr_vty_init(void) install_element(PBRMAP_NODE, &pbr_map_match_vlan_tag_cmd); install_element(PBRMAP_NODE, &pbr_map_match_pcp_cmd); install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_queue_id_cmd); install_element(PBRMAP_NODE, &pbr_map_action_strip_vlan_cmd); install_element(PBRMAP_NODE, &pbr_map_action_vlan_id_cmd); install_element(PBRMAP_NODE, &pbr_map_action_pcp_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_src_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dst_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dscp_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_ecn_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_src_port_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dst_port_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 030c4c1114..35c771469c 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -517,10 +517,14 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, 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). + * Opportunistic address family field is set when any of the IP + * address match/set fields is set, or when a NH/NHG is resolved. + * The value is needed by zebra for the underlying netlink + * messaging, particularly in delete operations, because it + * selects the rule database (IPv4 vs. IPv6). + * + * Historically the value has been encoded into any unused + * "match src/dst address" fields and picked off in zebra. */ family = AF_INET; if (pbrms->family) @@ -539,6 +543,8 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.priority = pbrms->ruleno; r.unique = pbrms->unique; + r.family = pbrms->family; + /* filter */ r.filter.filter_bm = pbrms->filter_bm; if (pbrms->src) @@ -558,44 +564,18 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.filter.fwmark = pbrms->mark; r.filter.ip_proto = pbrms->ip_proto; - /* - * 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); + r.filter.filter_bm = pbrms->filter_bm; /* actions */ + SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */ + /* * 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 (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - SET_FLAG(r.action.flags, PBR_ACTION_QUEUE_ID); - if (pbrms->action_pcp != 0) - SET_FLAG(r.action.flags, PBR_ACTION_PCP); - if (pbrms->action_vlan_id != 0) - SET_FLAG(r.action.flags, PBR_ACTION_VLAN_ID); - if (pbrms->action_vlan_flags != 0) - SET_FLAG(r.action.flags, PBR_ACTION_VLAN_FLAGS); + + r.action.flags = pbrms->action_bm; /* * if the user does not use the command "set vrf name unchanged" @@ -613,9 +593,18 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, } r.action.queue_id = pbrms->action_queue_id; + + r.action.src_ip = pbrms->action_src; + r.action.dst_ip = pbrms->action_dst; + + r.action.src_port = pbrms->action_src_port; + r.action.dst_port = pbrms->action_dst_port; + + r.action.dscp = pbrms->action_dscp; + r.action.ecn = pbrms->action_ecn; + 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)); diff --git a/tests/topotests/pbr_topo1/test_pbr_topo1.py b/tests/topotests/pbr_topo1/test_pbr_topo1.py index ad096fa14e..2b437170ff 100644 --- a/tests/topotests/pbr_topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr_topo1/test_pbr_topo1.py @@ -201,27 +201,38 @@ 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": "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$"}, + {"c": "set src-ip 44.100.1.1", "tm": r"Set SRC IP: 44.100.1.1$"}, + {"c": "no set src-ip 44.100.1.1", "tN": r"Set SRC IP: 44.100.1.1$"}, + {"c": "set dst-ip 44.105.1.1", "tm": r"Set DST IP: 44.105.1.1$"}, + {"c": "no set dst-ip 44.105.1.1", "tN": r"Set DST IP: 44.105.1.1$"}, + {"c": "set src-port 41", "tm": r"Set SRC PORT: 41$"}, + {"c": "no set src-port 41", "tN": r"Set SRC PORT: 41$"}, + {"c": "set dst-port 43", "tm": r"Set DST PORT: 43$"}, + {"c": "no set dst-port 43", "tN": r"Set DST PORT: 43$"}, + {"c": "set dscp 24", "tm": r"Set DSCP: 24$"}, + {"c": "no set dscp 24", "tN": r"Set DSCP: 24$"}, + {"c": "set dscp cs7", "tm": r"Set DSCP: 14$"}, + {"c": "no set dscp cs7", "tN": r"Set DSCP: 14$"}, + {"c": "set ecn 1", "tm": r"Set ECN: 1$"}, + {"c": "no set ecn 1", "tN": r"Set ECN: 1$"}, ] diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 3bb4936b81..bc96e12902 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -117,8 +117,8 @@ static ssize_t netlink_rule_msg_encode( } /* dsfield, if specified; mask off the ECN bits */ - if (filter_bm & PBR_FILTER_DSFIELD) - req->frh.tos = dsfield & 0xfc; + if (filter_bm & PBR_FILTER_DSCP) + req->frh.tos = dsfield & PBR_DSFIELD_DSCP; /* protocol to match on */ if (filter_bm & PBR_FILTER_IP_PROTOCOL) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index e9c243217a..f6afb006b7 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -3188,10 +3188,32 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) strlcpy(zpr.ifname, zpr.rule.ifname, sizeof(zpr.ifname)); + if ((zpr.rule.family != AF_INET) && + (zpr.rule.family != AF_INET6)) { + zlog_warn("Unsupported PBR source IP family: %s (%hhu)", + family2str(zpr.rule.family), zpr.rule.family); + return; + } + + /* + * Fixup filter src/dst IP addresses if they are unset + * because the netlink code currently obtains address family + * from them. Address family is used to specify which + * kernel database to use when adding/deleting rule. + * + * TBD: propagate zpr.rule.family into dataplane and + * netlink code so they can stop using filter src/dst addrs. + */ + if (!CHECK_FLAG(zpr.rule.filter.filter_bm, PBR_FILTER_SRC_IP)) + zpr.rule.filter.src_ip.family = zpr.rule.family; + if (!CHECK_FLAG(zpr.rule.filter.filter_bm, PBR_FILTER_DST_IP)) + zpr.rule.filter.dst_ip.family = zpr.rule.family; + + /* TBD delete below block when netlink code gets family from zpr.rule.family */ if (!(zpr.rule.filter.src_ip.family == AF_INET || zpr.rule.filter.src_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR source IP family: %s (%hhu)", + "Unsupported PBR source IP family: %s (%u)", family2str(zpr.rule.filter.src_ip.family), zpr.rule.filter.src_ip.family); return; @@ -3199,11 +3221,12 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (!(zpr.rule.filter.dst_ip.family == AF_INET || zpr.rule.filter.dst_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR destination IP family: %s (%hhu)", + "Unsupported PBR destination IP family: %s (%u)", family2str(zpr.rule.filter.dst_ip.family), zpr.rule.filter.dst_ip.family); return; } + /* TBD delete above block when netlink code gets family from zpr.rule.family */ zpr.vrf_id = zvrf->vrf->vrf_id; diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index eac93dca41..5124768a7c 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -507,6 +507,7 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) { struct pbr_rule *prule = &rule->rule; struct zebra_pbr_action *zaction = &rule->action; + struct pbr_action *pa = &prule->action; vty_out(vty, "Rules if %s\n", rule->ifname); vty_out(vty, " Seq %u pri %u\n", prule->seq, prule->priority); @@ -522,12 +523,12 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) if (prule->filter.filter_bm & PBR_FILTER_DST_PORT) vty_out(vty, " DST Port Match: %u\n", prule->filter.dst_port); - if (prule->filter.filter_bm & PBR_FILTER_DSFIELD) { + if (prule->filter.filter_bm & PBR_FILTER_DSCP) vty_out(vty, " DSCP Match: %u\n", (prule->filter.dsfield & PBR_DSFIELD_DSCP) >> 2); + if (prule->filter.filter_bm & PBR_FILTER_ECN) vty_out(vty, " ECN Match: %u\n", prule->filter.dsfield & PBR_DSFIELD_ECN); - } if (prule->filter.filter_bm & PBR_FILTER_FWMARK) vty_out(vty, " MARK Match: %u\n", prule->filter.fwmark); @@ -548,6 +549,30 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) vty_out(vty, "\n"); } + if (CHECK_FLAG(pa->flags, PBR_ACTION_ECN)) + vty_out(vty, " Action: Set ECN: %u\n", pa->ecn); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DSCP)) + vty_out(vty, " Action: Set DSCP: %u\n", pa->dscp >> 2); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_IP)) + vty_out(vty, " Action: Set SRC IP: %pSU\n", &pa->src_ip); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_IP)) + vty_out(vty, " Action: Set DST IP: %pSU\n", &pa->dst_ip); + if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_PORT)) + vty_out(vty, " Action: Set SRC PORT: %u\n", pa->src_port); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_PORT)) + vty_out(vty, " Action: Set DST PORT: %u\n", pa->dst_port); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_QUEUE_ID)) + vty_out(vty, " Action: Set Queue ID: %u\n", pa->queue_id); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_PCP)) + vty_out(vty, " Action: Set PCP: %u\n", pa->pcp); + if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_ID)) + vty_out(vty, " Action: Set VLAN ID: %u\n", pa->vlan_id); + if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + vty_out(vty, " Action: Strip VLAN ID\n"); + vty_out(vty, " Tableid: %u\n", prule->action.table); if (zaction->afi == AFI_IP) vty_out(vty, " Action: nh: %pI4 intf: %s\n", diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 15ad4e35cf..ddc1460d40 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -61,8 +61,6 @@ 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)