diff options
121 files changed, 4811 insertions, 1583 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c index eaff97a49..a3f2b4e7d 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -68,7 +68,7 @@ const unsigned char ones[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -static const char *state_file = DAEMON_VTY_DIR "/babel-state"; +static char state_file[1024]; unsigned char protocol_group[16]; /* babel's link-local multicast address */ int protocol_port; /* babel's port */ @@ -187,6 +187,9 @@ main(int argc, char **argv) } } + snprintf(state_file, sizeof(state_file), "%s/%s", + frr_vtydir, "babel-state"); + /* create the threads handler */ master = frr_init (); diff --git a/babeld/message.c b/babeld/message.c index 794b6e997..d88790824 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -1115,7 +1115,9 @@ really_send_update(struct interface *ifp, if(channels_len >= 0) { accumulate_byte(ifp, 2); accumulate_byte(ifp, channels_len); - accumulate_bytes(ifp, channels, channels_len); + + if (channels && channels_len > 0) + accumulate_bytes(ifp, channels, channels_len); } end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + channels_size); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index f3acfa416..d68a1ad5f 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -435,16 +435,18 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ *ifindex = pi6->ipi6_ifindex; + + /* Set scope ID for link local addresses. */ + if (IN6_IS_ADDR_LINKLOCAL( + &peer->sa_sin6.sin6_addr)) + peer->sa_sin6.sin6_scope_id = *ifindex; + if (IN6_IS_ADDR_LINKLOCAL( + &local->sa_sin6.sin6_addr)) + local->sa_sin6.sin6_scope_id = *ifindex; } } } - /* Set scope ID for link local addresses. */ - if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr)) - peer->sa_sin6.sin6_scope_id = *ifindex; - if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr)) - local->sa_sin6.sin6_scope_id = *ifindex; - return mlen; } diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 218f0883c..06e01abcf 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -20,6 +20,8 @@ #include <zebra.h> +#include "filter.h" + #include "bfd.h" #include "lib/version.h" @@ -158,7 +160,8 @@ static void bg_init(void) int main(int argc, char *argv[]) { - const char *ctl_path = BFDD_CONTROL_SOCKET; + char ctl_path[512]; + bool ctlsockused = false; int opt; /* Initialize system sockets. */ @@ -168,6 +171,8 @@ int main(int argc, char *argv[]) frr_opt_add("", longopts, " --bfdctl Specify bfdd control socket\n"); + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "", ""); while (true) { opt = frr_getopt(argc, argv, NULL); if (opt == EOF) @@ -175,7 +180,8 @@ int main(int argc, char *argv[]) switch (opt) { case OPTION_CTLSOCK: - ctl_path = optarg; + strlcpy(ctl_path, optarg, sizeof(ctl_path)); + ctlsockused = true; break; default: @@ -184,6 +190,10 @@ int main(int argc, char *argv[]) } } + if (bfdd_di.pathspace && !ctlsockused) + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "/", bfdd_di.pathspace); + #if 0 /* TODO add support for JSON configuration files. */ parse_config(conf); #endif @@ -202,6 +212,8 @@ int main(int argc, char *argv[]) bfd_vrf_init(); + access_list_init(); + /* Initialize zebra connection. */ bfdd_zclient_init(&bglobal.bfdd_privs); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index a58fca0cc..c4b2a606c 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -4411,7 +4411,7 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, /* apply the route-map */ if (bgp_vrf->adv_cmd_rmap[afi][safi].map) { - route_map_result_t ret; + int ret = 0; ret = route_map_apply( bgp_vrf->adv_cmd_rmap[afi][safi] diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index d5b3d6b19..648c3be47 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -294,6 +294,10 @@ static struct bgp_path_info_mpath * bgp_path_info_mpath_get(struct bgp_path_info *path) { struct bgp_path_info_mpath *mpath; + + if (!path) + return NULL; + if (!path->mpath) { mpath = bgp_path_info_mpath_new(); if (!mpath) @@ -523,6 +527,7 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, list_delete_node(mp_list, mp_node); bgp_path_info_mpath_dequeue(cur_mpath); if ((mpath_count < maxpaths) + && prev_mpath && bgp_path_info_nexthop_cmp(prev_mpath, cur_mpath)) { bgp_path_info_mpath_enqueue(prev_mpath, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 543df2b04..4de73f124 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1236,12 +1236,10 @@ static int bgp_cluster_filter(struct peer *peer, struct attr *attr) static int bgp_input_modifier(struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi, - const char *rmap_name, mpls_label_t *label, - uint32_t num_labels) + const char *rmap_name) { struct bgp_filter *filter; - struct bgp_path_info rmap_path = { 0 }; - struct bgp_path_info_extra extra = { 0 }; + struct bgp_path_info rmap_path; route_map_result_t ret; struct route_map *rmap = NULL; @@ -1271,11 +1269,6 @@ static int bgp_input_modifier(struct peer *peer, struct prefix *p, /* Duplicate current value to new strucutre for modification. */ rmap_path.peer = peer; rmap_path.attr = attr; - rmap_path.extra = &extra; - extra.num_labels = num_labels; - if (label && num_labels && num_labels <= BGP_MAX_LABELS) - memcpy(extra.label, label, - num_labels * sizeof(mpls_label_t)); SET_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN); @@ -1465,7 +1458,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, struct bgp *bgp; struct attr *piattr; char buf[PREFIX_STRLEN]; - route_map_result_t ret; + int ret; int transparent; int reflect; afi_t afi; @@ -2533,12 +2526,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* apply the route-map */ if (bgp->adv_cmd_rmap[afi][safi].map) { - route_map_result_t ret; + int ret = 0; ret = route_map_apply( bgp->adv_cmd_rmap[afi][safi].map, &rn->p, RMAP_BGP, new_select); - if (ret == RMAP_PERMITMATCH) + if (ret == RMAP_MATCH) bgp_evpn_advertise_type5_route( bgp, &rn->p, new_select->attr, afi, safi); @@ -3131,8 +3124,8 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, * commands, so we need bgp_attr_flush in the error paths, until we * intern * the attr (which takes over the memory references) */ - if (bgp_input_modifier(peer, p, &new_attr, afi, safi, NULL, - label, num_labels) == RMAP_DENY) { + if (bgp_input_modifier(peer, p, &new_attr, afi, safi, NULL) + == RMAP_DENY) { reason = "route-map;"; bgp_attr_flush(&new_attr); goto filtered; @@ -4564,7 +4557,7 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, struct bgp_path_info rmap_path; struct attr attr; struct attr *attr_new; - route_map_result_t ret; + int ret; #if ENABLE_BGP_VNC int vnc_implicit_withdraw = 0; #endif @@ -4912,7 +4905,7 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, if (bgp_static->rmap.name) { struct attr attr_tmp = attr; struct bgp_path_info rmap_path; - route_map_result_t ret; + int ret; rmap_path.peer = bgp->peer_self; rmap_path.attr = &attr_tmp; @@ -6574,7 +6567,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, struct attr attr; struct attr *new_attr; afi_t afi; - route_map_result_t ret; + int ret; struct bgp_redist *red; /* Make default attribute. */ @@ -9089,7 +9082,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct route_map *rmap = output_arg; struct bgp_path_info path; struct attr dummy_attr; - route_map_result_t ret; + int ret; bgp_attr_dup(&dummy_attr, pi->attr); @@ -9549,12 +9542,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "Paths: (%d available", count); if (best) { vty_out(vty, ", best #%d", best); - if (safi == SAFI_UNICAST) - vty_out(vty, ", table %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_DEFAULT) - ? VRF_DEFAULT_NAME - : bgp->name); + if (safi == SAFI_UNICAST) { + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + vty_out(vty, ", table %s", + VRF_DEFAULT_NAME); + else + vty_out(vty, ", vrf %s", + bgp->name); + } } else vty_out(vty, ", no best path"); @@ -11198,7 +11193,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, /* Filter prefix using route-map */ ret = bgp_input_modifier(peer, &rn->p, &attr, - afi, safi, rmap_name, NULL, 0); + afi, safi, rmap_name); if (type == bgp_show_adj_route_filtered && !route_filtered && ret != RMAP_DENY) { @@ -12380,30 +12375,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, p = &rn->p; - /* "network" configuration display. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - uint32_t destination; - struct in_addr netmask; - - destination = ntohl(p->u.prefix4.s_addr); - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " network %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN)); - - if ((IN_CLASSC(destination) && p->prefixlen == 24) - || (IN_CLASSB(destination) && p->prefixlen == 16) - || (IN_CLASSA(destination) && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* Natural mask is not display. */ - } else - vty_out(vty, " mask %s", inet_ntoa(netmask)); - } else { - vty_out(vty, " network %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " network %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) vty_out(vty, " label-index %u", @@ -12427,20 +12401,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, p = &rn->p; - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - struct in_addr netmask; - - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " aggregate-address %s %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - inet_ntoa(netmask)); - } else { - vty_out(vty, " aggregate-address %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " aggregate-address %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_aggregate->as_set) vty_out(vty, " as-set"); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 8c3a02429..28a763ed5 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -60,7 +60,6 @@ #include "bgpd/bgp_evpn_private.h" #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_mplsvpn.h" -#include "bgpd/bgp_encap_types.h" #if ENABLE_BGP_VNC #include "bgpd/rfapi/bgp_rfapi_cfg.h" @@ -238,9 +237,10 @@ struct bgp_match_peer_compiled { /* Compares the peer specified in the 'match peer' clause with the peer received in bgp_path_info->peer. If it is the same, or if the peer structure received is a peer_group containing it, returns RMAP_MATCH. */ -static enum route_map_match_result_t -route_match_peer(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_peer(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct bgp_match_peer_compiled *pc; union sockunion *su; @@ -333,9 +333,10 @@ struct route_map_rule_cmd route_match_peer_cmd = {"peer", route_match_peer, route_match_peer_free}; #if defined(HAVE_LUA) -static enum route_map_match_result_t -route_match_command(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_command(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { int status = RMAP_NOMATCH; u_int32_t locpref = 0; @@ -431,9 +432,10 @@ struct route_map_rule_cmd route_match_command_cmd = { /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -470,9 +472,10 @@ struct route_map_rule_cmd route_match_ip_address_cmd = { /* `match ip next-hop IP_ADDRESS' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_next_hop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_next_hop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; struct bgp_path_info *path; @@ -516,9 +519,10 @@ struct route_map_rule_cmd route_match_ip_next_hop_cmd = { /* `match ip route-source ACCESS-LIST' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_route_source(void *rule, const struct prefix *pfx, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_route_source(void *rule, + const struct prefix *pfx, + route_map_object_t type, + void *object) { struct access_list *alist; struct bgp_path_info *path; @@ -567,7 +571,7 @@ struct route_map_rule_cmd route_match_ip_route_source_cmd = { /* `match ip address prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -602,7 +606,7 @@ struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { /* `match ip next-hop prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -644,7 +648,7 @@ struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { /* `match ip next-hop type <blackhole>' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -683,7 +687,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_type_cmd = { /* `match ip route-source prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_route_source_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) @@ -733,9 +737,10 @@ struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = { /* `match evpn default-route' */ /* Match function should return 1 if match is success else 0 */ -static enum route_map_match_result_t -route_match_evpn_default_route(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t route_match_evpn_default_route(void *rule, + const struct prefix *p, + route_map_object_t + type, void *object) { if (type == RMAP_BGP && is_evpn_prefix_default(p)) return RMAP_MATCH; @@ -751,9 +756,10 @@ struct route_map_rule_cmd route_match_evpn_default_route_cmd = { /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_mac_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_mac_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; struct prefix p; @@ -796,49 +802,26 @@ struct route_map_rule_cmd route_match_mac_address_cmd = { "mac address", route_match_mac_address, route_match_mac_address_compile, route_match_mac_address_free}; -/* - * Match function returns: - * ...RMAP_MATCH if match is found. - * ...RMAP_NOMATCH if match is not found. - * ...RMAP_NOOP to ignore this match check. - */ -static enum route_map_match_result_t -route_match_vni(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +/* `match vni' */ + +/* Match function should return 1 if match is success else return + zero. */ +static route_map_result_t route_match_vni(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { vni_t vni = 0; - unsigned int label_cnt = 0; struct bgp_path_info *path = NULL; - struct prefix_evpn *evp = (struct prefix_evpn *) prefix; if (type == RMAP_BGP) { vni = *((vni_t *)rule); path = (struct bgp_path_info *)object; - /* - * This rmap filter is valid for vxlan tunnel type only. - * For any other tunnel type, return noop to ignore - * this check. - */ - if (path->attr && path->attr->encap_tunneltype != - BGP_ENCAP_TYPE_VXLAN) - return RMAP_NOOP; - - /* - * We do not want to filter type 3 routes because - * they do not have vni associated with them. - */ - if (evp && evp->prefix.route_type == BGP_EVPN_IMET_ROUTE) - return RMAP_NOOP; - if (path->extra == NULL) return RMAP_NOMATCH; - for ( ; label_cnt < BGP_MAX_LABELS && - label_cnt < path->extra->num_labels; label_cnt++) { - if (vni == label2vni(&path->extra->label[label_cnt])) - return RMAP_MATCH; - } + if (vni == label2vni(&path->extra->label[0])) + return RMAP_MATCH; } return RMAP_NOMATCH; @@ -876,9 +859,10 @@ struct route_map_rule_cmd route_match_evpn_vni_cmd = { /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_evpn_route_type(void *rule, const struct prefix *pfx, - route_map_object_t type, void *object) +static route_map_result_t route_match_evpn_route_type(void *rule, + const struct prefix *pfx, + route_map_object_t type, + void *object) { uint8_t route_type = 0; @@ -921,7 +905,7 @@ struct route_map_rule_cmd route_match_evpn_route_type_cmd = { route_match_evpn_route_type_compile, route_match_evpn_route_type_free}; /* Route map commands for VRF route leak with source vrf matching */ -static enum route_map_match_result_t +static route_map_result_t route_match_vrl_source_vrf(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -970,9 +954,10 @@ struct route_map_rule_cmd route_match_vrl_source_vrf_cmd = { /* `match local-preference LOCAL-PREF' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_local_pref(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_local_pref(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint32_t *local_pref; struct bgp_path_info *path; @@ -1026,9 +1011,10 @@ struct route_map_rule_cmd route_match_local_pref_cmd = { /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1049,9 +1035,10 @@ struct route_map_rule_cmd route_match_metric_cmd = { /* `match as-path ASPATH' */ /* Match function for as-path match. I assume given object is */ -static enum route_map_match_result_t -route_match_aspath(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_aspath(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct as_list *as_list; @@ -1098,9 +1085,10 @@ struct rmap_community { }; /* Match function for community match. */ -static enum route_map_match_result_t -route_match_community(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_community(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct community_list *list; struct bgp_path_info *path; @@ -1167,9 +1155,10 @@ struct route_map_rule_cmd route_match_community_cmd = { route_match_community_free}; /* Match function for lcommunity match. */ -static enum route_map_match_result_t -route_match_lcommunity(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_lcommunity(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct community_list *list; struct bgp_path_info *path; @@ -1229,9 +1218,10 @@ struct route_map_rule_cmd route_match_lcommunity_cmd = { /* Match function for extcommunity match. */ -static enum route_map_match_result_t -route_match_ecommunity(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ecommunity(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct community_list *list; struct bgp_path_info *path; @@ -1282,9 +1272,10 @@ struct route_map_rule_cmd route_match_ecommunity_cmd = { and `address-family vpnv4'. */ /* `match origin' */ -static enum route_map_match_result_t -route_match_origin(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_origin(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint8_t *origin; struct bgp_path_info *path; @@ -1329,9 +1320,10 @@ struct route_map_rule_cmd route_match_origin_cmd = { /* match probability { */ -static enum route_map_match_result_t -route_match_probability(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_probability(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { long r = random(); @@ -1383,9 +1375,10 @@ struct route_map_rule_cmd route_match_probability_cmd = { /* `match interface IFNAME' */ /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_interface(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_interface(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct interface *ifp; struct bgp_path_info *path; @@ -1429,9 +1422,9 @@ struct route_map_rule_cmd route_match_interface_cmd = { /* `set ip next-hop IP_ADDRESS' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_tag(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct bgp_path_info *path; @@ -1461,9 +1454,10 @@ struct rmap_ip_nexthop_set { int unchanged; }; -static enum route_map_match_result_t -route_set_ip_nexthop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_ip_nexthop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_ip_nexthop_set *rins = rule; struct bgp_path_info *path; @@ -1566,9 +1560,10 @@ struct route_map_rule_cmd route_set_ip_nexthop_cmd = { /* `set local-preference LOCAL_PREF' */ /* Set local preference. */ -static enum route_map_match_result_t -route_set_local_pref(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_local_pref(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1600,9 +1595,10 @@ struct route_map_rule_cmd route_set_local_pref_cmd = { /* `set weight WEIGHT' */ /* Set weight. */ -static enum route_map_match_result_t -route_set_weight(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_weight(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1627,9 +1623,10 @@ struct route_map_rule_cmd route_set_weight_cmd = { /* `set metric METRIC' */ /* Set metric to attribute. */ -static enum route_map_match_result_t -route_set_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1657,9 +1654,10 @@ struct route_map_rule_cmd route_set_metric_cmd = { /* `set as-path prepend ASPATH' */ /* For AS path prepend mechanism. */ -static enum route_map_match_result_t -route_set_aspath_prepend(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_aspath_prepend(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct aspath *aspath; struct aspath *new; @@ -1719,9 +1717,10 @@ struct route_map_rule_cmd route_set_aspath_prepend_cmd = { * one. * Make a deep copy of existing AS_PATH, but for the first ASn only. */ -static enum route_map_match_result_t -route_set_aspath_exclude(void *rule, const struct prefix *dummy, - route_map_object_t type, void *object) +static route_map_result_t route_set_aspath_exclude(void *rule, + const struct prefix *dummy, + route_map_object_t type, + void *object) { struct aspath *new_path, *exclude_path; struct bgp_path_info *path; @@ -1753,9 +1752,10 @@ struct rmap_com_set { }; /* For community set mechanism. */ -static enum route_map_match_result_t -route_set_community(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_community(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_com_set *rcs; struct bgp_path_info *path; @@ -1868,9 +1868,10 @@ struct rmap_lcom_set { /* For lcommunity set mechanism. */ -static enum route_map_match_result_t -route_set_lcommunity(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_lcommunity(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_lcom_set *rcs; struct bgp_path_info *path; @@ -1980,9 +1981,10 @@ struct route_map_rule_cmd route_set_lcommunity_cmd = { /* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */ /* For large community set mechanism. */ -static enum route_map_match_result_t -route_set_lcommunity_delete(void *rule, const struct prefix *pfx, - route_map_object_t type, void *object) +static route_map_result_t route_set_lcommunity_delete(void *rule, + const struct prefix *pfx, + route_map_object_t type, + void *object) { struct community_list *list; struct lcommunity *merge; @@ -2063,9 +2065,11 @@ struct route_map_rule_cmd route_set_lcommunity_delete_cmd = { /* `set comm-list (<1-99>|<100-500>|WORD) delete' */ /* For community set mechanism. */ -static enum route_map_match_result_t -route_set_community_delete(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_community_delete( + void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct community_list *list; struct community *merge; @@ -2145,9 +2149,10 @@ struct route_map_rule_cmd route_set_community_delete_cmd = { /* `set extcommunity rt COMMUNITY' */ /* For community set mechanism. Used by _rt and _soo. */ -static enum route_map_match_result_t -route_set_ecommunity(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_ecommunity(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct ecommunity *ecom; struct ecommunity *new_ecom; @@ -2232,9 +2237,10 @@ struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { /* `set origin ORIGIN' */ /* For origin set. */ -static enum route_map_match_result_t -route_set_origin(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_origin(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint8_t *origin; struct bgp_path_info *path; @@ -2281,9 +2287,10 @@ struct route_map_rule_cmd route_set_origin_cmd = { /* `set atomic-aggregate' */ /* For atomic aggregate set. */ -static enum route_map_match_result_t -route_set_atomic_aggregate(void *rule, const struct prefix *pfx, - route_map_object_t type, void *object) +static route_map_result_t route_set_atomic_aggregate(void *rule, + const struct prefix *pfx, + route_map_object_t type, + void *object) { struct bgp_path_info *path; @@ -2319,9 +2326,10 @@ struct aggregator { struct in_addr address; }; -static enum route_map_match_result_t -route_set_aggregator_as(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_aggregator_as(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct bgp_path_info *path; struct aggregator *aggregator; @@ -2372,9 +2380,9 @@ struct route_map_rule_cmd route_set_aggregator_as_cmd = { }; /* Set tag to object. object must be pointer to struct bgp_path_info */ -static enum route_map_match_result_t -route_set_tag(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct bgp_path_info *path; @@ -2397,9 +2405,10 @@ static struct route_map_rule_cmd route_set_tag_cmd = { }; /* Set label-index to object. object must be pointer to struct bgp_path_info */ -static enum route_map_match_result_t -route_set_label_index(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_label_index(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -2429,9 +2438,10 @@ static struct route_map_rule_cmd route_set_label_index_cmd = { /* `match ipv6 address IP_ACCESS_LIST' */ -static enum route_map_match_result_t -route_match_ipv6_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ipv6_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -2464,9 +2474,10 @@ struct route_map_rule_cmd route_match_ipv6_address_cmd = { /* `match ipv6 next-hop IP_ADDRESS' */ -static enum route_map_match_result_t -route_match_ipv6_next_hop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ipv6_next_hop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct in6_addr *addr = rule; struct bgp_path_info *path; @@ -2515,7 +2526,7 @@ struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = { /* `match ipv6 address prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -2550,9 +2561,9 @@ struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { /* `match ipv6 next-hop type <TYPE>' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) + route_map_object_t type, void *object) { struct bgp_path_info *path; struct in6_addr *addr = rule; @@ -2598,9 +2609,10 @@ struct route_map_rule_cmd route_match_ipv6_next_hop_type_cmd = { /* `set ipv6 nexthop global IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_ipv6_nexthop_global(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t route_set_ipv6_nexthop_global(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { struct in6_addr *address; struct bgp_path_info *path; @@ -2656,7 +2668,7 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_global_cmd = { route_set_ipv6_nexthop_global_free}; /* Set next-hop preference value. */ -static enum route_map_match_result_t +static route_map_result_t route_set_ipv6_nexthop_prefer_global(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -2710,9 +2722,10 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_prefer_global_cmd = { /* `set ipv6 nexthop local IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t route_set_ipv6_nexthop_local(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { struct in6_addr *address; struct bgp_path_info *path; @@ -2772,9 +2785,10 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = { /* `set ipv6 nexthop peer-address' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_ipv6_nexthop_peer(void *rule, const struct prefix *pfx, - route_map_object_t type, void *object) +static route_map_result_t route_set_ipv6_nexthop_peer(void *rule, + const struct prefix *pfx, + route_map_object_t type, + void *object) { struct in6_addr peer_address; struct bgp_path_info *path; @@ -2849,9 +2863,10 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd = { /* `set ipv4 vpn next-hop A.B.C.D' */ -static enum route_map_match_result_t -route_set_vpnv4_nexthop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_vpnv4_nexthop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct in_addr *address; struct bgp_path_info *path; @@ -2888,9 +2903,10 @@ static void *route_set_vpnv4_nexthop_compile(const char *arg) /* `set ipv6 vpn next-hop A.B.C.D' */ -static enum route_map_match_result_t -route_set_vpnv6_nexthop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_vpnv6_nexthop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct in6_addr *address; struct bgp_path_info *path; @@ -2943,9 +2959,10 @@ struct route_map_rule_cmd route_set_vpnv6_nexthop_cmd = { /* `set originator-id' */ /* For origin set. */ -static enum route_map_match_result_t -route_set_originator_id(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_originator_id(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct in_addr *address; struct bgp_path_info *path; diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index ab93ade56..063772399 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -129,10 +129,8 @@ static void print_record(const struct pfx_record *record, struct vty *vty); static int is_synchronized(void); static int is_running(void); static void route_match_free(void *rule); -static enum route_map_match_result_t route_match(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object); +static route_map_result_t route_match(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object); static void *route_match_compile(const char *arg); static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, safi_t safi); @@ -215,10 +213,8 @@ static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest) dest[i] = ntohl(src[i]); } -static enum route_map_match_result_t route_match(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static route_map_result_t route_match(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { int *rpki_status = rule; struct bgp_path_info *path; diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 21f1dff60..b64c51f34 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -716,7 +716,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) struct bgp_node *rn; struct bgp_path_info *ri; struct peer *peer; - route_map_result_t ret = RMAP_DENYMATCH; + int ret = RMAP_DENYMATCH; afi_t afi; safi_t safi; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6451c8d8e..7dc3f02b0 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -815,41 +815,6 @@ static void bgp_clear_star_soft_out(struct vty *vty, const char *name) #include "bgpd/bgp_vty_clippy.c" #endif -/* BGP global configuration. */ -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated bgp multiple-instance") -CPP_NOTICE("This includes BGP_OPT_MULTIPLE_INSTANCE") -#endif -DEFUN_HIDDEN (bgp_multiple_instance_func, - bgp_multiple_instance_cmd, - "bgp multiple-instance", - BGP_STR - "Enable bgp multiple instance\n") -{ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_multiple_instance, - no_bgp_multiple_instance_cmd, - "no bgp multiple-instance", - NO_STR - BGP_STR - "BGP multiple instance\n") -{ - int ret; - - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know\n"); - zlog_info("Deprecated option: `bgp multiple-instance` being used"); - ret = bgp_option_unset(BGP_OPT_MULTIPLE_INSTANCE); - if (ret < 0) { - vty_out(vty, "%% There are more than two BGP instances\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; -} - DEFUN_HIDDEN (bgp_local_mac, bgp_local_mac_cmd, "bgp local-mac vni " CMD_VNI_RANGE " mac WORD seq (0-4294967295)", @@ -931,44 +896,6 @@ DEFUN_HIDDEN (no_bgp_local_mac, return CMD_SUCCESS; } -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated cli bgp config-type cisco") -CPP_NOTICE("This includes BGP_OPT_CISCO_CONFIG") -#endif -DEFUN_HIDDEN (bgp_config_type, - bgp_config_type_cmd, - "bgp config-type <cisco|zebra>", - BGP_STR - "Configuration type\n" - "cisco\n" - "zebra\n") -{ - int idx = 0; - if (argv_find(argv, argc, "cisco", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know!\n"); - zlog_info("Deprecated option: `bgp config-type cisco` being used"); - bgp_option_set(BGP_OPT_CONFIG_CISCO); - } else - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_config_type, - no_bgp_config_type_cmd, - "no bgp config-type [<cisco|zebra>]", - NO_STR - BGP_STR - "Display configuration type\n" - "cisco\n" - "zebra\n") -{ - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - return CMD_SUCCESS; -} - - DEFUN (no_synchronization, no_synchronization_cmd, "no synchronization", @@ -7481,11 +7408,6 @@ DEFUN (show_bgp_views, struct listnode *node; struct bgp *bgp; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - vty_out(vty, "Defined BGP views:\n"); for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) { /* Skip VRFs. */ @@ -7516,11 +7438,6 @@ DEFUN (show_bgp_vrfs, json_object *json_vrfs = NULL; int count = 0; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - if (uj) { json = json_object_new_object(); json_vrfs = json_object_new_object(); @@ -12896,14 +12813,6 @@ void bgp_vty_init(void) install_default(BGP_EVPN_NODE); install_default(BGP_EVPN_VNI_NODE); - /* "bgp multiple-instance" commands. */ - install_element(CONFIG_NODE, &bgp_multiple_instance_cmd); - install_element(CONFIG_NODE, &no_bgp_multiple_instance_cmd); - - /* "bgp config-type" commands. */ - install_element(CONFIG_NODE, &bgp_config_type_cmd); - install_element(CONFIG_NODE, &no_bgp_config_type_cmd); - /* "bgp local-mac" hidden commands. */ install_element(CONFIG_NODE, &bgp_local_mac_cmd); install_element(CONFIG_NODE, &no_bgp_local_mac_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 2e648af1b..02eda7a43 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -171,8 +171,6 @@ int bgp_option_set(int flag) { switch (flag) { case BGP_OPT_NO_FIB: - case BGP_OPT_MULTIPLE_INSTANCE: - case BGP_OPT_CONFIG_CISCO: case BGP_OPT_NO_LISTEN: case BGP_OPT_NO_ZEBRA: SET_FLAG(bm->options, flag); @@ -186,13 +184,9 @@ int bgp_option_set(int flag) int bgp_option_unset(int flag) { switch (flag) { - case BGP_OPT_MULTIPLE_INSTANCE: - if (listcount(bm->bgp) > 1) - return BGP_ERR_MULTIPLE_INSTANCE_USED; /* Fall through. */ case BGP_OPT_NO_ZEBRA: case BGP_OPT_NO_FIB: - case BGP_OPT_CONFIG_CISCO: UNSET_FLAG(bm->options, flag); break; default: @@ -1205,21 +1199,18 @@ struct peer *peer_new(struct bgp *bgp) /* Set default flags. */ FOREACH_AFI_SAFI (afi, safi) { - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - } + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); + + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; } @@ -3174,40 +3165,21 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, struct vrf *vrf = NULL; /* Multiple instance check. */ - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (name) - bgp = bgp_lookup_by_name(name); - else - bgp = bgp_get_default(); - - /* Already exists. */ - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_INSTANCE_MISMATCH; - } - if (bgp->inst_type != inst_type) - return BGP_ERR_INSTANCE_MISMATCH; - *bgp_val = bgp; - return 0; - } - } else { - /* BGP instance name can not be specified for single instance. - */ - if (name) - return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET; - - /* Get default BGP structure if exists. */ + if (name) + bgp = bgp_lookup_by_name(name); + else bgp = bgp_get_default(); - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_AS_MISMATCH; - } - *bgp_val = bgp; - return 0; + /* Already exists. */ + if (bgp) { + if (bgp->as != *as) { + *as = bgp->as; + return BGP_ERR_INSTANCE_MISMATCH; } + if (bgp->inst_type != inst_type) + return BGP_ERR_INSTANCE_MISMATCH; + *bgp_val = bgp; + return 0; } bgp = bgp_create(as, name, inst_type); @@ -7319,45 +7291,19 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, flag_slcomm = peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY); - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " no neighbor %s send-community all\n", - addr); - } else { - if (flag_scomm) - vty_out(vty, - " no neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - - if (flag_slcomm) - vty_out(vty, - " no neighbor %s send-community large\n", - addr); - } + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " no neighbor %s send-community all\n", addr); } else { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " neighbor %s send-community all\n", + if (flag_scomm) + vty_out(vty, " no neighbor %s send-community\n", addr); + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", addr); - } else if (flag_scomm && flag_secomm) { - vty_out(vty, " neighbor %s send-community both\n", + + if (flag_slcomm) + vty_out(vty, " no neighbor %s send-community large\n", addr); - } else { - if (flag_scomm) - vty_out(vty, " neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " neighbor %s send-community extended\n", - addr); - if (flag_slcomm) - vty_out(vty, - " neighbor %s send-community large\n", - addr); - } } /* Default information */ @@ -7568,18 +7514,6 @@ int bgp_config_write(struct vty *vty) struct listnode *node, *nnode; struct listnode *mnode, *mnnode; - /* BGP Multiple instance. */ - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "no bgp multiple-instance\n"); - write++; - } - - /* BGP Config type. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - vty_out(vty, "bgp config-type cisco\n"); - write++; - } - if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) vty_out(vty, "bgp route-map delay-timer %u\n", bm->rmap_update_timer); @@ -7597,21 +7531,12 @@ int bgp_config_write(struct vty *vty) /* Router bgp ASN */ vty_out(vty, "router bgp %u", bgp->as); - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (bgp->name) - vty_out(vty, " %s %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_VIEW) - ? "view" - : "vrf", - bgp->name); - } + if (bgp->name) + vty_out(vty, " %s %s", + (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + ? "view" : "vrf", bgp->name); vty_out(vty, "\n"); - /* No Synchronization */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no synchronization\n"); - /* BGP fast-external-failover. */ if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) vty_out(vty, " no bgp fast-external-failover\n"); @@ -7825,10 +7750,6 @@ int bgp_config_write(struct vty *vty) if (bgp->autoshutdown) vty_out(vty, " bgp default shutdown\n"); - /* No auto-summary */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no auto-summary\n"); - /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); @@ -7904,9 +7825,6 @@ void bgp_master_init(struct thread_master *master) bf_init(bm->rd_idspace, UINT16_MAX); bf_assign_zero_index(bm->rd_idspace); - /* Enable multiple instances by default. */ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - /* mpls label dynamic allocation pool */ bgp_lp_init(bm->master, &bm->labelpool); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index c600d9f3f..518f1f671 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -134,10 +134,8 @@ struct bgp_master { /* Various BGP global configuration. */ uint8_t options; #define BGP_OPT_NO_FIB (1 << 0) -#define BGP_OPT_MULTIPLE_INSTANCE (1 << 1) -#define BGP_OPT_CONFIG_CISCO (1 << 2) -#define BGP_OPT_NO_LISTEN (1 << 3) -#define BGP_OPT_NO_ZEBRA (1 << 4) +#define BGP_OPT_NO_LISTEN (1 << 1) +#define BGP_OPT_NO_ZEBRA (1 << 2) uint64_t updgrp_idspace; uint64_t subgrp_idspace; diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index b6d32d36e..583adcda7 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -1143,6 +1143,9 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, break; } + memset(&pfx_un1, 0, sizeof(pfx_un1)); + memset(&pfx_un2, 0, sizeof(pfx_un2)); + /* * UN address comparisons */ @@ -1184,7 +1187,7 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, } } - if (!pfx_un1.family || !pfx_un2.family) + if (pfx_un1.family == 0 || pfx_un2.family == 0) return 0; if (pfx_un1.family != pfx_un2.family) diff --git a/configure.ac b/configure.ac index 906006a97..77d5ee155 100755 --- a/configure.ac +++ b/configure.ac @@ -2133,10 +2133,10 @@ fi AC_MSG_RESULT([${frr_statedir}]) AC_SUBST([frr_statedir]) -AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir/ldpd.sock"], [ldpd control socket]) -AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir/zserv.api"], [zebra api socket]) -AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir/bfdd.sock"], [bfdd control socket]) -AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir"], [daemon vty directory]) +AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control socket]) +AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket]) +AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) +AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) dnl autoconf does this, but it does it too late... diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index 1dc188515..e393fe6fb 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -6,6 +6,114 @@ to log, what level to log it at, and when to log it. Here is a list of recommendations for these decisions. +printfrr() +---------- + +``printfrr()`` is FRR's modified version of ``printf()``, designed to make +life easier when printing nontrivial datastructures. The following variants +are available: + +.. c:function:: ssize_t snprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + These correspond to ``snprintf``/``vsnprintf``. If you pass NULL for buf + or 0 for len, no output is written but the return value is still calculated. + + The return value is always the full length of the output, unconstrained by + `len`. It does **not** include the terminating ``\0`` character. A + malformed format string can result in a ``-1`` return value. + +.. c:function:: ssize_t csnprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vcsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + Same as above, but the ``c`` stands for "continue" or "concatenate". The + output is appended to the string instead of overwriting it. + +.. c:function:: char *asprintfrr(struct memtype *mt, const char *fmt, ...) +.. c:function:: char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) + + These functions allocate a dynamic buffer (using MTYPE `mt`) and print to + that. If the format string is malformed, they return a copy of the format + string, so the return value is always non-NULL and always dynamically + allocated with `mt`. + +.. c:function:: char *asnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, ...) +.. c:function:: char *vasnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, va_list) + + This variant tries to use the static buffer provided, but falls back to + dynamic allocation if it is insufficient. + + The return value can be either `buf` or a newly allocated string using + `mt`. You MUST free it like this:: + + char *ret = asnprintfrr(MTYPE_FOO, buf, sizeof(buf), ...); + if (ret != buf) + XFREE(MTYPE_FOO, ret); + +Extensions +^^^^^^^^^^ + +``printfrr()`` format strings can be extended with suffixes after `%p` or +`%d`. The following extended format specifiers are available: + ++-----------+--------------------------+----------------------------------------------+ +| Specifier | Argument | Output | ++===========+==========================+==============================================+ +| ``%Lu`` | ``uint64_t`` | ``12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%Ld`` | ``int64_t`` | ``-12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI4`` | ``struct in_addr *`` | ``1.2.3.4`` | +| | | | +| | ``in_addr_t *`` | | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pRN`` | ``struct route_node *`` | ``192.168.1.0/24`` (dst-only node) | +| | | | +| | | ``2001:db8::/32 from fe80::/64`` (SADR node) | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHv`` | ``struct nexthop *`` | ``1.2.3.4, via eth0`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` | ++-----------+--------------------------+----------------------------------------------+ + +Printf features like field lengths can be used normally with these extensions, +e.g. ``%-15pI4`` works correctly. + +The extension specifier after ``%p`` or ``%d`` is always an uppercase letter; +by means of established pattern uppercase letters and numbers form the type +identifier which may be followed by lowercase flags. + +You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended +printers and what exactly they do. More printers are likely to be added as +needed/useful, so the list above may become outdated. + +``%Ld`` is not an "extension" for printfrr; it's wired directly into the main +printf logic. + +.. note:: + + The ``zlog_*``/``flog_*`` and ``vty_out`` functions all use printfrr + internally, so these extensions are available there. However, they are + **not** available when calling ``snprintf`` directly. You need to call + ``snprintfrr`` instead. + +AS-Safety +^^^^^^^^^ + +``printfrr()`` are AS-Safe under the following conditions: + +* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory) +* floating point specifiers are not AS-Safe (system printf is used for these) +* the positional ``%1$d`` syntax should not be used (8 arguments are supported + while AS-Safe) +* extensions are only AS-Safe if their printer is AS-Safe + Errors and warnings ------------------- diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 16707c0bd..07c43ac2d 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -73,14 +73,20 @@ Releases FRR employs a ``<MAJOR>.<MINOR>.<BUGFIX>`` versioning scheme. ``MAJOR`` - Significant new features or multiple minor features. The addition of a new - routing protocol or daemon would fall under this class. + Significant new features or multiple minor features. This should mostly + cover any kind of disruptive change that is visible or "risky" to operators. + New features or protocols do not necessarily trigger this. (This was changed + for FRR 7.x after feedback from users that the pace of major version number + increments was too high.) ``MINOR`` - Small features, e.g. options for automatic BGP shutdown. + General incremental development releases, excluding "major" changes + mentioned above. Not necessarily fully backwards compatible, as smaller + (but still visible) changes or deprecated feature removals may still happen. + However, there shouldn't be any huge "surprises" between minor releases. ``BUGFIX`` - Fixes for actual bugs and/or security issues. + Fixes for actual bugs and/or security issues. Fully compatible. We will pull a new development branch for the next release every 4 months. The current schedule is Feb/June/October 1. The decision for a ``MAJOR/MINOR`` @@ -362,6 +368,19 @@ Documentation should be written in reStructuredText. Sphinx extensions may be utilized but pure ReST is preferred where possible. See :ref:`documentation`. +Use of C++ +---------- + +While C++ is not accepted for core components of FRR, extensions, modules or +other distinct components may want to use C++ and include FRR header files. +There is no requirement on contributors to work to retain C++ compatibility, +but fixes for C++ compatibility are welcome. + +This implies that the burden of work to keep C++ compatibility is placed with +the people who need it, and they may provide it at their leisure to the extent +it is useful to them. So, if only a subset of header files, or even parts of +a header file are made available to C++, this is perfectly fine. + Code Reviews ============ diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 3df60169f..3d3a75d4b 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -180,13 +180,14 @@ Basic Config Commands In this example, the precision is set to provide timestamps with millisecond accuracy. -.. index:: log commands -.. clicmd:: log commands +.. index:: [no] log commands +.. clicmd:: [no] log commands This command enables the logging of all commands typed by a user to all enabled log destinations. The note that logging includes full command lines, - including passwords. Once set, command logging can only be turned off by - restarting the daemon. + including passwords. If the daemon startup option `--command-log-always` + is used to start the daemon then this command is turned on by default + and cannot be turned off and the [no] form of the command is dissallowed. .. index:: service password-encryption .. clicmd:: service password-encryption @@ -478,10 +479,23 @@ These options apply to all |PACKAGE_NAME| daemons. Set the user and group to run as. +.. option:: -N <namespace> + + Set the namespace that the daemon will run in. A "/<namespace>" will + be added to all files that use the statedir. If you have "/var/run/frr" + as the default statedir then it will become "/var/run/frr/<namespace>". + .. option:: -v, --version Print program version. +.. option:: --command-log-always + + Cause the daemon to always log commands entered to the specified log file. + This also makes the `no log commands` command dissallowed. Enabling this + is suggested if you have need to track what the operator is doing on + this router. + .. option:: --log <stdout|syslog|file:/path/to/log/file> When initializing the daemon, setup the log to go to either stdout, diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 33bc77049..32ca5707d 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -47,6 +47,9 @@ may also be specified (:ref:`common-invocation-options`). #define BFDD_CONTROL_SOCKET "|INSTALL_PREFIX_STATE|/bfdd.sock" + This option overrides the location addition that the -N option provides + to the bfdd.sock + .. _bfd-commands: diff --git a/doc/user/ldpd.rst b/doc/user/ldpd.rst index 85d280343..977195d6a 100644 --- a/doc/user/ldpd.rst +++ b/doc/user/ldpd.rst @@ -24,6 +24,12 @@ Running Ldpd The *ldpd* daemon can be invoked with any of the common options (:ref:`common-invocation-options`). +..option:: --ctl_socket + + This option allows you to override the path to the ldpd.sock file + used to control this daemon. If specified this option overrides + the -N option path addition. + The *zebra* daemon must be running before *ldpd* is invoked. Configuration of *ldpd* is done in its configuration file diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 8b0fba5f8..e8c74f7b8 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -547,6 +547,13 @@ Clear commands reset various variables. Reset multicast routes. +.. index:: clear ip mroute [vrf NAME] count +.. clicmd:: clear ip mroute [vrf NAME] count + + When this command is issued, reset the counts of data shown for + packet count, byte count and wrong interface to 0 and start count + up from this spot. + .. index:: clear ip pim interfaces .. clicmd:: clear ip pim interfaces diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 40d894929..eefc5802a 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -58,6 +58,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. seealso:: :ref:`zebra-vrf` +.. option:: -z <path_to_socket>, --socket <path_to_socket> + + If this option is supplied on the cli, the path to the zebra + control socket(zapi), is used. This option overrides a -N <namespace> + option if handed to it on the cli. + .. option:: --v6-rr-semantics The linux kernel is receiving the ability to use the same route diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index 961556d43..0a00497d5 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -251,9 +251,9 @@ void eigrp_route_map_update(const char *notused) /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_metric(void *rule, struct prefix *prefix, route_map_object_t type, - void *object) +static route_map_result_t route_match_metric(void *rule, struct prefix *prefix, + route_map_object_t type, + void *object) { // uint32_t *metric; // uint32_t check; @@ -311,9 +311,10 @@ struct route_map_rule_cmd route_match_metric_cmd = { /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_interface(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_interface(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) { // struct rip_info *rinfo; // struct interface *ifp; @@ -359,9 +360,10 @@ struct route_map_rule_cmd route_match_interface_cmd = { /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_next_hop(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_next_hop(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) { // struct access_list *alist; // struct rip_info *rinfo; @@ -405,7 +407,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = { /* `match ip next-hop prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_next_hop_prefix_list(void *rule, struct prefix *prefix, route_map_object_t type, void *object) { @@ -450,9 +452,10 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_address(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_address(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -488,7 +491,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = { /* `match ip address prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_address_prefix_list(void *rule, struct prefix *prefix, route_map_object_t type, void *object) { @@ -523,9 +526,8 @@ static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_tag(void *rule, struct prefix *prefix, route_map_object_t type, - void *object) +static route_map_result_t route_match_tag(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // unsigned short *tag; // struct rip_info *rinfo; @@ -566,9 +568,9 @@ struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, route_match_tag_compile, route_match_tag_free}; /* Set metric to attribute. */ -static enum route_map_match_result_t -route_set_metric(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric(void *rule, struct prefix *prefix, + route_map_object_t type, + void *object) { // if (type == RMAP_RIP) // { @@ -660,9 +662,10 @@ static struct route_map_rule_cmd route_set_metric_cmd = { /* `set ip next-hop IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_ip_nexthop(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_ip_nexthop(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) { // struct in_addr *address; // struct rip_info *rinfo; @@ -715,9 +718,8 @@ static struct route_map_rule_cmd route_set_ip_nexthop_cmd = { /* `set tag TAG' */ /* Set tag to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_tag(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_tag(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // unsigned short *tag; // struct rip_info *rinfo; diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 69694f26e..d63676256 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -48,9 +48,10 @@ #include "isis_zebra.h" #include "isis_routemap.h" -static enum route_map_match_result_t -route_match_ip_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -80,7 +81,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = { /* ------------------------------------------------------------*/ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -113,9 +114,10 @@ struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { /* ------------------------------------------------------------*/ -static enum route_map_match_result_t -route_match_ipv6_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ipv6_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -145,7 +147,7 @@ static struct route_map_rule_cmd route_match_ipv6_address_cmd = { /* ------------------------------------------------------------*/ -static enum route_map_match_result_t +static route_map_result_t route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -178,9 +180,10 @@ struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { /* ------------------------------------------------------------*/ -static enum route_map_match_result_t -route_set_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint32_t *metric; struct isis_ext_info *info; diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 6b8e74f07..6edfeb4c9 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -3271,13 +3271,18 @@ void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs, { struct listnode *node; struct prefix_ipv6 *ip_addr; + unsigned int addr_count = 0; for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) { + if (addr_count >= 15) + break; + struct isis_ipv6_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); a->addr = ip_addr->prefix; append_item(&tlvs->ipv6_address, (struct isis_item *)a); + addr_count++; } } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 771d3b745..5aaa2ec32 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -116,7 +116,7 @@ struct zebra_privs_t ldpd_privs = }; /* CTL Socket path */ -char ctl_sock_path[MAXPATHLEN] = LDPD_SOCKET; +char ctl_sock_path[MAXPATHLEN]; /* LDPd options. */ #define OPTION_CTLSOCK 1001 @@ -219,6 +219,10 @@ main(int argc, char *argv[]) int pipe_parent2lde[2], pipe_parent2lde_sync[2]; char *ctl_sock_name; struct thread *thread = NULL; + bool ctl_sock_used = false; + + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "", ""); ldpd_process = PROC_MAIN; log_procname = log_procnames[ldpd_process]; @@ -244,6 +248,7 @@ main(int argc, char *argv[]) case 0: break; case OPTION_CTLSOCK: + ctl_sock_used = true; ctl_sock_name = strrchr(LDPD_SOCKET, '/'); if (ctl_sock_name) /* skip '/' */ @@ -277,6 +282,10 @@ main(int argc, char *argv[]) } } + if (ldpd_di.pathspace && !ctl_sock_used) + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "/", ldpd_di.pathspace); + strlcpy(init.user, ldpd_privs.user, sizeof(init.user)); strlcpy(init.group, ldpd_privs.group, sizeof(init.group)); strlcpy(init.ctl_sock_path, ctl_sock_path, sizeof(init.ctl_sock_path)); diff --git a/lib/compiler.h b/lib/compiler.h index c2e57db7f..750942822 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -50,6 +50,16 @@ extern "C" { #endif #endif +#if !defined(__GNUC__) +#error module code needs GCC visibility extensions +#elif __GNUC__ < 4 +#error module code needs GCC visibility extensions +#else +# define DSO_PUBLIC __attribute__ ((visibility ("default"))) +# define DSO_SELF __attribute__ ((visibility ("protected"))) +# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) +#endif + #ifdef __sun /* Solaris doesn't do constructor priorities due to linker restrictions */ #undef _CONSTRUCTOR diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 38494fb00..6d28a667b 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -56,7 +56,7 @@ int main(int argc, char **argv) host.name = strdup("test"); host.domainname = strdup("testdomainname"); - vty_init(master); + vty_init(master, true); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/lib/libfrr.c b/lib/libfrr.c index c60e26085..ed784fc73 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -46,7 +46,7 @@ DEFINE_KOOH(frr_early_fini, (), ()) DEFINE_KOOH(frr_fini, (), ()) const char frr_sysconfdir[] = SYSCONFDIR; -const char frr_vtydir[] = DAEMON_VTY_DIR; +char frr_vtydir[256]; #ifdef HAVE_SQLITE3 const char frr_dbdir[] = DAEMON_DB_DIR; #endif @@ -57,7 +57,7 @@ char frr_protonameinst[256] = "NONE"; char config_default[512]; char frr_zclientpath[256]; -static char pidfile_default[512]; +static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 static char dbfile_default[512]; #endif @@ -94,6 +94,7 @@ static void opt_extend(const struct optspec *os) #define OPTION_LOGLEVEL 1004 #define OPTION_TCLI 1005 #define OPTION_DB_FILE 1006 +#define OPTION_LOGGING 1007 static const struct option lo_always[] = { {"help", no_argument, NULL, 'h'}, @@ -105,6 +106,7 @@ static const struct option lo_always[] = { {"log", required_argument, NULL, OPTION_LOG}, {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, {"tcli", no_argument, NULL, OPTION_TCLI}, + {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { "hvdM:", @@ -177,7 +179,7 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, memset(sa, 0, sizeof(*sa)); if (!path) - path = ZEBRA_SERV_PATH; + path = frr_zclientpath; if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) { /* note: this functionality is disabled at bottom */ @@ -283,6 +285,11 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, static struct frr_daemon_info *di = NULL; +void frr_init_vtydir(void) +{ + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", ""); +} + void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) { di = daemon; @@ -305,10 +312,13 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) if (di->flags & FRR_DETACH_LATER) nodetach_daemon = true; + frr_init_vtydir(); snprintf(config_default, sizeof(config_default), "%s/%s.conf", frr_sysconfdir, di->name); snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", frr_vtydir, di->name); + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "", ""); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db", frr_dbdir, di->name); @@ -317,8 +327,6 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) strlcpy(frr_protoname, di->logname, sizeof(frr_protoname)); strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst)); - strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath)); - di->cli_mode = FRR_CLI_CLASSIC; } @@ -398,6 +406,10 @@ static int frr_opt(int opt) errors++; break; } + if (di->zpathspace) + fprintf(stderr, + "-N option overriden by -z for zebra named socket path\n"); + if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, "slashes or dots are not permitted in the --pathspace option.\n"); @@ -405,6 +417,14 @@ static int frr_opt(int opt) break; } di->pathspace = optarg; + + if (!di->zpathspace) + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "/", di->pathspace); + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/", + di->pathspace); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", + frr_vtydir, di->name); break; #ifdef HAVE_SQLITE3 case OPTION_DB_FILE: @@ -424,6 +444,10 @@ static int frr_opt(int opt) di->terminal = 1; break; case 'z': + di->zpathspace = true; + if (di->pathspace) + fprintf(stderr, + "-z option overrides -N option for zebra named socket path\n"); if (di->flags & FRR_NO_ZCLIENT) return 1; strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath)); @@ -496,6 +520,9 @@ static int frr_opt(int opt) case OPTION_LOGLEVEL: di->early_loglevel = optarg; break; + case OPTION_LOGGING: + di->log_always = true; + break; default: return 1; } @@ -590,8 +617,8 @@ struct thread_master *frr_init(void) snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf", frr_sysconfdir, p_pathspace, di->name, p_instance); - snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid", - frr_vtydir, p_pathspace, di->name, p_instance); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid", + frr_vtydir, di->name, p_instance); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db", frr_dbdir, p_pathspace, di->name, p_instance); @@ -648,7 +675,7 @@ struct thread_master *frr_init(void) else cmd_init(1); - vty_init(master); + vty_init(master, di->log_always); memory_init(); log_ref_init(); @@ -875,9 +902,7 @@ static void frr_vty_serv(void) const char *dir; char defvtydir[256]; - snprintf(defvtydir, sizeof(defvtydir), "%s%s%s", frr_vtydir, - di->pathspace ? "/" : "", - di->pathspace ? di->pathspace : ""); + snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir); dir = di->vty_sock_path ? di->vty_sock_path : defvtydir; diff --git a/lib/libfrr.h b/lib/libfrr.h index 891e2c128..e2b3db74a 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -81,7 +81,10 @@ struct frr_daemon_info { #endif const char *vty_path; const char *module_path; + const char *pathspace; + bool zpathspace; + const char *early_logging; const char *early_loglevel; @@ -97,6 +100,8 @@ struct frr_daemon_info { const struct frr_yang_module_info **yang_modules; size_t n_yang_modules; + + bool log_always; }; /* execname is the daemon's executable (and pidfile and configfile) name, @@ -118,6 +123,7 @@ struct frr_daemon_info { .version = FRR_VERSION, ) \ /* end */ +extern void frr_init_vtydir(void); extern void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv); extern void frr_opt_add(const char *optstr, const struct option *longopts, const char *helpstr); @@ -148,7 +154,7 @@ extern void frr_fini(void); extern char config_default[512]; extern char frr_zclientpath[256]; extern const char frr_sysconfdir[]; -extern const char frr_vtydir[]; +extern char frr_vtydir[256]; extern const char frr_moduledir[]; extern char frr_protoname[]; @@ -30,6 +30,7 @@ #include "command.h" #include "lib_errors.h" #include "lib/hook.h" +#include "printfrr.h" #ifndef SUNOS_5 #include <sys/un.h> @@ -191,19 +192,13 @@ static void time_print(FILE *fp, struct timestamp_control *ctl) static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, const char *proto_str, int record_priority, int priority, - FILE *fp, const char *format, va_list args) + FILE *fp, const char *msg) { - va_list ac; - time_print(fp, tsctl); if (record_priority) fprintf(fp, "%s: ", zlog_priority[priority]); - fprintf(fp, "%s", proto_str); - va_copy(ac, args); - vfprintf(fp, format, ac); - va_end(ac); - fprintf(fp, "\n"); + fprintf(fp, "%s%s\n", proto_str, msg); fflush(fp); } @@ -217,33 +212,26 @@ void vzlog(int priority, const char *format, va_list args) struct timestamp_control tsctl; tsctl.already_rendered = 0; struct zlog *zl = zlog_default; + char buf[256], *msg; /* call external hook */ hook_call(zebra_ext_log, priority, format, args); + msg = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), format, args); + /* When zlog_default is also NULL, use stderr for logging. */ if (zl == NULL) { tsctl.precision = 0; time_print(stderr, &tsctl); - fprintf(stderr, "%s: ", "unknown"); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + fprintf(stderr, "%s: %s\n", "unknown", msg); fflush(stderr); - - /* In this case we return at here. */ - errno = original_errno; - pthread_mutex_unlock(&loglock); - return; + goto out; } tsctl.precision = zl->timestamp_precision; /* Syslog output */ - if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) { - va_list ac; - va_copy(ac, args); - vsyslog(priority | zlog_default->facility, format, ac); - va_end(ac); - } + if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) + syslog(priority | zlog_default->facility, "%s", msg); if (zl->instance) sprintf(proto_str, "%s[%d]: ", zl->protoname, zl->instance); @@ -253,7 +241,7 @@ void vzlog(int priority, const char *format, va_list args) /* File output. */ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - zl->fp, format, args); + zl->fp, msg); /* fixed-config logging to stderr while we're stating up & haven't * daemonized / reached mainloop yet @@ -261,17 +249,19 @@ void vzlog(int priority, const char *format, va_list args) * note the "else" on stdout output -- we don't want to print the same * message to both stderr and stdout. */ if (zlog_startup_stderr && priority <= LOG_WARNING) - vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, format, - args); + vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, msg); else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - stdout, format, args); + stdout, msg); /* Terminal monitor. */ if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) vty_log((zl->record_priority ? zlog_priority[priority] : NULL), - proto_str, format, &tsctl, args); + proto_str, msg, &tsctl); +out: + if (msg != buf) + XFREE(MTYPE_TMP, msg); errno = original_errno; pthread_mutex_unlock(&loglock); } @@ -305,47 +295,11 @@ int vzlog_test(int priority) return ret; } -static char *str_append(char *dst, int len, const char *src) -{ - while ((len-- > 0) && *src) - *dst++ = *src++; - return dst; -} - -static char *num_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - *--t = '0' + (x % 10); - x /= 10; - } - return str_append(s, len, t); -} - -#if defined(SA_SIGINFO) \ - || defined(HAVE_PRINTSTACK) \ - || defined(HAVE_GLIBC_BACKTRACE) -static char *hex_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - unsigned int cc = (x % 16); - *--t = ((cc < 10) ? ('0' + cc) : ('a' + cc - 10)); - x /= 16; - } - return str_append(s, len, t); -} -#endif +/* + * crash handling + * + * NB: only AS-Safe (async-signal) functions can be used here! + */ /* Needs to be enhanced to support Solaris. */ static int syslog_connect(void) @@ -354,7 +308,6 @@ static int syslog_connect(void) return -1; #else int fd; - char *s; struct sockaddr_un addr; if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) @@ -365,10 +318,8 @@ static int syslog_connect(void) #else #define SYSLOG_SOCKET_PATH "/dev/log" #endif - s = str_append(addr.sun_path, sizeof(addr.sun_path), - SYSLOG_SOCKET_PATH); + strlcpy(addr.sun_path, SYSLOG_SOCKET_PATH, sizeof(addr.sun_path)); #undef SYSLOG_SOCKET_PATH - *s = '\0'; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(fd); return -1; @@ -381,168 +332,109 @@ static void syslog_sigsafe(int priority, const char *msg, size_t msglen) { static int syslog_fd = -1; char buf[sizeof("<1234567890>ripngd[1234567890]: ") + msglen + 50]; - char *s; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) return; -#define LOC s,buf+sizeof(buf)-s - s = buf; - s = str_append(LOC, "<"); - s = num_append(LOC, priority); - s = str_append(LOC, ">"); /* forget about the timestamp, too difficult in a signal handler */ - s = str_append(LOC, zlog_default->ident); - if (zlog_default->syslog_options & LOG_PID) { - s = str_append(LOC, "["); - s = num_append(LOC, getpid()); - s = str_append(LOC, "]"); - } - s = str_append(LOC, ": "); - s = str_append(LOC, msg); - write_wrapper(syslog_fd, buf, s - buf); -#undef LOC + bprintfrr(&fb, "<%d>%s", priority, zlog_default->ident); + if (zlog_default->syslog_options & LOG_PID) + bprintfrr(&fb, "[%ld]", (long)getpid()); + bprintfrr(&fb, ": %s", msg); + write_wrapper(syslog_fd, fb.buf, fb.pos - fb.buf); } static int open_crashlog(void) { -#define CRASHLOG_PREFIX "/var/tmp/quagga." -#define CRASHLOG_SUFFIX "crashlog" - if (zlog_default && zlog_default->ident) { - /* Avoid strlen since it is not async-signal-safe. */ - const char *p; - size_t ilen; - - for (p = zlog_default->ident, ilen = 0; *p; p++) - ilen++; - { - char buf[sizeof(CRASHLOG_PREFIX) + ilen - + sizeof(CRASHLOG_SUFFIX) + 3]; - char *s = buf; -#define LOC s,buf+sizeof(buf)-s - s = str_append(LOC, CRASHLOG_PREFIX); - s = str_append(LOC, zlog_default->ident); - s = str_append(LOC, "."); - s = str_append(LOC, CRASHLOG_SUFFIX); -#undef LOC - *s = '\0'; - return open(buf, O_WRONLY | O_CREAT | O_EXCL, - LOGFILE_MASK); - } - } - return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, - O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); -#undef CRASHLOG_SUFFIX -#undef CRASHLOG_PREFIX -} - -/* Note: the goal here is to use only async-signal-safe functions. */ -void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ) -{ - time_t now; - char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") - + 100]; - char *s = buf; - char *msgstart = buf; -#define LOC s,buf+sizeof(buf)-s + char crashlog_buf[PATH_MAX]; + const char *crashlog_default = "/var/tmp/frr.crashlog", *crashlog; - time(&now); - if (zlog_default) { - s = str_append(LOC, zlog_default->protoname); - *s++ = ':'; - *s++ = ' '; - msgstart = s; - } - s = str_append(LOC, "Received signal "); - s = num_append(LOC, signo); - s = str_append(LOC, " at "); - s = num_append(LOC, now); -#ifdef SA_SIGINFO - s = str_append(LOC, " (si_addr 0x"); - s = hex_append(LOC, (unsigned long)(siginfo->si_addr)); - if (program_counter) { - s = str_append(LOC, ", PC 0x"); - s = hex_append(LOC, (unsigned long)program_counter); + if (!zlog_default || !zlog_default->ident) + crashlog = crashlog_default; + else { + snprintfrr(crashlog_buf, sizeof(crashlog_buf), + "/var/tmp/frr.%s.crashlog", zlog_default->ident); + crashlog = crashlog_buf; } - s = str_append(LOC, "); "); -#else /* SA_SIGINFO */ - s = str_append(LOC, "; "); -#endif /* SA_SIGINFO */ - s = str_append(LOC, action); - if (s < buf + sizeof(buf)) - *s++ = '\n'; + return open(crashlog, O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); +} /* N.B. implicit priority is most severe */ #define PRI LOG_CRIT -#define DUMP(FD) write_wrapper(FD, buf, s-buf); +static void crash_write(struct fbuf *fb, char *msgstart) +{ + if (fb->pos == fb->buf) + return; + if (!msgstart) + msgstart = fb->buf; + /* If no file logging configured, try to write to fallback log file. */ if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - DUMP(logfile_fd) + write(logfile_fd, fb->buf, fb->pos - fb->buf); if (!zlog_default) - DUMP(STDERR_FILENO) + write(STDERR_FILENO, fb->buf, fb->pos - fb->buf); else { if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) + write(STDOUT_FILENO, fb->buf, fb->pos - fb->buf); /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; + fb->pos--; if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb->buf, fb->pos - fb->buf); if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); + fb->pos - msgstart); } -#undef DUMP +} - zlog_backtrace_sigsafe(PRI, +/* Note: the goal here is to use only async-signal-safe functions. */ +void zlog_signal(int signo, const char *action, void *siginfo_v, + void *program_counter) +{ #ifdef SA_SIGINFO - program_counter -#else - NULL + siginfo_t *siginfo = siginfo_v; #endif - ); + time_t now; + char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") + + 100]; + char *msgstart; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; + + time(&now); + if (zlog_default) + bprintfrr(&fb, "%s: ", zlog_default->protoname); + + msgstart = fb.pos; + + bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now); +#ifdef SA_SIGINFO + if (program_counter) + bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)", + (ptrdiff_t)siginfo->si_addr, + (ptrdiff_t)program_counter); + else + bprintfrr(&fb, " (si_addr 0x%tx)", + (ptrdiff_t)siginfo->si_addr); +#endif /* SA_SIGINFO */ + bprintfrr(&fb, "; %s\n", action); + + crash_write(&fb, msgstart); + + zlog_backtrace_sigsafe(PRI, program_counter); + + fb.pos = buf; - s = buf; struct thread *tc; tc = pthread_getspecific(thread_current); - if (!tc) - s = str_append(LOC, "no thread information available\n"); - else { - s = str_append(LOC, "in thread "); - s = str_append(LOC, tc->funcname); - s = str_append(LOC, " scheduled from "); - s = str_append(LOC, tc->schedfrom); - s = str_append(LOC, ":"); - s = num_append(LOC, tc->schedfrom_line); - s = str_append(LOC, "\n"); - } -#define DUMP(FD) write_wrapper(FD, buf, s-buf); - /* If no file logging configured, try to write to fallback log file. */ - if (logfile_fd >= 0) - DUMP(logfile_fd) - if (!zlog_default) - DUMP(STDERR_FILENO) - else { - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) - /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); - } -#undef DUMP + if (!tc) + bprintfrr(&fb, "no thread information available\n"); + else + bprintfrr(&fb, "in thread %s scheduled from %s:%d\n", + tc->funcname, tc->schedfrom, tc->schedfrom_line); -#undef PRI -#undef LOC + crash_write(&fb, NULL); } /* Log a backtrace using only async-signal-safe functions. @@ -550,7 +442,8 @@ void zlog_signal(int signo, const char *action void zlog_backtrace_sigsafe(int priority, void *program_counter) { #ifdef HAVE_LIBUNWIND - char buf[100]; + char buf[256]; + struct fbuf fb = { .buf = buf, .len = sizeof(buf) }; unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, off, sp; @@ -564,28 +457,27 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); - if (unw_is_signal_frame(&cursor)) - dprintf(2, " ---- signal ----\n"); - - if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) { - snprintf(name, sizeof(name), "%s+%#lx", - buf, (long)off); - } - dprintf(2, "%-30s %16lx %16lx", name, (long)ip, (long)sp); - if (dladdr((void *)ip, &dlinfo)) { - dprintf(2, " %s (mapped at %p)", - dlinfo.dli_fname, dlinfo.dli_fbase); - } - dprintf(2, "\n"); + if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) + snprintfrr(name, sizeof(name), "%s+%#lx", + buf, (long)off); + fb.pos = buf; + if (unw_is_signal_frame(&cursor)) + bprintfrr(&fb, " ---- signal ----\n"); + bprintfrr(&fb, "%-30s %16lx %16lx", name, (long)ip, (long)sp); + if (dladdr((void *)ip, &dlinfo)) + bprintfrr(&fb, " %s (mapped at %p)", + dlinfo.dli_fname, dlinfo.dli_fbase); + bprintfrr(&fb, "\n"); + crash_write(&fb, NULL); } #elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK) static const char pclabel[] = "Program counter: "; void *array[64]; int size; - char buf[100]; - char *s, **bt = NULL; -#define LOC s,buf+sizeof(buf)-s + char buf[128]; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; + char **bt = NULL; #ifdef HAVE_GLIBC_BACKTRACE size = backtrace(array, array_size(array)); @@ -598,7 +490,7 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) write_wrapper(FD, pclabel, sizeof(pclabel) - 1); \ backtrace_symbols_fd(&program_counter, 1, FD); \ } \ - write_wrapper(FD, buf, s - buf); \ + write_wrapper(FD, fb.buf, fb.pos - fb.buf); \ backtrace_symbols_fd(array, size, FD); \ } #elif defined(HAVE_PRINTSTACK) @@ -608,15 +500,12 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) { \ if (program_counter) \ write_wrapper((FD), pclabel, sizeof(pclabel) - 1); \ - write_wrapper((FD), buf, s - buf); \ + write_wrapper((FD), fb.buf, fb.pos - fb.buf); \ printstack((FD)); \ } #endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */ - s = buf; - s = str_append(LOC, "Backtrace for "); - s = num_append(LOC, size); - s = str_append(LOC, " stack frames:\n"); + bprintfrr(&fb, "Backtrace for %d stack frames:\n", size); if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) DUMP(logfile_fd) @@ -626,12 +515,12 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) DUMP(STDOUT_FILENO) /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; + fb.pos--; if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb.buf, fb.pos - fb.buf); if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(priority | zlog_default->facility, buf, - s - buf); + syslog_sigsafe(priority | zlog_default->facility, + fb.buf, fb.pos - fb.buf); { int i; #ifdef HAVE_GLIBC_BACKTRACE @@ -639,34 +528,26 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) #endif /* Just print the function addresses. */ for (i = 0; i < size; i++) { - s = buf; + fb.pos = buf; if (bt) - s = str_append(LOC, bt[i]); - else { - s = str_append(LOC, "[bt "); - s = num_append(LOC, i); - s = str_append(LOC, "] 0x"); - s = hex_append( - LOC, (unsigned long)(array[i])); - } - *s = '\0'; + bprintfrr(&fb, "%s", bt[i]); + else + bprintfrr(&fb, "[bt %d] 0x%tx", i, + (ptrdiff_t)(array[i])); if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb.buf, fb.pos - fb.buf); if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe( - priority - | zlog_default - ->facility, - buf, s - buf); + syslog_sigsafe(priority + | zlog_default->facility, + fb.buf, fb.pos - fb.buf); } if (bt) free(bt); } } #undef DUMP -#undef LOC #endif /* HAVE_STRACK_TRACE */ } @@ -128,12 +128,8 @@ const char *lookup_msg(const struct message *mz, int kz, const char *nf); extern const char *safe_strerror(int errnum); /* To be called when a fatal signal is caught. */ -extern void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ); +extern void zlog_signal(int signo, const char *action, void *siginfo, + void *program_counter); /* Log a backtrace. */ extern void zlog_backtrace(int priority); diff --git a/lib/module.h b/lib/module.h index c5f96db85..79cf52d75 100644 --- a/lib/module.h +++ b/lib/module.h @@ -24,16 +24,6 @@ extern "C" { #endif -#if !defined(__GNUC__) -#error module code needs GCC visibility extensions -#elif __GNUC__ < 4 -#error module code needs GCC visibility extensions -#else -# define DSO_PUBLIC __attribute__ ((visibility ("default"))) -# define DSO_SELF __attribute__ ((visibility ("protected"))) -# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) -#endif - struct frrmod_runtime; struct frrmod_info { diff --git a/lib/nexthop.c b/lib/nexthop.c index 57a2f1daa..4cea14955 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -32,6 +32,7 @@ #include "nexthop.h" #include "mpls.h" #include "jhash.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") @@ -423,3 +424,87 @@ uint32_t nexthop_hash(const struct nexthop *nexthop) } return key; } + +/* + * nexthop printing variants: + * %pNHvv + * via 1.2.3.4 + * via 1.2.3.4, eth0 + * is directly connected, eth0 + * unreachable (blackhole) + * %pNHv + * 1.2.3.4 + * 1.2.3.4, via eth0 + * directly connected, eth0 + * unreachable (blackhole) + * %pNHs + * nexthop2str() + */ +printfrr_ext_autoreg_p("NH", printfrr_nh) +static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct nexthop *nexthop = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + bool do_ifi = false; + const char *s, *v_is = "", *v_via = "", *v_viaif = "via "; + ssize_t ret = 3; + + switch (fmt[2]) { + case 'v': + if (fmt[3] == 'v') { + v_is = "is "; + v_via = "via "; + v_viaif = ""; + ret++; + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4); + do_ifi = true; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6); + do_ifi = true; + break; + case NEXTHOP_TYPE_IFINDEX: + bprintfrr(&fb, "%sdirectly connected, %s", v_is, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + s = " (ICMP unreachable)"; + break; + case BLACKHOLE_ADMINPROHIB: + s = " (ICMP admin-prohibited)"; + break; + case BLACKHOLE_NULL: + s = " (blackhole)"; + break; + default: + s = ""; + break; + } + bprintfrr(&fb, "unreachable%s", s); + break; + default: + break; + } + if (do_ifi && nexthop->ifindex) + bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + + *fb.pos = '\0'; + return ret; + case 's': + nexthop2str(nexthop, buf, bsz); + return 3; + } + return 0; +} diff --git a/lib/prefix.c b/lib/prefix.c index 42d202ddb..134d9cf90 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -28,6 +28,7 @@ #include "log.h" #include "jhash.h" #include "lib_errors.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix") DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") @@ -1626,3 +1627,48 @@ char *esi_to_str(const esi_t *esi, char *buf, int size) esi->val[9]); return ptr; } + +printfrr_ext_autoreg_p("I4", printfrr_i4) +static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("I6", printfrr_i6) +static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET6, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("FX", printfrr_pfx) +static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + prefix2str(ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("SG4", printfrr_psg) +static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct prefix_sg *sg = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + + if (sg->src.s_addr == INADDR_ANY) + bprintfrr(&fb, "(*,"); + else + bprintfrr(&fb, "(%pI4,", &sg->src); + + if (sg->grp.s_addr == INADDR_ANY) + bprintfrr(&fb, "*)"); + else + bprintfrr(&fb, "%pI4)", &sg->grp); + + fb.pos[0] = '\0'; + return 3; +} diff --git a/lib/printf/README b/lib/printf/README new file mode 100644 index 000000000..ac283642d --- /dev/null +++ b/lib/printf/README @@ -0,0 +1,9 @@ +This is the printf implementation from FreeBSD. It was imported on 2019-05-12, +from SVN revision 347514 (but the code hadn't been touched for 2 years before +that.) + +Please don't reindent or otherwise mangle the files to make importing fixes +easy (not that there are significant changes likely to happen...) + +The changes to this code are published under FreeBSD's license as listed in the +file headers. If you change license, please make that as obvious as possible. diff --git a/lib/printf/glue.c b/lib/printf/glue.c new file mode 100644 index 000000000..1b760dc2d --- /dev/null +++ b/lib/printf/glue.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <string.h> +#include <wchar.h> + +#include "printfrr.h" +#include "printflocal.h" + +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(out, fmt, ap); + va_end(ap); + return ret; +} + +ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + + ret = vbprintfrr(fb, fmt, ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(fb, fmt, ap); + va_end(ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + if (!out || !outsz) + return vbprintfrr(NULL, fmt, ap); + + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t ret; + size_t pos; + + pos = strnlen(out, outsz); + fbb.pos += pos; + + ret = vbprintfrr(&fbb, fmt, ap); + fbb.pos[0] = '\0'; + return ret >= 0 ? ret + (ssize_t)pos : ret; +} + +ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vcsnprintfrr(out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + va_list ap) +{ + struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t len; + va_list ap2; + char *ret = out; + + va_copy(ap2, ap); + len = vbprintfrr(&fb, fmt, ap); + if (len < 0) + /* error = malformed format string => try something useful */ + return qstrdup(mt, fmt); + + if ((size_t)len >= outsz - 1) { + ret = qmalloc(mt, len + 1); + fb.buf = fb.pos = ret; + fb.len = len; + + vbprintfrr(&fb, fmt, ap2); + } + ret[len] = '\0'; + return ret; +} + +char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap) +{ + char buf[256]; + char *ret; + + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +char *asprintfrr(struct memtype *mt, const char *fmt, ...) +{ + char buf[256]; + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +/* Q: WTF? + * A: since printf should be reasonably fast (think debugging logs), the idea + * here is to keep things close by each other in a cacheline. That's why + * ext_quick just has the first 2 characters of an extension, and we do a + * nice linear continuous sweep. Only if we find something, we go do more + * expensive things. + * + * Q: doesn't this need a mutex/lock? + * A: theoretically, yes, but that's quite expensive and I rather elide that + * necessity by putting down some usage rules. Just call this at startup + * while singlethreaded and all is fine. Ideally, just use constructors + * (and make sure dlopen() doesn't mess things up...) + */ +#define MAXEXT 64 + +struct ext_quick { + char fmt[2]; +}; + +static uint8_t ext_offsets[26] __attribute__((aligned(32))); +static struct ext_quick entries[MAXEXT] __attribute__((aligned(64))); +static const struct printfrr_ext *exts[MAXEXT] __attribute__((aligned(64))); + +void printfrr_ext_reg(const struct printfrr_ext *ext) +{ + uint8_t o; + ptrdiff_t i; + + if (!printfrr_ext_char(ext->match[0])) + return; + + o = ext->match[0] - 'A'; + for (i = ext_offsets[o]; + i < MAXEXT && entries[i].fmt[0] && + memcmp(entries[i].fmt, ext->match, 2) < 0; + i++) + ; + if (i == MAXEXT) + return; + for (o++; o <= 'Z' - 'A'; o++) + ext_offsets[o]++; + + memmove(entries + i + 1, entries + i, + (MAXEXT - i - 1) * sizeof(entries[0])); + memmove(exts + i + 1, exts + i, + (MAXEXT - i - 1) * sizeof(exts[0])); + + memcpy(entries[i].fmt, ext->match, 2); + exts[i] = ext; +} + +ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec, + const void *ptr) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_ptr) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_ptr(buf, sz, fmt, prec, ptr); + } + return 0; +} + +ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec, + uintmax_t num) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_int) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_int(buf, sz, fmt, prec, num); + } + return 0; +} diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c new file mode 100644 index 000000000..399573e6c --- /dev/null +++ b/lib/printf/printf-pos.c @@ -0,0 +1,786 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif + +/* + * This is the code responsible for handling positional arguments + * (%m$ and %m$.n$) for vfprintf() and vfwprintf(). + */ + +#include <sys/types.h> + +#include <limits.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include "printflocal.h" + +#ifdef NL_ARGMAX +#define MAX_POSARG NL_ARGMAX +#else +#define MAX_POSARG 65536 +#endif + +/* + * Type ids for argument type table. + */ +enum typeid { + T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, + T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, + T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET, + T_INT64T, T_UINT64T, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, + TP_CHAR, TP_SCHAR, T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR +}; + +/* An expandable array of types. */ +struct typetable { + enum typeid *table; /* table of types */ + enum typeid stattable[STATIC_ARG_TBL_SIZE]; + u_int tablesize; /* current size of type table */ + u_int tablemax; /* largest used index in table */ + u_int nextarg; /* 1-based argument index */ +}; + +static int __grow_type_table(struct typetable *); +static void build_arg_table (struct typetable *, va_list, union arg **); + +/* + * Initialize a struct typetable. + */ +static inline void +inittypes(struct typetable *types) +{ + u_int n; + + types->table = types->stattable; + types->tablesize = STATIC_ARG_TBL_SIZE; + types->tablemax = 0; + types->nextarg = 1; + for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) + types->table[n] = T_UNUSED; +} + +/* + * struct typetable destructor. + */ +static inline void +freetypes(struct typetable *types) +{ + + if (types->table != types->stattable) + free (types->table); +} + +/* + * Ensure that there is space to add a new argument type to the type table. + * Expand the table if necessary. Returns 0 on success. + */ +static inline int +_ensurespace(struct typetable *types) +{ + + if (types->nextarg >= types->tablesize) { + if (__grow_type_table(types)) + return (-1); + } + if (types->nextarg > types->tablemax) + types->tablemax = types->nextarg; + return (0); +} + +/* + * Add an argument type to the table, expanding if necessary. + * Returns 0 on success. + */ +static inline int +addtype(struct typetable *types, enum typeid type) +{ + + if (_ensurespace(types)) + return (-1); + types->table[types->nextarg++] = type; + return (0); +} + +static inline int +addsarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return (-1); + if (flags & LONGDBL) + types->table[types->nextarg++] = T_INT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_INTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SSIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_PTRDIFFT; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_LONG; + else + types->table[types->nextarg++] = T_INT; + return (0); +} + +static inline int +adduarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return (-1); + if (flags & LONGDBL) + types->table[types->nextarg++] = T_UINT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_UINTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_SIZET; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_U_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_U_LONG; + else + types->table[types->nextarg++] = T_U_INT; + return (0); +} + +/* + * Add * arguments to the type array. + */ +static inline int +addaster(struct typetable *types, char **fmtp) +{ + char *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return (-1); + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return (-1); + } + return (0); +} + +#ifdef WCHAR_SUPPORT +static inline int +addwaster(struct typetable *types, wchar_t **fmtp) +{ + wchar_t *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return (-1); + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return (-1); + } + return (0); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Find all arguments when a positional parameter is encountered. Returns a + * table, indexed by argument number, of pointers to each arguments. The + * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. + * It will be replaces with a malloc-ed one if it overflows. + * Returns 0 on success. On failure, returns nonzero and sets errno. + */ +int +_frr_find_arguments (const char *fmt0, va_list ap, union arg **argtable) +{ + char *fmt; /* format string */ + int ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (char *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} + +#ifdef WCHAR_SUPPORT +/* wchar version of __find_arguments. */ +int +_frr_find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable) +{ + wchar_t *fmt; /* format string */ + wchar_t ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (wchar_t *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Increase the size of the type table. Returns 0 on success. + */ +static int +__grow_type_table(struct typetable *types) +{ + enum typeid *const oldtable = types->table; + const int oldsize = types->tablesize; + enum typeid *newtable; + u_int n, newsize; + + /* Detect overflow */ + if (types->nextarg > MAX_POSARG) + return (-1); + + newsize = oldsize * 2; + if (newsize < types->nextarg + 1) + newsize = types->nextarg + 1; + if (oldsize == STATIC_ARG_TBL_SIZE) { + if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) + return (-1); + bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); + } else { + newtable = realloc(oldtable, newsize * sizeof(enum typeid)); + if (newtable == NULL) + return (-1); + } + for (n = oldsize; n < newsize; n++) + newtable[n] = T_UNUSED; + + types->table = newtable; + types->tablesize = newsize; + + return (0); +} + +/* + * Build the argument table from the completed type table. + * On malloc failure, *argtable is set to NULL. + */ +static void +build_arg_table(struct typetable *types, va_list ap, union arg **argtable) +{ + u_int n; + + if (types->tablemax >= STATIC_ARG_TBL_SIZE) { + *argtable = (union arg *) + malloc (sizeof (union arg) * (types->tablemax + 1)); + if (*argtable == NULL) + return; + } + + (*argtable) [0].intarg = 0; + for (n = 1; n <= types->tablemax; n++) { + switch (types->table[n]) { + case T_UNUSED: /* whoops! */ + (*argtable) [n].intarg = va_arg (ap, int); + break; + case TP_SCHAR: + (*argtable) [n].pschararg = va_arg (ap, signed char *); + break; + case TP_SHORT: + (*argtable) [n].pshortarg = va_arg (ap, short *); + break; + case T_INT: + (*argtable) [n].intarg = va_arg (ap, int); + break; + case T_U_INT: + (*argtable) [n].uintarg = va_arg (ap, unsigned int); + break; + case TP_INT: + (*argtable) [n].pintarg = va_arg (ap, int *); + break; + case T_LONG: + (*argtable) [n].longarg = va_arg (ap, long); + break; + case T_U_LONG: + (*argtable) [n].ulongarg = va_arg (ap, unsigned long); + break; + case TP_LONG: + (*argtable) [n].plongarg = va_arg (ap, long *); + break; + case T_LLONG: + (*argtable) [n].longlongarg = va_arg (ap, long long); + break; + case T_U_LLONG: + (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); + break; + case TP_LLONG: + (*argtable) [n].plonglongarg = va_arg (ap, long long *); + break; + case T_PTRDIFFT: + (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); + break; + case TP_PTRDIFFT: + (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); + break; + case T_SIZET: + (*argtable) [n].sizearg = va_arg (ap, size_t); + break; + case T_SSIZET: + (*argtable) [n].sizearg = va_arg (ap, ssize_t); + break; + case TP_SSIZET: + (*argtable) [n].pssizearg = va_arg (ap, ssize_t *); + break; + case T_INTMAXT: + (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); + break; + case T_UINTMAXT: + (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); + break; + case TP_INTMAXT: + (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); + break; + case T_INT64T: + (*argtable) [n].intmaxarg = va_arg (ap, int64_t); + break; + case T_UINT64T: + (*argtable) [n].uintmaxarg = va_arg (ap, uint64_t); + break; + case T_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].doublearg = va_arg (ap, double); +#endif + break; + case T_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].longdoublearg = va_arg (ap, long double); +#endif + break; + case TP_CHAR: + (*argtable) [n].pchararg = va_arg (ap, char *); + break; + case TP_VOID: + (*argtable) [n].pvoidarg = va_arg (ap, void *); + break; + case T_WINT: + (*argtable) [n].wintarg = va_arg (ap, wint_t); + break; + case TP_WCHAR: + (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); + break; + } + } +} diff --git a/lib/printf/printfcommon.h b/lib/printf/printfcommon.h new file mode 100644 index 000000000..5c45520b4 --- /dev/null +++ b/lib/printf/printfcommon.h @@ -0,0 +1,244 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file defines common routines used by both printf and wprintf. + * You must define CHAR to either char or wchar_t prior to including this. + */ + + +static CHAR *__ujtoa(uintmax_t, CHAR *, int, int, const char *); +static CHAR *__ultoa(u_long, CHAR *, int, int, const char *); + +#define NIOV 8 +struct io_state { + struct fbuf *cb; + size_t avail; +}; + +static inline void +io_init(struct io_state *iop, struct fbuf *cb) +{ + iop->cb = cb; + iop->avail = cb ? cb->len - (cb->pos - cb->buf) : 0; +} + +/* + * WARNING: The buffer passed to io_print() is not copied immediately; it must + * remain valid until io_flush() is called. + */ +static inline int +io_print(struct io_state *iop, const CHAR * __restrict ptr, size_t len) +{ + size_t copylen = len; + + if (!iop->cb) + return 0; + if (iop->avail < copylen) + copylen = iop->avail; + + memcpy(iop->cb->pos, ptr, copylen); + iop->avail -= copylen; + iop->cb->pos += copylen; + return 0; +} + +/* + * Choose PADSIZE to trade efficiency vs. size. If larger printf + * fields occur frequently, increase PADSIZE and make the initialisers + * below longer. + */ +#define PADSIZE 16 /* pad chunk size */ +static const CHAR blanks[PADSIZE] = +{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +static const CHAR zeroes[PADSIZE] = +{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +/* + * Pad with blanks or zeroes. 'with' should point to either the blanks array + * or the zeroes array. + */ +static inline int +io_pad(struct io_state *iop, int howmany, const CHAR * __restrict with) +{ + int n; + + while (howmany > 0) { + n = (howmany >= PADSIZE) ? PADSIZE : howmany; + if (io_print(iop, with, n)) + return (-1); + howmany -= n; + } + return (0); +} + +/* + * Print exactly len characters of the string spanning p to ep, truncating + * or padding with 'with' as necessary. + */ +static inline int +io_printandpad(struct io_state *iop, const CHAR *p, const CHAR *ep, + int len, const CHAR * __restrict with) +{ + int p_len; + + p_len = ep - p; + if (p_len > len) + p_len = len; + if (p_len > 0) { + if (io_print(iop, p, p_len)) + return (-1); + } else { + p_len = 0; + } + return (io_pad(iop, len - p_len, with)); +} + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static CHAR * +__ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + long sval; + + /* + * Handle the three cases separately, in the hope of getting + * better/faster code. + */ + switch (base) { + case 10: + if (val < 10) { /* many numbers are 1 digit */ + *--cp = to_char(val); + return (cp); + } + /* + * On many machines, unsigned arithmetic is harder than + * signed arithmetic, so we do at most one unsigned mod and + * divide; this is sufficient to reduce the range of + * the incoming value to where signed arithmetic works. + */ + if (val > LONG_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: /* oops */ + abort(); + } + return (cp); +} + +/* Identical to __ultoa, but for intmax_t. */ +static CHAR * +__ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + intmax_t sval; + + /* quick test for small values; __ultoa is typically much faster */ + /* (perhaps instead we should run until small, then call __ultoa?) */ + if (val <= ULONG_MAX) + return (__ultoa((u_long)val, endp, base, octzero, xdigs)); + switch (base) { + case 10: + if (val < 10) { + *--cp = to_char(val % 10); + return (cp); + } + if (val > INTMAX_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: + abort(); + } + return (cp); +} diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h new file mode 100644 index 000000000..335e09872 --- /dev/null +++ b/lib/printf/printflocal.h @@ -0,0 +1,105 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "compiler.h" +#include "printfrr.h" + +/* + * Flags used during conversion. + */ +#define ALT 0x001 /* alternate form */ +#define LADJUST 0x004 /* left adjustment */ +#define LONGDBL 0x008 /* long double */ +#define LONGINT 0x010 /* long integer */ +#define LLONGINT 0x020 /* long long integer */ +#define SHORTINT 0x040 /* short integer */ +#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ +#define FPT 0x100 /* Floating point number */ +#define GROUPING 0x200 /* use grouping ("'" flag) */ + /* C99 additional size modifiers: */ +#define SIZET 0x400 /* size_t */ +#define PTRDIFFT 0x800 /* ptrdiff_t */ +#define INTMAXT 0x1000 /* intmax_t */ +#define CHARINT 0x2000 /* print char using int format */ + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) ((unsigned)to_digit(c) <= 9) +#define to_char(n) ((n) + '0') + +/* Size of the static argument table. */ +#define STATIC_ARG_TBL_SIZE 8 + +union arg { + int intarg; + u_int uintarg; + long longarg; + u_long ulongarg; + long long longlongarg; + unsigned long long ulonglongarg; + ptrdiff_t ptrdiffarg; + size_t sizearg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; + void *pvoidarg; + char *pchararg; + signed char *pschararg; + short *pshortarg; + int *pintarg; + long *plongarg; + long long *plonglongarg; + ptrdiff_t *pptrdiffarg; + ssize_t *pssizearg; + intmax_t *pintmaxarg; +#ifndef NO_FLOATING_POINT + double doublearg; + long double longdoublearg; +#endif + wint_t wintarg; + wchar_t *pwchararg; +}; + +/* Handle positional parameters. */ +int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL; +#ifdef WCHAR_SUPPORT +int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL; +#endif + +/* returns number of bytes consumed for extended specifier */ +ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL; +ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL; diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c new file mode 100644 index 000000000..07df6dd8f --- /dev/null +++ b/lib/printf/vfprintf.c @@ -0,0 +1,730 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif + +/* + * Actual printf innards. + * + * This code is large and complicated... + */ + +#include <sys/types.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <stdarg.h> + +#include "printflocal.h" + +#define CHAR char +#include "printfcommon.h" + +#ifdef WCHAR_SUPPORT +/* + * Convert a wide character string argument for the %ls format to a multibyte + * string representation. If not -1, prec specifies the maximum number of + * bytes to output, and also means that we can't assume that the wide char. + * string ends is null-terminated. + */ +static char * +__wcsconv(wchar_t *wcsarg, int prec) +{ + static const mbstate_t initial; + mbstate_t mbs; + char buf[MB_LEN_MAX]; + wchar_t *p; + char *convbuf; + size_t clen, nbytes; + + /* Allocate space for the maximum number of bytes we could output. */ + if (prec < 0) { + p = wcsarg; + mbs = initial; + nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); + if (nbytes == (size_t)-1) + return (NULL); + } else { + /* + * Optimisation: if the output precision is small enough, + * just allocate enough memory for the maximum instead of + * scanning the string. + */ + if (prec < 128) + nbytes = prec; + else { + nbytes = 0; + p = wcsarg; + mbs = initial; + for (;;) { + clen = wcrtomb(buf, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1 || + nbytes + clen > (size_t)prec) + break; + nbytes += clen; + } + } + } + if ((convbuf = malloc(nbytes + 1)) == NULL) + return (NULL); + + /* Fill the output buffer. */ + p = wcsarg; + mbs = initial; + if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, + nbytes, &mbs)) == (size_t)-1) { + free(convbuf); + return (NULL); + } + convbuf[nbytes] = '\0'; + return (convbuf); +} +#endif /* WCHAR_SUPPORT */ + +/* + * The size of the buffer we use as scratch space for integer + * conversions, among other things. We need enough space to + * write a uintmax_t in octal (plus one byte). + */ +#if UINTMAX_MAX <= UINT64_MAX +#define BUF 64 +#else +#error "BUF must be large enough to format a uintmax_t" +#endif + +/* + * Non-MT-safe version + */ +ssize_t +vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) +{ + const char *fmt; /* format string */ + int ch; /* character from fmt */ + int n, n2; /* handy integer (short term usage) */ + const char *cp; /* handy char pointer (short term usage) */ + int flags; /* flags as above */ + int ret; /* return value accumulator */ + int width; /* width from format (%8d), or 0 */ + int prec; /* precision from format; <0 for N/A */ + int saved_errno; + char sign; /* sign prefix (' ', '+', '-', or \0) */ + + u_long ulval = 0; /* integer arguments %[diouxX] */ + uintmax_t ujval = 0; /* %j, %ll, %q, %t, %z integers */ + void *ptrval; /* %p */ + int base; /* base for [diouxX] conversion */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + int realsz; /* field size expanded by dprec, sign, etc */ + int size; /* size of converted field or string */ + int prsize; /* max size of printed field */ + const char *xdigs; /* digits for %[xX] conversion */ + struct io_state io; /* I/O buffering state */ + char buf[BUF]; /* buffer with space for digits of uintmax_t */ + char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ + union arg *argtable; /* args, built due to positional arg */ + union arg statargtable [STATIC_ARG_TBL_SIZE]; + int nextarg; /* 1-based argument index */ + va_list orgap; /* original argument pointer */ + char *convbuf; /* wide to multibyte conversion result */ + + static const char xdigs_lower[16] = "0123456789abcdef"; + static const char xdigs_upper[16] = "0123456789ABCDEF"; + + /* BEWARE, these `goto error' on error. */ +#define PRINT(ptr, len) { \ + if (io_print(&io, (ptr), (len))) \ + goto error; \ +} +#define PAD(howmany, with) { \ + if (io_pad(&io, (howmany), (with))) \ + goto error; \ +} +#define PRINTANDPAD(p, ep, len, with) { \ + if (io_printandpad(&io, (p), (ep), (len), (with))) \ + goto error; \ +} +#define FLUSH() do { } while (0) + + /* + * Get the argument indexed by nextarg. If the argument table is + * built, use it to get the argument. If its not, get the next + * argument (and arguments must be gotten sequentially). + */ +#define GETARG(type) \ + ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ + (nextarg++, va_arg(ap, type))) + + /* + * To extend shorts properly, we need both signed and unsigned + * argument extraction methods. + */ +#define SARG() \ + (flags&LONGINT ? GETARG(long) : \ + flags&SHORTINT ? (long)(short)GETARG(int) : \ + flags&CHARINT ? (long)(signed char)GETARG(int) : \ + (long)GETARG(int)) +#define UARG() \ + (flags&LONGINT ? GETARG(u_long) : \ + flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ + flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ + (u_long)GETARG(u_int)) +#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT|LONGDBL) +#define SJARG() \ + (flags&LONGDBL ? GETARG(int64_t) : \ + flags&INTMAXT ? GETARG(intmax_t) : \ + flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ + flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ + (intmax_t)GETARG(long long)) +#define UJARG() \ + (flags&LONGDBL ? GETARG(uint64_t) : \ + flags&INTMAXT ? GETARG(uintmax_t) : \ + flags&SIZET ? (uintmax_t)GETARG(size_t) : \ + flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ + (uintmax_t)GETARG(unsigned long long)) + + /* + * Get * arguments, including the form *nn$. Preserve the nextarg + * that the argument can be gotten once the type is determined. + */ +#define GETASTER(val) \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') { \ + int hold = nextarg; \ + if (argtable == NULL) { \ + argtable = statargtable; \ + if (_frr_find_arguments (fmt0, orgap, &argtable)) { \ + ret = EOF; \ + goto error; \ + } \ + } \ + nextarg = n2; \ + val = GETARG (int); \ + nextarg = hold; \ + fmt = ++cp; \ + } else { \ + val = GETARG (int); \ + } + + xdigs = xdigs_lower; + saved_errno = errno; + convbuf = NULL; + fmt = (char *)fmt0; + argtable = NULL; + nextarg = 1; + va_copy(orgap, ap); + io_init(&io, cb); + ret = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if ((n = fmt - cp) != 0) { + if ((unsigned)ret + n > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + PRINT(cp, n); + ret += n; + } + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + dprec = 0; + width = 0; + prec = -1; + sign = '\0'; + ox[1] = '\0'; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + /*- + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + /*- + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + GETASTER (width); + if (width >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '\'': + flags |= GROUPING; + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + GETASTER (prec); + goto rflag; + } + prec = 0; + while (is_digit(ch)) { + prec = 10 * prec + to_digit(ch); + ch = *fmt++; + } + goto reswitch; + case '0': + /*- + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + flags |= ZEROPAD; + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + nextarg = n; + if (argtable == NULL) { + argtable = statargtable; + if (_frr_find_arguments (fmt0, orgap, + &argtable)) { + ret = EOF; + goto error; + } + } + goto rflag; + } + width = n; + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + static const mbstate_t initial; + mbstate_t mbs; + size_t mbseqlen; + + mbs = initial; + mbseqlen = wcrtomb(cp = buf, + (wchar_t)GETARG(wint_t), &mbs); + if (mbseqlen == (size_t)-1) { + goto error; + } + size = (int)mbseqlen; + } else +#endif /* WCHAR_SUPPORT */ + { + buf[0] = GETARG(int); + cp = buf; + size = 1; + } + sign = '\0'; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if (flags & INTMAX_SIZE) + ujval = SJARG(); + else + ulval = SARG(); + + if (printfrr_ext_char(fmt[0])) { + n2 = printfrr_exti(buf, sizeof(buf), fmt, prec, + (flags & INTMAX_SIZE) ? ujval + : (uintmax_t)ulval); + if (n2 > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + } + if (flags & INTMAX_SIZE) { + if ((intmax_t)ujval < 0) { + ujval = -ujval; + sign = '-'; + } + } else { + if ((long)ulval < 0) { + ulval = -ulval; + sign = '-'; + } + } + base = 10; + goto number; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (flags & LONGDBL) { + long double arg = GETARG(long double); + char fmt[6] = "%.*L"; + fmt[4] = ch; + fmt[5] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } else { + double arg = GETARG(double); + char fmt[5] = "%.*"; + fmt[3] = ch; + fmt[4] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } + cp = buf; + /* for proper padding */ + if (*cp == '-') { + cp++; + sign = '-'; + } + /* "inf" */ + if (!is_digit(*cp) && *cp != '.') + flags &= ~ZEROPAD; + size = strlen(buf); + break; +#endif + case 'm': + cp = strerror(saved_errno); + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'n': + /* + * Assignment-like behavior is specified if the + * value overflows or is otherwise unrepresentable. + * C99 says to use `signed char' for %hhn conversions. + */ + if (flags & LLONGINT) + *GETARG(long long *) = ret; + else if (flags & SIZET) + *GETARG(ssize_t *) = (ssize_t)ret; + else if (flags & PTRDIFFT) + *GETARG(ptrdiff_t *) = ret; + else if (flags & INTMAXT) + *GETARG(intmax_t *) = ret; + else if (flags & LONGINT) + *GETARG(long *) = ret; + else if (flags & SHORTINT) + *GETARG(short *) = ret; + else if (flags & CHARINT) + *GETARG(signed char *) = ret; + else + *GETARG(int *) = ret; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 8; + goto nosign; + case 'p': + /*- + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + ptrval = GETARG(void *); + if (printfrr_ext_char(fmt[0]) && + (n2 = printfrr_extp(buf, sizeof(buf), + fmt, prec, ptrval)) > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + ujval = (uintmax_t)(uintptr_t)ptrval; + base = 16; + xdigs = xdigs_lower; + flags = flags | INTMAXT; + ox[1] = 'x'; + goto nosign; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + wchar_t *wcp; + + if (convbuf != NULL) + free(convbuf); + if ((wcp = GETARG(wchar_t *)) == NULL) + cp = "(null)"; + else { + convbuf = __wcsconv(wcp, prec); + if (convbuf == NULL) { + goto error; + } + cp = convbuf; + } + } else +#endif + if ((cp = GETARG(char *)) == NULL) + cp = "(null)"; + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 10; + goto nosign; + case 'X': + xdigs = xdigs_upper; + goto hex; + case 'x': + xdigs = xdigs_lower; +hex: + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 16; + /* leading 0x/X only if non-zero */ + if (flags & ALT && + (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) + ox[1] = ch; + + flags &= ~GROUPING; + /* unsigned conversions */ +nosign: sign = '\0'; + /*- + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /*- + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + * + * ``The C Standard is clear enough as is. The call + * printf("%#.0o", 0) should print 0.'' + * -- Defect Report #151 + */ + cp = buf + BUF; + if (flags & INTMAX_SIZE) { + if (ujval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ujtoa(ujval, buf + BUF, base, + flags & ALT, xdigs); + } else { + if (ulval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ultoa(ulval, buf + BUF, base, + flags & ALT, xdigs); + } + size = buf + BUF - cp; + if (size > BUF) /* should never happen */ + abort(); + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + /* pretend it was %c with argument ch */ + buf[0] = ch; + cp = buf; + size = 1; + sign = '\0'; + break; + } + + /* + * All reasonable formats wind up here. At this point, `cp' + * points to a string which (if not flags&LADJUST) should be + * padded out to `width' places. If flags&ZEROPAD, it should + * first be prefixed by any sign or other prefix; otherwise, + * it should be blank padded before the prefix is emitted. + * After any left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print the + * string proper, then emit zeroes required by any leftover + * floating precision; finally, if LADJUST, pad with blanks. + * + * Compute actual size, so we know how much to pad. + * size excludes decimal prec; realsz includes it. + */ + realsz = dprec > size ? dprec : size; + if (sign) + realsz++; + if (ox[1]) + realsz += 2; + + prsize = width > realsz ? width : realsz; + if ((unsigned)ret + prsize > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0) + PAD(width - realsz, blanks); + + /* prefix */ + if (sign) + PRINT(&sign, 1); + + if (ox[1]) { /* ox[1] is either x, X, or \0 */ + ox[0] = '0'; + PRINT(ox, 2); + } + + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + PAD(width - realsz, zeroes); + + /* the string or number proper */ + /* leading zeroes from decimal precision */ + PAD(dprec - size, zeroes); + PRINT(cp, size); + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += prsize; + + FLUSH(); /* copy out the I/O vectors */ + } +done: + FLUSH(); +error: + va_end(orgap); + if (convbuf != NULL) + free(convbuf); + if ((argtable != NULL) && (argtable != statargtable)) + free (argtable); + return (ret); + /* NOTREACHED */ +} + diff --git a/lib/printfrr.h b/lib/printfrr.h new file mode 100644 index 000000000..95dace702 --- /dev/null +++ b/lib/printfrr.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_PRINTFRR_H +#define _FRR_PRINTFRR_H + +#include <stddef.h> +#include <stdarg.h> +#include <stdint.h> + +#include "compiler.h" +#include "memory.h" + +struct fbuf { + char *buf; + char *pos; + size_t len; +}; + +#define at(a, b) \ + __attribute__((format(printf, a, b))) +#define atn(a, b) \ + at(a, b) __attribute__((nonnull(1) _RET_NONNULL)) +#define atm(a, b) \ + atn(a, b) __attribute__((malloc)) + +/* return value is length needed for the full string (excluding \0) in all + * cases. The functions write as much as they can, but continue regardless, + * so the return value is independent of buffer length. Both bprintfrr and + * snprintf also accept NULL as output buffer. + */ + +/* bprintfrr does NOT null terminate! use sparingly (only provided since it's + * the most direct interface) - useful for incrementally building long text + * (call bprintfrr repeatedly with the same buffer) + */ +ssize_t vbprintfrr(struct fbuf *out, const char *fmt, va_list) at(2, 0); +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) at(2, 3); + +/* these do null terminate like their snprintf cousins */ +ssize_t vsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t snprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* c = continue / concatenate (append at the end of the string) + * return value is would-be string length (regardless of buffer length), + * i.e. includes already written chars */ +ssize_t vcsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t csnprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* memory allocations don't fail in FRR, so you always get something here. + * (in case of error, returns a strdup of the format string) */ +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) atm(2, 0); +char *asprintfrr(struct memtype *mt, const char *fmt, ...) atm(2, 3); + +/* try to use provided buffer (presumably from stack), allocate if it's too + * short. Must call XFREE(mt, return value) if return value != out. + */ +char *vasnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, va_list) atn(4, 0); +char *asnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, ...) atn(4, 5); + +#undef at +#undef atm + +/* extension specs must start with a capital letter (this is a restriction + * for both performance's and human understanding's sake.) + * + * Note that the entire thing mostly works because a letter directly following + * a %p print specifier is extremely unlikely to occur (why would you want to + * print "0x12345678HELLO"?) Normally, you'd expect spacing or punctuation + * after a placeholder. That also means that neither of those works well for + * extension purposes, e.g. "%p{foo}" is reasonable to see actually used. + * + * TODO: would be nice to support a "%pF%dF" specifier that consumes 2 + * arguments, e.g. to pass an integer + a list of known values... can be + * done, but a bit tricky. + */ +#define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z') + +struct printfrr_ext { + /* embedded string to minimize cache line pollution */ + char match[8]; + + /* both can be given, if not the code continues searching + * (you can do %pX and %dX in 2 different entries) + * + * return value: number of bytes consumed from the format string, so + * you can consume extra flags (e.g. register for "%pX", consume + * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags + * lowercase letters or numbers. + * + * bsz is a compile-time constant in printf; it's gonna be relatively + * small. This isn't designed to print Shakespeare from a pointer. + * + * prec is the precision specifier (the 999 in "%.999p") -1 means + * none given (value in the format string cannot be negative) + */ + ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec, + const void *); + ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec, + uintmax_t); +}; + +/* no locking - must be called when single threaded (e.g. at startup.) + * this restriction hopefully won't be a huge bother considering normal usage + * scenarios... + */ +void printfrr_ext_reg(const struct printfrr_ext *); + +#define printfrr_ext_autoreg_p(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, \ + const void *); \ + static struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_ptr = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#define printfrr_ext_autoreg_i(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \ + static struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_int = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#endif diff --git a/lib/routemap.c b/lib/routemap.c index 807eec9c5..9336154b1 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1333,6 +1333,13 @@ static route_map_event_t get_route_map_delete_event(route_map_event_t type) */ assert(0); } + + assert(0); + /* + * Return to make c happy but if we get here something has gone + * terribly terribly wrong, so yes this return makes no sense. + */ + return RMAP_EVENT_CALL_ADDED; } /* Add match statement to route map. */ @@ -1524,14 +1531,14 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name, (note, this includes the description for the "NEXT" and "GOTO" frobs now - | Match | No Match | No op - |-----------|--------------|------- - permit | action | cont | cont. - | | default:deny | default:permit - -------------------+----------------------- - | deny | cont | cont. - deny | | default:deny | default:permit - |-----------|--------------|-------- + Match | No Match + | + permit action | cont + | + ------------------+--------------- + | + deny deny | cont + | action) -Apply Set statements, accept route @@ -1565,12 +1572,12 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name, We need to make sure our route-map processing matches the above */ -static enum route_map_match_result_t +static route_map_result_t route_map_apply_match(struct route_map_rule_list *match_list, const struct prefix *prefix, route_map_object_t type, void *object) { - enum route_map_match_result_t ret = RMAP_NOMATCH; + route_map_result_t ret = RMAP_NOMATCH; struct route_map_rule *match; @@ -1602,8 +1609,7 @@ route_map_result_t route_map_apply(struct route_map *map, route_map_object_t type, void *object) { static int recursion = 0; - enum route_map_match_result_t match_ret = RMAP_NOMATCH; - route_map_result_t ret = 0; + int ret = 0; struct route_map_index *index; struct route_map_rule *set; @@ -1623,33 +1629,24 @@ route_map_result_t route_map_apply(struct route_map *map, for (index = map->head; index; index = index->next) { /* Apply this index. */ index->applied++; - match_ret = route_map_apply_match(&index->match_list, prefix, - type, object); + ret = route_map_apply_match(&index->match_list, prefix, type, + object); /* Now we apply the matrix from above */ - if (match_ret == RMAP_NOMATCH || match_ret == RMAP_NOOP) + if (ret == RMAP_NOMATCH) /* 'cont' from matrix - continue to next route-map * sequence */ continue; - else if (match_ret == RMAP_MATCH) { + else if (ret == RMAP_MATCH) { if (index->type == RMAP_PERMIT) /* 'action' */ { - /* Match succeeded, rmap is of type permit */ - ret = RMAP_PERMITMATCH; - /* permit+match must execute sets */ for (set = index->set_list.head; set; set = set->next) - /* - * We dont care abt the return value - * for set cmd. Almost always, - * RMAP_OKAY is returned. Rarely - * do we see RMAP_ERROR - */ - match_ret = (*set->cmd->func_apply)( - set->value, prefix, type, - object); + ret = (*set->cmd->func_apply)( + set->value, prefix, type, + object); /* Call another route-map if available */ if (index->nextrm) { @@ -1700,10 +1697,6 @@ route_map_result_t route_map_apply(struct route_map *map, } } } - - if (match_ret == RMAP_NOOP) - return RMAP_PERMITMATCH; - /* Finally route-map does not match at all. */ return RMAP_DENYMATCH; } diff --git a/lib/routemap.h b/lib/routemap.h index d7acd7f3f..3781d227d 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -34,36 +34,15 @@ DECLARE_MTYPE(ROUTE_MAP_NAME) DECLARE_MTYPE(ROUTE_MAP_RULE) DECLARE_MTYPE(ROUTE_MAP_COMPILED) -/* - * Route-map match or set result "Eg: match evpn vni xx" - * route-map match cmd always returns match/nomatch/noop - * match--> found a match - * nomatch--> didnt find a match - * noop--> invalid - * route-map set retuns okay/error - * okay --> set was successful - * error --> set was not successful - */ -enum route_map_match_result_t { - /* - * route-map match cmd results - */ - RMAP_MATCH, - RMAP_NOMATCH, - RMAP_NOOP, - /* - * route-map set cmd results - */ - RMAP_OKAY, - RMAP_ERROR -}; - /* Route map's type. */ enum route_map_type { RMAP_PERMIT, RMAP_DENY, RMAP_ANY }; typedef enum { + RMAP_MATCH, RMAP_DENYMATCH, - RMAP_PERMITMATCH + RMAP_NOMATCH, + RMAP_ERROR, + RMAP_OKAY } route_map_result_t; typedef enum { @@ -112,10 +91,10 @@ struct route_map_rule_cmd { const char *str; /* Function for value set or match. */ - enum route_map_match_result_t (*func_apply)(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object); + route_map_result_t (*func_apply)(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object); /* Compile argument and return result as void *. */ void *(*func_compile)(const char *); diff --git a/lib/sigevent.c b/lib/sigevent.c index f00ff4921..d02b07422 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -209,12 +209,14 @@ exit_handler(int signo #endif ) { - zlog_signal(signo, "exiting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) +#ifndef SA_SIGINFO + void *siginfo = NULL; + void *pc = NULL; +#else + void *pc = program_counter(context); #endif - ); + + zlog_signal(signo, "exiting...", siginfo, pc); _exit(128 + signo); } @@ -226,6 +228,13 @@ core_handler(int signo #endif ) { +#ifndef SA_SIGINFO + void *siginfo = NULL; + void *pc = NULL; +#else + void *pc = program_counter(context); +#endif + /* make sure we don't hang in here. default for SIGALRM is terminate. * - if we're in backtrace for more than a second, abort. */ struct sigaction sa_default = {.sa_handler = SIG_DFL}; @@ -238,12 +247,8 @@ core_handler(int signo alarm(1); - zlog_signal(signo, "aborting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) -#endif - ); + zlog_signal(signo, "aborting...", siginfo, pc); + /* dump memory stats on core */ log_memstats(stderr, "core_handler"); abort(); diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 80004b41a..ee87d7307 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -28,6 +28,7 @@ #include "memory.h" #include "prefix.h" #include "table.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node") @@ -264,7 +265,8 @@ struct route_node *srcdest_rnode_lookup(struct route_table *table, return srn; } -void srcdest_rnode_prefixes(struct route_node *rn, const struct prefix **p, +void srcdest_rnode_prefixes(const struct route_node *rn, + const struct prefix **p, const struct prefix **src_p) { if (rnode_is_srcnode(rn)) { @@ -296,10 +298,22 @@ const char *srcdest2str(const struct prefix *dst_p, return str; } -const char *srcdest_rnode2str(struct route_node *rn, char *str, int size) +const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) { const struct prefix *dst_p, *src_p; srcdest_rnode_prefixes(rn, &dst_p, &src_p); return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size); } + +printfrr_ext_autoreg_p("RN", printfrr_rn) +static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct route_node *rn = ptr; + const struct prefix *dst_p, *src_p; + + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz); + return 2; +} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index 8845931de..90418944c 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -65,22 +65,22 @@ extern struct route_node *srcdest_rnode_get(struct route_table *table, extern struct route_node *srcdest_rnode_lookup(struct route_table *table, union prefixconstptr dst_pu, const struct prefix_ipv6 *src_p); -extern void srcdest_rnode_prefixes(struct route_node *rn, +extern void srcdest_rnode_prefixes(const struct route_node *rn, const struct prefix **p, const struct prefix **src_p); extern const char *srcdest2str(const struct prefix *dst_p, const struct prefix_ipv6 *src_p, char *str, int size); -extern const char *srcdest_rnode2str(struct route_node *rn, char *str, +extern const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size); extern struct route_node *srcdest_route_next(struct route_node *rn); -static inline int rnode_is_dstnode(struct route_node *rn) +static inline int rnode_is_dstnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_dstnode_delegate; } -static inline int rnode_is_srcnode(struct route_node *rn) +static inline int rnode_is_srcnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_srcnode_delegate; } diff --git a/lib/subdir.am b/lib/subdir.am index 4897f5e8e..50ff1feec 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -94,6 +94,9 @@ lib_libfrr_la_SOURCES = \ lib/yang_wrappers.c \ lib/zclient.c \ lib/logicalrouter.c \ + lib/printf/printf-pos.c \ + lib/printf/vfprintf.c \ + lib/printf/glue.c \ # end nodist_lib_libfrr_la_SOURCES = \ @@ -131,6 +134,8 @@ lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) lib/nexthop_group.lo: lib/nexthop_group_clippy.c lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) lib/northbound_cli.lo: lib/northbound_cli_clippy.c +lib/vty_clippy.c: $(CLIPPY_DEPS) +lib/vty.lo: lib/vty_clippy.c pkginclude_HEADERS += \ lib/agg_table.h \ @@ -190,6 +195,7 @@ pkginclude_HEADERS += \ lib/plist.h \ lib/pqueue.h \ lib/prefix.h \ + lib/printfrr.h \ lib/privs.h \ lib/ptm_lib.h \ lib/pw.h \ @@ -242,6 +248,8 @@ noinst_HEADERS += \ lib/clippy.h \ lib/log_int.h \ lib/plist_int.h \ + lib/printf/printfcommon.h \ + lib/printf/printflocal.h \ #end # General note about module and module helper library (libfrrsnmp, libfrrzmq) @@ -42,10 +42,15 @@ #include "frrstr.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "printfrr.h" #include <arpa/telnet.h> #include <termios.h> +#ifndef VTYSH_EXTRACT_PL +#include "lib/vty_clippy.c" +#endif + DEFINE_MTYPE_STATIC(LIB, VTY, "VTY") DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer") DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history") @@ -92,7 +97,8 @@ static int no_password_check = 0; /* Integrated configuration file path */ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; -static int do_log_commands = 0; +static bool do_log_commands; +static bool do_log_commands_perm; void vty_frame(struct vty *vty, const char *format, ...) { @@ -143,8 +149,7 @@ bool vty_set_include(struct vty *vty, const char *regexp) int vty_out(struct vty *vty, const char *format, ...) { va_list args; - int len = 0; - int size = 1024; + ssize_t len; char buf[1024]; char *p = NULL; char *filtered; @@ -154,35 +159,11 @@ int vty_out(struct vty *vty, const char *format, ...) vty_out(vty, "%s", vty->frame); } - /* Try to write to initial buffer. */ va_start(args, format); - len = vsnprintf(buf, sizeof(buf), format, args); + p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args); va_end(args); - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) { - while (1) { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size); - if (!p) - return -1; - - va_start(args, format); - len = vsnprintf(p, size, format, args); - va_end(args); - - if (len > -1 && len < size) - break; - } - } - - /* When initial buffer is enough to store all output. */ - if (!p) - p = buf; + len = strlen(p); /* filter buffer */ if (vty->filter) { @@ -269,8 +250,8 @@ done: } static int vty_log_out(struct vty *vty, const char *level, - const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) + const char *proto_str, const char *msg, + struct timestamp_control *ctl) { int ret; int len; @@ -295,7 +276,7 @@ static int vty_log_out(struct vty *vty, const char *level, if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) return -1; - if (((ret = vsnprintf(buf + len, sizeof(buf) - len, format, va)) < 0) + if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0) || ((size_t)((len += ret) + 2) > sizeof(buf))) return -1; @@ -2547,8 +2528,8 @@ tmp_free_and_out: } /* Small utility function which output log to the VTY. */ -void vty_log(const char *level, const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) +void vty_log(const char *level, const char *proto_str, const char *msg, + struct timestamp_control *ctl) { unsigned int i; struct vty *vty; @@ -2558,13 +2539,8 @@ void vty_log(const char *level, const char *proto_str, const char *format, for (i = 0; i < vector_active(vtyvec); i++) if ((vty = vector_slot(vtyvec, i)) != NULL) - if (vty->monitor) { - va_list ac; - va_copy(ac, va); - vty_log_out(vty, level, proto_str, format, ctl, - ac); - va_end(ac); - } + if (vty->monitor) + vty_log_out(vty, level, proto_str, msg, ctl); } /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2975,13 +2951,24 @@ DEFUN_NOSH (show_history, } /* vty login. */ -DEFUN (log_commands, +DEFPY (log_commands, log_commands_cmd, - "log commands", + "[no] log commands", + NO_STR "Logging control\n" - "Log all commands (can't be unset without restart)\n") + "Log all commands\n") { - do_log_commands = 1; + if (no) { + if (do_log_commands_perm) { + vty_out(vty, + "Daemon started with permanent logging turned on for commands, ignoring\n"); + return CMD_WARNING; + } + + do_log_commands = false; + } else + do_log_commands = true; + return CMD_SUCCESS; } @@ -3101,7 +3088,7 @@ void vty_init_vtysh(void) } /* Install vty's own commands like `who' command. */ -void vty_init(struct thread_master *master_thread) +void vty_init(struct thread_master *master_thread, bool do_command_logging) { /* For further configuration read, preserve current directory. */ vty_save_cwd(); @@ -3125,6 +3112,12 @@ void vty_init(struct thread_master *master_thread) install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); install_element(CONFIG_NODE, &show_history_cmd); install_element(CONFIG_NODE, &log_commands_cmd); + + if (do_command_logging) { + do_log_commands = true; + do_log_commands_perm = true; + } + install_element(ENABLE_NODE, &terminal_monitor_cmd); install_element(ENABLE_NODE, &terminal_no_monitor_cmd); install_element(ENABLE_NODE, &no_terminal_monitor_cmd); @@ -290,7 +290,7 @@ struct vty_arg { #endif /* Prototypes. */ -extern void vty_init(struct thread_master *); +extern void vty_init(struct thread_master *, bool do_command_logging); extern void vty_init_vtysh(void); extern void vty_terminate(void); extern void vty_reset(void); @@ -313,8 +313,8 @@ extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); extern char *vty_get_cwd(void); -extern void vty_log(const char *level, const char *proto, const char *fmt, - struct timestamp_control *, va_list); +extern void vty_log(const char *level, const char *proto, const char *msg, + struct timestamp_control *); extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); extern void vty_config_exit(struct vty *); diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 3de4609a2..d6105b71d 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -207,7 +207,7 @@ static void parse_sa_message(struct vici_message_ctx *ctx, } break; default: - if (!key) + if (!key || !key->ptr) break; switch (key->ptr[0]) { @@ -550,7 +550,7 @@ int sock_open_unix(const char *path) memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(addr.sun_path)); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 4fbf70c9d..946bbf8cc 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1018,7 +1018,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, unsigned int nexthop_num, struct in6_addr *nexthop, route_tag_t tag) { - route_map_result_t ret; + int ret; struct ospf6_route troute; struct ospf6_external_info tinfo; struct ospf6_route *route, *match; @@ -1355,7 +1355,7 @@ static void ospf6_redistribute_show_config(struct vty *vty) /* Routemap Functions */ -static enum route_map_match_result_t +static route_map_result_t ospf6_routemap_rule_match_address_prefixlist(void *rule, const struct prefix *prefix, route_map_object_t type, @@ -1395,7 +1395,7 @@ struct route_map_rule_cmd ospf6_routemap_rule_match_address_prefixlist_cmd = { /* `match interface IFNAME' */ /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t +static route_map_result_t ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1433,9 +1433,10 @@ struct route_map_rule_cmd ospf6_routemap_rule_match_interface_cmd = { ospf6_routemap_rule_match_interface_free}; /* Match function for matching route tags */ -static enum route_map_match_result_t -ospf6_routemap_rule_match_tag(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t ospf6_routemap_rule_match_tag(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { route_tag_t *tag = rule; struct ospf6_route *route = object; @@ -1452,7 +1453,7 @@ static struct route_map_rule_cmd ospf6_routemap_rule_match_tag_cmd = { route_map_rule_tag_free, }; -static enum route_map_match_result_t +static route_map_result_t ospf6_routemap_rule_set_metric_type(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1488,7 +1489,7 @@ struct route_map_rule_cmd ospf6_routemap_rule_set_metric_type_cmd = { ospf6_routemap_rule_set_metric_type_free, }; -static enum route_map_match_result_t +static route_map_result_t ospf6_routemap_rule_set_metric(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1523,7 +1524,7 @@ struct route_map_rule_cmd ospf6_routemap_rule_set_metric_cmd = { ospf6_routemap_rule_set_metric_free, }; -static enum route_map_match_result_t +static route_map_result_t ospf6_routemap_rule_set_forwarding(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1561,9 +1562,10 @@ struct route_map_rule_cmd ospf6_routemap_rule_set_forwarding_cmd = { ospf6_routemap_rule_set_forwarding_free, }; -static enum route_map_match_result_t -ospf6_routemap_rule_set_tag(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t ospf6_routemap_rule_set_tag(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { route_tag_t *tag = rule; struct ospf6_route *route = object; diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index d725837fe..ab2d5ae58 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -126,9 +126,10 @@ static void ospf_route_map_event(const char *name) /* `match ip netxthop ' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_nexthop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_nexthop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; struct external_info *ei = object; @@ -170,7 +171,7 @@ struct route_map_rule_cmd route_match_ip_nexthop_cmd = { /* `match ip next-hop prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -212,9 +213,10 @@ struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; /* struct prefix_ipv4 match; */ @@ -250,7 +252,7 @@ struct route_map_rule_cmd route_match_ip_address_cmd = { route_match_ip_address_free}; /* `match ip address prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -286,9 +288,10 @@ struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { /* `match interface IFNAME' */ /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_interface(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_interface(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct interface *ifp; struct external_info *ei; @@ -324,9 +327,9 @@ struct route_map_rule_cmd route_match_interface_cmd = { route_match_interface_free}; /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_tag(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct external_info *ei; @@ -355,9 +358,10 @@ struct ospf_metric { /* `set metric METRIC' */ /* Set metric to attribute. */ -static enum route_map_match_result_t -route_set_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct ospf_metric *metric; struct external_info *ei; @@ -435,9 +439,10 @@ struct route_map_rule_cmd route_set_metric_cmd = { /* `set metric-type TYPE' */ /* Set metric-type to attribute. */ -static enum route_map_match_result_t -route_set_metric_type(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric_type(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint32_t *metric_type; struct external_info *ei; @@ -484,9 +489,8 @@ struct route_map_rule_cmd route_set_metric_type_cmd = { route_set_metric_type_free, }; -static enum route_map_match_result_t -route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type, - void *object) +static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct external_info *ei; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 951402f47..c178e367d 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -943,7 +943,7 @@ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, /* apply route-map if needed */ red = ospf_redist_lookup(ospf, type, instance); if (red && ROUTEMAP_NAME(red)) { - route_map_result_t ret; + int ret; ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p, RMAP_OSPF, ei); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 2b97dd382..778819145 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3082,7 +3082,7 @@ static void pim_show_group_rp_mappings_info(struct pim_instance *pim, json_object *json_row = NULL; if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) - strncpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); else pim_inet4_dump("<bsr?>", pim->global_scope.current_bsr, bsr_str, @@ -3650,7 +3650,7 @@ static void pim_show_bsr(struct pim_instance *pim, vty_out(vty, "PIMv2 Bootstrap information\n"); if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) { - strncpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); pim_time_uptime(uptime, sizeof(uptime), pim->global_scope.current_bsr_first_ts); pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen), @@ -3669,16 +3669,16 @@ static void pim_show_bsr(struct pim_instance *pim, switch (pim->global_scope.state) { case NO_INFO: - strncpy(bsr_state, "NO_INFO", sizeof(bsr_state)); + strlcpy(bsr_state, "NO_INFO", sizeof(bsr_state)); break; case ACCEPT_ANY: - strncpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state)); + strlcpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state)); break; case ACCEPT_PREFERRED: - strncpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state)); + strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state)); break; default: - strncpy(bsr_state, "", sizeof(bsr_state)); + strlcpy(bsr_state, "", sizeof(bsr_state)); } if (uj) { @@ -3806,48 +3806,6 @@ DEFUN (clear_ip_pim_statistics, return CMD_SUCCESS; } -static void mroute_add_all(struct pim_instance *pim) -{ - struct listnode *node; - struct channel_oil *c_oil; - - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); - } - } -} - -static void mroute_del_all(struct pim_instance *pim) -{ - struct listnode *node; - struct channel_oil *c_oil; - - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); - } - } -} - static void clear_mroute(struct pim_instance *pim) { struct pim_upstream *up; @@ -5658,11 +5616,54 @@ DEFUN (show_ip_mroute_vrf_all, return CMD_SUCCESS; } +DEFUN (clear_ip_mroute_count, + clear_ip_mroute_count_cmd, + "clear ip mroute [vrf NAME] count", + CLEAR_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Route and packet count data\n") +{ + int idx = 2; + struct listnode *node; + struct channel_oil *c_oil; + struct static_route *sr; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct pim_instance *pim; + + if (!vrf) + return CMD_WARNING; + + pim = vrf->info; + for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + if (!c_oil->installed) + continue; + + pim_mroute_update_counters(c_oil); + c_oil->cc.origpktcnt = c_oil->cc.pktcnt; + c_oil->cc.origbytecnt = c_oil->cc.bytecnt; + c_oil->cc.origwrong_if = c_oil->cc.wrong_if; + } + + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { + if (!sr->c_oil.installed) + continue; + + pim_mroute_update_counters(&sr->c_oil); + + sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt; + sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt; + sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if; + } + return CMD_SUCCESS; +} + static void show_mroute_count(struct pim_instance *pim, struct vty *vty) { struct listnode *node; struct channel_oil *c_oil; - struct static_route *s_route; + struct static_route *sr; vty_out(vty, "\n"); @@ -5686,28 +5687,30 @@ static void show_mroute_count(struct pim_instance *pim, struct vty *vty) vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", source_str, group_str, c_oil->cc.lastused / 100, - c_oil->cc.pktcnt, c_oil->cc.bytecnt, - c_oil->cc.wrong_if); + c_oil->cc.pktcnt - c_oil->cc.origpktcnt, + c_oil->cc.bytecnt - c_oil->cc.origbytecnt, + c_oil->cc.wrong_if - c_oil->cc.origwrong_if); } - for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) { + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; - if (!s_route->c_oil.installed) + if (!sr->c_oil.installed) continue; - pim_mroute_update_counters(&s_route->c_oil); + pim_mroute_update_counters(&sr->c_oil); - pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, + pim_inet4_dump("<group?>", sr->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, + pim_inet4_dump("<source?>", sr->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", - source_str, group_str, s_route->c_oil.cc.lastused, - s_route->c_oil.cc.pktcnt, s_route->c_oil.cc.bytecnt, - s_route->c_oil.cc.wrong_if); + source_str, group_str, sr->c_oil.cc.lastused, + sr->c_oil.cc.pktcnt - sr->c_oil.cc.origpktcnt, + sr->c_oil.cc.bytecnt - sr->c_oil.cc.origbytecnt, + sr->c_oil.cc.wrong_if - sr->c_oil.cc.origwrong_if); } } @@ -10351,6 +10354,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd); install_element(VIEW_NODE, &show_ip_pim_statistics_cmd); + install_element(ENABLE_NODE, &clear_ip_mroute_count_cmd); install_element(ENABLE_NODE, &clear_ip_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_mroute_cmd); diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index c5106d01c..57930e341 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -53,10 +53,13 @@ struct channel_counts { unsigned long long lastused; + unsigned long origpktcnt; unsigned long pktcnt; unsigned long oldpktcnt; + unsigned long origbytecnt; unsigned long bytecnt; unsigned long oldbytecnt; + unsigned long origwrong_if; unsigned long wrong_if; unsigned long oldwrong_if; }; diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index 3c1c779ba..3216b8f89 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -42,9 +42,10 @@ struct rip_metric_modifier { /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint32_t *metric; uint32_t check; @@ -94,9 +95,10 @@ struct route_map_rule_cmd route_match_metric_cmd = { /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_interface(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_interface(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct rip_info *rinfo; struct interface *ifp; @@ -141,9 +143,10 @@ struct route_map_rule_cmd route_match_interface_cmd = { /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_next_hop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_next_hop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; struct rip_info *rinfo; @@ -187,7 +190,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = { /* `match ip next-hop prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -232,9 +235,10 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -270,7 +274,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = { /* `match ip address prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -305,9 +309,8 @@ static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_tag(void *rule, const struct prefix *p, route_map_object_t type, - void *object) +static route_map_result_t route_match_tag(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { route_tag_t *tag; struct rip_info *rinfo; @@ -336,9 +339,10 @@ static struct route_map_rule_cmd route_match_tag_cmd = { /* `set metric METRIC' */ /* Set metric to attribute. */ -static enum route_map_match_result_t -route_set_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { if (type == RMAP_RIP) { struct rip_metric_modifier *mod; @@ -434,7 +438,7 @@ static struct route_map_rule_cmd route_set_metric_cmd = { /* `set ip next-hop IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t route_set_ip_nexthop(void *rule, +static route_map_result_t route_set_ip_nexthop(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) @@ -487,9 +491,8 @@ static struct route_map_rule_cmd route_set_ip_nexthop_cmd = { /* `set tag TAG' */ /* Set tag to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type, - void *object) +static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct rip_info *rinfo; diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 5a4087b17..d83f4d279 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -184,7 +184,8 @@ static int ripng_if_down(struct interface *ifp) zlog_debug("turn off %s", ifp->name); /* Leave from multicast group. */ - ripng_multicast_leave(ifp, ripng->sock); + if (ripng) + ripng_multicast_leave(ifp, ripng->sock); ri->running = 0; } diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 671586cfd..0604e272c 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -38,9 +38,10 @@ struct rip_metric_modifier { /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { uint32_t *metric; struct ripng_info *rinfo; @@ -85,9 +86,10 @@ static struct route_map_rule_cmd route_match_metric_cmd = { /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_interface(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_interface(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct ripng_info *rinfo; struct interface *ifp; @@ -127,10 +129,9 @@ static struct route_map_rule_cmd route_match_interface_cmd = { /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t route_match_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static route_map_result_t route_match_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct ripng_info *rinfo; @@ -158,9 +159,10 @@ static struct route_map_rule_cmd route_match_tag_cmd = { /* `set metric METRIC' */ /* Set metric to attribute. */ -static enum route_map_match_result_t -route_set_metric(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_set_metric(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { if (type == RMAP_RIPNG) { struct rip_metric_modifier *mod; @@ -254,9 +256,10 @@ static struct route_map_rule_cmd route_set_metric_cmd = { /* `set ipv6 next-hop local IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t route_set_ipv6_nexthop_local(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { struct in6_addr *address; struct ripng_info *rinfo; @@ -307,9 +310,9 @@ static struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = { /* `set tag TAG' */ /* Set tag to object. ojbect must be pointer to struct attr. */ -static enum route_map_match_result_t -route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type, - void *object) +static route_map_result_t route_set_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct ripng_info *rinfo; diff --git a/tests/.gitignore b/tests/.gitignore index 380172487..7177165e4 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -30,6 +30,7 @@ /lib/test_idalloc /lib/test_memory /lib/test_nexthop_iter +/lib/test_printfrr /lib/test_privs /lib/test_ringbuf /lib/test_segv diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 11db2dabc..b1dcfcf70 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -153,7 +153,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 393b58874..2071ae08c 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -82,7 +82,7 @@ int main(int argc, char **argv) cmd_hostname_set("test"); cmd_domainname_set("test.domain"); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 7cd622854..3180f9f9f 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -411,7 +411,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); cmd_hostname_set("test"); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, modules, array_size(modules)); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c new file mode 100644 index 000000000..c8ef150b0 --- /dev/null +++ b/tests/lib/test_printfrr.c @@ -0,0 +1,148 @@ +/* + * printfrr() unit test + * Copyright (C) 2019 David Lamparter + * + * 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; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" + +#include <math.h> + +#include "lib/printfrr.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +static int errors; + +static void printcmp(const char *fmt, ...) +{ + va_list ap; + char buf[256], bufrr[256], *p; + int cmp; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + cmp = strcmp(buf, bufrr); + + /* OS dependent "+nan" vs. "nan" */ + if (cmp && (p = strstr(bufrr, "+nan"))) { + p[0] = ' '; + if (!strcmp(buf, bufrr)) + cmp = 0; + p[0] = '+'; + } + printf("fmt: \"%s\"\nsys: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, buf, bufrr, cmp ? "ERROR" : "ok"); + + if (cmp) + errors++; +} + +static void printchk(const char *ref, const char *fmt, ...) +{ + va_list ap; + char bufrr[256]; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + if (strcmp(ref, bufrr)) + errors++; +} + +int main(int argc, char **argv) +{ + size_t i; + float flts[] = { + 123.456789, + 23.456789e-30, + 3.456789e+30, + INFINITY, + NAN, + }; + uint64_t ui64 = 0xfeed1278cafef00d; + struct in_addr ip; + char *p; + char buf[256]; + + printcmp("%d %u %d %u", 123, 123, -456, -456); + printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); + + printcmp("%-20s,%20s,%.20s", "test", "test", "test"); + printcmp("%-3s,%3s,%.3s", "test", "test", "test"); + printcmp("%-6.3s,%6.3s,%6.3s", "test", "test", "test"); + printcmp("%*s,%*s,%.*s", -3, "test", 3, "test", 3, "test"); + + for (i = 0; i < array_size(flts); i++) { + printcmp("%-6.3e,%6.3e,%+06.3e", flts[i], flts[i], flts[i]); + printcmp("%-6.3f,%6.3f,%+06.3f", flts[i], flts[i], flts[i]); + printcmp("%-6.3g,%6.3g,%+06.3g", flts[i], flts[i], flts[i]); + printcmp("%-6.3a,%6.3a,%+06.3a", flts[i], flts[i], flts[i]); + } + + printchk("-77385308584349683 18369358765125201933 feed1278cafef00d", + "%Ld %Lu %Lx", ui64, ui64, ui64); + + inet_aton("192.168.1.2", &ip); + printchk("192.168.1.2", "%pI4", &ip); + printchk(" 192.168.1.2", "%20pI4", &ip); + + printcmp("%p", &ip); + + snprintfrr(buf, sizeof(buf), "test%s", "#1"); + csnprintfrr(buf, sizeof(buf), "test%s", "#2"); + assert(strcmp(buf, "test#1test#2") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "test%s", "#3"); + assert(p == buf); + assert(strcmp(buf, "test#3") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, 4, "test%s", "#4"); + assert(p != buf); + assert(strcmp(p, "test#4") == 0); + XFREE(MTYPE_TMP, p); + + p = asprintfrr(MTYPE_TMP, "test%s", "#5"); + assert(strcmp(p, "test#5") == 0); + XFREE(MTYPE_TMP, p); + + struct prefix_sg sg; + sg.src.s_addr = INADDR_ANY; + sg.grp.s_addr = INADDR_ANY; + printchk("(*,*)", "%pSG4", &sg); + + inet_aton("192.168.1.2", &sg.src); + printchk("(192.168.1.2,*)", "%pSG4", &sg); + + inet_aton("224.1.2.3", &sg.grp); + printchk("(192.168.1.2,224.1.2.3)", "%pSG4", &sg); + + sg.src.s_addr = INADDR_ANY; + printchk("(*,224.1.2.3)", "%pSG4", &sg); + + return !!errors; +} diff --git a/tests/lib/test_printfrr.py b/tests/lib/test_printfrr.py new file mode 100644 index 000000000..4fe238618 --- /dev/null +++ b/tests/lib/test_printfrr.py @@ -0,0 +1,6 @@ +import frrtest + +class TestPrintfrr(frrtest.TestMultiOut): + program = './test_printfrr' + +TestPrintfrr.exit_cleanly() diff --git a/tests/subdir.am b/tests/subdir.am index 10a78b98a..1d29a418c 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -54,6 +54,7 @@ check_PROGRAMS = \ tests/lib/test_idalloc \ tests/lib/test_memory \ tests/lib/test_nexthop_iter \ + tests/lib/test_printfrr \ tests/lib/test_privs \ tests/lib/test_ringbuf \ tests/lib/test_srcdest_table \ @@ -230,6 +231,10 @@ tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c +tests_lib_test_printfrr_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_printfrr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_printfrr_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_printfrr_SOURCES = tests/lib/test_printfrr.c tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD) @@ -317,6 +322,7 @@ EXTRA_DIST += \ tests/lib/northbound/test_oper_data.refout \ tests/lib/test_atomlist.py \ tests/lib/test_nexthop_iter.py \ + tests/lib/test_printfrr.py \ tests/lib/test_ringbuf.py \ tests/lib/test_srcdest_table.py \ tests/lib/test_stream.py \ diff --git a/tests/topotests/bgp_ipv6_rtadv/__init__.py b/tests/topotests/bgp_ipv6_rtadv/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/__init__.py diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 000000000..1623b4578 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 101 + bgp router-id 10.254.254.1 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 000000000..8718e4977 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "interfaceIndex": 1, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 000000000..d0378b564 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,39 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 0, + "metric": 0, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "flags": 1, + "active": true, + "afi": "ipv6" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 000000000..f95c3b07a --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 000000000..bf42d2181 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 + bgp router-id 10.254.254.2 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 000000000..fe26937e8 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "interfaceIndex": 1, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r2-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 000000000..d5ad1a2c5 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,23 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 000000000..0131a11be --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot new file mode 100644 index 000000000..da67c29a0 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py new file mode 100644 index 000000000..6cf223af4 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPIPV6RTADVTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 000000000..3c974c767 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 101 vrf r1-cust1 + bgp router-id 10.254.254.1 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 000000000..e32c84b7d --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "bgp", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "internalStatus": 34, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 4, + "interfaceName": "loop1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 000000000..88e8c5cd8 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,44 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "bgp", + "vrfId":3, + "distance": 20, + "metric": 0, + "internalStatus": 2, + "internalFlags": 0, + "nexthops": [ + { + "flags": 1, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + }, + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 000000000..f19c49720 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface loop1 vrf r1-cust1 + ip address 10.254.254.1/32 +! +interface r1-eth0 vrf r1-cust1 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 000000000..39362abd4 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 vrf r2-cust1 + bgp router-id 10.254.254.2 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 000000000..9d7c0e6e4 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 4, + "interfaceName": "loop1", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "bgp", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "internalStatus": 34, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 000000000..230fe3874 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 000000000..c3795ab95 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface loop1 vrf r2-cust1 + ip address 10.254.254.2/32 +! +interface r2-eth0 vrf r2-cust1 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot new file mode 100644 index 000000000..da67c29a0 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py new file mode 100644 index 000000000..6b4df78c6 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVVRFTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPIPV6RTADVVRFTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info('Testing with VRF Lite support') + + cmds = ['ip link add {0}-cust1 type vrf table 1001', + 'ip link add loop1 type dummy', + 'ip link set loop1 master {0}-cust1', + 'ip link set {0}-eth0 master {0}-cust1'] + + for rname, router in router_list.iteritems(): + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + + for router in tgen.routers().values(): + json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route vrf {}-cust1 json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route vrf {}-cust1 json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index e0da20e07..2acb04fb0 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -429,6 +429,33 @@ def ip4_route_zebra(node, vrf_name=None): lines = lines[1:] return '\n'.join(lines) +def ip6_route_zebra(node, vrf_name=None): + """ + Retrieves the output of 'show ipv6 route [vrf vrf_name]', then + canonicalizes it by eliding link-locals. + """ + + if vrf_name == None: + tmp = node.vtysh_cmd('show ipv6 route') + else: + tmp = node.vtysh_cmd('show ipv6 route vrf {0}'.format(vrf_name)) + + # Mask out timestamp + output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp) + + # Mask out the link-local addresses + output = re.sub(r'fe80::[^ ]+,', 'fe80::XXXX:XXXX:XXXX:XXXX,', output) + + lines = output.splitlines() + header_found = False + while lines and (not lines[0].strip() or not header_found): + if '> - selected route' in lines[0]: + header_found = True + lines = lines[1:] + + return '\n'.join(lines) + + def proto_name_to_number(protocol): return { 'bgp': '186', diff --git a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 deleted file mode 100644 index c4b382171..000000000 --- a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r1 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r1-stubnet - ipv6 ospf6 network broadcast -! -interface r1-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.1 - log-adjacency-changes detail - redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref index c961512bd..2db6f620f 100644 --- a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 +O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r1/zebra.conf b/tests/topotests/ospf6-topo1/r1/zebra.conf index de298f40e..dfbcea8d2 100644 --- a/tests/topotests/ospf6-topo1/r1/zebra.conf +++ b/tests/topotests/ospf6-topo1/r1/zebra.conf @@ -2,6 +2,9 @@ hostname r1 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r1-stubnet ipv6 address fc00:1:1:1::1/64 ! diff --git a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 deleted file mode 100644 index bb9958d17..000000000 --- a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r2 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r2-stubnet - ipv6 ospf6 network broadcast -! -interface r2-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.2 - log-adjacency-changes detail - redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref index 014eddbcb..9060b0739 100644 --- a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r2/zebra.conf b/tests/topotests/ospf6-topo1/r2/zebra.conf index d5345fede..f05d1a60f 100644 --- a/tests/topotests/ospf6-topo1/r2/zebra.conf +++ b/tests/topotests/ospf6-topo1/r2/zebra.conf @@ -2,6 +2,9 @@ hostname r2 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r2-stubnet ipv6 address fc00:2:2:2::2/64 ! diff --git a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 deleted file mode 100644 index d2dbc4a41..000000000 --- a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,31 +0,0 @@ -hostname r3 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r3-stubnet - ipv6 ospf6 network broadcast -! -interface r3-sw5 - ipv6 ospf6 network broadcast -! -interface r3-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.3 - log-adjacency-changes detail - redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref index 1ac7cbd6b..9406f41e9 100644 --- a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet -O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 -O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5 -O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, XX:XX:XX +O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r3/zebra.conf b/tests/topotests/ospf6-topo1/r3/zebra.conf index 11f1ff59f..d8051c350 100644 --- a/tests/topotests/ospf6-topo1/r3/zebra.conf +++ b/tests/topotests/ospf6-topo1/r3/zebra.conf @@ -2,6 +2,9 @@ hostname r3 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r3-stubnet ipv6 address fc00:3:3:3::3/64 ! diff --git a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 deleted file mode 100644 index 6f9c30d75..000000000 --- a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r4 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r4-stubnet - ipv6 ospf6 network broadcast -! -interface r4-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.4 - log-adjacency-changes detail - redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref index 698dea6c7..9bf032b5e 100644 --- a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet -O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6 -O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 +O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, XX:XX:XX +O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r4/zebra.conf b/tests/topotests/ospf6-topo1/r4/zebra.conf index 4b0a8a1f9..cada58bd0 100644 --- a/tests/topotests/ospf6-topo1/r4/zebra.conf +++ b/tests/topotests/ospf6-topo1/r4/zebra.conf @@ -2,6 +2,9 @@ hostname r4 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r4-stubnet ipv6 address fc00:4:4:4::4/64 ! diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 5da04b644..b70ae0226 100755 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -76,20 +76,19 @@ import sys import pytest from time import sleep -from mininet.topo import Topo -from mininet.net import Mininet -from mininet.node import Node, OVSSwitch, Host -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import Intf - from functools import partial -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from lib import topotest +from mininet.topo import Topo +# Save the Current Working Directory to find configuration files later. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) -fatal_error = "" +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger ##################################################### @@ -102,34 +101,45 @@ class NetworkTopo(Topo): "OSPFv3 (IPv6) Test Topology 1" def build(self, **_opts): - # - # Define Switches first - # - switch = {} - for i in range(1, 7): - switch[i] = self.addSwitch('SW%s' % i, - dpid=topotest.int2dpid(i), - cls=topotest.LegacySwitch) - # - # Define FRR/Quagga Routers - # - router = {} - for i in range(1, 5): - router[i] = topotest.addRouter(self, 'r%s' % i) + "Build function" + + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router('r{}'.format(routern)) # # Wire up the switches and routers + # Note that we specify the link names so we match the config files # - # Stub nets - for i in range(1, 5): - self.addLink(switch[i], router[i], intfName2='r%s-stubnet' % i) - # Switch 5 - self.addLink(switch[5], router[1], intfName2='r1-sw5') - self.addLink(switch[5], router[2], intfName2='r2-sw5') - self.addLink(switch[5], router[3], intfName2='r3-sw5') - # Switch 6 - self.addLink(switch[6], router[3], intfName2='r3-sw6') - self.addLink(switch[6], router[4], intfName2='r4-sw6') + + # Create a empty network for router 1 + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1'], nodeif='r1-stubnet') + + # Create a empty network for router 2 + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2'], nodeif='r2-stubnet') + + # Create a empty network for router 3 + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r3'], nodeif='r3-stubnet') + + # Create a empty network for router 4 + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['r4'], nodeif='r4-stubnet') + + # Interconnect routers 1, 2, and 3 + switch = tgen.add_switch('s5') + switch.add_link(tgen.gears['r1'], nodeif='r1-sw5') + switch.add_link(tgen.gears['r2'], nodeif='r2-sw5') + switch.add_link(tgen.gears['r3'], nodeif='r3-sw5') + + # Interconnect routers 3 and 4 + switch = tgen.add_switch('s6') + switch.add_link(tgen.gears['r3'], nodeif='r3-sw6') + switch.add_link(tgen.gears['r4'], nodeif='r4-sw6') ##################################################### @@ -138,192 +148,178 @@ class NetworkTopo(Topo): ## ##################################################### -def setup_module(module): - global topo, net +def setup_module(mod): + "Sets up the pytest environment" - print("\n\n** %s: Setup Topology" % module.__name__) - print("******************************************\n") + tgen = Topogen(NetworkTopo, mod.__name__) + tgen.start_topology() - print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + logger.info("** %s: Setup Topology" % mod.__name__) + logger.info("******************************************") - thisDir = os.path.dirname(os.path.realpath(__file__)) - topo = NetworkTopo() + # For debugging after starting net, but before starting FRR, + # uncomment the next line + # tgen.mininet_cli() - net = Mininet(controller=None, topo=topo) - net.start() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, + os.path.join(CWD, '{}/ospf6d.conf'.format(rname)) + ) - # For debugging after starting net, but before starting FRR/Quagga, uncomment the next line - # CLI(net) + # Initialize all routers. + tgen.start_router() - ospf_config = 'ospf6d.conf' - if net['r1'].checkRouterVersion('<', '4.0'): - ospf_config = 'ospf6d.conf-pre-v4' + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() - # Starting Routers - for i in range(1, 5): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ospf6d', '%s/r%s/%s' % (thisDir, i, ospf_config)) - net['r%s' % i].startRouter() - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() -def teardown_module(module): - global net +def test_ospf6_converged(): - print("\n\n** %s: Shutdown Topology" % module.__name__) - print("******************************************\n") + tgen = get_topogen() - # End - Shutdown network - net.stop() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # For debugging, uncomment the next line + #tgen.mininet_cli() -def test_router_running(): - global fatal_error - global net + # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) + logger.info("Waiting for OSPF6 convergence") - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # Set up for regex + pat1 = re.compile('^[0-9]') + pat2 = re.compile('Full') - print("\n\n** Check if FRR/Quagga is running on each Router node") - print("******************************************\n") - sleep(5) + timeout = 60 + while timeout > 0: + logger.info("Timeout in %s: " % timeout), + sys.stdout.flush() - # Make sure that all daemons are running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + # Look for any node not yet converged + for router, rnode in tgen.routers().iteritems(): + resStr = rnode.vtysh_cmd('show ipv6 ospf neigh') - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) + isConverged = False -def test_ospf6_converged(): - global fatal_error - global net + for line in resStr.splitlines(): + res1 = pat1.match(line) + if res1: + isConverged = True + res2 = pat2.search(line) - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + if res2 == None: + isConverged = False + break - # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) - print("\n\n** Verify OSPF6 daemons to converge") - print("******************************************\n") - timeout = 60 - while timeout > 0: - print("Timeout in %s: " % timeout), - sys.stdout.flush() - # Look for any node not yet converged - for i in range(1, 5): - notConverged = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh" 2> /dev/null | grep ^[0-9] | grep -v Full') - if notConverged: - print('Waiting for r%s' %i) + if isConverged == False: + logger.info('Waiting for {}'.format(router)) sys.stdout.flush() break - if notConverged: + + if isConverged: + logger.info('Done') + break + else: sleep(5) timeout -= 5 - else: - print('Done') - print(notConverged) - break - else: + + if timeout == 0: # Bail out with error if a router fails to converge - ospfStatus = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh"') - fatal_error = "OSPFv6 did not converge" - assert False, "OSPFv6 did not converge:\n%s" % ospfStatus + ospfStatus = rnode.vtysh_cmd('show ipv6 ospf neigh') + assert False, "OSPFv6 did not converge:\n{}".format(ospfStatus) - print("OSPFv3 converged.") + logger.info("OSPFv3 converged.") - if timeout < 60: - # Only wait if we actually went through a convergence - print("\nwaiting 15s for routes to populate") - sleep(15) + # For debugging, uncomment the next line + # tgen.mininet_cli() # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + if tgen.routers_have_failure(): + assert tgen.errors == "", tgen.errors -def test_ospfv3_routingTable(): - global fatal_error - global net +def compare_show_ipv6(rname, expected): + """ + Calls 'show ipv6 route' for router `rname` and compare the obtained + result with the expected output. + """ + tgen = get_topogen() - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # Use the vtysh output, with some masking to make comparison easy + current = topotest.ip6_route_zebra(tgen.gears[rname]) - thisDir = os.path.dirname(os.path.realpath(__file__)) + # Use just the 'O'spf lines of the output + linearr = [] + for line in current.splitlines(): + if re.match('^O', line): + linearr.append(line) - # Verify OSPFv3 Routing Table - print("\n\n** Verifying OSPFv3 Routing Table") - print("******************************************\n") - failures = 0 - for i in range(1, 5): - refTableFile = '%s/r%s/show_ipv6_route.ref' % (thisDir, i) - if os.path.isfile(refTableFile): - # Read expected result from file - expected = open(refTableFile).read().rstrip() - # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + current = '\n'.join(linearr) - # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^O"').rstrip() - # Mask out Link-Local mac address portion. They are random... - actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) - # Drop timers on end of line (older Quagga Versions) - actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) - # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + return topotest.difflines(topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output") - # Generate Diff - diff = topotest.get_textdiff(actual, expected, - title1="actual OSPFv3 IPv6 routing table", - title2="expected OSPFv3 IPv6 routing table") +def test_ospfv3_routingTable(): - # Empty string if it matches, otherwise diff contains unified diff - if diff: - sys.stderr.write('r%s failed OSPFv3 (IPv6) Routing Table Check:\n%s\n' % (i, diff)) - failures += 1 - else: - print("r%s ok" % i) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') - assert failures == 0, "OSPFv3 (IPv6) Routing Table verification failed for router r%s:\n%s" % (i, diff) + # For debugging, uncomment the next line + # tgen.mininet_cli() - # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, '{}/show_ipv6_route.ref'.format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial( + compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, '', + count=120, wait=0.5) + assert result, 'OSPFv3 did not converge on {}:\n{}'.format(router, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) def test_linux_ipv6_kernel_routingTable(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() - thisDir = os.path.dirname(os.path.realpath(__file__)) + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') # Verify Linux Kernel Routing Table - print("\n\n** Verifying Linux IPv6 Kernel Routing Table") - print("******************************************\n") + logger.info("Verifying Linux IPv6 Kernel Routing Table") + failures = 0 # Get a list of all current link-local addresses first as they change for # each run and we need to translate them linklocals = [] for i in range(1, 5): - linklocals += net['r%s' % i].get_ipv6_linklocal() + linklocals += tgen.net['r{}'.format(i)].get_ipv6_linklocal() + + # Now compare the routing tables (after substituting link-local addresses) - # Now compare the routing tables (after substituting link-local addresses) for i in range(1, 5): - refTableFile = '%s/r%s/ip_6_address.ref' % (thisDir, i) + refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i)) if os.path.isfile(refTableFile): expected = open(refTableFile).read().rstrip() @@ -331,7 +327,7 @@ def test_linux_ipv6_kernel_routingTable(): expected = ('\n'.join(expected.splitlines())).splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('ip -6 route').rstrip() + actual = tgen.gears['r{}'.format(i)].run('ip -6 route').rstrip() # Mask out Link-Local mac addresses for ll in linklocals: actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) @@ -354,9 +350,9 @@ def test_linux_ipv6_kernel_routingTable(): actual = '\n'.join(filtered_lines).splitlines(1) # Print Actual table - # print("Router r%s table" % i) + # logger.info("Router r%s table" % i) # for line in actual: - # print(line.rstrip()) + # logger.info(line.rstrip()) # Generate Diff diff = topotest.get_textdiff(actual, expected, @@ -368,64 +364,60 @@ def test_linux_ipv6_kernel_routingTable(): sys.stderr.write('r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n' % (i, diff)) failures += 1 else: - print("r%s ok" % i) + logger.info("r%s ok" % i) assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) - def test_shutdown_check_stderr(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") + logger.info("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") pytest.skip('Skipping test for Stderr output') - thisDir = os.path.dirname(os.path.realpath(__file__)) + net = tgen.net - print("\n\n** Verifying unexpected STDERR output from daemons") - print("******************************************\n") + logger.info("\n\n** Verifying unexpected STDERR output from daemons") + logger.info("******************************************") for i in range(1, 5): net['r%s' % i].stopRouter() log = net['r%s' % i].getStdErr('ospf6d') if log: - print("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) log = net['r%s' % i].getStdErr('zebra') if log: - print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) def test_shutdown_check_memleak(): - global fatal_error - global net - - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + "Run the memory leak test and report results." if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") + logger.info("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)") pytest.skip('Skipping test for memory leaks') - - thisDir = os.path.dirname(os.path.realpath(__file__)) + + tgen = get_topogen() + + net = tgen.net for i in range(1, 5): net['r%s' % i].stopRouter() - net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net['r%s' % i].report_memory_leaks( + os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), + os.path.basename(__file__)) if __name__ == '__main__': - setLogLevel('info') - # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # To suppress tracebacks, either use the following pytest call or + # add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tools/frr.in b/tools/frr.in index d871afa42..50f7ecaa9 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -561,30 +561,7 @@ case "$1" in stop_prio 0 $dmn fi - if [ -z "$dmn" -o "$dmn" = "zebra" ]; then - echo "Removing all routes made by FRR." - # Specific values for each proto can be found - # in /etc/iproute2/rt_protos as well as FRR - # specific ones in /etc/iproute2/rt_protos.d - # Additionally if a new protocol is added - # we need to add it here as well as - # in rt_netlink.h( follow the directions! ) - ip route flush proto 4 - ip route flush proto 11 - ip route flush proto 42 - ip route flush proto 186 - ip route flush proto 187 - ip route flush proto 188 - ip route flush proto 189 - ip route flush proto 190 - ip route flush proto 191 - ip route flush proto 192 - ip route flush proto 193 - ip route flush proto 194 - ip route flush proto 195 - ip route flush proto 196 - ip route flush proto 197 - else + if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr fi diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 2e4510a45..966746018 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -332,6 +332,8 @@ int main(int argc, char **argv, char **env) progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir)); + + frr_init_vtydir(); strlcpy(vtydir, frr_vtydir, sizeof(vtydir)); /* Option handling. */ diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 34f8dabdf..1cc7722f4 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -648,6 +648,7 @@ static void daemon_send_ready(int exitcode) { FILE *fp; static int sent = 0; + char started[1024]; if (sent) return; @@ -669,7 +670,9 @@ static void daemon_send_ready(int exitcode) frr_detach(); - fp = fopen(DAEMON_VTY_DIR "/watchfrr.started", "w"); + snprintf(started, sizeof(started), "%s%s", frr_vtydir, + "watchfrr.started"); + fp = fopen(started, "w"); if (fp) fclose(fp); #if defined HAVE_SYSTEMD diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index fe37a3335..387a3531b 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -340,8 +340,7 @@ static void netlink_write_incoming(const char *buf, const unsigned int size, char fname[MAXPATHLEN]; FILE *f; - snprintf(fname, MAXPATHLEN, "%s/%s_%u", DAEMON_VTY_DIR, "netlink", - counter); + snprintf(fname, MAXPATHLEN, "%s/%s_%u", frr_vtydir, "netlink", counter); frr_elevate_privs(&zserv_privs) { f = fopen(fname, "w"); } diff --git a/zebra/redistribute.c b/zebra/redistribute.c index dfff76664..b13f1170c 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -574,7 +574,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, struct route_entry *newre; struct route_entry *same; struct prefix p; - route_map_result_t ret = RMAP_PERMITMATCH; + route_map_result_t ret = RMAP_MATCH; afi_t afi; afi = family2afi(rn->p.family); @@ -583,7 +583,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, afi, re->type, re->instance, &rn->p, re->ng.nexthop, re->vrf_id, re->tag, rmap_name); - if (ret != RMAP_PERMITMATCH) { + if (ret != RMAP_MATCH) { UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); zebra_del_import_table_entry(rn, re); return 0; diff --git a/zebra/rib.h b/zebra/rib.h index a54e164d9..292f6bc60 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -340,19 +340,6 @@ extern void route_entry_copy_nexthops(struct route_entry *re, extern void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re); -/* RPF lookup behaviour */ -enum multicast_mode { - MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ - MCAST_MRIB_ONLY, /* MRIB only */ - MCAST_URIB_ONLY, /* URIB only */ - MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ - MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ - MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ - /* on equal value, MRIB wins for last 2 */ -}; - -extern void multicast_mode_ipv4_set(enum multicast_mode mode); -extern enum multicast_mode multicast_mode_ipv4_get(void); extern void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id); extern void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id); diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 5088e2e8e..e181b495b 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -42,7 +42,6 @@ #include "zebra/debug.h" #include "zebra/rib.h" #include "zebra/zapi_msg.h" -#include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_router.h" @@ -81,18 +80,25 @@ enum rtadv_event { RTADV_READ }; -static void rtadv_event(struct zebra_ns *, enum rtadv_event, int); +static void rtadv_event(struct zebra_vrf *, enum rtadv_event, int); static int if_join_all_router(int, struct interface *); static int if_leave_all_router(int, struct interface *); -static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) +static int rtadv_get_socket(struct zebra_vrf *zvrf) +{ + if (zvrf->rtadv.sock > 0) + return zvrf->rtadv.sock; + return zrouter.rtadv_sock; +} + +static int rtadv_increment_received(struct zebra_vrf *zvrf, ifindex_t *ifindex) { int ret = -1; struct interface *iface; struct zebra_if *zif; - iface = if_lookup_by_index_per_ns(zns, *ifindex); + iface = if_lookup_by_index(*ifindex, zvrf->vrf->vrf_id); if (iface && iface->info) { zif = iface->info; zif->ra_rcvd++; @@ -101,7 +107,7 @@ static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) return ret; } -static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, +static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, int buflen, struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { @@ -149,7 +155,7 @@ static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, } } - rtadv_increment_received(zns, ifindex); + rtadv_increment_received(zvrf, ifindex); return ret; } @@ -461,19 +467,19 @@ no_more_opts: static int rtadv_timer(struct thread *thread) { - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); struct vrf *vrf; struct interface *ifp; struct zebra_if *zif; int period; - zrouter.rtadv.ra_timer = NULL; - if (zrouter.rtadv.adv_msec_if_count == 0) { + zvrf->rtadv.ra_timer = NULL; + if (zvrf->rtadv.adv_msec_if_count == 0) { period = 1000; /* 1 s */ - rtadv_event(zns, RTADV_TIMER, 1 /* 1 s */); + rtadv_event(zvrf, RTADV_TIMER, 1 /* 1 s */); } else { period = 10; /* 10 ms */ - rtadv_event(zns, RTADV_TIMER_MSEC, 10 /* 10 ms */); + rtadv_event(zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */); } RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) @@ -500,7 +506,7 @@ static int rtadv_timer(struct thread *thread) "Fast RA Rexmit on interface %s", ifp->name); - rtadv_send_packet(zrouter.rtadv.sock, + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); } else { zif->rtadv.AdvIntervalTimer -= period; @@ -514,8 +520,8 @@ static int rtadv_timer(struct thread *thread) zif->rtadv .MaxRtrAdvInterval; rtadv_send_packet( - zrouter.rtadv.sock, - ifp); + rtadv_get_socket(zvrf), + ifp); } } } @@ -527,10 +533,9 @@ static int rtadv_timer(struct thread *thread) static void rtadv_process_solicit(struct interface *ifp) { struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - struct zebra_ns *zns = zvrf->zns; - assert(zns); - rtadv_send_packet(zrouter.rtadv.sock, ifp); + assert(zvrf); + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); } /* @@ -652,7 +657,7 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, static void rtadv_process_packet(uint8_t *buf, unsigned int len, ifindex_t ifindex, int hoplimit, struct sockaddr_in6 *from, - struct zebra_ns *zns) + struct zebra_vrf *zvrf) { struct icmp6_hdr *icmph; struct interface *ifp; @@ -662,7 +667,7 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, inet_ntop(AF_INET6, &from->sin6_addr, addr_str, INET6_ADDRSTRLEN); /* Interface search. */ - ifp = if_lookup_by_index_per_ns(zns, ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (ifp == NULL) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "RA/RS received on unknown IF %u from %s", ifindex, @@ -724,15 +729,15 @@ static int rtadv_read(struct thread *thread) struct sockaddr_in6 from; ifindex_t ifindex = 0; int hoplimit = -1; - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); sock = THREAD_FD(thread); - zrouter.rtadv.ra_read = NULL; + zvrf->rtadv.ra_read = NULL; /* Register myself. */ - rtadv_event(zns, RTADV_READ, sock); + rtadv_event(zvrf, RTADV_READ, sock); - len = rtadv_recv_packet(zns, sock, buf, sizeof(buf), &from, &ifindex, + len = rtadv_recv_packet(zvrf, sock, buf, sizeof(buf), &from, &ifindex, &hoplimit); if (len < 0) { @@ -742,7 +747,7 @@ static int rtadv_read(struct thread *thread) return len; } - rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zns); + rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zvrf); return 0; } @@ -875,29 +880,27 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, { struct zebra_if *zif; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zif = ifp->info; zvrf = vrf_info_lookup(ifp->vrf_id); - zns = zvrf->zns; if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count--; + zvrf->rtadv.adv_if_count--; - if_leave_all_router(zrouter.rtadv.sock, ifp); + if_leave_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 0) - rtadv_event(zns, RTADV_STOP, 0); + if (zvrf->rtadv.adv_if_count == 0) + rtadv_event(zvrf, RTADV_STOP, 0); } } else { if (!zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 1; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count++; + zvrf->rtadv.adv_if_count++; if (zif->rtadv.MaxRtrAdvInterval >= 1000) { /* Enable Fast RA only when RA interval is in @@ -907,11 +910,11 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, RTADV_NUM_FAST_REXMITS; } - if_join_all_router(zrouter.rtadv.sock, ifp); + if_join_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 1) - rtadv_event(zns, RTADV_START, - zrouter.rtadv.sock); + if (zvrf->rtadv.adv_if_count == 1) + rtadv_event(zvrf, RTADV_START, + rtadv_get_socket(zvrf)); } } } @@ -944,7 +947,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) zebra_route_string(client->proto), ra_interval); /* Locate interface and check VRF match. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (!ifp) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "%u: IF %u RA %s client %s - interface unknown", @@ -1051,6 +1054,9 @@ DEFUN (ipv6_nd_ra_interval_msec, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1061,10 +1067,10 @@ DEFUN (ipv6_nd_ra_interval_msec, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; if (interval % 1000) - zrouter.rtadv.adv_msec_if_count++; + zvrf->rtadv.adv_msec_if_count++; SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); zif->rtadv.MaxRtrAdvInterval = interval; @@ -1086,6 +1092,9 @@ DEFUN (ipv6_nd_ra_interval, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1096,7 +1105,7 @@ DEFUN (ipv6_nd_ra_interval, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; /* convert to milliseconds */ interval = interval * 1000; @@ -1122,9 +1131,12 @@ DEFUN (no_ipv6_nd_ra_interval, { VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); @@ -2094,15 +2106,15 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) } -static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) +static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val) { - struct rtadv *rtadv = &zrouter.rtadv; + struct rtadv *rtadv = &zvrf->rtadv; switch (event) { case RTADV_START: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); - thread_add_event(zrouter.master, rtadv_timer, zns, 0, + thread_add_event(zrouter.master, rtadv_timer, zvrf, 0, &rtadv->ra_timer); break; case RTADV_STOP: @@ -2116,15 +2128,15 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) } break; case RTADV_TIMER: - thread_add_timer(zrouter.master, rtadv_timer, zns, val, + thread_add_timer(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_TIMER_MSEC: - thread_add_timer_msec(zrouter.master, rtadv_timer, zns, val, + thread_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_READ: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); break; default: @@ -2133,21 +2145,30 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) return; } -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { - zrouter.rtadv.sock = rtadv_make_socket(zns->ns_id); + if (vrf_is_backend_netns()) { + zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id); + zrouter.rtadv_sock = -1; + } else if (!zrouter.rtadv_sock) { + zvrf->rtadv.sock = -1; + if (!zrouter.rtadv_sock) + zrouter.rtadv_sock = rtadv_make_socket(zvrf->zns->ns_id); + } } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(struct zebra_vrf *zvrf) { - rtadv_event(zns, RTADV_STOP, 0); - if (zrouter.rtadv.sock >= 0) { - close(zrouter.rtadv.sock); - zrouter.rtadv.sock = -1; + rtadv_event(zvrf, RTADV_STOP, 0); + if (zvrf->rtadv.sock >= 0) { + close(zvrf->rtadv.sock); + zvrf->rtadv.sock = -1; + } else if (zrouter.rtadv_sock >= 0) { + close(zrouter.rtadv_sock); + zrouter.rtadv_sock = -1; } - - zrouter.rtadv.adv_if_count = 0; - zrouter.rtadv.adv_msec_if_count = 0; + zvrf->rtadv.adv_if_count = 0; + zvrf->rtadv.adv_msec_if_count = 0; } void rtadv_cmd_init(void) @@ -2243,11 +2264,11 @@ static int if_leave_all_router(int sock, struct interface *ifp) } #else -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { /* Empty.*/; } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(struct zebra_vrf *zvrf) { /* Empty.*/; } diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 53c497fc0..d692ef241 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -135,8 +135,8 @@ typedef enum { RA_SUPPRESS, } ipv6_nd_suppress_ra_status; -extern void rtadv_init(struct zebra_ns *); -extern void rtadv_terminate(struct zebra_ns *); +extern void rtadv_init(struct zebra_vrf *zvrf); +extern void rtadv_terminate(struct zebra_vrf *zvrf); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 49e43f494..61200806b 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2526,7 +2526,7 @@ static void zserv_write_incoming(struct stream *orig, uint16_t command) copy = stream_dup(orig); stream_set_getp(copy, 0); - snprintf(fname, MAXPATHLEN, "%s/%u", DAEMON_VTY_DIR, command); + snprintf(fname, MAXPATHLEN, "%s/%u", frr_vtydir, command); frr_elevate_privs(&zserv_privs) { fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, 0644); diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index caab06d2f..f2a76d1c5 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -338,7 +338,7 @@ static unsigned nexthop_active_check(struct route_node *rn, struct nexthop *nexthop) { struct interface *ifp; - route_map_result_t ret = RMAP_PERMITMATCH; + route_map_result_t ret = RMAP_MATCH; int family; char buf[SRCDEST2STR_BUFFER]; const struct prefix *p, *src_p; diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 0c743d867..db4f9d001 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -27,7 +27,6 @@ #include "lib/prefix.h" #include "lib/memory.h" -#include "rtadv.h" #include "zebra_ns.h" #include "zebra_vrf.h" #include "zebra_memory.h" @@ -122,10 +121,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zns->ns_id = ns_id; -#if defined(HAVE_RTADV) - rtadv_init(zns); -#endif - kernel_init(zns); interface_list(zns); route_read(zns); @@ -142,9 +137,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) { route_table_finish(zns->if_table); -#if defined(HAVE_RTADV) - rtadv_terminate(zns); -#endif kernel_terminate(zns, complete); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 83eb5f422..cc614abac 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -108,10 +108,6 @@ static const struct { /* no entry/default: 150 */ }; -/* RPF lookup behaviour */ -static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; - - static void __attribute__((format(printf, 5, 6))) _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int priority, const char *msgfmt, ...) @@ -404,7 +400,7 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, struct route_node *m_rn = NULL, *u_rn = NULL; union g_addr gaddr = {.ipv4 = addr}; - switch (ipv4_multicast_mode) { + switch (zrouter.ipv4_multicast_mode) { case MCAST_MRIB_ONLY: return rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, rn_out); @@ -456,19 +452,6 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, return re; } -void multicast_mode_ipv4_set(enum multicast_mode mode) -{ - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%s: multicast lookup mode set (%d)", __func__, - mode); - ipv4_multicast_mode = mode; -} - -enum multicast_mode multicast_mode_ipv4_get(void) -{ - return ipv4_multicast_mode; -} - struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index a8389f2ad..a63d01571 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -400,7 +400,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, { int at_least_one = 0; struct nexthop *nexthop; - route_map_result_t ret; + int ret; if (prn && re) { for (nexthop = re->ng.nexthop; nexthop; diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 78804635f..dbfe695a0 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -136,9 +136,9 @@ static int zebra_route_match_delete(struct vty *vty, const char *command, /* 'match tag TAG' * Match function return 1 if match is success else return 0 */ -static enum route_map_match_result_t -route_match_tag(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct nh_rmap_obj *nh_data; @@ -162,9 +162,10 @@ static struct route_map_rule_cmd route_match_tag_cmd = { /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_interface(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_interface(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct nh_rmap_obj *nh_data; char *ifname = rule; @@ -1024,9 +1025,10 @@ DEFPY (show_ipv6_protocol_nht, /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_ip_next_hop(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_next_hop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; struct nh_rmap_obj *nh_data; @@ -1081,7 +1083,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = { /* `match ip next-hop prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1137,9 +1139,10 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { /* Match function should return 1 if match is success else return zero. */ -static enum route_map_match_result_t -route_match_address(afi_t afi, void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_address(afi_t afi, void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct access_list *alist; @@ -1155,16 +1158,19 @@ route_match_address(afi_t afi, void *rule, const struct prefix *prefix, return RMAP_NOMATCH; } -static enum route_map_match_result_t -route_match_ip_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ip_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { return route_match_address(AFI_IP, rule, prefix, type, object); } -static enum route_map_match_result_t -route_match_ipv6_address(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static route_map_result_t route_match_ipv6_address(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) + { return route_match_address(AFI_IP6, rule, prefix, type, object); } @@ -1194,7 +1200,7 @@ static struct route_map_rule_cmd route_match_ipv6_address_cmd = { /* `match ip address prefix-list PREFIX_LIST' */ -static enum route_map_match_result_t +static route_map_result_t route_match_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object, afi_t afi) { @@ -1212,7 +1218,7 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix, return RMAP_NOMATCH; } -static enum route_map_match_result_t +static route_map_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1235,7 +1241,7 @@ static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { route_match_address_prefix_list_compile, route_match_address_prefix_list_free}; -static enum route_map_match_result_t +static route_map_result_t route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1250,7 +1256,7 @@ static struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { /* `match ip address prefix-len PREFIXLEN' */ -static enum route_map_match_result_t +static route_map_result_t route_match_address_prefix_len(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1301,7 +1307,7 @@ static struct route_map_rule_cmd route_match_ipv6_address_prefix_len_cmd = { /* `match ip nexthop prefix-len PREFIXLEN' */ -static enum route_map_match_result_t +static route_map_result_t route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1341,9 +1347,10 @@ static struct route_map_rule_cmd route_match_ip_nexthop_prefix_len_cmd = { /* `match source-protocol PROTOCOL' */ -static enum route_map_match_result_t -route_match_source_protocol(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t route_match_source_protocol(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { uint32_t *rib_type = (uint32_t *)rule; struct nh_rmap_obj *nh_data; @@ -1382,9 +1389,10 @@ static struct route_map_rule_cmd route_match_source_protocol_cmd = { route_match_source_protocol_compile, route_match_source_protocol_free}; /* `source-instance` */ -static enum route_map_match_result_t -route_match_source_instance(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static route_map_result_t route_match_source_instance(void *rule, + const struct prefix *p, + route_map_object_t type, + void *object) { uint8_t *instance = (uint8_t *)rule; struct nh_rmap_obj *nh_data; @@ -1424,9 +1432,8 @@ static struct route_map_rule_cmd route_match_source_instance_cmd = { /* `set src A.B.C.D' */ /* Set src. */ -static enum route_map_match_result_t -route_set_src(void *rule, const struct prefix *prefix, route_map_object_t type, - void *object) +static route_map_result_t route_set_src(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct nh_rmap_obj *nh_data; @@ -1692,7 +1699,7 @@ zebra_route_map_check(int family, int rib_type, uint8_t instance, struct zebra_vrf *zvrf, route_tag_t tag) { struct route_map *rmap = NULL; - route_map_result_t ret = RMAP_PERMITMATCH; + route_map_result_t ret = RMAP_MATCH; struct nh_rmap_obj nh_obj; nh_obj.nexthop = nexthop; @@ -1738,7 +1745,7 @@ zebra_import_table_route_map_check(int family, int re_type, uint8_t instance, const char *rmap_name) { struct route_map *rmap = NULL; - enum route_map_match_result_t ret = RMAP_DENYMATCH; + route_map_result_t ret = RMAP_DENYMATCH; struct nh_rmap_obj nh_obj; nh_obj.nexthop = nexthop; @@ -1764,7 +1771,7 @@ route_map_result_t zebra_nht_route_map_check(afi_t afi, int client_proto, struct nexthop *nexthop) { struct route_map *rmap = NULL; - route_map_result_t ret = RMAP_PERMITMATCH; + route_map_result_t ret = RMAP_MATCH; struct nh_rmap_obj nh_obj; nh_obj.nexthop = nexthop; diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 610d51d3e..4352d688a 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -29,9 +29,12 @@ #include "zebra_pbr.h" #include "zebra_vxlan.h" #include "zebra_mlag.h" +#include "zebra_nhg.h" +#include "debug.h" struct zebra_router zrouter = { .multipath_num = MULTIPATH_NUM, + .ipv4_multicast_mode = MCAST_NO_CONFIG, }; static inline int @@ -187,6 +190,19 @@ uint32_t zebra_router_get_next_sequence(void) memory_order_relaxed); } +void multicast_mode_ipv4_set(enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, + mode); + zrouter.ipv4_multicast_mode = mode; +} + +enum multicast_mode multicast_mode_ipv4_get(void) +{ + return zrouter.ipv4_multicast_mode; +} + void zebra_router_terminate(void) { struct zebra_router_table *zrt, *tmp; diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 6c9f3a0f2..e50f8a118 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -50,6 +50,17 @@ RB_HEAD(zebra_router_table_head, zebra_router_table); RB_PROTOTYPE(zebra_router_table_head, zebra_router_table, zebra_router_table_entry, zebra_router_table_entry_compare) +/* RPF lookup behaviour */ +enum multicast_mode { + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + struct zebra_mlag_info { /* Role this zebra router is playing */ enum mlag_role role; @@ -82,9 +93,8 @@ struct zebra_router { struct hash *iptable_hash; -#if defined(HAVE_RTADV) - struct rtadv rtadv; -#endif /* HAVE_RTADV */ + /* used if vrf backend is not network namespace */ + int rtadv_sock; /* A sequence number used for tracking routes */ _Atomic uint32_t sequence_num; @@ -113,6 +123,9 @@ struct zebra_router { uint32_t multipath_num; + /* RPF Lookup behavior */ + enum multicast_mode ipv4_multicast_mode; + /* * Time for when we sweep the rib from old routes */ @@ -153,6 +166,10 @@ static inline struct zebra_vrf *zebra_vrf_get_evpn(void) : zebra_vrf_lookup_by_id(VRF_DEFAULT); } +extern void multicast_mode_ipv4_set(enum multicast_mode mode); + +extern enum multicast_mode multicast_mode_ipv4_get(void); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 634305494..fdf0cbc69 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -29,6 +29,7 @@ #include "vty.h" #include "zebra/zebra_router.h" +#include "zebra/rtadv.h" #include "zebra/debug.h" #include "zebra/zapi_msg.h" #include "zebra/rib.h" @@ -119,6 +120,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); else zvrf->zns = zebra_ns_lookup(NS_DEFAULT); +#if defined(HAVE_RTADV) + rtadv_init(zvrf); +#endif + /* Inform clients that the VRF is now active. This is an * add for the clients. */ @@ -161,6 +166,10 @@ static int zebra_vrf_disable(struct vrf *vrf) /* Stop any VxLAN-EVPN processing. */ zebra_vxlan_vrf_disable(zvrf); +#if defined(HAVE_RTADV) + rtadv_terminate(zvrf); +#endif + /* Inform clients that the VRF is now inactive. This is a * delete for the clients. */ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index febaf3c84..972fe381c 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -169,6 +169,10 @@ struct zebra_vrf { uint64_t lsp_removals_queued; uint64_t lsp_installs; uint64_t lsp_removals; + +#if defined(HAVE_RTADV) + struct rtadv rtadv; +#endif /* HAVE_RTADV */ }; #define PROTO_RM_NAME(zvrf, afi, rtype) zvrf->proto_rm[afi][rtype].name #define NHT_RM_NAME(zvrf, afi, rtype) zvrf->nht_rm[afi][rtype].name diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index f2f8a2a05..077c1ff8f 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -839,9 +839,9 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) return; if (json_vni == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n", -wctx->addr_width, buf2, "local", - state_str, buf1); + state_str, buf1, n->loc_seq, n->rem_seq); } else { json_object_string_add(json_row, "type", "local"); json_object_string_add(json_row, "state", state_str); @@ -875,9 +875,9 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) "%*s %-6s %-8s %-17s %-21s\n", -wctx->addr_width, "Neighbor", "Type", "State", "MAC", "Remote VTEP"); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-21s %u/%u\n", -wctx->addr_width, buf2, "remote", state_str, - buf1, inet_ntoa(n->r_vtep_ip)); + buf1, inet_ntoa(n->r_vtep_ip), n->loc_seq, n->rem_seq); } else { json_object_string_add(json_row, "type", "remote"); json_object_string_add(json_row, "state", state_str); @@ -987,9 +987,9 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); if (json == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); + "State", "MAC", "Remote VTEP", "Seq #'s"); } if (print_dup) hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, |