if (node_to_del)
list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
+ assert(bgp_vrf->vrf_import_rtl);
/* fallback to auto import rt, if this was the last RT */
- if (list_isempty(bgp_vrf->vrf_import_rtl)) {
+ if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) {
UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
}
json_object *json;
};
-#if defined(HAVE_CUMULUS)
static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt,
json_object *json)
{
for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) {
if (json)
json_object_array_add(
- json_vnis, json_object_new_int64(tmp_vpn->vni));
+ json_vnis, json_object_new_int(tmp_vpn->vni));
else
vty_out(vty, " %u\n", tmp_vpn->vni);
}
vty_out(vty, "\n");
}
}
-#endif /* HAVE_CUMULUS */
static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
enum bgp_show_type type, void *output_arg,
argv[idx_gwip]->arg, argv[idx_ethtag]->arg);
}
-#if defined(HAVE_CUMULUS)
-
static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl);
list_delete_node(vpn->import_rtl, node_to_del);
}
+ assert(vpn->import_rtl);
/* Reset to auto RT - this also rebuilds the RT to VNI mapping */
if (list_isempty(vpn->import_rtl)) {
UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
list_delete_node(vpn->export_rtl, node_to_del);
}
+ assert(vpn->export_rtl);
if (list_isempty(vpn->export_rtl)) {
UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
bgp_evpn_derive_auto_rt_export(bgp, vpn);
bgp->advertise_autort_rfc8365 = 0;
bgp_evpn_handle_autort_change(bgp);
}
-#endif /* HAVE_CUMULUS */
static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
{
}
}
-#if defined(HAVE_CUMULUS)
DEFUN (bgp_evpn_advertise_default_gw_vni,
bgp_evpn_advertise_default_gw_vni_cmd,
"advertise-default-gw",
{
int idx = 0;
uint8_t uj = 0;
- esi_t esi = {0};
+ esi_t esi;
json_object *json = NULL;
struct bgp *bgp = NULL;
+ memset(&esi, 0, sizeof(esi));
uj = use_json(argc, argv);
bgp = bgp_get_default();
JSON_STR)
{
int uj = 0;
- esi_t esi = {0};
+ esi_t esi;
struct bgp *bgp = NULL;
json_object *json = NULL;
+ memset(&esi, 0, sizeof(esi));
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
return CMD_SUCCESS;
}
-#if defined(HAVE_CUMULUS)
DEFUN(test_adv_evpn_type4_route,
test_adv_evpn_type4_route_cmd,
"advertise es ESI",
ALIAS_HIDDEN(show_bgp_l2vpn_evpn_import_rt, show_bgp_evpn_import_rt_cmd,
"show bgp evpn import-rt",
SHOW_STR BGP_STR EVPN_HELP_STR "Show import route target\n")
-#endif
DEFUN_NOSH (bgp_evpn_vni,
bgp_evpn_vni_cmd,
evpn_unconfigure_export_rt(bgp, vpn, NULL);
return CMD_SUCCESS;
}
-#endif
static int vni_cmp(const void **a, const void **b)
{
install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_overlay_cmd);
install_element(BGP_EVPN_NODE, &no_evpnrt5_network_cmd);
install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd);
-#if defined(HAVE_CUMULUS)
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_autort_rfc8365_cmd);
install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_vni_subnet_cmd);
install_element(BGP_EVPN_VNI_NODE,
&no_bgp_evpn_advertise_vni_subnet_cmd);
-#endif
}
len - offset, NULL, &error);
break;
case FLOWSPEC_TCP_FLAGS:
- ret = bgp_flowspec_tcpflags_decode(
+ case FLOWSPEC_FRAGMENT:
+ ret = bgp_flowspec_bitmask_decode(
BGP_FLOWSPEC_VALIDATE_ONLY,
nlri_content + offset,
len - offset, NULL, &error);
nlri_content + offset,
len - offset, NULL, &error);
break;
- case FLOWSPEC_FRAGMENT:
- ret = bgp_flowspec_fragment_type_decode(
- BGP_FLOWSPEC_VALIDATE_ONLY,
- nlri_content + offset,
- len - offset, NULL, &error);
- break;
default:
error = -1;
break;
len - offset,
NULL, &error);
break;
+ case FLOWSPEC_FRAGMENT:
case FLOWSPEC_TCP_FLAGS:
- ret = bgp_flowspec_tcpflags_decode(
+ ret = bgp_flowspec_bitmask_decode(
BGP_FLOWSPEC_VALIDATE_ONLY,
nlri_content+offset,
len - offset,
len - offset, NULL,
&error);
break;
- case FLOWSPEC_FRAGMENT:
- ret = bgp_flowspec_fragment_type_decode(
- BGP_FLOWSPEC_VALIDATE_ONLY,
- nlri_content + offset,
- len - offset, NULL,
- &error);
- break;
default:
error = -1;
break;
/*
- * handle the flowspec tcpflags field
+ * handle the flowspec tcpflags or fragment field
* return number of bytes analysed
* if there is an error, the passed error param is used to give error:
* -1 if decoding error,
* if result is a string, its assumed length
* is BGP_FLOWSPEC_STRING_DISPLAY_MAX
*/
-int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type,
+int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
case BGP_FLOWSPEC_RETURN_STRING:
if (op[1] == 1 && loop != 0) {
len_written = snprintf(ptr, len_string,
- ", and ");
+ ",&");
len_string -= len_written;
ptr += len_written;
} else if (op[1] == 0 && loop != 0) {
len_written = snprintf(ptr, len_string,
- ", or ");
+ ",|");
len_string -= len_written;
ptr += len_written;
}
- len_written = snprintf(ptr, len_string,
- "tcp flags is ");
- len_string -= len_written;
- ptr += len_written;
- if (op[6] == 1) {
- ptr += snprintf(ptr, len_string,
- "not ");
+ if (op[7] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "= ");
+ len_string -= len_written;
+ ptr += len_written;
+ } else {
+ len_written = snprintf(ptr, len_string,
+ "∋ ");
len_string -= len_written;
ptr += len_written;
}
- if (op[7] == 1) {
- ptr += snprintf(ptr, len_string,
- "exactly match ");
+ if (op[6] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "! ");
len_string -= len_written;
ptr += len_written;
}
- ptr += snprintf(ptr, len_string,
+ len_written = snprintf(ptr, len_string,
"%d", value);
len_string -= len_written;
ptr += len_written;
return offset;
}
-/*
- * handle the flowspec fragment type field
- * return error (returned values are invalid) or number of bytes analysed
- * -1 if error in decoding
- * >= 0 : number of bytes analysed (ok).
- */
-int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
- uint8_t *nlri_ptr,
- uint32_t max_len,
- void *result, int *error)
-{
- int op[8];
- int len, value, value_size, loop = 0;
- char *ptr = (char *)result; /* for return_string */
- struct bgp_pbr_fragment_val *mval =
- (struct bgp_pbr_fragment_val *)result;
- uint32_t offset = 0;
- int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
- int len_written;
-
- *error = 0;
- do {
- hex2bin(&nlri_ptr[offset], op);
- offset++;
- len = 2 * op[2] + op[3];
- value_size = 1 << len;
- value = hexstr2num(&nlri_ptr[offset], value_size);
- if (value != 1 && value != 2 && value != 4 && value != 8)
- *error = -1;
- offset += value_size;
- /* TODO : as per RFC5574 : first Fragment bits are Reserved
- * does that mean that it is not possible
- * to handle multiple occurences ?
- * as of today, we only grab the first TCP fragment
- */
- if (loop) {
- *error = -2;
- loop++;
- continue;
- }
- switch (type) {
- case BGP_FLOWSPEC_RETURN_STRING:
- switch (value) {
- case 1:
- len_written = snprintf(ptr, len_string,
- "dont-fragment");
- len_string -= len_written;
- ptr += len_written;
- break;
- case 2:
- len_written = snprintf(ptr, len_string,
- "is-fragment");
- len_string -= len_written;
- ptr += len_written;
- break;
- case 4:
- len_written = snprintf(ptr, len_string,
- "first-fragment");
- len_string -= len_written;
- ptr += len_written;
- break;
- case 8:
- len_written = snprintf(ptr, len_string,
- "last-fragment");
- len_string -= len_written;
- ptr += len_written;
- break;
- default:
- {}
- }
- break;
- case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
- mval->bitmask = (uint8_t)value;
- break;
- case BGP_FLOWSPEC_VALIDATE_ONLY:
- default:
- /* no action */
- break;
- }
- loop++;
- } while (op[0] == 0 && offset < max_len - 1);
- if (offset > max_len)
- *error = -1;
- return offset;
-}
-
int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
struct bgp_pbr_entry_main *bpem)
{
&error);
break;
case FLOWSPEC_TCP_FLAGS:
- ret = bgp_flowspec_tcpflags_decode(
+ ret = bgp_flowspec_bitmask_decode(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content + offset,
len - offset,
offset += ret;
break;
case FLOWSPEC_FRAGMENT:
- ret = bgp_flowspec_fragment_type_decode(
+ ret = bgp_flowspec_bitmask_decode(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content + offset,
len - offset, &bpem->fragment,
zlog_err("%s: flowspec_fragment_type_decode error %d",
__func__, error);
else
- bpem->match_bitmask |= FRAGMENT_PRESENT;
+ bpem->match_fragment_num = error;
offset += ret;
break;
default:
uint32_t max_len,
void *result, int *error);
-extern int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type,
+extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error);
-extern int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
- uint8_t *nlri_ptr,
- uint32_t max_len,
- void *result, int *error);
struct bgp_pbr_entry_main;
extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
struct bgp_pbr_entry_main *bpem);
{FLOWSPEC_SRC_PORT, "srcp"},
{FLOWSPEC_ICMP_TYPE, "type"},
{FLOWSPEC_ICMP_CODE, "code"},
- {FLOWSPEC_TCP_FLAGS, "flags"},
+ {FLOWSPEC_TCP_FLAGS, "tcp"},
{FLOWSPEC_PKT_LEN, "pktlen"},
{FLOWSPEC_DSCP, "dscp"},
{FLOWSPEC_FRAGMENT, "pktfrag"},
ptr += len_written;
break;
case FLOWSPEC_TCP_FLAGS:
- ret = bgp_flowspec_tcpflags_decode(
+ ret = bgp_flowspec_bitmask_decode(
type_util,
nlri_content+offset,
len - offset,
ptr += len_written;
break;
case FLOWSPEC_FRAGMENT:
- ret = bgp_flowspec_fragment_type_decode(
- type_util,
- nlri_content + offset,
- len - offset, local_string,
- &error);
+ ret = bgp_flowspec_bitmask_decode(
+ type_util,
+ nlri_content+offset,
+ len - offset,
+ local_string, &error);
if (ret <= 0)
break;
if (json_path) {
afi_t afi;
safi_t safi;
- bgp_map_afi_safi_iana2int(ntohs(mpc.afi), mpc.safi,
- &afi, &safi);
+ (void)bgp_map_afi_safi_iana2int(ntohs(mpc.afi),
+ mpc.safi, &afi, &safi);
+
if (use_json) {
switch (afi) {
case AFI_IP:
#include "prefix.h"
#include "zclient.h"
#include "jhash.h"
+#include "pbr.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_pbr.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_flowspec_private.h"
DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry")
DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match")
DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action")
DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context")
+DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value")
RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
id_entry, bgp_pbr_interface_compare);
_cnt++; \
} while (0)
+/* this structure can be used for port range,
+ * but also for other values range like packet length range
+ */
struct bgp_pbr_range_port {
uint16_t min_port;
uint16_t max_port;
};
+/* this structure can be used to filter with a mask
+ * for instance it supports not instructions like for
+ * tcpflags
+ */
+struct bgp_pbr_val_mask {
+ uint16_t val;
+ uint16_t mask;
+};
+
+/* this structure is used to pass instructs
+ * so that BGP can create pbr instructions to ZEBRA
+ */
+struct bgp_pbr_filter {
+ vrf_id_t vrf_id;
+ struct prefix *src;
+ struct prefix *dst;
+ uint8_t protocol;
+ struct bgp_pbr_range_port *pkt_len;
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_val_mask *tcp_flags;
+ struct bgp_pbr_val_mask *dscp;
+ struct bgp_pbr_val_mask *pkt_len_val;
+ struct bgp_pbr_val_mask *fragment;
+};
+
+/* this structure is used to contain OR instructions
+ * so that BGP can create multiple pbr instructions
+ * to ZEBRA
+ */
+struct bgp_pbr_or_filter {
+ struct list *tcpflags;
+ struct list *dscp;
+ struct list *pkt_len;
+ struct list *fragment;
+ struct list *icmp_type;
+ struct list *icmp_code;
+};
+
+static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf,
+ struct nexthop *nh,
+ float *rate);
+
+static bool bgp_pbr_extract_enumerate_unary_opposite(
+ uint8_t unary_operator,
+ struct bgp_pbr_val_mask *and_valmask,
+ struct list *or_valmask, uint32_t value,
+ uint8_t type_entry)
+{
+ if (unary_operator == OPERATOR_UNARY_AND && and_valmask) {
+ if (type_entry == FLOWSPEC_TCP_FLAGS) {
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS &
+ ~(value);
+ } else if (type_entry == FLOWSPEC_DSCP ||
+ type_entry == FLOWSPEC_PKT_LEN ||
+ type_entry == FLOWSPEC_FRAGMENT) {
+ and_valmask->val = value;
+ and_valmask->mask = 1; /* inverse */
+ }
+ } else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) {
+ and_valmask = XCALLOC(MTYPE_PBR_VALMASK,
+ sizeof(struct bgp_pbr_val_mask));
+ if (type_entry == FLOWSPEC_TCP_FLAGS) {
+ and_valmask->val = TCP_HEADER_ALL_FLAGS;
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS &
+ ~(value);
+ } else if (type_entry == FLOWSPEC_DSCP ||
+ type_entry == FLOWSPEC_FRAGMENT ||
+ type_entry == FLOWSPEC_PKT_LEN) {
+ and_valmask->val = value;
+ and_valmask->mask = 1; /* inverse */
+ }
+ listnode_add(or_valmask, and_valmask);
+ } else if (type_entry == FLOWSPEC_ICMP_CODE ||
+ type_entry == FLOWSPEC_ICMP_TYPE)
+ return false;
+ return true;
+}
+
+/* TCP : FIN and SYN -> val = ALL; mask = 3
+ * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST)
+ * other variables type: dscp, pkt len, fragment
+ * - value is copied in bgp_pbr_val_mask->val value
+ * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1
+ */
+static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[],
+ int num, uint8_t unary_operator,
+ void *valmask, uint8_t type_entry)
+{
+ int i = 0;
+ struct bgp_pbr_val_mask *and_valmask = NULL;
+ struct list *or_valmask = NULL;
+ bool ret;
+
+ if (valmask) {
+ if (unary_operator == OPERATOR_UNARY_AND) {
+ and_valmask = (struct bgp_pbr_val_mask *)valmask;
+ memset(and_valmask, 0, sizeof(struct bgp_pbr_val_mask));
+ } else if (unary_operator == OPERATOR_UNARY_OR) {
+ or_valmask = (struct list *)valmask;
+ }
+ }
+ for (i = 0; i < num; i++) {
+ if (i != 0 && list[i].unary_operator !=
+ unary_operator)
+ return false;
+ if (!(list[i].compare_operator &
+ OPERATOR_COMPARE_EQUAL_TO) &&
+ !(list[i].compare_operator &
+ OPERATOR_COMPARE_EXACT_MATCH)) {
+ if ((list[i].compare_operator &
+ OPERATOR_COMPARE_LESS_THAN) &&
+ (list[i].compare_operator &
+ OPERATOR_COMPARE_GREATER_THAN)) {
+ ret = bgp_pbr_extract_enumerate_unary_opposite(
+ unary_operator, and_valmask,
+ or_valmask, list[i].value,
+ type_entry);
+ if (ret == false)
+ return ret;
+ continue;
+ }
+ return false;
+ }
+ if (unary_operator == OPERATOR_UNARY_AND && and_valmask) {
+ if (type_entry == FLOWSPEC_TCP_FLAGS)
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS & list[i].value;
+ } else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) {
+ and_valmask = XCALLOC(MTYPE_PBR_VALMASK,
+ sizeof(struct bgp_pbr_val_mask));
+ if (type_entry == FLOWSPEC_TCP_FLAGS) {
+ and_valmask->val = TCP_HEADER_ALL_FLAGS;
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS & list[i].value;
+ } else if (type_entry == FLOWSPEC_DSCP ||
+ type_entry == FLOWSPEC_ICMP_TYPE ||
+ type_entry == FLOWSPEC_ICMP_CODE ||
+ type_entry == FLOWSPEC_FRAGMENT ||
+ type_entry == FLOWSPEC_PKT_LEN)
+ and_valmask->val = list[i].value;
+ listnode_add(or_valmask, and_valmask);
+ }
+ }
+ if (unary_operator == OPERATOR_UNARY_AND && and_valmask
+ && type_entry == FLOWSPEC_TCP_FLAGS)
+ and_valmask->val = TCP_HEADER_ALL_FLAGS;
+ return true;
+}
+
+/* if unary operator can either be UNARY_OR/AND/OR-AND.
+ * in the latter case, combinationf of both is not handled
+ */
+static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[],
+ int num, uint8_t unary_operator,
+ void *valmask, uint8_t type_entry)
+{
+ bool ret;
+ uint8_t unary_operator_val = unary_operator;
+ bool double_check = false;
+
+ if ((unary_operator & OPERATOR_UNARY_OR) &&
+ (unary_operator & OPERATOR_UNARY_AND)) {
+ unary_operator_val = OPERATOR_UNARY_AND;
+ double_check = true;
+ } else
+ unary_operator_val = unary_operator;
+ ret = bgp_pbr_extract_enumerate_unary(list, num, unary_operator_val,
+ valmask, type_entry);
+ if (!ret && double_check)
+ ret = bgp_pbr_extract_enumerate_unary(list, num,
+ OPERATOR_UNARY_OR,
+ valmask,
+ type_entry);
+ return ret;
+}
+
+/* returns the unary operator that is in the list
+ * return 0 if both operators are used
+ */
+static uint8_t bgp_pbr_match_val_get_operator(struct bgp_pbr_match_val list[],
+ int num)
+
+{
+ int i;
+ uint8_t unary_operator = OPERATOR_UNARY_AND;
+
+ for (i = 0; i < num; i++) {
+ if (i == 0)
+ continue;
+ if (list[i].unary_operator & OPERATOR_UNARY_OR)
+ unary_operator = OPERATOR_UNARY_OR;
+ if ((list[i].unary_operator & OPERATOR_UNARY_AND
+ && unary_operator == OPERATOR_UNARY_OR) ||
+ (list[i].unary_operator & OPERATOR_UNARY_OR
+ && unary_operator == OPERATOR_UNARY_AND))
+ return 0;
+ }
+ return unary_operator;
+}
+
+
/* return true if extraction ok
*/
static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
{
+ bool enumerate_icmp = false;
+
/* because bgp pbr entry may contain unsupported
* combinations, a message will be displayed here if
* not supported.
* - combination src/dst => drop
* - combination srcport + @IP
*/
- if (api->match_icmp_type_num || api->match_packet_length_num
- || api->match_dscp_num || api->match_tcpflags_num) {
- if (BGP_DEBUG(pbr, PBR)) {
- bgp_pbr_print_policy_route(api);
- zlog_debug("BGP: some SET actions not supported by Zebra. ignoring.");
- zlog_debug("BGP: case icmp or length or dscp or tcp flags");
- }
- return 0;
- }
-
if (api->match_protocol_num > 1) {
if (BGP_DEBUG(pbr, PBR))
zlog_debug("BGP: match protocol operations:"
}
if (api->match_protocol_num == 1 &&
api->protocol[0].value != PROTOCOL_UDP &&
+ api->protocol[0].value != PROTOCOL_ICMP &&
api->protocol[0].value != PROTOCOL_TCP) {
if (BGP_DEBUG(pbr, PBR))
zlog_debug("BGP: match protocol operations:"
"too complex. ignoring.");
return 0;
}
+ if (!bgp_pbr_extract_enumerate(api->tcpflags,
+ api->match_tcpflags_num,
+ OPERATOR_UNARY_AND |
+ OPERATOR_UNARY_OR, NULL,
+ FLOWSPEC_TCP_FLAGS)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match tcp flags:"
+ "too complex. ignoring.");
+ return 0;
+ }
+ if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) {
+ if (!bgp_pbr_extract_enumerate(api->icmp_type,
+ api->match_icmp_type_num,
+ OPERATOR_UNARY_OR, NULL,
+ FLOWSPEC_ICMP_TYPE)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match icmp type operations:"
+ "too complex. ignoring.");
+ return 0;
+ }
+ enumerate_icmp = true;
+ }
+ if (!bgp_pbr_extract(api->icmp_code, api->match_icmp_code_num, NULL)) {
+ if (!bgp_pbr_extract_enumerate(api->icmp_code,
+ api->match_icmp_code_num,
+ OPERATOR_UNARY_OR, NULL,
+ FLOWSPEC_ICMP_CODE)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match icmp code operations:"
+ "too complex. ignoring.");
+ return 0;
+ } else if (api->match_icmp_type_num > 1 &&
+ enumerate_icmp == false) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match icmp code is enumerate"
+ ", and icmp type is not."
+ " too complex. ignoring.");
+ return 0;
+ }
+ }
if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) {
if (BGP_DEBUG(pbr, PBR))
zlog_debug("BGP: match port operations:"
"too complex. ignoring.");
return 0;
}
+ if (api->match_packet_length_num) {
+ bool ret;
+
+ ret = bgp_pbr_extract(api->packet_length,
+ api->match_packet_length_num, NULL);
+ if (!ret)
+ ret = bgp_pbr_extract_enumerate(api->packet_length,
+ api->match_packet_length_num,
+ OPERATOR_UNARY_OR
+ | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_PKT_LEN);
+ if (!ret) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match packet length operations:"
+ "too complex. ignoring.");
+ return 0;
+ }
+ }
+ if (api->match_dscp_num) {
+ if (!bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num,
+ OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_DSCP)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match DSCP operations:"
+ "too complex. ignoring.");
+ return 0;
+ }
+ }
+ if (api->match_fragment_num) {
+ char fail_str[64];
+ bool success;
+
+ success = bgp_pbr_extract_enumerate(api->fragment,
+ api->match_fragment_num,
+ OPERATOR_UNARY_OR
+ | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_FRAGMENT);
+ if (success) {
+ int i;
+
+ for (i = 0; i < api->match_fragment_num; i++) {
+ if (api->fragment[i].value != 1 &&
+ api->fragment[i].value != 2 &&
+ api->fragment[i].value != 4 &&
+ api->fragment[i].value != 8) {
+ success = false;
+ sprintf(fail_str,
+ "Value not valid (%d) for this implementation",
+ api->fragment[i].value);
+ }
+ }
+ } else
+ sprintf(fail_str, "too complex. ignoring");
+ if (!success) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match fragment operation (%d) %s",
+ api->match_fragment_num,
+ fail_str);
+ return 0;
+ }
+ }
+
/* no combinations with both src_port and dst_port
* or port with src_port and dst_port
*/
" too complex. ignoring.");
return 0;
}
+ if ((api->match_src_port_num || api->match_dst_port_num
+ || api->match_port_num) && (api->match_icmp_type_num
+ || api->match_icmp_code_num)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match multiple port/imcp operations:"
+ " too complex. ignoring.");
+ return 0;
+ }
if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
!(api->match_bitmask & PREFIX_DST_PRESENT)) {
if (BGP_DEBUG(pbr, PBR)) {
key = jhash_1word(pbm->vrf_id, 0x4312abde);
key = jhash_1word(pbm->flags, key);
+ key = jhash_1word(pbm->pkt_len_min, key);
+ key = jhash_1word(pbm->pkt_len_max, key);
+ key = jhash_1word(pbm->tcp_flags, key);
+ key = jhash_1word(pbm->tcp_mask_flags, key);
+ key = jhash_1word(pbm->dscp_value, key);
+ key = jhash_1word(pbm->fragment, key);
return jhash_1word(pbm->type, key);
}
if (r1->action != r2->action)
return 0;
+ if (r1->pkt_len_min != r2->pkt_len_min)
+ return 0;
+
+ if (r1->pkt_len_max != r2->pkt_len_max)
+ return 0;
+
+ if (r1->tcp_flags != r2->tcp_flags)
+ return 0;
+
+ if (r1->tcp_mask_flags != r2->tcp_mask_flags)
+ return 0;
+
+ if (r1->dscp_value != r2->dscp_value)
+ return 0;
+
+ if (r1->fragment != r2->fragment)
+ return 0;
return 1;
}
ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
i > 0 ? NULL : "@tcpflags ");
- if (api->match_bitmask & FRAGMENT_PRESENT) {
+ if (api->match_fragment_num)
INCREMENT_DISPLAY(ptr, nb_items);
- ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask);
- }
+ for (i = 0; i < api->match_fragment_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i],
+ i > 0 ? NULL : "@fragment ");
if (!nb_items)
ptr = return_string;
else
return HASHWALK_ABORT;
}
-static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
- struct bgp_info *binfo,
- vrf_id_t vrf_id,
- struct prefix *src,
- struct prefix *dst,
- uint8_t protocol,
- struct bgp_pbr_range_port *src_port,
- struct bgp_pbr_range_port *dst_port)
+static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf)
{
struct bgp_pbr_match temp;
struct bgp_pbr_match_entry temp2;
struct bgp_pbr_match *bpm;
struct bgp_pbr_match_entry *bpme;
struct bgp_pbr_match_entry_remain bpmer;
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_range_port *pkt_len;
+
+ if (!bpf)
+ return;
+ src_port = bpf->src_port;
+ dst_port = bpf->dst_port;
+ pkt_len = bpf->pkt_len;
/* as we don't know information from EC
* look for bpm that have the bpm
*/
memset(&temp2, 0, sizeof(temp2));
memset(&temp, 0, sizeof(temp));
- if (src) {
+ if (bpf->src) {
temp.flags |= MATCH_IP_SRC_SET;
- prefix_copy(&temp2.src, src);
+ prefix_copy(&temp2.src, bpf->src);
} else
temp2.src.family = AF_INET;
- if (dst) {
+ if (bpf->dst) {
temp.flags |= MATCH_IP_DST_SET;
- prefix_copy(&temp2.dst, dst);
+ prefix_copy(&temp2.dst, bpf->dst);
} else
temp2.dst.family = AF_INET;
- if (src_port) {
+ if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
temp.flags |= MATCH_PORT_SRC_SET;
temp2.src_port_min = src_port->min_port;
if (src_port->max_port) {
temp2.src_port_max = src_port->max_port;
}
}
- if (dst_port) {
+ if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
temp.flags |= MATCH_PORT_DST_SET;
temp2.dst_port_min = dst_port->min_port;
if (dst_port->max_port) {
temp2.dst_port_max = dst_port->max_port;
}
}
- temp2.proto = protocol;
+ temp2.proto = bpf->protocol;
+
+ if (pkt_len) {
+ temp.pkt_len_min = pkt_len->min_port;
+ if (pkt_len->max_port)
+ temp.pkt_len_max = pkt_len->max_port;
+ } else if (bpf->pkt_len_val) {
+ if (bpf->pkt_len_val->mask)
+ temp.flags |= MATCH_PKT_LEN_INVERSE_SET;
+ temp.pkt_len_min = bpf->pkt_len_val->val;
+ }
+ if (bpf->tcp_flags) {
+ temp.tcp_flags = bpf->tcp_flags->val;
+ temp.tcp_mask_flags = bpf->tcp_flags->mask;
+ }
+ if (bpf->dscp) {
+ if (bpf->dscp->mask)
+ temp.flags |= MATCH_DSCP_INVERSE_SET;
+ else
+ temp.flags |= MATCH_DSCP_SET;
+ temp.dscp_value = bpf->dscp->val;
+ }
+ if (bpf->fragment) {
+ if (bpf->fragment->mask)
+ temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
+ temp.fragment = bpf->fragment->val;
+ }
- if (src == NULL || dst == NULL) {
+ if (bpf->src == NULL || bpf->dst == NULL) {
if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
temp.type = IPSET_NET_PORT;
else
else
temp.type = IPSET_NET_NET;
}
- if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
+ if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
temp.vrf_id = 0;
else
- temp.vrf_id = vrf_id;
+ temp.vrf_id = bpf->vrf_id;
bpme = &temp2;
bpm = &temp;
bpme->backpointer = bpm;
}
}
-static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
+static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry)
+{
+ if (type_entry == FLOWSPEC_TCP_FLAGS)
+ return FLOWSPEC_DSCP;
+ if (type_entry == FLOWSPEC_DSCP)
+ return FLOWSPEC_PKT_LEN;
+ if (type_entry == FLOWSPEC_PKT_LEN)
+ return FLOWSPEC_FRAGMENT;
+ if (type_entry == FLOWSPEC_FRAGMENT)
+ return FLOWSPEC_ICMP_TYPE;
+ return 0;
+}
+
+static void bgp_pbr_icmp_action(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof,
+ bool add,
+ struct nexthop *nh,
+ float *rate)
+{
+ struct bgp_pbr_range_port srcp, dstp;
+ struct bgp_pbr_val_mask *icmp_type, *icmp_code;
+ struct listnode *tnode, *cnode;
+
+ if (!bpf)
+ return;
+ if (bpf->protocol != IPPROTO_ICMP)
+ return;
+ bpf->src_port = &srcp;
+ bpf->dst_port = &dstp;
+ /* parse icmp type and lookup appropriate icmp code
+ * if no icmp code found, create as many entryes as
+ * there are listed icmp codes for that icmp type
+ */
+ if (!bpof->icmp_type) {
+ srcp.min_port = 0;
+ srcp.max_port = 255;
+ for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) {
+ dstp.min_port = icmp_code->val;
+ if (add)
+ bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo,
+ bpf, nh, rate);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(
+ bgp, binfo, bpf);
+ }
+ return;
+ }
+ for (ALL_LIST_ELEMENTS_RO(bpof->icmp_type, tnode, icmp_type)) {
+ srcp.min_port = icmp_type->val;
+ srcp.max_port = 0;
+ dstp.max_port = 0;
+ /* only icmp type. create an entry only with icmp type */
+ if (!bpof->icmp_code) {
+ /* icmp type is not one of the above
+ * forge an entry only based on the icmp type
+ */
+ dstp.min_port = 0;
+ dstp.max_port = 255;
+ if (add)
+ bgp_pbr_policyroute_add_to_zebra_unit(
+ bgp, binfo,
+ bpf, nh, rate);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(bgp,
+ binfo, bpf);
+ continue;
+ }
+ for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) {
+ dstp.min_port = icmp_code->val;
+ if (add)
+ bgp_pbr_policyroute_add_to_zebra_unit(
+ bgp, binfo,
+ bpf, nh, rate);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(
+ bgp, binfo, bpf);
+ }
+ }
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof,
+ uint8_t type_entry)
+{
+ struct listnode *node, *nnode;
+ struct bgp_pbr_val_mask *valmask;
+ uint8_t next_type_entry;
+ struct list *orig_list;
+ struct bgp_pbr_val_mask **target_val;
+
+ if (type_entry == 0)
+ return bgp_pbr_policyroute_remove_from_zebra_unit(bgp,
+ binfo, bpf);
+ next_type_entry = bgp_pbr_next_type_entry(type_entry);
+ if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) {
+ orig_list = bpof->tcpflags;
+ target_val = &bpf->tcp_flags;
+ } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
+ orig_list = bpof->dscp;
+ target_val = &bpf->dscp;
+ } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
+ orig_list = bpof->pkt_len;
+ target_val = &bpf->pkt_len_val;
+ } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) {
+ orig_list = bpof->fragment;
+ target_val = &bpf->fragment;
+ } else if (type_entry == FLOWSPEC_ICMP_TYPE &&
+ (bpof->icmp_type || bpof->icmp_code)) {
+ /* enumerate list for icmp - must be last one */
+ bgp_pbr_icmp_action(bgp, binfo, bpf, bpof, false, NULL, NULL);
+ return;
+ } else {
+ return bgp_pbr_policyroute_remove_from_zebra_recursive(bgp,
+ binfo,
+ bpf, bpof,
+ next_type_entry);
+ }
+ for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) {
+ *target_val = valmask;
+ bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ next_type_entry);
+ }
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof)
+{
+ if (!bpof)
+ return bgp_pbr_policyroute_remove_from_zebra_unit(bgp,
+ binfo,
+ bpf);
+ if (bpof->tcpflags)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ FLOWSPEC_TCP_FLAGS);
+ else if (bpof->dscp)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ FLOWSPEC_DSCP);
+ else if (bpof->pkt_len)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ FLOWSPEC_PKT_LEN);
+ else if (bpof->fragment)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ FLOWSPEC_FRAGMENT);
+ else if (bpof->icmp_type || bpof->icmp_code)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ FLOWSPEC_ICMP_TYPE);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf);
+ /* flush bpof */
+ if (bpof->tcpflags)
+ list_delete_all_node(bpof->tcpflags);
+ if (bpof->dscp)
+ list_delete_all_node(bpof->dscp);
+ if (bpof->pkt_len)
+ list_delete_all_node(bpof->pkt_len);
+ if (bpof->fragment)
+ list_delete_all_node(bpof->fragment);
+}
+
+static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
struct bgp_info *binfo,
- vrf_id_t vrf_id,
- struct prefix *src,
- struct prefix *dst,
+ struct bgp_pbr_filter *bpf,
struct nexthop *nh,
- float *rate,
- uint8_t protocol,
- struct bgp_pbr_range_port *src_port,
- struct bgp_pbr_range_port *dst_port)
+ float *rate)
{
struct bgp_pbr_match temp;
struct bgp_pbr_match_entry temp2;
struct bgp_pbr_action temp3;
struct bgp_pbr_action *bpa = NULL;
struct bgp_pbr_match_entry_remain bpmer;
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_range_port *pkt_len;
+ if (!bpf)
+ return;
+ src_port = bpf->src_port;
+ dst_port = bpf->dst_port;
+ pkt_len = bpf->pkt_len;
+
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ char bufsrc[64], bufdst[64];
+ char buffer[64];
+ int remaining_len = 0;
+ char protocol_str[16];
+
+ protocol_str[0] = '\0';
+ if (bpf->tcp_flags && bpf->tcp_flags->mask)
+ bpf->protocol = IPPROTO_TCP;
+ if (bpf->protocol)
+ snprintf(protocol_str, sizeof(protocol_str),
+ "proto %d", bpf->protocol);
+ buffer[0] = '\0';
+ if (bpf->protocol == IPPROTO_ICMP && src_port && dst_port)
+ remaining_len += snprintf(buffer, sizeof(buffer),
+ "type %d, code %d",
+ src_port->min_port, dst_port->min_port);
+ else if (bpf->protocol == IPPROTO_UDP ||
+ bpf->protocol == IPPROTO_TCP) {
+
+ if (src_port && src_port->min_port)
+ remaining_len += snprintf(buffer,
+ sizeof(buffer),
+ "from [%u:%u]",
+ src_port->min_port,
+ src_port->max_port ?
+ src_port->max_port :
+ src_port->min_port);
+ if (dst_port && dst_port->min_port)
+ remaining_len += snprintf(buffer +
+ remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "to [%u:%u]",
+ dst_port->min_port,
+ dst_port->max_port ?
+ dst_port->max_port :
+ dst_port->min_port);
+ }
+ if (pkt_len && (pkt_len->min_port || pkt_len->max_port)) {
+ remaining_len += snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ " len [%u:%u]",
+ pkt_len->min_port,
+ pkt_len->max_port ?
+ pkt_len->max_port :
+ pkt_len->min_port);
+ } else if (bpf->pkt_len_val) {
+ remaining_len += snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ " %s len %u",
+ bpf->pkt_len_val->mask
+ ? "!" : "",
+ bpf->pkt_len_val->val);
+ }
+ if (bpf->tcp_flags) {
+ remaining_len += snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "tcpflags %x/%x",
+ bpf->tcp_flags->val,
+ bpf->tcp_flags->mask);
+ }
+ if (bpf->dscp) {
+ snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "%s dscp %d",
+ bpf->dscp->mask
+ ? "!" : "",
+ bpf->dscp->val);
+ }
+ zlog_info("BGP: adding FS PBR from %s to %s, %s %s",
+ bpf->src == NULL ? "<all>" :
+ prefix2str(bpf->src, bufsrc, sizeof(bufsrc)),
+ bpf->dst == NULL ? "<all>" :
+ prefix2str(bpf->dst, bufdst, sizeof(bufdst)),
+ protocol_str, buffer);
+ }
/* look for bpa first */
memset(&temp3, 0, sizeof(temp3));
if (rate)
temp3.rate = *rate;
if (nh)
memcpy(&temp3.nh, nh, sizeof(struct nexthop));
- temp3.vrf_id = vrf_id;
+ temp3.vrf_id = bpf->vrf_id;
bpa = hash_get(bgp->pbr_action_hash, &temp3,
bgp_pbr_action_alloc_intern);
/* then look for bpm */
memset(&temp, 0, sizeof(temp));
- if (src == NULL || dst == NULL) {
- if ((src_port && src_port->min_port) ||
- (dst_port && dst_port->min_port))
- temp.type = IPSET_NET_PORT;
- else
- temp.type = IPSET_NET;
- } else {
- if ((src_port && src_port->min_port) ||
- (dst_port && dst_port->min_port))
- temp.type = IPSET_NET_PORT_NET;
- else
- temp.type = IPSET_NET_NET;
- }
- temp.vrf_id = vrf_id;
- if (src)
+ temp.vrf_id = bpf->vrf_id;
+ if (bpf->src)
temp.flags |= MATCH_IP_SRC_SET;
- if (dst)
+ if (bpf->dst)
temp.flags |= MATCH_IP_DST_SET;
- if (src_port && src_port->min_port)
+ if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
temp.flags |= MATCH_PORT_SRC_SET;
- if (dst_port && dst_port->min_port)
+ }
+ if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
temp.flags |= MATCH_PORT_DST_SET;
+ }
if (src_port && src_port->max_port)
temp.flags |= MATCH_PORT_SRC_RANGE_SET;
if (dst_port && dst_port->max_port)
temp.flags |= MATCH_PORT_DST_RANGE_SET;
+
+ if (bpf->src == NULL || bpf->dst == NULL) {
+ if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+ temp.type = IPSET_NET_PORT;
+ else
+ temp.type = IPSET_NET;
+ } else {
+ if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+ temp.type = IPSET_NET_PORT_NET;
+ else
+ temp.type = IPSET_NET_NET;
+ }
+ if (pkt_len) {
+ temp.pkt_len_min = pkt_len->min_port;
+ if (pkt_len->max_port)
+ temp.pkt_len_max = pkt_len->max_port;
+ } else if (bpf->pkt_len_val) {
+ if (bpf->pkt_len_val->mask)
+ temp.flags |= MATCH_PKT_LEN_INVERSE_SET;
+ temp.pkt_len_min = bpf->pkt_len_val->val;
+ }
+ if (bpf->tcp_flags) {
+ temp.tcp_flags = bpf->tcp_flags->val;
+ temp.tcp_mask_flags = bpf->tcp_flags->mask;
+ }
+ if (bpf->dscp) {
+ if (bpf->dscp->mask)
+ temp.flags |= MATCH_DSCP_INVERSE_SET;
+ else
+ temp.flags |= MATCH_DSCP_SET;
+ temp.dscp_value = bpf->dscp->val;
+ }
+ if (bpf->fragment) {
+ if (bpf->fragment->mask)
+ temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
+ temp.fragment = bpf->fragment->val;
+ }
temp.action = bpa;
bpm = hash_get(bgp->pbr_match_hash, &temp,
bgp_pbr_match_alloc_intern);
}
memset(&temp2, 0, sizeof(temp2));
- if (src)
- prefix_copy(&temp2.src, src);
+ if (bpf->src)
+ prefix_copy(&temp2.src, bpf->src);
else
temp2.src.family = AF_INET;
- if (dst)
- prefix_copy(&temp2.dst, dst);
+ if (bpf->dst)
+ prefix_copy(&temp2.dst, bpf->dst);
else
temp2.dst.family = AF_INET;
temp2.src_port_min = src_port ? src_port->min_port : 0;
temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
temp2.src_port_max = src_port ? src_port->max_port : 0;
temp2.dst_port_max = dst_port ? dst_port->max_port : 0;
- temp2.proto = protocol;
+ temp2.proto = bpf->protocol;
if (bpm)
bpme = hash_get(bpm->entry_hash, &temp2,
bgp_pbr_match_entry_alloc_intern);
* it will be suppressed subsequently
*/
/* ip rule add */
- if (!bpa->installed) {
+ if (!bpa->installed && !bpa->install_in_progress) {
bgp_send_pbr_rule_action(bpa, true);
bgp_zebra_announce_default(bgp, nh,
AFI_IP, bpa->table_id, true);
}
+static void bgp_pbr_policyroute_add_to_zebra_recursive(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof,
+ struct nexthop *nh,
+ float *rate,
+ uint8_t type_entry)
+{
+ struct listnode *node, *nnode;
+ struct bgp_pbr_val_mask *valmask;
+ uint8_t next_type_entry;
+ struct list *orig_list;
+ struct bgp_pbr_val_mask **target_val;
+
+ if (type_entry == 0)
+ return bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf,
+ nh, rate);
+ next_type_entry = bgp_pbr_next_type_entry(type_entry);
+ if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) {
+ orig_list = bpof->tcpflags;
+ target_val = &bpf->tcp_flags;
+ } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
+ orig_list = bpof->dscp;
+ target_val = &bpf->dscp;
+ } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
+ orig_list = bpof->pkt_len;
+ target_val = &bpf->pkt_len_val;
+ } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) {
+ orig_list = bpof->fragment;
+ target_val = &bpf->fragment;
+ } else if (type_entry == FLOWSPEC_ICMP_TYPE &&
+ (bpof->icmp_type || bpof->icmp_code)) {
+ /* enumerate list for icmp - must be last one */
+ bgp_pbr_icmp_action(bgp, binfo, bpf, bpof, true, nh, rate);
+ return;
+ } else {
+ return bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof, nh, rate,
+ next_type_entry);
+ }
+ for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) {
+ *target_val = valmask;
+ bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ nh, rate,
+ next_type_entry);
+ }
+}
+
+static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof,
+ struct nexthop *nh,
+ float *rate)
+{
+ if (!bpof)
+ return bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo,
+ bpf, nh, rate);
+ if (bpof->tcpflags)
+ bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ nh, rate,
+ FLOWSPEC_TCP_FLAGS);
+ else if (bpof->dscp)
+ bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ nh, rate,
+ FLOWSPEC_DSCP);
+ else if (bpof->pkt_len)
+ bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ nh, rate,
+ FLOWSPEC_PKT_LEN);
+ else if (bpof->fragment)
+ bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof,
+ nh, rate,
+ FLOWSPEC_FRAGMENT);
+ else if (bpof->icmp_type || bpof->icmp_code)
+ bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo,
+ bpf, bpof, nh, rate,
+ FLOWSPEC_ICMP_TYPE);
+ else
+ bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf,
+ nh, rate);
+ /* flush bpof */
+ if (bpof->tcpflags)
+ list_delete_all_node(bpof->tcpflags);
+ if (bpof->dscp)
+ list_delete_all_node(bpof->dscp);
+ if (bpof->pkt_len)
+ list_delete_all_node(bpof->pkt_len);
+ if (bpof->fragment)
+ list_delete_all_node(bpof->fragment);
+ if (bpof->icmp_type)
+ list_delete_all_node(bpof->icmp_type);
+ if (bpof->icmp_code)
+ list_delete_all_node(bpof->icmp_code);
+}
+
static void bgp_pbr_handle_entry(struct bgp *bgp,
struct bgp_info *binfo,
struct bgp_pbr_entry_main *api,
struct prefix *src = NULL, *dst = NULL;
uint8_t proto = 0;
struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL;
- struct bgp_pbr_range_port range;
+ struct bgp_pbr_range_port range, range_icmp_code;
+ struct bgp_pbr_range_port pkt_len;
+ struct bgp_pbr_filter bpf;
+ uint8_t kind_enum;
+ struct bgp_pbr_or_filter bpof;
+ struct bgp_pbr_val_mask bpvm;
memset(&nh, 0, sizeof(struct nexthop));
+ memset(&bpf, 0, sizeof(struct bgp_pbr_filter));
+ memset(&bpof, 0, sizeof(struct bgp_pbr_or_filter));
if (api->match_bitmask & PREFIX_SRC_PRESENT)
src = &api->src_prefix;
if (api->match_bitmask & PREFIX_DST_PRESENT)
dstp = ⦥
srcp = NULL;
}
+ if (api->match_icmp_type_num >= 1) {
+ proto = IPPROTO_ICMP;
+ if (bgp_pbr_extract(api->icmp_type,
+ api->match_icmp_type_num,
+ &range))
+ srcp = ⦥
+ else {
+ bpof.icmp_type = list_new();
+ bgp_pbr_extract_enumerate(api->icmp_type,
+ api->match_icmp_type_num,
+ OPERATOR_UNARY_OR,
+ bpof.icmp_type,
+ FLOWSPEC_ICMP_TYPE);
+ }
+ }
+ if (api->match_icmp_code_num >= 1) {
+ proto = IPPROTO_ICMP;
+ if (bgp_pbr_extract(api->icmp_code,
+ api->match_icmp_code_num,
+ &range_icmp_code))
+ dstp = &range_icmp_code;
+ else {
+ bpof.icmp_code = list_new();
+ bgp_pbr_extract_enumerate(api->icmp_code,
+ api->match_icmp_code_num,
+ OPERATOR_UNARY_OR,
+ bpof.icmp_code,
+ FLOWSPEC_ICMP_CODE);
+ }
+ }
+
+ if (api->match_tcpflags_num) {
+ kind_enum = bgp_pbr_match_val_get_operator(api->tcpflags,
+ api->match_tcpflags_num);
+ if (kind_enum == OPERATOR_UNARY_AND) {
+ bpf.tcp_flags = &bpvm;
+ bgp_pbr_extract_enumerate(api->tcpflags,
+ api->match_tcpflags_num,
+ OPERATOR_UNARY_AND,
+ bpf.tcp_flags,
+ FLOWSPEC_TCP_FLAGS);
+ } else if (kind_enum == OPERATOR_UNARY_OR) {
+ bpof.tcpflags = list_new();
+ bgp_pbr_extract_enumerate(api->tcpflags,
+ api->match_tcpflags_num,
+ OPERATOR_UNARY_OR,
+ bpof.tcpflags,
+ FLOWSPEC_TCP_FLAGS);
+ }
+ }
+ if (api->match_packet_length_num) {
+ bool ret;
+
+ ret = bgp_pbr_extract(api->packet_length,
+ api->match_packet_length_num,
+ &pkt_len);
+ if (ret)
+ bpf.pkt_len = &pkt_len;
+ else {
+ bpof.pkt_len = list_new();
+ bgp_pbr_extract_enumerate(api->packet_length,
+ api->match_packet_length_num,
+ OPERATOR_UNARY_OR,
+ bpof.pkt_len,
+ FLOWSPEC_PKT_LEN);
+ }
+ }
+ if (api->match_dscp_num >= 1) {
+ bpof.dscp = list_new();
+ bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num,
+ OPERATOR_UNARY_OR,
+ bpof.dscp, FLOWSPEC_DSCP);
+ }
+ if (api->match_fragment_num) {
+ bpof.fragment = list_new();
+ bgp_pbr_extract_enumerate(api->fragment,
+ api->match_fragment_num,
+ OPERATOR_UNARY_OR,
+ bpof.fragment,
+ FLOWSPEC_FRAGMENT);
+ }
+ bpf.vrf_id = api->vrf_id;
+ bpf.src = src;
+ bpf.dst = dst;
+ bpf.protocol = proto;
+ bpf.src_port = srcp;
+ bpf.dst_port = dstp;
if (!add)
- return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
- api->vrf_id, src, dst,
- proto, srcp, dstp);
+ return bgp_pbr_policyroute_remove_from_zebra(bgp,
+ binfo,
+ &bpf, &bpof);
/* no action for add = true */
for (i = 0; i < api->action_num; i++) {
switch (api->actions[i].action) {
nh.vrf_id = api->vrf_id;
nh.type = NEXTHOP_TYPE_BLACKHOLE;
bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
- api->vrf_id, src, dst,
- &nh, &rate, proto,
- srcp, dstp);
+ &bpf, &bpof,
+ &nh, &rate);
} else {
/* update rate. can be reentrant */
rate = api->actions[i].u.r.rate;
api->actions[i].u.zr.redirect_ip_v4.s_addr;
nh.vrf_id = api->vrf_id;
bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
- api->vrf_id,
- src, dst,
- &nh, &rate, proto,
- srcp, dstp);
+ &bpf, &bpof,
+ &nh, &rate);
/* XXX combination with REDIRECT_VRF
* + REDIRECT_NH_IP not done
*/
nh.vrf_id = api->actions[i].u.redirect_vrf;
nh.type = NEXTHOP_TYPE_IPV4;
bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
- api->vrf_id,
- src, dst,
- &nh, &rate, proto,
- srcp, dstp);
+ &bpf, &bpof,
+ &nh, &rate);
continue_loop = 0;
break;
case ACTION_MARKING:
#define PREFIX_SRC_PRESENT (1 << 0)
#define PREFIX_DST_PRESENT (1 << 1)
-#define FRAGMENT_PRESENT (1 << 2)
uint8_t match_bitmask;
uint8_t match_src_port_num;
uint8_t match_packet_length_num;
uint8_t match_dscp_num;
uint8_t match_tcpflags_num;
+ uint8_t match_fragment_num;
struct prefix src_prefix;
struct prefix dst_prefix;
#define PROTOCOL_UDP 17
#define PROTOCOL_TCP 6
+#define PROTOCOL_ICMP 1
struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX];
+
struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX];
- struct bgp_pbr_fragment_val fragment;
+ struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX];
uint16_t action_num;
struct bgp_pbr_entry_action actions[ACTIONS_MAX_NUM];
*/
uint32_t type;
-#define MATCH_IP_SRC_SET (1 << 0)
-#define MATCH_IP_DST_SET (1 << 1)
-#define MATCH_PORT_SRC_SET (1 << 2)
-#define MATCH_PORT_DST_SET (1 << 3)
-#define MATCH_PORT_SRC_RANGE_SET (1 << 4)
-#define MATCH_PORT_DST_RANGE_SET (1 << 5)
uint32_t flags;
+ uint16_t pkt_len_min;
+ uint16_t pkt_len_max;
+ uint16_t tcp_flags;
+ uint16_t tcp_mask_flags;
+ uint8_t dscp_value;
+ uint8_t fragment;
+
vrf_id_t vrf_id;
/* unique identifier for ipset create transaction
prefix2str(p, buf, PREFIX_STRLEN);
len = vty_out(vty, "%s", buf);
} else if (p->family == AF_EVPN) {
-#if defined(HAVE_CUMULUS)
if (!json)
len = vty_out(
vty, "%s",
BUFSIZ));
else
bgp_evpn_route2json((struct prefix_evpn *)p, json);
-#else
- prefix2str(p, buf, PREFIX_STRLEN);
- len = vty_out(vty, "%s", buf);
-#endif
} else if (p->family == AF_FLOWSPEC) {
route_vty_out_flowspec(vty, p, NULL,
json ?
{
char buf[INET6_ADDRSTRLEN];
char buf1[BUFSIZ];
-#if defined(HAVE_CUMULUS)
char buf2[EVPN_ROUTE_STRLEN];
-#endif
struct attr *attr;
int sockunion_vty_out(struct vty *, union sockunion *);
time_t tbuf;
json_nexthop_global = json_object_new_object();
}
-#if defined(HAVE_CUMULUS)
if (!json_paths && safi == SAFI_EVPN) {
char tag_buf[30];
}
}
}
-#endif
attr = binfo->attr;
if (binfo->extra && binfo->extra->damp_info)
bgp_damp_info_vty(vty, binfo, json_path);
-/* Remote Label */
-#if defined(HAVE_CUMULUS)
+ /* Remote Label */
if (binfo->extra && bgp_is_valid_label(&binfo->extra->label[0])
- && safi != SAFI_EVPN)
-#else
- if (binfo->extra && bgp_is_valid_label(&binfo->extra->label[0]))
-#endif
- {
+ && safi != SAFI_EVPN) {
mpls_label_t label =
label_pton(&binfo->extra->label[0]);
if (json_paths)
struct listnode *node, *nnode;
char buf1[RD_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
-#if defined(HAVE_CUMULUS)
char buf3[EVPN_ROUTE_STRLEN];
-#endif
char prefix_str[BUFSIZ];
int count = 0;
int best = 0;
json, "prefix",
prefix2str(p, prefix_str, sizeof(prefix_str)));
} else {
-#if defined(HAVE_CUMULUS)
if (safi == SAFI_EVPN)
vty_out(vty, "BGP routing table entry for %s%s%s\n",
prd ? prefix_rd2str(prd, buf1, sizeof(buf1))
inet_ntop(p->family, &p->u.prefix, buf2,
INET6_ADDRSTRLEN),
p->prefixlen);
-#else
- if (p->family == AF_ETHERNET)
- prefix2str(p, buf2, INET6_ADDRSTRLEN);
- else
- inet_ntop(p->family, &p->u.prefix, buf2,
- INET6_ADDRSTRLEN);
- vty_out(vty, "BGP routing table entry for %s%s%s/%d\n",
- ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
- || safi == SAFI_EVPN)
- ? prefix_rd2str(prd, buf1, sizeof(buf1))
- : ""),
- ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":"
- : "",
- buf2, p->prefixlen);
-#endif
if (has_valid_label)
vty_out(vty, "Local label: %d\n", label);
-#if defined(HAVE_CUMULUS)
if (bgp_labeled_safi(safi) && safi != SAFI_EVPN)
-#else
- if (bgp_labeled_safi(safi))
-#endif
vty_out(vty, "not allocated\n");
}
stream_putl(s, bpa->fwmark);
stream_put(s, pbm->ipset_name,
ZEBRA_IPSET_NAME_SIZE);
+ stream_putw(s, pbm->pkt_len_min);
+ stream_putw(s, pbm->pkt_len_max);
+ stream_putw(s, pbm->tcp_flags);
+ stream_putw(s, pbm->tcp_mask_flags);
+ stream_putc(s, pbm->dscp_value);
+ stream_putc(s, pbm->fragment);
}
/* BGP has established connection with Zebra. */
else {
struct peer *peer1;
+
+ assert(peer->group);
peer1 = listnode_head(peer->group->peer);
if (peer1)
if (peer->group) {
assert(group && peer->group == group);
} else {
- struct listnode *pn;
- pn = listnode_lookup(bgp->peer, peer);
- list_delete_node(bgp->peer, pn);
+ listnode_delete(bgp->peer, peer);
+
peer->group = group;
listnode_add_sort(bgp->peer, peer);
if (VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)) {
redist++;
vty_out(vty, "%sToZebra Groups={", (redist == 1 ? "" : " "));
- if (hc->rfg_export_direct_bgp_l) {
+ if (hc->rfg_export_zebra_l) {
int cnt = 0;
struct listnode *node, *nnode;
struct rfapi_rfg_name *rfgn;
return 1;
}
- if (stream) {
- *vty = stream; /* VTYNL requires vty to be legit */
- *fp = (int (*)(void *, const char *, ...))vty_out;
- *outstream = stream;
- *vty_newline = str_vty_newline(*vty);
- return 1;
- }
-
- return 0;
+ *vty = stream; /* VTYNL requires vty to be legit */
+ *fp = (int (*)(void *, const char *, ...))vty_out;
+ *outstream = stream;
+ *vty_newline = str_vty_newline(*vty);
+ return 1;
}
/* called from bgpd/bgp_vty.c'route_vty_out() */
* match un, vn addresses of NVEs
*/
if (pUn && (rfapi_ip_addr_cmp(pUn, &rfd->un_addr)))
- continue;
+ break;
if (pVn && (rfapi_ip_addr_cmp(pVn, &rfd->vn_addr)))
- continue;
+ break;
#if DEBUG_L2_EXTRA
vnc_zlog_debug_verbose("%s: un, vn match", __func__);
pos += sprintf(pos, "%d/",
ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0);
- pos += sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0);
+ sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0);
return buf;
}
uint8_t subtlv_len;
if (IS_MPLS_TE(isisMplsTE)
+ && circuit->interface != NULL
&& HAS_LINK_PARAMS(
circuit->interface))
/* Update Local and Remote IP
addr_len = sizeof(s_addr);
/* we can read directly to the stream */
- stream_recvfrom(circuit->rcv_stream, circuit->fd,
- circuit->interface->mtu, 0, (struct sockaddr *)&s_addr,
- (socklen_t *)&addr_len);
+ (void)stream_recvfrom(
+ circuit->rcv_stream, circuit->fd, circuit->interface->mtu, 0,
+ (struct sockaddr *)&s_addr, (socklen_t *)&addr_len);
if (s_addr.sll_pkttype == PACKET_OUTGOING) {
/* Read the packet into discard buff */
vty_out(vty, "%-20s %-12s %-6u ",
vid2string(vertex, buff, sizeof(buff)),
vtype2string(vertex->type), vertex->d_N);
- for (unsigned int i = 0; i < MAX(listcount(vertex->Adj_N),
- listcount(vertex->parents));
+ for (unsigned int i = 0;
+ i < MAX(vertex->Adj_N ? listcount(vertex->Adj_N) : 0,
+ vertex->parents ? listcount(vertex->parents) : 0);
i++) {
if (anode) {
adj = listgetdata(anode);
void listnode_delete(struct list *list, void *val)
{
- struct listnode *node;
+ struct listnode *node = listnode_lookup(list, val);
- assert(list);
- for (node = list->head; node; node = node->next) {
- if (node->data == val) {
- if (node->prev)
- node->prev->next = node->next;
- else
- list->head = node->next;
-
- if (node->next)
- node->next->prev = node->prev;
- else
- list->tail = node->prev;
-
- list->count--;
- listnode_free(node);
- return;
- }
- }
+ if (node)
+ list_delete_node(list, node);
}
void *listnode_head(struct list *list)
uint32_t ifindex;
};
+/* TCP flags value shared
+ * those are values of byte 13 of TCP header
+ * as mentioned in rfc793
+ */
+#define TCP_HEADER_FIN (0x01)
+#define TCP_HEADER_SYN (0x02)
+#define TCP_HEADER_RST (0x04)
+#define TCP_HEADER_PSH (0x08)
+#define TCP_HEADER_ACK (0x10)
+#define TCP_HEADER_URG (0x20)
+#define TCP_HEADER_ALL_FLAGS (TCP_HEADER_FIN | TCP_HEADER_SYN \
+ | TCP_HEADER_RST | TCP_HEADER_PSH \
+ | TCP_HEADER_ACK | TCP_HEADER_URG)
+
+/* Pbr IPTable defines
+ * those are common flags shared between BGP and Zebra
+ */
+#define MATCH_IP_SRC_SET (1 << 0)
+#define MATCH_IP_DST_SET (1 << 1)
+#define MATCH_PORT_SRC_SET (1 << 2)
+#define MATCH_PORT_DST_SET (1 << 3)
+#define MATCH_PORT_SRC_RANGE_SET (1 << 4)
+#define MATCH_PORT_DST_RANGE_SET (1 << 5)
+#define MATCH_DSCP_SET (1 << 6)
+#define MATCH_DSCP_INVERSE_SET (1 << 7)
+#define MATCH_PKT_LEN_INVERSE_SET (1 << 8)
+#define MATCH_FRAGMENT_INVERSE_SET (1 << 9)
+#define MATCH_ICMP_SET (1 << 10)
+
extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
struct pbr_rule *zrule);
/* Add new stream to fifo. */
void stream_fifo_push(struct stream_fifo *fifo, struct stream *s)
{
+#if defined DEV_BUILD
+ size_t max, curmax;
+#endif
+
if (fifo->tail)
fifo->tail->next = s;
else
fifo->tail = s;
fifo->tail->next = NULL;
-
+#if !defined DEV_BUILD
atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release);
+#else
+ max = atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release);
+ curmax = atomic_load_explicit(&fifo->max_count, memory_order_relaxed);
+ if (max > curmax)
+ atomic_store_explicit(&fifo->max_count, max,
+ memory_order_relaxed);
+#endif
}
void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s)
/* number of streams in this fifo */
_Atomic size_t count;
+#if defined DEV_BUILD
+ _Atomic size_t max_count;
+#endif
struct stream *head;
struct stream *tail;
char buf[32];
struct handle_sa_ctx ctx = {
.event = event,
+ .msgctx.nsections = 0
};
vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
&& route->type != OSPF6_DEST_TYPE_RANGE
&& ((route->type != OSPF6_DEST_TYPE_ROUTER)
|| !CHECK_FLAG(route->path.router_bits, OSPF6_ROUTER_BIT_E))) {
- if (is_debug)
- zlog_debug(
- "Route type is none of network, range nor ASBR, ignore");
+#if 0
+ zlog_debug(
+ "Route type is none of network, range nor ASBR, ignore");
+#endif
return 0;
}
/* do not generate if the path's area is the same as target area */
if (route->path.area_id == area->area_id) {
- if (is_debug)
- zlog_debug("The route is in the area itself, ignore");
+#if 0
+ zlog_debug("The route is in the area itself, ignore");
+#endif
return 0;
}
/* do not generate if the nexthops belongs to the target area */
if (ospf6_abr_nexthops_belong_to_area(route, area)) {
- if (is_debug)
- zlog_debug(
- "The route's nexthop is in the same area, ignore");
+#if 0
+ zlog_debug("The route's nexthop is in the same area, ignore");
+#endif
return 0;
}
lsa->header);
prefix.family = AF_INET6;
prefix.prefixlen = prefix_lsa->prefix.prefix_length;
- ospf6_prefix_in6_addr(&prefix.u.prefix6, &prefix_lsa->prefix);
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, prefix_lsa,
+ &prefix_lsa->prefix);
if (is_debug)
prefix2str(&prefix, buf, sizeof(buf));
table = oa->ospf6->route_table;
(struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END(
lsa->header);
- ospf6_prefix_in6_addr(&in6, &prefix_lsa->prefix);
+ ospf6_prefix_in6_addr(&in6, prefix_lsa, &prefix_lsa->prefix);
if (buf) {
inet_ntop(AF_INET6, &in6, buf, buflen);
sprintf(&buf[strlen(buf)], "/%d",
route->type = OSPF6_DEST_TYPE_NETWORK;
route->prefix.family = AF_INET6;
route->prefix.prefixlen = external->prefix.prefix_length;
- ospf6_prefix_in6_addr(&route->prefix.u.prefix6, &external->prefix);
+ ospf6_prefix_in6_addr(&route->prefix.u.prefix6, external,
+ &external->prefix);
route->path.area_id = asbr_entry->path.area_id;
route->path.origin.type = lsa->header->type;
route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
route_to_del->prefix.family = AF_INET6;
route_to_del->prefix.prefixlen = external->prefix.prefix_length;
- ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6,
+ ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, external,
&external->prefix);
route_to_del->path.origin.type = lsa->header->type;
memset(&prefix, 0, sizeof(struct prefix));
prefix.family = AF_INET6;
prefix.prefixlen = external->prefix.prefix_length;
- ospf6_prefix_in6_addr(&prefix.u.prefix6, &external->prefix);
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix);
route = ospf6_route_lookup(&prefix, ospf6->route_table);
if (route == NULL) {
lsa->header);
if (pos == 0) {
- ospf6_prefix_in6_addr(&in6, &external->prefix);
+ ospf6_prefix_in6_addr(&in6, external,
+ &external->prefix);
prefix_length = external->prefix.prefix_length;
} else {
in6 = *((struct in6_addr
memset(&route->prefix, 0, sizeof(struct prefix));
route->prefix.family = AF_INET6;
route->prefix.prefixlen = op->prefix_length;
- ospf6_prefix_in6_addr(&route->prefix.u.prefix6, op);
+ ospf6_prefix_in6_addr(&route->prefix.u.prefix6,
+ intra_prefix_lsa, op);
route->type = OSPF6_DEST_TYPE_NETWORK;
route->path.origin.type = lsa->header->type;
memset(&prefix, 0, sizeof(struct prefix));
prefix.family = AF_INET6;
prefix.prefixlen = op->prefix_length;
- ospf6_prefix_in6_addr(&prefix.u.prefix6, op);
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, intra_prefix_lsa, op);
route = ospf6_route_lookup(&prefix, oa->route_table);
if (route == NULL)
#include "ospf6_proto.h"
+void ospf6_prefix_in6_addr(struct in6_addr *in6, const void *prefix_buf,
+ const struct ospf6_prefix *p)
+{
+ ptrdiff_t in6_off = (caddr_t)p->addr - (caddr_t)prefix_buf;
+
+ memset(in6, 0, sizeof(struct in6_addr));
+ memcpy(in6, (uint8_t *)prefix_buf + in6_off,
+ OSPF6_PREFIX_SPACE(p->prefix_length));
+}
+
void ospf6_prefix_apply_mask(struct ospf6_prefix *op)
{
uint8_t *pnt, mask;
#define OSPF6_PREFIX_NEXT(x) \
((struct ospf6_prefix *)((caddr_t)(x) + OSPF6_PREFIX_SIZE(x)))
-#define ospf6_prefix_in6_addr(in6, op) \
- do { \
- memset(in6, 0, sizeof(struct in6_addr)); \
- memcpy(in6, (caddr_t)(op) + sizeof(struct ospf6_prefix), \
- OSPF6_PREFIX_SPACE((op)->prefix_length)); \
- } while (0)
-
+extern void ospf6_prefix_in6_addr(struct in6_addr *in6, const void *prefix_buf,
+ const struct ospf6_prefix *p);
extern void ospf6_prefix_apply_mask(struct ospf6_prefix *op);
extern void ospf6_prefix_options_printbuf(uint8_t prefix_options, char *buf,
int size);
void pim_zebra_init(void)
{
- int i;
-
/* Socket for receiving updates from Zebra daemon */
zclient = zclient_new_notify(master, &zclient_options_default);
zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs);
if (PIM_DEBUG_PIM_TRACE) {
- zlog_info("zclient_init cleared redistribution request");
- }
-
- /* Request all redistribution */
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
- if (i == zclient->redist_default)
- continue;
- vrf_bitmap_set(zclient->redist[AFI_IP][i], pimg->vrf_id);
- ;
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_debug("%s: requesting redistribution for %s (%i)",
- __PRETTY_FUNCTION__, zebra_route_string(i),
- i);
- }
- }
-
- /* Request default information */
- zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient,
- pimg->vrf_id);
-
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_info("%s: requesting default information redistribution",
- __PRETTY_FUNCTION__);
-
- zlog_notice("%s: zclient update socket initialized",
+ zlog_notice("%s: zclient socket initialized",
__PRETTY_FUNCTION__);
}
snprintf(accmd, sizeof(accmd), "autocomplete %d %s %s", token->type,
token->text, token->varname ? token->varname : "-");
+ vty->of_saved = vty->of;
+ vty->of = NULL;
for (i = 0; i < array_size(vtysh_client); i++)
vtysh_client_run_all(&vtysh_client[i], accmd, 1, vtysh_ac_line,
comps);
+ vty->of = vty->of_saved;
}
static const struct cmd_variable_handler vtysh_var_handler[] = {
if (off > h->nlmsg_len) {
zlog_err("Invalid offset for NLMSGERR_ATTR_OFFS\n");
- off = 0;
} else if (!(h->nlmsg_flags & NLM_F_CAPPED)) {
/*
* Header of failed message
struct zapi_nexthop *api_nh;
struct nexthop *nexthop;
int count = 0;
+ afi_t afi;
memset(&api, 0, sizeof(api));
api.vrf_id = re->vrf_id;
api.instance = re->instance;
api.flags = re->flags;
+ afi = family2afi(p->family);
+ switch (afi) {
+ case AFI_IP:
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ client->redist_v4_add_cnt++;
+ else
+ client->redist_v4_del_cnt++;
+ break;
+ case AFI_IP6:
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
+ client->redist_v6_add_cnt++;
+ else
+ client->redist_v6_del_cnt++;
+ break;
+ default:
+ break;
+ }
+
/* Prefix. */
api.prefix = *p;
if (src_p) {
if (!is_default_prefix(&zpi.dst))
zpi.filter_bm |= PBR_FILTER_DST_IP;
- if (zpi.dst_port_min != 0)
+ if (zpi.dst_port_min != 0 || zpi.proto == IPPROTO_ICMP)
zpi.filter_bm |= PBR_FILTER_DST_PORT;
- if (zpi.src_port_min != 0)
+ if (zpi.src_port_min != 0 || zpi.proto == IPPROTO_ICMP)
zpi.filter_bm |= PBR_FILTER_SRC_PORT;
if (zpi.dst_port_max != 0)
zpi.filter_bm |= PBR_FILTER_DST_PORT_RANGE;
STREAM_GETL(s, zpi.action);
STREAM_GETL(s, zpi.fwmark);
STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
+ STREAM_GETW(s, zpi.pkt_len_min);
+ STREAM_GETW(s, zpi.pkt_len_max);
+ STREAM_GETW(s, zpi.tcp_flags);
+ STREAM_GETW(s, zpi.tcp_mask_flags);
+ STREAM_GETC(s, zpi.dscp_value);
+ STREAM_GETC(s, zpi.fragment);
STREAM_GETL(s, zpi.nb_interface);
zebra_pbr_iptable_update_interfacelist(s, &zpi);
nlh = (struct nlmsghdr *)((char *)nlh
+ NETLINK_ALIGN(
nlh->nlmsg_len));
- } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0);
+ } while (len != 0 && ret == 0);
}
close(fd);
{0}
};
+const struct message icmp_typecode_str[] = {
+ { 0 << 8, "echo-reply"},
+ { 0 << 8, "pong"},
+ { 3 << 8, "network-unreachable"},
+ { (3 << 8) + 1, "host-unreachable"},
+ { (3 << 8) + 2, "protocol-unreachable"},
+ { (3 << 8) + 3, "port-unreachable"},
+ { (3 << 8) + 4, "fragmentation-needed"},
+ { (3 << 8) + 5, "source-route-failed"},
+ { (3 << 8) + 6, "network-unknown"},
+ { (3 << 8) + 7, "host-unknown"},
+ { (3 << 8) + 9, "network-prohibited"},
+ { (3 << 8) + 10, "host-prohibited"},
+ { (3 << 8) + 11, "TOS-network-unreachable"},
+ { (3 << 8) + 12, "TOS-host-unreachable"},
+ { (3 << 8) + 13, "communication-prohibited"},
+ { (3 << 8) + 14, "host-precedence-violation"},
+ { (3 << 8) + 15, "precedence-cutoff"},
+ { 4 << 8, "source-quench"},
+ { 5 << 8, "network-redirect"},
+ { (5 << 8) + 1, "host-redirect"},
+ { (5 << 8) + 2, "TOS-network-redirect"},
+ { (5 << 8) + 3, "TOS-host-redirect"},
+ { 8 << 8, "echo-request"},
+ { 8 << 8, "ping"},
+ { 9 << 8, "router-advertisement"},
+ { 10 << 8, "router-solicitation"},
+ { 11 << 8, "ttl-zero-during-transit"},
+ { (11 << 8) + 1, "ttl-zero-during-reassembly"},
+ { 12 << 8, "ip-header-bad"},
+ { (12 << 8) + 1, "required-option-missing"},
+ { 13 << 8, "timestamp-request"},
+ { 14 << 8, "timestamp-reply"},
+ { 17 << 8, "address-mask-request"},
+ { 18 << 8, "address-mask-reply"},
+ {0}
+};
+
+/* definitions */
+static const struct message tcp_value_str[] = {
+ {TCP_HEADER_FIN, "FIN"},
+ {TCP_HEADER_SYN, "SYN"},
+ {TCP_HEADER_RST, "RST"},
+ {TCP_HEADER_PSH, "PSH"},
+ {TCP_HEADER_ACK, "ACK"},
+ {TCP_HEADER_URG, "URG"},
+ {0}
+};
+
+static const struct message fragment_value_str[] = {
+ {1, "dont-fragment"},
+ {2, "is-fragment"},
+ {4, "first-fragment"},
+ {8, "last-fragment"},
+ {0}
+};
+
/* static function declarations */
DEFINE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns,
struct zebra_pbr_ipset_entry *ipset,
key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
0x63ab42de);
key = jhash_1word(iptable->fwmark, key);
+ key = jhash_1word(iptable->pkt_len_min, key);
+ key = jhash_1word(iptable->pkt_len_max, key);
+ key = jhash_1word(iptable->tcp_flags, key);
+ key = jhash_1word(iptable->tcp_mask_flags, key);
+ key = jhash_1word(iptable->dscp_value, key);
+ key = jhash_1word(iptable->fragment, key);
return jhash_3words(iptable->filter_bm, iptable->type,
iptable->unique, key);
}
if (strncmp(r1->ipset_name, r2->ipset_name,
ZEBRA_IPSET_NAME_SIZE))
return 0;
+ if (r1->pkt_len_min != r2->pkt_len_min)
+ return 0;
+ if (r1->pkt_len_max != r2->pkt_len_max)
+ return 0;
+ if (r1->tcp_flags != r2->tcp_flags)
+ return 0;
+ if (r1->tcp_mask_flags != r2->tcp_mask_flags)
+ return 0;
+ if (r1->dscp_value != r2->dscp_value)
+ return 0;
+ if (r1->fragment != r2->fragment)
+ return 0;
return 1;
}
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
-static const char *zebra_pbr_ipset_type2str(uint32_t type)
+const char *zebra_pbr_ipset_type2str(uint32_t type)
{
return lookup_msg(ipset_type_msg, type,
"Unrecognized IPset Type");
return prefix2str(pu, str, size);
}
+static void zebra_pbr_display_icmp(struct vty *vty,
+ struct zebra_pbr_ipset_entry *zpie)
+{
+ char decoded_str[20];
+ uint16_t port;
+
+ /* range icmp type */
+ if (zpie->src_port_max || zpie->dst_port_max) {
+ vty_out(vty, ":icmp:[type <%d:%d>;code <%d:%d>",
+ zpie->src_port_min, zpie->src_port_max,
+ zpie->dst_port_min, zpie->dst_port_max);
+ } else {
+ port = ((zpie->src_port_min << 8) & 0xff00) +
+ (zpie->dst_port_min & 0xff);
+ memset(decoded_str, 0, sizeof(decoded_str));
+ sprintf(decoded_str, "%d/%d",
+ zpie->src_port_min,
+ zpie->dst_port_min);
+ vty_out(vty, ":icmp:%s",
+ lookup_msg(icmp_typecode_str,
+ port, decoded_str));
+ }
+}
+
static void zebra_pbr_display_port(struct vty *vty, uint32_t filter_bm,
uint16_t port_min, uint16_t port_max,
uint8_t proto)
zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf));
vty_out(vty, "\tfrom %s", buf);
- if (zpie->filter_bm & PBR_FILTER_SRC_PORT)
+ if (zpie->filter_bm & PBR_FILTER_SRC_PORT &&
+ zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->src_port_min,
zpie->src_port_max,
vty_out(vty, " to ");
zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf));
vty_out(vty, "%s", buf);
- if (zpie->filter_bm & PBR_FILTER_DST_PORT)
+ if (zpie->filter_bm & PBR_FILTER_DST_PORT &&
+ zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->dst_port_min,
zpie->dst_port_max,
zpie->proto);
+ if (zpie->proto == IPPROTO_ICMP)
+ zebra_pbr_display_icmp(vty, zpie);
} else if ((zpi->type == IPSET_NET) ||
(zpi->type == IPSET_NET_PORT)) {
char buf[PREFIX_STRLEN];
zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf));
vty_out(vty, "\tfrom %s", buf);
}
- if (zpie->filter_bm & PBR_FILTER_SRC_PORT)
+ if (zpie->filter_bm & PBR_FILTER_SRC_PORT &&
+ zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->src_port_min,
zpie->src_port_max,
zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf));
vty_out(vty, "\tto %s", buf);
}
- if (zpie->filter_bm & PBR_FILTER_DST_PORT)
+ if (zpie->filter_bm & PBR_FILTER_DST_PORT &&
+ zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->dst_port_min,
zpie->dst_port_max,
zpie->proto);
+ if (zpie->proto == IPPROTO_ICMP)
+ zebra_pbr_display_icmp(vty, zpie);
}
vty_out(vty, " (%u)\n", zpie->unique);
return HASHWALK_CONTINUE;
}
+size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len,
+ uint16_t tcp_val)
+{
+ size_t len_written = 0;
+ static struct message nt = {0};
+ const struct message *pnt;
+ int incr = 0;
+
+ for (pnt = tcp_value_str;
+ memcmp(pnt, &nt, sizeof(struct message)); pnt++)
+ if (pnt->key & tcp_val) {
+ len_written += snprintf(buffer + len_written,
+ len - len_written,
+ "%s%s", incr ?
+ ",":"", pnt->str);
+ incr++;
+ }
+ return len_written;
+}
+
/*
*/
void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname)
vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name,
iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect",
iptable->unique);
-
+ if (iptable->pkt_len_min || iptable->pkt_len_max) {
+ if (!iptable->pkt_len_max)
+ vty_out(vty, "\t pkt len %u\n",
+ iptable->pkt_len_min);
+ else
+ vty_out(vty, "\t pkt len [%u;%u]\n",
+ iptable->pkt_len_min,
+ iptable->pkt_len_max);
+ }
+ if (iptable->tcp_flags || iptable->tcp_mask_flags) {
+ char tcp_flag_str[64];
+ char tcp_flag_mask_str[64];
+
+ zebra_pbr_tcpflags_snprintf(tcp_flag_str,
+ sizeof(tcp_flag_str),
+ iptable->tcp_flags);
+ zebra_pbr_tcpflags_snprintf(tcp_flag_mask_str,
+ sizeof(tcp_flag_mask_str),
+ iptable->tcp_mask_flags);
+ vty_out(vty, "\t tcpflags [%s/%s]\n",
+ tcp_flag_str, tcp_flag_mask_str);
+ }
+ if (iptable->filter_bm & (MATCH_DSCP_SET | MATCH_DSCP_INVERSE_SET)) {
+ vty_out(vty, "\t dscp %s %d\n",
+ iptable->filter_bm & MATCH_DSCP_INVERSE_SET ?
+ "not" : "", iptable->dscp_value);
+ }
+ if (iptable->fragment) {
+ char val_str[10];
+
+ sprintf(val_str, "%d", iptable->fragment);
+ vty_out(vty, "\t fragment%s %s\n",
+ iptable->filter_bm & MATCH_FRAGMENT_INVERSE_SET ?
+ " not" : "", lookup_msg(fragment_value_str,
+ iptable->fragment, val_str));
+ }
ret = hook_call(zebra_pbr_iptable_wrap_script_get_stat,
zns, iptable, &pkts, &bytes);
if (ret && pkts > 0)
struct prefix src;
struct prefix dst;
+ /* udp/tcp src port or icmp type */
uint16_t src_port_min;
uint16_t src_port_max;
+ /* udp/tcp dst port or icmp code */
uint16_t dst_port_min;
uint16_t dst_port_max;
uint32_t action;
+ uint16_t pkt_len_min;
+ uint16_t pkt_len_max;
+ uint16_t tcp_flags;
+ uint16_t tcp_mask_flags;
+ uint8_t dscp_value;
+ uint8_t fragment;
+
uint32_t nb_interface;
struct list *interface_name_list;
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
+extern const struct message icmp_typecode_str[];
+
+const char *zebra_pbr_ipset_type2str(uint32_t type);
+
void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule);
void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule);
void zebra_pbr_create_ipset(struct zebra_ns *zns,
extern void zebra_pbr_show_iptable(struct vty *vty);
extern void zebra_pbr_iptable_update_interfacelist(struct stream *s,
struct zebra_pbr_iptable *zpi);
+size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len,
+ uint16_t tcp_val);
DECLARE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns,
struct zebra_pbr_ipset_entry *ipset,
if (new != old)
zlog_debug(
"%u:%s: Deleting route rn %p, re %p (type %d) "
- "old %p (type %d) - %s",
+ "old %p (type %d) - nexthop inactive",
zvrf_id(zvrf), buf, rn, new,
- new->type, old, old->type,
- nh_active ? "install failed"
- : "nexthop inactive");
+ new->type, old, old->type);
else
zlog_debug(
- "%u:%s: Deleting route rn %p, re %p (type %d) - %s",
+ "%u:%s: Deleting route rn %p, re %p (type %d) - nexthop inactive",
zvrf_id(zvrf), buf, rn, new,
- new->type,
- nh_active ? "install failed"
- : "nexthop inactive");
+ new->type);
}
/* If labeled-unicast route, uninstall transit LSP. */
{
struct zserv *client = THREAD_ARG(thread);
struct stream *msg;
- uint32_t wcmd;
+ uint32_t wcmd = 0;
struct stream_fifo *cache;
/* If we have any data pending, try to flush it first */
vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt);
vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt);
+#if defined DEV_BUILD
+ vty_out(vty, "Input Fifo: %zu:%zu Output Fifo: %zu:%zu\n",
+ client->ibuf_fifo->count, client->ibuf_fifo->max_count,
+ client->obuf_fifo->count, client->obuf_fifo->max_count);
+#endif
vty_out(vty, "\n");
return;
}