diff --git a/.gitignore b/.gitignore index 30006f7e28..d592e8b3f3 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,10 @@ cscope.* *.pb.h *.pb-c.h *.pb-c.c +TAGS +tags +GTAGS +GSYMS +GRTAGS +GPATH + diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index ad221d922d..3def3421a8 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -171,6 +171,9 @@ bgp_bfd_deregister_peer (struct peer *peer) if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG)) return; + bfd_info->status = BFD_STATUS_DOWN; + bfd_info->last_update = bgp_clock(); + bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER); } @@ -311,14 +314,14 @@ bgp_bfd_dest_update (int command, struct zclient *zclient, prefix2str(&dp, buf[0], sizeof(buf[0])); if (ifp) { - zlog_debug("Zebra: interface %s bfd destination %s %s", - ifp->name, buf[0], bfd_get_status_str(status)); + zlog_debug("Zebra: vrf %d interface %s bfd destination %s %s", + vrf_id, ifp->name, buf[0], bfd_get_status_str(status)); } else { prefix2str(&sp, buf[1], sizeof(buf[1])); - zlog_debug("Zebra: source %s bfd destination %s %s", - buf[1], buf[0], bfd_get_status_str(status)); + zlog_debug("Zebra: vrf %d source %s bfd destination %s %s", + vrf_id, buf[1], buf[0], bfd_get_status_str(status)); } } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a27a5f6b26..79e5a0c332 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -4714,6 +4714,7 @@ peer_update_source_vty (struct vty *vty, const char *peer_str, const char *source_str) { struct peer *peer; + struct prefix p; peer = peer_and_group_lookup_vty (vty, peer_str); if (! peer) @@ -4730,7 +4731,16 @@ peer_update_source_vty (struct vty *vty, const char *peer_str, if (ret == 0) peer_update_source_addr_set (peer, &su); else - peer_update_source_if_set (peer, source_str); + { + if (str2prefix (source_str, &p)) + { + vty_out (vty, "%% Invalid update-source, remove prefix length %s", + VTY_NEWLINE); + return CMD_WARNING; + } + else + peer_update_source_if_set (peer, source_str); + } } else peer_update_source_unset (peer); @@ -11907,11 +11917,19 @@ bgp_show_peer (struct vty *vty, struct peer *p, u_char use_json, json_object *js tm = gmtime(&uptime); json_object_int_add(json_neigh, "lastResetTimerMsecs", (tm->tm_sec * 1000) + (tm->tm_min * 60000) + (tm->tm_hour * 3600000)); json_object_string_add(json_neigh, "lastResetDueTo", peer_down_str[(int) p->last_reset]); - if (p->last_reset_cause_size) + if (p->last_reset == PEER_DOWN_NOTIFY_SEND || + p->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { char errorcodesubcode_hexstr[5]; + char errorcodesubcode_str[256]; + + code_str = bgp_notify_code_str(p->notify.code); + subcode_str = bgp_notify_subcode_str(p->notify.code, p->notify.subcode); + sprintf(errorcodesubcode_hexstr, "%02X%02X", p->notify.code, p->notify.subcode); json_object_string_add(json_neigh, "lastErrorCodeSubcode", errorcodesubcode_hexstr); + snprintf(errorcodesubcode_str, 255, "%s%s", code_str, subcode_str); + json_object_string_add(json_neigh, "lastNotificationReason", errorcodesubcode_str); } } else diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 244b1930b3..ef633c16e7 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -6680,26 +6680,27 @@ bgp_config_write_peer_global (struct vty *vty, struct bgp *bgp, } /* advertisement-interval */ - if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV) - && peer->v_routeadv != BGP_DEFAULT_EBGP_ROUTEADV - && ! peer_group_active (peer)) + if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV) && + ((! peer_group_active (peer) && peer->v_routeadv != BGP_DEFAULT_EBGP_ROUTEADV) || + (peer_group_active (peer) && peer->v_routeadv != g_peer->v_routeadv))) { vty_out (vty, " neighbor %s advertisement-interval %d%s", addr, peer->v_routeadv, VTY_NEWLINE); } /* timers */ - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER) - && (peer->keepalive != BGP_DEFAULT_KEEPALIVE || peer->holdtime != BGP_DEFAULT_HOLDTIME) - && ! peer_group_active (peer)) + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER) && + ((! peer_group_active (peer) && (peer->keepalive != BGP_DEFAULT_KEEPALIVE || peer->holdtime != BGP_DEFAULT_HOLDTIME)) || + (peer_group_active (peer) && (peer->keepalive != g_peer->keepalive || peer->holdtime != g_peer->holdtime)))) { vty_out (vty, " neighbor %s timers %d %d%s", addr, peer->keepalive, peer->holdtime, VTY_NEWLINE); } if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT) && - peer->connect != BGP_DEFAULT_CONNECT_RETRY && - ! peer_group_active (peer)) + ((! peer_group_active (peer) && peer->connect != BGP_DEFAULT_CONNECT_RETRY) || + (peer_group_active (peer) && peer->connect != g_peer->connect))) + { vty_out (vty, " neighbor %s timers connect %d%s", addr, peer->connect, VTY_NEWLINE); diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 463784de11..1cad55ac34 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -38,7 +38,7 @@ import string import subprocess import sys from collections import OrderedDict -from ipaddr import IPv6Address +from ipaddr import IPv6Address, IPNetwork from pprint import pformat @@ -173,6 +173,100 @@ class Config(object): if not key: return + ''' + IP addresses specified in "network" statements, "ip prefix-lists" + etc. can differ in the host part of the specification the user + provides and what the running config displays. For example, user + can specify 11.1.1.1/24, and the running config displays this as + 11.1.1.0/24. Ensure we don't do a needless operation for such + lines. IS-IS & OSPFv3 have no "network" support. + ''' + re_key_rt = re.match(r'(ip|ipv6)\s+route\s+([A-Fa-f:.0-9/]+)(.*)$', key[0]) + if re_key_rt: + addr = re_key_rt.group(2) + if '/' in addr: + try: + newaddr = IPNetwork(addr) + key[0] = '%s route %s/%s%s' % (re_key_rt.group(1), + newaddr.network, + newaddr.prefixlen, + re_key_rt.group(3)) + except ValueError: + pass + + re_key_rt = re.match( + r'(ip|ipv6)\s+prefix-list(.*)(permit|deny)\s+([A-Fa-f:.0-9/]+)(.*)$', + key[0] + ) + if re_key_rt: + addr = re_key_rt.group(4) + if '/' in addr: + try: + newaddr = '%s/%s' % (IPNetwork(addr).network, + IPNetwork(addr).prefixlen) + except ValueError: + newaddr = addr + else: + newaddr = addr + + legestr = re_key_rt.group(5) + re_lege = re.search(r'(.*)le\s+(\d+)\s+ge\s+(\d+)(.*)', legestr) + if re_lege: + legestr = '%sge %s le %s%s' % (re_lege.group(1), + re_lege.group(3), + re_lege.group(2), + re_lege.group(4)) + re_lege = re.search(r'(.*)ge\s+(\d+)\s+le\s+(\d+)(.*)', legestr) + + if (re_lege and ((re_key_rt.group(1) == "ip" and + re_lege.group(3) == "32") or + (re_key_rt.group(1) == "ipv6" and + re_lege.group(3) == "128"))): + legestr = '%sge %s%s' % (re_lege.group(1), + re_lege.group(2), + re_lege.group(4)) + + key[0] = '%s prefix-list%s%s %s%s' % (re_key_rt.group(1), + re_key_rt.group(2), + re_key_rt.group(3), + newaddr, + legestr) + + if lines and key[0].startswith('router bgp'): + newlines = [] + for line in lines: + re_net = re.match(r'network\s+([A-Fa-f:.0-9/]+)(.*)$', line) + if re_net: + addr = re_net.group(1) + if '/' not in addr and key[0].startswith('router bgp'): + # This is most likely an error because with no + # prefixlen, BGP treats the prefixlen as 8 + addr = addr + '/8' + + try: + newaddr = IPNetwork(addr) + line = 'network %s/%s %s' % (newaddr.network, + newaddr.prefixlen, + re_net.group(2)) + newlines.append(line) + except ValueError: + # Really this should be an error. Whats a network + # without an IP Address following it ? + newlines.append(line) + else: + newlines.append(line) + lines = newlines + + ''' + More fixups in user specification and what running config shows. + "null0" in routes must be replaced by Null0, and "blackhole" must + be replaced by Null0 as well. + ''' + if (key[0].startswith('ip route') or key[0].startswith('ipv6 route') and + 'null0' in key[0] or 'blackhole' in key[0]): + key[0] = re.sub(r'\s+null0(\s*$)', ' Null0', key[0]) + key[0] = re.sub(r'\s+blackhole(\s*$)', ' Null0', key[0]) + if lines: if tuple(key) not in self.contexts: ctx = Context(tuple(key), lines) @@ -437,16 +531,25 @@ def get_normalized_ipv6_line(line): """ Return a normalized IPv6 line as produced by frr, with all letters in lower case and trailing and leading - zeros removed + zeros removed, and only the network portion present if + the IPv6 word is a network """ norm_line = "" words = line.split(' ') for word in words: if ":" in word: - try: - norm_word = str(IPv6Address(word)).lower() - except: - norm_word = word + norm_word = None + if "/" in word: + try: + v6word = IPNetwork(word) + norm_word = '%s/%s' % (v6word.network, v6word.prefixlen) + except ValueError: + pass + if not norm_word: + try: + norm_word = '%s' % IPv6Address(word) + except ValueError: + norm_word = word else: norm_word = word norm_line = norm_line + " " + norm_word @@ -579,6 +682,60 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_add_to_del.append((ctx_keys, swpx_interface)) lines_to_add_to_del.append((tmp_ctx_keys, swpx_remoteas)) + ''' + In 3.0, we made bgp bestpath multipath as-relax command + automatically assume no-as-set since the lack of this option caused + weird routing problems and this problem was peculiar to this + implementation. When the running config is shown in relases after + 3.0, the no-as-set is not shown as its the default. This causes + reload to unnecessarily unapply this option to only apply it back + again, causing unnecessary session resets. Handle this. + ''' + if ctx_keys[0].startswith('router bgp') and line and 'multipath-relax' in line: + re_asrelax_new = re.search('^bgp\s+bestpath\s+as-path\s+multipath-relax$', line) + old_asrelax_cmd = 'bgp bestpath as-path multipath-relax no-as-set' + found_asrelax_old = line_exist(lines_to_add, ctx_keys, old_asrelax_cmd) + + if re_asrelax_new and found_asrelax_old: + deleted = True + lines_to_del_to_del.append((ctx_keys, line)) + lines_to_add_to_del.append((ctx_keys, old_asrelax_cmd)) + + ''' + More old-to-new config handling. ip import-table no longer accepts + distance, but we honor the old syntax. But 'show running' shows only + the new syntax. This causes an unnecessary 'no import-table' followed + by the same old 'ip import-table' which causes perturbations in + announced routes leading to traffic blackholes. Fix this issue. + ''' + re_importtbl = re.search('^ip\s+import-table\s+(\d+)$', ctx_keys[0]) + if re_importtbl: + table_num = re_importtbl.group(1) + for ctx in lines_to_add: + if ctx[0][0].startswith('ip import-table %s distance' % table_num): + lines_to_del_to_del.append((('ip import-table %s' % table_num,), None)) + lines_to_add_to_del.append((ctx[0], None)) + + ''' + ip/ipv6 prefix-list can be specified without a seq number. However, + the running config always adds 'seq x', where x is a number incremented + by 5 for every element, to the prefix list. So, ignore such lines as + well. Sample prefix-list lines: + ip prefix-list PR-TABLE-2 seq 5 permit 20.8.2.0/24 le 32 + ip prefix-list PR-TABLE-2 seq 10 permit 20.8.2.0/24 le 32 + ipv6 prefix-list vrfdev6-12 permit 2000:9:2::/64 gt 64 + ''' + re_ip_pfxlst = re.search('^(ip|ipv6)(\s+prefix-list\s+)(\S+\s+)(seq \d+\s+)(permit|deny)(.*)$', + ctx_keys[0]) + if re_ip_pfxlst: + tmpline = (re_ip_pfxlst.group(1) + re_ip_pfxlst.group(2) + + re_ip_pfxlst.group(3) + re_ip_pfxlst.group(5) + + re_ip_pfxlst.group(6)) + for ctx in lines_to_add: + if ctx[0][0] == tmpline: + lines_to_del_to_del.append((ctx_keys, None)) + lines_to_add_to_del.append(((tmpline,), None)) + if not deleted: found_add_line = line_exist(lines_to_add, ctx_keys, line) @@ -646,6 +803,11 @@ def compare_context_objects(newconf, running): delete_bgpd = True lines_to_del.append((running_ctx_keys, None)) + # We cannot do 'no interface' in quagga, and so deal with it + elif running_ctx_keys[0].startswith('interface'): + for line in running_ctx.lines: + lines_to_del.append((running_ctx_keys, line)) + # If this is an address-family under 'router bgp' and we are already deleting the # entire 'router bgp' context then ignore this sub-context elif "router bgp" in running_ctx_keys[0] and len(running_ctx_keys) > 1 and delete_bgpd: @@ -697,6 +859,7 @@ if __name__ == '__main__': parser.add_argument('--debug', action='store_true', help='Enable debugs', default=False) parser.add_argument('--stdout', action='store_true', help='Log to STDOUT', default=False) parser.add_argument('filename', help='Location of new frr config file') + parser.add_argument('--overwrite', action='store_true', help='Overwrite Quagga.conf with running config output', default=False) args = parser.parse_args() # Logging @@ -904,5 +1067,6 @@ if __name__ == '__main__': subprocess.call(['/usr/bin/vtysh', '-f', filename]) os.unlink(filename) - # Make these changes persistent + # Make these changes persistent + if args.overwrite or args.filename != '/etc/quagga/Quagga.conf': subprocess.call(['/usr/bin/vtysh', '-c', 'write']) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index c04f9188fa..4913aa878f 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -736,8 +736,10 @@ _netlink_route_build_singlepath( if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET, nlmsg, - req_size, bytelen, nexthop); + /* Send deletes to the kernel without specifying the next-hop */ + if (cmd != RTM_DELROUTE) + _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET, nlmsg, + req_size, bytelen, nexthop); if (cmd == RTM_NEWROUTE) { diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index c5223199a4..a633ce6332 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -459,6 +459,7 @@ zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, struct interface *ifp) char vrf_str[64]; struct prefix dest_prefix; struct prefix src_prefix; + vrf_id_t vrf_id; ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDSTATUS_STR, bfdst_str); @@ -491,7 +492,8 @@ zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, struct interface *ifp) } if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s: Recv Port [%s] bfd status [%s] vrf [%s] peer [%s] local [%s]", + zlog_debug("%s: Recv Port [%s] bfd status [%s] vrf [%s]" + " peer [%s] local [%s]", __func__, ifp ? ifp->name : "N/A", bfdst_str, vrf_str, dest_str, src_str); @@ -510,12 +512,18 @@ zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, struct interface *ifp) } } + if (!strcmp(ZEBRA_PTM_INVALID_VRF, vrf_str) && ifp) { + vrf_id = ifp->vrf_id; + } else { + vrf_id = vrf_name_to_id(vrf_str); + } + if (!strcmp (bfdst_str, ZEBRA_PTM_BFDSTATUS_DOWN_STR)) { if_bfd_session_update(ifp, &dest_prefix, &src_prefix, BFD_STATUS_DOWN, - vrf_name_to_id(vrf_str)); + vrf_id); } else { if_bfd_session_update(ifp, &dest_prefix, &src_prefix, BFD_STATUS_UP, - vrf_name_to_id(vrf_str)); + vrf_id); } return 0; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 5088198624..40bf0eac78 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -791,7 +791,7 @@ ALIAS (no_ip_route, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") -ALIAS (no_ip_route_tag, +DEFUN (no_ip_route_flags_tag, no_ip_route_flags_tag_cmd, "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", NO_STR @@ -804,6 +804,10 @@ ALIAS (no_ip_route_tag, "Silently discard pkts when matched\n" "Tag of this route\n" "Tag value\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], argv[2], argv[3], + NULL, NULL, NULL); +} DEFUN (no_ip_route_flags2, no_ip_route_flags2_cmd, @@ -831,7 +835,7 @@ DEFUN (no_ip_route_flags2_tag, "Tag of this route\n" "Tag value\n") { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, NULL, argv[1], + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, NULL, argv[2], NULL, NULL, NULL); } @@ -882,7 +886,7 @@ ALIAS (no_ip_route_mask, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") -ALIAS (no_ip_route_mask_tag, +DEFUN (no_ip_route_mask_flags_tag, no_ip_route_mask_flags_tag_cmd, "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", NO_STR @@ -896,6 +900,10 @@ ALIAS (no_ip_route_mask_tag, "Silently discard pkts when matched\n" "Tag of this route\n" "Tag value\n") +{ + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], NULL, argv[4], + NULL, NULL, NULL); +} DEFUN (no_ip_route_mask_flags2, no_ip_route_mask_flags2_cmd, @@ -925,7 +933,7 @@ DEFUN (no_ip_route_mask_flags2_tag, "Tag of this route\n" "Tag value\n") { - return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, NULL, argv[2], + return zebra_static_ipv4 (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, NULL, argv[3], NULL, NULL, NULL); }