diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 5db7e37089bf..5f13e6a9dc23 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -59,7 +59,8 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) 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); @@ -71,12 +72,6 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) 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; diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 956cf28c211d..1b874276613c 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -124,8 +124,9 @@ static bool bgp_flowspec_contains_prefix(struct prefix *pfs, 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, @@ -139,13 +140,6 @@ static bool bgp_flowspec_contains_prefix(struct prefix *pfs, 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; @@ -312,14 +306,14 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, /* - * 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) @@ -348,32 +342,33 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, 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; @@ -419,92 +414,6 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, 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) { @@ -623,7 +532,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, &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, @@ -637,7 +546,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, 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, @@ -646,7 +555,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, 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: diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index e4454ab4dba3..2d16e57a36fd 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -41,15 +41,11 @@ extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, 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); diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index b21e5ae0dcc4..90acd8fcb16f 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -62,7 +62,7 @@ static const struct message bgp_flowspec_display_min[] = { {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"}, @@ -173,7 +173,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, 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, @@ -221,11 +221,11 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, 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) { diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 1054ea402743..45ec21631c0f 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -21,6 +21,7 @@ #include "prefix.h" #include "zclient.h" #include "jhash.h" +#include "pbr.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_pbr.h" @@ -31,11 +32,13 @@ #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); @@ -175,11 +178,220 @@ static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, _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[], @@ -231,6 +443,8 @@ 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. @@ -240,17 +454,6 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => drop * - combination srcport + @IP */ - if (api->match_icmp_type_num || 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:" @@ -260,6 +463,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } 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:" @@ -279,12 +483,114 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "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 */ @@ -295,6 +601,14 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) " 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)) { @@ -519,6 +833,12 @@ uint32_t bgp_pbr_match_hash_key(void *arg) 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); } @@ -541,6 +861,23 @@ int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) 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; } @@ -808,10 +1145,11 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) 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 @@ -954,20 +1292,24 @@ static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg) 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 @@ -975,17 +1317,19 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, */ 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) { @@ -993,7 +1337,9 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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) { @@ -1001,9 +1347,35 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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 @@ -1014,10 +1386,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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; @@ -1038,16 +1410,182 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, } } -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; @@ -1056,14 +1594,104 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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 ? "" : + prefix2str(bpf->src, bufsrc, sizeof(bufsrc)), + bpf->dst == NULL ? "" : + 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); @@ -1085,33 +1713,63 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, /* 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); @@ -1135,19 +1793,19 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, } 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); @@ -1172,7 +1830,7 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, * 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); @@ -1208,6 +1866,107 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, } +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, @@ -1220,9 +1979,16 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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) @@ -1252,10 +2018,97 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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) { @@ -1265,9 +2118,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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; @@ -1308,10 +2160,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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 */ @@ -1321,10 +2171,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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: diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 20edaf30b8a5..307a34e34f4c 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -107,7 +107,6 @@ struct bgp_pbr_entry_main { #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; @@ -119,12 +118,14 @@ struct bgp_pbr_entry_main { 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]; @@ -133,8 +134,9 @@ struct bgp_pbr_entry_main { 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]; @@ -176,14 +178,15 @@ struct bgp_pbr_match { */ 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 diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 48aa24ddad15..87c903614767 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2233,6 +2233,12 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, 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. */ diff --git a/lib/pbr.h b/lib/pbr.h index 401cfb08133d..76b91e6205bb 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -83,6 +83,35 @@ struct pbr_rule { 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); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 4703f406f98a..5a80072e546b 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2864,9 +2864,9 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) 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; @@ -2906,6 +2906,12 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) 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); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 303ea44d080c..86e054795bb5 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -43,6 +43,63 @@ static const struct message ipset_type_msg[] = { {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, @@ -321,6 +378,12 @@ uint32_t zebra_pbr_iptable_hash_key(void *arg) 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); } @@ -345,6 +408,18 @@ int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) 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; } @@ -512,7 +587,7 @@ struct pbr_ipset_name_lookup { 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"); @@ -770,6 +845,30 @@ static const char *zebra_pbr_prefix2str(union prefixconstptr pu, 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) @@ -813,7 +912,8 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, 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, @@ -821,11 +921,14 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, 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]; @@ -834,7 +937,8 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, 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, @@ -843,11 +947,14 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, 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); @@ -879,6 +986,26 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_backet *backet, void *arg) 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) @@ -943,7 +1070,42 @@ static int zebra_pbr_show_iptable_walkcb(struct hash_backet *backet, void *arg) 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) diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 31fc553581f8..3d26281d0dad 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -91,8 +91,10 @@ struct zebra_pbr_ipset_entry { 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; @@ -131,6 +133,13 @@ struct zebra_pbr_iptable { 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; @@ -138,6 +147,10 @@ struct zebra_pbr_iptable { 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, @@ -225,6 +238,8 @@ extern void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname); 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,