diff options
122 files changed, 7000 insertions, 4651 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c index e7ba29ed06..14e583a35c 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -137,6 +137,7 @@ struct option longopts[] = }; static const struct frr_yang_module_info *const babeld_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_vrf_info, }; diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 39d51eb649..258f074e8c 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -111,6 +111,7 @@ static struct quagga_signal_t bfd_signals[] = { }; static const struct frr_yang_module_info *const bfdd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_bfdd_info, &frr_vrf_info, diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index ab50c545b5..412c8e3e5e 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -201,7 +201,6 @@ static int bgp_process_reads(struct thread *thread) while (more) { /* static buffer for transferring packets */ - static unsigned char pktbuf[BGP_MAX_PACKET_SIZE]; /* shorter alias to peer's input buffer */ struct ringbuf *ibw = peer->ibuf_work; /* packet size as given by header */ @@ -231,8 +230,9 @@ static int bgp_process_reads(struct thread *thread) */ if (ringbuf_remain(ibw) >= pktsize) { struct stream *pkt = stream_new(pktsize); - assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize); - stream_put(pkt, pktbuf, pktsize); + assert(STREAM_WRITEABLE(pkt) == pktsize); + assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize); + stream_set_endp(pkt, pktsize); frr_with_mutex(&peer->io_mtx) { stream_fifo_push(peer->ibuf, pkt); diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 04be8d83eb..f9ff99cab0 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -359,6 +359,7 @@ static void bgp_vrf_terminate(void) } static const struct frr_yang_module_info *const bgpd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_vrf_info, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 32489459cb..b0fbdbcf01 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -13525,14 +13525,21 @@ void bgp_route_init(void) install_element(BGP_IPV6M_NODE, &no_ipv6_bgp_distance_source_access_list_cmd); + /* BGP dampening */ install_element(BGP_NODE, &bgp_damp_set_cmd); install_element(BGP_NODE, &bgp_damp_unset_cmd); install_element(BGP_IPV4_NODE, &bgp_damp_set_cmd); install_element(BGP_IPV4_NODE, &bgp_damp_unset_cmd); - - /* IPv4 Multicast Mode */ install_element(BGP_IPV4M_NODE, &bgp_damp_set_cmd); install_element(BGP_IPV4M_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV4L_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV4L_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV6_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV6_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV6M_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV6M_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV6L_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV6L_NODE, &bgp_damp_unset_cmd); /* Large Communities */ install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index e16030d82d..aa5857b9d4 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -14815,11 +14815,6 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, vty_out(vty, "\n"); } - /* BGP flag dampening. */ - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING)) - bgp_config_write_damp(vty, afi, safi); - /* Route reflector client. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)) { @@ -15044,6 +15039,10 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, bgp_config_write_redistribute(vty, bgp, afi, safi); + /* BGP flag dampening. */ + if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) + bgp_config_write_damp(vty, afi, safi); + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); @@ -17620,8 +17619,7 @@ DEFUN (extcommunity_list_standard, argv_find(argv, argc, "WORD", &idx); cl_number_or_name = argv[idx]->arg; - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) + if (argv_find(argv, argc, "(1-4294967295)", &idx)) seq = argv[idx]->arg; direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT @@ -17666,8 +17664,7 @@ DEFUN (extcommunity_list_name_expanded, argv_find(argv, argc, "WORD", &idx); cl_number_or_name = argv[idx]->arg; - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) + if (argv_find(argv, argc, "(1-4294967295)", &idx)) seq = argv[idx]->arg; direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT @@ -17710,8 +17707,7 @@ DEFUN (no_extcommunity_list_standard_all, char *seq = NULL; int idx = 0; - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) + if (argv_find(argv, argc, "(1-4294967295)", &idx)) seq = argv[idx]->arg; idx = 0; @@ -17775,8 +17771,7 @@ DEFUN (no_extcommunity_list_expanded_all, char *seq = NULL; int idx = 0; - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) + if (argv_find(argv, argc, "(1-4294967295)", &idx)) seq = argv[idx]->arg; idx = 0; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index faee7dad4a..ec6e00ea34 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2511,6 +2511,9 @@ static void peer_group2peer_config_copy(struct peer_group *group, } } + /* Update GR flags for the peer. */ + bgp_peer_gr_flags_update(peer); + bgp_bfd_peer_group2peer_copy(conf, peer); } diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 49fa4fe832..eaf6c67bc9 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -191,6 +191,21 @@ review can be made and the branch merged into master. If a development branch is becomes un-maintained or not being actively worked on after three months then the Maintainers can decide to remove the branch. +Debian Branches +--------------- + +The Debian project contains "official" packages for FRR. While FRR +Maintainers may participate in creating these, it is entirely the Debian +project's decision what to ship and how to work on this. + +As a courtesy and for FRR's benefit, this packaging work is currently visible +in git branches named ``debian/*`` on the main FRR git repository. These +branches are for the exclusive use by people involved in Debian packaging work +for FRR. Direct commit access may be handed out and FRR git rules (review, +testing, etc.) do not apply. Do not push to these branches without talking +to the people noted under ``Maintainer:`` and ``Uploaders:`` in +``debian/control`` on the target branch -- even if you are a FRR Maintainer. + Changelog --------- The changelog will be the base for the release notes. A changelog entry for diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index 199685cdfb..dd0e67d4b7 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -103,3 +103,25 @@ keyword. At present, no sharp commands will be preserved in the config. nexthops are specified in nexthop-group ``NAME``. If ``prefix`` is specified, remove label bindings from the route of type ``TYPE`` also. + +.. index:: sharp send opaque +.. clicmd:: sharp send opaque type (1-255) (1-1000) + + Send opaque ZAPI messages with subtype ``type``. Sharpd will send + a stream of messages if the count is greater than one. + +.. index:: sharp send opaque unicast +.. clicmd:: sharp send opaque unicast type (1-255) $proto_str [{instance (0-1000) | session (1-1000)}] (1-1000) + + Send unicast opaque ZAPI messages with subtype ``type``. The + protocol, instance, and session_id identify a single target zapi + client. Sharpd will send a stream of messages if the count is + greater than one. + +.. index:: sharp send opaque reg unreg +.. clicmd:: sharp send opaque <reg | unreg> $proto_str [{instance (0-1000) | session (1-1000)}] type (1-1000) + + Send opaque ZAPI registration and unregistration messages for a + single subtype. The messages must specify a protocol daemon by + name, and can include optional zapi ``instance`` and ``session`` + values. diff --git a/docker/alpine/docker-start b/docker/alpine/docker-start index 52cfb664ce..3f7737d3bf 100755 --- a/docker/alpine/docker-start +++ b/docker/alpine/docker-start @@ -5,7 +5,7 @@ set -e ## # For volume mounts... ## -chown -R frr:frr /etc/frr +chown -R frr:frr /etc/frr || true /usr/lib/frr/frrinit.sh start # Sleep forever diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index cdf1c6acdb..6c44ce361c 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -139,6 +139,7 @@ struct quagga_signal_t eigrp_signals[] = { static const struct frr_yang_module_info *const eigrpd_yang_modules[] = { &frr_eigrpd_info, + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_vrf_info, diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index acfe3e2e1f..94e38435a3 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -268,10 +268,11 @@ void isis_adj_state_change(struct isis_adjacency **padj, struct isis_circuit *circuit = adj->circuit; bool del = false; + if (new_state == old_state) + return; + adj->adj_state = new_state; - if (new_state != old_state) { - send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); - } + send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); if (isis->debugs & DEBUG_ADJ_PACKETS) { zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s", @@ -314,7 +315,7 @@ void isis_adj_state_change(struct isis_adjacency **padj, * purposes */ adj->last_flap = time(NULL); adj->flaps++; - } else if (new_state == ISIS_ADJ_DOWN) { + } else if (old_state == ISIS_ADJ_UP) { listnode_delete(circuit->u.bc.adjdb[level - 1], adj); @@ -323,7 +324,8 @@ void isis_adj_state_change(struct isis_adjacency **padj, isis_tx_queue_clean(circuit->tx_queue); hook_call(isis_adj_state_change_hook, adj); - del = true; + if (new_state == ISIS_ADJ_DOWN) + del = true; } if (circuit->u.bc.lan_neighs[level - 1]) { @@ -362,7 +364,7 @@ void isis_adj_state_change(struct isis_adjacency **padj, circuit, 0, &circuit->t_send_csnp[1]); } - } else if (new_state == ISIS_ADJ_DOWN) { + } else if (old_state == ISIS_ADJ_UP) { if (adj->circuit->u.p2p.neighbor == adj) adj->circuit->u.p2p.neighbor = NULL; circuit->upadjcount[level - 1]--; @@ -370,7 +372,8 @@ void isis_adj_state_change(struct isis_adjacency **padj, isis_tx_queue_clean(circuit->tx_queue); hook_call(isis_adj_state_change_hook, adj); - del = true; + if (new_state == ISIS_ADJ_DOWN) + del = true; } } } diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 003be8d682..e0013e4f1f 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -1233,23 +1233,35 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, bool ipv6_router) { struct isis_area *area = circuit->area; - bool change = circuit->ip_router != ip_router - || circuit->ipv6_router != ipv6_router; + int old_ipr = circuit->ip_router; + int old_ipv6r = circuit->ipv6_router; + + /* is there something to do? */ + if (old_ipr == ip_router && old_ipv6r == ipv6_router) + return; - area->ip_circuits += ip_router - circuit->ip_router; - area->ipv6_circuits += ipv6_router - circuit->ipv6_router; circuit->ip_router = ip_router; circuit->ipv6_router = ipv6_router; + circuit_update_nlpids(circuit); - if (!change) + /* the area should always be there if we get here, but in the past + * there were corner cases where the area was NULL (e.g. because the + * circuit was deconfigured following a validation error). Do not + * segfault if this happens again. + */ + if (!area) { + zlog_err("%s: NULL area for circuit %u", __func__, + circuit->circuit_id); return; + } - circuit_update_nlpids(circuit); + area->ip_circuits += ip_router - old_ipr; + area->ipv6_circuits += ipv6_router - old_ipv6r; if (!ip_router && !ipv6_router) isis_csm_state_change(ISIS_DISABLE, circuit, area); else - lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); + lsp_regenerate_schedule(area, circuit->is_type, 0); } ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive) diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 9202ed5a88..9ff5c86fb0 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -141,23 +141,6 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) EC_ISIS_CONFIG, "Could not bring up %s because of invalid config.", circuit->interface->name); - flog_err( - EC_ISIS_CONFIG, - "Clearing config for %s. Please re-examine it.", - circuit->interface->name); - if (circuit->ip_router) { - circuit->ip_router = 0; - circuit->area->ip_circuits--; - } - if (circuit->ipv6_router) { - circuit->ipv6_router = 0; - circuit->area->ipv6_circuits--; - } - circuit_update_nlpids(circuit); - isis_circuit_deconfigure(circuit, - circuit->area); - listnode_add(isis->init_circ_list, circuit); - circuit->state = C_STATE_INIT; break; } circuit->state = C_STATE_UP; diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 78654b2f1c..7d45dd9c2e 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -166,6 +166,7 @@ struct quagga_signal_t isisd_signals[] = { static const struct frr_yang_module_info *const isisd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, #ifndef FABRICD &frr_isisd_info, diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index a649e896fa..9633e46415 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -116,8 +116,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) addr.addr_len = dotformat2buff(buff, net_title); memcpy(addr.area_addr, buff, addr.addr_len); if (addr.area_addr[addr.addr_len - 1] != 0) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, + snprintf( + args->errmsg, args->errmsg_len, "nsel byte (last byte) in area address must be 0"); return NB_ERR_VALIDATION; } @@ -125,8 +125,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) /* Check that the SystemID portions match */ if (memcmp(isis->sysid, GETSYSID((&addr)), ISIS_SYS_ID_LEN)) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, + snprintf( + args->errmsg, args->errmsg_len, "System ID must not change when defining additional area addresses"); return NB_ERR_VALIDATION; } @@ -332,8 +332,8 @@ int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args) && circuit->state != C_STATE_UP) continue; if (lsp_mtu > isis_circuit_pdu_size(circuit)) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, + snprintf( + args->errmsg, args->errmsg_len, "ISIS area contains circuit %s, which has a maximum PDU size of %zu", circuit->interface->name, isis_circuit_pdu_size(circuit)); @@ -1047,6 +1047,7 @@ int isis_instance_redistribute_ipv6_metric_modify( */ static int isis_multi_topology_common(enum nb_event event, const struct lyd_node *dnode, + char *errmsg, size_t errmsg_len, const char *topology, bool create) { struct isis_area *area; @@ -1056,8 +1057,8 @@ static int isis_multi_topology_common(enum nb_event event, switch (event) { case NB_EV_VALIDATE: if (mtid == (uint16_t)-1) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Unknown topology %s", topology); + snprintf(errmsg, errmsg_len, "Unknown topology %s", + topology); return NB_ERR_VALIDATION; } break; @@ -1100,6 +1101,7 @@ int isis_instance_multi_topology_ipv4_multicast_create( struct nb_cb_create_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv4-multicast", true); } @@ -1107,6 +1109,7 @@ int isis_instance_multi_topology_ipv4_multicast_destroy( struct nb_cb_destroy_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv4-multicast", false); } @@ -1126,15 +1129,17 @@ int isis_instance_multi_topology_ipv4_multicast_overload_modify( int isis_instance_multi_topology_ipv4_management_create( struct nb_cb_create_args *args) { - return isis_multi_topology_common(args->event, args->dnode, "ipv4-mgmt", - true); + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv4-mgmt", true); } int isis_instance_multi_topology_ipv4_management_destroy( struct nb_cb_destroy_args *args) { - return isis_multi_topology_common(args->event, args->dnode, "ipv4-mgmt", - false); + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv4-mgmt", false); } /* @@ -1154,6 +1159,7 @@ int isis_instance_multi_topology_ipv6_unicast_create( struct nb_cb_create_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv6-unicast", true); } @@ -1161,6 +1167,7 @@ int isis_instance_multi_topology_ipv6_unicast_destroy( struct nb_cb_destroy_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv6-unicast", false); } @@ -1181,6 +1188,7 @@ int isis_instance_multi_topology_ipv6_multicast_create( struct nb_cb_create_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv6-multicast", true); } @@ -1188,6 +1196,7 @@ int isis_instance_multi_topology_ipv6_multicast_destroy( struct nb_cb_destroy_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv6-multicast", false); } @@ -1207,15 +1216,17 @@ int isis_instance_multi_topology_ipv6_multicast_overload_modify( int isis_instance_multi_topology_ipv6_management_create( struct nb_cb_create_args *args) { - return isis_multi_topology_common(args->event, args->dnode, "ipv6-mgmt", - true); + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-mgmt", true); } int isis_instance_multi_topology_ipv6_management_destroy( struct nb_cb_destroy_args *args) { - return isis_multi_topology_common(args->event, args->dnode, "ipv6-mgmt", - false); + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-mgmt", false); } /* @@ -1235,6 +1246,7 @@ int isis_instance_multi_topology_ipv6_dstsrc_create( struct nb_cb_create_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv6-dstsrc", true); } @@ -1242,6 +1254,7 @@ int isis_instance_multi_topology_ipv6_dstsrc_destroy( struct nb_cb_destroy_args *args) { return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, "ipv6-dstsrc", false); } @@ -1721,10 +1734,10 @@ int lib_interface_isis_create(struct nb_cb_create_args *args) min_mtu = DEFAULT_LSP_MTU; #endif /* ifndef FABRICD */ if (actual_mtu < min_mtu) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Interface %s has MTU %" PRIu32 - ", minimum MTU for the area is %" PRIu32 "", - ifp->name, actual_mtu, min_mtu); + snprintf(args->errmsg, args->errmsg_len, + "Interface %s has MTU %" PRIu32 + ", minimum MTU for the area is %" PRIu32 "", + ifp->name, actual_mtu, min_mtu); return NB_ERR_VALIDATION; } break; @@ -1801,9 +1814,9 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) area_tag = yang_dnode_get_string(args->dnode, NULL); if (circuit && circuit->area && circuit->area->area_tag && strcmp(circuit->area->area_tag, area_tag)) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "ISIS circuit is already defined on %s", - circuit->area->area_tag); + snprintf(args->errmsg, args->errmsg_len, + "ISIS circuit is already defined on %s", + circuit->area->area_tag); return NB_ERR_VALIDATION; } } @@ -1839,9 +1852,9 @@ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args) if (circuit && circuit->state == C_STATE_UP && circuit->area->is_type != IS_LEVEL_1_AND_2 && circuit->area->is_type != circ_type) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Invalid circuit level for area %s", - circuit->area->area_tag); + snprintf(args->errmsg, args->errmsg_len, + "Invalid circuit level for area %s", + circuit->area->area_tag); return NB_ERR_VALIDATION; } break; @@ -2163,16 +2176,16 @@ int lib_interface_isis_network_type_modify(struct nb_cb_modify_args *args) if (!circuit) break; if (circuit->circ_type == CIRCUIT_T_LOOPBACK) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, + snprintf( + args->errmsg, args->errmsg_len, "Cannot change network type on loopback interface"); return NB_ERR_VALIDATION; } if (net_type == CIRCUIT_T_BROADCAST && circuit->state == C_STATE_UP && !if_is_broadcast(circuit->interface)) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, + snprintf( + args->errmsg, args->errmsg_len, "Cannot configure non-broadcast interface for broadcast operation"); return NB_ERR_VALIDATION; } @@ -2208,8 +2221,8 @@ int lib_interface_isis_passive_modify(struct nb_cb_modify_args *args) if (!ifp) return NB_OK; if (if_is_loopback(ifp)) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Loopback is always passive"); + snprintf(args->errmsg, args->errmsg_len, + "Loopback is always passive"); return NB_ERR_VALIDATION; } } @@ -2312,7 +2325,8 @@ int lib_interface_isis_disable_three_way_handshake_modify( * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast */ static int lib_interface_isis_multi_topology_common( - enum nb_event event, const struct lyd_node *dnode, uint16_t mtid) + enum nb_event event, const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len, uint16_t mtid) { struct isis_circuit *circuit; bool value; @@ -2321,8 +2335,8 @@ static int lib_interface_isis_multi_topology_common( case NB_EV_VALIDATE: circuit = nb_running_get_entry(dnode, NULL, false); if (circuit && circuit->area && circuit->area->oldmetric) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, + snprintf( + errmsg, errmsg_len, "Multi topology IS-IS can only be used with wide metrics"); return NB_ERR_VALIDATION; } @@ -2344,7 +2358,8 @@ int lib_interface_isis_multi_topology_ipv4_unicast_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV4_UNICAST); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV4_UNICAST); } /* @@ -2355,7 +2370,8 @@ int lib_interface_isis_multi_topology_ipv4_multicast_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV4_MULTICAST); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV4_MULTICAST); } /* @@ -2366,7 +2382,8 @@ int lib_interface_isis_multi_topology_ipv4_management_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV4_MGMT); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV4_MGMT); } /* @@ -2377,7 +2394,8 @@ int lib_interface_isis_multi_topology_ipv6_unicast_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV6_UNICAST); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_UNICAST); } /* @@ -2388,7 +2406,8 @@ int lib_interface_isis_multi_topology_ipv6_multicast_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV6_MULTICAST); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_MULTICAST); } /* @@ -2399,7 +2418,8 @@ int lib_interface_isis_multi_topology_ipv6_management_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV6_MGMT); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_MGMT); } /* @@ -2409,5 +2429,6 @@ int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( struct nb_cb_modify_args *args) { return lib_interface_isis_multi_topology_common( - args->event, args->dnode, ISIS_MT_IPV6_DSTSRC); + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_DSTSRC); } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index c6126b9396..56734a4f76 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -180,6 +180,7 @@ static struct quagga_signal_t ldp_signals[] = }; static const struct frr_yang_module_info *const ldpd_yang_modules[] = { + &frr_filter_info, &frr_vrf_info, }; diff --git a/lib/command.h b/lib/command.h index ed0ea18d6f..21bb613540 100644 --- a/lib/command.h +++ b/lib/command.h @@ -406,6 +406,7 @@ struct cmd_node { #define FILTER_LOG_STR "Filter Logs\n" #define BFD_PROFILE_STR "BFD profile.\n" #define BFD_PROFILE_NAME_STR "BFD profile name.\n" +#define SHARP_STR "Sharp Routing Protocol\n" #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" diff --git a/lib/filter.c b/lib/filter.c index 4a83b8b043..e6add0462b 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -29,70 +29,12 @@ #include "log.h" #include "routemap.h" #include "libfrr.h" +#include "northbound_cli.h" DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List") DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str") DEFINE_MTYPE_STATIC(LIB, ACCESS_FILTER, "Access Filter") -struct filter_cisco { - /* Cisco access-list */ - int extended; - struct in_addr addr; - struct in_addr addr_mask; - struct in_addr mask; - struct in_addr mask_mask; -}; - -struct filter_zebra { - /* If this filter is "exact" match then this flag is set. */ - int exact; - - /* Prefix information. */ - struct prefix prefix; -}; - -/* Filter element of access list */ -struct filter { - /* For doubly linked list. */ - struct filter *next; - struct filter *prev; - - /* Filter type information. */ - enum filter_type type; - - /* Sequence number */ - int64_t seq; - - /* Cisco access-list */ - int cisco; - - union { - struct filter_cisco cfilter; - struct filter_zebra zfilter; - } u; -}; - -/* List of access_list. */ -struct access_list_list { - struct access_list *head; - struct access_list *tail; -}; - -/* Master structure of access_list. */ -struct access_master { - /* List of access_list which name is number. */ - struct access_list_list num; - - /* List of access_list which name is string. */ - struct access_list_list str; - - /* Hook function which is executed when new access_list is added. */ - void (*add_hook)(struct access_list *); - - /* Hook function which is executed when access_list is deleted. */ - void (*delete_hook)(struct access_list *); -}; - /* Static structure for mac access_list's master. */ static struct access_master access_master_mac = { {NULL, NULL}, @@ -129,7 +71,7 @@ static struct access_master *access_master_get(afi_t afi) } /* Allocate new filter structure. */ -static struct filter *filter_new(void) +struct filter *filter_new(void) { return XCALLOC(MTYPE_ACCESS_FILTER, sizeof(struct filter)); } @@ -210,7 +152,7 @@ static void access_list_free(struct access_list *access) } /* Delete access_list from access_master and free it. */ -static void access_list_delete(struct access_list *access) +void access_list_delete(struct access_list *access) { struct filter *filter; struct filter *next; @@ -356,7 +298,7 @@ struct access_list *access_list_lookup(afi_t afi, const char *name) /* Get access list from list of access_list. If there isn't matched access_list create new one and return it. */ -static struct access_list *access_list_get(afi_t afi, const char *name) +struct access_list *access_list_get(afi_t afi, const char *name) { struct access_list *access; @@ -406,7 +348,7 @@ void access_list_delete_hook(void (*func)(struct access_list *access)) } /* Calculate new sequential number. */ -static int64_t filter_new_seq_get(struct access_list *access) +int64_t filter_new_seq_get(struct access_list *access) { int64_t maxseq; int64_t newseq; @@ -436,22 +378,12 @@ static struct filter *filter_seq_check(struct access_list *access, return NULL; } -/* If access_list has no filter then return 1. */ -static bool access_list_empty(struct access_list *access) -{ - if (access->head == NULL && access->tail == NULL) - return true; - else - return false; -} - /* Delete filter from specified access_list. If there is hook function execute it. */ -static void access_list_filter_delete(struct access_list *access, - struct filter *filter) +void access_list_filter_delete(struct access_list *access, + struct filter *filter) { struct access_master *master; - struct filter *replace = filter; master = access->master; @@ -471,15 +403,11 @@ static void access_list_filter_delete(struct access_list *access, /* Run hook function. */ if (master->delete_hook) (*master->delete_hook)(access); - - /* If access_list becomes empty delete it from access_master. */ - if (access_list_empty(access) && !replace) - access_list_delete(access); } /* Add new filter to the end of specified access_list. */ -static void access_list_filter_add(struct access_list *access, - struct filter *filter) +void access_list_filter_add(struct access_list *access, + struct filter *filter) { struct filter *replace; struct filter *point; @@ -541,8 +469,8 @@ static void access_list_filter_add(struct access_list *access, host A single host address */ -static struct filter *filter_lookup_cisco(struct access_list *access, - struct filter *mnew) +struct filter *filter_lookup_cisco(struct access_list *access, + struct filter *mnew) { struct filter *mfilter; struct filter_cisco *filter; @@ -573,8 +501,8 @@ static struct filter *filter_lookup_cisco(struct access_list *access, return NULL; } -static struct filter *filter_lookup_zebra(struct access_list *access, - struct filter *mnew) +struct filter *filter_lookup_zebra(struct access_list *access, + struct filter *mnew) { struct filter *mfilter; struct filter_zebra *filter; @@ -594,1905 +522,6 @@ static struct filter *filter_lookup_zebra(struct access_list *access, return NULL; } -static int vty_access_list_remark_unset(struct vty *vty, afi_t afi, - const char *name) -{ - struct access_list *access; - - access = access_list_lookup(afi, name); - if (!access) { - vty_out(vty, "%% access-list %s doesn't exist\n", name); - return CMD_WARNING_CONFIG_FAILED; - } - - XFREE(MTYPE_TMP, access->remark); - - if (access->head == NULL && access->tail == NULL) - access_list_delete(access); - - return CMD_SUCCESS; -} - -static int filter_set_cisco(struct vty *vty, const char *name_str, - const char *seq, const char *type_str, - const char *addr_str, const char *addr_mask_str, - const char *mask_str, const char *mask_mask_str, - int extended, int set) -{ - int ret; - enum filter_type type = FILTER_DENY; - struct filter *mfilter; - struct filter_cisco *filter; - struct access_list *access; - struct in_addr addr; - struct in_addr addr_mask; - struct in_addr mask; - struct in_addr mask_mask; - int64_t seqnum = -1; - - if (seq) - seqnum = (int64_t)atol(seq); - - /* Check of filter type. */ - if (type_str) { - if (strncmp(type_str, "p", 1) == 0) - type = FILTER_PERMIT; - else if (strncmp(type_str, "d", 1) == 0) - type = FILTER_DENY; - else { - vty_out(vty, "%% filter type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - ret = inet_aton(addr_str, &addr); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = inet_aton(addr_mask_str, &addr_mask); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (extended) { - ret = inet_aton(mask_str, &mask); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = inet_aton(mask_mask_str, &mask_mask); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - mfilter = filter_new(); - mfilter->type = type; - mfilter->cisco = 1; - mfilter->seq = seqnum; - filter = &mfilter->u.cfilter; - filter->extended = extended; - filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr; - filter->addr_mask.s_addr = addr_mask.s_addr; - - if (extended) { - filter->mask.s_addr = mask.s_addr & ~mask_mask.s_addr; - filter->mask_mask.s_addr = mask_mask.s_addr; - } - - /* Install new filter to the access_list. */ - access = access_list_get(AFI_IP, name_str); - - if (set) { - if (filter_lookup_cisco(access, mfilter)) - filter_free(mfilter); - else - access_list_filter_add(access, mfilter); - } else { - struct filter *delete_filter; - - delete_filter = filter_lookup_cisco(access, mfilter); - if (delete_filter) - access_list_filter_delete(access, delete_filter); - - filter_free(mfilter); - } - - return CMD_SUCCESS; -} - -/* Standard access-list */ -DEFUN (access_list_standard, - access_list_standard_cmd, - "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n" - "Wildcard bits\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *address = NULL; - char *wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - address = argv[idx]->arg; - wildcard = argv[idx + 1]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - address, wildcard, NULL, NULL, 0, 1); -} - -DEFUN (access_list_standard_nomask, - access_list_standard_nomask_cmd, - "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *address = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - address = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - address, "0.0.0.0", NULL, NULL, 0, 1); -} - -DEFUN (access_list_standard_host, - access_list_standard_host_cmd, - "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> host A.B.C.D", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "A single host address\n" - "Address to match\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *address = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - address = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - address, "0.0.0.0", NULL, NULL, 0, 1); -} - -DEFUN (access_list_standard_any, - access_list_standard_any_cmd, - "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> any", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any source host\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 1); -} - -DEFUN (no_access_list_standard, - no_access_list_standard_cmd, - "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n" - "Wildcard bits\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *address = NULL; - char *wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - address = argv[idx]->arg; - wildcard = argv[idx + 1]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - address, wildcard, NULL, NULL, 0, 0); -} - -DEFUN (no_access_list_standard_nomask, - no_access_list_standard_nomask_cmd, - "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *address = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - address = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - address, "0.0.0.0", NULL, NULL, 0, 0); -} - -DEFUN (no_access_list_standard_host, - no_access_list_standard_host_cmd, - "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "A single host address\n" - "Address to match\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *address = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - address = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - address, "0.0.0.0", NULL, NULL, 0, 0); -} - -DEFUN (no_access_list_standard_any, - no_access_list_standard_any_cmd, - "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> any", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any source host\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 0); -} - -/* Extended access-list */ -DEFUN (access_list_extended, - access_list_extended_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - char *src_wildcard = NULL; - char *dst_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - src_wildcard = argv[idx + 1]->arg; - dst = argv[idx + 2]->arg; - dst_wildcard = argv[idx + 3]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - src_wildcard, dst, dst_wildcard, 1, 1); -} - -DEFUN (access_list_extended_mask_any, - access_list_extended_mask_any_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D any", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Any destination host\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *src_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - src_wildcard = argv[idx + 1]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - src_wildcard, "0.0.0.0", "255.255.255.255", 1, - 1); -} - -DEFUN (access_list_extended_any_mask, - access_list_extended_any_mask_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *dst = NULL; - char *dst_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - dst = argv[idx]->arg; - dst_wildcard = argv[idx + 1]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", dst, dst_wildcard, - 1, 1); -} - -DEFUN (access_list_extended_any_any, - access_list_extended_any_any_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any any", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Any destination host\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", "0.0.0.0", - "255.255.255.255", 1, 1); -} - -DEFUN (access_list_extended_mask_host, - access_list_extended_mask_host_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - char *src_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - src_wildcard = argv[idx + 1]->arg; - dst = argv[idx + 3]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - src_wildcard, dst, "0.0.0.0", 1, 1); -} - -DEFUN (access_list_extended_host_mask, - access_list_extended_host_mask_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - char *dst_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - dst = argv[idx + 1]->arg; - dst_wildcard = argv[idx + 2]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - "0.0.0.0", dst, dst_wildcard, 1, 1); -} - -DEFUN (access_list_extended_host_host, - access_list_extended_host_host_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D host A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - dst = argv[idx + 2]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - "0.0.0.0", dst, "0.0.0.0", 1, 1); -} - -DEFUN (access_list_extended_any_host, - access_list_extended_any_host_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any host A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *dst = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - dst = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", dst, "0.0.0.0", 1, - 1); -} - -DEFUN (access_list_extended_host_any, - access_list_extended_host_any_cmd, - "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D any", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Any destination host\n") -{ - int idx_acl = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - src = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 1); -} - -DEFUN (no_access_list_extended, - no_access_list_extended_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - char *src_wildcard = NULL; - char *dst_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - src_wildcard = argv[idx + 1]->arg; - dst = argv[idx + 2]->arg; - dst_wildcard = argv[idx + 3]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - src_wildcard, dst, dst_wildcard, 1, 0); -} - -DEFUN (no_access_list_extended_mask_any, - no_access_list_extended_mask_any_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D any", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Any destination host\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *src_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - src_wildcard = argv[idx + 1]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - src_wildcard, "0.0.0.0", "255.255.255.255", 1, - 0); -} - -DEFUN (no_access_list_extended_any_mask, - no_access_list_extended_any_mask_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *dst = NULL; - char *dst_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - dst = argv[idx]->arg; - dst_wildcard = argv[idx + 1]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", dst, dst_wildcard, - 1, 0); -} - -DEFUN (no_access_list_extended_any_any, - no_access_list_extended_any_any_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any any", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Any destination host\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", "0.0.0.0", - "255.255.255.255", 1, 0); -} - -DEFUN (no_access_list_extended_mask_host, - no_access_list_extended_mask_host_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - char *src_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - src_wildcard = argv[idx + 1]->arg; - dst = argv[idx + 3]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - src_wildcard, dst, "0.0.0.0", 1, 0); -} - -DEFUN (no_access_list_extended_host_mask, - no_access_list_extended_host_mask_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - char *dst_wildcard = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - dst = argv[idx + 1]->arg; - dst_wildcard = argv[idx + 2]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - "0.0.0.0", dst, dst_wildcard, 1, 0); -} - -DEFUN (no_access_list_extended_host_host, - no_access_list_extended_host_host_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - char *dst = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) { - src = argv[idx]->arg; - dst = argv[idx + 2]->arg; - } - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - "0.0.0.0", dst, "0.0.0.0", 1, 0); -} - -DEFUN (no_access_list_extended_any_host, - no_access_list_extended_any_host_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *dst = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - dst = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, - "0.0.0.0", "255.255.255.255", dst, "0.0.0.0", 1, - 0); -} - -DEFUN (no_access_list_extended_host_any, - no_access_list_extended_host_any_cmd, - "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D any", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Any destination host\n") -{ - int idx_acl = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *src = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D", &idx); - if (idx) - src = argv[idx]->arg; - - return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, - "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 0); -} - -static int filter_set_zebra(struct vty *vty, const char *name_str, - const char *seq, const char *type_str, afi_t afi, - const char *prefix_str, int exact, int set) -{ - int ret; - enum filter_type type = FILTER_DENY; - struct filter *mfilter; - struct filter_zebra *filter; - struct access_list *access; - struct prefix p; - int64_t seqnum = -1; - - if (strlen(name_str) > ACL_NAMSIZ) { - vty_out(vty, - "%% ACL name %s is invalid: length exceeds " - "%d characters\n", - name_str, ACL_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - if (seq) - seqnum = (int64_t)atol(seq); - - /* Check of filter type. */ - if (type_str) { - if (strncmp(type_str, "p", 1) == 0) - type = FILTER_PERMIT; - else if (strncmp(type_str, "d", 1) == 0) - type = FILTER_DENY; - else { - vty_out(vty, "filter type must be [permit|deny]\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - /* Check string format of prefix and prefixlen. */ - if (afi == AFI_IP) { - ret = str2prefix_ipv4(prefix_str, (struct prefix_ipv4 *)&p); - if (ret <= 0) { - vty_out(vty, - "IP address prefix/prefixlen is malformed\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } else if (afi == AFI_IP6) { - ret = str2prefix_ipv6(prefix_str, (struct prefix_ipv6 *)&p); - if (ret <= 0) { - vty_out(vty, - "IPv6 address prefix/prefixlen is malformed\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } else if (afi == AFI_L2VPN) { - ret = str2prefix_eth(prefix_str, (struct prefix_eth *)&p); - if (ret <= 0) { - vty_out(vty, "MAC address is malformed\n"); - return CMD_WARNING; - } - } else - return CMD_WARNING_CONFIG_FAILED; - - mfilter = filter_new(); - mfilter->type = type; - mfilter->seq = seqnum; - filter = &mfilter->u.zfilter; - prefix_copy(&filter->prefix, &p); - - /* "exact-match" */ - if (exact) - filter->exact = 1; - - /* Install new filter to the access_list. */ - access = access_list_get(afi, name_str); - - if (set) { - if (filter_lookup_zebra(access, mfilter)) - filter_free(mfilter); - else - access_list_filter_add(access, mfilter); - } else { - struct filter *delete_filter; - delete_filter = filter_lookup_zebra(access, mfilter); - if (delete_filter) - access_list_filter_delete(access, delete_filter); - - filter_free(mfilter); - } - - return CMD_SUCCESS; -} - -DEFUN (mac_access_list, - mac_access_list_cmd, - "mac access-list WORD [seq (1-4294967295)] <deny|permit> X:X:X:X:X:X", - "Add a mac access-list\n" - "Add an access list entry\n" - "MAC zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *mac = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "X:X:X:X:X:X", &idx); - if (idx) - mac = argv[idx]->arg; - assert(mac); - - return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, - mac, 0, 1); -} - -DEFUN (no_mac_access_list, - no_mac_access_list_cmd, - "no mac access-list WORD [seq (1-4294967295)] <deny|permit> X:X:X:X:X:X", - NO_STR - "Remove a mac access-list\n" - "Remove an access list entry\n" - "MAC zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *mac = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "X:X:X:X:X:X", &idx); - if (idx) - mac = argv[idx]->arg; - assert(mac); - - return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, - mac, 0, 0); -} - -DEFUN (mac_access_list_any, - mac_access_list_any_cmd, - "mac access-list WORD [seq (1-4294967295)] <deny|permit> any", - "Add a mac access-list\n" - "Add an access list entry\n" - "MAC zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, - "00:00:00:00:00:00", 0, 1); -} - -DEFUN (no_mac_access_list_any, - no_mac_access_list_any_cmd, - "no mac access-list WORD [seq (1-4294967295)] <deny|permit> any", - NO_STR - "Remove a mac access-list\n" - "Remove an access list entry\n" - "MAC zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, - "00:00:00:00:00:00", 0, 0); -} - -DEFUN (access_list_exact, - access_list_exact_cmd, - "access-list WORD [seq (1-4294967295)] <deny|permit> A.B.C.D/M [exact-match]", - "Add an access list entry\n" - "IP zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *prefix = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D/M", &idx); - if (idx) - prefix = argv[idx]->arg; - assert(prefix); - - idx = 0; - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[1]->arg, seq, permit_deny, - AFI_IP, prefix, exact, 1); -} - -DEFUN (access_list_any, - access_list_any_cmd, - "access-list WORD [seq (1-4294967295)] <deny|permit> any", - "Add an access list entry\n" - "IP zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n") -{ - int idx_word = 1; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, - AFI_IP, "0.0.0.0/0", 0, 1); -} - -DEFUN (no_access_list_exact, - no_access_list_exact_cmd, - "no access-list WORD [seq (1-4294967295)] <deny|permit> A.B.C.D/M [exact-match]", - NO_STR - "Add an access list entry\n" - "IP zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - char *seq = NULL; - char *permit_deny = NULL; - char *prefix = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "A.B.C.D/M", &idx); - if (idx) - prefix = argv[idx]->arg; - assert(prefix); - - idx = 0; - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, - AFI_IP, prefix, exact, 0); -} - -DEFUN (no_access_list_any, - no_access_list_any_cmd, - "no access-list WORD [seq (1-4294967295)] <deny|permit> any", - NO_STR - "Add an access list entry\n" - "IP zebra access-list name\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n") -{ - int idx_word = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, - AFI_IP, "0.0.0.0/0", 0, 0); -} - -DEFUN (no_access_list_all, - no_access_list_all_cmd, - "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list name\n") -{ - int idx_acl = 2; - struct access_list *access; - struct access_master *master; - - /* Looking up access_list. */ - access = access_list_lookup(AFI_IP, argv[idx_acl]->arg); - if (access == NULL) { - vty_out(vty, "%% access-list %s doesn't exist\n", - argv[idx_acl]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - - master = access->master; - - route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); - /* Run hook function. */ - if (master->delete_hook) - (*master->delete_hook)(access); - - /* Delete all filter from access-list. */ - access_list_delete(access); - - return CMD_SUCCESS; -} - -DEFUN (access_list_remark, - access_list_remark_cmd, - "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...", - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - int idx_acl = 1; - int idx_remark = 3; - struct access_list *access; - - access = access_list_get(AFI_IP, argv[idx_acl]->arg); - - if (access->remark) { - XFREE(MTYPE_TMP, access->remark); - access->remark = NULL; - } - access->remark = argv_concat(argv, argc, idx_remark); - - return CMD_SUCCESS; -} - -DEFUN (no_access_list_remark, - no_access_list_remark_cmd, - "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" - "Access list entry comment\n") -{ - int idx_acl = 2; - return vty_access_list_remark_unset(vty, AFI_IP, argv[idx_acl]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_access_list_remark_comment, - no_access_list_remark_comment_cmd, - "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - return no_access_list_remark(self, vty, argc, argv); -} - -DEFUN (ipv6_access_list_exact, - ipv6_access_list_exact_cmd, - "ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> X:X::X:X/M [exact-match]", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "IPv6 prefix\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - int idx_word = 2; - char *seq = NULL; - char *permit_deny = NULL; - char *prefix = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "X:X::X:X/M", &idx); - if (idx) - prefix = argv[idx]->arg; - - idx = 0; - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - assert(prefix); - return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, - AFI_IP6, prefix, exact, 1); -} - -DEFUN (ipv6_access_list_any, - ipv6_access_list_any_cmd, - "ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> any", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefixi to match\n") -{ - int idx_word = 2; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, - AFI_IP6, "::/0", 0, 1); -} - -DEFUN (no_ipv6_access_list_exact, - no_ipv6_access_list_exact_cmd, - "no ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> X:X::X:X/M [exact-match]", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 3ffe:506::/32\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - int idx_word = 3; - char *seq = NULL; - char *permit_deny = NULL; - char *prefix = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "X:X::X:X/M", &idx); - if (idx) - prefix = argv[idx]->arg; - assert(prefix); - - idx = 0; - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, - AFI_IP6, prefix, exact, 0); -} - -DEFUN (no_ipv6_access_list_any, - no_ipv6_access_list_any_cmd, - "no ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> any", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefixi to match\n") -{ - int idx_word = 3; - int idx = 0; - char *seq = NULL; - char *permit_deny = NULL; - - argv_find(argv, argc, "(1-4294967295)", &idx); - if (idx) - seq = argv[idx]->arg; - - idx = 0; - argv_find(argv, argc, "permit", &idx); - argv_find(argv, argc, "deny", &idx); - if (idx) - permit_deny = argv[idx]->arg; - - return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, - AFI_IP6, "::/0", 0, 0); -} - - -DEFUN (no_ipv6_access_list_all, - no_ipv6_access_list_all_cmd, - "no ipv6 access-list WORD", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n") -{ - int idx_word = 3; - struct access_list *access; - struct access_master *master; - - /* Looking up access_list. */ - access = access_list_lookup(AFI_IP6, argv[idx_word]->arg); - if (access == NULL) { - vty_out(vty, "%% access-list %s doesn't exist\n", - argv[idx_word]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - - master = access->master; - - route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); - /* Run hook function. */ - if (master->delete_hook) - (*master->delete_hook)(access); - - /* Delete all filter from access-list. */ - access_list_delete(access); - - return CMD_SUCCESS; -} - -DEFUN (ipv6_access_list_remark, - ipv6_access_list_remark_cmd, - "ipv6 access-list WORD remark LINE...", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - int idx_word = 2; - int idx_line = 4; - struct access_list *access; - - access = access_list_get(AFI_IP6, argv[idx_word]->arg); - - if (access->remark) { - XFREE(MTYPE_TMP, access->remark); - access->remark = NULL; - } - access->remark = argv_concat(argv, argc, idx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_access_list_remark, - no_ipv6_access_list_remark_cmd, - "no ipv6 access-list WORD remark", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Access list entry comment\n") -{ - int idx_word = 3; - return vty_access_list_remark_unset(vty, AFI_IP6, argv[idx_word]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_ipv6_access_list_remark_comment, - no_ipv6_access_list_remark_comment_cmd, - "no ipv6 access-list WORD remark LINE...", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - return no_ipv6_access_list_remark(self, vty, argc, argv); -} - static void config_write_access_zebra(struct vty *, struct filter *); static void config_write_access_cisco(struct vty *, struct filter *); @@ -2747,86 +776,12 @@ static void config_write_access_zebra(struct vty *vty, struct filter *mfilter) vty_out(vty, "\n"); } -static int config_write_access(struct vty *vty, afi_t afi) -{ - struct access_list *access; - struct access_master *master; - struct filter *mfilter; - int write = 0; - - master = access_master_get(afi); - if (master == NULL) - return 0; - - for (access = master->num.head; access; access = access->next) { - if (access->remark) { - vty_out(vty, "%saccess-list %s remark %s\n", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, access->remark); - write++; - } - - for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - vty_out(vty, "%saccess-list %s seq %" PRId64 " %s", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, mfilter->seq, - filter_type_str(mfilter)); - - if (mfilter->cisco) - config_write_access_cisco(vty, mfilter); - else - config_write_access_zebra(vty, mfilter); - - write++; - } - } - - for (access = master->str.head; access; access = access->next) { - if (access->remark) { - vty_out(vty, "%saccess-list %s remark %s\n", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, access->remark); - write++; - } - - for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - vty_out(vty, "%saccess-list %s seq %" PRId64 " %s", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, mfilter->seq, - filter_type_str(mfilter)); - - if (mfilter->cisco) - config_write_access_cisco(vty, mfilter); - else - config_write_access_zebra(vty, mfilter); - - write++; - } - } - return write; -} - -static int config_write_access_mac(struct vty *vty); static struct cmd_node access_mac_node = { .name = "MAC access list", .node = ACCESS_MAC_NODE, .prompt = "", - .config_write = config_write_access_mac, }; -static int config_write_access_mac(struct vty *vty) -{ - return config_write_access(vty, AFI_L2VPN); -} - static void access_list_reset_mac(void) { struct access_list *access; @@ -2860,26 +815,29 @@ static void access_list_init_mac(void) install_element(ENABLE_NODE, &show_mac_access_list_cmd); install_element(ENABLE_NODE, &show_mac_access_list_name_cmd); - - /* Zebra access-list */ - install_element(CONFIG_NODE, &mac_access_list_cmd); - install_element(CONFIG_NODE, &no_mac_access_list_cmd); - install_element(CONFIG_NODE, &mac_access_list_any_cmd); - install_element(CONFIG_NODE, &no_mac_access_list_any_cmd); } /* Access-list node. */ -static int config_write_access_ipv4(struct vty *vty); +static int config_write_access(struct vty *vty); static struct cmd_node access_node = { .name = "ipv4 access list", .node = ACCESS_NODE, .prompt = "", - .config_write = config_write_access_ipv4, + .config_write = config_write_access, }; -static int config_write_access_ipv4(struct vty *vty) +static int config_write_access(struct vty *vty) { - return config_write_access(vty, AFI_IP); + struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, "/frr-filter:lib"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; } static void access_list_reset_ipv4(void) @@ -2915,62 +873,14 @@ static void access_list_init_ipv4(void) install_element(ENABLE_NODE, &show_ip_access_list_cmd); install_element(ENABLE_NODE, &show_ip_access_list_name_cmd); - - /* Zebra access-list */ - install_element(CONFIG_NODE, &access_list_exact_cmd); - install_element(CONFIG_NODE, &access_list_any_cmd); - install_element(CONFIG_NODE, &no_access_list_exact_cmd); - install_element(CONFIG_NODE, &no_access_list_any_cmd); - - /* Standard access-list */ - install_element(CONFIG_NODE, &access_list_standard_cmd); - install_element(CONFIG_NODE, &access_list_standard_nomask_cmd); - install_element(CONFIG_NODE, &access_list_standard_host_cmd); - install_element(CONFIG_NODE, &access_list_standard_any_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_nomask_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_host_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_any_cmd); - - /* Extended access-list */ - install_element(CONFIG_NODE, &access_list_extended_cmd); - install_element(CONFIG_NODE, &access_list_extended_any_mask_cmd); - install_element(CONFIG_NODE, &access_list_extended_mask_any_cmd); - install_element(CONFIG_NODE, &access_list_extended_any_any_cmd); - install_element(CONFIG_NODE, &access_list_extended_host_mask_cmd); - install_element(CONFIG_NODE, &access_list_extended_mask_host_cmd); - install_element(CONFIG_NODE, &access_list_extended_host_host_cmd); - install_element(CONFIG_NODE, &access_list_extended_any_host_cmd); - install_element(CONFIG_NODE, &access_list_extended_host_any_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_any_mask_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_mask_any_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_any_any_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_host_mask_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_mask_host_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_host_host_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_any_host_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_host_any_cmd); - - install_element(CONFIG_NODE, &access_list_remark_cmd); - install_element(CONFIG_NODE, &no_access_list_all_cmd); - install_element(CONFIG_NODE, &no_access_list_remark_cmd); - install_element(CONFIG_NODE, &no_access_list_remark_comment_cmd); } -static int config_write_access_ipv6(struct vty *vty); static struct cmd_node access_ipv6_node = { .name = "ipv6 access list", .node = ACCESS_IPV6_NODE, .prompt = "", - .config_write = config_write_access_ipv6, }; -static int config_write_access_ipv6(struct vty *vty) -{ - return config_write_access(vty, AFI_IP6); -} - static void access_list_reset_ipv6(void) { struct access_list *access; @@ -3003,16 +913,6 @@ static void access_list_init_ipv6(void) install_element(ENABLE_NODE, &show_ipv6_access_list_cmd); install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd); - - install_element(CONFIG_NODE, &ipv6_access_list_exact_cmd); - install_element(CONFIG_NODE, &ipv6_access_list_any_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_exact_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_any_cmd); - - install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd); - install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_remark_comment_cmd); } void access_list_init(void) @@ -3020,6 +920,8 @@ void access_list_init(void) access_list_init_ipv4(); access_list_init_ipv6(); access_list_init_mac(); + + filter_cli_init(); } void access_list_reset(void) diff --git a/lib/filter.h b/lib/filter.h index 3dd2eaaf15..76e992bf8e 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -23,6 +23,7 @@ #define _ZEBRA_FILTER_H #include "if.h" +#include "prefix.h" #ifdef __cplusplus extern "C" { @@ -41,6 +42,50 @@ enum filter_type { FILTER_DENY, FILTER_PERMIT, FILTER_DYNAMIC }; enum access_type { ACCESS_TYPE_STRING, ACCESS_TYPE_NUMBER }; +struct filter_cisco { + /* Cisco access-list */ + int extended; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; +}; + +struct filter_zebra { + /* If this filter is "exact" match then this flag is set. */ + int exact; + + /* Prefix information. */ + struct prefix prefix; +}; + +/* Forward declaration of access-list struct. */ +struct access_list; + +/* Filter element of access list */ +struct filter { + /* For doubly linked list. */ + struct filter *next; + struct filter *prev; + + /* Parent access-list pointer. */ + struct access_list *acl; + + /* Filter type information. */ + enum filter_type type; + + /* Sequence number */ + int64_t seq; + + /* Cisco access-list */ + int cisco; + + union { + struct filter_cisco cfilter; + struct filter_zebra zfilter; + } u; +}; + /* Access list */ struct access_list { char *name; @@ -57,6 +102,28 @@ struct access_list { struct filter *tail; }; +/* List of access_list. */ +struct access_list_list { + struct access_list *head; + struct access_list *tail; +}; + +/* Master structure of access_list. */ +struct access_master { + /* List of access_list which name is number. */ + struct access_list_list num; + + /* List of access_list which name is string. */ + struct access_list_list str; + + /* Hook function which is executed when new access_list is added. */ + void (*add_hook)(struct access_list *); + + /* Hook function which is executed when access_list is deleted. */ + void (*delete_hook)(struct access_list *); +}; + + /* Prototypes for access-list. */ extern void access_list_init(void); extern void access_list_reset(void); @@ -66,6 +133,59 @@ extern struct access_list *access_list_lookup(afi_t, const char *); extern enum filter_type access_list_apply(struct access_list *access, const void *object); +struct access_list *access_list_get(afi_t afi, const char *name); +void access_list_delete(struct access_list *access); +struct filter *filter_new(void); +void access_list_filter_add(struct access_list *access, + struct filter *filter); +void access_list_filter_delete(struct access_list *access, + struct filter *filter); +int64_t filter_new_seq_get(struct access_list *access); +struct filter *filter_lookup_cisco(struct access_list *access, + struct filter *mnew); +struct filter *filter_lookup_zebra(struct access_list *access, + struct filter *mnew); + +extern const struct frr_yang_module_info frr_filter_info; + + +/* filter_nb.c */ +enum yang_access_list_type { + YALT_IPV4 = 0, + YALT_IPV6 = 1, + YALT_MAC = 2, +}; + +enum yang_prefix_list_type { + YPLT_IPV4 = 0, + YPLT_IPV6 = 1, +}; + +enum yang_prefix_list_action { + YPLA_DENY = 0, + YPLA_PERMIT = 1, +}; + +/* filter_cli.c */ +struct lyd_node; +struct vty; + +extern void access_list_legacy_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void access_list_legacy_remark_show(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void access_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void prefix_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); + +void filter_cli_init(void); + #ifdef __cplusplus } #endif diff --git a/lib/filter_cli.c b/lib/filter_cli.c new file mode 100644 index 0000000000..0091d8b03f --- /dev/null +++ b/lib/filter_cli.c @@ -0,0 +1,1693 @@ +/* + * FRR filter CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include "northbound.h" +#include "prefix.h" +#include "zebra.h" + +#include "lib/command.h" +#include "lib/filter.h" +#include "lib/northbound_cli.h" +#include "lib/plist.h" +#include "lib/plist_int.h" + +#ifndef VTYSH_EXTRACT_PL +#include "lib/filter_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +#define ACCESS_LIST_STR "Access list entry\n" +#define ACCESS_LIST_LEG_STR "IP standard access list\n" +#define ACCESS_LIST_LEG_EXT_STR "IP standard access list (expanded range)\n" +#define ACCESS_LIST_ELEG_STR "IP extended access list\n" +#define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n" +#define ACCESS_LIST_XLEG_STR \ + ACCESS_LIST_LEG_STR \ + ACCESS_LIST_LEG_EXT_STR \ + ACCESS_LIST_ELEG_STR \ + ACCESS_LIST_ELEG_EXT_STR +#define ACCESS_LIST_ZEBRA_STR "Access list entry\n" +#define ACCESS_LIST_SEQ_STR \ + "Sequence number of an entry\n" \ + "Sequence number\n" +#define ACCESS_LIST_ACTION_STR \ + "Specify packets to reject\n" \ + "Specify packets to forward\n" +#define ACCESS_LIST_REMARK_STR "Access list entry comment\n" +#define ACCESS_LIST_REMARK_LINE_STR "Comment up to 100 characters\n" + +#define PREFIX_LIST_NAME_STR "Prefix list entry name\n" + +/* + * Helper function to locate filter data structures for Cisco-style ACLs. + */ +static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action, + const char *src, const char *src_mask, + const char *dst, const char *dst_mask) +{ + struct filter_cisco *fc; + struct filter f, *fn; + + memset(&f, 0, sizeof(f)); + memset(&fc, 0, sizeof(fc)); + f.cisco = 1; + if (strcmp(action, "permit") == 0) + f.type = FILTER_PERMIT; + else + f.type = FILTER_DENY; + + fc = &f.u.cfilter; + inet_pton(AF_INET, src, &fc->addr); + inet_pton(AF_INET, src_mask, &fc->addr_mask); + fc->addr.s_addr &= ~fc->addr_mask.s_addr; + if (dst != NULL) { + fc->extended = 1; + inet_pton(AF_INET, dst, &fc->mask); + inet_pton(AF_INET, dst_mask, &fc->mask_mask); + fc->mask.s_addr &= ~fc->mask_mask.s_addr; + } + + fn = filter_lookup_cisco(acl, &f); + if (fn == NULL) + return -1; + + return fn->seq; +} + +/* + * Helper function to locate filter data structures for zebra-style ACLs. + */ +static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action, + const struct prefix *p, bool exact) +{ + struct filter_zebra *fz; + struct filter f, *fn; + + memset(&f, 0, sizeof(f)); + memset(&fz, 0, sizeof(fz)); + if (strcmp(action, "permit") == 0) + f.type = FILTER_PERMIT; + else + f.type = FILTER_DENY; + + fz = &f.u.zfilter; + fz->prefix = *p; + fz->exact = exact; + + fn = filter_lookup_zebra(acl, &f); + if (fn == NULL) + return -1; + + return fn->seq; +} + +/* + * Helper function to concatenate address with mask in Cisco style. + */ +static void concat_addr_mask_v4(const char *addr, const char *mask, char *dst, + size_t dstlen) +{ + struct in_addr ia; + int plen; + + assert(inet_pton(AF_INET, mask, &ia) == 1); + plen = ip_masklen(ia); + snprintf(dst, dstlen, "%s/%d", addr, plen); +} + +/* + * Helper function to generate a sequence number for legacy commands. + */ +static int acl_get_seq_cb(const struct lyd_node *dnode, void *arg) +{ + int64_t *seq = arg; + int64_t cur_seq = yang_dnode_get_uint32(dnode, "sequence"); + + if (cur_seq > *seq) + *seq = cur_seq; + + return YANG_ITER_CONTINUE; +} + +/** + * Helper function that iterates over the XPath `xpath` on the candidate + * configuration in `vty->candidate_config`. + * + * \param[in] vty shell context with the candidate configuration. + * \param[in] xpath the XPath to look for the sequence leaf. + * \returns next unused sequence number. + */ +static long acl_get_seq(struct vty *vty, const char *xpath) +{ + int64_t seq = 0; + + yang_dnode_iterate(acl_get_seq_cb, &seq, vty->candidate_config->dnode, + "%s/entry", xpath); + + return seq + 5; +} + +/* + * Cisco (legacy) access lists. + */ +DEFPY( + access_list_std, access_list_std_cmd, + "access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>", + ACCESS_LIST_STR + ACCESS_LIST_LEG_STR + ACCESS_LIST_LEG_EXT_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "A single host address\n" + "Address to match\n" + "Address to match\n" + "Wildcard bits\n" + "Any source host\n") +{ + int64_t sseq; + char ipmask[64]; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']", number_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (host_str != NULL && mask_str == NULL) { + nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, host_str); + } else if (host_str != NULL && mask_str != NULL) { + concat_addr_mask_v4(host_str, mask_str, ipmask, sizeof(ipmask)); + nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_access_list_std, no_access_list_std_cmd, + "no access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_LEG_STR + ACCESS_LIST_LEG_EXT_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "A single host address\n" + "Address to match\n" + "Address to match\n" + "Wildcard bits\n" + "Any source host\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']/entry[sequence='%s']", + number_str, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']", number_str); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (host_str != NULL) + sseq = acl_cisco_get_seq(acl, action, host_str, + mask_str ? mask_str : "0.0.0.0", NULL, + NULL); + else + sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", + "255.255.255.255", NULL, NULL); + if (sseq == -1) + return CMD_WARNING; + + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + access_list_ext, access_list_ext_cmd, + "access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + ACCESS_LIST_STR + ACCESS_LIST_ELEG_STR + ACCESS_LIST_ELEG_EXT_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv4 address\n" + "Source address to match\n" + "Source address mask to apply\n" + "Single source host\n" + "Source address to match\n" + "Any source host\n" + "Destination address to match\n" + "Destination address mask to apply\n" + "Single destination host\n" + "Destination address to match\n" + "Any destination host\n") +{ + int64_t sseq; + char ipmask[64]; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']", number_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (src_str != NULL && src_mask_str == NULL) { + nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, src_str); + } else if (src_str != NULL && src_mask_str != NULL) { + concat_addr_mask_v4(src_str, src_mask_str, ipmask, + sizeof(ipmask)); + nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + if (dst_str != NULL && dst_mask_str == NULL) { + nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY, + src_str); + } else if (dst_str != NULL && dst_mask_str != NULL) { + concat_addr_mask_v4(dst_str, dst_mask_str, ipmask, + sizeof(ipmask)); + nb_cli_enqueue_change(vty, "./destination-network", + NB_OP_MODIFY, ipmask); + } else { + nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE, + NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_access_list_ext, no_access_list_ext_cmd, + "no access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ELEG_STR + ACCESS_LIST_ELEG_EXT_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any Internet Protocol\n" + "Source address to match\n" + "Source address mask to apply\n" + "Single source host\n" + "Source address to match\n" + "Any source host\n" + "Destination address to match\n" + "Destination address mask to apply\n" + "Single destination host\n" + "Destination address to match\n" + "Any destination host\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']/entry[sequence='%s']", + number_str, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']", number_str); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (src_str != NULL) { + if (dst_str != NULL) + sseq = acl_cisco_get_seq( + acl, action, src_str, + src_mask_str ? src_mask_str : "0.0.0.0", + dst_str, + dst_mask_str ? dst_mask_str : "0.0.0.0"); + else + sseq = acl_cisco_get_seq(acl, action, src_str, + src_mask_str ? src_mask_str + : "0.0.0.0", + "0.0.0.0", "255.255.255.255"); + } else { + if (dst_str != NULL) + sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", + "255.255.255.255", dst_str, + dst_mask_str ? dst_mask_str + : "0.0.0.0"); + else + sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", + "255.255.255.255", "0.0.0.0", + "255.255.255.255"); + } + if (sseq == -1) + return CMD_WARNING; + + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_access_list_legacy, no_access_list_legacy_cmd, + "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_XLEG_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']", number_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void access_list_legacy_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + uint16_t number = yang_dnode_get_uint16(dnode, "../number"); + bool extended; + struct prefix p; + struct in_addr mask; + + vty_out(vty, "access-list %d seq %s %s", number, + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + extended = (number >= 100 && number <= 199) + || (number >= 2000 && number <= 2699); + if (extended) + vty_out(vty, " ip"); + + if (yang_dnode_exists(dnode, "./network")) { + yang_dnode_get_prefix(&p, dnode, "./network"); + masklen2ip(p.prefixlen, &mask); + vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask); + } else if (yang_dnode_exists(dnode, "./host")) { + if (extended) + vty_out(vty, " host"); + + vty_out(vty, " %s", yang_dnode_get_string(dnode, "./host")); + } else if (yang_dnode_exists(dnode, "./any")) + vty_out(vty, " any"); + + if (extended) { + if (yang_dnode_exists(dnode, "./destination-network")) { + yang_dnode_get_prefix(&p, dnode, + "./destination-network"); + masklen2ip(p.prefixlen, &mask); + vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask); + } else if (yang_dnode_exists(dnode, "./destination-host")) + vty_out(vty, " host %s", + yang_dnode_get_string(dnode, + "./destination-host")); + else if (yang_dnode_exists(dnode, "./destination-any")) + vty_out(vty, " any"); + } + + vty_out(vty, "\n"); +} + +DEFPY( + access_list_legacy_remark, access_list_legacy_remark_cmd, + "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark LINE...", + ACCESS_LIST_STR + ACCESS_LIST_XLEG_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']", number_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 3); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY( + no_access_list_legacy_remark, no_access_list_legacy_remark_cmd, + "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_XLEG_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list-legacy[number='%s']/remark", + number_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_access_list_legacy_remark, no_access_list_legacy_remark_line_cmd, + "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark LINE...", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_XLEG_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +void access_list_legacy_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, "access-list %s remark %s\n", + yang_dnode_get_string(dnode, "../number"), + yang_dnode_get_string(dnode, NULL)); +} + +/* + * Zebra access lists. + */ +DEFPY( + access_list, access_list_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n" + "Match any IPv4\n") +{ + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY, + prefix_str); + nb_cli_enqueue_change(vty, "./ipv4-exact-match", NB_OP_MODIFY, + exact ? "true" : "false"); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_access_list, no_access_list_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n" + "Match any IPv4\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + struct prefix pany; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (prefix == NULL) { + memset(&pany, 0, sizeof(pany)); + pany.family = AF_INET; + sseq = acl_zebra_get_seq(acl, action, &pany, exact); + } else + sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, + exact); + if (sseq == -1) + return CMD_WARNING; + + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_access_list_all, no_access_list_all_cmd, + "no access-list WORD$name", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + access_list_remark, access_list_remark_cmd, + "access-list WORD$name remark LINE...", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 3); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY( + no_access_list_remark, no_access_list_remark_cmd, + "no access-list WORD$name remark", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_access_list_remark, no_access_list_remark_line_cmd, + "no access-list WORD$name remark LINE...", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY( + ipv6_access_list, ipv6_access_list_cmd, + "ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>", + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv6 prefix\n" + "Exact match of the prefixes\n" + "Match any IPv6\n") +{ + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY, + prefix_str); + nb_cli_enqueue_change(vty, "./ipv6-exact-match", NB_OP_MODIFY, + exact ? "true" : "false"); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_ipv6_access_list, no_ipv6_access_list_cmd, + "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv6 prefix\n" + "Exact match of the prefixes\n" + "Match any IPv6\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + struct prefix pany; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (prefix == NULL) { + memset(&pany, 0, sizeof(pany)); + pany.family = AF_INET6; + sseq = acl_zebra_get_seq(acl, action, &pany, exact); + } else + sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, + exact); + if (sseq == -1) + return CMD_WARNING; + + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_ipv6_access_list_all, no_ipv6_access_list_all_cmd, + "no ipv6 access-list WORD$name", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + ipv6_access_list_remark, ipv6_access_list_remark_cmd, + "ipv6 access-list WORD$name remark LINE...", + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY( + no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd, + "no ipv6 access-list WORD$name remark", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_ipv6_access_list_remark, no_ipv6_access_list_remark_line_cmd, + "no ipv6 access-list WORD$name remark LINE...", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY( + mac_access_list, mac_access_list_cmd, + "mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "MAC address\n" + "Match any MAC address\n") +{ + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (mac_str != NULL) { + nb_cli_enqueue_change(vty, "./mac", NB_OP_MODIFY, mac_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_mac_access_list, no_mac_access_list_cmd, + "no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$prefix|any>", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "MAC address\n" + "Match any MAC address\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + struct prefix pany; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (prefix == NULL) { + memset(&pany, 0, sizeof(pany)); + pany.family = AF_ETHERNET; + sseq = acl_zebra_get_seq(acl, action, &pany, false); + } else + sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, + false); + if (sseq == -1) + return CMD_WARNING; + + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_mac_access_list_all, no_mac_access_list_all_cmd, + "no mac access-list WORD$name", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + mac_access_list_remark, mac_access_list_remark_cmd, + "mac access-list WORD$name remark LINE...", + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY( + no_mac_access_list_remark, no_mac_access_list_remark_cmd, + "no mac access-list WORD$name remark", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_mac_access_list_remark, no_mac_access_list_remark_line_cmd, + "no mac access-list WORD$name remark LINE...", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +void access_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + struct prefix p; + bool is_any; + bool is_exact = false; + char macstr[PREFIX2STR_BUFFER]; + + is_any = yang_dnode_exists(dnode, "./any"); + switch (type) { + case YALT_IPV4: + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + is_exact = yang_dnode_get_bool(dnode, "./ipv4-exact-match"); + break; + case YALT_IPV6: /* ipv6 */ + vty_out(vty, "ipv6 "); + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix"); + is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match"); + break; + case YALT_MAC: /* mac */ + vty_out(vty, "mac "); + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./mac"); + break; + } + + vty_out(vty, "access-list %s seq %s %s", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + if (!is_any) { + /* If type is MAC don't show '/mask'. */ + if (type == 2 /* mac */) { + prefix_mac2str(&p.u.prefix_eth, macstr, sizeof(macstr)); + vty_out(vty, " %s", macstr); + } else + vty_out(vty, " %pFX", &p); + } else + vty_out(vty, " any"); + + if (is_exact) + vty_out(vty, " exact-match"); + + vty_out(vty, "\n"); +} + +void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + + switch (type) { + case YALT_IPV4: + break; + case YALT_IPV6: + vty_out(vty, "ipv6 "); + break; + case YALT_MAC: + vty_out(vty, "mac "); + break; + } + + vty_out(vty, "access-list %s remark %s\n", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, NULL)); +} + +/* + * Prefix lists. + */ + +/** + * Remove main data structure prefix list if there are no more entries or + * remark. This fixes compatibility with old CLI and tests. + */ +static int plist_remove_if_empty(struct vty *vty, const char *iptype, + const char *name) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark", + iptype, name); + /* List is not empty if there is a remark, check that: */ + if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) + return CMD_SUCCESS; + + /* Check if we have any entries: */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, + name); + /* + * NOTE: if the list is empty it will return the first sequence + * number: 5. + */ + if (acl_get_seq(vty, xpath) != 5) + return CMD_SUCCESS; + + /* Nobody is using this list, lets remove it. */ + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static int plist_remove(struct vty *vty, const char *iptype, const char *name, + const char *seq, const char *action, struct prefix *p, + long ge, long le) +{ + struct prefix_list_entry *pentry; + enum prefix_list_type plt; + struct prefix_list *pl; + struct lyd_node *dnode; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + int rv; + + /* If the user provided sequence number, then just go for it. */ + if (seq != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']", + iptype, name, seq); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, iptype, name); + + return rv; + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, + name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING; + + /* Use access-list data structure to fetch sequence. */ + assert(action != NULL); + if (strcmp(action, "permit") == 0) + plt = PREFIX_PERMIT; + else + plt = PREFIX_DENY; + + dnode = yang_dnode_get(running_config->dnode, xpath); + pl = nb_running_get_entry(dnode, NULL, true); + pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge); + if (pentry == NULL) + return CMD_WARNING; + + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, iptype, name); + + return rv; +} + +DEFPY( + ip_prefix_list, ip_prefix_list_cmd, + "ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>", + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the prefix-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY, + prefix_str); + + if (ge_str) + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-greater-or-equal", + NB_OP_MODIFY, ge_str); + if (le_str) + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-lesser-or-equal", + NB_OP_MODIFY, le_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_ip_prefix_list, no_ip_prefix_list_cmd, + "no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return plist_remove(vty, "ipv4", name, seq_str, action, + (struct prefix *)prefix, ge, le); +} + +DEFPY( + no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, + "no ip prefix-list WORD$name seq (1-4294967295)$seq", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR) +{ + return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0); +} + +DEFPY( + no_ip_prefix_list_all, no_ip_prefix_list_all_cmd, + "no ip prefix-list WORD$name", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + ip_prefix_list_remark, ip_prefix_list_remark_cmd, + "ip prefix-list WORD$name description LINE...", + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY( + no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd, + "no ip prefix-list WORD$name description", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd, + "no ip prefix-list WORD$name description LINE...", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY( + ipv6_prefix_list, ipv6_prefix_list_cmd, + "ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>", + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"::0/0 le 128\"\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Create the prefix-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintf(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY, + prefix_str); + + if (ge_str) + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-greater-or-equal", + NB_OP_MODIFY, ge_str); + if (le_str) + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-lesser-or-equal", + NB_OP_MODIFY, le_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY( + no_ipv6_prefix_list, no_ipv6_prefix_list_cmd, + "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"::0/0 le 128\"\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return plist_remove(vty, "ipv6", name, seq_str, action, + (struct prefix *)prefix, ge, le); +} + +DEFPY( + no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd, + "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR) +{ + return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0); +} + +DEFPY( + no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd, + "no ipv6 prefix-list WORD$name", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd, + "ipv6 prefix-list WORD$name description LINE...", + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY( + no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd, + "no ipv6 prefix-list WORD$name description", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd, + "no ipv6 prefix-list WORD$name description LINE...", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +void prefix_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + const char *ge_str = NULL, *le_str = NULL; + bool is_any; + struct prefix p; + + is_any = yang_dnode_exists(dnode, "./any"); + switch (type) { + case YPLT_IPV4: + if (!is_any) + yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-greater-or-equal")) + ge_str = yang_dnode_get_string( + dnode, "./ipv4-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-lesser-or-equal")) + le_str = yang_dnode_get_string( + dnode, "./ipv4-prefix-length-lesser-or-equal"); + + vty_out(vty, "ip "); + break; + case YPLT_IPV6: + if (!is_any) + yang_dnode_get_prefix(&p, dnode, "ipv6-prefix"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-greater-or-equal")) + ge_str = yang_dnode_get_string( + dnode, "./ipv6-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-lesser-or-equal")) + le_str = yang_dnode_get_string( + dnode, "./ipv6-prefix-length-lesser-or-equal"); + + vty_out(vty, "ipv6 "); + break; + } + + vty_out(vty, "prefix-list %s seq %s %s", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + if (is_any) { + vty_out(vty, " any\n"); + return; + } + + vty_out(vty, " %pFX", &p); + if (ge_str) + vty_out(vty, " ge %s", ge_str); + if (le_str) + vty_out(vty, " le %s", le_str); + + vty_out(vty, "\n"); +} + +void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + + switch (type) { + case YPLT_IPV4: + vty_out(vty, "ip "); + break; + case YPLT_IPV6: + vty_out(vty, "ipv6 "); + break; + } + + vty_out(vty, "prefix-list %s description %s\n", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, NULL)); +} + +void filter_cli_init(void) +{ + /* access-list cisco-style (legacy). */ + install_element(CONFIG_NODE, &access_list_std_cmd); + install_element(CONFIG_NODE, &no_access_list_std_cmd); + install_element(CONFIG_NODE, &access_list_ext_cmd); + install_element(CONFIG_NODE, &no_access_list_ext_cmd); + install_element(CONFIG_NODE, &no_access_list_legacy_cmd); + install_element(CONFIG_NODE, &access_list_legacy_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_legacy_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_legacy_remark_line_cmd); + + /* access-list zebra-style. */ + install_element(CONFIG_NODE, &access_list_cmd); + install_element(CONFIG_NODE, &no_access_list_cmd); + install_element(CONFIG_NODE, &no_access_list_all_cmd); + install_element(CONFIG_NODE, &access_list_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_remark_line_cmd); + + install_element(CONFIG_NODE, &ipv6_access_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd); + install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_remark_line_cmd); + + install_element(CONFIG_NODE, &mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_all_cmd); + install_element(CONFIG_NODE, &mac_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_remark_line_cmd); + + /* prefix lists. */ + install_element(CONFIG_NODE, &ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd); + install_element(CONFIG_NODE, &ip_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_remark_line_cmd); + + install_element(CONFIG_NODE, &ipv6_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd); + install_element(CONFIG_NODE, &ipv6_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_line_cmd); +} diff --git a/lib/filter_nb.c b/lib/filter_nb.c new file mode 100644 index 0000000000..83cf586ed3 --- /dev/null +++ b/lib/filter_nb.c @@ -0,0 +1,1286 @@ +/* + * FRR filter northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include "zebra.h" + +#include "lib/northbound.h" +#include "lib/prefix.h" + +#include "lib/filter.h" +#include "lib/plist.h" +#include "lib/plist_int.h" + +/* Helper function. */ +static in_addr_t +ipv4_network_addr(in_addr_t hostaddr, int masklen) +{ + struct in_addr mask; + + masklen2ip(masklen, &mask); + return hostaddr & mask.s_addr; +} + +static enum nb_error +prefix_list_length_validate(const struct lyd_node *dnode) +{ + int type = yang_dnode_get_enum(dnode, "../../type"); + const char *xpath_le = NULL, *xpath_ge = NULL; + struct prefix p; + uint8_t le, ge; + + if (type == YPLT_IPV4) { + yang_dnode_get_prefix(&p, dnode, "../ipv4-prefix"); + xpath_le = "../ipv4-prefix-length-lesser-or-equal"; + xpath_ge = "../ipv4-prefix-length-greater-or-equal"; + } else { + yang_dnode_get_prefix(&p, dnode, "../ipv6-prefix"); + xpath_le = "../ipv6-prefix-length-lesser-or-equal"; + xpath_ge = "../ipv6-prefix-length-greater-or-equal"; + } + + /* + * Check rule: + * prefix length <= le. + */ + if (yang_dnode_exists(dnode, xpath_le)) { + le = yang_dnode_get_uint8(dnode, xpath_le); + if (p.prefixlen > le) + goto log_and_fail; + + } + + /* + * Check rule: + * prefix length < ge. + */ + if (yang_dnode_exists(dnode, xpath_ge)) { + ge = yang_dnode_get_uint8(dnode, xpath_ge); + if (p.prefixlen >= ge) + goto log_and_fail; + } + + /* + * Check rule: + * ge <= le. + */ + if (yang_dnode_exists(dnode, xpath_le) && + yang_dnode_exists(dnode, xpath_ge)) { + le = yang_dnode_get_uint8(dnode, xpath_le); + ge = yang_dnode_get_uint8(dnode, xpath_ge); + if (ge > le) + goto log_and_fail; + } + + return NB_OK; + + log_and_fail: + zlog_info("prefix-list: invalid prefix range for %pFX: " + "Make sure that mask length < ge <= le", &p); + return NB_ERR_VALIDATION; +} + +/** + * Sets prefix list entry to blank value. + * + * \param[out] ple prefix list entry to modify. + */ +static void prefix_list_entry_set_empty(struct prefix_list_entry *ple) +{ + ple->any = false; + memset(&ple->prefix, 0, sizeof(ple->prefix)); + ple->ge = 0; + ple->le = 0; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy + */ +static int lib_access_list_legacy_create(struct nb_cb_create_args *args) +{ + struct access_list *acl; + const char *acl_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl_name = yang_dnode_get_string(args->dnode, "./number"); + acl = access_list_get(AFI_IP, acl_name); + nb_running_set_entry(args->dnode, acl); + + return NB_OK; +} + +static int lib_access_list_legacy_destroy(struct nb_cb_destroy_args *args) +{ + struct access_master *am; + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_unset_entry(args->dnode); + am = acl->master; + if (am->delete_hook) + am->delete_hook(acl); + + access_list_delete(acl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/remark + */ +static int lib_access_list_legacy_remark_modify(struct nb_cb_modify_args *args) +{ + struct access_list *acl; + const char *remark; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_get_entry(args->dnode, NULL, true); + if (acl->remark) + XFREE(MTYPE_TMP, acl->remark); + + remark = yang_dnode_get_string(args->dnode, NULL); + acl->remark = XSTRDUP(MTYPE_TMP, remark); + + return NB_OK; +} + +static int +lib_access_list_legacy_remark_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_get_entry(args->dnode, NULL, true); + if (acl->remark) + XFREE(MTYPE_TMP, acl->remark); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry + */ +static int lib_access_list_legacy_entry_create(struct nb_cb_create_args *args) +{ + struct filter_cisco *fc; + struct access_list *acl; + struct filter *f; + uint32_t aclno; + + /* TODO: validate `filter_lookup_cisco` returns NULL. */ + + if (args->event != NB_EV_APPLY) + return NB_OK; + + aclno = yang_dnode_get_uint16(args->dnode, "../number"); + + f = filter_new(); + f->cisco = 1; + f->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + fc = &f->u.cfilter; + if ((aclno >= 1 && aclno <= 99) || (aclno >= 1300 && aclno <= 1999)) + fc->extended = 0; + else + fc->extended = 1; + + acl = nb_running_get_entry(args->dnode, NULL, true); + f->acl = acl; + access_list_filter_add(acl, f); + nb_running_set_entry(args->dnode, f); + + return NB_OK; +} + +static int lib_access_list_legacy_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_unset_entry(args->dnode); + acl = f->acl; + access_list_filter_delete(acl, f); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/action + */ +static int +lib_access_list_legacy_entry_action_modify(struct nb_cb_modify_args *args) +{ + const char *filter_type; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + filter_type = yang_dnode_get_string(args->dnode, NULL); + if (strcmp(filter_type, "permit") == 0) + f->type = FILTER_PERMIT; + else + f->type = FILTER_DENY; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/host + */ +static int +lib_access_list_legacy_entry_host_modify(struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); + fc->addr_mask.s_addr = INADDR_ANY; + + return NB_OK; +} + +static int +lib_access_list_legacy_entry_host_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->addr.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/network + */ +static int +lib_access_list_legacy_entry_network_modify(struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + yang_dnode_get_prefix(&p, args->dnode, NULL); + fc->addr.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen); + masklen2ip(p.prefixlen, &fc->addr_mask); + + return NB_OK; +} + +static int +lib_access_list_legacy_entry_network_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->addr.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/any + */ +static int +lib_access_list_legacy_entry_any_create(struct nb_cb_create_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->addr.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +static int +lib_access_list_legacy_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->addr.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/destination-host + */ +static int lib_access_list_legacy_entry_destination_host_modify( + struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); + fc->mask_mask.s_addr = INADDR_ANY; + + return NB_OK; +} + +static int lib_access_list_legacy_entry_destination_host_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/destination-network + */ +static int lib_access_list_legacy_entry_destination_network_modify( + struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + yang_dnode_get_prefix(&p, args->dnode, NULL); + fc->mask.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen); + masklen2ip(p.prefixlen, &fc->mask_mask); + + return NB_OK; +} + +static int lib_access_list_legacy_entry_destination_network_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list-legacy/entry/destination-any + */ +static int lib_access_list_legacy_entry_destination_any_create( + struct nb_cb_create_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +static int lib_access_list_legacy_entry_destination_any_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = INADDR_NONE; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list + */ +static int lib_access_list_create(struct nb_cb_create_args *args) +{ + struct access_list *acl = NULL; + const char *acl_name; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + type = yang_dnode_get_enum(args->dnode, "./type"); + acl_name = yang_dnode_get_string(args->dnode, "./name"); + + switch (type) { + case YALT_IPV4: + acl = access_list_get(AFI_IP, acl_name); + break; + case YALT_IPV6: + acl = access_list_get(AFI_IP6, acl_name); + break; + case YALT_MAC: + acl = access_list_get(AFI_L2VPN, acl_name); + break; + } + + nb_running_set_entry(args->dnode, acl); + + return NB_OK; +} + +static int lib_access_list_destroy(struct nb_cb_destroy_args *args) +{ + struct access_master *am; + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_unset_entry(args->dnode); + am = acl->master; + if (am->delete_hook) + am->delete_hook(acl); + + access_list_delete(acl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry + */ +static int lib_access_list_entry_create(struct nb_cb_create_args *args) +{ + struct access_list *acl; + struct filter *f; + + /* TODO: validate `filter_lookup_zebra` returns NULL. */ + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = filter_new(); + f->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + + acl = nb_running_get_entry(args->dnode, NULL, true); + f->acl = acl; + access_list_filter_add(acl, f); + nb_running_set_entry(args->dnode, f); + + return NB_OK; +} + +static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_unset_entry(args->dnode); + acl = f->acl; + access_list_filter_delete(acl, f); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/ipv4-prefix + */ +static int +lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL); + + return NB_OK; +} + +static int +lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + memset(&fz->prefix, 0, sizeof(fz->prefix)); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/ipv4-exact-match + */ +static int +lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->exact = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +static int +lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->exact = 0; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/any + */ +static int lib_access_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + memset(&fz->prefix, 0, sizeof(fz->prefix)); + + type = yang_dnode_get_enum(args->dnode, "../../type"); + switch (type) { + case YALT_IPV4: + fz->prefix.family = AF_INET; + break; + case YALT_IPV6: + fz->prefix.family = AF_INET6; + break; + case YALT_MAC: + fz->prefix.family = AF_ETHERNET; + break; + } + + return NB_OK; +} + +static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->prefix.family = 0; + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list + */ +static int lib_prefix_list_create(struct nb_cb_create_args *args) +{ + struct prefix_list *pl = NULL; + const char *name; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + type = yang_dnode_get_enum(args->dnode, "./type"); + name = yang_dnode_get_string(args->dnode, "./name"); + switch (type) { + case 0: /* ipv4 */ + pl = prefix_list_get(AFI_IP, 0, name); + break; + case 1: /* ipv6 */ + pl = prefix_list_get(AFI_IP6, 0, name); + break; + } + + nb_running_set_entry(args->dnode, pl); + + return NB_OK; +} + +static int lib_prefix_list_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_unset_entry(args->dnode); + prefix_list_delete(pl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/remark + */ +static int lib_prefix_list_remark_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list *pl; + const char *remark; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + if (pl->desc) + XFREE(MTYPE_TMP, pl->desc); + + remark = yang_dnode_get_string(args->dnode, NULL); + pl->desc = XSTRDUP(MTYPE_TMP, remark); + + return NB_OK; +} + +static int lib_prefix_list_remark_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + if (pl->desc) + XFREE(MTYPE_TMP, pl->desc); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry + */ +static int lib_prefix_list_entry_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + ple = prefix_list_entry_new(); + ple->pl = pl; + ple->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + prefix_list_entry_set_empty(ple); + nb_running_set_entry(args->dnode, ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_unset_entry(args->dnode); + if (ple->installed) + prefix_list_entry_delete2(ple); + else + prefix_list_entry_free(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/action + */ +static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + int action_type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + action_type = yang_dnode_get_enum(args->dnode, NULL); + if (action_type == YPLA_PERMIT) + ple->type = PREFIX_PERMIT; + else + ple->type = PREFIX_DENY; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix + */ +static int +lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + struct prefix p; + + if (args->event == NB_EV_VALIDATE) { + /* + * TODO: validate prefix_entry_dup_check() passes. + * + * This needs to be implemented using YANG lyd_node + * navigation, because the `priv` data structures are not + * available at `NB_EV_VALIDATE` phase. An easier + * alternative would be mark `ipvx-prefix` as unique + * (see RFC 7950, Section 7.8.3. The list "unique" Statement). + */ + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + yang_dnode_get_prefix(&ple->prefix, args->dnode, NULL); + + /* Apply mask and correct original address if necessary. */ + prefix_copy(&p, &ple->prefix); + apply_mask(&p); + if (!prefix_same(&ple->prefix, &p)) { + zlog_info("%s: bad network %pFX correcting it to %pFX", + __func__, &ple->prefix, &p); + prefix_copy(&ple->prefix, &p); + } + + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int +lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + memset(&ple->prefix, 0, sizeof(ple->prefix)); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event == NB_EV_VALIDATE && + prefix_list_length_validate(args->dnode) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event == NB_EV_VALIDATE && + prefix_list_length_validate(args->dnode) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/any + */ +static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->any = true; + + /* Fill prefix struct from scratch. */ + memset(&ple->prefix, 0, sizeof(ple->prefix)); + + type = yang_dnode_get_enum(args->dnode, "../../type"); + switch (type) { + case YPLT_IPV4: + ple->prefix.family = AF_INET; + ple->ge = 0; + ple->le = IPV4_MAX_BITLEN; + break; + case YPLT_IPV6: + ple->prefix.family = AF_INET6; + ple->ge = 0; + ple->le = IPV6_MAX_BITLEN; + break; + } + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + prefix_list_entry_set_empty(ple); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_filter_info = { + .name = "frr-filter", + .nodes = { + { + .xpath = "/frr-filter:lib/access-list-legacy", + .cbs = { + .create = lib_access_list_legacy_create, + .destroy = lib_access_list_legacy_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/remark", + .cbs = { + .modify = lib_access_list_legacy_remark_modify, + .destroy = lib_access_list_legacy_remark_destroy, + .cli_show = access_list_legacy_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry", + .cbs = { + .create = lib_access_list_legacy_entry_create, + .destroy = lib_access_list_legacy_entry_destroy, + .cli_show = access_list_legacy_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/action", + .cbs = { + .modify = lib_access_list_legacy_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/host", + .cbs = { + .modify = lib_access_list_legacy_entry_host_modify, + .destroy = lib_access_list_legacy_entry_host_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/network", + .cbs = { + .modify = lib_access_list_legacy_entry_network_modify, + .destroy = lib_access_list_legacy_entry_network_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/any", + .cbs = { + .create = lib_access_list_legacy_entry_any_create, + .destroy = lib_access_list_legacy_entry_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-host", + .cbs = { + .modify = lib_access_list_legacy_entry_destination_host_modify, + .destroy = lib_access_list_legacy_entry_destination_host_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-network", + .cbs = { + .modify = lib_access_list_legacy_entry_destination_network_modify, + .destroy = lib_access_list_legacy_entry_destination_network_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-any", + .cbs = { + .create = lib_access_list_legacy_entry_destination_any_create, + .destroy = lib_access_list_legacy_entry_destination_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list", + .cbs = { + .create = lib_access_list_create, + .destroy = lib_access_list_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/remark", + .cbs = { + .modify = lib_access_list_legacy_remark_modify, + .destroy = lib_access_list_legacy_remark_destroy, + .cli_show = access_list_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry", + .cbs = { + .create = lib_access_list_entry_create, + .destroy = lib_access_list_entry_destroy, + .cli_show = access_list_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/action", + .cbs = { + .modify = lib_access_list_legacy_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv4-prefix", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv4-exact-match", + .cbs = { + .modify = lib_access_list_entry_ipv4_exact_match_modify, + .destroy = lib_access_list_entry_ipv4_exact_match_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv6-prefix", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv6-exact-match", + .cbs = { + .modify = lib_access_list_entry_ipv4_exact_match_modify, + .destroy = lib_access_list_entry_ipv4_exact_match_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/mac", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/any", + .cbs = { + .create = lib_access_list_entry_any_create, + .destroy = lib_access_list_entry_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list", + .cbs = { + .create = lib_prefix_list_create, + .destroy = lib_prefix_list_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/remark", + .cbs = { + .modify = lib_prefix_list_remark_modify, + .destroy = lib_prefix_list_remark_destroy, + .cli_show = prefix_list_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry", + .cbs = { + .create = lib_prefix_list_entry_create, + .destroy = lib_prefix_list_entry_destroy, + .cli_show = prefix_list_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/action", + .cbs = { + .modify = lib_prefix_list_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/any", + .cbs = { + .create = lib_prefix_list_entry_any_create, + .destroy = lib_prefix_list_entry_any_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; @@ -263,15 +263,21 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) */ if (yang_module_find("frr-interface")) { struct lyd_node *if_dnode; + char oldpath[XPATH_MAXLEN]; + char newpath[XPATH_MAXLEN]; if_dnode = yang_dnode_get( running_config->dnode, "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", ifp->name, old_vrf->name); + if (if_dnode) { - nb_running_unset_entry(if_dnode->parent); + yang_dnode_get_path(if_dnode->parent, oldpath, + sizeof(oldpath)); yang_dnode_change_leaf(if_dnode, vrf->name); - nb_running_set_entry(if_dnode->parent, ifp); + yang_dnode_get_path(if_dnode->parent, newpath, + sizeof(newpath)); + nb_running_move_tree(oldpath, newpath); running_config->version++; } } @@ -1561,8 +1567,8 @@ static int lib_interface_destroy(struct nb_cb_destroy_args *args) case NB_EV_VALIDATE: ifp = nb_running_get_entry(args->dnode, NULL, true); if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - zlog_warn("%s: only inactive interfaces can be deleted", - __func__); + snprintf(args->errmsg, args->errmsg_len, + "only inactive interfaces can be deleted"); return NB_ERR_VALIDATION; } break; diff --git a/lib/libfrr.c b/lib/libfrr.c index ac165f254e..b3df7de6d3 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -661,7 +661,6 @@ struct thread_master *frr_init(void) zlog_init(di->progname, di->logname, di->instance, ids.uid_normal, ids.gid_normal); - zlog_tls_buffer_init(); command_setup_early_logging(di->early_logging, di->early_loglevel); @@ -902,14 +901,18 @@ static int frr_config_read_in(struct thread *t) * reading the configuration file. */ if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) { + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; int ret; - ret = nb_candidate_commit(vty_shared_candidate_config, - NB_CLIENT_CLI, NULL, true, - "Read configuration file", NULL); + context.client = NB_CLIENT_CLI; + ret = nb_candidate_commit(&context, vty_shared_candidate_config, + true, "Read configuration file", NULL, + errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - zlog_err("%s: failed to read configuration file.", - __func__); + zlog_err( + "%s: failed to read configuration file: %s (%s)", + __func__, nb_err_name(ret), errmsg); } return 0; @@ -936,6 +939,7 @@ void frr_config_fork(void) if (!di->pid_file) di->pid_file = pidfile_default; pid_output(di->pid_file); + zlog_tls_buffer_init(); } static void frr_vty_serv(void) diff --git a/lib/northbound.c b/lib/northbound.c index 18bd4f5fd8..48b8499bfc 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -30,6 +30,7 @@ #include "northbound.h" #include "northbound_cli.h" #include "northbound_db.h" +#include "frrstr.h" DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node") DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration") @@ -62,19 +63,24 @@ static struct { */ static bool transaction_in_progress; -static int nb_callback_pre_validate(const struct nb_node *nb_node, - const struct lyd_node *dnode); -static int nb_callback_configuration(const enum nb_event event, - struct nb_config_change *change); -static struct nb_transaction *nb_transaction_new(struct nb_config *config, - struct nb_config_cbs *changes, - enum nb_client client, - const void *user, - const char *comment); +static int nb_callback_pre_validate(struct nb_context *context, + const struct nb_node *nb_node, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len); +static int nb_callback_configuration(struct nb_context *context, + const enum nb_event event, + struct nb_config_change *change, + char *errmsg, size_t errmsg_len); +static struct nb_transaction * +nb_transaction_new(struct nb_context *context, struct nb_config *config, + struct nb_config_cbs *changes, const char *comment, + char *errmsg, size_t errmsg_len); static void nb_transaction_free(struct nb_transaction *transaction); static int nb_transaction_process(enum nb_event event, - struct nb_transaction *transaction); -static void nb_transaction_apply_finish(struct nb_transaction *transaction); + struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); +static void nb_transaction_apply_finish(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); static int nb_oper_data_iter_node(const struct lys_node *snode, const char *xpath, const void *list_entry, const struct yang_list_keys *list_keys, @@ -580,20 +586,25 @@ int nb_candidate_update(struct nb_config *candidate) * WARNING: lyd_validate() can change the configuration as part of the * validation process. */ -static int nb_candidate_validate_yang(struct nb_config *candidate) +static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg, + size_t errmsg_len) { if (lyd_validate(&candidate->dnode, LYD_OPT_STRICT | LYD_OPT_CONFIG | LYD_OPT_WHENAUTODEL, ly_native_ctx) - != 0) + != 0) { + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); return NB_ERR_VALIDATION; + } return NB_OK; } /* Perform code-level validation using the northbound callbacks. */ -static int nb_candidate_validate_code(struct nb_config *candidate, - struct nb_config_cbs *changes) +static int nb_candidate_validate_code(struct nb_context *context, + struct nb_config *candidate, + struct nb_config_cbs *changes, + char *errmsg, size_t errmsg_len) { struct nb_config_cb *cb; struct lyd_node *root, *next, *child; @@ -608,7 +619,8 @@ static int nb_candidate_validate_code(struct nb_config *candidate, if (!nb_node->cbs.pre_validate) goto next; - ret = nb_callback_pre_validate(nb_node, child); + ret = nb_callback_pre_validate(context, nb_node, child, + errmsg, errmsg_len); if (ret != NB_OK) return NB_ERR_VALIDATION; @@ -621,7 +633,8 @@ static int nb_candidate_validate_code(struct nb_config *candidate, RB_FOREACH (cb, nb_config_cbs, changes) { struct nb_config_change *change = (struct nb_config_change *)cb; - ret = nb_callback_configuration(NB_EV_VALIDATE, change); + ret = nb_callback_configuration(context, NB_EV_VALIDATE, change, + errmsg, errmsg_len); if (ret != NB_OK) return NB_ERR_VALIDATION; } @@ -629,30 +642,36 @@ static int nb_candidate_validate_code(struct nb_config *candidate, return NB_OK; } -int nb_candidate_validate(struct nb_config *candidate) +int nb_candidate_validate(struct nb_context *context, + struct nb_config *candidate, char *errmsg, + size_t errmsg_len) { struct nb_config_cbs changes; int ret; - if (nb_candidate_validate_yang(candidate) != NB_OK) + if (nb_candidate_validate_yang(candidate, errmsg, sizeof(errmsg_len)) + != NB_OK) return NB_ERR_VALIDATION; RB_INIT(nb_config_cbs, &changes); nb_config_diff(running_config, candidate, &changes); - ret = nb_candidate_validate_code(candidate, &changes); + ret = nb_candidate_validate_code(context, candidate, &changes, errmsg, + errmsg_len); nb_config_diff_del_changes(&changes); return ret; } -int nb_candidate_commit_prepare(struct nb_config *candidate, - enum nb_client client, const void *user, +int nb_candidate_commit_prepare(struct nb_context *context, + struct nb_config *candidate, const char *comment, - struct nb_transaction **transaction) + struct nb_transaction **transaction, + char *errmsg, size_t errmsg_len) { struct nb_config_cbs changes; - if (nb_candidate_validate_yang(candidate) != NB_OK) { + if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) + != NB_OK) { flog_warn(EC_LIB_NB_CANDIDATE_INVALID, "%s: failed to validate candidate configuration", __func__); @@ -664,7 +683,9 @@ int nb_candidate_commit_prepare(struct nb_config *candidate, if (RB_EMPTY(nb_config_cbs, &changes)) return NB_ERR_NO_CHANGES; - if (nb_candidate_validate_code(candidate, &changes) != NB_OK) { + if (nb_candidate_validate_code(context, candidate, &changes, errmsg, + errmsg_len) + != NB_OK) { flog_warn(EC_LIB_NB_CANDIDATE_INVALID, "%s: failed to validate candidate configuration", __func__); @@ -672,29 +693,37 @@ int nb_candidate_commit_prepare(struct nb_config *candidate, return NB_ERR_VALIDATION; } - *transaction = - nb_transaction_new(candidate, &changes, client, user, comment); + *transaction = nb_transaction_new(context, candidate, &changes, comment, + errmsg, errmsg_len); if (*transaction == NULL) { flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED, - "%s: failed to create transaction", __func__); + "%s: failed to create transaction: %s", __func__, + errmsg); nb_config_diff_del_changes(&changes); return NB_ERR_LOCKED; } - return nb_transaction_process(NB_EV_PREPARE, *transaction); + return nb_transaction_process(NB_EV_PREPARE, *transaction, errmsg, + errmsg_len); } void nb_candidate_commit_abort(struct nb_transaction *transaction) { - (void)nb_transaction_process(NB_EV_ABORT, transaction); + char errmsg[BUFSIZ] = {0}; + + (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg, + sizeof(errmsg)); nb_transaction_free(transaction); } void nb_candidate_commit_apply(struct nb_transaction *transaction, bool save_transaction, uint32_t *transaction_id) { - (void)nb_transaction_process(NB_EV_APPLY, transaction); - nb_transaction_apply_finish(transaction); + char errmsg[BUFSIZ] = {0}; + + (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg, + sizeof(errmsg)); + nb_transaction_apply_finish(transaction, errmsg, sizeof(errmsg)); /* Replace running by candidate. */ transaction->config->version++; @@ -709,15 +738,16 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction, nb_transaction_free(transaction); } -int nb_candidate_commit(struct nb_config *candidate, enum nb_client client, - const void *user, bool save_transaction, - const char *comment, uint32_t *transaction_id) +int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate, + bool save_transaction, const char *comment, + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len) { struct nb_transaction *transaction = NULL; int ret; - ret = nb_candidate_commit_prepare(candidate, client, user, comment, - &transaction); + ret = nb_candidate_commit_prepare(context, candidate, comment, + &transaction, errmsg, errmsg_len); /* * Apply the changes if the preparation phase succeeded. Otherwise abort * the transaction. @@ -735,7 +765,7 @@ int nb_running_lock(enum nb_client client, const void *user) { int ret = -1; - frr_with_mutex(&running_config_mgmt_lock.mtx) { + frr_with_mutex (&running_config_mgmt_lock.mtx) { if (!running_config_mgmt_lock.locked) { running_config_mgmt_lock.locked = true; running_config_mgmt_lock.owner_client = client; @@ -751,7 +781,7 @@ int nb_running_unlock(enum nb_client client, const void *user) { int ret = -1; - frr_with_mutex(&running_config_mgmt_lock.mtx) { + frr_with_mutex (&running_config_mgmt_lock.mtx) { if (running_config_mgmt_lock.locked && running_config_mgmt_lock.owner_client == client && running_config_mgmt_lock.owner_user == user) { @@ -769,7 +799,7 @@ int nb_running_lock_check(enum nb_client client, const void *user) { int ret = -1; - frr_with_mutex(&running_config_mgmt_lock.mtx) { + frr_with_mutex (&running_config_mgmt_lock.mtx) { if (!running_config_mgmt_lock.locked || (running_config_mgmt_lock.owner_client == client && running_config_mgmt_lock.owner_user == user)) @@ -801,78 +831,237 @@ static void nb_log_config_callback(const enum nb_event event, value); } -static int nb_callback_create(const struct nb_node *nb_node, +static int nb_callback_create(struct nb_context *context, + const struct nb_node *nb_node, enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + union nb_resource *resource, char *errmsg, + size_t errmsg_len) { struct nb_cb_create_args args = {}; + bool unexpected_error = false; + int ret; nb_log_config_callback(event, NB_OP_CREATE, dnode); + args.context = context; args.event = event; args.dnode = dnode; args.resource = resource; - return nb_node->cbs.create(&args); + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.create(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_RESOURCE: + if (event != NB_EV_PREPARE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; } -static int nb_callback_modify(const struct nb_node *nb_node, +static int nb_callback_modify(struct nb_context *context, + const struct nb_node *nb_node, enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) + union nb_resource *resource, char *errmsg, + size_t errmsg_len) { struct nb_cb_modify_args args = {}; + bool unexpected_error = false; + int ret; nb_log_config_callback(event, NB_OP_MODIFY, dnode); + args.context = context; args.event = event; args.dnode = dnode; args.resource = resource; - return nb_node->cbs.modify(&args); + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.modify(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_RESOURCE: + if (event != NB_EV_PREPARE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; } -static int nb_callback_destroy(const struct nb_node *nb_node, +static int nb_callback_destroy(struct nb_context *context, + const struct nb_node *nb_node, enum nb_event event, - const struct lyd_node *dnode) + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) { struct nb_cb_destroy_args args = {}; + bool unexpected_error = false; + int ret; nb_log_config_callback(event, NB_OP_DESTROY, dnode); + args.context = context; args.event = event; args.dnode = dnode; - return nb_node->cbs.destroy(&args); + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.destroy(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; } -static int nb_callback_move(const struct nb_node *nb_node, enum nb_event event, - const struct lyd_node *dnode) +static int nb_callback_move(struct nb_context *context, + const struct nb_node *nb_node, enum nb_event event, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) { struct nb_cb_move_args args = {}; + bool unexpected_error = false; + int ret; nb_log_config_callback(event, NB_OP_MOVE, dnode); + args.context = context; args.event = event; args.dnode = dnode; - return nb_node->cbs.move(&args); + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.move(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; } -static int nb_callback_pre_validate(const struct nb_node *nb_node, - const struct lyd_node *dnode) +static int nb_callback_pre_validate(struct nb_context *context, + const struct nb_node *nb_node, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) { struct nb_cb_pre_validate_args args = {}; + bool unexpected_error = false; + int ret; nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode); args.dnode = dnode; - return nb_node->cbs.pre_validate(&args); + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.pre_validate(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR_VALIDATION: + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; } -static void nb_callback_apply_finish(const struct nb_node *nb_node, - const struct lyd_node *dnode) +static void nb_callback_apply_finish(struct nb_context *context, + const struct nb_node *nb_node, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) { struct nb_cb_apply_finish_args args = {}; nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode); + args.context = context; args.dnode = dnode; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; nb_node->cbs.apply_finish(&args); } @@ -952,8 +1141,10 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, * Call the northbound configuration callback associated to a given * configuration change. */ -static int nb_callback_configuration(const enum nb_event event, - struct nb_config_change *change) +static int nb_callback_configuration(struct nb_context *context, + const enum nb_event event, + struct nb_config_change *change, + char *errmsg, size_t errmsg_len) { enum nb_operation operation = change->cb.operation; char xpath[XPATH_MAXLEN]; @@ -962,7 +1153,6 @@ static int nb_callback_configuration(const enum nb_event event, union nb_resource *resource; int ret = NB_ERR; - if (event == NB_EV_VALIDATE) resource = NULL; else @@ -970,16 +1160,20 @@ static int nb_callback_configuration(const enum nb_event event, switch (operation) { case NB_OP_CREATE: - ret = nb_callback_create(nb_node, event, dnode, resource); + ret = nb_callback_create(context, nb_node, event, dnode, + resource, errmsg, errmsg_len); break; case NB_OP_MODIFY: - ret = nb_callback_modify(nb_node, event, dnode, resource); + ret = nb_callback_modify(context, nb_node, event, dnode, + resource, errmsg, errmsg_len); break; case NB_OP_DESTROY: - ret = nb_callback_destroy(nb_node, event, dnode); + ret = nb_callback_destroy(context, nb_node, event, dnode, + errmsg, errmsg_len); break; case NB_OP_MOVE: - ret = nb_callback_move(nb_node, event, dnode); + ret = nb_callback_move(context, nb_node, event, dnode, errmsg, + errmsg_len); break; default: yang_dnode_get_path(dnode, xpath, sizeof(xpath)); @@ -1014,45 +1208,48 @@ static int nb_callback_configuration(const enum nb_event event, break; default: flog_err(EC_LIB_DEVELOPMENT, - "%s: unknown event (%u) [xpath %s]", - __func__, event, xpath); + "%s: unknown event (%u) [xpath %s]", __func__, + event, xpath); exit(1); } flog(priority, ref, - "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", - __func__, nb_err_name(ret), nb_event_name(event), + "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", + nb_err_name(ret), nb_event_name(event), nb_operation_name(operation), xpath); + if (strlen(errmsg) > 0) + flog(priority, ref, + "error processing configuration change: %s", + errmsg); } return ret; } static struct nb_transaction * -nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes, - enum nb_client client, const void *user, const char *comment) +nb_transaction_new(struct nb_context *context, struct nb_config *config, + struct nb_config_cbs *changes, const char *comment, + char *errmsg, size_t errmsg_len) { struct nb_transaction *transaction; - if (nb_running_lock_check(client, user)) { - flog_warn( - EC_LIB_NB_TRANSACTION_CREATION_FAILED, - "%s: running configuration is locked by another client", - __func__); + if (nb_running_lock_check(context->client, context->user)) { + strlcpy(errmsg, + "running configuration is locked by another client", + errmsg_len); return NULL; } if (transaction_in_progress) { - flog_warn( - EC_LIB_NB_TRANSACTION_CREATION_FAILED, - "%s: error - there's already another transaction in progress", - __func__); + strlcpy(errmsg, + "there's already another transaction in progress", + errmsg_len); return NULL; } transaction_in_progress = true; transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction)); - transaction->client = client; + transaction->context = context; if (comment) strlcpy(transaction->comment, comment, sizeof(transaction->comment)); @@ -1071,7 +1268,8 @@ static void nb_transaction_free(struct nb_transaction *transaction) /* Process all configuration changes associated to a transaction. */ static int nb_transaction_process(enum nb_event event, - struct nb_transaction *transaction) + struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len) { struct nb_config_cb *cb; @@ -1087,7 +1285,8 @@ static int nb_transaction_process(enum nb_event event, break; /* Call the appropriate callback. */ - ret = nb_callback_configuration(event, change); + ret = nb_callback_configuration(transaction->context, event, + change, errmsg, errmsg_len); switch (event) { case NB_EV_PREPARE: if (ret != NB_OK) @@ -1141,7 +1340,8 @@ nb_apply_finish_cb_find(struct nb_config_cbs *cbs, } /* Call the 'apply_finish' callbacks. */ -static void nb_transaction_apply_finish(struct nb_transaction *transaction) +static void nb_transaction_apply_finish(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len) { struct nb_config_cbs cbs; struct nb_config_cb *cb; @@ -1200,7 +1400,8 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) /* Call the 'apply_finish' callbacks, sorted by their priorities. */ RB_FOREACH (cb, nb_config_cbs, &cbs) - nb_callback_apply_finish(cb->nb_node, cb->dnode); + nb_callback_apply_finish(transaction->context, cb->nb_node, + cb->dnode, errmsg, errmsg_len); /* Release memory. */ while (!RB_EMPTY(nb_config_cbs, &cbs)) { @@ -1789,6 +1990,29 @@ void nb_running_set_entry(const struct lyd_node *dnode, void *entry) config->entry = entry; } +void nb_running_move_tree(const char *xpath_from, const char *xpath_to) +{ + struct nb_config_entry *entry; + struct list *entries = hash_to_list(running_config_entries); + struct listnode *ln; + + for (ALL_LIST_ELEMENTS_RO(entries, ln, entry)) { + if (!frrstr_startswith(entry->xpath, xpath_from)) + continue; + + hash_release(running_config_entries, entry); + + char *newpath = + frrstr_replace(entry->xpath, xpath_from, xpath_to); + strlcpy(entry->xpath, newpath, sizeof(entry->xpath)); + XFREE(MTYPE_TMP, newpath); + + hash_get(running_config_entries, entry, hash_alloc_intern); + } + + list_delete(&entries); +} + static void *nb_running_unset_entry_helper(const struct lyd_node *dnode) { struct nb_config_entry *config, s; @@ -1915,7 +2139,7 @@ const char *nb_err_name(enum nb_error error) case NB_ERR_LOCKED: return "resource is locked"; case NB_ERR_VALIDATION: - return "validation error"; + return "validation"; case NB_ERR_RESOURCE: return "failed to allocate resource"; case NB_ERR_INCONSISTENCY: diff --git a/lib/northbound.h b/lib/northbound.h index 84382eeb60..bd57013f59 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -91,6 +91,9 @@ union nb_resource { */ struct nb_cb_create_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + /* * The transaction phase. Refer to the documentation comments of * nb_event for more details. @@ -107,9 +110,18 @@ struct nb_cb_create_args { * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. */ union nb_resource *resource; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; }; struct nb_cb_modify_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + /* * The transaction phase. Refer to the documentation comments of * nb_event for more details. @@ -126,9 +138,18 @@ struct nb_cb_modify_args { * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. */ union nb_resource *resource; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; }; struct nb_cb_destroy_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + /* * The transaction phase. Refer to the documentation comments of * nb_event for more details. @@ -137,9 +158,18 @@ struct nb_cb_destroy_args { /* libyang data node that is being deleted. */ const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; }; struct nb_cb_move_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + /* * The transaction phase. Refer to the documentation comments of * nb_event for more details. @@ -148,16 +178,40 @@ struct nb_cb_move_args { /* libyang data node that is being moved. */ const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; }; struct nb_cb_pre_validate_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + /* libyang data node associated with the 'pre_validate' callback. */ const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; }; struct nb_cb_apply_finish_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + /* libyang data node associated with the 'apply_finish' callback. */ const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; }; struct nb_cb_get_elem_args { @@ -305,6 +359,10 @@ struct nb_callbacks { * args * Refer to the documentation comments of nb_cb_pre_validate_args for * details. + * + * Returns: + * - NB_OK on success. + * - NB_ERR_VALIDATION when a validation error occurred. */ int (*pre_validate)(struct nb_cb_pre_validate_args *args); @@ -538,6 +596,29 @@ enum nb_client { NB_CLIENT_GRPC, }; +/* Northbound context. */ +struct nb_context { + /* Northbound client. */ + enum nb_client client; + + /* Northbound user (can be NULL). */ + const void *user; + + /* Client-specific data. */ +#if 0 + union { + struct { + } cli; + struct { + } confd; + struct { + } sysrepo; + struct { + } grpc; + } client_data; +#endif +}; + /* Northbound configuration. */ struct nb_config { struct lyd_node *dnode; @@ -564,7 +645,7 @@ struct nb_config_change { /* Northbound configuration transaction. */ struct nb_transaction { - enum nb_client client; + struct nb_context *context; char comment[80]; struct nb_config *config; struct nb_config_cbs changes; @@ -762,28 +843,36 @@ extern int nb_candidate_update(struct nb_config *candidate); * WARNING: the candidate can be modified as part of the validation process * (e.g. add default nodes). * + * context + * Context of the northbound transaction. + * * candidate * Candidate configuration to validate. * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * * Returns: * NB_OK on success, NB_ERR_VALIDATION otherwise. */ -extern int nb_candidate_validate(struct nb_config *candidate); +extern int nb_candidate_validate(struct nb_context *context, + struct nb_config *candidate, char *errmsg, + size_t errmsg_len); /* * Create a new configuration transaction but do not commit it yet. Only * validate the candidate and prepare all resources required to apply the * configuration changes. * + * context + * Context of the northbound transaction. + * * candidate * Candidate configuration to commit. * - * client - * Northbound client performing the commit. - * - * user - * Northbound user performing the commit (can be NULL). - * * comment * Optional comment describing the commit. * @@ -793,6 +882,12 @@ extern int nb_candidate_validate(struct nb_config *candidate); * nb_candidate_commit_abort() or committed using * nb_candidate_commit_apply(). * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * * Returns: * - NB_OK on success. * - NB_ERR_NO_CHANGES when the candidate is identical to the running @@ -803,10 +898,11 @@ extern int nb_candidate_validate(struct nb_config *candidate); * the candidate configuration. * - NB_ERR for other errors. */ -extern int nb_candidate_commit_prepare(struct nb_config *candidate, - enum nb_client client, const void *user, +extern int nb_candidate_commit_prepare(struct nb_context *context, + struct nb_config *candidate, const char *comment, - struct nb_transaction **transaction); + struct nb_transaction **transaction, + char *errmsg, size_t errmsg_len); /* * Abort a previously created configuration transaction, releasing all resources @@ -842,16 +938,13 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * take into account the results of the preparation phase of multiple managed * entities. * + * context + * Context of the northbound transaction. + * * candidate * Candidate configuration to commit. It's preserved regardless if the commit * operation fails or not. * - * client - * Northbound client performing the commit. - * - * user - * Northbound user performing the commit (can be NULL). - * * save_transaction * Specify whether the transaction should be recorded in the transactions log * or not. @@ -862,6 +955,12 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * transaction_id * Optional output parameter providing the ID of the committed transaction. * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * * Returns: * - NB_OK on success. * - NB_ERR_NO_CHANGES when the candidate is identical to the running @@ -872,10 +971,11 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * the candidate configuration. * - NB_ERR for other errors. */ -extern int nb_candidate_commit(struct nb_config *candidate, - enum nb_client client, const void *user, +extern int nb_candidate_commit(struct nb_context *context, + struct nb_config *candidate, bool save_transaction, const char *comment, - uint32_t *transaction_id); + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len); /* * Lock the running configuration. @@ -993,6 +1093,23 @@ extern int nb_notification_send(const char *xpath, struct list *arguments); extern void nb_running_set_entry(const struct lyd_node *dnode, void *entry); /* + * Move an entire tree of user pointer nodes. + * + * Suppose we have xpath A/B/C/D, with user pointers associated to C and D. We + * need to move B to be under Z, so the new xpath is Z/B/C/D. Because user + * pointers are indexed with their absolute path, We need to move all user + * pointers at and below B to their new absolute paths; this function does + * that. + * + * xpath_from + * base xpath of tree to move (A/B) + * + * xpath_to + * base xpath of new location of tree (Z/B) + */ +extern void nb_running_move_tree(const char *xpath_from, const char *xpath_to); + +/* * Unset the user pointer associated to a configuration node. * * This should be called by northbound 'destroy' callbacks in the NB_EV_APPLY @@ -1044,8 +1161,8 @@ extern void *nb_running_unset_entry(const struct lyd_node *dnode); * Returns: * User pointer if found, NULL otherwise. */ -extern void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, - bool abort_if_not_found); +extern void *nb_running_get_entry(const struct lyd_node *dnode, + const char *xpath, bool abort_if_not_found); /* * Return a human-readable string representing a northbound event. diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index d4467facaf..105fc83cef 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -46,23 +46,11 @@ struct debug nb_dbg_libyang = {0, "libyang debugging"}; struct nb_config *vty_shared_candidate_config; static struct thread_master *master; -static void vty_show_libyang_errors(struct vty *vty, struct ly_ctx *ly_ctx) +static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg) { - struct ly_err_item *ei; - const char *path; - - ei = ly_err_first(ly_ctx); - if (!ei) - return; - - for (; ei; ei = ei->next) - vty_out(vty, "%s\n", ei->msg); - - path = ly_errpath(ly_ctx); - if (path) - vty_out(vty, "YANG path: %s\n", path); - - ly_err_clean(ly_ctx, NULL); + vty_out(vty, "Error type: %s\n", nb_err_name(error)); + if (strlen(errmsg) > 0) + vty_out(vty, "Error description: %s\n", errmsg); } void nb_cli_enqueue_change(struct vty *vty, const char *xpath, @@ -158,24 +146,31 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) } if (error) { + char buf[BUFSIZ]; + /* * Failure to edit the candidate configuration should never * happen in practice, unless there's a bug in the code. When * that happens, log the error but otherwise ignore it. */ vty_out(vty, "%% Failed to edit configuration.\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); } /* Do an implicit "commit" when using the classic CLI mode. */ if (frr_get_cli_mode() == FRR_CLI_CLASSIC) { - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, - vty, false, NULL, NULL); + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; + + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, vty->candidate_config, + false, NULL, NULL, errmsg, + sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { - vty_out(vty, "%% Configuration failed: %s.\n\n", - nb_err_name(ret)); - vty_out(vty, - "Please check the logs for more details.\n"); + vty_out(vty, "%% Configuration failed.\n\n"); + vty_show_nb_errors(vty, ret, errmsg); /* Regenerate candidate for consistency. */ nb_config_replace(vty->candidate_config, running_config, @@ -217,20 +212,27 @@ void nb_cli_confirmed_commit_clean(struct vty *vty) int nb_cli_confirmed_commit_rollback(struct vty *vty) { + struct nb_context context = {}; uint32_t transaction_id; + char errmsg[BUFSIZ] = {0}; int ret; /* Perform the rollback. */ + context.client = NB_CLIENT_CLI; + context.user = vty; ret = nb_candidate_commit( - vty->confirmed_commit_rollback, NB_CLIENT_CLI, vty, true, + &context, vty->confirmed_commit_rollback, true, "Rollback to previous configuration - confirmed commit has timed out", - &transaction_id); + &transaction_id, errmsg, sizeof(errmsg)); if (ret == NB_OK) vty_out(vty, "Rollback performed successfully (Transaction ID #%u).\n", transaction_id); - else - vty_out(vty, "Failed to rollback to previous configuration.\n"); + else { + vty_out(vty, + "Failed to rollback to previous configuration.\n\n"); + vty_show_nb_errors(vty, ret, errmsg); + } return ret; } @@ -252,7 +254,9 @@ static int nb_cli_confirmed_commit_timeout(struct thread *thread) static int nb_cli_commit(struct vty *vty, bool force, unsigned int confirmed_timeout, char *comment) { + struct nb_context context = {}; uint32_t transaction_id = 0; + char errmsg[BUFSIZ] = {0}; int ret; /* Check if there's a pending confirmed commit. */ @@ -295,8 +299,11 @@ static int nb_cli_commit(struct vty *vty, bool force, &vty->t_confirmed_commit_timeout); } - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, vty, - true, comment, &transaction_id); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, vty->candidate_config, true, + comment, &transaction_id, errmsg, + sizeof(errmsg)); /* Map northbound return code to CLI return code. */ switch (ret) { @@ -312,9 +319,8 @@ static int nb_cli_commit(struct vty *vty, bool force, return CMD_SUCCESS; default: vty_out(vty, - "%% Failed to commit candidate configuration: %s.\n\n", - nb_err_name(ret)); - vty_out(vty, "Please check the logs for more details.\n"); + "%% Failed to commit candidate configuration.\n\n"); + vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; } } @@ -328,6 +334,7 @@ static int nb_cli_candidate_load_file(struct vty *vty, struct lyd_node *dnode; struct ly_ctx *ly_ctx; int ly_format; + char buf[BUFSIZ]; switch (format) { case NB_CFG_FMT_CMDS: @@ -350,7 +357,9 @@ static int nb_cli_candidate_load_file(struct vty *vty, flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_path() failed", __func__); vty_out(vty, "%% Failed to load configuration:\n\n"); - vty_show_libyang_errors(vty, ly_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, + sizeof(buf))); return CMD_WARNING; } if (translator @@ -371,7 +380,8 @@ static int nb_cli_candidate_load_file(struct vty *vty, != NB_OK) { vty_out(vty, "%% Failed to merge the loaded configuration:\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); return CMD_WARNING; } @@ -383,6 +393,7 @@ static int nb_cli_candidate_load_transaction(struct vty *vty, bool replace) { struct nb_config *loaded_config; + char buf[BUFSIZ]; loaded_config = nb_db_transaction_load(transaction_id); if (!loaded_config) { @@ -397,7 +408,8 @@ static int nb_cli_candidate_load_transaction(struct vty *vty, != NB_OK) { vty_out(vty, "%% Failed to merge the loaded configuration:\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); return CMD_WARNING; } @@ -690,13 +702,18 @@ DEFPY (config_commit_check, "Commit changes into the running configuration\n" "Check if the configuration changes are valid\n") { + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; int ret; - ret = nb_candidate_validate(vty->candidate_config); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_validate(&context, vty->candidate_config, errmsg, + sizeof(errmsg)); if (ret != NB_OK) { vty_out(vty, "%% Failed to validate candidate configuration.\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; } @@ -1530,8 +1547,10 @@ DEFPY (show_yang_module_translator, static int nb_cli_rollback_configuration(struct vty *vty, uint32_t transaction_id) { + struct nb_context context = {}; struct nb_config *candidate; char comment[80]; + char errmsg[BUFSIZ] = {0}; int ret; candidate = nb_db_transaction_load(transaction_id); @@ -1544,8 +1563,10 @@ static int nb_cli_rollback_configuration(struct vty *vty, snprintf(comment, sizeof(comment), "Rollback to transaction %u", transaction_id); - ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, vty, true, comment, - NULL); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, candidate, true, comment, NULL, + errmsg, sizeof(errmsg)); nb_config_free(candidate); switch (ret) { case NB_OK: @@ -1558,7 +1579,7 @@ static int nb_cli_rollback_configuration(struct vty *vty, return CMD_WARNING; default: vty_out(vty, "%% Rollback failed.\n\n"); - vty_out(vty, "Please check the logs for more details.\n"); + vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; } } diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index 2fc3c81cf2..a3aaf02f08 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -285,8 +285,10 @@ frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) { + struct nb_context context = {}; struct nb_config *candidate; struct cdb_iter_args iter_args; + char errmsg[BUFSIZ] = {0}; int ret; candidate = nb_config_dup(running_config); @@ -321,24 +323,21 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) * required to apply them. */ transaction = NULL; - ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_CONFD, NULL, - NULL, &transaction); + context.client = NB_CLIENT_CONFD; + ret = nb_candidate_commit_prepare(&context, candidate, NULL, + &transaction, errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { enum confd_errcode errcode; - const char *errmsg; switch (ret) { case NB_ERR_LOCKED: errcode = CONFD_ERRCODE_IN_USE; - errmsg = "Configuration is locked by another process"; break; case NB_ERR_RESOURCE: errcode = CONFD_ERRCODE_RESOURCE_DENIED; - errmsg = "Failed do allocate resources"; break; default: - errcode = CONFD_ERRCODE_INTERNAL; - errmsg = "Internal error"; + errcode = CONFD_ERRCODE_APPLICATION; break; } @@ -612,7 +611,7 @@ static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp) { struct nb_node *nb_node; - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct yang_data *data; confd_value_t v; const void *list_entry = NULL; @@ -650,7 +649,7 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next) { struct nb_node *nb_node; - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct yang_data *data; const void *parent_list_entry, *nb_next; confd_value_t v[LIST_MAXKEYS]; @@ -758,8 +757,8 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, { struct nb_node *nb_node; const struct lys_node *child; - char xpath[BUFSIZ]; - char xpath_child[XPATH_MAXLEN]; + char xpath[XPATH_MAXLEN]; + char xpath_child[XPATH_MAXLEN * 2]; struct list *elements; struct yang_data *data; const void *list_entry; @@ -832,7 +831,7 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next) { - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct nb_node *nb_node; struct list *elements; const void *parent_list_entry; @@ -916,7 +915,7 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, /* Loop through list child nodes. */ LY_TREE_FOR (nb_node->snode->child, child) { struct nb_node *nb_node_child = child->priv; - char xpath_child[XPATH_MAXLEN]; + char xpath_child[XPATH_MAXLEN * 2]; confd_value_t *v; if (nvalues > CONFD_MAX_CHILD_NODES) @@ -1059,7 +1058,7 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo, struct xml_tag *name, confd_hkeypath_t *kp, confd_tag_value_t *params, int nparams) { - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct nb_node *nb_node; struct list *input; struct list *output; @@ -1091,7 +1090,7 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo, /* Process input nodes. */ for (int i = 0; i < nparams; i++) { - char xpath_input[BUFSIZ]; + char xpath_input[XPATH_MAXLEN * 2]; char value_str[YANG_VALUE_MAXLEN]; snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath, diff --git a/lib/northbound_db.c b/lib/northbound_db.c index 598805b961..244e760b2b 100644 --- a/lib/northbound_db.c +++ b/lib/northbound_db.c @@ -86,7 +86,7 @@ int nb_db_transaction_save(const struct nb_transaction *transaction, if (!ss) goto exit; - client_name = nb_client_name(transaction->client); + client_name = nb_client_name(transaction->context->client); /* Always record configurations in the XML format. */ if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML, LYP_FORMAT | LYP_WITHSIBLINGS) diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 2962a977eb..83d7e0ce95 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -175,14 +175,13 @@ class NorthboundImpl void HandleGetCapabilities(RpcState<frr::GetCapabilitiesRequest, frr::GetCapabilitiesResponse> *tag) { - if (nb_dbg_client_grpc) - zlog_debug("received RPC GetCapabilities()"); - switch (tag->state) { case CREATE: REQUEST_RPC(GetCapabilities); tag->state = PROCESS; case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC GetCapabilities()"); // Response: string frr_version = 1; tag->response.set_frr_version(FRR_VERSION); @@ -298,14 +297,14 @@ class NorthboundImpl void HandleCreateCandidate(RpcState<frr::CreateCandidateRequest, frr::CreateCandidateResponse> *tag) { - if (nb_dbg_client_grpc) - zlog_debug("received RPC CreateCandidate()"); - switch (tag->state) { case CREATE: REQUEST_RPC(CreateCandidate); tag->state = PROCESS; case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC CreateCandidate()"); + struct candidate *candidate = create_candidate(); if (!candidate) { tag->responder.Finish( @@ -672,15 +671,22 @@ class NorthboundImpl // Execute the user request. + struct nb_context context = {}; + context.client = NB_CLIENT_GRPC; + char errmsg[BUFSIZ] = {0}; + switch (phase) { case frr::CommitRequest::VALIDATE: - ret = nb_candidate_validate(candidate->config); + ret = nb_candidate_validate( + &context, candidate->config, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::PREPARE: ret = nb_candidate_commit_prepare( - candidate->config, NB_CLIENT_GRPC, NULL, + &context, candidate->config, comment.c_str(), - &candidate->transaction); + &candidate->transaction, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::ABORT: nb_candidate_commit_abort( @@ -693,71 +699,51 @@ class NorthboundImpl break; case frr::CommitRequest::ALL: ret = nb_candidate_commit( - candidate->config, NB_CLIENT_GRPC, NULL, - true, comment.c_str(), &transaction_id); + &context, candidate->config, true, + comment.c_str(), &transaction_id, + errmsg, sizeof(errmsg)); break; } - // Map northbound error codes to gRPC error codes. + // Map northbound error codes to gRPC status codes. + grpc::Status status; switch (ret) { + case NB_OK: + status = grpc::Status::OK; + break; case NB_ERR_NO_CHANGES: - tag->responder.Finish( - tag->response, - grpc::Status( - grpc::StatusCode::ABORTED, - "No configuration changes detected"), - tag); - tag->state = FINISH; - return; + status = grpc::Status(grpc::StatusCode::ABORTED, + errmsg); + break; case NB_ERR_LOCKED: - tag->responder.Finish( - tag->response, - grpc::Status( - grpc::StatusCode::UNAVAILABLE, - "There's already a transaction in progress"), - tag); - tag->state = FINISH; - return; + status = grpc::Status( + grpc::StatusCode::UNAVAILABLE, errmsg); + break; case NB_ERR_VALIDATION: - tag->responder.Finish( - tag->response, - grpc::Status(grpc::StatusCode:: - INVALID_ARGUMENT, - "Validation error"), - tag); - tag->state = FINISH; - return; + status = grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + errmsg); + break; case NB_ERR_RESOURCE: - tag->responder.Finish( - tag->response, - grpc::Status( - grpc::StatusCode:: - RESOURCE_EXHAUSTED, - "Failed do allocate resources"), - tag); - tag->state = FINISH; - return; + status = grpc::Status( + grpc::StatusCode::RESOURCE_EXHAUSTED, + errmsg); + break; case NB_ERR: - tag->responder.Finish( - tag->response, - grpc::Status(grpc::StatusCode::INTERNAL, - "Internal error"), - tag); - tag->state = FINISH; - return; default: + status = grpc::Status( + grpc::StatusCode::INTERNAL, errmsg); break; } + if (ret == NB_OK) { + // Response: uint32 transaction_id = 1; + if (transaction_id) + tag->response.set_transaction_id( + transaction_id); + } - // Response: uint32 transaction_id = 1; - if (transaction_id) - tag->response.set_transaction_id( - transaction_id); - - tag->responder.Finish(tag->response, grpc::Status::OK, - tag); + tag->responder.Finish(tag->response, status, tag); tag->state = FINISH; - break; } case FINISH: @@ -769,9 +755,6 @@ class NorthboundImpl HandleListTransactions(RpcState<frr::ListTransactionsRequest, frr::ListTransactionsResponse> *tag) { - if (nb_dbg_client_grpc) - zlog_debug("received RPC ListTransactions()"); - switch (tag->state) { case CREATE: REQUEST_RPC_STREAMING(ListTransactions); @@ -781,6 +764,9 @@ class NorthboundImpl tag->context); tag->state = PROCESS; case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC ListTransactions()"); + auto list = static_cast<std::list<std::tuple< int, std::string, std::string, std::string>> *>( tag->context); @@ -889,14 +875,14 @@ class NorthboundImpl void HandleLockConfig( RpcState<frr::LockConfigRequest, frr::LockConfigResponse> *tag) { - if (nb_dbg_client_grpc) - zlog_debug("received RPC LockConfig()"); - switch (tag->state) { case CREATE: REQUEST_RPC(LockConfig); tag->state = PROCESS; case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC LockConfig()"); + if (nb_running_lock(NB_CLIENT_GRPC, NULL)) { tag->responder.Finish( tag->response, @@ -922,14 +908,14 @@ class NorthboundImpl void HandleUnlockConfig(RpcState<frr::UnlockConfigRequest, frr::UnlockConfigResponse> *tag) { - if (nb_dbg_client_grpc) - zlog_debug("received RPC UnlockConfig()"); - switch (tag->state) { case CREATE: REQUEST_RPC(UnlockConfig); tag->state = PROCESS; case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC UnlockConfig()"); + if (nb_running_unlock(NB_CLIENT_GRPC, NULL)) { tag->responder.Finish( tag->response, diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index b94c939763..500203173c 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -245,7 +245,9 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, sr_change_oper_t sr_op; sr_val_t *sr_old_val, *sr_new_val; char xpath[XPATH_MAXLEN]; + struct nb_context context = {}; struct nb_config *candidate; + char errmsg[BUFSIZ] = {0}; snprintf(xpath, sizeof(xpath), "/%s:*", module_name); ret = sr_get_changes_iter(session, xpath, &it); @@ -276,21 +278,33 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, } transaction = NULL; + context.client = NB_CLIENT_SYSREPO; if (startup_config) { /* * sysrepod sends the entire startup configuration using a * single event (SR_EV_ENABLED). This means we need to perform * the full two-phase commit protocol in one go here. */ - ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, NULL, - true, NULL, NULL); + ret = nb_candidate_commit(&context, candidate, true, NULL, NULL, + errmsg, sizeof(errmsg)); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) + flog_warn( + EC_LIB_LIBSYSREPO, + "%s: failed to apply startup configuration: %s (%s)", + __func__, nb_err_name(ret), errmsg); } else { /* * Validate the configuration changes and allocate all resources * required to apply them. */ - ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_SYSREPO, - NULL, NULL, &transaction); + ret = nb_candidate_commit_prepare(&context, candidate, NULL, + &transaction, errmsg, + sizeof(errmsg)); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) + flog_warn( + EC_LIB_LIBSYSREPO, + "%s: failed to prepare configuration transaction: %s (%s)", + __func__, nb_err_name(ret), errmsg); } /* Map northbound return code to sysrepo return code. */ diff --git a/lib/plist.c b/lib/plist.c index d18d51618a..981e86e2ac 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -178,7 +178,7 @@ static void prefix_list_free(struct prefix_list *plist) XFREE(MTYPE_PREFIX_LIST, plist); } -static struct prefix_list_entry *prefix_list_entry_new(void) +struct prefix_list_entry *prefix_list_entry_new(void) { struct prefix_list_entry *new; @@ -187,7 +187,7 @@ static struct prefix_list_entry *prefix_list_entry_new(void) return new; } -static void prefix_list_entry_free(struct prefix_list_entry *pentry) +void prefix_list_entry_free(struct prefix_list_entry *pentry) { XFREE(MTYPE_PREFIX_LIST_ENTRY, pentry); } @@ -279,7 +279,7 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf, return plist; } -static struct prefix_list *prefix_list_get(afi_t afi, int orf, const char *name) +struct prefix_list *prefix_list_get(afi_t afi, int orf, const char *name) { struct prefix_list *plist; @@ -294,7 +294,7 @@ static void prefix_list_trie_del(struct prefix_list *plist, struct prefix_list_entry *pentry); /* Delete prefix-list from prefix_list_master and free it. */ -static void prefix_list_delete(struct prefix_list *plist) +void prefix_list_delete(struct prefix_list *plist) { struct prefix_list_list *list; struct prefix_master *master; @@ -381,7 +381,7 @@ void prefix_list_delete_hook(void (*func)(struct prefix_list *plist)) } /* Calculate new sequential number. */ -static int64_t prefix_new_seq_get(struct prefix_list *plist) +int64_t prefix_new_seq_get(struct prefix_list *plist) { int64_t maxseq; int64_t newseq; @@ -411,7 +411,7 @@ static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist, return NULL; } -static struct prefix_list_entry * +struct prefix_list_entry * prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix, enum prefix_list_type type, int64_t seq, int le, int ge) @@ -502,9 +502,9 @@ static void prefix_list_trie_del(struct prefix_list *plist, } -static void prefix_list_entry_delete(struct prefix_list *plist, - struct prefix_list_entry *pentry, - int update_list) +void prefix_list_entry_delete(struct prefix_list *plist, + struct prefix_list_entry *pentry, + int update_list) { if (plist == NULL || pentry == NULL) return; @@ -646,6 +646,133 @@ static void prefix_list_entry_add(struct prefix_list *plist, plist->master->recent = plist; } +/** + * Prefix list entry update start procedure: + * Remove entry from previosly installed master list, tries and notify + * observers. + * + * \param[in] ple prefix list entry. + */ +void prefix_list_entry_update_start(struct prefix_list_entry *ple) +{ + struct prefix_list *pl = ple->pl; + + /* Not installed, nothing to do. */ + if (!ple->installed) + return; + + prefix_list_trie_del(pl, ple); + + /* List manipulation: shameless copy from `prefix_list_entry_delete`. */ + if (ple->prev) + ple->prev->next = ple->next; + else + pl->head = ple->next; + if (ple->next) + ple->next->prev = ple->prev; + else + pl->tail = ple->prev; + + route_map_notify_pentry_dependencies(pl->name, ple, + RMAP_EVENT_PLIST_DELETED); + pl->count--; + + route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_DELETED); + if (pl->master->delete_hook) + (*pl->master->delete_hook)(pl); + + if (pl->head || pl->tail || pl->desc) + pl->master->recent = pl; + + ple->installed = false; +} + +/** + * Prefix list entry update finish procedure: + * Add entry back master list, to the trie, notify observers and call master + * hook. + * + * \param[in] ple prefix list entry. + */ +void prefix_list_entry_update_finish(struct prefix_list_entry *ple) +{ + struct prefix_list *pl = ple->pl; + struct prefix_list_entry *point; + + /* Already installed, nothing to do. */ + if (ple->installed) + return; + + /* + * Check if the entry is installable: + * We can only install entry if at least the prefix is provided (IPv4 + * or IPv6). + */ + if (ple->prefix.family != AF_INET && ple->prefix.family != AF_INET6) + return; + + /* List manipulation: shameless copy from `prefix_list_entry_add`. */ + if (pl->tail && ple->seq > pl->tail->seq) + point = NULL; + else { + /* Check insert point. */ + for (point = pl->head; point; point = point->next) + if (point->seq >= ple->seq) + break; + } + + /* In case of this is the first element of the list. */ + ple->next = point; + + if (point) { + if (point->prev) + point->prev->next = ple; + else + pl->head = ple; + + ple->prev = point->prev; + point->prev = ple; + } else { + if (pl->tail) + pl->tail->next = ple; + else + pl->head = ple; + + ple->prev = pl->tail; + pl->tail = ple; + } + + prefix_list_trie_add(pl, ple); + pl->count++; + + route_map_notify_pentry_dependencies(pl->name, ple, + RMAP_EVENT_PLIST_ADDED); + + /* Run hook function. */ + if (pl->master->add_hook) + (*pl->master->add_hook)(pl); + + route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_ADDED); + pl->master->recent = pl; + + ple->installed = true; +} + +/** + * Same as `prefix_list_entry_delete` but without `free()`ing the list if its + * empty. + * + * \param[in] ple prefix list entry. + */ +void prefix_list_entry_delete2(struct prefix_list_entry *ple) +{ + /* Does the boiler plate list removal and entry removal notification. */ + prefix_list_entry_update_start(ple); + + /* Effective `free()` memory. */ + prefix_list_entry_free(ple); +} + /* Return string of prefix_list_type. */ static const char *prefix_list_type_str(struct prefix_list_entry *pentry) { @@ -832,280 +959,6 @@ prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new) return NULL; } -static int vty_invalid_prefix_range(struct vty *vty, const char *prefix) -{ - vty_out(vty, - "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value\n", - prefix); - return CMD_WARNING_CONFIG_FAILED; -} - -static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, - const char *seq, const char *typestr, - const char *prefix, const char *ge, - const char *le) -{ - int ret; - enum prefix_list_type type; - struct prefix_list *plist; - struct prefix_list_entry *pentry; - struct prefix_list_entry *dup; - struct prefix p, p_tmp; - bool any = false; - int64_t seqnum = -1; - int lenum = 0; - int genum = 0; - - if (name == NULL || prefix == NULL || typestr == NULL) { - vty_out(vty, "%% Missing prefix or type\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Sequential number. */ - if (seq) - seqnum = (int64_t)atol(seq); - - /* ge and le number */ - if (ge) - genum = atoi(ge); - if (le) - lenum = atoi(le); - - /* Check filter type. */ - if (strncmp("permit", typestr, 1) == 0) - type = PREFIX_PERMIT; - else if (strncmp("deny", typestr, 1) == 0) - type = PREFIX_DENY; - else { - vty_out(vty, "%% prefix type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* "any" is special token for matching any IPv4 addresses. */ - switch (afi) { - case AFI_IP: - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv4("0.0.0.0/0", - (struct prefix_ipv4 *)&p); - genum = 0; - lenum = IPV4_MAX_BITLEN; - any = true; - } else - ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv4 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* make a copy to verify prefix matches mask length */ - prefix_copy(&p_tmp, &p); - apply_mask_ipv4((struct prefix_ipv4 *)&p_tmp); - - break; - case AFI_IP6: - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p); - genum = 0; - lenum = IPV6_MAX_BITLEN; - any = true; - } else - ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* make a copy to verify prefix matches mask length */ - prefix_copy(&p_tmp, &p); - apply_mask_ipv6((struct prefix_ipv6 *)&p_tmp); - - break; - case AFI_L2VPN: - default: - vty_out(vty, "%% Unrecognized AFI (%d)\n", afi); - return CMD_WARNING_CONFIG_FAILED; - } - - /* If prefix has bits not under the mask, adjust it to fit */ - if (!prefix_same(&p_tmp, &p)) { - char buf[PREFIX2STR_BUFFER]; - char buf_tmp[PREFIX2STR_BUFFER]; - prefix2str(&p, buf, sizeof(buf)); - prefix2str(&p_tmp, buf_tmp, sizeof(buf_tmp)); - vty_out(vty, - "%% Prefix-list %s prefix changed from %s to %s to match length\n", - name, buf, buf_tmp); - zlog_info( - "Prefix-list %s prefix changed from %s to %s to match length", - name, buf, buf_tmp); - p = p_tmp; - } - - /* ge and le check. */ - if (genum && (genum <= p.prefixlen)) - return vty_invalid_prefix_range(vty, prefix); - - if (lenum && (lenum < p.prefixlen)) - return vty_invalid_prefix_range(vty, prefix); - - if (lenum && (genum > lenum)) - return vty_invalid_prefix_range(vty, prefix); - - if (genum && (lenum == (afi == AFI_IP ? 32 : 128))) - lenum = 0; - - /* Get prefix_list with name. */ - plist = prefix_list_get(afi, 0, name); - - /* Make prefix entry. */ - pentry = prefix_list_entry_make(&p, type, seqnum, lenum, genum, any); - - /* Check same policy. */ - dup = prefix_entry_dup_check(plist, pentry); - - if (dup) { - prefix_list_entry_free(pentry); - return CMD_SUCCESS; - } - - /* Install new filter to the access_list. */ - prefix_list_entry_add(plist, pentry); - - return CMD_SUCCESS; -} - -static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi, - const char *name, const char *seq, - const char *typestr, const char *prefix, - const char *ge, const char *le) -{ - int ret; - enum prefix_list_type type; - struct prefix_list *plist; - struct prefix_list_entry *pentry; - struct prefix p; - int64_t seqnum = -1; - int lenum = 0; - int genum = 0; - - /* Check prefix list name. */ - plist = prefix_list_lookup(afi, name); - if (!plist) { - vty_out(vty, "%% Can't find specified prefix-list\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Only prefix-list name specified, delete the entire prefix-list. */ - if (seq == NULL && typestr == NULL && prefix == NULL && ge == NULL - && le == NULL) { - prefix_list_delete(plist); - return CMD_SUCCESS; - } - - /* Check sequence number. */ - if (seq) - seqnum = (int64_t)atol(seq); - - /* Sequence number specified, but nothing else. */ - if (seq && typestr == NULL && prefix == NULL && ge == NULL - && le == NULL) { - pentry = prefix_seq_check(plist, seqnum); - - if (pentry == NULL) { - vty_out(vty, - "%% Can't find prefix-list %s with sequence number %" PRIu64 "\n", - name, seqnum); - return CMD_WARNING_CONFIG_FAILED; - } - - prefix_list_entry_delete(plist, pentry, 1); - return CMD_SUCCESS; - } - - /* ge and le number */ - if (ge) - genum = atoi(ge); - if (le) - lenum = atoi(le); - - /* We must have, at a minimum, both the type and prefix here */ - if ((typestr == NULL) || (prefix == NULL)) - return CMD_WARNING_CONFIG_FAILED; - - /* Check of filter type. */ - if (strncmp("permit", typestr, 1) == 0) - type = PREFIX_PERMIT; - else if (strncmp("deny", typestr, 1) == 0) - type = PREFIX_DENY; - else { - vty_out(vty, "%% prefix type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* "any" is special token for matching any IPv4 addresses. */ - if (afi == AFI_IP) { - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv4("0.0.0.0/0", - (struct prefix_ipv4 *)&p); - genum = 0; - lenum = IPV4_MAX_BITLEN; - } else - ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv4 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } else if (afi == AFI_IP6) { - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p); - genum = 0; - lenum = IPV6_MAX_BITLEN; - } else - ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - /* Lookup prefix entry. */ - pentry = - prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum); - - if (pentry == NULL) { - vty_out(vty, "%% Can't find specified prefix-list\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Install new filter to the access_list. */ - prefix_list_entry_delete(plist, pentry, 1); - - return CMD_SUCCESS; -} - -static int vty_prefix_list_desc_unset(struct vty *vty, afi_t afi, - const char *name) -{ - struct prefix_list *plist; - - plist = prefix_list_lookup(afi, name); - if (!plist) { - vty_out(vty, "%% Can't find specified prefix-list\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - XFREE(MTYPE_TMP, plist->desc); - - if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) - prefix_list_delete(plist); - - return CMD_SUCCESS; -} - enum display_type { normal_display, summary_display, @@ -1349,72 +1202,6 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, #include "lib/plist_clippy.c" #endif -DEFPY (ip_prefix_list, - ip_prefix_list_cmd, - "ip prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|A.B.C.D/M$dest [{ge (0-32)|le (0-32)}]>", - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" - "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n") -{ - return vty_prefix_list_install(vty, AFI_IP, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ip_prefix_list, - no_ip_prefix_list_cmd, - "no ip prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|A.B.C.D/M$dest [{ge (0-32)|le (0-32)}]>", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" - "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY(no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, - "no ip prefix-list WORD seq (1-4294967295)", - NO_STR IP_STR PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, seq_str, - NULL, NULL, NULL, NULL); -} - -DEFPY (no_ip_prefix_list_all, - no_ip_prefix_list_all_cmd, - "no ip prefix-list WORD", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, NULL, NULL, - NULL, NULL, NULL); -} - DEFPY (ip_prefix_list_sequence_number, ip_prefix_list_sequence_number_cmd, "[no] ip prefix-list sequence-number", @@ -1427,56 +1214,6 @@ DEFPY (ip_prefix_list_sequence_number, return CMD_SUCCESS; } -DEFUN (ip_prefix_list_description, - ip_prefix_list_description_cmd, - "ip prefix-list WORD description LINE...", - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - int idx_word = 2; - int idx_line = 4; - struct prefix_list *plist; - - plist = prefix_list_get(AFI_IP, 0, argv[idx_word]->arg); - - if (plist->desc) { - XFREE(MTYPE_TMP, plist->desc); - plist->desc = NULL; - } - plist->desc = argv_concat(argv, argc, idx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_ip_prefix_list_description, - no_ip_prefix_list_description_cmd, - "no ip prefix-list WORD description", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n") -{ - int idx_word = 3; - return vty_prefix_list_desc_unset(vty, AFI_IP, argv[idx_word]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_ip_prefix_list_description_comment, - no_ip_prefix_list_description_comment_cmd, - "no ip prefix-list WORD description LINE...", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - return no_ip_prefix_list_description(self, vty, argc, argv); -} DEFPY (show_ip_prefix_list, show_ip_prefix_list_cmd, @@ -1554,61 +1291,6 @@ DEFPY (clear_ip_prefix_list, return vty_clear_prefix_list(vty, AFI_IP, prefix_list, prefix_str); } -DEFPY (ipv6_prefix_list, - ipv6_prefix_list_cmd, - "ipv6 prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|X:X::X:X/M$dest [{ge (0-128)|le (0-128)}]>", - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"::0/0 le 128\"\n" - "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n") -{ - return vty_prefix_list_install(vty, AFI_IP6, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ipv6_prefix_list, - no_ipv6_prefix_list_cmd, - "no ipv6 prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|X:X::X:X/M$dest [{ge (0-128)|le (0-128)}]>", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"::0/0 le 128\"\n" - "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP6, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ipv6_prefix_list_all, - no_ipv6_prefix_list_all_cmd, - "no ipv6 prefix-list WORD", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP6, prefix_list, NULL, NULL, - NULL, NULL, NULL); -} - DEFPY (ipv6_prefix_list_sequence_number, ipv6_prefix_list_sequence_number_cmd, "[no] ipv6 prefix-list sequence-number", @@ -1621,58 +1303,6 @@ DEFPY (ipv6_prefix_list_sequence_number, return CMD_SUCCESS; } -DEFUN (ipv6_prefix_list_description, - ipv6_prefix_list_description_cmd, - "ipv6 prefix-list WORD description LINE...", - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - int idx_word = 2; - int iddx_line = 4; - struct prefix_list *plist; - - plist = prefix_list_get(AFI_IP6, 0, argv[idx_word]->arg); - - if (plist->desc) { - XFREE(MTYPE_TMP, plist->desc); - plist->desc = NULL; - } - plist->desc = argv_concat(argv, argc, iddx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_prefix_list_description, - no_ipv6_prefix_list_description_cmd, - "no ipv6 prefix-list WORD description", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n") -{ - int idx_word = 3; - return vty_prefix_list_desc_unset(vty, AFI_IP6, argv[idx_word]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_ipv6_prefix_list_description_comment, - no_ipv6_prefix_list_description_comment_cmd, - "no ipv6 prefix-list WORD description LINE...", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - return no_ipv6_prefix_list_description(self, vty, argc, argv); -} - - DEFPY (show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]]", @@ -1749,104 +1379,6 @@ DEFPY (clear_ipv6_prefix_list, return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str); } -/* Configuration write function. */ -static int config_write_prefix_afi(afi_t afi, struct vty *vty) -{ - struct prefix_list *plist; - struct prefix_list_entry *pentry; - struct prefix_master *master; - int write = 0; - - master = prefix_master_get(afi, 0); - if (master == NULL) - return 0; - - if (!master->seqnum) { - vty_out(vty, "no ip%s prefix-list sequence-number\n", - afi == AFI_IP ? "" : "v6"); - vty_out(vty, "!\n"); - } - - for (plist = master->num.head; plist; plist = plist->next) { - if (plist->desc) { - vty_out(vty, "ip%s prefix-list %s description %s\n", - afi == AFI_IP ? "" : "v6", plist->name, - plist->desc); - write++; - } - - for (pentry = plist->head; pentry; pentry = pentry->next) { - vty_out(vty, "ip%s prefix-list %s ", - afi == AFI_IP ? "" : "v6", plist->name); - - if (master->seqnum) - vty_out(vty, "seq %" PRId64 " ", pentry->seq); - - vty_out(vty, "%s ", prefix_list_type_str(pentry)); - - if (pentry->any) - vty_out(vty, "any"); - else { - struct prefix *p = &pentry->prefix; - char buf[BUFSIZ]; - - vty_out(vty, "%s/%d", - inet_ntop(p->family, p->u.val, buf, - BUFSIZ), - p->prefixlen); - - if (pentry->ge) - vty_out(vty, " ge %d", pentry->ge); - if (pentry->le) - vty_out(vty, " le %d", pentry->le); - } - vty_out(vty, "\n"); - write++; - } - /* vty_out (vty, "!\n"); */ - } - - for (plist = master->str.head; plist; plist = plist->next) { - if (plist->desc) { - vty_out(vty, "ip%s prefix-list %s description %s\n", - afi == AFI_IP ? "" : "v6", plist->name, - plist->desc); - write++; - } - - for (pentry = plist->head; pentry; pentry = pentry->next) { - vty_out(vty, "ip%s prefix-list %s ", - afi == AFI_IP ? "" : "v6", plist->name); - - if (master->seqnum) - vty_out(vty, "seq %" PRId64 " ", pentry->seq); - - vty_out(vty, "%s", prefix_list_type_str(pentry)); - - if (pentry->any) - vty_out(vty, " any"); - else { - struct prefix *p = &pentry->prefix; - char buf[BUFSIZ]; - - vty_out(vty, " %s/%d", - inet_ntop(p->family, p->u.val, buf, - BUFSIZ), - p->prefixlen); - - if (pentry->ge) - vty_out(vty, " ge %d", pentry->ge); - if (pentry->le) - vty_out(vty, " le %d", pentry->le); - } - vty_out(vty, "\n"); - write++; - } - } - - return write; -} - struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist, uint8_t init_flag, uint8_t permit_flag, uint8_t deny_flag) @@ -2042,21 +1574,13 @@ static void prefix_list_reset_afi(afi_t afi, int orf) master->recent = NULL; } - -static int config_write_prefix_ipv4(struct vty *vty); /* Prefix-list node. */ static struct cmd_node prefix_node = { .name = "ipv4 prefix list", .node = PREFIX_NODE, .prompt = "", - .config_write = config_write_prefix_ipv4, }; -static int config_write_prefix_ipv4(struct vty *vty) -{ - return config_write_prefix_afi(AFI_IP, vty); -} - static void plist_autocomplete_afi(afi_t afi, vector comps, struct cmd_token *token) { @@ -2090,16 +1614,6 @@ static void prefix_list_init_ipv4(void) { install_node(&prefix_node); - install_element(CONFIG_NODE, &ip_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd); - - install_element(CONFIG_NODE, &ip_prefix_list_description_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_description_cmd); - install_element(CONFIG_NODE, - &no_ip_prefix_list_description_comment_cmd); - install_element(CONFIG_NODE, &ip_prefix_list_sequence_number_cmd); install_element(VIEW_NODE, &show_ip_prefix_list_cmd); @@ -2110,33 +1624,17 @@ static void prefix_list_init_ipv4(void) install_element(ENABLE_NODE, &clear_ip_prefix_list_cmd); } -static int config_write_prefix_ipv6(struct vty *vty); /* Prefix-list node. */ static struct cmd_node prefix_ipv6_node = { .name = "ipv6 prefix list", .node = PREFIX_IPV6_NODE, .prompt = "", - .config_write = config_write_prefix_ipv6, }; -static int config_write_prefix_ipv6(struct vty *vty) -{ - return config_write_prefix_afi(AFI_IP6, vty); -} - static void prefix_list_init_ipv6(void) { install_node(&prefix_ipv6_node); - install_element(CONFIG_NODE, &ipv6_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd); - - install_element(CONFIG_NODE, &ipv6_prefix_list_description_cmd); - install_element(CONFIG_NODE, &no_ipv6_prefix_list_description_cmd); - install_element(CONFIG_NODE, - &no_ipv6_prefix_list_description_comment_cmd); - install_element(CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd); install_element(VIEW_NODE, &show_ipv6_prefix_list_cmd); diff --git a/lib/plist.h b/lib/plist.h index ba2846d74a..57eb763a68 100644 --- a/lib/plist.h +++ b/lib/plist.h @@ -79,6 +79,20 @@ extern void prefix_bgp_orf_remove_all(afi_t, char *); extern int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name, bool use_json); +extern struct prefix_list *prefix_list_get(afi_t afi, int orf, + const char *name); +extern void prefix_list_delete(struct prefix_list *plist); +extern int64_t prefix_new_seq_get(struct prefix_list *plist); + +extern struct prefix_list_entry *prefix_list_entry_new(void); +extern void prefix_list_entry_delete(struct prefix_list *plist, + struct prefix_list_entry *pentry, + int update_list); +extern struct prefix_list_entry * +prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix, + enum prefix_list_type type, int64_t seq, int le, + int ge); + #ifdef __cplusplus } #endif diff --git a/lib/plist_int.h b/lib/plist_int.h index ec8bbe1315..5e0beabbc6 100644 --- a/lib/plist_int.h +++ b/lib/plist_int.h @@ -65,13 +65,23 @@ struct prefix_list_entry { unsigned long refcnt; unsigned long hitcnt; + struct prefix_list *pl; + struct prefix_list_entry *next; struct prefix_list_entry *prev; /* up the chain for best match search */ struct prefix_list_entry *next_best; + + /* Flag to track trie/list installation status. */ + bool installed; }; +extern void prefix_list_entry_free(struct prefix_list_entry *pentry); +extern void prefix_list_entry_delete2(struct prefix_list_entry *ple); +extern void prefix_list_entry_update_start(struct prefix_list_entry *ple); +extern void prefix_list_entry_update_finish(struct prefix_list_entry *ple); + #ifdef __cplusplus } #endif diff --git a/lib/routemap.c b/lib/routemap.c index 210512212d..3d69a3495a 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -2747,7 +2747,12 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); tmp_dep_data.rname = rname; dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); - dep_data->refcnt--; + + if (!dep_data) + goto out; + + if (dep_data->refcnt) + dep_data->refcnt--; if (!dep_data->refcnt) { ret_dep_data = hash_release(dep->dep_rmap_hash, diff --git a/lib/subdir.am b/lib/subdir.am index b2f3e7c5de..57b2cea832 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -22,6 +22,8 @@ lib_libfrr_la_SOURCES = \ lib/distribute.c \ lib/ferr.c \ lib/filter.c \ + lib/filter_cli.c \ + lib/filter_nb.c \ lib/frrcu.c \ lib/frrlua.c \ lib/frr_pthread.c \ @@ -127,6 +129,7 @@ nodist_lib_libfrr_la_SOURCES = \ vtysh_scan += \ lib/distribute.c \ lib/filter.c \ + lib/filter_cli.c \ lib/if.c \ lib/if_rmap.c \ lib/keychain.c \ @@ -148,6 +151,7 @@ endif clippy_scan += \ lib/if.c \ + lib/filter_cli.c \ lib/log_vty.c \ lib/nexthop_group.c \ lib/northbound_cli.c \ @@ -1082,8 +1082,8 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args) case NB_EV_VALIDATE: vrfp = nb_running_get_entry(args->dnode, NULL, true); if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) { - zlog_debug("%s Only inactive VRFs can be deleted", - __func__); + snprintf(args->errmsg, args->errmsg_len, + "Only inactive VRFs can be deleted"); return NB_ERR_VALIDATION; } break; @@ -2349,12 +2349,18 @@ static void vty_read_file(struct nb_config *config, FILE *confp) * reading the configuration file. */ if (config == NULL) { - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, - vty, true, "Read configuration file", - NULL); + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; + + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, vty->candidate_config, true, + "Read configuration file", NULL, + errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - zlog_err("%s: failed to read configuration file.", - __func__); + zlog_err( + "%s: failed to read configuration file: %s (%s)", + __func__, nb_err_name(ret), errmsg); } vty_close(vty); @@ -43,7 +43,7 @@ extern "C" { #define VTY_MAXHIST 20 #define VTY_MAXDEPTH 8 -#define VTY_MAXCFGCHANGES 8 +#define VTY_MAXCFGCHANGES 16 struct vty_error { char error_buf[VTY_BUFSIZ]; diff --git a/lib/yang.c b/lib/yang.c index c80bf20306..0714ddf7bb 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -448,6 +448,32 @@ bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...) return found; } +void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg, + const struct lyd_node *dnode, const char *xpath_fmt, + ...) +{ + va_list ap; + char xpath[XPATH_MAXLEN]; + struct ly_set *set; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + set = lyd_find_path(dnode, xpath); + assert(set); + for (unsigned int i = 0; i < set->number; i++) { + int ret; + + dnode = set->set.d[i]; + ret = (*cb)(dnode, arg); + if (ret == YANG_ITER_STOP) + return; + } + + ly_set_free(set); +} + bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt, ...) { @@ -621,6 +647,34 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) zlog(priority, "libyang: %s", msg); } +const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len) +{ + struct ly_err_item *ei; + const char *path; + + ei = ly_err_first(ly_ctx); + if (!ei) + return ""; + + strlcpy(buf, "YANG error(s):\n", buf_len); + for (; ei; ei = ei->next) { + strlcat(buf, " ", buf_len); + strlcat(buf, ei->msg, buf_len); + strlcat(buf, "\n", buf_len); + } + + path = ly_errpath(ly_ctx); + if (path) { + strlcat(buf, " YANG path: ", buf_len); + strlcat(buf, path, buf_len); + strlcat(buf, "\n", buf_len); + } + + ly_err_clean(ly_ctx, NULL); + + return buf; +} + void yang_debugging_set(bool enable) { if (enable) { diff --git a/lib/yang.h b/lib/yang.h index 126521707b..85ef0d758c 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -107,6 +107,9 @@ enum yang_iter_flags { /* Callback used by the yang_snodes_iterate_*() family of functions. */ typedef int (*yang_iterate_cb)(const struct lys_node *snode, void *arg); +/* Callback used by the yang_dnode_iterate() function. */ +typedef int (*yang_dnode_iter_cb)(const struct lyd_node *dnode, void *arg); + /* Return values of the 'yang_iterate_cb' callback. */ #define YANG_ITER_CONTINUE 0 #define YANG_ITER_STOP -1 @@ -358,6 +361,25 @@ extern bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...); /* + * Iterate over all libyang data nodes that satisfy an XPath query. + * + * cb + * Function to call with each data node. + * + * arg + * Arbitrary argument passed as the second parameter in each call to 'cb'. + * + * dnode + * Base libyang data node to operate on. + * + * xpath_fmt + * XPath expression (absolute or relative). + */ +void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg, + const struct lyd_node *dnode, const char *xpath_fmt, + ...); + +/* * Check if the libyang data node contains a default value. Non-presence * containers are assumed to always contain a default value. * @@ -497,6 +519,24 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules); extern void yang_debugging_set(bool enable); /* + * Print libyang error messages into the provided buffer. + * + * ly_ctx + * libyang context to operate on. + * + * buf + * Buffer to store the libyang error messages. + * + * buf_len + * Size of buf. + * + * Returns: + * The provided buffer. + */ +extern const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, + size_t buf_len); + +/* * Initialize the YANG subsystem. Should be called only once during the * daemon initialization process. * diff --git a/lib/zclient.c b/lib/zclient.c index 9b1ff7237f..aa5fa082a1 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3121,6 +3121,7 @@ int zclient_send_opaque(struct zclient *zclient, uint32_t type, { int ret; struct stream *s; + uint16_t flags = 0; /* Check buffer size */ if (STREAM_SIZE(zclient->obuf) < @@ -3132,8 +3133,9 @@ int zclient_send_opaque(struct zclient *zclient, uint32_t type, zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); - /* Send sub-type */ + /* Send sub-type and flags */ stream_putl(s, type); + stream_putw(s, flags); /* Send opaque data */ stream_write(s, data, datasize); @@ -3147,6 +3149,77 @@ int zclient_send_opaque(struct zclient *zclient, uint32_t type, } /* + * Send an OPAQUE message to a specific zclient. The contents are opaque + * to zebra. + */ +int zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type, + uint8_t proto, uint16_t instance, + uint32_t session_id, const uint8_t *data, + size_t datasize) +{ + int ret; + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) < + (ZEBRA_HEADER_SIZE + sizeof(struct zapi_opaque_msg) + datasize)) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Send sub-type and flags */ + SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); + stream_putl(s, type); + stream_putw(s, flags); + + /* Send destination client info */ + stream_putc(s, proto); + stream_putw(s, instance); + stream_putl(s, session_id); + + /* Send opaque data */ + stream_write(s, data, datasize); + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = zclient_send_message(zclient); + + return ret; +} + +/* + * Decode incoming opaque message into info struct + */ +int zclient_opaque_decode(struct stream *s, struct zapi_opaque_msg *info) +{ + memset(info, 0, sizeof(*info)); + + /* Decode subtype and flags */ + STREAM_GETL(s, info->type); + STREAM_GETW(s, info->flags); + + /* Decode unicast client info if present */ + if (CHECK_FLAG(info->flags, ZAPI_OPAQUE_FLAG_UNICAST)) { + STREAM_GETC(s, info->proto); + STREAM_GETW(s, info->instance); + STREAM_GETL(s, info->session_id); + } + + info->len = STREAM_READABLE(s); + + return 0; + +stream_failure: + + return -1; +} + +/* * Send a registration request for opaque messages with a specified subtype. */ int zclient_register_opaque(struct zclient *zclient, uint32_t type) @@ -3204,9 +3277,8 @@ int zclient_unregister_opaque(struct zclient *zclient, uint32_t type) return ret; } -/* Utility to parse opaque registration info */ -int zapi_parse_opaque_reg(struct stream *s, - struct zapi_opaque_reg_info *info) +/* Utility to decode opaque registration info */ +int zapi_opaque_reg_decode(struct stream *s, struct zapi_opaque_reg_info *info) { STREAM_GETL(s, info->type); STREAM_GETC(s, info->proto); diff --git a/lib/zclient.h b/lib/zclient.h index d99b6ddf8f..3ded2f55d7 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -457,6 +457,7 @@ struct zapi_route { */ #define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 + /* The older XXX_MESSAGE flags live here */ uint8_t message; /* @@ -849,6 +850,25 @@ extern void zclient_send_mlag_data(struct zclient *client, int zclient_send_opaque(struct zclient *zclient, uint32_t type, const uint8_t *data, size_t datasize); +int zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type, + uint8_t proto, uint16_t instance, + uint32_t session_id, const uint8_t *data, + size_t datasize); + +/* Struct representing the decoded opaque header info */ +struct zapi_opaque_msg { + uint32_t type; /* Subtype */ + uint16_t len; /* len after zapi header and this info */ + uint16_t flags; + + /* Client-specific info - *if* UNICAST flag is set */ + uint8_t proto; + uint16_t instance; + uint32_t session_id; +}; + +#define ZAPI_OPAQUE_FLAG_UNICAST 0x01 + /* Simple struct to convey registration/unreg requests */ struct zapi_opaque_reg_info { /* Message subtype */ @@ -860,10 +880,13 @@ struct zapi_opaque_reg_info { uint32_t session_id; }; +/* Decode incoming opaque */ +int zclient_opaque_decode(struct stream *msg, struct zapi_opaque_msg *info); + int zclient_register_opaque(struct zclient *zclient, uint32_t type); int zclient_unregister_opaque(struct zclient *zclient, uint32_t type); -int zapi_parse_opaque_reg(struct stream *msg, - struct zapi_opaque_reg_info *info); +int zapi_opaque_reg_decode(struct stream *msg, + struct zapi_opaque_reg_info *info); /* * Registry of opaque message types. Please do not reuse an in-use diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index b58fe776ab..9fc13761c8 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -23,6 +23,7 @@ #include "memory.h" #include "command.h" #include "libfrr.h" +#include "filter.h" #include "nhrpd.h" #include "netlink.h" @@ -116,6 +117,7 @@ static struct quagga_signal_t sighandlers[] = { }; static const struct frr_yang_module_info *const nhrpd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, }; diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 4dbe5ca321..8ae5fdcf06 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -166,6 +166,7 @@ struct quagga_signal_t ospf6_signals[] = { }; static const struct frr_yang_module_info *const ospf6d_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_vrf_info, diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 3682b4cdd3..1480b0e391 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -213,25 +213,35 @@ struct ospf_lsa *ospf_external_info_find_lsa(struct ospf *ospf, struct as_external_lsa *al; struct in_addr mask, id; - lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, - p->prefix, ospf->router_id); - - if (!lsa) - return NULL; - - al = (struct as_external_lsa *)lsa->data; + /* Fisrt search the lsdb with address specifc LSID + * where all the host bits are set, if there a matched + * LSA, return. + * Ex: For route 10.0.0.0/16, LSID is 10.0.255.255 + * If no lsa with above LSID, use received address as + * LSID and check if any LSA in LSDB. + * If LSA found, check if the mask is same b/w the matched + * LSA and received prefix, if same then it is the LSA for + * this prefix. + * Ex: For route 10.0.0.0/16, LSID is 10.0.0.0 + */ masklen2ip(p->prefixlen, &mask); + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, id, + ospf->router_id); + if (lsa) + return lsa; - if (mask.s_addr != al->mask.s_addr) { - id.s_addr = p->prefix.s_addr | (~mask.s_addr); - lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, - id, ospf->router_id); - if (!lsa) - return NULL; + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, + p->prefix, ospf->router_id); + + if (lsa) { + al = (struct as_external_lsa *)lsa->data; + if (mask.s_addr == al->mask.s_addr) + return lsa; } - return lsa; + return NULL; } diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index 1543e2015d..86088a7137 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -443,6 +443,32 @@ static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif) exti->rmt_itf_addr.value = rmtif; } +/* Delete Extended LSA */ +static void ospf_extended_lsa_delete(struct ext_itf *exti) +{ + + /* Process only Active Extended Prefix/Link LSA */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + return; + + osr_debug("EXT (%s): Disable %s%s%s-SID on interface %s", __func__, + exti->stype == PREF_SID ? "Prefix" : "", + exti->stype == ADJ_SID ? "Adjacency" : "", + exti->stype == LAN_ADJ_SID ? "LAN-Adjacency" : "", + exti->ifp->name); + + /* Flush LSA if already engaged */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + } + + /* De-activate this Extended Prefix/Link and remove corresponding + * Segment-Routing Prefix-SID or (LAN)-ADJ-SID */ + exti->flags = EXT_LPFLG_LSA_INACTIVE; + ospf_sr_ext_itf_delete(exti); +} + /* * Update Extended prefix SID index for Loopback interface type * @@ -465,12 +491,8 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, return rc; if (p != NULL) { - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Schedule new prefix %s/%u with " - "index %u on interface %s", - __func__, inet_ntoa(p->prefix), p->prefixlen, - index, ifp->name); + osr_debug("EXT (%s): Schedule new prefix %pFX with index %u " + "on interface %s", __func__, p, index, ifp->name); /* Set first Extended Prefix then the Prefix SID information */ set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG, @@ -488,9 +510,8 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, exti, REORIGINATE_THIS_LSA); } } else { - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): Remove prefix for interface %s", - __func__, ifp->name); + osr_debug("EXT (%s): Remove prefix for interface %s", __func__, + ifp->name); if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA); @@ -513,15 +534,15 @@ void ospf_ext_update_sr(bool enable) struct listnode *node; struct ext_itf *exti; - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): %s Extended LSAs for Segment Routing ", - __func__, enable ? "Enable" : "Disable"); + osr_debug("EXT (%s): %s Extended LSAs for Segment Routing ", __func__, + enable ? "Enable" : "Disable"); if (enable) { OspfEXT.enabled = true; /* Refresh LSAs if already engaged or originate */ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Skip inactive Extended Link */ if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) continue; @@ -532,17 +553,15 @@ void ospf_ext_update_sr(bool enable) REORIGINATE_THIS_LSA); } } else { - /* Start by Flushing engaged LSAs */ - for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { - if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) - ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); - exti->flags = EXT_LPFLG_LSA_INACTIVE; - } + /* Start by Removing Extended LSA */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + ospf_extended_lsa_delete(exti); /* And then disable Extended Link/Prefix */ OspfEXT.enabled = false; } } + /* * ----------------------------------------------------------------------- * Followings are callback functions against generic Opaque-LSAs handling @@ -580,10 +599,11 @@ static int ospf_ext_link_del_if(struct interface *ifp) exti = lookup_ext_by_ifp(ifp); if (exti != NULL) { - struct list *iflist = OspfEXT.iflist; + /* Flush LSA and remove Adjacency SID */ + ospf_extended_lsa_delete(exti); /* Dequeue listnode entry from the list. */ - listnode_delete(iflist, exti); + listnode_delete(OspfEXT.iflist, exti); XFREE(MTYPE_OSPF_EXT_PARAMS, exti); @@ -616,6 +636,7 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status) /* Reset Extended information if ospf interface goes Down */ if (oi->state == ISM_Down) { + ospf_extended_lsa_delete(exti); exti->area = NULL; exti->flags = EXT_LPFLG_LSA_INACTIVE; return; @@ -629,12 +650,12 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status) exti->instance = get_ext_pref_instance_value(); exti->area = oi->area; - zlog_debug("EXT (%s): Set Prefix SID to interface %s ", - __func__, oi->ifp->name); + osr_debug("EXT (%s): Set Prefix SID to interface %s ", + __func__, oi->ifp->name); /* Complete SRDB if the interface belongs to a Prefix */ if (OspfEXT.enabled) - ospf_sr_update_prefix(oi->ifp, oi->address); + ospf_sr_update_local_prefix(oi->ifp, oi->address); } else { /* Determine if interface is related to Adj. or LAN Adj. SID */ if (oi->state == ISM_DR) @@ -650,9 +671,9 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status) * Note: Adjacency SID information are completed when ospf * adjacency become up see ospf_ext_link_nsm_change() */ - zlog_debug("EXT (%s): Set %sAdjacency SID for interface %s ", - __func__, exti->stype == ADJ_SID ? "" : "LAN-", - oi->ifp->name); + osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); } } @@ -666,8 +687,8 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) struct ext_itf *exti; uint32_t label; - /* Process Neighbor only when its state is NSM Full */ - if (nbr->state != NSM_Full) + /* Process Link only when neighbor old or new state is NSM Full */ + if (nbr->state != NSM_Full && old_status != NSM_Full) return; /* Get interface information for Segment Routing */ @@ -679,6 +700,23 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) return; } + /* Check that we have a valid area and ospf context */ + if (oi->area == NULL || oi->area->ospf == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Cannot refer to OSPF from OI(%s)", + __func__, IF_NAME(oi)); + return; + } + + /* Remove Extended Link if Neighbor State goes Down or Deleted */ + if (nbr->state == NSM_Down || nbr->state == NSM_Deleted) { + ospf_extended_lsa_delete(exti); + return; + } + + /* Keep Area information in combination with SR info. */ + exti->area = oi->area; + /* Process only Adjacency/LAN SID */ if (exti->stype == PREF_SID) return; @@ -742,11 +780,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) return; } - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Complete %sAdjacency SID for interface %s ", - __func__, exti->stype == ADJ_SID ? "" : "LAN-", - oi->ifp->name); + osr_debug("EXT (%s): Complete %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); /* flood this links params if everything is ok */ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); @@ -756,6 +792,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) else ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA); } + + /* Finally install (LAN)Adjacency-SID in the SRDB */ + ospf_sr_ext_itf_add(exti); } /* Callbacks to handle Extended Link Segment Routing LSA information */ @@ -778,6 +817,10 @@ static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa) != OPAQUE_TYPE_EXTENDED_LINK_LSA) return 0; + /* Check if it is not my LSA */ + if (IS_LSA_SELF(lsa)) + return 0; + /* Check if Extended is enable */ if (!OspfEXT.enabled) return 0; @@ -962,12 +1005,10 @@ static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area, /* Set opaque-LSA header fields. */ lsa_header_set(s, options, lsa_type, lsa_id, router_id); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Create an Opaque-LSA " - "Extended Prefix Opaque LSA instance", - __func__, lsa_type, inet_ntoa(lsa_id)); - + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended " + "Prefix Opaque LSA instance", + __func__, lsa_type, &lsa_id); /* Set opaque-LSA body fields. */ ospf_ext_pref_lsa_body_set(s, exti); @@ -1022,11 +1063,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area, tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); lsa_id.s_addr = htonl(tmp); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "EXT (%s) LSA[Type%u:%s]: Create an Opaque-LSA " - "Extended Link Opaque LSA instance", - __func__, lsa_type, inet_ntoa(lsa_id)); + osr_debug( + "EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended " + "Link Opaque LSA instance", + __func__, lsa_type, &lsa_id); /* Set opaque-LSA header fields. */ lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id); @@ -1089,17 +1129,13 @@ static int ospf_ext_pref_lsa_originate1(struct ospf_area *area, /* Flood new LSA through area. */ ospf_flood_through_area(area, NULL /*nbr */, new); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - char area_id[INET_ADDRSTRLEN]; - - inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id)); - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA " - "Extended Prefix Opaque LSA: Area(%s), Link(%s)", - __func__, new->data->type, inet_ntoa(new->data->id), - area_id, exti->ifp->name); + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA" + "Extended Prefix Opaque LSA: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, + &area->area_id, exti->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } rc = 0; @@ -1141,17 +1177,13 @@ static int ospf_ext_link_lsa_originate1(struct ospf_area *area, /* Flood new LSA through area. */ ospf_flood_through_area(area, NULL /*nbr */, new); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - char area_id[INET_ADDRSTRLEN]; - - inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id)); - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA " - "Extended Link Opaque LSA: Area(%s), Link(%s)", - __func__, new->data->type, inet_ntoa(new->data->id), - area_id, exti->ifp->name); + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA " + "Extended Link Opaque LSA: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, + &area->area_id, exti->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } rc = 0; @@ -1168,15 +1200,13 @@ static int ospf_ext_pref_lsa_originate(void *arg) if (!OspfEXT.enabled) { zlog_info( - "EXT (%s): Segment Routing " - "functionality is Disabled now", + "EXT (%s): Segment Routing functionality is Disabled now", __func__); rc = 0; /* This is not an error case. */ return rc; } - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): Start Originate Prefix LSA for area %s", - __func__, inet_ntoa(area->area_id)); + osr_debug("EXT (%s): Start Originate Prefix LSA for area %pI4", + __func__, &area->area_id); /* Check if Extended Prefix Opaque LSA is already engaged */ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { @@ -1206,12 +1236,10 @@ static int ospf_ext_pref_lsa_originate(void *arg) } /* Ok, let's try to originate an LSA */ - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Let's finally reoriginate the " - "LSA 7.0.0.%u for Itf %s", - __func__, exti->instance, - exti->ifp ? exti->ifp->name : ""); + osr_debug( + "EXT (%s): Let's finally re-originate the LSA 7.0.0.%u " + "for Itf %s", __func__, exti->instance, + exti->ifp ? exti->ifp->name : ""); ospf_ext_pref_lsa_originate1(area, exti); } @@ -1229,8 +1257,7 @@ static int ospf_ext_link_lsa_originate(void *arg) if (!OspfEXT.enabled) { zlog_info( - "EXT (%s): Segment Routing " - "functionality is Disabled now", + "EXT (%s): Segment Routing functionality is Disabled now", __func__); rc = 0; /* This is not an error case. */ return rc; @@ -1242,6 +1269,10 @@ static int ospf_ext_link_lsa_originate(void *arg) if (exti->stype == PREF_SID) continue; + /* Skip Inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + /* Process only Extended Link with valid Area ID */ if ((exti->area == NULL) || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id))) @@ -1268,13 +1299,11 @@ static int ospf_ext_link_lsa_originate(void *arg) } /* Ok, let's try to originate an LSA */ - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Let's finally reoriginate the " - "LSA 8.0.0.%u for Itf %s through the Area %s", - __func__, exti->instance, - exti->ifp ? exti->ifp->name : "-", - inet_ntoa(area->area_id)); + osr_debug( + "EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u " + "for Itf %s through the Area %pI4", __func__, + exti->instance, exti->ifp ? exti->ifp->name : "-", + &area->area_id); ospf_ext_link_lsa_originate1(area, exti); } @@ -1297,8 +1326,7 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa) * It seems a slip among routers in the routing domain. */ zlog_info( - "EXT (%s): Segment Routing functionality is " - "Disabled", + "EXT (%s): Segment Routing functionality is Disabled", __func__); /* Flush it anyway. */ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); @@ -1362,12 +1390,11 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa) ospf_flood_through_area(area, NULL /*nbr */, new); /* Debug logging. */ - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug( - "EXT (%s): LSA[Type%u:%s] Refresh Extended Prefix LSA", - __func__, new->data->type, inet_ntoa(new->data->id)); + osr_debug("EXT (%s): LSA[Type%u:%pI4] Refresh Extended Prefix LSA", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } + return new; } @@ -1438,12 +1465,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa) ospf_flood_through_area(area, NULL /*nbr */, new); /* Debug logging. */ - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Refresh Extended Link LSA", - __func__, new->data->type, inet_ntoa(new->data->id)); + osr_debug("EXT (%s): LSA[Type%u:%pI4]: Refresh Extended Link LSA", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } return new; } @@ -1468,18 +1493,17 @@ static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti, if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))) return; - zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, - opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", - opcode == REFRESH_THIS_LSA ? "Refresh" : "", - opcode == FLUSH_THIS_LSA ? "Flush" : "", - exti->ifp ? exti->ifp->name : "-"); + osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + exti->ifp ? exti->ifp->name : "-"); /* Verify Area */ if (exti->area == NULL) { - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "EXT (%s): Area is not yet set. Try to use Backbone Area", - __func__); + osr_debug( + "EXT (%s): Area is not yet set. Try to use Backbone Area", + __func__); top = ospf_lookup_by_vrf_id(VRF_DEFAULT); struct in_addr backbone = {.s_addr = INADDR_ANY}; @@ -1533,18 +1557,17 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))) return; - zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, - opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", - opcode == REFRESH_THIS_LSA ? "Refresh" : "", - opcode == FLUSH_THIS_LSA ? "Flush" : "", - exti->ifp ? exti->ifp->name : "-"); + osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + exti->ifp ? exti->ifp->name : "-"); /* Verify Area */ if (exti->area == NULL) { - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "EXT (%s): Area is not yet set. Try to use Backbone Area", - __func__); + osr_debug( + "EXT (%s): Area is not yet set. Try to use Backbone Area", + __func__); top = ospf_lookup_by_vrf_id(VRF_DEFAULT); struct in_addr backbone = {.s_addr = INADDR_ANY}; diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 6a3ba9902d..e534e72a64 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -126,6 +126,7 @@ struct quagga_signal_t ospf_signals[] = { }; static const struct frr_yang_module_info *const ospfd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_vrf_info, diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 8cb5d32a8a..20cdc75fe8 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -33,12 +33,24 @@ #define OSPF_PATH_TYPE2_EXTERNAL 4 #define OSPF_PATH_MAX 5 +/* Segment Routing information to complement ospf_path structure */ +struct sr_nexthop_info { + /* Output label associated to this route */ + mpls_label_t label_out; + /* + * Pointer to SR Node which is the next hop for this route + * or NULL if next hop is the destination of the prefix + */ + struct sr_node *nexthop; +}; + /* OSPF Path. */ struct ospf_path { struct in_addr nexthop; struct in_addr adv_router; ifindex_t ifindex; unsigned char unnumbered; + struct sr_nexthop_info srni; }; /* Below is the structure linked to every diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index ae70a5c789..64c59c9abf 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -1369,7 +1369,7 @@ static int ospf_spf_calculate_timer(struct thread *thread) abr_time = monotime_since(&start_time, NULL); /* Schedule Segment Routing update */ - ospf_sr_update_timer_add(ospf); + ospf_sr_update_task(ospf); total_spf_time = monotime_since(&spf_start_time, &ospf->ts_spf_duration); diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index 2a35bd2ef9..1a65bfa411 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <zebra.h> +#include "printfrr.h" #include "command.h" #include "hash.h" #include "if.h" @@ -51,6 +52,7 @@ #include "thread.h" #include "vty.h" #include "zclient.h" +#include "sbuf.h" #include <lib/json.h> #include "ospf_errors.h" @@ -79,7 +81,7 @@ */ static struct ospf_sr_db OspfSR; static void ospf_sr_register_vty(void); -static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe); +static inline void del_adj_sid(struct sr_nhlfe nhlfe); /* * Segment Routing Data Base functions @@ -107,8 +109,8 @@ static void del_sr_link(void *val) { struct sr_link *srl = (struct sr_link *)val; - del_sid_nhlfe(srl->nhlfe[0]); - del_sid_nhlfe(srl->nhlfe[1]); + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); XFREE(MTYPE_OSPF_SR_PARAMS, val); } @@ -117,7 +119,7 @@ static void del_sr_pref(void *val) { struct sr_prefix *srp = (struct sr_prefix *)val; - del_sid_nhlfe(srp->nhlfe); + ospf_zebra_delete_prefix_sid(srp); XFREE(MTYPE_OSPF_SR_PARAMS, val); } @@ -151,9 +153,7 @@ static struct sr_node *sr_node_new(struct in_addr *rid) new->neighbor = NULL; new->instance = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Created new SR node for %s", - inet_ntoa(new->adv_router)); + osr_debug(" |- Created new SR node for %pI4", &new->adv_router); return new; } @@ -164,6 +164,8 @@ static void sr_node_del(struct sr_node *srn) if (srn == NULL) return; + osr_debug(" |- Delete SR node for %pI4", &srn->adv_router); + /* Clean Extended Link */ list_delete(&srn->ext_link); @@ -188,9 +190,7 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, if (OspfSR.neighbors == NULL) return NULL; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Search SR-Node for nexthop %s", - inet_ntoa(nexthop)); + osr_debug(" |- Search SR-Node for nexthop %pI4", &nexthop); /* First, search neighbor Router ID for this nexthop */ found = false; @@ -209,9 +209,8 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, if (!found) return NULL; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found nexthop Router ID %s", - inet_ntoa(nbr->router_id)); + osr_debug(" |- Found nexthop Router ID %pI4", &nbr->router_id); + /* Then, search SR Node */ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id); @@ -230,8 +229,7 @@ static int ospf_sr_start(struct ospf *ospf) struct sr_node *srn; int rc = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Start Segment Routing", __func__); + osr_debug("SR (%s): Start Segment Routing", __func__); /* Initialize self SR Node */ srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), @@ -244,8 +242,7 @@ static int ospf_sr_start(struct ospf *ospf) srn->msd = OspfSR.msd; OspfSR.self = srn; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR (%s): Update SR-DB from LSDB", __func__); + osr_debug("SR (%s): Update SR-DB from LSDB", __func__); /* Start by looking to Router Info & Extended LSA in lsdb */ if ((ospf != NULL) && (ospf->backbone != NULL)) { @@ -278,14 +275,15 @@ static int ospf_sr_start(struct ospf *ospf) static void ospf_sr_stop(void) { - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Stop Segment Routing", __func__); + osr_debug("SR (%s): Stop Segment Routing", __func__); /* * Remove all SR Nodes from the Hash table. Prefix and Link SID will * be remove though list_delete() call. See sr_node_del() */ hash_clean(OspfSR.neighbors, (void *)sr_node_del); + OspfSR.self = NULL; + OspfSR.enabled = false; } /* @@ -299,8 +297,7 @@ int ospf_sr_init(void) { int rc = -1; - if (IS_DEBUG_OSPF_SR) - zlog_info("SR (%s): Initialize SR Data Base", __func__); + osr_debug("SR (%s): Initialize SR Data Base", __func__); memset(&OspfSR, 0, sizeof(struct ospf_sr_db)); OspfSR.enabled = false; @@ -353,9 +350,6 @@ void ospf_sr_term(void) /* Clear Prefix Table */ if (OspfSR.prefix) route_table_finish(OspfSR.prefix); - - OspfSR.enabled = false; - OspfSR.self = NULL; } /* @@ -368,8 +362,6 @@ void ospf_sr_finish(void) { /* Stop Segment Routing */ ospf_sr_stop(); - - OspfSR.enabled = false; } /* @@ -417,21 +409,17 @@ static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top, } /* Get OSPF Path from address */ -static struct ospf_path *get_nexthop_by_addr(struct ospf *top, - struct prefix_ipv4 p) +static struct ospf_route *get_nexthop_by_addr(struct ospf *top, + struct prefix_ipv4 p) { - struct ospf_route * or ; - struct ospf_path *path; - struct listnode *node; struct route_node *rn; /* Sanity Check */ if (top == NULL) return NULL; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Search Nexthop for prefix %s/%u", - inet_ntoa(p.prefix), p.prefixlen); + osr_debug(" |- Search Nexthop for prefix %pFX", + (struct prefix *)&p); rn = route_node_lookup(top->new_table, (struct prefix *)&p); @@ -443,16 +431,7 @@ static struct ospf_path *get_nexthop_by_addr(struct ospf *top, return NULL; route_unlock_node(rn); - or = rn->info; - if (or == NULL) - return NULL; - - /* Then search path from this route */ - for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) - if (path->nexthop.s_addr != INADDR_ANY || path->ifindex != 0) - return path; - - return NULL; + return rn->info; } /* Compute NHLFE entry for Extended Link */ @@ -462,10 +441,7 @@ static int compute_link_nhlfe(struct sr_link *srl) struct ospf_neighbor *nh; int rc = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Compute NHLFE for link %s/%u", - inet_ntoa(srl->nhlfe[0].prefv4.prefix), - srl->nhlfe[0].prefv4.prefixlen); + osr_debug(" |- Compute NHLFE for link %pI4", &srl->itf_addr); /* First determine the OSPF Neighbor */ nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop); @@ -477,9 +453,7 @@ static int compute_link_nhlfe(struct sr_link *srl) if (nh == NULL) return rc; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found nexthop NHLFE %s", - inet_ntoa(nh->router_id)); + osr_debug(" |- Found nexthop %pI4", &nh->router_id); /* Set ifindex for this neighbor */ srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex; @@ -515,150 +489,108 @@ static int compute_link_nhlfe(struct sr_link *srl) * * @param srp - Segment Routing Prefix * - * @return -1 if next hop is not found, 0 if nexthop has not changed - * and 1 if success + * @return -1 if no route is found, 0 if there is no SR route ready + * and 1 if success or update */ static int compute_prefix_nhlfe(struct sr_prefix *srp) { struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT); - struct ospf_path *nh = NULL; + struct ospf_path *path; + struct listnode *node; struct sr_node *srnext; int rc = -1; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Compute NHLFE for prefix %s/%u", - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen); + osr_debug(" |- Compute NHLFE for prefix %pFX", + (struct prefix *)&srp->prefv4); + /* First determine the nexthop */ - nh = get_nexthop_by_addr(top, srp->nhlfe.prefv4); + srp->route = get_nexthop_by_addr(top, srp->prefv4); /* Nexthop could be not found when OSPF Adjacency just fire up * because SPF don't yet populate routing table. This NHLFE will * be fixed later when SR SPF schedule will be called. */ - if (nh == NULL) + if (srp->route == NULL) return rc; - /* Check if NextHop has changed when call after running a new SPF */ - if (IPV4_ADDR_SAME(&nh->nexthop, &srp->nhlfe.nexthop) - && (nh->ifindex == srp->nhlfe.ifindex)) - return 0; - - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found new next hop for this NHLFE: %s", - inet_ntoa(nh->nexthop)); + /* Compute Input Label with self SRGB */ + srp->label_in = index2label(srp->sid, OspfSR.srgb); - /* - * Get SR-Node for this nexthop. Could be not yet available - * as Extende Link / Prefix and Router Information are flooded - * after LSA Type 1 & 2 which populate the OSPF Route Table - */ - srnext = get_sr_node_by_nexthop(top, nh->nexthop); - if (srnext == NULL) - return rc; + rc = 0; + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { - /* And store this information for later update if SR Node is found */ - srnext->neighbor = OspfSR.self; - if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router)) - srp->nexthop = NULL; - else - srp->nexthop = srnext; + osr_debug(" |- Process new route via %pI4 for this prefix", + &path->nexthop); - /* - * SR Node could be known, but SRGB could be not initialize - * This is due to the fact that Extended Link / Prefix could - * be received before corresponding Router Information LSA - */ - if ((srnext == NULL) || (srnext->srgb.lower_bound == 0) - || (srnext->srgb.range_size == 0)) - return rc; + /* + * Get SR-Node for this nexthop. Could be not yet available + * as Extended Link / Prefix and Router Information are flooded + * after LSA Type 1 & 2 which populate the OSPF Route Table + */ + srnext = get_sr_node_by_nexthop(top, path->nexthop); + if (srnext == NULL) + continue; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found SRGB %u/%u for next hop SR-Node %s", - srnext->srgb.range_size, srnext->srgb.lower_bound, - inet_ntoa(srnext->adv_router)); + /* And store this information for later update */ + srnext->neighbor = OspfSR.self; + if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router)) + path->srni.nexthop = NULL; + else + path->srni.nexthop = srnext; - /* Set ip addr & ifindex for this neighbor */ - IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &nh->nexthop); - srp->nhlfe.ifindex = nh->ifindex; + /* + * SR Node could be known, but SRGB could be not initialize + * This is due to the fact that Extended Link / Prefix could + * be received before corresponding Router Information LSA + */ + if (srnext == NULL || srnext->srgb.lower_bound == 0 + || srnext->srgb.range_size == 0) + continue; - /* Compute Input Label with self SRGB */ - srp->nhlfe.label_in = index2label(srp->sid, OspfSR.srgb); - /* - * and Output Label with Next hop SR Node SRGB or Implicit Null label - * if next hop is the destination and request PHP - */ - if ((srp->nexthop == NULL) - && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) - srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; - else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) - srp->nhlfe.label_out = srp->sid; - else - srp->nhlfe.label_out = index2label(srp->sid, srnext->srgb); + osr_debug(" |- Found SRGB %u/%u for next hop SR-Node %pI4", + srnext->srgb.range_size, srnext->srgb.lower_bound, + &srnext->adv_router); - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Computed new labels in: %u out: %u", - srp->nhlfe.label_in, srp->nhlfe.label_out); + /* + * Compute Output Label with Nexthop SR Node SRGB or Implicit + * Null label if next hop is the destination and request PHP + */ + if ((path->srni.nexthop == NULL) + && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) + path->srni.label_out = MPLS_LABEL_IMPLICIT_NULL; + else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) + path->srni.label_out = srp->sid; + else + path->srni.label_out = + index2label(srp->sid, srnext->srgb); - rc = 1; + osr_debug(" |- Computed new labels in: %u out: %u", + srp->label_in, path->srni.label_out); + rc = 1; + } return rc; } -/* Send MPLS Label entry to Zebra for installation or deletion */ -static int ospf_zebra_send_mpls_labels(int cmd, struct sr_nhlfe nhlfe) -{ - struct zapi_labels zl = {}; - struct zapi_nexthop *znh; - - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s LSP %u/%u for %s/%u via %u", - cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", - nhlfe.label_in, nhlfe.label_out, - inet_ntoa(nhlfe.prefv4.prefix), - nhlfe.prefv4.prefixlen, nhlfe.ifindex); - - zl.type = ZEBRA_LSP_OSPF_SR; - zl.local_label = nhlfe.label_in; - - SET_FLAG(zl.message, ZAPI_LABELS_FTN); - zl.route.prefix.family = nhlfe.prefv4.family; - zl.route.prefix.prefixlen = nhlfe.prefv4.prefixlen; - zl.route.prefix.u.prefix4 = nhlfe.prefv4.prefix; - zl.route.type = ZEBRA_ROUTE_OSPF; - zl.route.instance = 0; - - zl.nexthop_num = 1; - znh = &zl.nexthops[0]; - znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - znh->gate.ipv4 = nhlfe.nexthop; - znh->ifindex = nhlfe.ifindex; - znh->label_num = 1; - znh->labels[0] = nhlfe.label_out; - - return zebra_send_mpls_labels(zclient, cmd, &zl); -} - -/* Add new NHLFE entry for SID */ -static inline void add_sid_nhlfe(struct sr_nhlfe nhlfe) +/* Add new NHLFE entry for Adjacency SID */ +static inline void add_adj_sid(struct sr_nhlfe nhlfe) { - if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) - ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, nhlfe); + if (nhlfe.label_in != 0) + ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, nhlfe); } -/* Remove NHLFE entry for SID */ -static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe) +/* Remove NHLFE entry for Adjacency SID */ +static inline void del_adj_sid(struct sr_nhlfe nhlfe) { - if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) - ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, nhlfe); + if (nhlfe.label_in != 0) + ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, nhlfe); } -/* Update NHLFE entry for SID */ -static inline void update_sid_nhlfe(struct sr_nhlfe n1, struct sr_nhlfe n2) +/* Update NHLFE entry for Adjacency SID */ +static inline void update_adj_sid(struct sr_nhlfe n1, struct sr_nhlfe n2) { - - del_sid_nhlfe(n1); - add_sid_nhlfe(n2); + del_adj_sid(n1); + add_adj_sid(n2); } /* @@ -729,23 +661,10 @@ static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh) sum += TLV_SIZE(sub_tlvh); } - IPV4_ADDR_COPY(&srl->nhlfe[0].prefv4.prefix, &link->link_data); - srl->nhlfe[0].prefv4.prefixlen = IPV4_MAX_PREFIXLEN; - srl->nhlfe[0].prefv4.family = AF_INET; - apply_mask_ipv4(&srl->nhlfe[0].prefv4); - IPV4_ADDR_COPY(&srl->nhlfe[1].prefv4.prefix, &link->link_data); - srl->nhlfe[1].prefv4.prefixlen = IPV4_MAX_PREFIXLEN; - srl->nhlfe[1].prefv4.family = AF_INET; - apply_mask_ipv4(&srl->nhlfe[1].prefv4); + IPV4_ADDR_COPY(&srl->itf_addr, &link->link_data); - if (IS_DEBUG_OSPF_SR) { - zlog_debug(" |- Found primary Adj/Lan Sid %u for %s/%u", - srl->sid[0], inet_ntoa(srl->nhlfe[0].prefv4.prefix), - srl->nhlfe[0].prefv4.prefixlen); - zlog_debug(" |- Found backup Adj/Lan Sid %u for %s/%u", - srl->sid[1], inet_ntoa(srl->nhlfe[1].prefv4.prefix), - srl->nhlfe[1].prefv4.prefixlen); - } + osr_debug(" |- Found primary %u and backup %u Adj/Lan Sid for %pI4", + srl->sid[0], srl->sid[1], &srl->itf_addr); return srl; } @@ -784,11 +703,10 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh) srp->sid = GET_LABEL(ntohl(psid->value)); else srp->sid = ntohl(psid->value); - IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix, - &pref->address); - srp->nhlfe.prefv4.prefixlen = pref->pref_length; - srp->nhlfe.prefv4.family = AF_INET; - apply_mask_ipv4(&srp->nhlfe.prefv4); + IPV4_ADDR_COPY(&srp->prefv4.prefix, &pref->address); + srp->prefv4.prefixlen = pref->pref_length; + srp->prefv4.family = AF_INET; + apply_mask_ipv4(&srp->prefv4); break; default: break; @@ -796,10 +714,9 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh) sum += TLV_SIZE(sub_tlvh); } - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found SID %u for prefix %s/%u", srp->sid, - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen); + osr_debug(" |- Found SID %u for prefix %pFX", srp->sid, + (struct prefix *)&srp->prefv4); + return srp; } @@ -839,13 +756,12 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, if ((srn == NULL) || (srl == NULL)) return; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Process Extended Link Adj/Lan-SID"); + osr_debug(" |- Process Extended Link Adj/Lan-SID"); - /* Process only Local Adj/Lan_Adj SID coming from LSA SELF */ - if (!CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG) - || !CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG) - || !CHECK_FLAG(lsa_flags, OSPF_LSA_SELF)) + /* Skip Local Adj/Lan_Adj SID coming from neighbors */ + if (!CHECK_FLAG(lsa_flags, OSPF_LSA_SELF) + && (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG) + || CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG))) return; /* Search for existing Segment Link */ @@ -855,11 +771,9 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, break; } - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s SR Link 8.0.0.%u for SR node %s", - found ? "Update" : "Add", - GET_OPAQUE_ID(srl->instance), - inet_ntoa(srn->adv_router)); + osr_debug(" |- %s SR Link 8.0.0.%u for SR node %pI4", + found ? "Update" : "Add", GET_OPAQUE_ID(srl->instance), + &srn->adv_router); /* if not found, add new Segment Link and install NHLFE */ if (!found) { @@ -869,14 +783,14 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, listnode_add(srn->ext_link, srl); /* Try to set MPLS table */ if (compute_link_nhlfe(srl)) { - add_sid_nhlfe(srl->nhlfe[0]); - add_sid_nhlfe(srl->nhlfe[1]); + add_adj_sid(srl->nhlfe[0]); + add_adj_sid(srl->nhlfe[1]); } } else { if (sr_link_cmp(lk, srl)) { if (compute_link_nhlfe(srl)) { - update_sid_nhlfe(lk->nhlfe[0], srl->nhlfe[0]); - update_sid_nhlfe(lk->nhlfe[1], srl->nhlfe[1]); + update_adj_sid(lk->nhlfe[0], srl->nhlfe[0]); + update_adj_sid(lk->nhlfe[1], srl->nhlfe[1]); /* Replace Segment List */ listnode_delete(srn->ext_link, lk); XFREE(MTYPE_OSPF_SR_PARAMS, lk); @@ -912,8 +826,7 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) if (srn == NULL || srp == NULL) return; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Process Extended Prefix SID %u", srp->sid); + osr_debug(" |- Process Extended Prefix SID %u", srp->sid); /* Process only Global Prefix SID */ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG)) @@ -926,11 +839,9 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) break; } - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %s", - found ? "Update" : "Add", - GET_OPAQUE_ID(srp->instance), - inet_ntoa(srn->adv_router)); + osr_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %pI4", + found ? "Update" : "Add", GET_OPAQUE_ID(srp->instance), + &srn->adv_router); /* if not found, add new Segment Prefix and install NHLFE */ if (!found) { @@ -940,11 +851,11 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) listnode_add(srn->ext_prefix, srp); /* Try to set MPLS table */ if (compute_prefix_nhlfe(srp) == 1) - add_sid_nhlfe(srp->nhlfe); + ospf_zebra_update_prefix_sid(srp); } else { if (sr_prefix_cmp(pref, srp)) { if (compute_prefix_nhlfe(srp) == 1) { - update_sid_nhlfe(pref->nhlfe, srp->nhlfe); + ospf_zebra_delete_prefix_sid(pref); /* Replace Segment Prefix */ listnode_delete(srn->ext_prefix, pref); XFREE(MTYPE_OSPF_SR_PARAMS, pref); @@ -952,6 +863,7 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) IPV4_ADDR_COPY(&srp->adv_router, &srn->adv_router); listnode_add(srn->ext_prefix, srp); + ospf_zebra_update_prefix_sid(srp); } else { /* New NHLFE was not found. * Just free the SR Prefix @@ -976,7 +888,6 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) struct listnode *node; struct sr_node *srn = (struct sr_node *)bucket->data; struct sr_prefix *srp; - struct sr_nhlfe new; /* Process Every Extended Prefix for this SR-Node */ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { @@ -989,13 +900,10 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) continue; - /* OK. Compute new NHLFE */ - memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe)); - new.label_in = index2label(srp->sid, OspfSR.srgb); - /* Update MPLS LFIB */ - update_sid_nhlfe(srp->nhlfe, new); - /* Finally update Input Label */ - srp->nhlfe.label_in = new.label_in; + /* OK. Compute new input label ... */ + srp->label_in = index2label(srp->sid, OspfSR.srgb); + /* ... and update MPLS LFIB */ + ospf_zebra_update_prefix_sid(srp); } } @@ -1005,21 +913,27 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) */ static void update_out_nhlfe(struct hash_bucket *bucket, void *args) { - struct listnode *node; + struct listnode *node, *pnode; struct sr_node *srn = (struct sr_node *)bucket->data; struct sr_node *srnext = (struct sr_node *)args; struct sr_prefix *srp; - struct sr_nhlfe new; + struct ospf_path *path; for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { - /* Process only SID Index for next hop without PHP */ - if ((srp->nexthop == NULL) - && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) + /* Process only SID Index with valid route */ + if (srp->route == NULL) continue; - memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe)); - new.label_out = index2label(srp->sid, srnext->srgb); - update_sid_nhlfe(srp->nhlfe, new); - srp->nhlfe.label_out = new.label_out; + + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) { + /* Process only SID Index for next hop without PHP */ + if ((path->srni.nexthop == NULL) + && (!CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_NPFLG))) + continue; + path->srni.label_out = + index2label(srp->sid, srnext->srgb); + ospf_zebra_update_prefix_sid(srp); + } } } @@ -1036,17 +950,15 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) struct sr_node *srn; struct tlv_header *tlvh; struct lsa_header *lsah = lsa->data; - struct ri_sr_tlv_sid_label_range *ri_srgb; - struct ri_sr_tlv_sr_algorithm *algo; + struct ri_sr_tlv_sid_label_range *ri_srgb = NULL; + struct ri_sr_tlv_sr_algorithm *algo = NULL; struct sr_srgb srgb; uint16_t length = 0, sum = 0; + uint8_t msd = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Process Router " - "Information LSA 4.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Process Router Information LSA 4.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (IS_LSA_SELF(lsa)) @@ -1058,26 +970,10 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) return; } - /* Get SR Node in hash table from Router ID */ - srn = hash_get(OspfSR.neighbors, (void *)&(lsah->adv_router), - (void *)sr_node_new); - - /* Sanity check */ - if (srn == NULL) { - flog_err(EC_OSPF_SR_NODE_CREATE, - "SR (%s): Abort! can't create SR node in hash table", - __func__); - return; - } + /* Search SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, + &lsah->adv_router); - if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) { - flog_err(EC_OSPF_SR_INVALID_LSA_ID, - "SR (%s): Abort! Wrong " - "LSA ID 4.0.0.%u for SR node %s/%u", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router), srn->instance); - return; - } /* Collect Router Information Sub TLVs */ /* Initialize TLV browsing */ @@ -1090,23 +986,14 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) switch (ntohs(tlvh->type)) { case RI_SR_TLV_SR_ALGORITHM: algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; - int i; - - for (i = 0; i < ntohs(algo->header.length); i++) - srn->algo[i] = algo->value[0]; - for (; i < ALGORITHM_COUNT; i++) - srn->algo[i] = SR_ALGORITHM_UNSET; sum += TLV_SIZE(tlvh); break; case RI_SR_TLV_SID_LABEL_RANGE: ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh; - srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size)); - srgb.lower_bound = - GET_LABEL(ntohl(ri_srgb->lower.value)); sum += TLV_SIZE(tlvh); break; case RI_SR_TLV_NODE_MSD: - srn->msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value; + msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value; sum += TLV_SIZE(tlvh); break; default: @@ -1115,38 +1002,82 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) } } - /* Check that we collect mandatory parameters */ - if (srn->algo[0] == SR_ALGORITHM_UNSET || srgb.range_size == 0 - || srgb.lower_bound == 0) { - flog_err(EC_OSPF_SR_NODE_CREATE, - "SR (%s): Missing mandatory parameters. Abort!", - __func__); - hash_release(OspfSR.neighbors, &(srn->adv_router)); - XFREE(MTYPE_OSPF_SR_PARAMS, srn); + /* Check if Segment Routing Capabilities has been found */ + if (ri_srgb == NULL) { + /* Skip Router Information without SR capabilities + * advertise by a non SR Node */ + if (srn == NULL) { + return; + } else { + /* Remove SR Node that advertise Router Information + * without SR capabilities. This could correspond to a + * Node stopping Segment Routing */ + hash_release(OspfSR.neighbors, &(srn->adv_router)); + sr_node_del(srn); + return; + } + } + + /* Check that RI LSA belongs to the correct SR Node */ + if ((srn != NULL) && (srn->instance != 0) + && (srn->instance != ntohl(lsah->id.s_addr))) { + flog_err(EC_OSPF_SR_INVALID_LSA_ID, + "SR (%s): Abort! Wrong " + "LSA ID 4.0.0.%u for SR node %pI4/%u", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router, srn->instance); return; } + /* OK. All things look good. Get SRGB */ + srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size)); + srgb.lower_bound = GET_LABEL(ntohl(ri_srgb->lower.value)); + /* Check if it is a new SR Node or not */ - if (srn->instance == 0) { + if (srn == NULL) { + /* Get a new SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_get(OspfSR.neighbors, + &lsah->adv_router, + (void *)sr_node_new); + /* Sanity check */ + if (srn == NULL) { + flog_err( + EC_OSPF_SR_NODE_CREATE, + "SR (%s): Abort! can't create SR node in hash table", + __func__); + return; + } /* update LSA ID */ srn->instance = ntohl(lsah->id.s_addr); /* Copy SRGB */ srn->srgb.range_size = srgb.range_size; srn->srgb.lower_bound = srgb.lower_bound; + /* Set Algorithm */ + if (algo != NULL) { + int i; + for (i = 0; i < ntohs(algo->header.length); i++) + srn->algo[i] = algo->value[0]; + for (; i < ALGORITHM_COUNT; i++) + srn->algo[i] = SR_ALGORITHM_UNSET; + } else { + srn->algo[0] = SR_ALGORITHM_SPF; + } + /* set MSD */ + srn->msd = msd; + return; } /* Check if SRGB has changed */ - if ((srn->srgb.range_size != srgb.range_size) - || (srn->srgb.lower_bound != srgb.lower_bound)) { - srn->srgb.range_size = srgb.range_size; - srn->srgb.lower_bound = srgb.lower_bound; - /* Update NHLFE if it is a neighbor SR node */ - if (srn->neighbor == OspfSR.self) - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, - void *))update_out_nhlfe, - (void *)srn); - } + if ((srn->srgb.range_size == srgb.range_size) + && (srn->srgb.lower_bound == srgb.lower_bound)) + return; + + /* Update SRGB ... */ + srn->srgb.range_size = srgb.range_size; + srn->srgb.lower_bound = srgb.lower_bound; + /* ... and NHLFE if it is a neighbor SR node */ + if (srn->neighbor == OspfSR.self) + hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn); } /* @@ -1158,10 +1089,8 @@ void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa) struct sr_node *srn; struct lsa_header *lsah = lsa->data; - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Remove SR node %s from lsa_id 4.0.0.%u", - __func__, inet_ntoa(lsah->adv_router), - GET_OPAQUE_ID(ntohl(lsah->id.s_addr))); + osr_debug("SR (%s): Remove SR node %pI4 from lsa_id 4.0.0.%u", __func__, + &lsah->adv_router, GET_OPAQUE_ID(ntohl(lsah->id.s_addr))); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1176,16 +1105,17 @@ void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa) /* Sanity check */ if (srn == NULL) { flog_err(EC_OSPF_SR_NODE_CREATE, - "SR (%s): Abort! no entry in SRDB for SR Node %s", - __func__, inet_ntoa(lsah->adv_router)); + "SR (%s): Abort! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); return; } if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) { - flog_err(EC_OSPF_SR_INVALID_LSA_ID, - "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + flog_err( + EC_OSPF_SR_INVALID_LSA_ID, + "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); return; } @@ -1203,11 +1133,9 @@ void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa) uint16_t length, sum; - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Process Extended Link LSA 8.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Process Extended Link LSA 8.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1253,13 +1181,12 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) struct listnode *node; struct sr_link *srl; struct sr_node *srn; - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; uint32_t instance = ntohl(lsah->id.s_addr); - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1278,8 +1205,8 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) */ if (srn == NULL) { flog_err(EC_OSPF_SR_INVALID_DB, - "SR (%s): Stop! no entry in SRDB for SR Node %s", - __func__, inet_ntoa(lsah->adv_router)); + "SR (%s): Stop! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); return; } @@ -1288,18 +1215,143 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) if (srl->instance == instance) break; - /* Remove Segment Link if found */ + /* Remove Segment Link if found. Note that for Neighbors, only Global + * Adj/Lan-Adj SID are stored in the SR-DB */ if ((srl != NULL) && (srl->instance == instance)) { - del_sid_nhlfe(srl->nhlfe[0]); - del_sid_nhlfe(srl->nhlfe[1]); + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); listnode_delete(srn->ext_link, srl); XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } +} + +/* Add (LAN)Adjacency-SID from Extended Link Information */ +void ospf_sr_ext_itf_add(struct ext_itf *exti) +{ + struct sr_node *srn = OspfSR.self; + struct sr_link *srl; + + osr_debug("SR (%s): Add Extended Link LSA 8.0.0.%u from self", __func__, + exti->instance); + + /* Sanity check */ + if (srn == NULL) + return; + + /* Initialize new Segment Routing Link */ + srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link)); + srl->srn = srn; + srl->adv_router = srn->adv_router; + srl->itf_addr = exti->link.link_data; + srl->instance = + SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); + switch (exti->stype) { + case ADJ_SID: + srl->type = ADJ_SID; + /* Primary information */ + srl->flags[0] = exti->adj_sid[0].flags; + if (CHECK_FLAG(exti->adj_sid[0].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[0] = GET_LABEL(ntohl(exti->adj_sid[0].value)); + else + srl->sid[0] = ntohl(exti->adj_sid[0].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[0].nexthop = exti->link.link_id; + else + srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value; + /* Backup Information if set */ + if (exti->adj_sid[1].header.type == 0) + break; + srl->flags[1] = exti->adj_sid[1].flags; + if (CHECK_FLAG(exti->adj_sid[1].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[1] = GET_LABEL(ntohl(exti->adj_sid[1].value)); + else + srl->sid[1] = ntohl(exti->adj_sid[1].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[1].nexthop = exti->link.link_id; + else + srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value; + break; + case LAN_ADJ_SID: + srl->type = LAN_ADJ_SID; + /* Primary information */ + srl->flags[0] = exti->lan_sid[0].flags; + if (CHECK_FLAG(exti->lan_sid[0].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[0] = GET_LABEL(ntohl(exti->lan_sid[0].value)); + else + srl->sid[0] = ntohl(exti->lan_sid[0].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[0].nexthop = exti->lan_sid[0].neighbor_id; + else + srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value; + /* Backup Information if set */ + if (exti->lan_sid[1].header.type == 0) + break; + srl->flags[1] = exti->lan_sid[1].flags; + if (CHECK_FLAG(exti->lan_sid[1].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[1] = GET_LABEL(ntohl(exti->lan_sid[1].value)); + else + srl->sid[1] = ntohl(exti->lan_sid[1].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[1].nexthop = exti->lan_sid[1].neighbor_id; + else + srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value; + break; + default: + /* Wrong SID Type. Abort! */ + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + return; + } + + /* Segment Routing Link is ready, update it */ + update_ext_link_sid(srn, srl, OSPF_LSA_SELF); +} + +/* Delete Prefix or (LAN)Adjacency-SID from Extended Link Information */ +void ospf_sr_ext_itf_delete(struct ext_itf *exti) +{ + struct listnode *node; + struct sr_node *srn = OspfSR.self; + struct sr_prefix *srp = NULL; + struct sr_link *srl = NULL; + uint32_t instance; + + osr_debug("SR (%s): Remove Extended LSA %u.0.0.%u from self", + __func__, exti->stype == PREF_SID ? 7 : 8, exti->instance); + + /* Sanity check: SR-Node and Extended Prefix/Link list may have been + * removed earlier when stopping OSPF or OSPF-SR */ + if (srn == NULL || srn->ext_prefix == NULL || srn->ext_link == NULL) + return; + + if (exti->stype == PREF_SID) { + instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, + exti->instance); + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) + if (srp->instance == instance) + break; + + /* Uninstall Segment Prefix SID if found */ + if ((srp != NULL) && (srp->instance == instance)) + ospf_zebra_delete_prefix_sid(srp); } else { - flog_err(EC_OSPF_SR_INVALID_DB, - "SR (%s): Didn't found corresponding SR Link 8.0.0.%u " - "for SR Node %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + /* Search for corresponding Segment Link for self SR-Node */ + instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, + exti->instance); + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) + if (srl->instance == instance) + break; + + /* Remove Segment Link if found */ + if ((srl != NULL) && (srl->instance == instance)) { + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); + listnode_delete(srn->ext_link, srl); + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } } } @@ -1308,17 +1360,14 @@ void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa) { struct sr_node *srn; struct tlv_header *tlvh; - struct lsa_header *lsah = lsa->data; + struct lsa_header *lsah = (struct lsa_header *)lsa->data; struct sr_prefix *srp; uint16_t length, sum; - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Process Extended Prefix LSA " - "7.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Process Extended Prefix LSA 7.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1367,11 +1416,9 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa) struct lsa_header *lsah = (struct lsa_header *)lsa->data; uint32_t instance = ntohl(lsah->id.s_addr); - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1390,27 +1437,27 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa) */ if (srn == NULL) { flog_err(EC_OSPF_SR_INVALID_DB, - "SR (%s): Stop! no entry in SRDB for SR Node %s", - __func__, inet_ntoa(lsah->adv_router)); + "SR (%s): Stop! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); return; } - /* Search for corresponding Segment Link */ + /* Search for corresponding Segment Prefix */ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) if (srp->instance == instance) break; - /* Remove Segment Link if found */ + /* Remove Prefix if found */ if ((srp != NULL) && (srp->instance == instance)) { - del_sid_nhlfe(srp->nhlfe); - listnode_delete(srn->ext_link, srp); + ospf_zebra_delete_prefix_sid(srp); + listnode_delete(srn->ext_prefix, srp); XFREE(MTYPE_OSPF_SR_PARAMS, srp); } else { flog_err( EC_OSPF_SR_INVALID_DB, - "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %s", + "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %pI4", __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + &lsah->adv_router); } } @@ -1428,14 +1475,14 @@ uint32_t get_ext_link_label_value(void) /* * Update Prefix SID. Call by ospf_ext_pref_ism_change to - * complete initial CLI command at startutp. + * complete initial CLI command at startup. * * @param ifp - Loopback interface * @param pref - Prefix address of this interface * * @return - void */ -void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p) +void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p) { struct listnode *node; struct sr_prefix *srp; @@ -1450,29 +1497,31 @@ void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p) */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { if ((srp->nhlfe.ifindex == ifp->ifindex) - || ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, - &p->u.prefix4)) - && (srp->nhlfe.prefv4.prefixlen == p->prefixlen))) { + || ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p->u.prefix4)) + && (srp->prefv4.prefixlen == p->prefixlen))) { /* Update Interface & Prefix info */ srp->nhlfe.ifindex = ifp->ifindex; - IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix, - &p->u.prefix4); - srp->nhlfe.prefv4.prefixlen = p->prefixlen; - srp->nhlfe.prefv4.family = p->family; + IPV4_ADDR_COPY(&srp->prefv4.prefix, &p->u.prefix4); + srp->prefv4.prefixlen = p->prefixlen; + srp->prefv4.family = p->family; IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &p->u.prefix4); /* OK. Let's Schedule Extended Prefix LSA */ srp->instance = ospf_ext_schedule_prefix_index( - ifp, srp->sid, &srp->nhlfe.prefv4, srp->flags); + ifp, srp->sid, &srp->prefv4, srp->flags); + + osr_debug( + " |- Update Node SID %pFX - %u for self SR Node", + (struct prefix *)&srp->prefv4, srp->sid); /* Install NHLFE if NO-PHP is requested */ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) { - srp->nhlfe.label_in = index2label( - srp->sid, OspfSR.self->srgb); + srp->label_in = index2label(srp->sid, + OspfSR.self->srgb); srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; - add_sid_nhlfe(srp->nhlfe); + ospf_zebra_update_prefix_sid(srp); } } } @@ -1488,12 +1537,10 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) struct sr_node *srn = (struct sr_node *)bucket->data; struct listnode *node; struct sr_prefix *srp; - struct sr_nhlfe old; + bool old; int rc; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Update Prefix for SR Node %s", - inet_ntoa(srn->adv_router)); + osr_debug(" |- Update Prefix for SR Node %pI4", &srn->adv_router); /* Skip Self SR Node */ if (srn == OspfSR.self) @@ -1502,24 +1549,25 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) /* Update Extended Prefix */ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { - /* Backup current NHLFE */ - memcpy(&old, &srp->nhlfe, sizeof(struct sr_nhlfe)); + /* Keep track of valid route */ + old = srp->route != NULL; /* Compute the new NHLFE */ rc = compute_prefix_nhlfe(srp); /* Check computation result */ switch (rc) { - /* next hop is not know, remove old NHLFE to avoid loop */ + /* Routes are not know, remove old NHLFE if any to avoid loop */ case -1: - del_sid_nhlfe(srp->nhlfe); + if (old) + ospf_zebra_delete_prefix_sid(srp); break; - /* next hop has not changed, skip it */ + /* Routes exist but are not ready, skip it */ case 0: break; - /* there is a new next hop, update NHLFE */ + /* There is at least one route, update NHLFE */ case 1: - update_sid_nhlfe(old, srp->nhlfe); + ospf_zebra_update_prefix_sid(srp); break; default: break; @@ -1527,22 +1575,17 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) } } -static int ospf_sr_update_schedule(struct thread *t) +void ospf_sr_update_task(struct ospf *ospf) { - struct ospf *ospf; struct timeval start_time, stop_time; - ospf = THREAD_ARG(t); - ospf->t_sr_update = NULL; - - if (!OspfSR.update) - return 0; + if (ospf == NULL) + return; monotime(&start_time); - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Start SPF update", __func__); + osr_debug("SR (%s): Start SPF update", __func__); hash_iterate(OspfSR.neighbors, (void (*)(struct hash_bucket *, void *))ospf_sr_nhlfe_update, @@ -1550,32 +1593,9 @@ static int ospf_sr_update_schedule(struct thread *t) monotime(&stop_time); - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): SPF Processing Time(usecs): %lld", - __func__, - (stop_time.tv_sec - start_time.tv_sec) * 1000000LL - + (stop_time.tv_usec - start_time.tv_usec)); - - OspfSR.update = false; - return 1; -} - -#define OSPF_SR_UPDATE_INTERVAL 1 - -void ospf_sr_update_timer_add(struct ospf *ospf) -{ - - if (ospf == NULL) - return; - - /* Check if an update is not alreday engage */ - if (OspfSR.update) - return; - - OspfSR.update = true; - - thread_add_timer(master, ospf_sr_update_schedule, ospf, - OSPF_SR_UPDATE_INTERVAL, &ospf->t_sr_update); + osr_debug("SR (%s): SPF Processing Time(usecs): %lld", __func__, + (stop_time.tv_sec - start_time.tv_sec) * 1000000LL + + (stop_time.tv_usec - start_time.tv_usec)); } /* @@ -1620,8 +1640,8 @@ void ospf_sr_config_write_router(struct vty *vty) vty_out(vty, " segment-routing prefix %s/%u " "index %u%s\n", - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen, srp->sid, + inet_ntoa(srp->prefv4.prefix), + srp->prefv4.prefixlen, srp->sid, CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) ? " no-php-flag" @@ -1650,22 +1670,19 @@ DEFUN(ospf_sr_enable, return CMD_WARNING_CONFIG_FAILED; } - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Segment Routing: OFF -> ON"); + osr_debug("SR: Segment Routing: OFF -> ON"); /* Start Segment Routing */ OspfSR.enabled = true; ospf_sr_start(ospf); /* Set Router Information SR parameters */ - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Activate SR for Router Information LSA"); + osr_debug("SR: Activate SR for Router Information LSA"); ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); /* Update Ext LSA */ - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Activate SR for Extended Link/Prefix LSA"); + osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); ospf_ext_update_sr(true); @@ -1683,8 +1700,7 @@ DEFUN (no_ospf_sr_enable, if (!OspfSR.enabled) return CMD_SUCCESS; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Segment Routing: ON -> OFF"); + osr_debug("SR: Segment Routing: ON -> OFF"); /* Start by Disabling Extended Link & Prefix LSA */ ospf_ext_update_sr(false); @@ -1694,7 +1710,6 @@ DEFUN (no_ospf_sr_enable, /* Finally, stop Segment Routing */ ospf_sr_stop(); - OspfSR.enabled = false; return CMD_SUCCESS; } @@ -1913,22 +1928,20 @@ DEFUN (sr_prefix_sid, /* Create new Extended Prefix to SRDB if not found */ new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); - IPV4_ADDR_COPY(&new->nhlfe.prefv4.prefix, &p.u.prefix4); - IPV4_ADDR_COPY(&new->nhlfe.nexthop, &p.u.prefix4); - new->nhlfe.prefv4.prefixlen = p.prefixlen; - new->nhlfe.prefv4.family = p.family; + IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); + new->prefv4.prefixlen = p.prefixlen; + new->prefv4.family = p.family; new->sid = index; + new->type = LOCAL_SID; /* Set NO PHP flag if present and compute NHLFE */ if (argv_find(argv, argc, "no-php-flag", &idx)) { SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); - new->nhlfe.label_in = index2label(new->sid, OspfSR.self->srgb); + new->label_in = index2label(new->sid, OspfSR.self->srgb); new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; } - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Add new index %u to Prefix %s/%u", - __func__, index, inet_ntoa(new->nhlfe.prefv4.prefix), - new->nhlfe.prefv4.prefixlen); + osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index, + (struct prefix *)&new->prefv4); /* Get Interface and check if it is a Loopback */ ifp = if_lookup_prefix(&p, VRF_DEFAULT); @@ -1941,9 +1954,9 @@ DEFUN (sr_prefix_sid, */ listnode_add(OspfSR.self->ext_prefix, new); zlog_info( - "Interface for prefix %s/%u not found. Deferred LSA " + "Interface for prefix %pFX not found. Deferred LSA " "flooding", - inet_ntoa(p.u.prefix4), p.prefixlen); + &p); return CMD_SUCCESS; } @@ -1956,8 +1969,8 @@ DEFUN (sr_prefix_sid, /* Search if this prefix already exist */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { - if ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4) - && srp->nhlfe.prefv4.prefixlen == p.prefixlen)) + if ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4) + && srp->prefv4.prefixlen == p.prefixlen)) break; else srp = NULL; @@ -1965,17 +1978,16 @@ DEFUN (sr_prefix_sid, /* Update or Add this new SR Prefix */ if (srp) { - update_sid_nhlfe(srp->nhlfe, new->nhlfe); listnode_delete(OspfSR.self->ext_prefix, srp); listnode_add(OspfSR.self->ext_prefix, new); } else { listnode_add(OspfSR.self->ext_prefix, new); - add_sid_nhlfe(new->nhlfe); } + ospf_zebra_update_prefix_sid(new); /* Finally, update Extended Prefix LSA */ new->instance = ospf_ext_schedule_prefix_index( - ifp, new->sid, &new->nhlfe.prefv4, new->flags); + ifp, new->sid, &new->prefv4, new->flags); if (new->instance == 0) { vty_out(vty, "Unable to set index %u for prefix %s/%u\n", index, inet_ntoa(p.u.prefix4), p.prefixlen); @@ -2017,8 +2029,8 @@ DEFUN (no_sr_prefix_sid, /* check that the prefix is already set */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) - if (IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4) - && (srp->nhlfe.prefv4.prefixlen == p.prefixlen)) { + if (IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4) + && (srp->prefv4.prefixlen == p.prefixlen)) { found = true; break; } @@ -2043,14 +2055,12 @@ DEFUN (no_sr_prefix_sid, return CMD_WARNING; } - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Remove Prefix %s/%u with index %u", - __func__, inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen, srp->sid); + osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__, + (struct prefix *)&srp->prefv4, srp->sid); - /* Delete NHLFE is NO-PHP is set */ + /* Delete NHLFE if NO-PHP is set */ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) - del_sid_nhlfe(srp->nhlfe); + ospf_zebra_delete_prefix_sid(srp); /* OK, all is clean, remove SRP from SRDB */ listnode_delete(OspfSR.self->ext_prefix, srp); @@ -2060,6 +2070,116 @@ DEFUN (no_sr_prefix_sid, } +static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, + mpls_label_t label_out) +{ + if (size < 24) + return NULL; + + switch (label_out) { + case MPLS_LABEL_IMPLICIT_NULL: + snprintf(buf, size, "Pop(%u)", label_in); + break; + case MPLS_LABEL_IPV4_EXPLICIT_NULL: + snprintf(buf, size, "Swap(%u, null)", label_in); + break; + case MPLS_INVALID_LABEL: + snprintf(buf, size, "no-op."); + break; + default: + snprintf(buf, size, "Swap(%u, %u)", label_in, label_out); + break; + } + return buf; +} + +static void show_sr_prefix(struct sbuf *sbuf, struct json_object *json, + struct sr_prefix *srp) +{ + + struct listnode *node; + struct ospf_path *path; + struct interface *itf; + json_object *json_route = NULL, *json_obj; + char pref[19]; + char sid[22]; + char op[32]; + int indent = 0; + + snprintfrr(pref, 19, "%pFX", (struct prefix *)&srp->prefv4); + snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid); + if (json) { + json_object_string_add(json, "prefix", pref); + json_object_int_add(json, "sid", srp->sid); + json_object_int_add(json, "inputLabel", srp->label_in); + } else { + sbuf_push(sbuf, 0, "%18s %21s ", pref, sid); + } + + /* Check if it is a Local Node SID */ + if (srp->type == LOCAL_SID) { + itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); + if (json) { + if (!json_route) { + json_route = json_object_new_array(); + json_object_object_add(json, "prefixRoute", + json_route); + } + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "outputLabel", + srp->nhlfe.label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_add(json_obj, "nexthop", + inet_ntoa(srp->nhlfe.nexthop)); + json_object_array_add(json_route, json_obj); + } else { + sbuf_push(sbuf, 0, "%20s %9s %15s\n", + sr_op2str(op, 32, srp->label_in, + srp->nhlfe.label_out), + itf ? itf->name : "-", + inet_ntoa(srp->nhlfe.nexthop)); + } + return; + } + + /* Check if we have a valid path for this prefix */ + if (srp->route == NULL) { + if (!json) { + sbuf_push(sbuf, 0, "\n"); + } + return; + } + + /* Process list of OSPF paths */ + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + itf = if_lookup_by_index(path->ifindex, VRF_DEFAULT); + if (json) { + if (!json_route) { + json_route = json_object_new_array(); + json_object_object_add(json, "prefixRoute", + json_route); + } + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "outputLabel", + path->srni.label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_add(json_obj, "nexthop", + inet_ntoa(path->nexthop)); + json_object_array_add(json_route, json_obj); + } else { + sbuf_push(sbuf, indent, "%20s %9s %15s\n", + sr_op2str(op, 32, srp->label_in, + path->srni.label_out), + itf ? itf->name : "-", + inet_ntoa(path->nexthop)); + /* Offset to align information for ECMP */ + indent = 43; + } + } +} + static void show_sr_node(struct vty *vty, struct json_object *json, struct sr_node *srn) { @@ -2068,9 +2188,10 @@ static void show_sr_node(struct vty *vty, struct json_object *json, struct sr_link *srl; struct sr_prefix *srp; struct interface *itf; + struct sbuf sbuf; char pref[19]; char sid[22]; - char label[8]; + char op[32]; json_object *json_node = NULL, *json_algo, *json_obj; json_object *json_prefix = NULL, *json_link = NULL; @@ -2078,6 +2199,8 @@ static void show_sr_node(struct vty *vty, struct json_object *json, if (srn == NULL) return; + sbuf_init(&sbuf, NULL, 0); + if (json) { json_node = json_object_new_object(); json_object_string_add(json_node, "routerID", @@ -2104,41 +2227,31 @@ static void show_sr_node(struct vty *vty, struct json_object *json, if (srn->msd != 0) json_object_int_add(json_node, "nodeMsd", srn->msd); } else { - vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router)); - vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size, - srn->srgb.lower_bound); - vty_out(vty, "\tAlgorithm(s): %s", - srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); + sbuf_push(&sbuf, 0, "SR-Node: %s", inet_ntoa(srn->adv_router)); + sbuf_push(&sbuf, 0, "\tSRGB (Size/Label): %u/%u", + srn->srgb.range_size, srn->srgb.lower_bound); + sbuf_push(&sbuf, 0, "\tAlgorithm(s): %s", + srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); for (int i = 1; i < ALGORITHM_COUNT; i++) { if (srn->algo[i] == SR_ALGORITHM_UNSET) continue; - vty_out(vty, "/%s", - srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" - : "S-SPF"); + sbuf_push(&sbuf, 0, "/%s", + srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" + : "S-SPF"); } if (srn->msd != 0) - vty_out(vty, "\tMSD: %u", srn->msd); + sbuf_push(&sbuf, 0, "\tMSD: %u", srn->msd); } if (!json) { - vty_out(vty, - "\n\n Prefix or Link Label In Label Out " - "Node or Adj. SID Interface Nexthop\n"); - vty_out(vty, - "------------------ -------- --------- " - "--------------------- --------- ---------------\n"); + sbuf_push(&sbuf, 0, + "\n\n Prefix or Link Node or Adj. SID " + " Label Operation Interface Nexthop\n"); + sbuf_push(&sbuf, 0, + "------------------ --------------------- " + "-------------------- --------- ---------------\n"); } for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { - snprintf(pref, sizeof(pref), "%s/%u", - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen); - snprintf(sid, sizeof(sid), "SR Pfx (idx %u)", srp->sid); - if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL) - snprintf(label, sizeof(label), "pop"); - else - snprintf(label, sizeof(label), "%u", - srp->nhlfe.label_out); - itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); if (json) { if (!json_prefix) { json_prefix = json_object_new_array(); @@ -2147,34 +2260,16 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_prefix); } json_obj = json_object_new_object(); - json_object_string_add(json_obj, "prefix", pref); - json_object_int_add(json_obj, "sid", srp->sid); - json_object_int_add(json_obj, "inputLabel", - srp->nhlfe.label_in); - json_object_string_add(json_obj, "outputLabel", label); - json_object_string_add(json_obj, "interface", - itf ? itf->name : "-"); - json_object_string_add(json_obj, "nexthop", - inet_ntoa(srp->nhlfe.nexthop)); + show_sr_prefix(NULL, json_obj, srp); json_object_array_add(json_prefix, json_obj); } else { - vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref, - srp->nhlfe.label_in, label, sid, - itf ? itf->name : "-", - inet_ntoa(srp->nhlfe.nexthop)); + show_sr_prefix(&sbuf, NULL, srp); } } for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) { - snprintf(pref, sizeof(pref), "%s/%u", - inet_ntoa(srl->nhlfe[0].prefv4.prefix), - srl->nhlfe[0].prefv4.prefixlen); - snprintf(sid, sizeof(sid), "SR Adj. (lbl %u)", srl->sid[0]); - if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL) - snprintf(label, sizeof(label), "pop"); - else - snprintf(label, sizeof(label), "%u", - srl->nhlfe[0].label_out); + snprintfrr(pref, 19, "%pI4/32", &srl->itf_addr); + snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]); itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT); if (json) { if (!json_link) { @@ -2188,7 +2283,8 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_object_int_add(json_obj, "sid", srl->sid[0]); json_object_int_add(json_obj, "inputLabel", srl->nhlfe[0].label_in); - json_object_string_add(json_obj, "outputLabel", label); + json_object_int_add(json_obj, "outputLabel", + srl->nhlfe[0].label_out); json_object_string_add(json_obj, "interface", itf ? itf->name : "-"); json_object_string_add( @@ -2197,18 +2293,13 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_object_array_add(json_link, json_obj); /* Backup Link */ json_obj = json_object_new_object(); - snprintf(sid, sizeof(sid), "SR Adj. (lbl %u)", - srl->sid[1]); - if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL) - snprintf(label, sizeof(label), "pop"); - else - snprintf(label, sizeof(label), "%u", - srl->nhlfe[0].label_out); + snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); json_object_string_add(json_obj, "prefix", pref); json_object_int_add(json_obj, "sid", srl->sid[1]); json_object_int_add(json_obj, "inputLabel", srl->nhlfe[1].label_in); - json_object_string_add(json_obj, "outputLabel", label); + json_object_int_add(json_obj, "outputLabel", + srl->nhlfe[1].label_out); json_object_string_add(json_obj, "interface", itf ? itf->name : "-"); json_object_string_add( @@ -2216,27 +2307,27 @@ static void show_sr_node(struct vty *vty, struct json_object *json, inet_ntoa(srl->nhlfe[1].nexthop)); json_object_array_add(json_link, json_obj); } else { - vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref, - srl->nhlfe[0].label_in, label, sid, - itf ? itf->name : "-", - inet_ntoa(srl->nhlfe[0].nexthop)); - snprintf(sid, sizeof(sid), "SR Adj. (lbl %u)", - srl->sid[1]); - if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL) - snprintf(label, sizeof(label), "pop"); - else - snprintf(label, sizeof(label), "%u", - srl->nhlfe[1].label_out); - vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref, - srl->nhlfe[1].label_in, label, sid, - itf ? itf->name : "-", - inet_ntoa(srl->nhlfe[1].nexthop)); + sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n", + pref, sid, + sr_op2str(op, 32, srl->nhlfe[0].label_in, + srl->nhlfe[0].label_out), + itf ? itf->name : "-", + inet_ntoa(srl->nhlfe[0].nexthop)); + snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); + sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n", + pref, sid, + sr_op2str(op, 32, srl->nhlfe[1].label_in, + srl->nhlfe[1].label_out), + itf ? itf->name : "-", + inet_ntoa(srl->nhlfe[1].nexthop)); } } if (json) json_object_array_add(json, json_node); else - vty_out(vty, "\n"); + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + + sbuf_free(&sbuf); } static void show_vty_srdb(struct hash_bucket *bucket, void *args) diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h index df923e970f..3621ea53de 100644 --- a/ospfd/ospf_sr.h +++ b/ospfd/ospf_sr.h @@ -51,6 +51,17 @@ #define SID_INDEX 4 #define SID_INDEX_SIZE(U) (U) +/* Macro to log debug message */ +#define osr_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_SR) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + +/* Macro to check if SR Prefix has no valid route */ +#define IS_NO_ROUTE(srp) ((srp->route == NULL) || (srp->route->paths == NULL) \ + || list_isempty(srp->route->paths)) + /* SID/Label Sub TLV - section 2.1 */ #define SUBTLV_SID_LABEL 1 #define SUBTLV_SID_LABEL_SIZE 8 @@ -180,16 +191,13 @@ struct sr_srgb { }; /* SID type to make difference between loopback interfaces and others */ -enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID }; +enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID }; /* Structure aggregating all OSPF Segment Routing information for the node */ struct ospf_sr_db { /* Status of Segment Routing: enable or disable */ bool enabled; - /* Ongoing Update following an OSPF SPF */ - bool update; - /* Flooding Scope: Area = 10 or AS = 11 */ uint8_t scope; @@ -237,7 +245,6 @@ struct sr_node { /* Segment Routing - NHLFE info: support IPv4 Only */ struct sr_nhlfe { - struct prefix_ipv4 prefv4; struct in_addr nexthop; ifindex_t ifindex; mpls_label_t label_in; @@ -251,6 +258,9 @@ struct sr_link { /* 24-bit Opaque-ID field value according to RFC 7684 specification */ uint32_t instance; + /* Interface address */ + struct in_addr itf_addr; + /* Flags to manage this link parameters. */ uint8_t flags[2]; @@ -258,7 +268,7 @@ struct sr_link { uint32_t sid[2]; enum sid_type type; - /* SR NHLFE for this link */ + /* SR NHLFE (Primary + Backup) for this link */ struct sr_nhlfe nhlfe[2]; /* Back pointer to SR Node which advertise this Link */ @@ -271,6 +281,9 @@ struct sr_prefix { /* 24-bit Opaque-ID field value according to RFC 7684 specification */ uint32_t instance; + /* Prefix itself */ + struct prefix_ipv4 prefv4; + /* Flags to manage this prefix parameters. */ uint8_t flags; @@ -278,17 +291,17 @@ struct sr_prefix { uint32_t sid; enum sid_type type; - /* SR NHLFE for this prefix */ + /* Incoming label for this prefix */ + mpls_label_t label_in; + + /* Back pointer to OSPF Route for remote prefix */ + struct ospf_route *route; + + /* NHLFE for local prefix */ struct sr_nhlfe nhlfe; /* Back pointer to SR Node which advertise this Prefix */ struct sr_node *srn; - - /* - * Pointer to SR Node which is the next hop for this Prefix - * or NULL if next hop is the destination of the prefix - */ - struct sr_node *nexthop; }; /* Prototypes definition */ @@ -303,10 +316,15 @@ extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa); extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa); extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa); extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa); +/* Segment Routing Extending Link management */ +struct ext_itf; +extern void ospf_sr_ext_itf_add(struct ext_itf *exti); +extern void ospf_sr_ext_itf_delete(struct ext_itf *exti); /* Segment Routing configuration functions */ extern uint32_t get_ext_link_label_value(void); extern void ospf_sr_config_write_router(struct vty *vty); -extern void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p); +extern void ospf_sr_update_local_prefix(struct interface *ifp, + struct prefix *p); /* Segment Routing re-routing function */ -extern void ospf_sr_update_timer_add(struct ospf *ospf); +extern void ospf_sr_update_task(struct ospf *ospf); #endif /* _FRR_OSPF_SR_H */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index e8e2fbeaa6..644ea7f922 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -50,6 +50,7 @@ #include "ospfd/ospf_nsm.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_te.h" +#include "ospfd/ospf_sr.h" DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table") DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute") @@ -415,6 +416,119 @@ void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance) } } +/* Update NHLFE for Prefix SID */ +void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + struct listnode *node; + struct ospf_path *path; + + osr_debug("SR (%s): Update Labels %u for Prefix %pFX", __func__, + srp->label_in, (struct prefix *)&srp->prefv4); + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = srp->label_in; + + switch (srp->type) { + case LOCAL_SID: + /* Set Label for local Prefix */ + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IFINDEX; + znh->ifindex = srp->nhlfe.ifindex; + znh->label_num = 1; + znh->labels[0] = srp->nhlfe.label_out; + break; + + case PREF_SID: + /* Update route in the RIB too. */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.u.prefix4 = srp->prefv4.prefix; + zl.route.prefix.prefixlen = srp->prefv4.prefixlen; + zl.route.prefix.family = srp->prefv4.family; + zl.route.type = ZEBRA_ROUTE_OSPF; + zl.route.instance = 0; + + /* Check that SRP contains at least one valid path */ + if (srp->route == NULL) { + return; + } + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + if (path->srni.label_out == MPLS_INVALID_LABEL) + continue; + + if (zl.nexthop_num >= MULTIPATH_NUM) + break; + + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + znh->gate.ipv4 = path->nexthop; + znh->ifindex = path->ifindex; + znh->label_num = 1; + znh->labels[0] = path->srni.label_out; + } + break; + default: + return; + } + + /* Finally, send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl); +} + +/* Remove NHLFE for Prefix-SID */ +void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + + osr_debug("SR (%s): Delete Labels %u for Prefix %pFX", __func__, + srp->label_in, (struct prefix *)&srp->prefv4); + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = srp->label_in; + + if (srp->type == PREF_SID) { + /* Update route in the RIB too */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.u.prefix4 = srp->prefv4.prefix; + zl.route.prefix.prefixlen = srp->prefv4.prefixlen; + zl.route.prefix.family = srp->prefv4.family; + zl.route.type = ZEBRA_ROUTE_OSPF; + zl.route.instance = 0; + } + + /* Send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl); +} + +/* Send MPLS Label entry to Zebra for installation or deletion */ +void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + + osr_debug("SR (%s): %s Labels %u/%u for Adjacency via %u", __func__, + cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", + nhlfe.label_in, nhlfe.label_out, nhlfe.ifindex); + + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = nhlfe.label_in; + zl.nexthop_num = 1; + znh = &zl.nexthops[0]; + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + znh->gate.ipv4 = nhlfe.nexthop; + znh->ifindex = nhlfe.ifindex; + znh->label_num = 1; + znh->labels[0] = nhlfe.label_out; + + (void)zebra_send_mpls_labels(zclient, cmd, &zl); +} + struct ospf_redist *ospf_redist_lookup(struct ospf *ospf, uint8_t type, unsigned short instance) { @@ -1351,6 +1465,7 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf) zclient_send_dereg_requests(zclient, ospf->vrf_id); } } + static void ospf_zebra_connected(struct zclient *zclient) { /* Send the client registration */ diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index d3f8a0380b..253d2e0a3f 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -63,6 +63,13 @@ extern struct ospf_external *ospf_external_lookup(struct ospf *, uint8_t, unsigned short); extern struct ospf_external *ospf_external_add(struct ospf *, uint8_t, unsigned short); + +struct sr_prefix; +struct sr_nhlfe; +extern void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp); +extern void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp); +extern void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe); + extern void ospf_external_del(struct ospf *, uint8_t, unsigned short); extern struct ospf_redist *ospf_redist_lookup(struct ospf *, uint8_t, unsigned short); @@ -70,7 +77,6 @@ extern struct ospf_redist *ospf_redist_add(struct ospf *, uint8_t, unsigned short); extern void ospf_redist_del(struct ospf *, uint8_t, unsigned short); - extern int ospf_redistribute_set(struct ospf *, int, unsigned short, int, int); extern int ospf_redistribute_unset(struct ospf *, int, unsigned short); extern int ospf_redistribute_default_set(struct ospf *, int, int, int); diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index b228847f06..57338738f6 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -113,6 +113,7 @@ struct quagga_signal_t pbr_signals[] = { #define PBR_VTY_PORT 2615 static const struct frr_yang_module_info *const pbrd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, }; diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 5c4c7151a5..132d913f68 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -73,6 +73,7 @@ struct zebra_privs_t pimd_privs = { .cap_num_i = 0}; static const struct frr_yang_module_info *const pimd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_vrf_info, diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c index 78be914cee..02f206144f 100644 --- a/pimd/pim_mlag.c +++ b/pimd/pim_mlag.c @@ -721,14 +721,19 @@ static void pim_mlag_process_vxlan_update(struct mlag_vxlan *msg) static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg) { if (PIM_DEBUG_MLAG) { + struct prefix_sg sg; + + sg.grp.s_addr = ntohl(msg.group_ip); + sg.src.s_addr = ntohl(msg.source_ip); + zlog_debug( - "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x cost: %u", - __func__, msg.vrf_name, msg.source_ip, - msg.group_ip, msg.cost_to_rp); + "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x (%pSG4) cost: %u", + __func__, msg.vrf_name, msg.source_ip, msg.group_ip, + &sg, msg.cost_to_rp); zlog_debug( - "owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s", - msg.owner_id, msg.am_i_dr, msg.am_i_dual_active, - msg.vrf_id, msg.intf_name); + "(%pSG4)owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s", + &sg, msg.owner_id, msg.am_i_dr, msg.am_i_dual_active, + msg.vrf_id, msg.intf_name); } if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { @@ -746,12 +751,16 @@ static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg) static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg) { if (PIM_DEBUG_MLAG) { + struct prefix_sg sg; + + sg.grp.s_addr = ntohl(msg.group_ip); + sg.src.s_addr = ntohl(msg.source_ip); zlog_debug( - "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x ", - __func__, msg.vrf_name, msg.source_ip, - msg.group_ip); - zlog_debug("owner_id: %d, vrf_id: 0x%x intf_name: %s", - msg.owner_id, msg.vrf_id, msg.intf_name); + "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x(%pSG4)", + __func__, msg.vrf_name, msg.source_ip, msg.group_ip, + &sg); + zlog_debug("(%pSG4)owner_id: %d, vrf_id: 0x%x intf_name: %s", + &sg, msg.owner_id, msg.vrf_id, msg.intf_name); } if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 21febcc969..3ec0720fc4 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -375,6 +375,20 @@ static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil, return do_mute; } + if (PIM_I_am_DualActive(pim_ifp)) { + struct pim_upstream *starup = c_oil->up->parent; + if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags) + && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) + do_mute = true; + + /* In case entry is (S,G), Negotiation happens at (*.G) */ + if (starup + + && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags) + && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags))) + do_mute = true; + return do_mute; + } return do_mute; } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index b3379c67b2..8030835fb2 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -286,7 +286,8 @@ static inline bool pim_up_mlag_is_local(struct pim_upstream *up) /* XXX: extend this to also return true if the channel-oil has * any AA devices */ - return (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN); + return (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN + | PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)); } struct pim_upstream *pim_upstream_find(struct pim_instance *pim, diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 9ec32a53e3..7e381887fc 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -114,6 +114,7 @@ static struct quagga_signal_t ripd_signals[] = { }; static const struct frr_yang_module_info *const ripd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_ripd_info, &frr_route_map_info, diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index fbac750db3..010bac851b 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -114,6 +114,7 @@ struct quagga_signal_t ripng_signals[] = { }; static const struct frr_yang_module_info *const ripngd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_ripngd_info, &frr_route_map_info, diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index 120d704918..db65836388 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -112,6 +112,7 @@ struct quagga_signal_t sharp_signals[] = { #define SHARP_VTY_PORT 2614 static const struct frr_yang_module_info *const sharpd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_vrf_info, diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 77d0d02e7d..72e9c22f17 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -550,14 +550,41 @@ DEFPY (logpump, DEFPY (send_opaque, send_opaque_cmd, "sharp send opaque type (1-255) (1-1000)$count", - "Sharp Routing Protocol\n" + SHARP_STR "Send messages for testing\n" "Send opaque messages\n" "Type code to send\n" "Type code to send\n" "Number of messages to send\n") { - sharp_opaque_send(type, count); + sharp_opaque_send(type, 0, 0, 0, count); + return CMD_SUCCESS; +} + +DEFPY (send_opaque_unicast, + send_opaque_unicast_cmd, + "sharp send opaque unicast type (1-255) \ + " FRR_IP_REDIST_STR_ZEBRA "$proto_str \ + [{instance (0-1000) | session (1-1000)}] (1-1000)$count", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Send unicast messages\n" + "Type code to send\n" + "Type code to send\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Daemon instance\n" + "Daemon instance\n" + "Session ID\n" + "Session ID\n" + "Number of messages to send\n") +{ + uint32_t proto; + + proto = proto_redistnum(AFI_IP, proto_str); + + sharp_opaque_send(type, proto, instance, session, count); + return CMD_SUCCESS; } @@ -566,7 +593,7 @@ DEFPY (send_opaque_reg, "sharp send opaque <reg$reg | unreg> \ " FRR_IP_REDIST_STR_ZEBRA "$proto_str \ [{instance (0-1000) | session (1-1000)}] type (1-1000)", - "Sharp Routing Protocol\n" + SHARP_STR "Send messages for testing\n" "Send opaque messages\n" "Send opaque registration\n" @@ -600,6 +627,7 @@ void sharp_vty_init(void) install_element(ENABLE_NODE, &sharp_remove_lsp_prefix_v4_cmd); install_element(ENABLE_NODE, &logpump_cmd); install_element(ENABLE_NODE, &send_opaque_cmd); + install_element(ENABLE_NODE, &send_opaque_unicast_cmd); install_element(ENABLE_NODE, &send_opaque_reg_cmd); install_element(VIEW_NODE, &show_debugging_sharpd_cmd); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 34cc1a4b5a..6ebc04b9eb 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -483,16 +483,16 @@ static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS) /* Handler for opaque messages */ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) { - uint32_t type; struct stream *s; + struct zapi_opaque_msg info; s = zclient->ibuf; - STREAM_GETL(s, type); - - zlog_debug("%s: received opaque type %u", __func__, type); + if (zclient_opaque_decode(s, &info) != 0) + return -1; -stream_failure: + zlog_debug("%s: [%d] received opaque type %u", __func__, + zclient->session_id, info.type); return 0; } @@ -500,7 +500,8 @@ stream_failure: /* * Send OPAQUE messages, using subtype 'type'. */ -void sharp_opaque_send(uint32_t type, uint32_t count) +void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, + uint32_t session_id, uint32_t count) { uint8_t buf[32]; int ret; @@ -514,9 +515,15 @@ void sharp_opaque_send(uint32_t type, uint32_t count) buf[i] = 255; } - /* Send some messages */ + /* Send some messages - broadcast and unicast are supported */ for (i = 0; i < count; i++) { - ret = zclient_send_opaque(zclient, type, buf, sizeof(buf)); + if (proto == 0) + ret = zclient_send_opaque(zclient, type, buf, + sizeof(buf)); + else + ret = zclient_send_opaque_unicast(zclient, type, proto, + instance, session_id, + buf, sizeof(buf)); if (ret < 0) { zlog_debug("%s: send_opaque() failed => %d", __func__, ret); diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 7c714b52d3..7604f4b0a4 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -46,7 +46,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p, const struct nexthop_group *backup_nhg); /* Send OPAQUE messages, using subtype 'type'. */ -void sharp_opaque_send(uint32_t type, uint32_t count); +void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, + uint32_t session_id, uint32_t count); /* Send OPAQUE registration messages, using subtype 'type'. */ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, diff --git a/staticd/static_main.c b/staticd/static_main.c index c77a99f280..08062f19d8 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -104,6 +104,7 @@ struct quagga_signal_t static_signals[] = { }; static const struct frr_yang_module_info *const staticd_yang_modules[] = { + &frr_filter_info, &frr_vrf_info, }; diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py index ebd6075b52..459af486ff 100644 --- a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py +++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py @@ -102,12 +102,12 @@ def test_bgp_as_wide_bgp_identifier(): return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) - success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5) assert result is None, 'Failed to converge: "{}"'.format(tgen.gears["r1"]) test_func = functools.partial(_bgp_failed, tgen.gears["r3"]) - success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5) assert result is None, 'Bad BGP Identifier notification not sent: "{}"'.format( tgen.gears["r3"] diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py index 18e36311ad..5c2af2b30b 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py +++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py @@ -121,7 +121,7 @@ def test_ebgp_requires_policy(): return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge, "r2") - success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=65, wait=2) assert success is True, 'Failed bgp convergence (r2) in "{}"'.format(tgen.gears["r2"]) test_func = functools.partial(_bgp_has_routes, "r2") @@ -129,7 +129,7 @@ def test_ebgp_requires_policy(): assert success is True, 'eBGP policy is not working (r2) in "{}"'.format(tgen.gears["r2"]) test_func = functools.partial(_bgp_converge, "r4") - success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=65, wait=2) assert success is True, 'Failed bgp convergence (r4) in "{}"'.format(tgen.gears["r4"]) test_func = functools.partial(_bgp_has_routes, "r4") @@ -137,7 +137,7 @@ def test_ebgp_requires_policy(): assert success is False, 'eBGP policy is not working (r4) in "{}"'.format(tgen.gears["r4"]) test_func = functools.partial(_bgp_converge, "r6") - success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=65, wait=2) assert success is True, 'Failed bgp convergence (r6) in "{}"'.format(tgen.gears["r6"]) test_func = functools.partial(_bgp_has_routes, "r6") diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 69c807f300..2ba0c68c2f 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -868,7 +868,7 @@ def verify_router_id(tgen, topo, input_dict): return True -@retry(attempts=20, wait=2, return_is_str=True) +@retry(attempts=44, wait=3, return_is_str=True) def verify_bgp_convergence(tgen, topo, dut=None): """ API will verify if BGP is converged with in the given time frame. diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 414dc17874..efd5b90685 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -658,7 +658,7 @@ class TopoRouter(TopoGear): Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP, TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, - TopoRouter.RD_PIM. + TopoRouter.RD_PIM, TopoRouter.RD_PBR. """ daemonstr = self.RD.get(daemon) self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source)) @@ -1064,6 +1064,7 @@ def diagnose_env_linux(): "isisd", "pimd", "ldpd", + "pbrd" ]: path = os.path.join(frrdir, fname) if not os.path.isfile(path): @@ -1121,6 +1122,7 @@ def diagnose_env_linux(): "ripngd", "isisd", "pimd", + "pbrd" ]: path = os.path.join(quaggadir, fname) if not os.path.isfile(path): diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 6262082193..9d945d5262 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -727,6 +727,57 @@ def ip6_route(node): return result +def ip_rules(node): + """ + Gets a structured return of the command 'ip rule'. It can be used in + conjuction with json_cmp() to provide accurate assert explanations. + + Return example: + [ + { + "pref": "0" + "from": "all" + }, + { + "pref": "32766" + "from": "all" + }, + { + "to": "3.4.5.0/24", + "iif": "r1-eth2", + "pref": "304", + "from": "1.2.0.0/16", + "proto": "zebra" + } + ] + """ + output = normalize_text(node.run("ip rule")).splitlines() + result = [] + for line in output: + columns = line.split(" ") + + route = {} + # remove last character, since it is ':' + pref = columns[0][:-1] + route["pref"] = pref + prev = None + for column in columns: + if prev == "from": + route["from"] = column + if prev == "to": + route["to"] = column + if prev == "proto": + route["proto"] = column + if prev == "iif": + route["iif"] = column + if prev == "fwmark": + route["fwmark"] = column + prev = column + + result.append(route) + return result + + def sleep(amount, reason=None): """ Sleep wrapper that registers in the log the amount of sleep diff --git a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json index 4ffca6f940..0416bd6ce2 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json @@ -15,9 +15,18 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":20200, - "outputLabel":"pop", - "interface":"r1-eth0", - "nexthop":"10.0.1.2" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "outputLabel":3, + "interface":"r1-eth1", + "nexthop":"10.0.1.2" + } + ] } ] }, @@ -36,9 +45,18 @@ "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":20400, - "outputLabel":"8400", - "interface":"r1-eth0", - "nexthop":"10.0.1.2" + "prefixRoute":[ + { + "outputLabel":8400, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "outputLabel":8400, + "interface":"r1-eth1", + "nexthop":"10.0.1.2" + } + ] } ] }, @@ -47,19 +65,24 @@ "srgbSize":10000, "srgbLabel":10000, "algorithms":[ - { - "0":"SPF" - } ], - "nodeMsd":8, "extendedPrefix":[ { "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":20300, - "outputLabel":"8300", - "interface":"r1-eth0", - "nexthop":"10.0.1.2" + "prefixRoute":[ + { + "outputLabel":8300, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "outputLabel":8300, + "interface":"r1-eth1", + "nexthop":"10.0.1.2" + } + ] } ] }, @@ -78,26 +101,46 @@ "prefix":"10.0.255.1\/32", "sid":100, "inputLabel":20100, - "outputLabel":"pop", - "interface":"lo", - "nexthop":"10.0.255.1" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"lo", + "nexthop":"10.0.255.1" + } + ] } ], "extendedLink":[ { - "prefix":"10.0.1.1\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", + "prefix":"10.0.0.1\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "prefix":"10.0.0.1\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "prefix":"10.0.1.1\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r1-eth1", "nexthop":"10.0.1.2" }, { "prefix":"10.0.1.1\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", - "interface":"r1-eth0", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r1-eth1", "nexthop":"10.0.1.2" } ] diff --git a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf index e8593d1a1a..292d4e6367 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf @@ -3,6 +3,11 @@ interface lo ip ospf area 0.0.0.0 ! interface r1-eth0 + ip ospf network point-to-point + ip ospf area 0.0.0.0 +! +interface r1-eth1 + ip ospf network point-to-point ip ospf area 0.0.0.0 ! router ospf diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra.conf b/tests/topotests/ospf-sr-topo1/r1/zebra.conf index f1fcc7dd3b..faf71db25c 100644 --- a/tests/topotests/ospf-sr-topo1/r1/zebra.conf +++ b/tests/topotests/ospf-sr-topo1/r1/zebra.conf @@ -3,6 +3,9 @@ interface lo ip address 10.0.255.1/32 ! interface r1-eth0 + ip address 10.0.0.1/24 +! +interface r1-eth1 ip address 10.0.1.1/24 ! ip forwarding diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json index 6b1fe76b6e..5ae2399e5c 100644 --- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json @@ -1,5 +1,5 @@ -{ - "20100":{ +[ + { "inLabel":20100, "installed":true, "nexthops":[ @@ -7,12 +7,11 @@ "type":"SR (OSPF)", "outLabel":3, "distance":150, - "installed":true, - "nexthop":"10.0.255.1" + "installed":true } ] }, - "20200":{ + { "inLabel":20200, "installed":true, "nexthops":[ @@ -22,10 +21,17 @@ "distance":150, "installed":true, "nexthop":"10.0.1.2" + }, + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.0.2" } ] }, - "20300":{ + { "inLabel":20300, "installed":true, "nexthops":[ @@ -35,10 +41,17 @@ "distance":150, "installed":true, "nexthop":"10.0.1.2" + }, + { + "type":"SR (OSPF)", + "outLabel":8300, + "distance":150, + "installed":true, + "nexthop":"10.0.0.2" } ] }, - "20400":{ + { "inLabel":20400, "installed":true, "nexthops":[ @@ -48,11 +61,44 @@ "distance":150, "installed":true, "nexthop":"10.0.1.2" + }, + { + "type":"SR (OSPF)", + "outLabel":8400, + "distance":150, + "installed":true, + "nexthop":"10.0.0.2" + } + ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.0.2" + } + ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.0.2" } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -64,8 +110,8 @@ } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -77,4 +123,4 @@ } ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json index 2548299cc7..eb202b82cd 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json @@ -15,59 +15,79 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":0, - "outputLabel":"0", - "interface":"lo", - "nexthop":"10.0.255.2" + "prefixRoute":[ + { + "outputLabel":0, + "interface":"lo", + "nexthop":"10.0.255.2" + } + ] } ], "extendedLink":[ { "prefix":"10.0.4.2\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", - "interface":"r2-eth2", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth3", "nexthop":"10.0.4.1" }, { "prefix":"10.0.4.2\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", - "interface":"r2-eth2", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth3", "nexthop":"10.0.4.1" }, { - "prefix":"10.0.3.2\/32", - "sid":50003, - "inputLabel":50003, - "outputLabel":"pop", - "interface":"r2-eth1", - "nexthop":"10.0.3.1" + "prefix":"10.0.0.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth0", + "nexthop":"10.0.0.1" }, { - "prefix":"10.0.3.2\/32", - "sid":50002, - "inputLabel":50002, - "outputLabel":"pop", - "interface":"r2-eth1", - "nexthop":"10.0.3.1" + "prefix":"10.0.0.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth0", + "nexthop":"10.0.0.1" }, { "prefix":"10.0.1.2\/32", - "sid":50005, - "inputLabel":50005, - "outputLabel":"pop", - "interface":"r2-eth0", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth1", "nexthop":"10.0.1.1" }, { "prefix":"10.0.1.2\/32", - "sid":50004, - "inputLabel":50004, - "outputLabel":"pop", - "interface":"r2-eth0", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth1", "nexthop":"10.0.1.1" + }, + { + "prefix":"10.0.3.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth2", + "nexthop":"10.0.3.1" + }, + { + "prefix":"10.0.3.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth2", + "nexthop":"10.0.3.1" } ] }, @@ -76,19 +96,19 @@ "srgbSize":10000, "srgbLabel":10000, "algorithms":[ - { - "0":"SPF" - } ], - "nodeMsd":12, "extendedPrefix":[ { "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":8400, - "outputLabel":"10400", - "interface":"r2-eth2", - "nexthop":"10.0.4.1" + "prefixRoute":[ + { + "outputLabel":10400, + "interface":"r2-eth3", + "nexthop":"10.0.4.1" + } + ] } ] }, @@ -97,19 +117,19 @@ "srgbSize":10000, "srgbLabel":10000, "algorithms":[ - { - "0":"SPF" - } ], - "nodeMsd":8, "extendedPrefix":[ { "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":8300, - "outputLabel":"pop", - "interface":"r2-eth1", - "nexthop":"10.0.3.1" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r2-eth2", + "nexthop":"10.0.3.1" + } + ] } ] }, @@ -118,19 +138,24 @@ "srgbSize":10000, "srgbLabel":20000, "algorithms":[ - { - "0":"SPF" - } ], - "nodeMsd":16, "extendedPrefix":[ { "prefix":"10.0.255.1\/32", "sid":100, "inputLabel":8100, - "outputLabel":"20100", - "interface":"r2-eth0", - "nexthop":"10.0.1.1" + "prefixRoute":[ + { + "outputLabel":20100, + "interface":"r2-eth0", + "nexthop":"10.0.0.1" + }, + { + "outputLabel":20100, + "interface":"r2-eth1", + "nexthop":"10.0.1.1" + } + ] } ] } diff --git a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf index 4d6146aaa7..b8c7b999e8 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf @@ -5,6 +5,7 @@ interface lo ip ospf area 0.0.0.0 ! interface r2-eth0 + ip ospf network point-to-point ip ospf area 0.0.0.0 ! interface r2-eth1 @@ -12,6 +13,9 @@ interface r2-eth1 ip ospf area 0.0.0.0 ! interface r2-eth2 + ip ospf area 0.0.0.0 +! +interface r2-eth3 ip ospf network point-to-point ip ospf area 0.0.0.0 ! diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra.conf b/tests/topotests/ospf-sr-topo1/r2/zebra.conf index f89548d8c5..ba1d833f50 100644 --- a/tests/topotests/ospf-sr-topo1/r2/zebra.conf +++ b/tests/topotests/ospf-sr-topo1/r2/zebra.conf @@ -3,12 +3,15 @@ interface lo ip address 10.0.255.2/32 ! interface r2-eth0 - ip address 10.0.1.2/24 + ip address 10.0.0.2/24 ! interface r2-eth1 - ip address 10.0.3.2/24 + ip address 10.0.1.2/24 ! interface r2-eth2 + ip address 10.0.3.2/24 +! +interface r2-eth3 ip address 10.0.4.2/24 ! ip forwarding diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json index 79965d280a..aedcc5b8f8 100644 --- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json @@ -1,5 +1,5 @@ -{ - "8100":{ +[ + { "inLabel":8100, "installed":true, "nexthops":[ @@ -9,10 +9,17 @@ "distance":150, "installed":true, "nexthop":"10.0.1.1" + }, + { + "type":"SR (OSPF)", + "outLabel":20100, + "distance":150, + "installed":true, + "nexthop":"10.0.0.1" } ] }, - "8300":{ + { "inLabel":8300, "installed":true, "nexthops":[ @@ -25,7 +32,7 @@ } ] }, - "8400":{ + { "inLabel":8400, "installed":true, "nexthops":[ @@ -38,8 +45,8 @@ } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -51,8 +58,8 @@ } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -64,8 +71,8 @@ } ] }, - "50002":{ - "inLabel":50002, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -73,12 +80,12 @@ "outLabel":3, "distance":150, "installed":true, - "nexthop":"10.0.3.1" + "nexthop":"10.0.0.1" } ] }, - "50003":{ - "inLabel":50003, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -86,12 +93,12 @@ "outLabel":3, "distance":150, "installed":true, - "nexthop":"10.0.3.1" + "nexthop":"10.0.0.1" } ] }, - "50004":{ - "inLabel":50004, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -103,8 +110,8 @@ } ] }, - "50005":{ - "inLabel":50005, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -115,5 +122,31 @@ "nexthop":"10.0.1.1" } ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.3.1" + } + ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.3.1" + } + ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json index 4b618cc7ad..b36fe674ad 100644 --- a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json @@ -15,9 +15,13 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":10200, - "outputLabel":"pop", - "interface":"r3-eth0", - "nexthop":"10.0.3.2" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r3-eth0", + "nexthop":"10.0.3.2" + } + ] } ] }, @@ -36,9 +40,13 @@ "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":10400, - "outputLabel":"8400", - "interface":"r3-eth0", - "nexthop":"10.0.3.2" + "prefixRoute":[ + { + "outputLabel":8400, + "interface":"r3-eth0", + "nexthop":"10.0.3.2" + } + ] } ] }, @@ -57,25 +65,29 @@ "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":0, - "outputLabel":"0", - "interface":"lo", - "nexthop":"10.0.255.3" + "prefixRoute":[ + { + "outputLabel":0, + "interface":"lo", + "nexthop":"10.0.255.3" + } + ] } ], "extendedLink":[ { "prefix":"10.0.3.1\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r3-eth0", "nexthop":"10.0.3.2" }, { "prefix":"10.0.3.1\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r3-eth0", "nexthop":"10.0.3.2" } @@ -96,9 +108,13 @@ "prefix":"10.0.255.1\/32", "sid":100, "inputLabel":10100, - "outputLabel":"8100", - "interface":"r3-eth0", - "nexthop":"10.0.3.2" + "prefixRoute":[ + { + "outputLabel":8100, + "interface":"r3-eth0", + "nexthop":"10.0.3.2" + } + ] } ] } diff --git a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf index 5aaa14fac4..cf274bed2e 100644 --- a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf @@ -3,7 +3,6 @@ interface lo ip ospf area 0.0.0.0 ! interface r3-eth0 - ip ospf network point-to-point ip ospf area 0.0.0.0 ! ! diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json index ceb2f7a0e5..71e8366137 100644 --- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json @@ -1,5 +1,5 @@ -{ - "10100":{ +[ + { "inLabel":10100, "installed":true, "nexthops":[ @@ -12,7 +12,7 @@ } ] }, - "10200":{ + { "inLabel":10200, "installed":true, "nexthops":[ @@ -25,7 +25,7 @@ } ] }, - "10400":{ + { "inLabel":10400, "installed":true, "nexthops":[ @@ -38,8 +38,8 @@ } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -51,8 +51,8 @@ } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -64,4 +64,4 @@ } ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json index 098e87dc25..d92ec91c72 100644 --- a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json @@ -6,18 +6,19 @@ "srgbSize":20000, "srgbLabel":8000, "algorithms":[ - { - "0":"SPF" - } ], "extendedPrefix":[ { "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":10200, - "outputLabel":"pop", - "interface":"r4-eth0", - "nexthop":"10.0.4.2" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r4-eth0", + "nexthop":"10.0.4.2" + } + ] } ] }, @@ -36,25 +37,29 @@ "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":10400, - "outputLabel":"pop", - "interface":"lo", - "nexthop":"10.0.255.4" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"lo", + "nexthop":"10.0.255.4" + } + ] } ], "extendedLink":[ { "prefix":"10.0.4.1\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r4-eth0", "nexthop":"10.0.4.2" }, { "prefix":"10.0.4.1\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r4-eth0", "nexthop":"10.0.4.2" } @@ -65,19 +70,19 @@ "srgbSize":10000, "srgbLabel":10000, "algorithms":[ - { - "0":"SPF" - } ], - "nodeMsd":8, "extendedPrefix":[ { "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":10300, - "outputLabel":"8300", - "interface":"r4-eth0", - "nexthop":"10.0.4.2" + "prefixRoute":[ + { + "outputLabel":8300, + "interface":"r4-eth0", + "nexthop":"10.0.4.2" + } + ] } ] }, @@ -86,19 +91,19 @@ "srgbSize":10000, "srgbLabel":20000, "algorithms":[ - { - "0":"SPF" - } ], - "nodeMsd":16, "extendedPrefix":[ { "prefix":"10.0.255.1\/32", "sid":100, "inputLabel":10100, - "outputLabel":"8100", - "interface":"r4-eth0", - "nexthop":"10.0.4.2" + "prefixRoute":[ + { + "outputLabel":8100, + "interface":"r4-eth0", + "nexthop":"10.0.4.2" + } + ] } ] } diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json index d7f54b224d..b5767e1d7d 100644 --- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json @@ -1,5 +1,5 @@ -{ - "10100":{ +[ + { "inLabel":10100, "installed":true, "nexthops":[ @@ -12,7 +12,7 @@ } ] }, - "10200":{ + { "inLabel":10200, "installed":true, "nexthops":[ @@ -25,7 +25,7 @@ } ] }, - "10300":{ + { "inLabel":10300, "installed":true, "nexthops":[ @@ -38,7 +38,7 @@ } ] }, - "10400":{ + { "inLabel":10400, "installed":true, "nexthops":[ @@ -46,13 +46,12 @@ "type":"SR (OSPF)", "outLabel":3, "distance":150, - "installed":true, - "nexthop":"10.0.255.4" + "installed":true } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -64,8 +63,8 @@ } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { @@ -77,4 +76,4 @@ } ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py index 92cebfe0b6..6792c56b3b 100755 --- a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py +++ b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py @@ -27,7 +27,9 @@ test_ospf_sr_topo1.py: Test the FRR OSPF routing daemon with Segment Routing. """ import os +import re import sys +import json from functools import partial # Save the Current Working Directory to find configuration files. @@ -62,20 +64,23 @@ class OspfSrTopo(Topo): for routern in range(1, 5): tgen.add_router("r{}".format(routern)) - # Interconect router 1 and 2 - switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) + # Interconect router 1 and 2 with 2 links + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) # Interconect router 3 and 2 - switch = tgen.add_switch("s2") - switch.add_link(tgen.gears["r3"]) - switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r3']) + switch.add_link(tgen.gears['r2']) # Interconect router 4 and 2 - switch = tgen.add_switch("s3") - switch.add_link(tgen.gears["r4"]) - switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['r4']) + switch.add_link(tgen.gears['r2']) def setup_module(mod): @@ -99,19 +104,6 @@ def setup_module(mod): # Initialize all routers. tgen.start_router() - # Verify that version, MPLS and Segment Routing are OK - for router in router_list.values(): - # Check for Version - if router.has_version("<", "4"): - tgen.set_error("Unsupported FRR version") - break - # Check that Segment Routing is available - output = tgen.gears[router.name].vtysh_cmd( - "show ip ospf database segment-routing json" - ) - if output.find("Unknown") != -1: - tgen.set_error("Segment Routing is not available") - def teardown_module(mod): "Teardown the pytest environment" @@ -122,31 +114,6 @@ def teardown_module(mod): logger.info("\n\n---- OSPF Segment Routing tests End ----\n") -# Shared test function to validate expected output. -def compare_ospf_srdb(rname, expected): - """ - Calls 'show ip ospf database segment-routing json' for router `rname` - and compare the obtained result with the expected output. - """ - tgen = get_topogen() - current = tgen.gears[rname].vtysh_cmd("show ip ospf database segment-routing json") - return topotest.difflines( - current, expected, title1="Current output", title2="Expected output" - ) - - -def compare_mpls_table(rname, expected): - """ - Calls 'show mpls table json' for router `rname` and compare the obtained - result with the expected output. - """ - tgen = get_topogen() - current = tgen.gears[rname].vtysh_cmd("show mpls table json") - return topotest.difflines( - current, expected, title1="Current output", title2="Expected output" - ) - - def test_ospf_sr(): "Test OSPF daemon Segment Routing" tgen = get_topogen() @@ -162,12 +129,15 @@ def test_ospf_sr(): # Load expected results from the command reffile = os.path.join(CWD, "{}/ospf_srdb.json".format(router)) - expected = open(reffile).read() + expected = json.loads(open(reffile).read()) # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(compare_ospf_srdb, router, expected) - result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3) - assert result, ("OSPF did not start Segment Routing on {}:\n{}").format( + rt = tgen.gears[router] + test_func = partial( + topotest.router_json_cmp, rt, 'show ip ospf database segment-routing json', expected + ) + rv, diff = topotest.run_and_expect(test_func, None, count=25, wait=3) + assert rv, "OSPF did not start Segment Routing on {}:\n{}".format( router, diff ) @@ -180,6 +150,33 @@ def test_ospf_kernel_route(): logger.info("--- test OSPF Segment Routing MPLS tables ---") + def show_mpls_table_json_cmp(rt, expected): + """ + Reformat MPLS table output to use a list of labels instead of dict. + + Original: + { + "X": { + inLabel: "X", + # ... + } + } + + List format: + [ + { + inLabel: "X", + } + ] + """ + out = rt.vtysh_cmd('show mpls table json', isjson=True) + + outlist = [] + for key in out.keys(): + outlist.append(out[key]) + + return topotest.json_cmp(outlist, expected) + for rnum in range(1, 5): router = "r{}".format(rnum) @@ -187,12 +184,13 @@ def test_ospf_kernel_route(): # Load expected results from the command reffile = os.path.join(CWD, "{}/zebra_mpls.json".format(router)) - expected = open(reffile).read() + expected = json.loads(open(reffile).read()) # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(compare_mpls_table, router, expected) - result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3) - assert result, ("OSPF did not properly instal MPLS table on {}:\n{}").format( + rt = tgen.gears[router] + test_func = partial(show_mpls_table_json_cmp, rt, expected) + rv, diff = topotest.run_and_expect(test_func, None, count=25, wait=3) + assert rv, "OSPF did not properly instal MPLS table on {}:\n{}".format( router, diff ) diff --git a/tests/topotests/pbr-topo1/__init__.py b/tests/topotests/pbr-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/pbr-topo1/__init__.py diff --git a/tests/topotests/pbr-topo1/r1/linux-rules.json b/tests/topotests/pbr-topo1/r1/linux-rules.json new file mode 100644 index 0000000000..5af4363418 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/linux-rules.json @@ -0,0 +1,19 @@ +[ + { + "iif": "r1-eth1", + "pref": "304", + "from": "4.5.6.7" + }, + { + "to": "3.4.5.0/24", + "iif": "r1-eth2", + "pref": "304", + "from": "1.2.0.0/16" + }, + { + "to": "9.9.9.9", + "iif": "r1-eth1", + "pref": "309", + "from": "all" + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-interface.json b/tests/topotests/pbr-topo1/r1/pbr-interface.json index 452b24dcd7..e28d9fb149 100644 --- a/tests/topotests/pbr-topo1/r1/pbr-interface.json +++ b/tests/topotests/pbr-topo1/r1/pbr-interface.json @@ -8,5 +8,20 @@ "name":"r1-eth2", "policy":"DONNA", "valid":true + }, + { + "name":"r1-eth3", + "policy":"AKIHABARA", + "valid":true + }, + { + "name":"r1-eth4", + "policy":"ASAKUSA", + "valid":true + }, + { + "name":"r1-noexist", + "policy":"NOEXIST", + "valid":false } ] diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json index 6b9eaa9ceb..f0738dc540 100644 --- a/tests/topotests/pbr-topo1/r1/pbr-map.json +++ b/tests/topotests/pbr-topo1/r1/pbr-map.json @@ -1,21 +1,92 @@ [ { - "name":"DONNA", - "valid":true, + "name":"AKIHABARA", + "valid":false, "policies":[ { - "id":3, "sequenceNumber":5, - "ruleNumber":304, "vrfUnchanged":false, "installed":true, "installedReason":"Valid", + "nexthopGroup": { + "name":"C", + "installed":true, + "installedInternally":1 + }, + "matchDst":"192.168.4.0\/24" + }, + { + "sequenceNumber":10, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Invalid Src or Dst", "nexthopGroup":{ - "tableId":10002, "name":"C", "installed":true, "installedInternally":1 + } + }, + { + "sequenceNumber":15, + "vrfUnchanged":false, + "installed":false, + "installedReason":"No Nexthops" + } + ] + }, + { + "name":"ASAKUSA", + "valid":true, + "policies":[ + { + "sequenceNumber":5, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "matchDst":"c0ff:ee::\/64", + "nexthopGroup":{ + "name":"D", + "installed":true, + "installedInternally":1 + } + }, + { + "sequenceNumber":10, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "name":"ASAKUSA10", + "installed":true, + "installedInternally":1 }, + "matchDst":"dead:beef::\/64", + "matchMark":314159 + } + ] + }, + { + "name":"DONNA", + "valid":false, + "policies":[ + { + "sequenceNumber":5, + "vrfUnchanged":false, + "installed":false, + "installedReason":"Invalid NH-group", + "nexthopGroup":{ + "name":"B", + "installed":false, + "installedInternally":0 + }, + "matchSrc":"1.2.0.0\/16", + "matchDst":"3.4.5.0\/24" + }, + { + "sequenceNumber":10, + "vrfUnchanged":true, + "installed":false, + "installedReason":"Valid", "matchSrc":"1.2.0.0\/16", "matchDst":"3.4.5.0\/24" } @@ -26,14 +97,11 @@ "valid":true, "policies":[ { - "id":1, "sequenceNumber":5, - "ruleNumber":304, "vrfUnchanged":false, "installed":true, "installedReason":"Valid", "nexthopGroup":{ - "tableId":10003, "name":"EVA5", "installed":true, "installedInternally":1 @@ -41,14 +109,12 @@ "matchSrc":"4.5.6.7\/32" }, { - "id":2, "sequenceNumber":10, "ruleNumber":309, "vrfUnchanged":false, "installed":true, "installedReason":"Valid", "nexthopGroup":{ - "tableId":10000, "name":"A", "installed":true, "installedInternally":1 diff --git a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json index ff85438ad5..540ea28158 100644 --- a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json +++ b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json @@ -1,6 +1,16 @@ [ { - "id":10000, + "name":"ASAKUSA10", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"c0ff:ee::1", + "valid":true + } + ] + }, + { "name":"A", "valid":true, "installed":true, @@ -20,19 +30,47 @@ ] }, { - "id":10002, + "name":"D", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"c0ff:ee::3", + "valid":true + }, + { + "nexthop":"c0ff:ee::2", + "valid":true + }, + { + "nexthop":"c0ff:ee::1", + "valid":true + } + ] + }, + { "name":"C", "valid":true, "installed":true, "nexthops":[ { - "nexthop":"192.168.1.44", + "nexthop":"192.168.4.3", + "targetVrf":"vrf-chiyoda", + "valid":true + }, + { + "nexthop":"192.168.4.2", + "targetVrf":"vrf-chiyoda", + "valid":true + }, + { + "nexthop":"192.168.4.1", + "targetVrf":"vrf-chiyoda", "valid":true } ] }, { - "id":10001, "name":"B", "valid":false, "installed":false, @@ -44,7 +82,6 @@ ] }, { - "id":10003, "name":"EVA5", "valid":true, "installed":true, diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf index 234683f307..4a126151b0 100644 --- a/tests/topotests/pbr-topo1/r1/pbrd.conf +++ b/tests/topotests/pbr-topo1/r1/pbrd.conf @@ -1,8 +1,16 @@ +debug pbr +# Valid table range +pbr table range 10000 50000 +# Try to set invalid bounds +pbr table range 10000 10001 +pbr table range 50000 10000 +# Reset table range +no pbr table range +! nexthop-group A nexthop 192.168.1.2 nexthop 192.168.2.2 nexthop 192.168.3.2 - nexhtop 192.168.4.2 ! # This one is bogus and should # never work @@ -10,7 +18,14 @@ nexthop-group B nexthop 192.168.50.1 ! nexthop-group C - nexthop 192.168.1.44 + nexthop 192.168.4.1 nexthop-vrf vrf-chiyoda + nexthop 192.168.4.2 nexthop-vrf vrf-chiyoda + nexthop 192.168.4.3 nexthop-vrf vrf-chiyoda +! +nexthop-group D + nexthop c0ff:ee::1 + nexthop c0ff:ee::2 + nexthop c0ff:ee::3 ! pbr-map EVA seq 5 match src-ip 4.5.6.7/32 @@ -23,11 +38,50 @@ pbr-map EVA seq 10 pbr-map DONNA seq 5 match dst-ip 3.4.5.0/24 match src-ip 1.2.0.0/16 - set nexthop-group C -! - + set nexthop-group B +! +pbr-map DONNA seq 10 + match dst-ip 3.4.5.0/24 + match src-ip 1.2.0.0/16 + set vrf unchanged +! +pbr-map AKIHABARA seq 5 + no set vrf unchanged + match dst-ip 192.168.4.0/24 + set nexthop-group C +! +pbr-map AKIHABARA seq 10 + match dst-ip 192.168.4.0/24 + no match dst-ip 192.168.4.0/24 + set nexthop-group C +! +pbr-map AKIHABARA seq 15 + set vrf noexist-vrf + match dst-ip 192.168.4.0/24 + set nexthop-group C + no set nexthop-group C +! +pbr-map ASAKUSA seq 5 + match dst-ip c0ff:ee::/64 + set nexthop-group D +! +pbr-map ASAKUSA seq 10 + match dst-ip dead:beef::/64 + match mark 314159 + set nexthop c0ff:ee::1 +! +# Interface policies int r1-eth1 pbr-policy EVA ! int r1-eth2 pbr-policy DONNA +! +int r1-eth3 + pbr-policy AKIHABARA +! +int r1-eth4 + pbr-policy ASAKUSA +! +int r1-noexist + pbr-policy NOEXIST diff --git a/tests/topotests/pbr-topo1/r1/zebra.conf b/tests/topotests/pbr-topo1/r1/zebra.conf index f29b146a62..2ec947c275 100644 --- a/tests/topotests/pbr-topo1/r1/zebra.conf +++ b/tests/topotests/pbr-topo1/r1/zebra.conf @@ -7,5 +7,8 @@ int r1-eth1 int r1-eth2 ip address 192.168.3.1/24 -int r1-eth3 +int r1-eth3 vrf vrf-chiyoda ip address 192.168.4.1/24 + +int r1-eth4 + ipv6 address c0ff:ee::/64 diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py index 2853165d45..7de1cfa519 100755 --- a/tests/topotests/pbr-topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -42,6 +42,7 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger +from lib.common_config import shutdown_bringup_interface # Required to instantiate the topology builder class. from mininet.topo import Topo @@ -61,26 +62,14 @@ class NetworkTopo(Topo): tgen = get_topogen(self) + # Populate routers for routern in range(1, 2): tgen.add_router("r{}".format(routern)) - # On main router - # First switch is for a dummy interface (for local network) - switch = tgen.add_switch("sw1") - switch.add_link(tgen.gears["r1"]) - - # Switches for PBR - # switch 2 switch is for connection to PBR router - switch = tgen.add_switch("sw2") - switch.add_link(tgen.gears["r1"]) - - # switch 4 is stub on remote PBR router - switch = tgen.add_switch("sw4") - switch.add_link(tgen.gears["r1"]) - - # switch 3 is between PBR routers - switch = tgen.add_switch("sw3") - switch.add_link(tgen.gears["r1"]) + # Populate switches + for switchn in range(1, 6): + switch = tgen.add_switch("sw{}".format(switchn)) + switch.add_link(tgen.gears["r1"]) ##################################################### @@ -95,9 +84,13 @@ def setup_module(module): tgen = Topogen(NetworkTopo, module.__name__) tgen.start_topology() - # This is a sample of configuration loading. router_list = tgen.routers() for rname, router in router_list.iteritems(): + # Install vrf into the kernel and slave eth3 + router.run("ip link add vrf-chiyoda type vrf table 1000") + router.run("ip link set dev {}-eth3 master vrf-chiyoda".format(rname)) + router.run("ip link set vrf-chiyoda up") + router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -106,7 +99,7 @@ def setup_module(module): ) tgen.start_router() - #gen.mininet_cli() + def teardown_module(_mod): "Teardown the pytest environment" @@ -141,19 +134,19 @@ def test_pbr_data(): router_list = tgen.routers().values() for router in router_list: intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name) - logger.info(intf_file) + # Read expected result from file expected = json.loads(open(intf_file).read()) # Actual output from router actual = router.vtysh_cmd("show pbr interface json", isjson=True) - assertmsg = '"show pbr interface" mismatches on {}'.format(router.name) assert topotest.json_cmp(actual, expected) is None, assertmsg map_file = "{}/{}/pbr-map.json".format(CWD, router.name) logger.info(map_file) + # Read expected result from file expected = json.loads(open(map_file).read()) @@ -164,7 +157,8 @@ def test_pbr_data(): assert topotest.json_cmp(actual, expected) is None, assertmsg nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name) - + logger.info(nexthop_file) + # Read expected result from file expected = json.loads(open(nexthop_file).read()) @@ -174,7 +168,64 @@ def test_pbr_data(): assertmsg = '"show pbr nexthop-groups" mismatches on {}'.format(router.name) assert topotest.json_cmp(actual, expected) is None, assertmsg + +def test_pbr_flap(): + "Test PBR interface flapping" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify PBR Status + logger.info("Flapping PBR Interfaces") + + router_list = tgen.routers().values() + for router in router_list: + # Flap interface to see if route-map properties are intact + # Shutdown interface + + for i in range(5): + intf = "r1-eth{}".format(i) + + # Down and back again + shutdown_bringup_interface(tgen, router.name, intf, False) + shutdown_bringup_interface(tgen, router.name, intf, True) + + intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name) + logger.info(intf_file) + + # Read expected result from file + expected = json.loads(open(intf_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr interface json", isjson=True) + assertmsg = '"show pbr interface" mismatches on {}'.format(router.name) + + assert topotest.json_cmp(actual, expected) is None, assertmsg + + +def test_rule_linux_installation(): + "Ensure that rule is installed in the kernel" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking for installed PBR rules in OS") + + router_list = tgen.routers().values() + for router in router_list: + rules_file = "{}/{}/linux-rules.json".format(CWD, router.name) + + actual = topotest.ip_rules(router) + expected = json.loads(open(rules_file).read()) + + assertmsg = "Router {} OS rules mismatch".format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) - diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 95b3cfad8f..d8c35dd2a3 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -111,6 +111,7 @@ struct quagga_signal_t vrrp_signals[] = { }; static const struct frr_yang_module_info *const vrrp_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_vrrpd_info, }; diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index d5142b1b55..794e1f4c73 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -98,7 +98,7 @@ sub scan_file { elsif ($file =~ /lib\/if\.c$/) { $protocol = "VTYSH_INTERFACE"; } - elsif ($file =~ /lib\/(filter|lib_vty)\.c$/) { + elsif ($file =~ /lib\/(filter|filter_cli|lib_vty)\.c$/) { $protocol = "VTYSH_ALL"; } elsif ($file =~ /lib\/agentx\.c$/) { diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang index c55af34161..78db201ea1 100644 --- a/yang/frr-filter.yang +++ b/yang/frr-filter.yang @@ -73,7 +73,9 @@ module frr-filter { typedef access-list-name { description "Access list name formatting"; - type string; + type string { + length 1..128; + } } typedef access-list-sequence { @@ -100,69 +102,76 @@ module frr-filter { /* * Configuration data. */ - container filter-list { + container lib { list access-list-legacy { description "Access list legacy instance"; - key "number sequence"; + key "number"; leaf number { description "Access list sequence value"; type access-list-legacy; } - leaf sequence { - description "Access list sequence value"; - type access-list-sequence; - } - - leaf action { - description "Access list action on match"; - type access-list-action; - mandatory true; - } - leaf remark { description "Access list remark"; type string; } - choice value { - description - "Standard access list: value to match. - Extended access list: source value to match."; - mandatory true; + list entry { + description "Access list legacy entry"; - leaf host { - description "Host to match"; - type inet:ipv4-address; - } - leaf network { - description "Network to match"; - type inet:ipv4-prefix; + key "sequence"; + + leaf sequence { + description "Access list sequence value"; + type access-list-sequence; } - leaf any { - description "Match any"; - type empty; + + leaf action { + description "Access list action on match"; + type access-list-action; + mandatory true; } - } - choice extended-value { - when "./sequence >= 100 and ./sequence <= 199 or - ./sequence >= 2000 and ./sequence <= 2699"; - description "Destination value to match"; + choice value { + description + "Standard access list: value to match. + Extended access list: source value to match."; + mandatory true; - leaf destination-host { - description "Host to match"; - type inet:ipv4-address; - } - leaf destination-network { - description "Network to match"; - type inet:ipv4-prefix; + leaf host { + description "Host to match"; + type inet:ipv4-address; + } + leaf network { + description "Network to match"; + type inet:ipv4-prefix; + } + leaf any { + description "Match any"; + type empty; + } } - leaf destination-any { - description "Match any"; - type empty; + + choice extended-value { + when "../number >= 100 and ../number <= 199 or + ../number >= 2000 and ../number <= 2699"; + description "Destination value to match"; + mandatory true; + + leaf destination-host { + description "Host to match"; + type inet:ipv4-address; + } + leaf destination-network { + description "Network to match"; + type inet:ipv4-prefix; + } + leaf destination-any { + description "Match any"; + type empty; + } } } } @@ -170,7 +179,7 @@ module frr-filter { list access-list { description "Access list instance"; - key "type identifier sequence"; + key "type name"; leaf type { description "Access list content type"; @@ -187,85 +196,80 @@ module frr-filter { description "Media Access Control address"; value 2; } - - /* - * Protocol YANG models should augment the parent node to - * contain the routing protocol specific value. The protocol - * must also augment `value` leaf to include its specific - * values or expand the `when` statement on the existing cases. - */ - enum custom { - description "Custom data type"; - value 100; - } } } - leaf identifier { - description "Access list identifier"; + leaf name { + description "Access list name"; type access-list-name; } - leaf sequence { - description "Access list sequence value"; - type access-list-sequence; - } - - leaf action { - description "Access list action on match"; - type access-list-action; - mandatory true; - } - leaf remark { description "Access list remark"; type string; } - choice value { - description "Access list value to match"; - mandatory true; + list entry { + description "Access list entry"; - case ipv4-prefix { - when "./type = 'ipv4'"; + key "sequence"; - leaf ipv4-prefix { - description "Configure IPv4 prefix to match"; - type inet:ipv4-prefix; - } + leaf sequence { + description "Access list sequence value"; + type access-list-sequence; + } - leaf ipv4-exact-match { - description "Exact match of prefix"; - type boolean; - default false; - } + leaf action { + description "Access list action on match"; + type access-list-action; + mandatory true; } - case ipv6-prefix { - when "./type = 'ipv6'"; - leaf ipv6-prefix { - description "Configure IPv6 prefix to match"; - type inet:ipv6-prefix; + choice value { + description "Access list value to match"; + mandatory true; + + case ipv4-prefix { + when "../type = 'ipv4'"; + + leaf ipv4-prefix { + description "Configure IPv4 prefix to match"; + type inet:ipv4-prefix; + } + + leaf ipv4-exact-match { + description "Exact match of prefix"; + type boolean; + default false; + } } + case ipv6-prefix { + when "../type = 'ipv6'"; + + leaf ipv6-prefix { + description "Configure IPv6 prefix to match"; + type inet:ipv6-prefix; + } - leaf ipv6-exact-match { - description "Exact match of prefix"; - type boolean; - default false; + leaf ipv6-exact-match { + description "Exact match of prefix"; + type boolean; + default false; + } } - } - case mac { - when "./type = 'mac'"; + case mac { + when "../type = 'mac'"; - leaf mac { - description "Configure MAC address to match"; - type yang:mac-address; + leaf mac { + description "Configure MAC address to match"; + type yang:mac-address; + } } - } - case any { - leaf any { - description "Match anything"; - type empty; + case any { + leaf any { + description "Match anything"; + type empty; + } } } } @@ -274,7 +278,7 @@ module frr-filter { list prefix-list { description "Prefix list instance"; - key "type name sequence"; + key "type name"; leaf type { description "Prefix list type"; @@ -295,82 +299,88 @@ module frr-filter { type access-list-name; } - leaf sequence { - description "Access list sequence value"; - type access-list-sequence; - } - - leaf action { - description "Prefix list action on match"; - type access-list-action; - mandatory true; - } - - leaf description { + leaf remark { description "Prefix list user description"; type string; } - choice value { - description "Prefix list value to match"; - mandatory true; + list entry { + description "Prefix list entry"; - case ipv4-prefix { - when "./type = 'ipv4'"; + key "sequence"; - leaf ipv4-prefix { - description "Configure IPv4 prefix to match"; - type inet:ipv4-prefix; - } + leaf sequence { + description "Access list sequence value"; + type access-list-sequence; + } + + leaf action { + description "Prefix list action on match"; + type access-list-action; + mandatory true; + } - leaf ipv4-prefix-length-greater-or-equal { - description - "Specifies if matching prefixes with length greater than - or equal to value"; - type uint8 { - range "0..32"; + choice value { + description "Prefix list value to match"; + mandatory true; + + case ipv4-prefix { + when "../type = 'ipv4'"; + + leaf ipv4-prefix { + description "Configure IPv4 prefix to match"; + type inet:ipv4-prefix; } - } - leaf ipv4-prefix-length-lesser-or-equal { - description - "Specifies if matching prefixes with length lesser than - or equal to value"; - type uint8 { - range "0..32"; + leaf ipv4-prefix-length-greater-or-equal { + description + "Specifies if matching prefixes with length greater than + or equal to value"; + type uint8 { + range "0..32"; + } } - } - } - case ipv6-prefix { - when "./type = 'ipv6'"; - leaf ipv6-prefix { - description "Configure IPv6 prefix to match"; - type inet:ipv6-prefix; + leaf ipv4-prefix-length-lesser-or-equal { + description + "Specifies if matching prefixes with length lesser than + or equal to value"; + type uint8 { + range "0..32"; + } + } } + case ipv6-prefix { + when "../type = 'ipv6'"; - leaf ipv6-prefix-length-greater-or-equal { - description - "Specifies if matching prefixes with length greater than - or equal to value"; - type uint8 { - range "0..128"; + leaf ipv6-prefix { + description "Configure IPv6 prefix to match"; + type inet:ipv6-prefix; + } + + leaf ipv6-prefix-length-greater-or-equal { + description + "Specifies if matching prefixes with length greater than + or equal to value"; + type uint8 { + range "0..128"; + } } - } - leaf ipv6-prefix-length-lesser-or-equal { - description - "Specifies if matching prefixes with length lesser than - or equal to value"; - type uint8 { - range "0..128"; + leaf ipv6-prefix-length-lesser-or-equal { + description + "Specifies if matching prefixes with length lesser than + or equal to value"; + type uint8 { + range "0..128"; + } } } - } - case any { - leaf any { - description "Match anything"; - type empty; + case any { + leaf any { + description "Match anything"; + type empty; + } } } } diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang index ce6f21a663..779c56df7f 100644 --- a/yang/frr-nexthop.yang +++ b/yang/frr-nexthop.yang @@ -137,6 +137,13 @@ module frr-nexthop { "Nexthop blackhole types."; } + typedef nexthop-group-ref { + type leafref { + path "/frr-nexthop:frr-nexthop-group/frr-nexthop:nexthop-groups/frr-nexthop:name"; + require-instance false; + } + } + /* * Common nexthop attributes grouping. */ @@ -147,6 +154,7 @@ module frr-nexthop { description "The nexthop type."; } + leaf vrf { type frr-vrf:vrf-ref; description @@ -157,17 +165,20 @@ module frr-nexthop { description "The nexthop gateway address."; } + leaf interface { type frr-interface:interface-ref; description "The nexthop egress interface."; } + leaf bh-type { when "../nh-type = 'blackhole'"; type blackhole-type; description "A blackhole sub-type, if the nexthop is a blackhole type."; } + leaf onlink { when "../nh-type = 'ip4-ifindex' or ../nh-type = 'ip6-ifindex'"; @@ -223,6 +234,15 @@ module frr-nexthop { } } + grouping nexthop-grouping { + list nexthop { + key "nh-type gateway interface"; + description + "A list of nexthop objects."; + uses frr-nexthop-attributes; + } + } + /* * Single nexthop grouping. */ @@ -230,12 +250,7 @@ module frr-nexthop { container frr-nexthops { description "FRR nexthop object."; - list nexthop { - key "nh-type gateway interface"; - description - "A list of nexthop objects."; - uses frr-nexthop-attributes; - } + uses nexthop-grouping; } } @@ -244,11 +259,10 @@ module frr-nexthop { * Container for FRR nexthop group. */ grouping frr-nexthop-grouping { - list nexthop-group { + list nexthop-groups { key "name"; description - "A group of nexthops."; - + "List of nexthop groups, each contains group of nexthops"; leaf name { type string; description @@ -259,23 +273,31 @@ module frr-nexthop { } } + /* Operational nexthop-group */ + grouping frr-nexthop-group-operational { + container nexthop-group { + description + "A group of nexthops."; + leaf id { + type uint32; + description + "The nexthop-group id."; + } + + uses nexthop-grouping; + } + } + container frr-nexthop-group { description "A nexthop-group, represented as a list of nexthop objects."; uses frr-nexthop-grouping; } - typedef nexthop-group-ref { - type leafref { - require-instance false; - path "/frr-nexthop:frr-nexthop-group/frr-nexthop:nexthop-group/frr-nexthop:name"; - } - } - /* * Augment weight attributes to nexthop group. */ - augment "/frr-nexthop-group/nexthop-group/frr-nexthops/nexthop" { + augment "/frr-nexthop-group/nexthop-groups/frr-nexthops/nexthop" { leaf weight { type uint8; description diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index 4aeba14129..159dd8f791 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -316,7 +316,7 @@ module frr-zebra { "Uptime for the route."; } - uses frr-nh:frr-nexthop-grouping; + uses frr-nh:frr-nexthop-group-operational; } // End of route-common @@ -2074,7 +2074,7 @@ module frr-zebra { } } - augment "/frr-vrf:lib/frr-vrf:vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop" { + augment "/frr-vrf:lib/frr-vrf:vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop" { uses frr-nh:frr-nexthop-operational; } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index c743011224..7677db7efd 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1121,16 +1121,23 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) nl_msg_type_to_str(h->nlmsg_type)); return -1; } + if (h->nlmsg_type == RTM_NEWADDR) connected_add_ipv4(ifp, flags, (struct in_addr *)addr, ifa->ifa_prefixlen, (struct in_addr *)broad, label, metric); - else + else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) { + /* Delete with a peer address */ + connected_delete_ipv4( + ifp, flags, (struct in_addr *)addr, + ifa->ifa_prefixlen, broad); + } else connected_delete_ipv4( ifp, flags, (struct in_addr *)addr, ifa->ifa_prefixlen, NULL); } + if (ifa->ifa_family == AF_INET6) { if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) { zlog_err( diff --git a/zebra/main.c b/zebra/main.c index 05dd70ff7a..71c7ebb62f 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -253,6 +253,7 @@ struct quagga_signal_t zebra_signals[] = { }; static const struct frr_yang_module_info *const zebra_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, &frr_zebra_info, diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index a5a605f27e..9da008bb61 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -41,6 +41,7 @@ #include "zebra/rule_netlink.h" #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_dplane.h" /* definitions */ @@ -48,11 +49,19 @@ /* Private functions */ -/* Install or uninstall specified rule for a specific interface. - * Form netlink message and ship it. Currently, notify status after - * waiting for netlink status. + +/* + * netlink_rule_msg_encode + * + * Encodes netlink RTM_ADDRULE/RTM_DELRULE message to buffer buf of size buflen. + * + * Returns -1 on failure or the number of bytes + * written to buf. */ -static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) +static ssize_t netlink_rule_msg_encode( + int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm, + uint32_t priority, uint32_t table, const struct prefix *src_ip, + const struct prefix *dst_ip, uint32_t fwmark, void *buf, size_t buflen) { uint8_t protocol = RTPROT_ZEBRA; int family; @@ -60,142 +69,133 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) struct { struct nlmsghdr n; struct fib_rule_hdr frh; - char buf[NL_PKT_BUF_SIZE]; - } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); - struct sockaddr_nl snl; + char buf[]; + } *req = buf; + + const char *ifname = dplane_ctx_get_ifname(ctx); char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); - family = PREFIX_FAMILY(&rule->rule.filter.src_ip); + memset(req, 0, sizeof(*req)); + family = PREFIX_FAMILY(src_ip); bytelen = (family == AF_INET ? 4 : 16); - req.n.nlmsg_type = cmd; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + req->n.nlmsg_type = cmd; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; - req.frh.family = family; - req.frh.action = FR_ACT_TO_TBL; + req->frh.family = family; + req->frh.action = FR_ACT_TO_TBL; - addattr_l(&req.n, sizeof(req), - FRA_PROTOCOL, &protocol, sizeof(protocol)); + addattr_l(&req->n, buflen, FRA_PROTOCOL, &protocol, sizeof(protocol)); /* rule's pref # */ - addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); + addattr32(&req->n, buflen, FRA_PRIORITY, priority); /* interface on which applied */ - addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifname, - strlen(rule->ifname) + 1); + addattr_l(&req->n, buflen, FRA_IFNAME, ifname, strlen(ifname) + 1); /* source IP, if specified */ - if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { - req.frh.src_len = rule->rule.filter.src_ip.prefixlen; - addattr_l(&req.n, sizeof(req), FRA_SRC, - &rule->rule.filter.src_ip.u.prefix, bytelen); + if (filter_bm & PBR_FILTER_SRC_IP) { + req->frh.src_len = src_ip->prefixlen; + addattr_l(&req->n, buflen, FRA_SRC, &src_ip->u.prefix, bytelen); } + /* destination IP, if specified */ - if (IS_RULE_FILTERING_ON_DST_IP(rule)) { - req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen; - addattr_l(&req.n, sizeof(req), FRA_DST, - &rule->rule.filter.dst_ip.u.prefix, bytelen); + if (filter_bm & PBR_FILTER_DST_IP) { + req->frh.dst_len = dst_ip->prefixlen; + addattr_l(&req->n, buflen, FRA_DST, &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); - } + if (filter_bm & PBR_FILTER_FWMARK) + addattr32(&req->n, buflen, FRA_FWMARK, fwmark); /* Route table to use to forward, if filter criteria matches. */ - if (rule->rule.action.table < 256) - req.frh.table = rule->rule.action.table; + if (table < 256) + req->frh.table = table; else { - req.frh.table = RT_TABLE_UNSPEC; - addattr32(&req.n, sizeof(req), FRA_TABLE, - rule->rule.action.table); + req->frh.table = RT_TABLE_UNSPEC; + addattr32(&req->n, buflen, FRA_TABLE, table); } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u", nl_msg_type_to_str(cmd), nl_family_to_str(family), - rule->ifname, rule->rule.ifindex, rule->rule.priority, - rule->rule.filter.fwmark, - prefix2str(&rule->rule.filter.src_ip, buf1, - sizeof(buf1)), - prefix2str(&rule->rule.filter.dst_ip, buf2, - sizeof(buf2)), - rule->rule.action.table); + ifname, dplane_ctx_get_ifindex(ctx), priority, fwmark, + prefix2str(src_ip, buf1, sizeof(buf1)), + prefix2str(dst_ip, buf2, sizeof(buf2)), table); - /* Ship off the message. - * Note: Currently, netlink_talk() is a blocking call which returns - * back the status. - */ - memset(&snl, 0, sizeof(snl)); - snl.nl_family = AF_NETLINK; - return netlink_talk(netlink_talk_filter, &req.n, - &zns->netlink_cmd, zns, 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } - -/* Public functions */ -/* - * Install specified rule for a specific interface. The preference is what - * goes in the rule to denote relative ordering; it may or may not be the - * same as the rule's user-defined sequence number. +/* Install or uninstall specified rule for a specific interface. + * Form netlink message and ship it. */ -enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) +static int +netlink_rule_update_internal(int cmd, const struct zebra_dplane_ctx *ctx, + uint32_t filter_bm, uint32_t priority, + uint32_t table, const struct prefix *src_ip, + const struct prefix *dst_ip, uint32_t fwmark) { - int ret = 0; + char buf[NL_PKT_BUF_SIZE]; - ret = netlink_rule_update(RTM_NEWRULE, rule); - kernel_pbr_rule_add_del_status(rule, - (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS - : ZEBRA_DPLANE_INSTALL_FAILURE); - - return ZEBRA_DPLANE_REQUEST_SUCCESS; + netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip, + dst_ip, fwmark, buf, sizeof(buf)); + return netlink_talk_info(netlink_talk_filter, (void *)&buf, + dplane_ctx_get_ns(ctx), 0); } +/* Public functions */ /* - * Uninstall specified rule for a specific interface. + * Add, update or delete a rule from the + * kernel, using info from a dataplane context. */ -enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) +enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) { - int ret = 0; - - ret = netlink_rule_update(RTM_DELRULE, rule); - kernel_pbr_rule_add_del_status(rule, - (!ret) ? ZEBRA_DPLANE_DELETE_SUCCESS - : ZEBRA_DPLANE_DELETE_FAILURE); - - return ZEBRA_DPLANE_REQUEST_SUCCESS; -} + enum dplane_op_e op; + int cmd; + int ret; -/* - * Update specified rule for a specific interface. - */ -enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule, - struct zebra_pbr_rule *new_rule) -{ - int ret = 0; + op = dplane_ctx_get_op(ctx); + if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE) + cmd = RTM_NEWRULE; + else if (op == DPLANE_OP_RULE_DELETE) + cmd = RTM_DELRULE; + else { + flog_err( + EC_ZEBRA_PBR_RULE_UPDATE, + "Context received for kernel rule update with incorrect OP code (%u)", + op); + return ZEBRA_DPLANE_REQUEST_FAILURE; + } - /* Add the new, updated one */ - ret = netlink_rule_update(RTM_NEWRULE, new_rule); + ret = netlink_rule_update_internal( + cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx), + dplane_ctx_rule_get_priority(ctx), + dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx), + dplane_ctx_rule_get_dst_ip(ctx), + dplane_ctx_rule_get_fwmark(ctx)); /** * Delete the old one. * * Don't care about this result right? */ - netlink_rule_update(RTM_DELRULE, old_rule); - - kernel_pbr_rule_add_del_status(new_rule, - (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS - : ZEBRA_DPLANE_INSTALL_FAILURE); - - return ZEBRA_DPLANE_REQUEST_SUCCESS; + if (op == DPLANE_OP_RULE_UPDATE) + netlink_rule_update_internal( + RTM_DELRULE, ctx, + dplane_ctx_rule_get_old_filter_bm(ctx), + dplane_ctx_rule_get_old_priority(ctx), + dplane_ctx_rule_get_old_table(ctx), + dplane_ctx_rule_get_old_src_ip(ctx), + dplane_ctx_rule_get_old_dst_ip(ctx), + dplane_ctx_rule_get_old_fwmark(ctx)); + + + return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS + : ZEBRA_DPLANE_REQUEST_FAILURE); } /* @@ -296,14 +296,16 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) * It should have been flushed on a previous shutdown. */ if (startup && proto == RTPROT_ZEBRA) { - int ret; + enum zebra_dplane_result ret; - ret = netlink_rule_update(RTM_DELRULE, &rule); + ret = dplane_pbr_rule_delete(&rule); zlog_debug( "%s: %s leftover rule: family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", __func__, - ((ret == 0) ? "Removed" : "Failed to remove"), + ((ret == ZEBRA_DPLANE_REQUEST_FAILURE) + ? "Failed to remove" + : "Removed"), nl_family_to_str(frh->family), rule.ifname, rule.rule.ifindex, rule.rule.priority, prefix2str(&rule.rule.filter.src_ip, buf1, diff --git a/zebra/rule_socket.c b/zebra/rule_socket.c index 219fa7de6f..e629017bdf 100644 --- a/zebra/rule_socket.c +++ b/zebra/rule_socket.c @@ -43,26 +43,11 @@ #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" -enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) +enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) { flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", __func__); return ZEBRA_DPLANE_REQUEST_FAILURE; } -enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) -{ - flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", - __func__); - return ZEBRA_DPLANE_REQUEST_FAILURE; -} - -enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule, - struct zebra_pbr_rule *new_rule) -{ - flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", - __PRETTY_FUNCTION__); - return ZEBRA_DPLANE_REQUEST_FAILURE; -} - #endif diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index cea8edf752..a40aa8b643 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -785,7 +785,7 @@ int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, note)); } -void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, +void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, enum zapi_rule_notify_owner note) { struct listnode *node; @@ -793,10 +793,11 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, struct stream *s; if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s: Notifying %u", __func__, rule->rule.unique); + zlog_debug("%s: Notifying %u", __func__, + dplane_ctx_rule_get_unique(ctx)); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { - if (rule->sock == client->sock) + if (dplane_ctx_rule_get_sock(ctx) == client->sock) break; } @@ -807,10 +808,10 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT); stream_put(s, ¬e, sizeof(note)); - stream_putl(s, rule->rule.seq); - stream_putl(s, rule->rule.priority); - stream_putl(s, rule->rule.unique); - stream_putl(s, rule->rule.ifindex); + stream_putl(s, dplane_ctx_rule_get_seq(ctx)); + stream_putl(s, dplane_ctx_rule_get_priority(ctx)); + stream_putl(s, dplane_ctx_rule_get_unique(ctx)); + stream_putl(s, dplane_ctx_get_ifindex(ctx)); stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index 6d655e11aa..eb50e3c410 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -81,7 +81,7 @@ extern int zsend_route_notify_owner(struct route_entry *re, extern int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, enum zapi_route_notify_owner note); -extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, +extern void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, enum zapi_rule_notify_owner note); extern void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset, enum zapi_ipset_notify_owner note); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 568b398924..eb3d48d784 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -35,6 +35,7 @@ #include "zebra/zebra_mpls.h" #include "zebra/rt.h" #include "zebra/debug.h" +#include "zebra/zebra_pbr.h" /* Memory type for context blocks */ DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx") @@ -192,6 +193,36 @@ struct dplane_neigh_info { }; /* + * Policy based routing rule info for the dataplane + */ +struct dplane_ctx_rule { + uint32_t priority; + + /* The route table pointed by this rule */ + uint32_t table; + + /* Filter criteria */ + uint32_t filter_bm; + uint32_t fwmark; + struct prefix src_ip; + struct prefix dst_ip; +}; + +struct dplane_rule_info { + /* + * Originating zclient sock fd, so we can know who to send + * back to. + */ + int sock; + + int unique; + int seq; + + struct dplane_ctx_rule new; + struct dplane_ctx_rule old; +}; + +/* * The context block used to exchange info about route updates across * the boundary between the zebra main context (and pthread) and the * dataplane layer (and pthread). @@ -238,6 +269,7 @@ struct zebra_dplane_ctx { struct dplane_intf_info intf; struct dplane_mac_info macinfo; struct dplane_neigh_info neigh; + struct dplane_rule_info rule; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -361,6 +393,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_neighs_in; _Atomic uint32_t dg_neigh_errors; + _Atomic uint32_t dg_rules_in; + _Atomic uint32_t dg_rule_errors; + _Atomic uint32_t dg_update_yields; /* Dataplane pthread */ @@ -564,6 +599,9 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: case DPLANE_OP_NONE: break; } @@ -786,6 +824,16 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_VTEP_DELETE: ret = "VTEP_DELETE"; break; + + case DPLANE_OP_RULE_ADD: + ret = "RULE_ADD"; + break; + case DPLANE_OP_RULE_DELETE: + ret = "RULE_DELETE"; + break; + case DPLANE_OP_RULE_UPDATE: + ret = "RULE_UPDATE"; + break; } return ret; @@ -1517,6 +1565,116 @@ uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx) return ctx->u.neigh.state; } +/* Accessors for PBR rule information */ +int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.sock; +} + +int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.unique; +} + +int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.seq; +} + +uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.priority; +} + +uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.priority; +} + +uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.table; +} + +uint32_t dplane_ctx_rule_get_old_table(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.table; +} + +uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.filter_bm; +} + +uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.filter_bm; +} + +uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.fwmark; +} + +uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.fwmark; +} + +const struct prefix * +dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.new.src_ip); +} + +const struct prefix * +dplane_ctx_rule_get_old_src_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.old.src_ip); +} + +const struct prefix * +dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.new.dst_ip); +} + +const struct prefix * +dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.old.dst_ip); +} + /* * End of dplane context accessors */ @@ -1913,6 +2071,76 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, return AOK; } +/** + * dplane_ctx_rule_init_single() - Initialize a dataplane representation of a + * PBR rule. + * + * @dplane_rule: Dataplane internal representation of a rule + * @rule: PBR rule + */ +static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, + struct zebra_pbr_rule *rule) +{ + dplane_rule->priority = rule->rule.priority; + dplane_rule->table = rule->rule.action.table; + + dplane_rule->filter_bm = rule->rule.filter.filter_bm; + dplane_rule->fwmark = rule->rule.filter.fwmark; + prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip); + prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip); +} + +/** + * dplane_ctx_rule_init() - Initialize a context block for a PBR rule update. + * + * @ctx: Dataplane context to init + * @op: Operation being performed + * @new_rule: PBR rule + * + * Return: Result status + */ +static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, + enum dplane_op_e op, + struct zebra_pbr_rule *new_rule, + struct zebra_pbr_rule *old_rule) +{ + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + + zlog_debug( + "init dplane ctx %s: IF %s(%u) Prio %u Fwmark %u Src %s Dst %s Table %u", + dplane_op2str(op), new_rule->ifname, + new_rule->rule.ifindex, new_rule->rule.priority, + new_rule->rule.filter.fwmark, + prefix2str(&new_rule->rule.filter.src_ip, buf1, + sizeof(buf1)), + prefix2str(&new_rule->rule.filter.dst_ip, buf2, + sizeof(buf2)), + new_rule->rule.action.table); + } + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + + dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), + op == DPLANE_OP_RULE_UPDATE); + + ctx->zd_vrf_id = new_rule->vrf_id; + memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname)); + ctx->zd_ifindex = new_rule->rule.ifindex; + + ctx->u.rule.sock = new_rule->sock; + ctx->u.rule.unique = new_rule->rule.unique; + ctx->u.rule.seq = new_rule->rule.seq; + + dplane_ctx_rule_init_single(&ctx->u.rule.new, new_rule); + if (op == DPLANE_OP_RULE_UPDATE) + dplane_ctx_rule_init_single(&ctx->u.rule.old, old_rule); + + return AOK; +} + /* * Enqueue a new update, * and ensure an event is active for the dataplane pthread. @@ -2840,6 +3068,56 @@ neigh_update_internal(enum dplane_op_e op, } /* + * Common helper api for PBR rule updates + */ +static enum zebra_dplane_result +rule_update_internal(enum dplane_op_e op, struct zebra_pbr_rule *new_rule, + struct zebra_pbr_rule *old_rule) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + struct zebra_dplane_ctx *ctx; + int ret; + + ctx = dplane_ctx_alloc(); + + ret = dplane_ctx_rule_init(ctx, op, new_rule, old_rule); + if (ret != AOK) + goto done; + + ret = dplane_update_enqueue(ctx); + +done: + atomic_fetch_add_explicit(&zdplane_info.dg_rules_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info.dg_rule_errors, 1, + memory_order_relaxed); + dplane_ctx_free(&ctx); + } + + return result; +} + +enum zebra_dplane_result dplane_pbr_rule_add(struct zebra_pbr_rule *rule) +{ + return rule_update_internal(DPLANE_OP_RULE_ADD, rule, NULL); +} + +enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule) +{ + return rule_update_internal(DPLANE_OP_RULE_DELETE, rule, NULL); +} + +enum zebra_dplane_result dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule) +{ + return rule_update_internal(DPLANE_OP_RULE_UPDATE, new_rule, old_rule); +} + +/* * Handler for 'show dplane' */ int dplane_show_helper(struct vty *vty, bool detailed) @@ -2909,6 +3187,13 @@ int dplane_show_helper(struct vty *vty, bool detailed) vty_out(vty, "EVPN neigh updates: %"PRIu64"\n", incoming); vty_out(vty, "EVPN neigh errors: %"PRIu64"\n", errs); + incoming = atomic_load_explicit(&zdplane_info.dg_rules_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_rule_errors, + memory_order_relaxed); + vty_out(vty, "Rule updates: %" PRIu64 "\n", incoming); + vty_out(vty, "Rule errors: %" PRIu64 "\n", errs); + return CMD_SUCCESS; } @@ -3398,6 +3683,29 @@ kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx) } /* + * Handler for kernel PBR rule updates + */ +static enum zebra_dplane_result +kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx) +{ + enum zebra_dplane_result res; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifname(ctx), + dplane_ctx_get_ifindex(ctx), ctx); + + res = kernel_pbr_rule_update(ctx); + + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_rule_errors, 1, + memory_order_relaxed); + + return res; +} + +/* * Kernel provider callback */ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) @@ -3470,6 +3778,12 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) res = kernel_dplane_neigh_update(ctx); break; + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + res = kernel_dplane_rule_update(ctx); + break; + /* Ignore 'notifications' - no-op */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 7f8049b767..c93b95a6ad 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -144,6 +144,11 @@ enum dplane_op_e { /* EVPN VTEP updates */ DPLANE_OP_VTEP_ADD, DPLANE_OP_VTEP_DELETE, + + /* Policy based routing rule update */ + DPLANE_OP_RULE_ADD, + DPLANE_OP_RULE_DELETE, + DPLANE_OP_RULE_UPDATE, }; /* @@ -384,6 +389,27 @@ const struct ethaddr *dplane_ctx_neigh_get_mac( uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx); +/* Accessors for policy based routing rule information */ +int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_table(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_old_src_ip(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx); + /* Namespace info - esp. for netlink communication */ const struct zebra_dplane_info *dplane_ctx_get_ns( const struct zebra_dplane_ctx *ctx); @@ -509,6 +535,21 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, const struct in_addr *ip, vni_t vni); +/* Forward ref of zebra_pbr_rule */ +struct zebra_pbr_rule; + +/* + * Enqueue policy based routing rule for the dataplane. + * It is possible that the user-defined sequence number and the one in the + * forwarding plane may not coincide, hence the API requires a separate + * rule priority - maps to preference/FRA_PRIORITY on Linux. + */ +enum zebra_dplane_result dplane_pbr_rule_add(struct zebra_pbr_rule *rule); +enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule); +enum zebra_dplane_result +dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule); + /* Encode route information into data plane context. */ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct route_node *rn, struct route_entry *re); diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 395004d0bb..5f2a7a12c6 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -77,6 +77,7 @@ enum zebra_log_refs { EC_ZEBRA_NHG_FIB_UPDATE, EC_ZEBRA_IF_LOOKUP_FAILED, EC_ZEBRA_NS_NO_DEFAULT, + EC_ZEBRA_PBR_RULE_UPDATE, /* warnings */ EC_ZEBRA_NS_NOTIFY_READ, EC_ZEBRAING_LM_PROTO_MISMATCH, diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index c580fe40d5..b194f80fc7 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -339,7 +339,7 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, } /* If there is no useful nexthop then return. */ - if (ri->num_nhs == 0) { + if (ri->rtm_type != RTN_BLACKHOLE && ri->num_nhs == 0) { zfpm_debug("netlink_encode_route(): No useful nexthop."); return 0; } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 1210430b06..31b100598f 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1258,8 +1258,7 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; - if (!lsp) - return NULL; + assert(lsp); nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t)); @@ -1268,10 +1267,6 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, nhlfe->distance = lsp_distance(lsp_type); nexthop = nexthop_new(); - if (!nexthop) { - XFREE(MTYPE_NHLFE, nhlfe); - return NULL; - } nexthop_add_labels(nexthop, lsp_type, num_labels, labels); @@ -1322,8 +1317,7 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, labels); /* Enqueue to LSP, at head of list. */ - if (nhlfe) - nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); + nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); return nhlfe; } @@ -1351,8 +1345,7 @@ static zebra_nhlfe_t *nhlfe_backup_add(zebra_lsp_t *lsp, SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); /* Enqueue to LSP, at tail of list. */ - if (nhlfe) - nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); return nhlfe; } @@ -1568,7 +1561,8 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: vty_out(vty, " via %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); if (nexthop->ifindex) vty_out(vty, " dev %s", ifindex2ifname(nexthop->ifindex, diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index 25b8b44ec9..53fe8e8e3f 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -558,117 +558,115 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/id", .cbs = { - .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_next, - .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_keys, - .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_lookup_entry, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/name", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_name_get_elem, + .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/nh-type", .cbs = { - .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_next, - .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_keys, - .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_lookup_entry, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/nh-type", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/vrf", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_type_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/vrf", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/gateway", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/gateway", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/interface", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gateway_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/interface", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/bh-type", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_interface_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/bh-type", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/onlink", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_type_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/onlink", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onlink_get_elem, + .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/id", .cbs = { - .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_next, - .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_keys, - .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_lookup_entry, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/id", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/label", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_id_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/label", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/ttl", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_label_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/ttl", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/traffic-class", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/duplicate", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/duplicate", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/recursive", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_duplicate_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/recursive", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/active", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recursive_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/active", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/fib", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_active_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem, } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/fib", + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/weight", .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_get_elem, + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem, } }, { @@ -714,12 +712,6 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { - .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/weight", - .cbs = { - .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_weight_get_elem, - } - }, - { .xpath = NULL, }, } diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index 15350eb53b..a9e7fd5fb0 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -256,75 +256,69 @@ lib_vrf_zebra_ribs_rib_route_route_entry_internal_status_get_elem( struct nb_cb_get_elem_args *args); struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem( struct nb_cb_get_elem_args *args); -const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_next( - struct nb_cb_get_next_args *args); -int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_keys( - struct nb_cb_get_keys_args *args); -const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_lookup_entry( - struct nb_cb_lookup_entry_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_name_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem( struct nb_cb_get_elem_args *args); const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_next( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next( struct nb_cb_get_next_args *args); -int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_keys( +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys( struct nb_cb_get_keys_args *args); const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_lookup_entry( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry( struct nb_cb_lookup_entry_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_type_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gateway_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_interface_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_type_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onlink_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( struct nb_cb_get_elem_args *args); const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_next( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( struct nb_cb_get_next_args *args); -int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_keys( +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys( struct nb_cb_get_keys_args *args); const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_lookup_entry( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry( struct nb_cb_lookup_entry_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_id_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_label_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_duplicate_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recursive_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_active_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem( struct nb_cb_get_elem_args *args); struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_weight_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem( struct nb_cb_get_elem_args *args); #ifdef __cplusplus diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index 5b87a18a06..948ef51320 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -941,13 +941,15 @@ int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args) case NB_EV_VALIDATE: if (prefix.family == AF_INET && ipv4_martian(&prefix.u.prefix4)) { - zlog_debug("invalid address %s", - prefix2str(&prefix, buf, sizeof(buf))); + snprintf(args->errmsg, args->errmsg_len, + "invalid address %s", + prefix2str(&prefix, buf, sizeof(buf))); return NB_ERR_VALIDATION; } else if (prefix.family == AF_INET6 && ipv6_martian(&prefix.u.prefix6)) { - zlog_debug("invalid address %s", - prefix2str(&prefix, buf, sizeof(buf))); + snprintf(args->errmsg, args->errmsg_len, + "invalid address %s", + prefix2str(&prefix, buf, sizeof(buf))); return NB_ERR_VALIDATION; } break; @@ -982,16 +984,18 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) /* Check current interface address. */ ifc = connected_check_ptp(ifp, &prefix, NULL); if (!ifc) { - zlog_debug("interface %s Can't find address\n", - ifp->name); + snprintf(args->errmsg, args->errmsg_len, + "interface %s Can't find address\n", + ifp->name); return NB_ERR_VALIDATION; } } else if (prefix.family == AF_INET6) { /* Check current interface address. */ ifc = connected_check(ifp, &prefix); if (!ifc) { - zlog_debug("interface can't find address %s", - ifp->name); + snprintf(args->errmsg, args->errmsg_len, + "interface can't find address %s", + ifp->name); return NB_ERR_VALIDATION; } } else @@ -999,7 +1003,8 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) /* This is not configured address. */ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { - zlog_debug("interface %s not configured", ifp->name); + snprintf(args->errmsg, args->errmsg_len, + "interface %s not configured", ifp->name); return NB_ERR_VALIDATION; } @@ -1244,8 +1249,8 @@ int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args) switch (args->event) { case NB_EV_VALIDATE: if (!zrt) { - zlog_debug("%s: vrf %s table is not found.", __func__, - vrf->name); + snprintf(args->errmsg, args->errmsg_len, + "vrf %s table is not found.", vrf->name); return NB_ERR_VALIDATION; } break; @@ -1376,7 +1381,8 @@ int lib_route_map_entry_match_condition_source_protocol_modify( case NB_EV_VALIDATE: type = yang_dnode_get_string(args->dnode, NULL); if (proto_name2num(type) == -1) { - zlog_warn("%s: invalid protocol: %s", __func__, type); + snprintf(args->errmsg, args->errmsg_len, + "invalid protocol: %s", type); return NB_ERR_VALIDATION; } return NB_OK; @@ -1470,8 +1476,9 @@ int lib_route_map_entry_set_action_source_v4_modify( memset(&p, 0, sizeof(p)); yang_dnode_get_ipv4p(&p, args->dnode, NULL); if (zebra_check_addr(&p) == 0) { - zlog_warn("%s: invalid IPv4 address: %s", __func__, - yang_dnode_get_string(args->dnode, NULL)); + snprintf(args->errmsg, args->errmsg_len, + "invalid IPv4 address: %s", + yang_dnode_get_string(args->dnode, NULL)); return NB_ERR_VALIDATION; } @@ -1482,8 +1489,9 @@ int lib_route_map_entry_set_action_source_v4_modify( break; } if (pif == NULL) { - zlog_warn("%s: is not a local adddress: %s", __func__, - yang_dnode_get_string(args->dnode, NULL)); + snprintf(args->errmsg, args->errmsg_len, + "is not a local adddress: %s", + yang_dnode_get_string(args->dnode, NULL)); return NB_ERR_VALIDATION; } return NB_OK; @@ -1536,8 +1544,9 @@ int lib_route_map_entry_set_action_source_v6_modify( memset(&p, 0, sizeof(p)); yang_dnode_get_ipv6p(&p, args->dnode, NULL); if (zebra_check_addr(&p) == 0) { - zlog_warn("%s: invalid IPv6 address: %s", __func__, - yang_dnode_get_string(args->dnode, NULL)); + snprintf(args->errmsg, args->errmsg_len, + "invalid IPv6 address: %s", + yang_dnode_get_string(args->dnode, NULL)); return NB_ERR_VALIDATION; } @@ -1548,8 +1557,9 @@ int lib_route_map_entry_set_action_source_v6_modify( break; } if (pif == NULL) { - zlog_warn("%s: is not a local adddress: %s", __func__, - yang_dnode_get_string(args->dnode, NULL)); + snprintf(args->errmsg, args->errmsg_len, + "is not a local adddress: %s", + yang_dnode_get_string(args->dnode, NULL)); return NB_ERR_VALIDATION; } return NB_OK; diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index afbabe342c..4bca3b36d6 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -490,78 +490,38 @@ struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem( /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group - */ -const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_next( - struct nb_cb_get_next_args *args) -{ - struct route_entry *re = (struct route_entry *)args->parent_list_entry; - struct nhg_hash_entry *nhe = (struct nhg_hash_entry *)args->list_entry; - - if (nhe == NULL) { - nhe = re->nhe; - return nhe; - } - return NULL; -} - -int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_keys( - struct nb_cb_get_keys_args *args) -{ - struct nhg_hash_entry *nhe = (struct nhg_hash_entry *)args->list_entry; - - args->keys->num = 1; - snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%" PRIu32, - nhe->id); - - return NB_OK; -} - -const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_lookup_entry( - struct nb_cb_lookup_entry_args *args) -{ - struct route_entry *re = (struct route_entry *)args->parent_list_entry; - - return re->nhe; -} - -/* - * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/name + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/id */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_name_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem( struct nb_cb_get_elem_args *args) { - struct nhg_hash_entry *nhe = (struct nhg_hash_entry *)args->list_entry; - char name[20] = {'\0'}; - - snprintfrr(name, sizeof(name), "%" PRIu32, nhe->id); + struct route_entry *re = (struct route_entry *)args->list_entry; - return yang_data_new_string(args->xpath, name); + return yang_data_new_uint32(args->xpath, re->nhe->id); } /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop */ const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_next( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next( struct nb_cb_get_next_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; - struct nhg_hash_entry *nhe = - (struct nhg_hash_entry *)args->parent_list_entry; + struct route_entry *re = (struct route_entry *)args->parent_list_entry; + struct nhg_hash_entry *nhe = re->nhe; - if (args->list_entry == NULL) + if (args->list_entry == NULL) { nexthop = nhe->nhg.nexthop; - else + } else nexthop = nexthop_next(nexthop); return nexthop; } -int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_keys( +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys( struct nb_cb_get_keys_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -623,7 +583,7 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_ } const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_lookup_entry( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry( struct nb_cb_lookup_entry_args *args) { struct nhg_hash_entry *nhe; @@ -697,10 +657,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_look /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/nh-type + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/nh-type */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_type_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -730,10 +690,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_t /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/vrf + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/vrf */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -744,10 +704,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_ /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/gateway + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/gateway */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gateway_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -780,10 +740,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gate /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/interface + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/interface */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_interface_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -798,10 +758,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_inte /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/bh-type + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/bh-type */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_type_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -830,10 +790,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_t /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/onlink + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/onlink */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onlink_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -846,17 +806,17 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onli /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry */ const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_next( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( struct nb_cb_get_next_args *args) { /* TODO: implement me. */ return NULL; } -int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_keys( +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys( struct nb_cb_get_keys_args *args) { /* TODO: implement me. */ @@ -864,7 +824,7 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_ } const void * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_lookup_entry( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry( struct nb_cb_lookup_entry_args *args) { /* TODO: implement me. */ @@ -873,10 +833,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/id + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/id */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_id_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem( struct nb_cb_get_elem_args *args) { /* TODO: implement me. */ @@ -885,10 +845,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/label + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/label */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_label_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem( struct nb_cb_get_elem_args *args) { /* TODO: implement me. */ @@ -897,10 +857,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/ttl + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/ttl */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem( struct nb_cb_get_elem_args *args) { /* TODO: implement me. */ @@ -909,10 +869,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/traffic-class */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem( struct nb_cb_get_elem_args *args) { /* TODO: implement me. */ @@ -921,10 +881,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/duplicate + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/duplicate */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_duplicate_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -937,10 +897,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_dupl /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/recursive + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/recursive */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recursive_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -953,10 +913,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recu /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/active + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/active */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_active_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -969,10 +929,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_acti /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/fib + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/fib */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; @@ -985,10 +945,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_ /* * XPath: - * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/weight + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/weight */ struct yang_data * -lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_weight_get_elem( +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem( struct nb_cb_get_elem_args *args) { struct nexthop *nexthop = (struct nexthop *)args->list_entry; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index d07ceb652c..8723bd8d30 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2563,6 +2563,9 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: case DPLANE_OP_NONE: break; } diff --git a/zebra/zebra_opaque.c b/zebra/zebra_opaque.c index d1e0497154..41e278f71b 100644 --- a/zebra/zebra_opaque.c +++ b/zebra/zebra_opaque.c @@ -347,8 +347,9 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) { struct stream *msg, *dup; struct zmsghdr hdr; + struct zapi_opaque_msg info; struct opq_msg_reg *reg; - uint32_t type; + int ret; struct opq_client_reg *client; struct zserv *zclient; char buf[50]; @@ -372,15 +373,17 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) /* Dispatch to any registered ZAPI client(s) */ - /* Extract subtype */ - STREAM_GETL(msg, type); + /* Extract subtype and flags */ + ret = zclient_opaque_decode(msg, &info); + if (ret != 0) + goto drop_it; /* Look up registered ZAPI client(s) */ - reg = opq_reg_lookup(type); + reg = opq_reg_lookup(info.type); if (reg == NULL) { if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("%s: no registrations for opaque type %u", - __func__, type); + zlog_debug("%s: no registrations for opaque type %u, flags %#x", + __func__, info.type, info.flags); goto drop_it; } @@ -391,9 +394,25 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) for (client = reg->clients; client; client = client->next) { dup = NULL; - /* Copy message if necessary */ - if (client->next) - dup = stream_dup(msg); + if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST)) { + + if (client->proto != info.proto || + client->instance != info.instance || + client->session_id != info.session_id) + continue; + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: found matching unicast client %s", + __func__, + opq_client2str(buf, + sizeof(buf), + client)); + + } else { + /* Copy message if more clients */ + if (client->next) + dup = stream_dup(msg); + } /* * TODO -- this isn't ideal: we're going through an @@ -427,7 +446,7 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) } else { if (IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: type %u: no zclient for %s", - __func__, type, + __func__, info.type, opq_client2str(buf, sizeof(buf), client)); @@ -435,10 +454,14 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) if (dup) stream_free(dup); } + + /* If unicast, we're done */ + if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST)) + break; } drop_it: -stream_failure: + if (msg) stream_free(msg); } @@ -460,7 +483,7 @@ static int handle_opq_registration(const struct zmsghdr *hdr, memset(&info, 0, sizeof(info)); - if (zapi_parse_opaque_reg(msg, &info) < 0) { + if (zapi_opaque_reg_decode(msg, &info) < 0) { ret = -1; goto done; } @@ -540,7 +563,7 @@ static int handle_opq_unregistration(const struct zmsghdr *hdr, memset(&info, 0, sizeof(info)); - if (zapi_parse_opaque_reg(msg, &info) < 0) { + if (zapi_opaque_reg_decode(msg, &info) < 0) { ret = -1; goto done; } diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 62cbcbda4b..c5a7795273 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -131,7 +131,7 @@ void zebra_pbr_rules_free(void *arg) rule = (struct zebra_pbr_rule *)arg; - (void)kernel_del_pbr_rule(rule); + (void)dplane_pbr_rule_delete(rule); XFREE(MTYPE_TMP, rule); } @@ -460,7 +460,7 @@ void zebra_pbr_add_rule(struct zebra_pbr_rule *rule) /* If found, this is an update */ if (found) { - (void)kernel_update_pbr_rule(found, rule); + (void)dplane_pbr_rule_update(found, rule); if (pbr_rule_release(found)) zlog_debug( @@ -468,12 +468,12 @@ void zebra_pbr_add_rule(struct zebra_pbr_rule *rule) __PRETTY_FUNCTION__); } else - (void)kernel_add_pbr_rule(rule); + (void)dplane_pbr_rule_add(rule); } void zebra_pbr_del_rule(struct zebra_pbr_rule *rule) { - (void)kernel_del_pbr_rule(rule); + (void)dplane_pbr_rule_delete(rule); if (pbr_rule_release(rule)) zlog_debug("%s: Rule being deleted we know nothing about", @@ -486,7 +486,7 @@ static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data) int *sock = data; if (rule->sock == *sock) { - (void)kernel_del_pbr_rule(rule); + (void)dplane_pbr_rule_delete(rule); if (hash_release(zrouter.rules_hash, rule)) XFREE(MTYPE_TMP, rule); else @@ -735,25 +735,29 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable) /* * Handle success or failure of rule (un)install in the kernel. */ -void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, - enum zebra_dplane_status res) +void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx) { - switch (res) { - case ZEBRA_DPLANE_INSTALL_SUCCESS: - zsend_rule_notify_owner(rule, ZAPI_RULE_INSTALLED); - break; - case ZEBRA_DPLANE_INSTALL_FAILURE: - zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL); - break; - case ZEBRA_DPLANE_DELETE_SUCCESS: - zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED); - break; - case ZEBRA_DPLANE_DELETE_FAILURE: - zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_REMOVE); - break; - case ZEBRA_DPLANE_STATUS_NONE: - break; - } + enum zebra_dplane_result res; + enum dplane_op_e op; + + res = dplane_ctx_get_status(ctx); + op = dplane_ctx_get_op(ctx); + if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE) + zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS + ? ZAPI_RULE_INSTALLED + : ZAPI_RULE_FAIL_INSTALL); + else if (op == DPLANE_OP_RULE_DELETE) + zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS + ? ZAPI_RULE_REMOVED + : ZAPI_RULE_FAIL_REMOVE); + else + flog_err( + EC_ZEBRA_PBR_RULE_UPDATE, + "Context received in pbr rule dplane result handler with incorrect OP code (%u)", + op); + + + dplane_ctx_fini(&ctx); } /* diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 83797b9521..4bc0f40037 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -170,24 +170,11 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable); void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable); /* - * Install specified rule for a specific interface. - * It is possible that the user-defined sequence number and the one in the - * forwarding plane may not coincide, hence the API requires a separate - * rule priority - maps to preference/FRA_PRIORITY on Linux. - */ -extern enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule); - -/* - * Uninstall specified rule for a specific interface. - */ -extern enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule); - -/* - * Update specified rule for a specific interface. + * Add, update or delete a rule from the + * kernel, using info from a dataplane context. */ extern enum zebra_dplane_result -kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule, - struct zebra_pbr_rule *new_rule); +kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); /* * Get to know existing PBR rules in the kernel - typically called at startup. @@ -197,8 +184,7 @@ extern void kernel_read_pbr_rules(struct zebra_ns *zns); /* * Handle success or failure of rule (un)install in the kernel. */ -extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, - enum zebra_dplane_status res); +extern void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx); /* * Handle success or failure of ipset kinds (un)install in the kernel. diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 8f255ecfb0..8f0c964c18 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -125,9 +125,12 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, pw->flags = flags; pw->data = *data; - if (zebra_pw_enabled(pw)) - zebra_register_rnh_pseudowire(pw->vrf_id, pw); - else { + if (zebra_pw_enabled(pw)) { + bool nht_exists; + zebra_register_rnh_pseudowire(pw->vrf_id, pw, &nht_exists); + if (nht_exists) + zebra_pw_update(pw); + } else { if (pw->protocol == ZEBRA_ROUTE_STATIC) zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); zebra_pw_uninstall(pw); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0fc716db17..75619520dc 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3583,6 +3583,12 @@ static int rib_process_dplane_results(struct thread *thread) zebra_vxlan_handle_result(ctx); break; + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + zebra_pbr_dplane_result(ctx); + break; + /* Some op codes not handled here */ case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index ad2e00b1ec..20af96a557 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -343,25 +343,32 @@ static void addr2hostprefix(int af, const union g_addr *addr, } } -void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) +void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw, + bool *nht_exists) { struct prefix nh; struct rnh *rnh; bool exists; struct zebra_vrf *zvrf; + *nht_exists = false; + zvrf = vrf_info_lookup(vrf_id); if (!zvrf) return; addr2hostprefix(pw->af, &pw->nexthop, &nh); rnh = zebra_add_rnh(&nh, vrf_id, RNH_NEXTHOP_TYPE, &exists); - if (rnh && !listnode_lookup(rnh->zebra_pseudowire_list, pw)) { + if (!rnh) + return; + + if (!listnode_lookup(rnh->zebra_pseudowire_list, pw)) { listnode_add(rnh->zebra_pseudowire_list, pw); pw->rnh = rnh; zebra_evaluate_rnh(zvrf, family2afi(pw->af), 1, RNH_NEXTHOP_TYPE, &nh); - } + } else + *nht_exists = true; } void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) @@ -1001,12 +1008,13 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) static int send_client(struct rnh *rnh, struct zserv *client, enum rnh_type type, vrf_id_t vrf_id) { - struct stream *s; + struct stream *s = NULL; struct route_entry *re; unsigned long nump; uint8_t num; struct nexthop *nh; struct route_node *rn; + int ret; int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE : ZEBRA_NEXTHOP_UPDATE; @@ -1032,7 +1040,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY, "%s: Unknown family (%d) notification attempted\n", __func__, rn->p.family); - break; + goto failure; } if (re) { struct zapi_nexthop znh; @@ -1047,7 +1055,10 @@ static int send_client(struct rnh *rnh, struct zserv *client, for (ALL_NEXTHOPS(re->nhe->nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); - zapi_nexthop_encode(s, &znh, 0 /* flags */); + ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); + if (ret < 0) + goto failure; + num++; } stream_putc_at(s, nump, num); @@ -1063,6 +1074,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, client->nh_last_upd_time = monotime(NULL); client->last_write_cmd = cmd; return zserv_send_message(client, s); + +failure: + + stream_free(s); + return -1; } static void print_nh(struct nexthop *nexthop, struct vty *vty) diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index f07e5bc791..e744504920 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -50,7 +50,7 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, extern void zebra_free_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, enum rnh_type type, vrf_id_t vrfid); -extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *); +extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, enum rnh_type type); diff --git a/zebra/zserv.c b/zebra/zserv.c index cb863b258c..99a85fd2ce 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -811,14 +811,6 @@ struct zserv *zserv_acquire_client(uint8_t proto, unsigned short instance, */ void zserv_release_client(struct zserv *client) { - bool cleanup_p = false; - const char *proto_str; - uint16_t instance; - - /* Capture some info for debugging */ - proto_str = zebra_route_string(client->proto); - instance = client->instance; - /* * Once we've decremented the client object's refcount, it's possible * for it to be deleted as soon as we release the lock, so we won't @@ -833,13 +825,10 @@ void zserv_release_client(struct zserv *client) * session is closed, schedule cleanup on the zebra * main pthread. */ - if (client->is_closed) { + if (client->is_closed) thread_add_event(zrouter.master, zserv_handle_client_fail, client, 0, &client->t_cleanup); - - cleanup_p = true; - } } } @@ -847,10 +836,6 @@ void zserv_release_client(struct zserv *client) * Cleanup must take place on the zebra main pthread, so we've * scheduled an event. */ - if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s: %s clean-up for client '%s'[%u]", - __func__, (cleanup_p ? "scheduled" : "NO"), - proto_str, instance); } /* |
