diff options
-rw-r--r-- | bgpd/bgp_attr.c | 3 | ||||
-rw-r--r-- | bgpd/bgp_debug.c | 41 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.c | 45 | ||||
-rw-r--r-- | bgpd/bgp_flowspec.h | 3 | ||||
-rw-r--r-- | bgpd/bgp_flowspec_vty.c | 126 | ||||
-rw-r--r-- | bgpd/bgp_pbr.c | 352 | ||||
-rw-r--r-- | bgpd/bgp_pbr.h | 39 | ||||
-rw-r--r-- | bgpd/bgp_route.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_zebra.c | 138 | ||||
-rw-r--r-- | bgpd/bgp_zebra.h | 3 | ||||
-rw-r--r-- | bgpd/bgpd.c | 5 | ||||
-rw-r--r-- | bgpd/bgpd.h | 3 | ||||
-rw-r--r-- | lib/pbr.h | 13 | ||||
-rw-r--r-- | lib/zclient.h | 4 | ||||
-rw-r--r-- | pbrd/pbr_zebra.c | 1 | ||||
-rw-r--r-- | zebra/debug.c | 3 | ||||
-rw-r--r-- | zebra/debug.h | 2 | ||||
-rw-r--r-- | zebra/rule_netlink.c | 6 | ||||
-rw-r--r-- | zebra/zapi_msg.c | 20 | ||||
-rw-r--r-- | zebra/zebra_ns.c | 4 | ||||
-rw-r--r-- | zebra/zebra_pbr.c | 485 | ||||
-rw-r--r-- | zebra/zebra_pbr.h | 44 | ||||
-rw-r--r-- | zebra/zebra_vty.c | 35 |
23 files changed, 1240 insertions, 137 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 276a7054e..e714e17bd 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2276,7 +2276,8 @@ static int bgp_attr_check(struct peer *peer, struct attr *attr) are present, it should. Check for any other attribute being present instead. */ - if (attr->flag == ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI)) + if ((!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)) && + CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI)))) return BGP_ATTR_PARSE_PROCEED; if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGIN))) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 3e3fbcbfe..a4ded57c2 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -1661,11 +1661,23 @@ DEFUN (no_debug_bgp_vpn, /* debug bgp pbr */ DEFUN (debug_bgp_pbr, debug_bgp_pbr_cmd, - "debug bgp pbr", + "debug bgp pbr [error]", DEBUG_STR BGP_STR - "BGP policy based routing\n") + "BGP policy based routing\n" + "BGP PBR error\n") { + int idx = 3; + + if (argv_find(argv, argc, "error", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_ON(pbr, PBR_ERROR); + else { + TERM_DEBUG_ON(pbr, PBR_ERROR); + vty_out(vty, "BGP policy based routing error is on\n"); + } + return CMD_SUCCESS; + } if (vty->node == CONFIG_NODE) DEBUG_ON(pbr, PBR); else { @@ -1677,12 +1689,24 @@ DEFUN (debug_bgp_pbr, DEFUN (no_debug_bgp_pbr, no_debug_bgp_pbr_cmd, - "no debug bgp pbr", + "no debug bgp pbr [error]", NO_STR DEBUG_STR BGP_STR - "BGP policy based routing\n") + "BGP policy based routing\n" + "BGP PBR Error\n") { + int idx = 3; + + if (argv_find(argv, argc, "error", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_OFF(pbr, PBR_ERROR); + else { + TERM_DEBUG_OFF(pbr, PBR_ERROR); + vty_out(vty, "BGP policy based routing error is off\n"); + } + return CMD_SUCCESS; + } if (vty->node == CONFIG_NODE) DEBUG_OFF(pbr, PBR); else { @@ -1769,6 +1793,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(flowspec, FLOWSPEC); TERM_DEBUG_OFF(labelpool, LABELPOOL); TERM_DEBUG_OFF(pbr, PBR); + TERM_DEBUG_OFF(pbr, PBR_ERROR); vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -1846,6 +1871,8 @@ DEFUN_NOSH (show_debugging_bgp, if (BGP_DEBUG(pbr, PBR)) vty_out(vty, " BGP policy based routing debugging is on\n"); + if (BGP_DEBUG(pbr, PBR_ERROR)) + vty_out(vty, " BGP policy based routing error debugging is on\n"); vty_out(vty, "\n"); return CMD_SUCCESS; @@ -1906,6 +1933,8 @@ int bgp_debug_count(void) if (BGP_DEBUG(pbr, PBR)) ret++; + if (BGP_DEBUG(pbr, PBR_ERROR)) + ret++; return ret; } @@ -2012,6 +2041,10 @@ static int bgp_config_write_debug(struct vty *vty) vty_out(vty, "debug bgp pbr\n"); write++; } + if (CONF_BGP_DEBUG(pbr, PBR_ERROR)) { + vty_out(vty, "debug bgp pbr error\n"); + write++; + } return write; } diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 99fe80f05..20f5e15d6 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -642,9 +642,9 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) { int i; uint8_t *pnt; - int type = 0; - int sub_type = 0; -#define ECOMMUNITY_STR_DEFAULT_LEN 27 + uint8_t type = 0; + uint8_t sub_type = 0; +#define ECOMMUNITY_STR_DEFAULT_LEN 64 int str_size; int str_pnt; char *str_buf; @@ -750,10 +750,25 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) "FS:redirect IP 0x%x", *(pnt+5)); } else unk_ecom = 1; - } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP) { + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP || + type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || + type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { sub_type = *pnt++; + if (sub_type == ECOMMUNITY_REDIRECT_VRF) { + char buf[16]; - if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str(buf, (uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY); + len = snprintf(str_buf + str_pnt, + str_size - len, + "FS:redirect VRF %s", buf); + } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) + unk_ecom = 1; + else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { char action[64]; char *ptr = action; @@ -782,30 +797,20 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) len = sprintf( str_buf + str_pnt, "FS:rate %f", data.rate_float); - } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { - char buf[16]; - - memset(buf, 0, sizeof(buf)); - ecommunity_rt_soo_str(buf, (uint8_t *)pnt, - type & - ~ECOMMUNITY_ENCODE_TRANS_EXP, - ECOMMUNITY_ROUTE_TARGET, - ECOMMUNITY_FORMAT_DISPLAY); - len = snprintf( - str_buf + str_pnt, - str_size - len, - "FS:redirect VRF %s", buf); } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) { len = sprintf( str_buf + str_pnt, "FS:marking %u", *(pnt+5)); } else unk_ecom = 1; - } else + } else { + sub_type = *pnt++; unk_ecom = 1; + } if (unk_ecom) - len = sprintf(str_buf + str_pnt, "?"); + len = sprintf(str_buf + str_pnt, "UNK:%d, %d", + type, sub_type); str_pnt += len; first = 0; diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index 392b32153..5dd2c3931 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -47,4 +47,7 @@ extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, extern void route_vty_out_flowspec(struct vty *vty, struct prefix *p, struct bgp_info *binfo, int display, json_object *json_paths); +extern int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi); + #endif /* _FRR_BGP_FLOWSPEC_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 7bf11f12a..b21e5ae0d 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -30,6 +30,7 @@ #include "bgpd/bgp_flowspec_util.h" #include "bgpd/bgp_flowspec_private.h" #include "bgpd/bgp_debug.h" +#include "bgpd/bgp_pbr.h" /* Local Structures and variables declarations * This code block hosts the struct declared that host the flowspec rules @@ -318,16 +319,32 @@ void route_vty_out_flowspec(struct vty *vty, struct prefix *p, XFREE(MTYPE_ECOMMUNITY_STR, s); } peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); - if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\tup for %8s\n", timebuf); - else if (json_paths) { + if (display == NLRI_STRING_FORMAT_LARGE) { + vty_out(vty, "\treceived for %8s\n", timebuf); + } else if (json_paths) { json_time_path = json_object_new_object(); json_object_string_add(json_time_path, "time", timebuf); if (display == NLRI_STRING_FORMAT_JSON) json_object_array_add(json_paths, json_time_path); } + if (display == NLRI_STRING_FORMAT_LARGE) { + struct bgp_info_extra *extra = bgp_info_extra_get(binfo); + if (extra->bgp_fs_pbr) { + struct bgp_pbr_match_entry *bpme; + struct bgp_pbr_match *bpm; + + bpme = (struct bgp_pbr_match_entry *)extra->bgp_fs_pbr; + bpm = bpme->backpointer; + vty_out(vty, "\tinstalled in PBR"); + if (bpm) + vty_out(vty, " (%s)\n", bpm->ipset_name); + else + vty_out(vty, "\n"); + } else + vty_out(vty, "\tnot installed in PBR\n"); + } } int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, @@ -408,10 +425,113 @@ DEFUN (no_debug_bgp_flowspec, return CMD_SUCCESS; } +int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) +{ + struct bgp_pbr_interface *pbr_if; + bool declare_node = false; + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + bool bgp_pbr_interface_any; + + if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP) + return 0; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + if (!RB_EMPTY(bgp_pbr_interface_head, head) || + !bgp_pbr_interface_any) + declare_node = true; + RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { + vty_out(vty, " local-install %s\n", pbr_if->name); + } + if (!bgp_pbr_interface_any) + vty_out(vty, " no local-install any\n"); + return declare_node ? 1 : 0; +} + +static int bgp_fs_local_install_interface(struct bgp *bgp, + const char *no, const char *ifname) +{ + struct bgp_pbr_interface *pbr_if; + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + bool *bgp_pbr_interface_any; + + if (!bgp_pbr_cfg) + return CMD_SUCCESS; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + if (no) { + if (!ifname) { + if (*bgp_pbr_interface_any) { + *bgp_pbr_interface_any = false; + /* remove all other interface list */ + bgp_pbr_reset(bgp, AFI_IP); + } + return CMD_SUCCESS; + } + pbr_if = bgp_pbr_interface_lookup(ifname, head); + if (!pbr_if) + return CMD_SUCCESS; + RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); + return CMD_SUCCESS; + } + if (ifname) { + pbr_if = bgp_pbr_interface_lookup(ifname, head); + if (pbr_if) + return CMD_SUCCESS; + pbr_if = XCALLOC(MTYPE_TMP, + sizeof(struct bgp_pbr_interface)); + strlcpy(pbr_if->name, ifname, INTERFACE_NAMSIZ); + RB_INSERT(bgp_pbr_interface_head, head, pbr_if); + *bgp_pbr_interface_any = false; + } else { + /* set to default */ + if (!*bgp_pbr_interface_any) { + /* remove all other interface list + */ + bgp_pbr_reset(bgp, AFI_IP); + *bgp_pbr_interface_any = true; + } + } + return CMD_SUCCESS; +} + +DEFUN (bgp_fs_local_install_ifname, + bgp_fs_local_install_ifname_cmd, + "[no] local-install INTERFACE", + NO_STR + "Apply local policy routing\n" + "Interface name\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int idx = 0; + const char *no = strmatch(argv[0]->text, (char *)"no") ? "no" : NULL; + char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? + argv[idx]->arg : NULL; + + return bgp_fs_local_install_interface(bgp, no, ifname); +} + +DEFUN (bgp_fs_local_install_any, + bgp_fs_local_install_any_cmd, + "[no] local-install any", + NO_STR + "Apply local policy routing\n" + "Any Interface\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + const char *no = strmatch(argv[0]->text, (char *)"no") ? "no" : NULL; + + return bgp_fs_local_install_interface(bgp, no, NULL); +} + void bgp_flowspec_vty_init(void) { install_element(ENABLE_NODE, &debug_bgp_flowspec_cmd); install_element(CONFIG_NODE, &debug_bgp_flowspec_cmd); install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd); install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd); + install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_any_cmd); + install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd); } diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 04d6314fd..1054ea402 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -35,6 +35,12 @@ 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") + +RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, + id_entry, bgp_pbr_interface_compare); +struct bgp_pbr_interface_head ifaces_by_name_ipv4 = + RB_INITIALIZER(&ifaces_by_name_ipv4); static int bgp_pbr_match_counter_unique; static int bgp_pbr_match_entry_counter_unique; @@ -169,7 +175,60 @@ static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, _cnt++; \ } while (0) -/* return 1 if OK, 0 if validation should stop) */ +struct bgp_pbr_range_port { + uint16_t min_port; + uint16_t max_port; +}; + +/* return true if extraction ok + */ +static bool bgp_pbr_extract(struct bgp_pbr_match_val list[], + int num, + struct bgp_pbr_range_port *range) +{ + int i = 0; + bool exact_match = false; + + if (range) + memset(range, 0, sizeof(struct bgp_pbr_range_port)); + + if (num > 2) + return false; + for (i = 0; i < num; i++) { + if (i != 0 && (list[i].compare_operator == + OPERATOR_COMPARE_EQUAL_TO)) + return false; + if (i == 0 && (list[i].compare_operator == + OPERATOR_COMPARE_EQUAL_TO)) { + if (range) + range->min_port = list[i].value; + exact_match = true; + } + if (exact_match == true && i > 0) + return false; + if (list[i].compare_operator == + (OPERATOR_COMPARE_GREATER_THAN + + OPERATOR_COMPARE_EQUAL_TO)) { + if (range) + range->min_port = list[i].value; + } else if (list[i].compare_operator == + (OPERATOR_COMPARE_LESS_THAN + + OPERATOR_COMPARE_EQUAL_TO)) { + if (range) + range->max_port = list[i].value; + } else if (list[i].compare_operator == + OPERATOR_COMPARE_LESS_THAN) { + if (range) + range->max_port = list[i].value - 1; + } else if (list[i].compare_operator == + OPERATOR_COMPARE_GREATER_THAN) { + if (range) + range->min_port = list[i].value + 1; + } + } + return true; +} + static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) { /* because bgp pbr entry may contain unsupported @@ -179,18 +238,63 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => redirect nexthop [ + rate] * - combination src/dst => redirect VRF [ + rate] * - combination src/dst => drop + * - combination srcport + @IP */ - if (api->match_src_port_num || api->match_dst_port_num - || api->match_port_num || api->match_protocol_num - || api->match_icmp_type_num || api->match_icmp_type_num + 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:" + "multiple protocols ( %d). ignoring.", + api->match_protocol_num); + return 0; + } + if (api->match_protocol_num == 1 && + api->protocol[0].value != PROTOCOL_UDP && + api->protocol[0].value != PROTOCOL_TCP) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match protocol operations:" + "protocol (%d) not supported. ignoring", + api->match_protocol_num); + return 0; + } + if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match src port operations:" + "too complex. ignoring."); + return 0; + } + if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match dst port operations:" + "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; + } + /* no combinations with both src_port and dst_port + * or port with src_port and dst_port + */ + if (api->match_src_port_num + api->match_dst_port_num + + api->match_port_num > 3) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match multiple port operations:" + " too complex. ignoring."); + return 0; + } if (!(api->match_bitmask & PREFIX_SRC_PRESENT) && !(api->match_bitmask & PREFIX_DST_PRESENT)) { if (BGP_DEBUG(pbr, PBR)) { @@ -228,15 +332,15 @@ static int bgp_pbr_build_and_validate_entry(struct prefix *p, ecom = info->attr->ecommunity; for (i = 0; i < ecom->size; i++) { ecom_eval = (struct ecommunity_val *) - ecom->val + (i * ECOMMUNITY_SIZE); - + (ecom->val + (i * ECOMMUNITY_SIZE)); + action_count++; if (action_count > ACTIONS_MAX_NUM) { if (BGP_DEBUG(pbr, PBR_ERROR)) zlog_err("%s: flowspec actions exceeds limit (max %u)", __func__, action_count); break; } - api_action = &api->actions[action_count]; + api_action = &api->actions[action_count - 1]; if ((ecom_eval->val[1] == (char)ECOMMUNITY_REDIRECT_VRF) && @@ -376,6 +480,7 @@ static void bgp_pbr_action_free(void *arg) AFI_IP, bpa->table_id, false); + bpa->installed = false; } } XFREE(MTYPE_PBR_ACTION, bpa); @@ -447,6 +552,11 @@ uint32_t bgp_pbr_match_entry_hash_key(void *arg) pbme = (struct bgp_pbr_match_entry *)arg; key = prefix_hash_key(&pbme->src); key = jhash_1word(prefix_hash_key(&pbme->dst), key); + key = jhash(&pbme->dst_port_min, 2, key); + key = jhash(&pbme->src_port_min, 2, key); + key = jhash(&pbme->dst_port_max, 2, key); + key = jhash(&pbme->src_port_max, 2, key); + key = jhash(&pbme->proto, 1, key); return key; } @@ -474,6 +584,21 @@ int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2) if (!prefix_same(&r1->dst, &r2->dst)) return 0; + if (r1->src_port_min != r2->src_port_min) + return 0; + + if (r1->dst_port_min != r2->dst_port_min) + return 0; + + if (r1->src_port_max != r2->src_port_max) + return 0; + + if (r1->dst_port_max != r2->dst_port_max) + return 0; + + if (r1->proto != r2->proto) + return 0; + return 1; } @@ -497,10 +622,8 @@ int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2) /* unique value is self calculated * table and fwmark is self calculated + * rate is ignored */ - if (r1->rate != r2->rate) - return 0; - if (r1->vrf_id != r2->vrf_id) return 0; @@ -587,6 +710,11 @@ void bgp_pbr_cleanup(struct bgp *bgp) hash_free(bgp->pbr_action_hash); bgp->pbr_action_hash = NULL; } + if (bgp->bgp_pbr_cfg == NULL) + return; + bgp_pbr_reset(bgp, AFI_IP); + XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); + bgp->bgp_pbr_cfg = NULL; } void bgp_pbr_init(struct bgp *bgp) @@ -599,6 +727,9 @@ void bgp_pbr_init(struct bgp *bgp) hash_create_size(8, bgp_pbr_action_hash_key, bgp_pbr_action_hash_equal, "Match Hash Entry"); + + bgp->bgp_pbr_cfg = XCALLOC(MTYPE_PBR, sizeof(struct bgp_pbr_config)); + bgp->bgp_pbr_cfg->pbr_interface_any_ipv4 = true; } void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) @@ -749,6 +880,16 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, bgp_send_pbr_ipset_entry_match(bpme, false); bpme->installed = false; bpme->backpointer = NULL; + if (bpme->bgp_info) { + struct bgp_info *bgp_info; + struct bgp_info_extra *extra; + + /* unlink bgp_info to bpme */ + bgp_info = (struct bgp_info *)bpme->bgp_info; + extra = bgp_info_extra_get(bgp_info); + extra->bgp_fs_pbr = NULL; + bpme->bgp_info = NULL; + } } hash_release(bpm->entry_hash, bpme); if (hashcount(bpm->entry_hash) == 0) { @@ -777,6 +918,7 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, AFI_IP, bpa->table_id, false); + bpa->installed = false; } } } @@ -813,10 +955,13 @@ static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg) } 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) + 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) { struct bgp_pbr_match temp; struct bgp_pbr_match_entry temp2; @@ -840,11 +985,35 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, prefix_copy(&temp2.dst, dst); } else temp2.dst.family = AF_INET; - - if (src == NULL || dst == NULL) - temp.type = IPSET_NET; - else - temp.type = IPSET_NET_NET; + if (src_port) { + temp.flags |= MATCH_PORT_SRC_SET; + temp2.src_port_min = src_port->min_port; + if (src_port->max_port) { + temp.flags |= MATCH_PORT_SRC_RANGE_SET; + temp2.src_port_max = src_port->max_port; + } + } + if (dst_port) { + temp.flags |= MATCH_PORT_DST_SET; + temp2.dst_port_min = dst_port->min_port; + if (dst_port->max_port) { + temp.flags |= MATCH_PORT_DST_RANGE_SET; + temp2.dst_port_max = dst_port->max_port; + } + } + temp2.proto = protocol; + + if (src == NULL || 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 (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ temp.vrf_id = 0; else @@ -870,12 +1039,15 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, } static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, - struct bgp_info *binfo, - vrf_id_t vrf_id, - struct prefix *src, - struct prefix *dst, - struct nexthop *nh, - float *rate) + struct bgp_info *binfo, + vrf_id_t vrf_id, + struct prefix *src, + struct prefix *dst, + struct nexthop *nh, + float *rate, + uint8_t protocol, + struct bgp_pbr_range_port *src_port, + struct bgp_pbr_range_port *dst_port) { struct bgp_pbr_match temp; struct bgp_pbr_match_entry temp2; @@ -913,15 +1085,33 @@ 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) - temp.type = IPSET_NET; - else - temp.type = IPSET_NET_NET; + 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.flags |= MATCH_IP_SRC_SET; if (dst) temp.flags |= MATCH_IP_DST_SET; + + if (src_port && src_port->min_port) + temp.flags |= MATCH_PORT_SRC_SET; + if (dst_port && dst_port->min_port) + 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; temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -953,15 +1143,22 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, prefix_copy(&temp2.dst, 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; if (bpm) bpme = hash_get(bpm->entry_hash, &temp2, - bgp_pbr_match_entry_alloc_intern); + bgp_pbr_match_entry_alloc_intern); if (bpme && bpme->unique == 0) { bpme->unique = ++bgp_pbr_match_entry_counter_unique; /* 0 value is forbidden */ bpme->backpointer = bpm; bpme->installed = false; bpme->install_in_progress = false; + /* link bgp info to bpme */ + bpme->bgp_info = (void *)binfo; } /* BGP FS: append entry to zebra @@ -1021,17 +1218,44 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, int continue_loop = 1; float rate = 0; 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; + memset(&nh, 0, sizeof(struct nexthop)); if (api->match_bitmask & PREFIX_SRC_PRESENT) src = &api->src_prefix; if (api->match_bitmask & PREFIX_DST_PRESENT) dst = &api->dst_prefix; memset(&nh, 0, sizeof(struct nexthop)); nh.vrf_id = VRF_UNKNOWN; - + if (api->match_protocol_num) + proto = (uint8_t)api->protocol[0].value; + /* if match_port is selected, then either src or dst port will be parsed + * but not both at the same time + */ + if (api->match_port_num >= 1) { + bgp_pbr_extract(api->port, + api->match_port_num, + &range); + srcp = dstp = ⦥ + } else if (api->match_src_port_num >= 1) { + bgp_pbr_extract(api->src_port, + api->match_src_port_num, + &range); + srcp = ⦥ + dstp = NULL; + } else if (api->match_dst_port_num >= 1) { + bgp_pbr_extract(api->dst_port, + api->match_dst_port_num, + &range); + dstp = ⦥ + srcp = NULL; + } if (!add) return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, - api->vrf_id, src, dst); + api->vrf_id, src, dst, + proto, srcp, dstp); /* no action for add = true */ for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { @@ -1042,7 +1266,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, nh.type = NEXTHOP_TYPE_BLACKHOLE; bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, - &nh, &rate); + &nh, &rate, proto, + srcp, dstp); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1085,7 +1310,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, - &nh, &rate); + &nh, &rate, proto, + srcp, dstp); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -1097,7 +1323,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, - &nh, &rate); + &nh, &rate, proto, + srcp, dstp); continue_loop = 0; break; case ACTION_MARKING: @@ -1120,6 +1347,7 @@ void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, bool nlri_update) { struct bgp_pbr_entry_main api; + struct bgp_info_extra *extra = bgp_info_extra_get(info); if (afi == AFI_IP6) return; /* IPv6 not supported */ @@ -1130,11 +1358,61 @@ void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, api.vrf_id = bgp->vrf_id; api.afi = afi; - if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) { + if (!bgp_zebra_tm_chunk_obtained()) { if (BGP_DEBUG(pbr, PBR_ERROR)) - zlog_err("%s: cancel updating entry in bgp pbr", + zlog_err("%s: table chunk not obtained yet", __func__); return; } + /* already installed */ + if (nlri_update && extra->bgp_fs_pbr) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + zlog_err("%s: entry %p already installed in bgp pbr", + __func__, info); + return; + } + + if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + zlog_err("%s: cancel updating entry %p in bgp pbr", + __func__, info); + return; + } bgp_pbr_handle_entry(bgp, info, &api, nlri_update); } + +int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, + const struct bgp_pbr_interface *b) +{ + return strcmp(a->name, b->name); +} + +struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name, + struct bgp_pbr_interface_head *head) +{ + struct bgp_pbr_interface pbr_if; + + strlcpy(pbr_if.name, name, sizeof(pbr_if.name)); + return (RB_FIND(bgp_pbr_interface_head, + head, &pbr_if)); +} + +/* this function resets to the default policy routing + * go back to default status + */ +void bgp_pbr_reset(struct bgp *bgp, afi_t afi) +{ + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + struct bgp_pbr_interface *pbr_if; + + if (!bgp_pbr_cfg || afi != AFI_IP) + return; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + + while (!RB_EMPTY(bgp_pbr_interface_head, head)) { + pbr_if = RB_ROOT(bgp_pbr_interface_head, head); + RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); + XFREE(MTYPE_TMP, pbr_if); + } +} diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 5129ada37..20edaf30b 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -123,6 +123,8 @@ struct bgp_pbr_entry_main { struct prefix src_prefix; struct prefix dst_prefix; +#define PROTOCOL_UDP 17 +#define PROTOCOL_TCP 6 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]; @@ -148,6 +150,25 @@ struct bgp_pbr_entry_main { vrf_id_t vrf_id; }; +struct bgp_pbr_interface { + RB_ENTRY(bgp_pbr_interface) id_entry; + char name[INTERFACE_NAMSIZ]; +}; + +RB_HEAD(bgp_pbr_interface_head, bgp_pbr_interface); +RB_PROTOTYPE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, + bgp_pbr_interface_compare); + +extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, + const struct bgp_pbr_interface *b); + +struct bgp_pbr_config { + struct bgp_pbr_interface_head ifaces_by_name_ipv4; + bool pbr_interface_any_ipv4; +}; + +extern struct bgp_pbr_config *bgp_pbr_cfg; + struct bgp_pbr_match { char ipset_name[ZEBRA_IPSET_NAME_SIZE]; @@ -157,6 +178,10 @@ struct bgp_pbr_match { #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; vrf_id_t vrf_id; @@ -189,6 +214,14 @@ struct bgp_pbr_match_entry { struct prefix src; struct prefix dst; + uint16_t src_port_min; + uint16_t src_port_max; + uint16_t dst_port_min; + uint16_t dst_port_max; + uint8_t proto; + + void *bgp_info; + bool installed; bool install_in_progress; }; @@ -253,4 +286,10 @@ extern void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, bool nlri_update); +/* bgp pbr utilities */ +extern struct bgp_pbr_interface *pbr_interface_lookup(const char *name); +extern void bgp_pbr_reset(struct bgp *bgp, afi_t afi); +extern struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name, + struct bgp_pbr_interface_head *head); + #endif /* __BGP_PBR_H__ */ diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 288b66fb8..72923901b 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -146,6 +146,8 @@ struct bgp_info_extra { * Set nexthop_orig.family to 0 if not valid. */ struct prefix nexthop_orig; + /* presence of FS pbr entry */ + void *bgp_fs_pbr; }; struct bgp_info { diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 390eb44eb..7a6b80f3a 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1001,6 +1001,7 @@ static bool bgp_tm_status_connected; static bool bgp_tm_chunk_obtained; #define BGP_FLOWSPEC_TABLE_CHUNK 100000 static uint32_t bgp_tm_min, bgp_tm_max, bgp_tm_chunk_size; +struct bgp *bgp_tm_bgp; static int bgp_zebra_tm_connect(struct thread *t) { @@ -1024,8 +1025,11 @@ static int bgp_zebra_tm_connect(struct thread *t) if (!bgp_tm_chunk_obtained) { if (bgp_zebra_get_table_range(bgp_tm_chunk_size, &bgp_tm_min, - &bgp_tm_max) >= 0) + &bgp_tm_max) >= 0) { bgp_tm_chunk_obtained = true; + /* parse non installed entries */ + bgp_zebra_announce_table(bgp_tm_bgp, AFI_IP, SAFI_FLOWSPEC); + } } } thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, @@ -1033,6 +1037,11 @@ static int bgp_zebra_tm_connect(struct thread *t) return 0; } +bool bgp_zebra_tm_chunk_obtained(void) +{ + return bgp_tm_chunk_obtained; +} + uint32_t bgp_zebra_tm_get_id(void) { static int table_id; @@ -1042,7 +1051,7 @@ uint32_t bgp_zebra_tm_get_id(void) return bgp_tm_min++; } -void bgp_zebra_init_tm_connect(void) +void bgp_zebra_init_tm_connect(struct bgp *bgp) { int delay = 1; @@ -1054,6 +1063,7 @@ void bgp_zebra_init_tm_connect(void) bgp_tm_chunk_obtained = false; bgp_tm_min = bgp_tm_max = 0; bgp_tm_chunk_size = BGP_FLOWSPEC_TABLE_CHUNK; + bgp_tm_bgp = bgp; thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, &bgp_tm_thread_connect); } @@ -1962,6 +1972,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, zlog_debug("%s: Received RULE_INSTALLED", __PRETTY_FUNCTION__); break; + case ZAPI_RULE_FAIL_REMOVE: case ZAPI_RULE_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received RULE REMOVED", @@ -1987,8 +1998,8 @@ static int ipset_notify_owner(int command, struct zclient *zclient, bgp_pbim = bgp_pbr_match_ipset_lookup(vrf_id, unique); if (!bgp_pbim) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Fail to look BGP match (%u)", - __PRETTY_FUNCTION__, unique); + zlog_debug("%s: Fail to look BGP match ( %u %u)", + __PRETTY_FUNCTION__, note, unique); return 0; } @@ -2007,6 +2018,7 @@ static int ipset_notify_owner(int command, struct zclient *zclient, zlog_debug("%s: Received IPSET_INSTALLED", __PRETTY_FUNCTION__); break; + case ZAPI_IPSET_FAIL_REMOVE: case ZAPI_IPSET_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPSET REMOVED", @@ -2036,8 +2048,8 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, unique); if (!bgp_pbime) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Fail to look BGP match entry (%u)", - __PRETTY_FUNCTION__, unique); + zlog_debug("%s: Fail to look BGP match entry (%u %u)", + __PRETTY_FUNCTION__, note, unique); return 0; } @@ -2050,12 +2062,22 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, bgp_pbime->install_in_progress = false; break; case ZAPI_IPSET_ENTRY_INSTALLED: - bgp_pbime->installed = true; - bgp_pbime->install_in_progress = false; - if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received IPSET_ENTRY_INSTALLED", - __PRETTY_FUNCTION__); + { + struct bgp_info *bgp_info; + struct bgp_info_extra *extra; + + bgp_pbime->installed = true; + bgp_pbime->install_in_progress = false; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET_ENTRY_INSTALLED", + __PRETTY_FUNCTION__); + /* link bgp_info to bpme */ + bgp_info = (struct bgp_info *)bgp_pbime->bgp_info; + extra = bgp_info_extra_get(bgp_info); + extra->bgp_fs_pbr = (void *)bgp_pbime; + } break; + case ZAPI_IPSET_ENTRY_FAIL_REMOVE: case ZAPI_IPSET_ENTRY_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPSET_ENTRY_REMOVED", @@ -2080,8 +2102,8 @@ static int iptable_notify_owner(int command, struct zclient *zclient, bgpm = bgp_pbr_match_iptable_lookup(vrf_id, unique); if (!bgpm) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Fail to look BGP iptable (%u)", - __PRETTY_FUNCTION__, unique); + zlog_debug("%s: Fail to look BGP iptable (%u %u)", + __PRETTY_FUNCTION__, note, unique); return 0; } switch (note) { @@ -2100,6 +2122,7 @@ static int iptable_notify_owner(int command, struct zclient *zclient, __PRETTY_FUNCTION__); bgpm->action->refcnt++; break; + case ZAPI_IPTABLE_FAIL_REMOVE: case ZAPI_IPTABLE_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPTABLE REMOVED", @@ -2167,6 +2190,12 @@ static void bgp_encode_pbr_ipset_entry_match(struct stream *s, stream_putc(s, pbime->dst.family); stream_putc(s, pbime->dst.prefixlen); stream_put(s, &pbime->dst.u.prefix, prefix_blen(&pbime->dst)); + + stream_putw(s, pbime->src_port_min); + stream_putw(s, pbime->src_port_max); + stream_putw(s, pbime->dst_port_min); + stream_putw(s, pbime->dst_port_max); + stream_putc(s, pbime->proto); } static void bgp_encode_pbr_iptable_match(struct stream *s, @@ -2490,8 +2519,10 @@ void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, bool install) if (pbra->install_in_progress) return; - zlog_debug("%s: table %d fwmark %d %d", __PRETTY_FUNCTION__, - pbra->table_id, pbra->fwmark, install); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: table %d fwmark %d %d", + __PRETTY_FUNCTION__, + pbra->table_id, pbra->fwmark, install); s = zclient->obuf; stream_reset(s); @@ -2513,8 +2544,10 @@ void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, bool install) if (pbrim->install_in_progress) return; - zlog_debug("%s: name %s type %d %d", __PRETTY_FUNCTION__, - pbrim->ipset_name, pbrim->type, install); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: name %s type %d %d", + __PRETTY_FUNCTION__, + pbrim->ipset_name, pbrim->type, install); s = zclient->obuf; stream_reset(s); @@ -2539,9 +2572,10 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, if (pbrime->install_in_progress) return; - zlog_debug("%s: name %s %d %d", __PRETTY_FUNCTION__, - pbrime->backpointer->ipset_name, - pbrime->unique, install); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: name %s %d %d", __PRETTY_FUNCTION__, + pbrime->backpointer->ipset_name, + pbrime->unique, install); s = zclient->obuf; stream_reset(s); @@ -2559,16 +2593,56 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, pbrime->install_in_progress = true; } +static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) +{ + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + struct bgp_pbr_interface *pbr_if; + struct interface *ifp; + + if (!bgp_pbr_cfg) + return; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + + RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { + ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id); + if (ifp) + stream_putl(s, ifp->ifindex); + } +} + +static int bgp_pbr_get_ifnumber(struct bgp *bgp) +{ + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + struct bgp_pbr_interface *pbr_if; + int cnt = 0; + + if (!bgp_pbr_cfg) + return 0; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + + RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { + if (if_lookup_by_name(pbr_if->name, bgp->vrf_id)) + cnt++; + } + return cnt; +} + void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, struct bgp_pbr_match *pbm, bool install) { struct stream *s; + int ret = 0; + int nb_interface; if (pbm->install_iptable_in_progress) return; - zlog_debug("%s: name %s type %d mark %d %d", __PRETTY_FUNCTION__, - pbm->ipset_name, pbm->type, pba->fwmark, install); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: name %s type %d mark %d %d", + __PRETTY_FUNCTION__, pbm->ipset_name, + pbm->type, pba->fwmark, install); s = zclient->obuf; stream_reset(s); @@ -2578,11 +2652,17 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, VRF_DEFAULT); bgp_encode_pbr_iptable_match(s, pba, pbm); - + nb_interface = bgp_pbr_get_ifnumber(pba->bgp); + stream_putl(s, nb_interface); + if (nb_interface) + bgp_encode_pbr_interface_list(pba->bgp, s); stream_putw_at(s, 0, stream_get_endp(s)); - if (!zclient_send_message(zclient) && install) { - pbm->install_iptable_in_progress = true; - pba->refcnt++; + ret = zclient_send_message(zclient); + if (install) { + if (ret) + pba->refcnt++; + else + pbm->install_iptable_in_progress = true; } } @@ -2626,7 +2706,8 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info("BGP: sending default route to %s table %d (redirect IP)", + zlog_info("BGP: %s default route to %s table %d (redirect IP)", + announce ? "adding" : "withdrawing", buff, table_id); zclient_route_send(announce ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, @@ -2648,7 +2729,8 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, api_nh->type = NEXTHOP_TYPE_IFINDEX; api_nh->ifindex = ifp->ifindex; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info("BGP: sending default route to %s table %d (redirect VRF)", + zlog_info("BGP: %s default route to %s table %d (redirect VRF)", + announce ? "adding" : "withdrawing", vrf->name, table_id); zclient_route_send(announce ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 7ac40fecf..e3c88b9db 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -24,8 +24,9 @@ #include "vxlan.h" extern void bgp_zebra_init(struct thread_master *master); -extern void bgp_zebra_init_tm_connect(void); +extern void bgp_zebra_init_tm_connect(struct bgp *bgp); extern uint32_t bgp_zebra_tm_get_id(void); +extern bool bgp_zebra_tm_chunk_obtained(void); extern void bgp_zebra_destroy(void); extern int bgp_zebra_get_table_range(uint32_t chunk_size, uint32_t *start, uint32_t *end); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 71707b6af..69297cd3e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1988,7 +1988,7 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) if (safi == SAFI_FLOWSPEC) { /* connect to table manager */ - bgp_zebra_init_tm_connect(); + bgp_zebra_init_tm_connect(bgp); } return ret; } @@ -7266,6 +7266,9 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, if (safi == SAFI_EVPN) bgp_config_write_evpn_info(vty, bgp, afi, safi); + if (safi == SAFI_FLOWSPEC) + bgp_fs_config_write_pbr(vty, bgp, afi, safi); + if (safi == SAFI_UNICAST) { bgp_vpn_policy_config_write_afi(vty, bgp, afi); if (CHECK_FLAG(bgp->af_flags[afi][safi], diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 340851e8d..f663df162 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -47,6 +47,7 @@ struct update_subgroup; struct bpacket; +struct bgp_pbr_config; /* * Allow the neighbor XXXX remote-as to take internal or external @@ -531,6 +532,8 @@ struct bgp { struct vpn_policy vpn_policy[AFI_MAX]; + struct bgp_pbr_config *bgp_pbr_cfg; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) @@ -31,11 +31,14 @@ struct pbr_filter { uint32_t filter_bm; /* not encoded by zapi */ -#define PBR_FILTER_SRC_IP (1 << 0) -#define PBR_FILTER_DST_IP (1 << 1) -#define PBR_FILTER_SRC_PORT (1 << 2) -#define PBR_FILTER_DST_PORT (1 << 3) -#define PBR_FILTER_FWMARK (1 << 4) +#define PBR_FILTER_SRC_IP (1 << 0) +#define PBR_FILTER_DST_IP (1 << 1) +#define PBR_FILTER_SRC_PORT (1 << 2) +#define PBR_FILTER_DST_PORT (1 << 3) +#define PBR_FILTER_FWMARK (1 << 4) +#define PBR_FILTER_PROTO (1 << 5) +#define PBR_FILTER_SRC_PORT_RANGE (1 << 6) +#define PBR_FILTER_DST_PORT_RANGE (1 << 7) /* Source and Destination IP address with masks. */ struct prefix src_ip; diff --git a/lib/zclient.h b/lib/zclient.h index c5eaf9c0f..2ec03acc4 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -408,6 +408,7 @@ enum zapi_rule_notify_owner { ZAPI_RULE_FAIL_INSTALL, ZAPI_RULE_INSTALLED, ZAPI_RULE_REMOVED, + ZAPI_RULE_FAIL_REMOVE, }; enum ipset_type { @@ -421,18 +422,21 @@ enum zapi_ipset_notify_owner { ZAPI_IPSET_FAIL_INSTALL, ZAPI_IPSET_INSTALLED, ZAPI_IPSET_REMOVED, + ZAPI_IPSET_FAIL_REMOVE, }; enum zapi_ipset_entry_notify_owner { ZAPI_IPSET_ENTRY_FAIL_INSTALL, ZAPI_IPSET_ENTRY_INSTALLED, ZAPI_IPSET_ENTRY_REMOVED, + ZAPI_IPSET_ENTRY_FAIL_REMOVE, }; enum zapi_iptable_notify_owner { ZAPI_IPTABLE_FAIL_INSTALL, ZAPI_IPTABLE_INSTALLED, ZAPI_IPTABLE_REMOVED, + ZAPI_IPTABLE_FAIL_REMOVE, }; /* Zebra MAC types */ diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index cdacfad4b..8d336c9d0 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -222,6 +222,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED", __PRETTY_FUNCTION__); break; + case ZAPI_RULE_FAIL_REMOVE: case ZAPI_RULE_REMOVED: pbrms->installed &= ~installed; DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED", diff --git a/zebra/debug.c b/zebra/debug.c index 14b36cb5f..85be620bf 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -34,6 +34,8 @@ unsigned long zebra_debug_mpls; unsigned long zebra_debug_vxlan; unsigned long zebra_debug_pw; +DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); + DEFUN_NOSH (show_debugging_zebra, show_debugging_zebra_cmd, "show debugging [zebra]", @@ -88,6 +90,7 @@ DEFUN_NOSH (show_debugging_zebra, if (IS_ZEBRA_DEBUG_PW) vty_out(vty, " Zebra pseudowire debugging is on\n"); + hook_call(zebra_debug_show_debugging, vty); return CMD_SUCCESS; } diff --git a/zebra/debug.h b/zebra/debug.h index 987f9d012..1c08459e2 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -82,4 +82,6 @@ extern unsigned long zebra_debug_pw; extern void zebra_debug_init(void); +DECLARE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); + #endif /* _ZEBRA_DEBUG_H */ diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 5f7354585..f0ed8f2f5 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -98,6 +98,12 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) &rule->rule.filter.dst_ip.u.prefix, bytelen); } + /* fwmark, if specified */ + if (IS_RULE_FILTERING_ON_FWMARK(rule)) { + addattr32(&req.n, sizeof(req), FRA_FWMARK, + rule->rule.filter.fwmark); + } + /* Route table to use to forward, if filter criteria matches. */ if (rule->rule.action.table < 256) req.frh.table = rule->rule.action.table; diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 9a18cc22f..943329b19 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2847,6 +2847,7 @@ static inline void zread_ipset(ZAPI_HANDLER_ARGS) memset(&zpi, 0, sizeof(zpi)); zpi.sock = client->sock; + zpi.vrf_id = zvrf->vrf->vrf_id; STREAM_GETL(s, zpi.unique); STREAM_GETL(s, zpi.type); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); @@ -2885,11 +2886,26 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) STREAM_GETC(s, zpi.dst.prefixlen); STREAM_GET(&zpi.dst.u.prefix, s, prefix_blen(&zpi.dst)); + STREAM_GETW(s, zpi.src_port_min); + STREAM_GETW(s, zpi.src_port_max); + STREAM_GETW(s, zpi.dst_port_min); + STREAM_GETW(s, zpi.dst_port_max); + STREAM_GETC(s, zpi.proto); if (!is_default_prefix(&zpi.src)) zpi.filter_bm |= PBR_FILTER_SRC_IP; if (!is_default_prefix(&zpi.dst)) zpi.filter_bm |= PBR_FILTER_DST_IP; + if (zpi.dst_port_min != 0) + zpi.filter_bm |= PBR_FILTER_DST_PORT; + if (zpi.src_port_min != 0) + zpi.filter_bm |= PBR_FILTER_SRC_PORT; + if (zpi.dst_port_max != 0) + zpi.filter_bm |= PBR_FILTER_DST_PORT_RANGE; + if (zpi.src_port_max != 0) + zpi.filter_bm |= PBR_FILTER_SRC_PORT_RANGE; + if (zpi.proto != 0) + zpi.filter_bm |= PBR_FILTER_PROTO; /* calculate backpointer */ zpi.backpointer = zebra_pbr_lookup_ipset_pername( @@ -2913,13 +2929,17 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) memset(&zpi, 0, sizeof(zpi)); + zpi.interface_name_list = list_new(); zpi.sock = client->sock; + zpi.vrf_id = zvrf->vrf->vrf_id; STREAM_GETL(s, zpi.unique); STREAM_GETL(s, zpi.type); STREAM_GETL(s, zpi.filter_bm); STREAM_GETL(s, zpi.action); STREAM_GETL(s, zpi.fwmark); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + STREAM_GETL(s, zpi.nb_interface); + zebra_pbr_iptable_update_interfacelist(s, &zpi); if (hdr->command == ZEBRA_IPTABLE_ADD) zebra_pbr_add_iptable(zvrf->zns, &zpi); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 5c62e366a..4526a1487 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -262,10 +262,10 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) hash_clean(zns->rules_hash, zebra_pbr_rules_free); hash_free(zns->rules_hash); - hash_clean(zns->ipset_hash, zebra_pbr_ipset_free); - hash_free(zns->ipset_hash); hash_clean(zns->ipset_entry_hash, zebra_pbr_ipset_entry_free), + hash_clean(zns->ipset_hash, zebra_pbr_ipset_free); + hash_free(zns->ipset_hash); hash_free(zns->ipset_entry_hash); hash_clean(zns->iptable_hash, zebra_pbr_iptable_free); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 54a9cdbc5..6a42aaecb 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -23,14 +23,51 @@ #include <jhash.h> #include <hash.h> +#include <memory.h> +#include <hook.h> #include "zebra/zebra_pbr.h" #include "zebra/rt.h" #include "zebra/zapi_msg.h" +#include "zebra/zebra_memory.h" /* definitions */ +DEFINE_MTYPE_STATIC(ZEBRA, PBR_IPTABLE_IFNAME, "PBR interface list") + +/* definitions */ +static const struct message ipset_type_msg[] = { + {IPSET_NET_PORT_NET, "net,port,net"}, + {IPSET_NET_PORT, "net,port"}, + {IPSET_NET_NET, "net,net"}, + {IPSET_NET, "net"}, + {0} +}; /* static function declarations */ +DEFINE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns, + struct zebra_pbr_ipset_entry *ipset, + uint64_t *pkts, uint64_t *bytes), + (zns, ipset, pkts, bytes)) + +DEFINE_HOOK(zebra_pbr_iptable_wrap_script_get_stat, (struct zebra_ns *zns, + struct zebra_pbr_iptable *iptable, + uint64_t *pkts, uint64_t *bytes), + (zns, iptable, pkts, bytes)) + +DEFINE_HOOK(zebra_pbr_iptable_wrap_script_update, (struct zebra_ns *zns, + int cmd, + struct zebra_pbr_iptable *iptable), + (zns, cmd, iptable)); + +DEFINE_HOOK(zebra_pbr_ipset_entry_wrap_script_update, (struct zebra_ns *zns, + int cmd, + struct zebra_pbr_ipset_entry *ipset), + (zns, cmd, ipset)); + +DEFINE_HOOK(zebra_pbr_ipset_wrap_script_update, (struct zebra_ns *zns, + int cmd, + struct zebra_pbr_ipset *ipset), + (zns, cmd, ipset)); /* Private functions */ @@ -145,9 +182,15 @@ static struct zebra_pbr_rule *pbr_rule_lookup_unique(struct zebra_ns *zns, void zebra_pbr_ipset_free(void *arg) { struct zebra_pbr_ipset *ipset; + struct zebra_ns *zns; ipset = (struct zebra_pbr_ipset *)arg; - + if (vrf_is_backend_netns()) + zns = zebra_ns_lookup(ipset->vrf_id); + else + zns = zebra_ns_lookup(NS_DEFAULT); + hook_call(zebra_pbr_ipset_wrap_script_update, + zns, 0, ipset); XFREE(MTYPE_TMP, ipset); } @@ -179,8 +222,17 @@ int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2) void zebra_pbr_ipset_entry_free(void *arg) { struct zebra_pbr_ipset_entry *ipset; + struct zebra_ns *zns; ipset = (struct zebra_pbr_ipset_entry *)arg; + if (ipset->backpointer && vrf_is_backend_netns()) { + struct zebra_pbr_ipset *ips = ipset->backpointer; + + zns = zebra_ns_lookup((ns_id_t)ips->vrf_id); + } else + zns = zebra_ns_lookup(NS_DEFAULT); + hook_call(zebra_pbr_ipset_entry_wrap_script_update, + zns, 0, ipset); XFREE(MTYPE_TMP, ipset); } @@ -194,6 +246,11 @@ uint32_t zebra_pbr_ipset_entry_hash_key(void *arg) key = prefix_hash_key(&ipset->src); key = jhash_1word(ipset->unique, key); key = jhash_1word(prefix_hash_key(&ipset->dst), key); + key = jhash(&ipset->dst_port_min, 2, key); + key = jhash(&ipset->dst_port_max, 2, key); + key = jhash(&ipset->src_port_min, 2, key); + key = jhash(&ipset->src_port_max, 2, key); + key = jhash(&ipset->proto, 1, key); return key; } @@ -214,15 +271,44 @@ int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2) if (!prefix_same(&r1->dst, &r2->dst)) return 0; + if (r1->src_port_min != r2->src_port_min) + return 0; + + if (r1->src_port_max != r2->src_port_max) + return 0; + + if (r1->dst_port_min != r2->dst_port_min) + return 0; + + if (r1->dst_port_max != r2->dst_port_max) + return 0; + + if (r1->proto != r2->proto) + return 0; return 1; } void zebra_pbr_iptable_free(void *arg) { struct zebra_pbr_iptable *iptable; + struct listnode *node, *nnode; + char *name; + struct zebra_ns *zns; iptable = (struct zebra_pbr_iptable *)arg; - + if (vrf_is_backend_netns()) + zns = zebra_ns_lookup((ns_id_t)iptable->vrf_id); + else + zns = zebra_ns_lookup(NS_DEFAULT); + hook_call(zebra_pbr_iptable_wrap_script_update, + zns, 0, iptable); + + for (ALL_LIST_ELEMENTS(iptable->interface_name_list, + node, nnode, name)) { + XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); + list_delete_node(iptable->interface_name_list, + node); + } XFREE(MTYPE_TMP, iptable); } @@ -283,7 +369,6 @@ void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule) (void)hash_get(zns->rules_hash, rule, pbr_rule_alloc_intern); kernel_add_pbr_rule(rule); - /* * Rule Replace semantics, if we have an old, install the * new rule, look above, and then delete the old @@ -320,6 +405,45 @@ static void zebra_pbr_cleanup_rules(struct hash_backet *b, void *data) } } +static void zebra_pbr_cleanup_ipset(struct hash_backet *b, void *data) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_pbr_ipset *ipset = b->data; + int *sock = data; + + if (ipset->sock == *sock) { + hook_call(zebra_pbr_ipset_wrap_script_update, + zns, 0, ipset); + hash_release(zns->ipset_hash, ipset); + } +} + +static void zebra_pbr_cleanup_ipset_entry(struct hash_backet *b, void *data) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_pbr_ipset_entry *ipset = b->data; + int *sock = data; + + if (ipset->sock == *sock) { + hook_call(zebra_pbr_ipset_entry_wrap_script_update, + zns, 0, ipset); + hash_release(zns->ipset_entry_hash, ipset); + } +} + +static void zebra_pbr_cleanup_iptable(struct hash_backet *b, void *data) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_pbr_iptable *iptable = b->data; + int *sock = data; + + if (iptable->sock == *sock) { + hook_call(zebra_pbr_iptable_wrap_script_update, + zns, 0, iptable); + hash_release(zns->iptable_hash, iptable); + } +} + static int zebra_pbr_client_close_cleanup(struct zserv *client) { int sock = client->sock; @@ -328,6 +452,12 @@ static int zebra_pbr_client_close_cleanup(struct zserv *client) if (!sock) return 0; hash_iterate(zns->rules_hash, zebra_pbr_cleanup_rules, &sock); + hash_iterate(zns->iptable_hash, + zebra_pbr_cleanup_iptable, &sock); + hash_iterate(zns->ipset_entry_hash, + zebra_pbr_cleanup_ipset_entry, &sock); + hash_iterate(zns->ipset_hash, + zebra_pbr_cleanup_ipset, &sock); return 1; } @@ -353,10 +483,14 @@ static void *pbr_ipset_alloc_intern(void *arg) void zebra_pbr_create_ipset(struct zebra_ns *zns, struct zebra_pbr_ipset *ipset) { + int ret; + (void)hash_get(zns->ipset_hash, ipset, pbr_ipset_alloc_intern); - /* TODO: - * - Netlink call - */ + ret = hook_call(zebra_pbr_ipset_wrap_script_update, + zns, 1, ipset); + kernel_pbr_ipset_add_del_status(ipset, + ret ? SOUTHBOUND_INSTALL_SUCCESS + : SOUTHBOUND_INSTALL_FAILURE); } void zebra_pbr_destroy_ipset(struct zebra_ns *zns, @@ -365,13 +499,12 @@ void zebra_pbr_destroy_ipset(struct zebra_ns *zns, struct zebra_pbr_ipset *lookup; lookup = hash_lookup(zns->ipset_hash, ipset); - /* TODO: - * - Netlink destroy from kernel - * - ?? destroy ipset entries before - */ - if (lookup) + hook_call(zebra_pbr_ipset_wrap_script_update, + zns, 0, ipset); + if (lookup) { + hash_release(zns->ipset_hash, lookup); XFREE(MTYPE_TMP, lookup); - else + } else zlog_warn("%s: IPSet Entry being deleted we know nothing about", __PRETTY_FUNCTION__); } @@ -381,6 +514,12 @@ struct pbr_ipset_name_lookup { char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; +static const char *zebra_pbr_ipset_type2str(uint32_t type) +{ + return lookup_msg(ipset_type_msg, type, + "Unrecognized IPset Type"); +} + static int zebra_pbr_ipset_pername_walkcb(struct hash_backet *backet, void *arg) { struct pbr_ipset_name_lookup *pinl = @@ -427,12 +566,15 @@ static void *pbr_ipset_entry_alloc_intern(void *arg) void zebra_pbr_add_ipset_entry(struct zebra_ns *zns, struct zebra_pbr_ipset_entry *ipset) { + int ret; + (void)hash_get(zns->ipset_entry_hash, ipset, pbr_ipset_entry_alloc_intern); - /* TODO: - * - attach to ipset list - * - Netlink add to kernel - */ + ret = hook_call(zebra_pbr_ipset_entry_wrap_script_update, + zns, 1, ipset); + kernel_pbr_ipset_entry_add_del_status(ipset, + ret ? SOUTHBOUND_INSTALL_SUCCESS + : SOUTHBOUND_INSTALL_FAILURE); } void zebra_pbr_del_ipset_entry(struct zebra_ns *zns, @@ -441,14 +583,12 @@ void zebra_pbr_del_ipset_entry(struct zebra_ns *zns, struct zebra_pbr_ipset_entry *lookup; lookup = hash_lookup(zns->ipset_entry_hash, ipset); - /* TODO: - * - Netlink destroy - * - detach from ipset list - * - ?? if no more entres, delete ipset - */ - if (lookup) + hook_call(zebra_pbr_ipset_entry_wrap_script_update, + zns, 0, ipset); + if (lookup) { + hash_release(zns->ipset_entry_hash, lookup); XFREE(MTYPE_TMP, lookup); - else + } else zlog_warn("%s: IPSet being deleted we know nothing about", __PRETTY_FUNCTION__); } @@ -470,24 +610,36 @@ static void *pbr_iptable_alloc_intern(void *arg) void zebra_pbr_add_iptable(struct zebra_ns *zns, struct zebra_pbr_iptable *iptable) { + int ret; + (void)hash_get(zns->iptable_hash, iptable, pbr_iptable_alloc_intern); - /* TODO call netlink layer */ + ret = hook_call(zebra_pbr_iptable_wrap_script_update, zns, 1, iptable); + kernel_pbr_iptable_add_del_status(iptable, + ret ? SOUTHBOUND_INSTALL_SUCCESS + : SOUTHBOUND_INSTALL_FAILURE); } void zebra_pbr_del_iptable(struct zebra_ns *zns, struct zebra_pbr_iptable *iptable) { - struct zebra_pbr_ipset_entry *lookup; + struct zebra_pbr_iptable *lookup; lookup = hash_lookup(zns->iptable_hash, iptable); - /* TODO: - * - call netlink layer - * - detach from iptable list - */ - if (lookup) + hook_call(zebra_pbr_iptable_wrap_script_update, zns, 0, iptable); + if (lookup) { + struct listnode *node, *nnode; + char *name; + + hash_release(zns->iptable_hash, lookup); + for (ALL_LIST_ELEMENTS(iptable->interface_name_list, + node, nnode, name)) { + XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); + list_delete_node(iptable->interface_name_list, + node); + } XFREE(MTYPE_TMP, lookup); - else + } else zlog_warn("%s: IPTable being deleted we know nothing about", __PRETTY_FUNCTION__); } @@ -509,7 +661,7 @@ void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED); break; case SOUTHBOUND_DELETE_FAILURE: - zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED); + zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_REMOVE); break; } } @@ -528,8 +680,10 @@ void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset, zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_INSTALL); break; case SOUTHBOUND_DELETE_SUCCESS: + zsend_ipset_notify_owner(ipset, ZAPI_IPSET_REMOVED); + break; case SOUTHBOUND_DELETE_FAILURE: - /* TODO : handling of delete event */ + zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_REMOVE); break; } } @@ -551,8 +705,12 @@ void kernel_pbr_ipset_entry_add_del_status( ZAPI_IPSET_ENTRY_FAIL_INSTALL); break; case SOUTHBOUND_DELETE_SUCCESS: + zsend_ipset_entry_notify_owner(ipset, + ZAPI_IPSET_ENTRY_REMOVED); + break; case SOUTHBOUND_DELETE_FAILURE: - /* TODO : handling of delete event */ + zsend_ipset_entry_notify_owner(ipset, + ZAPI_IPSET_ENTRY_FAIL_REMOVE); break; } } @@ -571,8 +729,12 @@ void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_FAIL_INSTALL); break; case SOUTHBOUND_DELETE_SUCCESS: + zsend_iptable_notify_owner(iptable, + ZAPI_IPTABLE_REMOVED); + break; case SOUTHBOUND_DELETE_FAILURE: - /* TODO : handling of delete event */ + zsend_iptable_notify_owner(iptable, + ZAPI_IPTABLE_FAIL_REMOVE); break; } } @@ -584,3 +746,256 @@ int kernel_pbr_rule_del(struct zebra_pbr_rule *rule) { return 0; } + +struct zebra_pbr_ipset_entry_unique_display { + struct zebra_pbr_ipset *zpi; + struct vty *vty; + struct zebra_ns *zns; +}; + +struct zebra_pbr_env_display { + struct zebra_ns *zns; + struct vty *vty; +}; + +static const char *zebra_pbr_prefix2str(union prefixconstptr pu, + char *str, int size) +{ + const struct prefix *p = pu.p; + char buf[PREFIX2STR_BUFFER]; + + if (p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) { + snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix, + buf, PREFIX2STR_BUFFER)); + return str; + } + return prefix2str(pu, str, size); +} + +static void zebra_pbr_display_port(struct vty *vty, uint32_t filter_bm, + uint16_t port_min, uint16_t port_max, + uint8_t proto) +{ + if (!(filter_bm & PBR_FILTER_PROTO)) { + if (port_max) + vty_out(vty, ":udp/tcp:%d-%d", + port_min, port_max); + else + vty_out(vty, ":udp/tcp:%d", + port_min); + } else { + if (port_max) + vty_out(vty, ":proto %d:%d-%d", + proto, port_min, port_max); + else + vty_out(vty, ":proto %d:%d", + proto, port_min); + } +} + +static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, + void *arg) +{ + struct zebra_pbr_ipset_entry_unique_display *unique = + (struct zebra_pbr_ipset_entry_unique_display *)arg; + struct zebra_pbr_ipset *zpi = unique->zpi; + struct vty *vty = unique->vty; + struct zebra_pbr_ipset_entry *zpie = + (struct zebra_pbr_ipset_entry *)backet->data; + uint64_t pkts = 0, bytes = 0; + struct zebra_ns *zns = unique->zns; + int ret = 0; + + if (zpie->backpointer != zpi) + return HASHWALK_CONTINUE; + + if ((zpi->type == IPSET_NET_NET) || + (zpi->type == IPSET_NET_PORT_NET)) { + 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) + zebra_pbr_display_port(vty, zpie->filter_bm, + zpie->src_port_min, + zpie->src_port_max, + zpie->proto); + 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) + zebra_pbr_display_port(vty, zpie->filter_bm, + zpie->dst_port_min, + zpie->dst_port_max, + zpie->proto); + } else if ((zpi->type == IPSET_NET) || + (zpi->type == IPSET_NET_PORT)) { + char buf[PREFIX_STRLEN]; + + if (zpie->filter_bm & PBR_FILTER_SRC_IP) { + zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf)); + vty_out(vty, "\tfrom %s", buf); + } + if (zpie->filter_bm & PBR_FILTER_SRC_PORT) + zebra_pbr_display_port(vty, zpie->filter_bm, + zpie->src_port_min, + zpie->src_port_max, + zpie->proto); + if (zpie->filter_bm & PBR_FILTER_DST_IP) { + zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf)); + vty_out(vty, "\tto %s", buf); + } + if (zpie->filter_bm & PBR_FILTER_DST_PORT) + zebra_pbr_display_port(vty, zpie->filter_bm, + zpie->dst_port_min, + zpie->dst_port_max, + zpie->proto); + } + vty_out(vty, " (%u)\n", zpie->unique); + + ret = hook_call(zebra_pbr_ipset_entry_wrap_script_get_stat, + zns, zpie, &pkts, &bytes); + if (ret && pkts > 0) + vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n", + pkts, bytes); + return HASHWALK_CONTINUE; +} + +static int zebra_pbr_show_ipset_walkcb(struct hash_backet *backet, void *arg) +{ + struct zebra_pbr_env_display *uniqueipset = + (struct zebra_pbr_env_display *)arg; + struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)backet->data; + struct zebra_pbr_ipset_entry_unique_display unique; + struct vty *vty = uniqueipset->vty; + struct zebra_ns *zns = uniqueipset->zns; + + vty_out(vty, "IPset %s type %s\n", zpi->ipset_name, + zebra_pbr_ipset_type2str(zpi->type)); + unique.vty = vty; + unique.zpi = zpi; + unique.zns = zns; + hash_walk(zns->ipset_entry_hash, zebra_pbr_show_ipset_entry_walkcb, + &unique); + vty_out(vty, "\n"); + return HASHWALK_CONTINUE; +} + +/* + */ +void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) +{ + struct zebra_pbr_ipset *zpi; + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_pbr_ipset_entry_unique_display unique; + struct zebra_pbr_env_display uniqueipset; + + if (ipsetname) { + zpi = zebra_pbr_lookup_ipset_pername(zns, ipsetname); + if (!zpi) { + vty_out(vty, "No IPset %s found\n", ipsetname); + return; + } + vty_out(vty, "IPset %s type %s\n", ipsetname, + zebra_pbr_ipset_type2str(zpi->type)); + + unique.vty = vty; + unique.zpi = zpi; + unique.zns = zns; + hash_walk(zns->ipset_entry_hash, + zebra_pbr_show_ipset_entry_walkcb, + &unique); + return; + } + uniqueipset.zns = zns; + uniqueipset.vty = vty; + hash_walk(zns->ipset_hash, zebra_pbr_show_ipset_walkcb, + &uniqueipset); +} + +struct pbr_rule_fwmark_lookup { + struct zebra_pbr_rule *ptr; + uint32_t fwmark; +}; + +static int zebra_pbr_rule_lookup_fwmark_walkcb(struct hash_backet *backet, + void *arg) +{ + struct pbr_rule_fwmark_lookup *iprule = + (struct pbr_rule_fwmark_lookup *)arg; + struct zebra_pbr_rule *zpr = (struct zebra_pbr_rule *)backet->data; + + if (iprule->fwmark == zpr->rule.filter.fwmark) { + iprule->ptr = zpr; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +static int zebra_pbr_show_iptable_walkcb(struct hash_backet *backet, void *arg) +{ + struct zebra_pbr_iptable *iptable = + (struct zebra_pbr_iptable *)backet->data; + struct zebra_pbr_env_display *env = (struct zebra_pbr_env_display *)arg; + struct vty *vty = env->vty; + struct zebra_ns *zns = env->zns; + int ret; + uint64_t pkts = 0, bytes = 0; + + vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name, + iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", + iptable->unique); + + ret = hook_call(zebra_pbr_iptable_wrap_script_get_stat, + zns, iptable, &pkts, &bytes); + if (ret && pkts > 0) + vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n", + pkts, bytes); + if (iptable->action != ZEBRA_IPTABLES_DROP) { + struct pbr_rule_fwmark_lookup prfl; + + prfl.fwmark = iptable->fwmark; + prfl.ptr = NULL; + hash_walk(zns->rules_hash, + &zebra_pbr_rule_lookup_fwmark_walkcb, &prfl); + if (prfl.ptr) { + struct zebra_pbr_rule *zpr = prfl.ptr; + + vty_out(vty, "\t table %u, fwmark %u\n", + zpr->rule.action.table, + prfl.fwmark); + } + } + return HASHWALK_CONTINUE; +} + +void zebra_pbr_show_iptable(struct vty *vty) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_pbr_env_display env; + + env.vty = vty; + env.zns = zns; + + hash_walk(zns->iptable_hash, zebra_pbr_show_iptable_walkcb, + &env); +} + +void zebra_pbr_iptable_update_interfacelist(struct stream *s, + struct zebra_pbr_iptable *zpi) +{ + uint32_t i = 0, index; + struct interface *ifp; + char *name; + + for (i = 0; i < zpi->nb_interface; i++) { + STREAM_GETL(s, index); + ifp = if_lookup_by_index(index, zpi->vrf_id); + if (!ifp) + continue; + name = XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifp->name); + listnode_add(zpi->interface_name_list, name); + } +stream_failure: + return; +} diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 6b5cd1e8d..31fc55358 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -48,6 +48,8 @@ struct zebra_pbr_rule { (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT) #define IS_RULE_FILTERING_ON_DST_PORT(r) \ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT) +#define IS_RULE_FILTERING_ON_FWMARK(r) \ + (r->rule.filter.filter_bm & PBR_FILTER_FWMARK) /* * An IPSet Entry Filter @@ -61,6 +63,8 @@ struct zebra_pbr_ipset { */ int sock; + vrf_id_t vrf_id; + uint32_t unique; /* type is encoded as uint32_t @@ -87,6 +91,13 @@ struct zebra_pbr_ipset_entry { struct prefix src; struct prefix dst; + uint16_t src_port_min; + uint16_t src_port_max; + uint16_t dst_port_min; + uint16_t dst_port_max; + + uint8_t proto; + uint32_t filter_bm; struct zebra_pbr_ipset *backpointer; @@ -104,6 +115,8 @@ struct zebra_pbr_iptable { */ int sock; + vrf_id_t vrf_id; + uint32_t unique; /* include ipset type @@ -118,6 +131,10 @@ struct zebra_pbr_iptable { uint32_t action; + uint32_t nb_interface; + + struct list *interface_name_list; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; @@ -204,4 +221,31 @@ extern uint32_t zebra_pbr_iptable_hash_key(void *arg); extern int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_init(void); +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); + +DECLARE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns, + struct zebra_pbr_ipset_entry *ipset, + uint64_t *pkts, uint64_t *bytes), + (zns, ipset, pkts, bytes)) +DECLARE_HOOK(zebra_pbr_iptable_wrap_script_get_stat, (struct zebra_ns *zns, + struct zebra_pbr_iptable *iptable, + uint64_t *pkts, uint64_t *bytes), + (zns, iptable, pkts, bytes)) +DECLARE_HOOK(zebra_pbr_iptable_wrap_script_update, (struct zebra_ns *zns, + int cmd, + struct zebra_pbr_iptable *iptable), + (zns, cmd, iptable)); + +DECLARE_HOOK(zebra_pbr_ipset_entry_wrap_script_update, (struct zebra_ns *zns, + int cmd, + struct zebra_pbr_ipset_entry *ipset), + (zns, cmd, ipset)); +DECLARE_HOOK(zebra_pbr_ipset_wrap_script_update, (struct zebra_ns *zns, + int cmd, + struct zebra_pbr_ipset *ipset), + (zns, cmd, ipset)); + #endif /* _ZEBRA_PBR_H */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index e6f80f92a..a094ca585 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -51,6 +51,7 @@ #include "zebra/router-id.h" #include "zebra/ipforward.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_pbr.h" extern int allow_delete; @@ -3260,6 +3261,37 @@ DEFUN (show_evpn_neigh_vni_vtep, return CMD_SUCCESS; } +/* policy routing contexts */ +DEFUN (show_pbr_ipset, + show_pbr_ipset_cmd, + "show pbr ipset [WORD]", + SHOW_STR + "Policy-Based Routing\n" + "IPset Context information\n" + "IPset Name information\n") +{ + int idx = 0; + int found = 0; + found = argv_find(argv, argc, "WORD", &idx); + if (!found) + zebra_pbr_show_ipset_list(vty, NULL); + else + zebra_pbr_show_ipset_list(vty, argv[idx]->arg); + return CMD_SUCCESS; +} + +/* policy routing contexts */ +DEFUN (show_pbr_iptable, + show_pbr_iptable_cmd, + "show pbr iptable", + SHOW_STR + "Policy-Based Routing\n" + "IPtable Context information\n") +{ + zebra_pbr_show_iptable(vty); + return CMD_SUCCESS; +} + /* Static ip route configuration write function. */ static int zebra_ip_config(struct vty *vty) { @@ -3762,6 +3794,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd); + install_element(VIEW_NODE, &show_pbr_ipset_cmd); + install_element(VIEW_NODE, &show_pbr_iptable_cmd); + install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd); install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd); install_element(VRF_NODE, &vrf_vni_mapping_cmd); |