diff options
53 files changed, 1421 insertions, 408 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c index d52eeeddba..483beb1b17 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -454,7 +454,17 @@ void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo) static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd) { /* Send the scheduled echo packet */ - ptm_bfd_echo_snd(bfd); + /* if ipv4 use the new echo implementation that causes + * the packet to be looped in forwarding plane of peer + */ + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6) == 0) +#ifdef BFD_LINUX + ptm_bfd_echo_fp_snd(bfd); +#else + ptm_bfd_echo_snd(bfd); +#endif + else + ptm_bfd_echo_snd(bfd); /* Restart the timer for next time */ ptm_bfd_start_xmt_timer(bfd, true); @@ -558,6 +568,12 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag) state_list[bfd->ses_state].str, get_diag_str(bfd->local_diag)); } + + /* clear peer's mac address */ + UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET); + memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr)); + /* reset local address ,it might has been be changed after bfd is up*/ + memset(&bfd->local_address, 0, sizeof(bfd->local_address)); } static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa, diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 6aa9e00586..48a1e0bc31 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -168,9 +168,10 @@ enum bfd_session_flags { * expires */ BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */ - BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */ - BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */ + BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */ + BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */ BFD_SESS_FLAG_PASSIVE = 1 << 10, /* Passive mode */ + BFD_SESS_FLAG_MAC_SET = 1 << 11, /* MAC of peer known */ }; /* @@ -290,6 +291,8 @@ struct bfd_session { struct peer_label *pl; struct bfd_dplane_ctx *bdc; + struct sockaddr_any local_address; + uint8_t peer_hw_addr[ETH_ALEN]; struct interface *ifp; struct vrf *vrf; @@ -554,6 +557,7 @@ int bp_echov6_socket(const struct vrf *vrf); void ptm_bfd_snd(struct bfd_session *bfd, int fbit); void ptm_bfd_echo_snd(struct bfd_session *bfd); +void ptm_bfd_echo_fp_snd(struct bfd_session *bfd); void bfd_recv_cb(struct thread *t); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index c717a333a6..6b0afef65f 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -34,6 +34,8 @@ #include <netinet/udp.h> #include "lib/sockopt.h" +#include "lib/checksum.h" +#include "lib/network.h" #include "bfd.h" @@ -55,6 +57,18 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, struct sockaddr *to, socklen_t tolen); int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl, uint32_t *my_discr); +#ifdef BFD_LINUX +ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, + uint8_t *ttl, ifindex_t *ifindex, + struct sockaddr_any *local, struct sockaddr_any *peer); +void bfd_peer_mac_set(int sd, struct bfd_session *bfd, + struct sockaddr_any *peer, struct interface *ifp); +int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen, + struct bfd_session *bfd); +ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen, + uint8_t *ttl, ifindex_t *ifindex, + struct sockaddr_any *local, struct sockaddr_any *peer); +#endif /* socket related prototypes */ static void bp_set_ipopts(int sd); @@ -126,6 +140,142 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, return 0; } +#ifdef BFD_LINUX +/* + * Compute the UDP checksum. + * + * Checksum is not set in the packet, just computed. + * + * pkt + * Packet, fully filled out except for checksum field. + * + * pktsize + * sizeof(*pkt) + * + * ip + * IP address that pkt will be transmitted from and too. + * + * Returns: + * Checksum in network byte order. + */ +static uint16_t bfd_pkt_checksum(struct udphdr *pkt, size_t pktsize, + struct in6_addr *ip, sa_family_t family) +{ + uint16_t chksum; + + pkt->check = 0; + + if (family == AF_INET6) { + struct ipv6_ph ph = {}; + + memcpy(&ph.src, ip, sizeof(ph.src)); + memcpy(&ph.dst, ip, sizeof(ph.dst)); + ph.ulpl = htons(pktsize); + ph.next_hdr = IPPROTO_UDP; + chksum = in_cksum_with_ph6(&ph, pkt, pktsize); + } else { + struct ipv4_ph ph = {}; + + memcpy(&ph.src, ip, sizeof(ph.src)); + memcpy(&ph.dst, ip, sizeof(ph.dst)); + ph.proto = IPPROTO_UDP; + ph.len = htons(pktsize); + chksum = in_cksum_with_ph4(&ph, pkt, pktsize); + } + + return chksum; +} + +/* + * This routine creates the entire ECHO packet so that it will be looped + * in the forwarding plane of the peer router instead of going up the + * stack in BFD to be looped. If we haven't learned the peers MAC yet + * no echo is sent. + * + * echo packet with src/dst IP equal to local IP + * dest MAC as peer's MAC + * + * currently support ipv4 + */ +void ptm_bfd_echo_fp_snd(struct bfd_session *bfd) +{ + int sd; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); + int total_len = 0; + struct ethhdr *eth; + struct udphdr *uh; + struct iphdr *iph; + struct bfd_echo_pkt *beph; + static char sendbuff[100]; + + if (!bvrf) + return; + if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET)) + return; + if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); + + memset(sendbuff, 0, sizeof(sendbuff)); + + /* add eth hdr */ + eth = (struct ethhdr *)(sendbuff); + memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(bfd->ifp->hw_addr)); + memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr)); + + total_len += sizeof(struct ethhdr); + + sd = bvrf->bg_echo; + eth->h_proto = htons(ETH_P_IP); + + /* add ip hdr */ + iph = (struct iphdr *)(sendbuff + sizeof(struct ethhdr)); + + iph->ihl = sizeof(struct ip) >> 2; + iph->version = IPVERSION; + iph->tos = IPTOS_PREC_INTERNETCONTROL; + iph->id = (uint16_t)frr_weak_random(); + iph->ttl = BFD_TTL_VAL; + iph->protocol = IPPROTO_UDP; + memcpy(&iph->saddr, &bfd->local_address.sa_sin.sin_addr, + sizeof(bfd->local_address.sa_sin.sin_addr)); + memcpy(&iph->daddr, &bfd->local_address.sa_sin.sin_addr, + sizeof(bfd->local_address.sa_sin.sin_addr)); + total_len += sizeof(struct iphdr); + + /* add udp hdr */ + uh = (struct udphdr *)(sendbuff + sizeof(struct iphdr) + + sizeof(struct ethhdr)); + uh->source = htons(BFD_DEF_ECHO_PORT); + uh->dest = htons(BFD_DEF_ECHO_PORT); + + total_len += sizeof(struct udphdr); + + /* add bfd echo */ + beph = (struct bfd_echo_pkt *)(sendbuff + sizeof(struct udphdr) + + sizeof(struct iphdr) + + sizeof(struct ethhdr)); + + beph->ver = BFD_ECHO_VERSION; + beph->len = BFD_ECHO_PKT_LEN; + beph->my_discr = htonl(bfd->discrs.my_discr); + + total_len += sizeof(struct bfd_echo_pkt); + uh->len = + htons(total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)); + uh->check = bfd_pkt_checksum( + uh, (total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)), + (struct in6_addr *)&iph->saddr, AF_INET); + + iph->tot_len = htons(total_len - sizeof(struct ethhdr)); + iph->check = in_cksum((const void *)iph, sizeof(struct iphdr)); + + if (bp_udp_send_fp(sd, (uint8_t *)&sendbuff, total_len, bfd) == -1) + return; + + bfd->stats.tx_echo_pkt++; +} +#endif + void ptm_bfd_echo_snd(struct bfd_session *bfd) { struct sockaddr *sa; @@ -275,6 +425,94 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit) bfd->stats.tx_ctrl_pkt++; } +#ifdef BFD_LINUX +/* + * receive the ipv4 echo packet that was loopback in the peers forwarding plane + */ +ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, + uint8_t *ttl, ifindex_t *ifindex, + struct sockaddr_any *local, struct sockaddr_any *peer) +{ + ssize_t mlen; + struct sockaddr_ll msgaddr; + struct msghdr msghdr; + struct iovec iov[1]; + uint16_t recv_checksum; + uint16_t checksum; + struct iphdr *ip; + struct udphdr *uh; + + /* Prepare the recvmsg params. */ + iov[0].iov_base = msgbuf; + iov[0].iov_len = msgbuflen; + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_name = &msgaddr; + msghdr.msg_namelen = sizeof(msgaddr); + msghdr.msg_iov = iov; + msghdr.msg_iovlen = 1; + + mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT); + if (mlen == -1) { + if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR) + zlog_err("%s: recv failed: %s", __func__, + strerror(errno)); + + return -1; + } + + ip = (struct iphdr *)(msgbuf + sizeof(struct ethhdr)); + + /* verify ip checksum */ + recv_checksum = ip->check; + ip->check = 0; + checksum = in_cksum((const void *)ip, sizeof(struct iphdr)); + if (recv_checksum != checksum) { + if (bglobal.debug_network) + zlog_debug( + "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x", + __func__, checksum, recv_checksum); + return -1; + } + + *ttl = ip->ttl; + if (*ttl != 254) { + /* Echo should be looped in peer's forwarding plane, but it also + * comes up to BFD so silently drop it + */ + if (ip->daddr == ip->saddr) + return -1; + + if (bglobal.debug_network) + zlog_debug("%s: invalid TTL: %u", __func__, *ttl); + return -1; + } + + local->sa_sin.sin_family = AF_INET; + memcpy(&local->sa_sin.sin_addr, &ip->saddr, sizeof(ip->saddr)); + peer->sa_sin.sin_family = AF_INET; + memcpy(&peer->sa_sin.sin_addr, &ip->daddr, sizeof(ip->daddr)); + + *ifindex = msgaddr.sll_ifindex; + + /* verify udp checksum */ + uh = (struct udphdr *)(msgbuf + sizeof(struct iphdr) + + sizeof(struct ethhdr)); + recv_checksum = uh->check; + uh->check = 0; + checksum = bfd_pkt_checksum(uh, ntohs(uh->len), + (struct in6_addr *)&ip->saddr, AF_INET); + if (recv_checksum != checksum) { + if (bglobal.debug_network) + zlog_debug( + "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x", + __func__, checksum, recv_checksum); + return -1; + } + return mlen; +} +#endif + ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, ifindex_t *ifindex, struct sockaddr_any *local, struct sockaddr_any *peer) @@ -649,6 +887,8 @@ void bfd_recv_cb(struct thread *t) /* * Multi hop: validate packet TTL. + * Single hop: set local address that received the packet. + * set peers mac address for echo packets */ if (is_mhop) { if (ttl < bfd->mh_ttl) { @@ -657,6 +897,14 @@ void bfd_recv_cb(struct thread *t) bfd->mh_ttl, ttl); return; } + } else { + + if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC) + bfd->local_address = local; +#ifdef BFD_LINUX + if (ifp) + bfd_peer_mac_set(sd, bfd, &peer, ifp); +#endif } bfd->stats.rx_ctrl_pkt++; @@ -756,13 +1004,30 @@ int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, ifindex_t ifindex = IFINDEX_INTERNAL; vrf_id_t vrfid = VRF_DEFAULT; uint8_t msgbuf[1516]; + size_t bfd_offset = 0; + + if (sd == bvrf->bg_echo) { +#ifdef BFD_LINUX + rlen = bfd_recv_ipv4_fp(sd, msgbuf, sizeof(msgbuf), ttl, + &ifindex, &local, &peer); - if (sd == bvrf->bg_echo) + /* silently drop echo packet that is looped in fastpath but + * still comes up to BFD + */ + if (rlen == -1) + return -1; + bfd_offset = sizeof(struct udphdr) + sizeof(struct iphdr) + + sizeof(struct ethhdr); +#else rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex, &local, &peer); - else + bfd_offset = 0; +#endif + } else { rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex, &local, &peer); + bfd_offset = 0; + } /* Short packet, better not risk reading it. */ if (rlen < (ssize_t)sizeof(*bep)) { @@ -771,8 +1036,8 @@ int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, return -1; } - /* Test for loopback. */ - if (*ttl == BFD_TTL_VAL) { + /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */ + if ((*ttl == BFD_TTL_VAL) && (sd == bvrf->bg_echov6)) { bp_udp_send(sd, *ttl - 1, msgbuf, rlen, (struct sockaddr *)&peer, (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin) @@ -781,7 +1046,7 @@ int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, } /* Read my discriminator from BFD Echo packet. */ - bep = (struct bfd_echo_pkt *)msgbuf; + bep = (struct bfd_echo_pkt *)(msgbuf + bfd_offset); *my_discr = ntohl(bep->my_discr); if (*my_discr == 0) { cp_debug(false, &peer, &local, ifindex, vrfid, @@ -792,6 +1057,56 @@ int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, return 0; } +#ifdef BFD_LINUX +/* + * send a bfd packet with src/dst same IP so that the peer will receive + * the packet and forward it back to sender in the forwarding plane + */ +int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen, + struct bfd_session *bfd) +{ + ssize_t wlen; + struct msghdr msg; + struct iovec iov[1]; + uint8_t msgctl[255]; + struct sockaddr_ll sadr_ll; + + + sadr_ll.sll_ifindex = bfd->ifp->ifindex; + sadr_ll.sll_halen = ETH_ALEN; + memcpy(sadr_ll.sll_addr, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr)); + sadr_ll.sll_protocol = htons(ETH_P_IP); + + /* Prepare message data. */ + iov[0].iov_base = data; + iov[0].iov_len = datalen; + + memset(&msg, 0, sizeof(msg)); + memset(msgctl, 0, sizeof(msgctl)); + msg.msg_name = &sadr_ll; + msg.msg_namelen = sizeof(sadr_ll); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + /* Send echo to peer */ + wlen = sendmsg(sd, &msg, 0); + + if (wlen <= 0) { + if (bglobal.debug_network) + zlog_debug("udp-send: loopback failure: (%d) %s", errno, + strerror(errno)); + return -1; + } else if (wlen < (ssize_t)datalen) { + if (bglobal.debug_network) + zlog_debug("udp-send: partial send: %zd expected %zu", + wlen, datalen); + return -1; + } + + return 0; +} +#endif + int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, struct sockaddr *to, socklen_t tolen) { @@ -1219,6 +1534,57 @@ int bp_udp6_mhop(const struct vrf *vrf) return sd; } +#ifdef BFD_LINUX +/* tcpdump -dd udp dst port 3785 */ +struct sock_filter my_filterudp[] = { + {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800}, + {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011}, + {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff}, + {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010}, + {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000}, + {0x6, 0, 0, 0x00000000}, +}; + +#define MY_FILTER_LENGTH 11 + +int bp_echo_socket(const struct vrf *vrf) +{ + int s; + + frr_with_privs (&bglobal.bfdd_privs) { + s = vrf_socket(AF_PACKET, SOCK_RAW, ETH_P_IP, vrf->vrf_id, + vrf->name); + } + + if (s == -1) + zlog_fatal("echo-socket: socket: %s", strerror(errno)); + + struct sock_fprog pf; + struct sockaddr_ll sll; + + /* adjust filter for socket to only receive ECHO packets */ + pf.filter = my_filterudp; + pf.len = MY_FILTER_LENGTH; + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) == + -1) { + zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__, + strerror(errno)); + return -1; + } + + + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_IP); + sll.sll_ifindex = 0; + if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0) { + zlog_warn("Failed to bind echo socket: %s", + safe_strerror(errno)); + return -1; + } + + return s; +} +#else int bp_echo_socket(const struct vrf *vrf) { int s; @@ -1234,6 +1600,7 @@ int bp_echo_socket(const struct vrf *vrf) return s; } +#endif int bp_echov6_socket(const struct vrf *vrf) { @@ -1257,3 +1624,41 @@ int bp_echov6_socket(const struct vrf *vrf) return s; } + +#ifdef BFD_LINUX +/* get peer's mac address to be used with Echo packets when they are looped in + * peers forwarding plane + */ +void bfd_peer_mac_set(int sd, struct bfd_session *bfd, + struct sockaddr_any *peer, struct interface *ifp) +{ + struct arpreq arpreq_; + + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET)) + return; + + if (peer->sa_sin.sin_family == AF_INET) { + /* IPV4 */ + struct sockaddr_in *addr = + (struct sockaddr_in *)&arpreq_.arp_pa; + + memset(&arpreq_, 0, sizeof(struct arpreq)); + addr->sin_family = AF_INET; + memcpy(&addr->sin_addr.s_addr, &peer->sa_sin.sin_addr, + sizeof(addr->sin_addr)); + strlcpy(arpreq_.arp_dev, ifp->name, sizeof(arpreq_.arp_dev)); + + if (ioctl(sd, SIOCGARP, &arpreq_) < 0) { + zlog_warn("BFD: getting peer's mac failed error %s", + strerror(errno)); + UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET); + memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr)); + + } else { + memcpy(bfd->peer_hw_addr, arpreq_.arp_ha.sa_data, + sizeof(bfd->peer_hw_addr)); + SET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET); + } + } +} +#endif diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index d4e12e4f1a..69424c45d9 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -423,12 +423,19 @@ DEFPY_YANG( "Configure echo mode\n") { if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { - vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + vty_out(vty, + "%% Echo mode is only available for single hop sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } if (!no && !bglobal.bg_use_dplane) { - vty_out(vty, "%% Current implementation of echo mode works only when the peer is also FRR.\n"); +#ifdef BFD_LINUX + vty_out(vty, + "%% Echo mode works correctly for IPv4, but only works when the peer is also FRR for IPv6.\n"); +#else + vty_out(vty, + "%% Current implementation of echo mode works only when the peer is also FRR.\n"); +#endif /* BFD_LINUX */ } nb_cli_enqueue_change(vty, "./echo-mode", NB_OP_MODIFY, diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index e7690ed8df..1a253b122a 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1606,13 +1606,9 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr) { - in_addr_t nexthop_h; struct bgp *bgp = peer->bgp; - nexthop_h = ntohl(attr->nexthop.s_addr); - if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) || - !ipv4_unicast_valid(&attr->nexthop)) && - !bgp->allow_martian) { + if (ipv4_martian(&attr->nexthop) && !bgp->allow_martian) { uint8_t data[7]; /* type(2) + length(1) + nhop(4) */ char buf[INET_ADDRSTRLEN]; diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index dd1510a678..9c2826fa13 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -100,7 +100,8 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, if (BGP_DEBUG(update, UPDATE_OUT)) zlog_debug("%s: %s routes to/from %s for %s", __func__, - update_type == ADVERTISE ? "Advertise" : "Withdraw", + update_type == UPDATE_TYPE_ADVERTISE ? "Advertise" + : "Withdraw", peer->host, get_afi_safi_str(afi, safi, false)); addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); @@ -133,7 +134,7 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, * on same peer, routes in advertise-map may not * be advertised as expected. */ - if (update_type == ADVERTISE && + if (update_type == UPDATE_TYPE_ADVERTISE && subgroup_announce_check(dest, pi, subgrp, dest_p, &attr, &advmap_attr)) { bgp_adj_out_set_subgroup(dest, subgrp, &attr, @@ -248,12 +249,14 @@ static void bgp_conditional_adv_timer(struct thread *t) */ if (filter->advmap.condition == CONDITION_EXIST) filter->advmap.update_type = - (ret == RMAP_PERMITMATCH) ? ADVERTISE - : WITHDRAW; + (ret == RMAP_PERMITMATCH) + ? UPDATE_TYPE_ADVERTISE + : UPDATE_TYPE_WITHDRAW; else filter->advmap.update_type = - (ret == RMAP_PERMITMATCH) ? WITHDRAW - : ADVERTISE; + (ret == RMAP_PERMITMATCH) + ? UPDATE_TYPE_WITHDRAW + : UPDATE_TYPE_ADVERTISE; /* Send regular update as per the existing policy. * There is a change in route-map, match-rule, ACLs, @@ -312,8 +315,9 @@ void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi) } /* Register for conditional routes polling timer */ - thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, - bgp->condition_check_period, &bgp->t_condition_check); + if (!thread_is_scheduled(bgp->t_condition_check)) + thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0, + &bgp->t_condition_check); } void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 63b73bc583..19901792ea 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -12115,11 +12115,12 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, filter->advmap.cname); json_object_string_add(json_advmap, "advertiseMap", filter->advmap.aname); - json_object_string_add(json_advmap, "advertiseStatus", - filter->advmap.update_type - == ADVERTISE - ? "Advertise" - : "Withdraw"); + json_object_string_add( + json_advmap, "advertiseStatus", + filter->advmap.update_type == + UPDATE_TYPE_ADVERTISE + ? "Advertise" + : "Withdraw"); json_object_object_add(json_addr, "advertiseMap", json_advmap); } @@ -12429,7 +12430,8 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, filter->advmap.cname, filter->advmap.amap ? "*" : "", filter->advmap.aname, - filter->advmap.update_type == ADVERTISE + filter->advmap.update_type == + UPDATE_TYPE_ADVERTISE ? "Advertise" : "Withdraw"); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index b6f2a294a6..0bfcf5163f 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -7225,18 +7225,15 @@ static void peer_advertise_map_filter_update(struct peer *peer, afi_t afi, /* Increment condition_filter_count and/or create timer. */ if (!filter_exists) { - filter->advmap.update_type = ADVERTISE; + filter->advmap.update_type = UPDATE_TYPE_ADVERTISE; bgp_conditional_adv_enable(peer, afi, safi); } + + /* Process peer route updates. */ + peer_on_policy_change(peer, afi, safi, 1); } -/* Set advertise-map to the peer but do not process peer route updates here. * - * Hold filter changes until the conditional routes polling thread is called * - * AS we need to advertise/withdraw prefixes (in advertise-map) based on the * - * condition (exist-map/non-exist-map) and routes(specified in condition-map) * - * in BGP table. So do not call peer_on_policy_change() here, only create * - * polling timer thread, update filters and increment condition_filter_count. - */ +/* Set advertise-map to the peer. */ int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi, const char *advertise_name, struct route_map *advertise_map, @@ -7316,7 +7313,6 @@ int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi, __func__, peer->host, get_afi_safi_str(afi, safi, false)); - peer_on_policy_change(peer, afi, safi, 1); return 0; } @@ -7339,8 +7335,6 @@ int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi, zlog_debug("%s: Send normal update to %s for %s ", __func__, member->host, get_afi_safi_str(afi, safi, false)); - - peer_on_policy_change(member, afi, safi, 1); } return 0; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index bc5f6cf6fd..7f3d240b8e 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -859,7 +859,7 @@ struct bgp_nexthop { #define CONDITION_NON_EXIST false #define CONDITION_EXIST true -enum update_type { WITHDRAW, ADVERTISE }; +enum update_type { UPDATE_TYPE_WITHDRAW, UPDATE_TYPE_ADVERTISE }; #include "filter.h" diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 42faefd10b..7679a377eb 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -338,6 +338,12 @@ Basic Config Commands Restrict vty connections with an access list. +.. clicmd:: allow-reserved-ranges + + Allow using IPv4 reserved (Class E) IP ranges for daemons. E.g.: setting + IPv4 addresses for interfaces or allowing reserved ranges in BGP next-hops. + + Default: off. .. _sample-config-file: diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 09f92554c0..3aef8ada24 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1400,20 +1400,21 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree, spf_adj_list_parse_lsp(spftree, adj_list, lsp, id, metric); } -static void spf_adj_list_parse_lsp_frag(struct isis_spftree *spftree, - struct list *adj_list, - struct isis_lsp *lsp, - const uint8_t *pseudo_nodeid, - uint32_t pseudo_metric) +static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, + struct list *adj_list, struct isis_lsp *lsp, + const uint8_t *pseudo_nodeid, + uint32_t pseudo_metric) { bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; struct isis_item *head; struct isis_item_list *te_neighs; if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) return; - /* Parse main LSP. */ + /* Parse LSP. */ if (lsp->tlvs) { if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { head = lsp->tlvs->oldstyle_reach.head; @@ -1444,27 +1445,17 @@ static void spf_adj_list_parse_lsp_frag(struct isis_spftree *spftree, } } } -} - -static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, - struct list *adj_list, struct isis_lsp *lsp, - const uint8_t *pseudo_nodeid, - uint32_t pseudo_metric) -{ - struct isis_lsp *frag; - struct listnode *node; - - spf_adj_list_parse_lsp_frag(spftree, adj_list, lsp, pseudo_nodeid, - pseudo_metric); + if (LSP_FRAGMENT(lsp->hdr.lsp_id)) + return; /* Parse LSP fragments. */ for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { if (!frag->tlvs) continue; - spf_adj_list_parse_lsp_frag(spftree, adj_list, frag, - pseudo_nodeid, pseudo_metric); + spf_adj_list_parse_lsp(spftree, adj_list, frag, pseudo_nodeid, + pseudo_metric); } } diff --git a/lib/command.c b/lib/command.c index cbecc81574..a23afb1e43 100644 --- a/lib/command.c +++ b/lib/command.c @@ -121,6 +121,11 @@ const char *cmd_version_get(void) return host.version; } +bool cmd_allow_reserved_ranges_get(void) +{ + return host.allow_reserved_ranges; +} + static int root_on_exit(struct vty *vty); /* Standard command node structures. */ @@ -454,6 +459,9 @@ static int config_write_host(struct vty *vty) if (name && name[0] != '\0') vty_out(vty, "domainname %s\n", name); + if (cmd_allow_reserved_ranges_get()) + vty_out(vty, "allow-reserved-ranges\n"); + /* The following are all configuration commands that are not sent to * watchfrr. For instance watchfrr is hardcoded to log to syslog so * we would always display 'log syslog informational' in the config @@ -2294,6 +2302,21 @@ DEFUN (no_banner_motd, return CMD_SUCCESS; } +DEFUN(allow_reserved_ranges, allow_reserved_ranges_cmd, "allow-reserved-ranges", + "Allow using IPv4 (Class E) reserved IP space\n") +{ + host.allow_reserved_ranges = true; + return CMD_SUCCESS; +} + +DEFUN(no_allow_reserved_ranges, no_allow_reserved_ranges_cmd, + "no allow-reserved-ranges", + NO_STR "Allow using IPv4 (Class E) reserved IP space\n") +{ + host.allow_reserved_ranges = false; + return CMD_SUCCESS; +} + int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc) { const struct cmd_node *node; @@ -2483,6 +2506,7 @@ void cmd_init(int terminal) host.lines = -1; cmd_banner_motd_line(FRR_DEFAULT_MOTD); host.motdfile = NULL; + host.allow_reserved_ranges = false; /* Install top nodes. */ install_node(&view_node); @@ -2552,6 +2576,8 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &no_banner_motd_cmd); install_element(CONFIG_NODE, &service_terminal_length_cmd); install_element(CONFIG_NODE, &no_service_terminal_length_cmd); + install_element(CONFIG_NODE, &allow_reserved_ranges_cmd); + install_element(CONFIG_NODE, &no_allow_reserved_ranges_cmd); log_cmd_init(); vrf_install_commands(); diff --git a/lib/command.h b/lib/command.h index 7363ed84c8..70e52708a7 100644 --- a/lib/command.h +++ b/lib/command.h @@ -84,6 +84,9 @@ struct host { /* Banner configuration. */ char *motd; char *motdfile; + + /* Allow using IPv4 (Class E) reserved IP space */ + bool allow_reserved_ranges; }; /* List of CLI nodes. Please remember to update the name array in command.c. */ @@ -614,6 +617,7 @@ extern const char *cmd_domainname_get(void); extern const char *cmd_system_get(void); extern const char *cmd_release_get(void); extern const char *cmd_version_get(void); +extern bool cmd_allow_reserved_ranges_get(void); /* NOT safe for general use; call this only if DEV_BUILD! */ extern void grammar_sandbox_init(void); diff --git a/lib/prefix.c b/lib/prefix.c index 1a3efd32b1..e64b10bf24 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -21,6 +21,7 @@ #include <zebra.h> +#include "command.h" #include "prefix.h" #include "ipaddr.h" #include "vty.h" @@ -1386,6 +1387,23 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len) return buf; } +bool ipv4_unicast_valid(const struct in_addr *addr) +{ + in_addr_t ip = ntohl(addr->s_addr); + + if (IPV4_CLASS_D(ip)) + return false; + + if (IPV4_CLASS_E(ip)) { + if (cmd_allow_reserved_ranges_get()) + return true; + else + return false; + } + + return true; +} + printfrr_ext_autoreg_p("EA", printfrr_ea); static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) diff --git a/lib/prefix.h b/lib/prefix.h index f9eef28a0b..3a768572c4 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -382,6 +382,8 @@ static inline void ipv4_addr_copy(struct in_addr *dst, #define IPV4_NET0(a) ((((uint32_t)(a)) & 0xff000000) == 0x00000000) #define IPV4_NET127(a) ((((uint32_t)(a)) & 0xff000000) == 0x7f000000) #define IPV4_LINKLOCAL(a) ((((uint32_t)(a)) & 0xffff0000) == 0xa9fe0000) +#define IPV4_CLASS_D(a) ((((uint32_t)(a)) & 0xf0000000) == 0xe0000000) +#define IPV4_CLASS_E(a) ((((uint32_t)(a)) & 0xf0000000) == 0xf0000000) #define IPV4_CLASS_DE(a) ((((uint32_t)(a)) & 0xe0000000) == 0xe0000000) #define IPV4_MC_LINKLOCAL(a) ((((uint32_t)(a)) & 0xffffff00) == 0xe0000000) @@ -507,17 +509,7 @@ extern int str_to_esi(const char *str, esi_t *esi); extern char *esi_to_str(const esi_t *esi, char *buf, int size); extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); - -static inline bool ipv4_unicast_valid(const struct in_addr *addr) -{ - - in_addr_t ip = ntohl(addr->s_addr); - - if (IPV4_CLASS_DE(ip)) - return false; - - return true; -} +extern bool ipv4_unicast_valid(const struct in_addr *addr); static inline int ipv6_martian(const struct in6_addr *addr) { @@ -534,14 +526,14 @@ static inline int ipv6_martian(const struct in6_addr *addr) extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p); /* NOTE: This routine expects the address argument in network byte order. */ -static inline int ipv4_martian(const struct in_addr *addr) +static inline bool ipv4_martian(const struct in_addr *addr) { in_addr_t ip = ntohl(addr->s_addr); if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) { - return 1; + return true; } - return 0; + return false; } static inline bool is_default_prefix4(const struct prefix_ipv4 *p) diff --git a/lib/privs.c b/lib/privs.c index c012178e71..5cba90839f 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -286,9 +286,6 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) } } - if (!zprivs_state.syscaps_p) - return; - if (!(zprivs_state.caps = cap_init())) { fprintf(stderr, "privs_init: failed to cap_init, %s\n", safe_strerror(errno)); @@ -301,10 +298,12 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) exit(1); } - /* set permitted caps */ - cap_set_flag(zprivs_state.caps, CAP_PERMITTED, - zprivs_state.syscaps_p->num, zprivs_state.syscaps_p->caps, - CAP_SET); + /* set permitted caps, if any */ + if (zprivs_state.syscaps_p && zprivs_state.syscaps_p->num) { + cap_set_flag(zprivs_state.caps, CAP_PERMITTED, + zprivs_state.syscaps_p->num, + zprivs_state.syscaps_p->caps, CAP_SET); + } /* set inheritable caps, if any */ if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) { @@ -364,7 +363,7 @@ static void zprivs_caps_terminate(void) } /* free up private state */ - if (zprivs_state.syscaps_p->num) { + if (zprivs_state.syscaps_p && zprivs_state.syscaps_p->num) { XFREE(MTYPE_PRIVS, zprivs_state.syscaps_p->caps); XFREE(MTYPE_PRIVS, zprivs_state.syscaps_p); } diff --git a/lib/typesafe.h b/lib/typesafe.h index 06fdc52e78..df963f530d 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -309,7 +309,8 @@ static inline void typesafe_dlist_add(struct dlist_head *head, struct dlist_item *prev, struct dlist_item *item) { item->next = prev->next; - item->next->prev = item; + if (item->next) + item->next->prev = item; item->prev = prev; prev->next = item; head->count++; diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index 56116cd28d..fb117ecfc2 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -266,8 +266,12 @@ DEFUN (ip_ospf_bfd_prof, struct ospf_if_params *params; int idx_prof = 4; - ospf_interface_enable_bfd(ifp); params = IF_DEF_PARAMS(ifp); + if (!params->bfd_config) { + vty_out(vty, "ip ospf bfd has not been set\n"); + return CMD_WARNING; + } + strlcpy(params->bfd_config->profile, argv[idx_prof]->arg, sizeof(params->bfd_config->profile)); ospf_interface_bfd_apply(ifp); @@ -288,8 +292,10 @@ DEFUN (no_ip_ospf_bfd_prof, VTY_DECLVAR_CONTEXT(interface, ifp); struct ospf_if_params *params; - ospf_interface_enable_bfd(ifp); params = IF_DEF_PARAMS(ifp); + if (!params->bfd_config) + return CMD_SUCCESS; + params->bfd_config->profile[0] = 0; ospf_interface_bfd_apply(ifp); diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 9ff2224992..e46b4d6a10 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -686,9 +686,7 @@ DEFPY (ipv6_mld_group_watermark, "Group count to generate watermark warning\n") { PIM_DECLVAR_CONTEXT_VRF(vrf, pim); - - /* TBD Depends on MLD data structure changes */ - (void)pim; + pim->gm_watermark_limit = limit; return CMD_SUCCESS; } @@ -703,9 +701,7 @@ DEFPY (no_ipv6_mld_group_watermark, IGNORED_IN_NO_STR) { PIM_DECLVAR_CONTEXT_VRF(vrf, pim); - - /* TBD Depends on MLD data structure changes */ - (void)pim; + pim->gm_watermark_limit = 0; return CMD_SUCCESS; } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 8f3777e03e..10ea472fb5 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1135,12 +1135,12 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) json = json_object_new_object(); json_object_int_add(json, "totalGroups", pim->igmp_group_count); json_object_int_add(json, "watermarkLimit", - pim->igmp_watermark_limit); + pim->gm_watermark_limit); } else { vty_out(vty, "Total IGMP groups: %u\n", pim->igmp_group_count); vty_out(vty, "Watermark warn limit(%s): %u\n", - pim->igmp_watermark_limit ? "Set" : "Not Set", - pim->igmp_watermark_limit); + pim->gm_watermark_limit ? "Set" : "Not Set", + pim->gm_watermark_limit); vty_out(vty, "Interface Group Mode Timer Srcs V Uptime \n"); } @@ -3335,7 +3335,7 @@ DEFPY (ip_igmp_group_watermark, "Group count to generate watermark warning\n") { PIM_DECLVAR_CONTEXT_VRF(vrf, pim); - pim->igmp_watermark_limit = limit; + pim->gm_watermark_limit = limit; return CMD_SUCCESS; } @@ -3350,7 +3350,7 @@ DEFPY (no_ip_igmp_group_watermark, IGNORED_IN_NO_STR) { PIM_DECLVAR_CONTEXT_VRF(vrf, pim); - pim->igmp_watermark_limit = 0; + pim->gm_watermark_limit = 0; return CMD_SUCCESS; } diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 849216af62..6ffeeb9657 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -1009,8 +1009,8 @@ static void igmp_group_count_incr(struct pim_interface *pim_ifp) uint32_t group_count = listcount(pim_ifp->gm_group_list); ++pim_ifp->pim->igmp_group_count; - if (pim_ifp->pim->igmp_group_count - == pim_ifp->pim->igmp_watermark_limit) { + if (pim_ifp->pim->igmp_group_count == + pim_ifp->pim->gm_watermark_limit) { zlog_warn( "IGMP group count reached watermark limit: %u(vrf: %s)", pim_ifp->pim->igmp_group_count, diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 23b9df4aff..684785dd13 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -173,7 +173,7 @@ struct pim_instance { struct thread *t_gm_recv; unsigned int igmp_group_count; - unsigned int igmp_watermark_limit; + unsigned int gm_watermark_limit; unsigned int keep_alive_time; unsigned int rp_keep_alive_time; diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index d0c4921d07..5a2647b93f 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -187,9 +187,6 @@ extern int pim_channel_oil_compare(const struct channel_oil *c1, DECLARE_RBTREE_UNIQ(rb_pim_oil, struct channel_oil, oil_rb, pim_channel_oil_compare); - -extern struct list *pim_channel_oil_list; - void pim_oil_init(struct pim_instance *pim); void pim_oil_terminate(struct pim_instance *pim); diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 4dd1398733..43d9c29278 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -260,9 +260,14 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty) ++writes; } - if (pim->igmp_watermark_limit != 0) { - vty_out(vty, "%sip igmp watermark-warn %u\n", spaces, - pim->igmp_watermark_limit); + if (pim->gm_watermark_limit != 0) { +#if PIM_IPV == 4 + vty_out(vty, "%s" PIM_AF_NAME " igmp watermark-warn %u\n", + spaces, pim->gm_watermark_limit); +#else + vty_out(vty, "%s" PIM_AF_NAME " mld watermark-warn %u\n", + spaces, pim->gm_watermark_limit); +#endif ++writes; } diff --git a/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf b/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf index fcea5d48fc..373a0c5b94 100644 --- a/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf +++ b/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf @@ -2,6 +2,7 @@ interface r1-eth1 ip ospf area 0 ip ospf hello-interval 2 ip ospf dead-interval 10 + ip ospf bfd ip ospf bfd profile slowtx ! router ospf diff --git a/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf index 8fca099686..0476df740a 100644 --- a/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf @@ -5,7 +5,11 @@ ! bfd peer 192.168.0.2 vrf r1-bfd-cust1 + receive-interval 1000 + transmit-interval 1000 echo-mode + echo transmit-interval 1000 + echo receive-interval 1000 no shutdown ! ! diff --git a/tests/topotests/bfd_vrf_topo1/r1/peers.json b/tests/topotests/bfd_vrf_topo1/r1/peers.json index f49768ff75..57cea71e53 100644 --- a/tests/topotests/bfd_vrf_topo1/r1/peers.json +++ b/tests/topotests/bfd_vrf_topo1/r1/peers.json @@ -1,7 +1,7 @@ [ { "remote-receive-interval": 1000, - "remote-transmit-interval": 500, + "remote-transmit-interval": 1000, "peer": "192.168.0.2", "status": "up" } diff --git a/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf index 4490090ec6..69edd1536b 100644 --- a/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf @@ -6,12 +6,18 @@ bfd peer 192.168.0.1 vrf r2-bfd-cust1 receive-interval 1000 - transmit-interval 500 + transmit-interval 1000 echo-mode + echo transmit-interval 1000 + echo receive-interval 1000 no shutdown ! peer 192.168.1.1 vrf r2-bfd-cust1 + receive-interval 1000 + transmit-interval 1000 echo-mode + echo transmit-interval 1000 + echo receive-interval 1000 no shutdown ! ! diff --git a/tests/topotests/bfd_vrf_topo1/r2/peers.json b/tests/topotests/bfd_vrf_topo1/r2/peers.json index 267459c7a8..0a1c34224b 100644 --- a/tests/topotests/bfd_vrf_topo1/r2/peers.json +++ b/tests/topotests/bfd_vrf_topo1/r2/peers.json @@ -4,7 +4,7 @@ "status": "up" }, { - "remote-echo-receive-interval": 100, + "remote-echo-receive-interval": 1000, "peer": "192.168.1.1", "status": "up" }, diff --git a/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf index 0333320898..00162b5247 100644 --- a/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf @@ -5,7 +5,9 @@ ! bfd peer 192.168.1.2 vrf r3-bfd-cust1 - echo-interval 100 + receive-interval 1000 + transmit-interval 1000 + echo-interval 1000 echo-mode no shutdown ! diff --git a/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py index 6153aee418..1097be3d70 100644 --- a/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py +++ b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py @@ -197,11 +197,199 @@ def teardown_module(mod): logger.info("=" * 40) -def test_bgp_conditional_advertisement(): - """ - Test BGP conditional advertisement functionality. - """ +def all_routes_advertised(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def all_routes_withdrawn(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": None, + "10.139.224.0/20": None, + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +# BGP conditional advertisement with route-maps +# EXIST-MAP, ADV-MAP-1 and RMAP-1 +def exist_map_routes_present(router): + return all_routes_advertised(router) + + +def exist_map_routes_not_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": None, + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": None, + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": None, + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_not_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + +def exist_map_no_condition_route_map(router): + return non_exist_map_routes_present(router) + + +def non_exist_map_no_condition_route_map(router): + return all_routes_advertised(router) + + +def exist_map_routes_present_rmap_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": None, + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def exist_map_routes_present_no_rmap_filter(router): + return all_routes_advertised(router) + + +def non_exist_map_routes_present_rmap_filter(router): + return all_routes_withdrawn(router) + + +def non_exist_map_routes_present_no_rmap_filter(router): + return non_exist_map_routes_present(router) + + +def exist_map_routes_not_present_rmap_filter(router): + return all_routes_withdrawn(router) + + +def exist_map_routes_not_present_no_rmap_filter(router): + return exist_map_routes_not_present(router) + + +def non_exist_map_routes_not_present_rmap_filter(router): + return exist_map_routes_present_rmap_filter(router) + + +def non_exist_map_routes_not_present_no_rmap_filter(router): + return non_exist_map_routes_not_present(router) + + +# BGP conditional advertisement with route-maps +# EXIST-MAP, ADV-MAP-2 and RMAP-2 +def exist_map_routes_not_present_rmap2_filter(router): + return all_routes_withdrawn(router) + + +def exist_map_routes_not_present_no_rmap2_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def non_exist_map_routes_not_present_rmap2_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": None, + "10.139.224.0/20": None, + "203.0.113.1/32": [{"protocol": "bgp", "metric": 911}], + } + return topotest.json_cmp(output, expected) + +def non_exist_map_routes_not_present_no_rmap2_filter(router): + return non_exist_map_routes_not_present(router) + + +def exist_map_routes_present_rmap2_filter(router): + return non_exist_map_routes_not_present_rmap2_filter(router) + + +def exist_map_routes_present_no_rmap2_filter(router): + return all_routes_advertised(router) + + +def non_exist_map_routes_present_rmap2_filter(router): + return all_routes_withdrawn(router) + + +def non_exist_map_routes_present_no_rmap2_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + "203.0.113.1/32": None, + } + return topotest.json_cmp(output, expected) + + +def exist_map_routes_present_rmap2_network(router): + return non_exist_map_routes_not_present_rmap2_filter(router) + + +def exist_map_routes_present_rmap2_no_network(router): + return all_routes_withdrawn(router) + + +def non_exist_map_routes_not_present_rmap2_network(router): + return non_exist_map_routes_not_present_rmap2_filter(router) + + +def non_exist_map_routes_not_present_rmap2_no_network(router): + return all_routes_withdrawn(router) + + +passed = "PASSED!!!" +failed = "FAILED!!!" + + +def test_bgp_conditional_advertisement_step1(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -210,172 +398,9 @@ def test_bgp_conditional_advertisement(): router2 = tgen.gears["r2"] router3 = tgen.gears["r3"] - passed = "PASSED!!!" - failed = "FAILED!!!" - - def _all_routes_advertised(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": [{"protocol": "bgp"}], - "192.0.2.1/32": [{"protocol": "bgp"}], - "192.0.2.5/32": [{"protocol": "bgp"}], - "10.139.224.0/20": [{"protocol": "bgp"}], - "203.0.113.1/32": [{"protocol": "bgp"}], - } - return topotest.json_cmp(output, expected) - - def _all_routes_withdrawn(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": None, - "192.0.2.1/32": None, - "192.0.2.5/32": None, - "10.139.224.0/20": None, - "203.0.113.1/32": None, - } - return topotest.json_cmp(output, expected) - - # BGP conditional advertisement with route-maps - # EXIST-MAP, ADV-MAP-1 and RMAP-1 - def _exist_map_routes_present(router): - return _all_routes_advertised(router) - - def _exist_map_routes_not_present(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": None, - "192.0.2.1/32": None, - "192.0.2.5/32": [{"protocol": "bgp"}], - "10.139.224.0/20": None, - "203.0.113.1/32": [{"protocol": "bgp"}], - } - return topotest.json_cmp(output, expected) - - def _non_exist_map_routes_present(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": [{"protocol": "bgp"}], - "192.0.2.1/32": None, - "192.0.2.5/32": [{"protocol": "bgp"}], - "10.139.224.0/20": None, - "203.0.113.1/32": [{"protocol": "bgp"}], - } - return topotest.json_cmp(output, expected) - - def _non_exist_map_routes_not_present(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": None, - "192.0.2.1/32": [{"protocol": "bgp"}], - "192.0.2.5/32": [{"protocol": "bgp"}], - "10.139.224.0/20": [{"protocol": "bgp"}], - "203.0.113.1/32": [{"protocol": "bgp"}], - } - return topotest.json_cmp(output, expected) - - def _exist_map_no_condition_route_map(router): - return _non_exist_map_routes_present(router) - - def _non_exist_map_no_condition_route_map(router): - return _all_routes_advertised(router) - - def _exist_map_routes_present_rmap_filter(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": None, - "192.0.2.1/32": [{"protocol": "bgp"}], - "192.0.2.5/32": None, - "10.139.224.0/20": [{"protocol": "bgp"}], - "203.0.113.1/32": None, - } - return topotest.json_cmp(output, expected) - - def _exist_map_routes_present_no_rmap_filter(router): - return _all_routes_advertised(router) - - def _non_exist_map_routes_present_rmap_filter(router): - return _all_routes_withdrawn(router) - - def _non_exist_map_routes_present_no_rmap_filter(router): - return _non_exist_map_routes_present(router) - - def _exist_map_routes_not_present_rmap_filter(router): - return _all_routes_withdrawn(router) - - def _exist_map_routes_not_present_no_rmap_filter(router): - return _exist_map_routes_not_present(router) - - def _non_exist_map_routes_not_present_rmap_filter(router): - return _exist_map_routes_present_rmap_filter(router) - - def _non_exist_map_routes_not_present_no_rmap_filter(router): - return _non_exist_map_routes_not_present(router) - - # BGP conditional advertisement with route-maps - # EXIST-MAP, ADV-MAP-2 and RMAP-2 - def _exist_map_routes_not_present_rmap2_filter(router): - return _all_routes_withdrawn(router) - - def _exist_map_routes_not_present_no_rmap2_filter(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": None, - "192.0.2.1/32": [{"protocol": "bgp"}], - "192.0.2.5/32": [{"protocol": "bgp"}], - "10.139.224.0/20": [{"protocol": "bgp"}], - "203.0.113.1/32": None, - } - return topotest.json_cmp(output, expected) - - def _non_exist_map_routes_not_present_rmap2_filter(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": None, - "192.0.2.1/32": None, - "192.0.2.5/32": None, - "10.139.224.0/20": None, - "203.0.113.1/32": [{"protocol": "bgp", "metric": 911}], - } - return topotest.json_cmp(output, expected) - - def _non_exist_map_routes_not_present_no_rmap2_filter(router): - return _non_exist_map_routes_not_present(router) - - def _exist_map_routes_present_rmap2_filter(router): - return _non_exist_map_routes_not_present_rmap2_filter(router) - - def _exist_map_routes_present_no_rmap2_filter(router): - return _all_routes_advertised(router) - - def _non_exist_map_routes_present_rmap2_filter(router): - return _all_routes_withdrawn(router) - - def _non_exist_map_routes_present_no_rmap2_filter(router): - output = json.loads(router.vtysh_cmd("show ip route json")) - expected = { - "0.0.0.0/0": [{"protocol": "bgp"}], - "192.0.2.1/32": [{"protocol": "bgp"}], - "192.0.2.5/32": [{"protocol": "bgp"}], - "10.139.224.0/20": [{"protocol": "bgp"}], - "203.0.113.1/32": None, - } - return topotest.json_cmp(output, expected) - - def _exist_map_routes_present_rmap2_network(router): - return _non_exist_map_routes_not_present_rmap2_filter(router) - - def _exist_map_routes_present_rmap2_no_network(router): - return _all_routes_withdrawn(router) - - def _non_exist_map_routes_not_present_rmap2_network(router): - return _non_exist_map_routes_not_present_rmap2_filter(router) - - def _non_exist_map_routes_not_present_rmap2_no_network(router): - return _all_routes_withdrawn(router) - # TC11: R3 BGP convergence, without advertise-map configuration. # All routes are advertised to R3. - test_func = functools.partial(_all_routes_advertised, router3) + test_func = functools.partial(all_routes_advertised, router3) success, result = topotest.run_and_expect(test_func, None, count=130, wait=1) msg = 'TC11: "router3" BGP convergence - ' @@ -383,6 +408,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step2(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC21: exist-map routes present in R2's BGP table. # advertise-map routes present in R2's BGP table are advertised to R3. router2.vtysh_cmd( @@ -394,7 +429,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present, router3) + test_func = functools.partial(exist_map_routes_present, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = 'TC21: exist-map routes present in "router2" BGP table - ' @@ -402,6 +437,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step3(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC22: exist-map routes not present in R2's BGP table # advertise-map routes present in R2's BGP table are withdrawn from R3. router1.vtysh_cmd( @@ -413,7 +458,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_not_present, router3) + test_func = functools.partial(exist_map_routes_not_present, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = 'TC22: exist-map routes not present in "router2" BGP table - ' @@ -421,6 +466,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step4(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC31: non-exist-map routes not present in R2's BGP table # advertise-map routes present in R2's BGP table are advertised to R3. router2.vtysh_cmd( @@ -432,7 +487,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_non_exist_map_routes_not_present, router3) + test_func = functools.partial(non_exist_map_routes_not_present, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = 'TC31: non-exist-map routes not present in "router2" BGP table - ' @@ -440,6 +495,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step5(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC32: non-exist-map routes present in R2's BGP table # advertise-map routes present in R2's BGP table are withdrawn from R3. router1.vtysh_cmd( @@ -451,7 +516,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_non_exist_map_routes_present, router3) + test_func = functools.partial(non_exist_map_routes_present, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = 'TC32: non-exist-map routes present in "router2" BGP table - ' @@ -459,6 +524,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step6(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC41: non-exist-map route-map configuration removed in R2. # advertise-map routes present in R2's BGP table are advertised to R3. router2.vtysh_cmd( @@ -468,7 +543,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_non_exist_map_no_condition_route_map, router3) + test_func = functools.partial(non_exist_map_no_condition_route_map, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = 'TC41: non-exist-map route-map removed in "router2" - ' @@ -476,6 +551,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step7(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC42: exist-map route-map configuration removed in R2 # advertise-map routes present in R2's BGP table are withdrawn from R3. router2.vtysh_cmd( @@ -487,7 +572,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_no_condition_route_map, router3) + test_func = functools.partial(exist_map_no_condition_route_map, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = 'TC42: exist-map route-map removed in "router2" - ' @@ -495,6 +580,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step8(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC51: exist-map routes present in R2's BGP table, with route-map filter. # All routes are withdrawn from R3 except advertise-map routes. router2.vtysh_cmd( @@ -510,7 +605,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present_rmap_filter, router3) + test_func = functools.partial(exist_map_routes_present_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC51: exist-map routes present with route-map filter - " @@ -518,6 +613,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step9(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC52: exist-map routes present in R2's BGP table, no route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -529,7 +634,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present_no_rmap_filter, router3) + test_func = functools.partial(exist_map_routes_present_no_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC52: exist-map routes present, no route-map filter - " @@ -537,6 +642,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step10(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC53: non-exist-map routes present in R2's BGP table, with route-map filter. # All routes are withdrawn from R3 including advertise-map routes. router2.vtysh_cmd( @@ -549,7 +664,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_non_exist_map_routes_present_rmap_filter, router3) + test_func = functools.partial(non_exist_map_routes_present_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC53: non-exist-map routes present, with route-map filter - " @@ -557,6 +672,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step11(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC54: non-exist-map routes present in R2's BGP table, no route-map filter. # All routes are advertised to R3 except advertise-map routes. router2.vtysh_cmd( @@ -568,7 +693,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_non_exist_map_routes_present_no_rmap_filter, router3) + test_func = functools.partial(non_exist_map_routes_present_no_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC54: non-exist-map routes present, no route-map filter - " @@ -576,6 +701,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step12(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC61: exist-map routes not present in R2's BGP table, with route-map filter. # All routes are withdrawn from R3 including advertise-map routes. router1.vtysh_cmd( @@ -596,7 +731,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_not_present_rmap_filter, router3) + test_func = functools.partial(exist_map_routes_not_present_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC61: exist-map routes not present, route-map filter - " @@ -604,6 +739,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step13(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC62: exist-map routes not present in R2's BGP table, without route-map filter. # All routes are advertised to R3 except advertise-map routes. router2.vtysh_cmd( @@ -615,7 +760,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_not_present_no_rmap_filter, router3) + test_func = functools.partial(exist_map_routes_not_present_no_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC62: exist-map routes not present, no route-map filter - " @@ -623,6 +768,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step14(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC63: non-exist-map routes not present in R2's BGP table, with route-map filter. # All routes are withdrawn from R3 except advertise-map routes. router2.vtysh_cmd( @@ -635,9 +790,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial( - _non_exist_map_routes_not_present_rmap_filter, router3 - ) + test_func = functools.partial(non_exist_map_routes_not_present_rmap_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC63: non-exist-map routes not present, route-map filter - " @@ -645,6 +798,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step15(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC64: non-exist-map routes not present in R2's BGP table, without route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -657,7 +820,7 @@ def test_bgp_conditional_advertisement(): ) test_func = functools.partial( - _non_exist_map_routes_not_present_no_rmap_filter, router3 + non_exist_map_routes_not_present_no_rmap_filter, router3 ) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) @@ -666,6 +829,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step16(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC71: exist-map routes present in R2's BGP table, with route-map filter. # All routes are withdrawn from R3 except advertise-map routes. router1.vtysh_cmd( @@ -686,7 +859,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present_rmap2_filter, router3) + test_func = functools.partial(exist_map_routes_present_rmap2_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC71: exist-map routes present, route-map filter - " @@ -694,6 +867,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step17(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC72: exist-map routes present in R2's BGP table, without route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -705,7 +888,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present_no_rmap2_filter, router3) + test_func = functools.partial(exist_map_routes_present_no_rmap2_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC72: exist-map routes present, no route-map filter - " @@ -713,6 +896,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step18(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC73: non-exist-map routes present in R2's BGP table, with route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -725,7 +918,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_non_exist_map_routes_present_rmap2_filter, router3) + test_func = functools.partial(non_exist_map_routes_present_rmap2_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC73: non-exist-map routes present, route-map filter - " @@ -733,6 +926,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step19(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC74: non-exist-map routes present in R2's BGP table, without route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -744,9 +947,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial( - _non_exist_map_routes_present_no_rmap2_filter, router3 - ) + test_func = functools.partial(non_exist_map_routes_present_no_rmap2_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC74: non-exist-map routes present, no route-map filter - " @@ -754,6 +955,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step20(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC81: exist-map routes not present in R2's BGP table, with route-map filter. # All routes are withdrawn from R3 including advertise-map routes. router1.vtysh_cmd( @@ -774,7 +985,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_not_present_rmap2_filter, router3) + test_func = functools.partial(exist_map_routes_not_present_rmap2_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC81: exist-map routes not present, route-map filter - " @@ -782,6 +993,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step21(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC82: exist-map routes not present in R2's BGP table, without route-map filter. # All routes are advertised to R3 except advertise-map routes. router2.vtysh_cmd( @@ -793,9 +1014,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial( - _exist_map_routes_not_present_no_rmap2_filter, router3 - ) + test_func = functools.partial(exist_map_routes_not_present_no_rmap2_filter, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC82: exist-map routes not present, no route-map filter - " @@ -803,6 +1022,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step22(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC83: non-exist-map routes not present in R2's BGP table, with route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -816,7 +1045,7 @@ def test_bgp_conditional_advertisement(): ) test_func = functools.partial( - _non_exist_map_routes_not_present_rmap2_filter, router3 + non_exist_map_routes_not_present_rmap2_filter, router3 ) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) @@ -825,6 +1054,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step23(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC84: non-exist-map routes not present in R2's BGP table, without route-map filter. # All routes are advertised to R3 including advertise-map routes. router2.vtysh_cmd( @@ -837,7 +1076,7 @@ def test_bgp_conditional_advertisement(): ) test_func = functools.partial( - _non_exist_map_routes_not_present_no_rmap2_filter, router3 + non_exist_map_routes_not_present_no_rmap2_filter, router3 ) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) @@ -846,6 +1085,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step24(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC91: exist-map routes present in R2's BGP table, with route-map filter and network. # All routes are advertised to R3 including advertise-map routes. router1.vtysh_cmd( @@ -866,7 +1115,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present_rmap2_network, router3) + test_func = functools.partial(exist_map_routes_present_rmap2_network, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC91: exist-map routes present, route-map filter and network - " @@ -874,6 +1123,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step25(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC92: exist-map routes present in R2's BGP table, with route-map filter and no network. # All routes are advertised to R3 except advertise-map routes. router2.vtysh_cmd( @@ -885,7 +1144,7 @@ def test_bgp_conditional_advertisement(): """ ) - test_func = functools.partial(_exist_map_routes_present_rmap2_no_network, router3) + test_func = functools.partial(exist_map_routes_present_rmap2_no_network, router3) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) msg = "TC92: exist-map routes present, route-map filter and no network - " @@ -893,6 +1152,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step26(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC93: non-exist-map routes not present in R2's BGP table, with route-map filter and network. # All routes are advertised to R3 including advertise-map routes. router1.vtysh_cmd( @@ -914,7 +1183,7 @@ def test_bgp_conditional_advertisement(): ) test_func = functools.partial( - _non_exist_map_routes_not_present_rmap2_network, router3 + non_exist_map_routes_not_present_rmap2_network, router3 ) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) @@ -923,6 +1192,16 @@ def test_bgp_conditional_advertisement(): logger.info(msg + passed) + +def test_bgp_conditional_advertisement_step27(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + # TC94: non-exist-map routes not present in R2's BGP table, with route-map filter and no network. # All routes are advertised to R3 except advertise-map routes. router2.vtysh_cmd( @@ -935,7 +1214,7 @@ def test_bgp_conditional_advertisement(): ) test_func = functools.partial( - _non_exist_map_routes_not_present_rmap2_no_network, router3 + non_exist_map_routes_not_present_rmap2_no_network, router3 ) success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) diff --git a/tests/topotests/bgp_ipv4_class_e_peer/__init__.py b/tests/topotests/bgp_ipv4_class_e_peer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/__init__.py diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf b/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf new file mode 100644 index 0000000000..bf0a68eea2 --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf @@ -0,0 +1,12 @@ +! +allow-reserved-ranges +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 240.0.0.2 remote-as external + neighbor 240.0.0.2 timers 1 3 + neighbor 240.0.0.2 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf b/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf new file mode 100644 index 0000000000..d4ac46f248 --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf @@ -0,0 +1,11 @@ +! +allow-reserved-ranges +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 240.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf b/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf new file mode 100644 index 0000000000..7d08963633 --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf @@ -0,0 +1,9 @@ +! +allow-reserved-ranges +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 240.0.0.1 remote-as external + neighbor 240.0.0.1 timers 1 3 + neighbor 240.0.0.1 timers connect 1 +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf b/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf new file mode 100644 index 0000000000..f0a350fcea --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf @@ -0,0 +1,8 @@ +! +allow-reserved-ranges +! +interface r2-eth0 + ip address 240.0.0.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py new file mode 100644 index 0000000000..c2f0d2e0dc --- /dev/null +++ b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python + +# +# bgp_ipv4_class_e_peer.py +# +# Copyright (c) 2022 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# +# 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. +# + +""" +Check if the peering works by using IPv4 Class E IP ranges, and if +we don't treat next-hop as martian in such a case. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + 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)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_ipv4_class_e_peer(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 240.0.0.1 json")) + expected = { + "240.0.0.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_next_hop_ipv4_class_e(): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "nexthops": [ + { + "ip": "240.0.0.1", + "accessible": True, + } + ], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + step("Check if IPv4 BGP peering works with Class E IP ranges") + test_func = functools.partial(_bgp_next_hop_ipv4_class_e) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see 172.16.255.1/32 via 240.0.0.1 on R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 0e9e0ba403..4afa86f740 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -3437,8 +3437,23 @@ def verify_rib( found_hops = [ rib_r["ip"] for rib_r in rib_routes_json[st_rt][0]["nexthops"] + if "ip" in rib_r ] + # If somehow key "ip" is not found in nexthops JSON + # then found_hops would be 0, this particular + # situation will be handled here + if not len(found_hops): + errormsg = ( + "Nexthop {} is Missing for " + "route {} in RIB of router {}\n".format( + next_hop, + st_rt, + dut, + ) + ) + return errormsg + # Check only the count of nexthops if count_only: if len(next_hop) == len(found_hops): diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index a52bd7b116..21bd2f4883 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -3140,6 +3140,20 @@ DEFUN(vtysh_debug_uid_backtrace, return err; } +DEFUNSH(VTYSH_ALL, vtysh_allow_reserved_ranges, vtysh_allow_reserved_ranges_cmd, + "allow-reserved-ranges", + "Allow using IPv4 (Class E) reserved IP space\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_ALL, no_vtysh_allow_reserved_ranges, + no_vtysh_allow_reserved_ranges_cmd, "no allow-reserved-ranges", + NO_STR "Allow using IPv4 (Class E) reserved IP space\n") +{ + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ALL, vtysh_service_password_encrypt, vtysh_service_password_encrypt_cmd, "service password-encryption", "Set up miscellaneous service\n" @@ -4902,6 +4916,9 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &vtysh_service_password_encrypt_cmd); install_element(CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd); + install_element(CONFIG_NODE, &vtysh_allow_reserved_ranges_cmd); + install_element(CONFIG_NODE, &no_vtysh_allow_reserved_ranges_cmd); + install_element(CONFIG_NODE, &vtysh_password_cmd); install_element(CONFIG_NODE, &no_vtysh_password_cmd); install_element(CONFIG_NODE, &vtysh_enable_password_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 3bd5489eef..a7ec2a93c2 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -478,14 +478,18 @@ void vtysh_config_parse_line(void *arg, const char *line) else if (strncmp(line, "rpki", strlen("rpki")) == 0) config = config_get(RPKI_NODE, line); else { - if (strncmp(line, "log", strlen("log")) == 0 - || strncmp(line, "hostname", strlen("hostname")) == 0 - || strncmp(line, "domainname", strlen("domainname")) == 0 - || strncmp(line, "frr", strlen("frr")) == 0 - || strncmp(line, "agentx", strlen("agentx")) == 0 - || strncmp(line, "no log", strlen("no log")) == 0 - || strncmp(line, "no ip prefix-list", strlen("no ip prefix-list")) == 0 - || strncmp(line, "no ipv6 prefix-list", strlen("no ipv6 prefix-list")) == 0) + if (strncmp(line, "log", strlen("log")) == 0 || + strncmp(line, "hostname", strlen("hostname")) == + 0 || + strncmp(line, "domainname", strlen("domainname")) == + 0 || + strncmp(line, "frr", strlen("frr")) == 0 || + strncmp(line, "agentx", strlen("agentx")) == 0 || + strncmp(line, "no log", strlen("no log")) == 0 || + strncmp(line, "no ip prefix-list", + strlen("no ip prefix-list")) == 0 || + strncmp(line, "no ipv6 prefix-list", + strlen("no ipv6 prefix-list")) == 0) config_add_line_uniq(config_top, line); else config_add_line(config_top, line); diff --git a/zebra/interface.c b/zebra/interface.c index 9800e64503..de7db2d48f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1504,7 +1504,7 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: can't find zns id %u", __func__, ns_id); - goto done; + return; } ifp = if_lookup_by_index_per_ns(zns, ifindex); @@ -1516,7 +1516,7 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) "%s: can't find ifp at nsid %u index %d", __func__, ns_id, ifindex); - goto done; + return; } } @@ -1578,8 +1578,6 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_GRE_SET: break; /* should never hit here */ } -done: - dplane_ctx_fini(&ctx); } /* Dump if address information to vty. */ diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 4bd0ac27f6..31d8294a0f 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -290,9 +290,20 @@ static int netlink_recvbuf(struct nlsock *nl, uint32_t newsize) return 0; } +static const char *group2str(uint32_t group) +{ + switch (group) { + case RTNLGRP_TUNNEL: + return "RTNLGRP_TUNNEL"; + default: + return "UNKNOWN"; + } +} + /* Make socket for Linux netlink interface. */ static int netlink_socket(struct nlsock *nl, unsigned long groups, - unsigned long ext_groups, ns_id_t ns_id) + uint32_t ext_groups[], uint8_t ext_group_size, + ns_id_t ns_id) { int ret; struct sockaddr_nl snl; @@ -311,18 +322,30 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, snl.nl_family = AF_NETLINK; snl.nl_groups = groups; + if (ext_group_size) { + uint8_t i; + + for (i = 0; i < ext_group_size; i++) { #if defined SOL_NETLINK - if (ext_groups) { - ret = setsockopt(sock, SOL_NETLINK, - NETLINK_ADD_MEMBERSHIP, &ext_groups, - sizeof(ext_groups)); - if (ret < 0) { + ret = setsockopt(sock, SOL_NETLINK, + NETLINK_ADD_MEMBERSHIP, + &ext_groups[i], + sizeof(ext_groups[i])); + if (ret < 0) { + zlog_notice( + "can't setsockopt NETLINK_ADD_MEMBERSHIP for group %s(%u), this linux kernel does not support it: %s(%d)", + group2str(ext_groups[i]), + ext_groups[i], + safe_strerror(errno), errno); + } +#else zlog_notice( - "can't setsockopt NETLINK_ADD_MEMBERSHIP: %s(%d)", - safe_strerror(errno), errno); + "Unable to use NETLINK_ADD_MEMBERSHIP via SOL_NETLINK for %s(%u) since the linux kernel does not support the socket option", + group2str(ext_groups[i]), + ext_groups[i]); +#endif } } -#endif /* Bind the socket to the netlink structure for anything. */ ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); @@ -1734,7 +1757,8 @@ void kernel_init(struct zebra_ns *zns) snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); zns->netlink.sock = -1; - if (netlink_socket(&zns->netlink, groups, ext_groups, zns->ns_id) < 0) { + if (netlink_socket(&zns->netlink, groups, &ext_groups, 1, zns->ns_id) < + 0) { zlog_err("Failure to create %s socket", zns->netlink.name); exit(-1); @@ -1745,7 +1769,7 @@ void kernel_init(struct zebra_ns *zns) snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name), "netlink-cmd (NS %u)", zns->ns_id); zns->netlink_cmd.sock = -1; - if (netlink_socket(&zns->netlink_cmd, 0, 0, zns->ns_id) < 0) { + if (netlink_socket(&zns->netlink_cmd, 0, 0, 0, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", zns->netlink_cmd.name); exit(-1); @@ -1758,7 +1782,7 @@ void kernel_init(struct zebra_ns *zns) sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)", zns->ns_id); zns->netlink_dplane_out.sock = -1; - if (netlink_socket(&zns->netlink_dplane_out, 0, 0, zns->ns_id) < 0) { + if (netlink_socket(&zns->netlink_dplane_out, 0, 0, 0, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", zns->netlink_dplane_out.name); exit(-1); @@ -1771,7 +1795,7 @@ void kernel_init(struct zebra_ns *zns) sizeof(zns->netlink_dplane_in.name), "netlink-dp-in (NS %u)", zns->ns_id); zns->netlink_dplane_in.sock = -1; - if (netlink_socket(&zns->netlink_dplane_in, dplane_groups, 0, + if (netlink_socket(&zns->netlink_dplane_in, dplane_groups, 0, 0, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", zns->netlink_dplane_in.name); diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 57fd304ae8..2741a23242 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1354,6 +1354,16 @@ static void kernel_read(struct thread *thread) if (nbytes < 0) { if (errno == ENOBUFS) { +#ifdef __FreeBSD__ + /* + * ENOBUFS indicates a temporary resource + * shortage and is not harmful for consistency of + * reading the routing socket. Ignore it. + */ + thread_add_read(zrouter.master, kernel_read, NULL, sock, + NULL); + return; +#else flog_err(EC_ZEBRA_RECVMSG_OVERRUN, "routing socket overrun: %s", safe_strerror(errno)); @@ -1363,6 +1373,7 @@ static void kernel_read(struct thread *thread) * recover zebra at this point. */ exit(-1); +#endif } if (errno != EAGAIN && errno != EWOULDBLOCK) flog_err_sys(EC_LIB_SOCKET, "routing socket error: %s", diff --git a/zebra/main.c b/zebra/main.c index e516688a19..46cf2eea7d 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -71,9 +71,6 @@ struct thread_master *master; /* Route retain mode flag. */ int retain_mode = 0; -/* Allow non-frr entities to delete frr routes */ -int allow_delete = 0; - int graceful_restart; bool v6_rr_semantics = false; @@ -336,7 +333,7 @@ int main(int argc, char **argv) // batch_mode = 1; break; case 'a': - allow_delete = 1; + zrouter.allow_delete = true; break; case 'e': { unsigned long int parsed_multipath = diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 21c991c7db..ce06f1683d 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -341,6 +341,22 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) case RTPROT_SRTE: proto = ZEBRA_ROUTE_SRTE; break; + case RTPROT_UNSPEC: + case RTPROT_REDIRECT: + case RTPROT_KERNEL: + case RTPROT_BOOT: + case RTPROT_GATED: + case RTPROT_RA: + case RTPROT_MRT: + case RTPROT_BIRD: + case RTPROT_DNROUTED: + case RTPROT_XORP: + case RTPROT_NTK: + case RTPROT_MROUTED: + case RTPROT_KEEPALIVED: + case RTPROT_OPENR: + proto = ZEBRA_ROUTE_KERNEL; + break; case RTPROT_ZEBRA: if (is_nexthop) { proto = ZEBRA_ROUTE_NHG; @@ -977,8 +993,19 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (nhe_id || ng) rib_add_multipath(afi, SAFI_UNICAST, &p, &src_p, re, ng, startup); - else + else { + /* + * I really don't see how this is possible + * but since we are testing for it let's + * let the end user know why the route + * that was just received was swallowed + * up and forgotten + */ + zlog_err( + "%s: %pFX multipath RTM_NEWROUTE has a invalid nexthop group from the kernel", + __func__, &p); XFREE(MTYPE_RE, re); + } } } else { if (nhe_id) { diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 8237bebf3b..772e30833c 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1855,8 +1855,6 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) break; } /* Switch */ - - dplane_ctx_fini(&ctx); } /* @@ -2064,7 +2062,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) /* Look for zebra LSP object */ zvrf = vrf_info_lookup(VRF_DEFAULT); if (zvrf == NULL) - goto done; + return; lsp_table = zvrf->lsp_table; @@ -2074,7 +2072,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (is_debug) zlog_debug("dplane LSP notif: in-label %u not found", dplane_ctx_get_in_label(ctx)); - goto done; + return; } /* @@ -2147,9 +2145,6 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); clear_nhlfe_installed(lsp); } - -done: - dplane_ctx_fini(&ctx); } /* diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 9a0f48158f..8a255981b7 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -3076,8 +3076,6 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_DELETE: break; } - - dplane_ctx_fini(&ctx); } static int zebra_nhg_sweep_entry(struct hash_bucket *bucket, void *arg) diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index e6424fea4f..43e21a6d34 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -1068,9 +1068,6 @@ void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx) EC_ZEBRA_PBR_RULE_UPDATE, "Context received in pbr rule dplane result handler with incorrect OP code (%u)", op); - - - dplane_ctx_fini(&ctx); } /* diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 63f15b0f20..d92a4c2365 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -77,9 +77,6 @@ static struct dplane_ctx_q rib_dplane_q; DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)); -/* Should we allow non FRR processes to delete our routes */ -extern int allow_delete; - /* Each route type's string and default distance value. */ static const struct { int key; @@ -167,6 +164,30 @@ struct wq_evpn_wrapper { #pragma FRR printfrr_ext "%pZN" (struct route_node *) #endif +static const char *subqueue2str(uint8_t index) +{ + switch (index) { + case 0: + return "NHG Objects"; + case 1: + return "EVPN/VxLan Objects"; + case 2: + return "Connected Routes"; + case 3: + return "Kernel Routes"; + case 4: + return "Static Routes"; + case 5: + return "RIP/OSPF/ISIS/EIGRP/NHRP Routes"; + case 6: + return "BGP Routes"; + case 7: + return "Other Routes"; + } + + return "Unknown"; +} + printfrr_ext_autoreg_p("ZN", printfrr_zebra_node); static ssize_t printfrr_zebra_node(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) @@ -2087,9 +2108,6 @@ done: if (rn) route_unlock_node(rn); - - /* Return context to dataplane module */ - dplane_ctx_fini(&ctx); } /* @@ -2323,9 +2341,6 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) done: if (rn) route_unlock_node(rn); - - /* Return context to dataplane module */ - dplane_ctx_fini(&ctx); } /* @@ -2407,8 +2422,8 @@ static void process_subq_nhg(struct listnode *lnode) if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( - "NHG Context id=%u dequeued from sub-queue %u", - ctx->id, qindex); + "NHG Context id=%u dequeued from sub-queue %s", + ctx->id, subqueue2str(qindex)); /* Process nexthop group updates coming 'up' from the OS */ @@ -2418,8 +2433,8 @@ static void process_subq_nhg(struct listnode *lnode) nhe = w->u.nhe; if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("NHG %u dequeued from sub-queue %u", - nhe->id, qindex); + zlog_debug("NHG %u dequeued from sub-queue %s", nhe->id, + subqueue2str(qindex)); /* Process incoming nhg update, probably from a proto daemon */ newnhe = zebra_nhg_proto_add(nhe->id, nhe->type, @@ -2465,9 +2480,9 @@ static void process_subq_route(struct listnode *lnode, uint8_t qindex) if (dest) re = re_list_first(&dest->routes); - zlog_debug("%s(%u:%u):%pRN rn %p dequeued from sub-queue %u", + zlog_debug("%s(%u:%u):%pRN rn %p dequeued from sub-queue %s", zvrf_name(zvrf), zvrf_id(zvrf), re ? re->table : 0, - rnode, rnode, qindex); + rnode, rnode, subqueue2str(qindex)); } if (rnode->info) @@ -2578,8 +2593,8 @@ static int rib_meta_queue_add(struct meta_queue *mq, void *data) RIB_ROUTE_QUEUED(qindex))) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) rnode_debug(rn, re->vrf_id, - "rn %p is already queued in sub-queue %u", - (void *)rn, qindex); + "rn %p is already queued in sub-queue %s", + (void *)rn, subqueue2str(qindex)); return -1; } @@ -2589,8 +2604,8 @@ static int rib_meta_queue_add(struct meta_queue *mq, void *data) mq->size++; if (IS_ZEBRA_DEBUG_RIB_DETAILED) - rnode_debug(rn, re->vrf_id, "queued rn %p into sub-queue %u", - (void *)rn, qindex); + rnode_debug(rn, re->vrf_id, "queued rn %p into sub-queue %s", + (void *)rn, subqueue2str(qindex)); return 0; } @@ -2615,8 +2630,8 @@ static int rib_meta_queue_nhg_ctx_add(struct meta_queue *mq, void *data) mq->size++; if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("NHG Context id=%u queued into sub-queue %u", - ctx->id, qindex); + zlog_debug("NHG Context id=%u queued into sub-queue %s", + ctx->id, subqueue2str(qindex)); return 0; } @@ -2641,8 +2656,8 @@ static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data) mq->size++; if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("NHG id=%u queued into sub-queue %u", - nhe->id, qindex); + zlog_debug("NHG id=%u queued into sub-queue %s", nhe->id, + subqueue2str(qindex)); return 0; } @@ -3722,8 +3737,8 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, rn, fib, zebra_route_string(fib->type)); } - if (allow_delete - || CHECK_FLAG(dest->flags, RIB_ROUTE_ANY_QUEUED)) { + if (zrouter.allow_delete || + CHECK_FLAG(dest->flags, RIB_ROUTE_ANY_QUEUED)) { UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED); /* Unset flags. */ for (rtnh = fib->nhe->nhg.nexthop; rtnh; @@ -3768,8 +3783,8 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (same) { struct nexthop *tmp_nh; - if (fromkernel && CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) - && !allow_delete) { + if (fromkernel && CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) && + !zrouter.allow_delete) { rib_install_kernel(rn, same, NULL); route_unlock_node(rn); @@ -4191,7 +4206,7 @@ void rib_close_table(struct route_table *table) /* * Handler for async dataplane results after a pseudowire installation */ -static int handle_pw_result(struct zebra_dplane_ctx *ctx) +static void handle_pw_result(struct zebra_dplane_ctx *ctx) { struct zebra_pw *pw; struct zebra_vrf *vrf; @@ -4200,7 +4215,7 @@ static int handle_pw_result(struct zebra_dplane_ctx *ctx) * result for installation attempts here. */ if (dplane_ctx_get_op(ctx) != DPLANE_OP_PW_INSTALL) - goto done; + return; if (dplane_ctx_get_status(ctx) != ZEBRA_DPLANE_REQUEST_SUCCESS) { vrf = zebra_vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); @@ -4209,14 +4224,8 @@ static int handle_pw_result(struct zebra_dplane_ctx *ctx) zebra_pw_install_failure(pw, dplane_ctx_get_pw_status(ctx)); } - -done: - dplane_ctx_fini(&ctx); - - return 0; } - /* * Handle results from the dataplane system. Dequeue update context * structs, dispatch to appropriate internal handlers. @@ -4289,7 +4298,6 @@ static void rib_process_dplane_results(struct thread *thread) case DPLANE_OP_ROUTE_INSTALL: case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: - { /* Bit of special case for route updates * that were generated by async notifications: * we don't want to continue processing these @@ -4297,10 +4305,7 @@ static void rib_process_dplane_results(struct thread *thread) */ if (dplane_ctx_get_notif_provider(ctx) == 0) rib_process_result(ctx); - else - dplane_ctx_fini(&ctx); - } - break; + break; case DPLANE_OP_ROUTE_NOTIFY: rib_process_dplane_notify(ctx); @@ -4315,17 +4320,13 @@ static void rib_process_dplane_results(struct thread *thread) case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: - { /* Bit of special case for LSP updates * that were generated by async notifications: * we don't want to continue processing these. */ if (dplane_ctx_get_notif_provider(ctx) == 0) zebra_mpls_lsp_dplane_result(ctx); - else - dplane_ctx_fini(&ctx); - } - break; + break; case DPLANE_OP_LSP_NOTIFY: zebra_mpls_process_dplane_notify(ctx); @@ -4338,8 +4339,6 @@ static void rib_process_dplane_results(struct thread *thread) case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: - /* No further processing in zebra for these. */ - dplane_ctx_fini(&ctx); break; case DPLANE_OP_MAC_INSTALL: @@ -4383,12 +4382,11 @@ static void rib_process_dplane_results(struct thread *thread) case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_GRE_SET: case DPLANE_OP_NONE: - /* Don't expect this: just return the struct? */ - dplane_ctx_fini(&ctx); break; } /* Dispatch by op code */ + dplane_ctx_fini(&ctx); ctx = dplane_ctx_dequeue(&ctxlist); } diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 9fccda9e08..f7ad30b41f 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -278,6 +278,8 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack) { zrouter.sequence_num = 0; + zrouter.allow_delete = false; + zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS; zrouter.nhg_keep = ZEBRA_DEFAULT_NHG_KEEP_TIMER; diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index d51e7a2b7d..0e2725c977 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -229,6 +229,9 @@ struct zebra_router { #define ZEBRA_DEFAULT_NHG_KEEP_TIMER 180 uint32_t nhg_keep; + + /* Should we allow non FRR processes to delete our routes */ + bool allow_delete; }; #define GRACEFUL_RESTART_TIME 60 diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 6ae55cdba4..e6038d0bc2 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -66,8 +66,6 @@ #include "zebra/rtadv.h" #include "zebra/zebra_neigh.h" -extern int allow_delete; - /* context to manage dumps in multiple tables or vrfs */ struct route_show_ctx { bool multi; /* dump multiple tables or vrf */ @@ -2699,7 +2697,7 @@ DEFUN (allow_external_route_update, "allow-external-route-update", "Allow FRR routes to be overwritten by external processes\n") { - allow_delete = 1; + zrouter.allow_delete = true; return CMD_SUCCESS; } @@ -2710,7 +2708,7 @@ DEFUN (no_allow_external_route_update, NO_STR "Allow FRR routes to be overwritten by external processes\n") { - allow_delete = 0; + zrouter.allow_delete = false; return CMD_SUCCESS; } @@ -3911,7 +3909,7 @@ DEFPY (zebra_nexthop_group_keep, static int config_write_protocol(struct vty *vty) { - if (allow_delete) + if (zrouter.allow_delete) vty_out(vty, "allow-external-route-update\n"); if (zrouter.nhg_keep != ZEBRA_DEFAULT_NHG_KEEP_TIMER) @@ -4011,6 +4009,8 @@ DEFUN (show_zebra, ttable_add_row(table, "Kernel NHG|%s", zrouter.supports_nhgs ? "Available" : "Unavailable"); + ttable_add_row(table, "Allow Non FRR route deletion|%s", + zrouter.allow_delete ? "Yes" : "No"); ttable_add_row(table, "v4 All LinkDown Routes|%s", zrouter.all_linkdownv4 ? "On" : "Off"); ttable_add_row(table, "v4 Default LinkDown Routes|%s", diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 4cf309f7fc..dbe1ce3e46 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -6260,8 +6260,7 @@ static int zebra_evpn_cfg_clean_up(struct zserv *client) */ extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx) { - /* TODO -- anything other than freeing the context? */ - dplane_ctx_fini(&ctx); + return; } /* Cleanup BGP EVPN configuration upon client disconnect */ |
