package org.batfish.question; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.batfish.common.Answerer; import org.batfish.common.BatfishException; import org.batfish.common.plugin.IBatfish; import org.batfish.datamodel.OspfNeighbor; import org.batfish.datamodel.Configuration; import org.batfish.datamodel.Interface; import org.batfish.datamodel.Ip; import org.batfish.datamodel.Prefix; import org.batfish.datamodel.PrefixTrie; import org.batfish.datamodel.Topology; import org.batfish.datamodel.Vrf; import org.batfish.datamodel.OspfNeighbor.OspfNeighborSummary; import org.batfish.datamodel.OspfProcess; import org.batfish.datamodel.answers.AnswerElement; import org.batfish.datamodel.collections.IpPair; import org.batfish.datamodel.questions.Question; import com.fasterxml.jackson.annotation.JsonProperty; public class OspfSessionCheckQuestionPlugin extends QuestionPlugin { public static class OspfSessionCheckAnswerElement implements AnswerElement { private static final String ALL_OSPF_NEIGHBORS_VAR = "allOspfNeighbors"; private static final String BROKEN_VAR = "broken"; private static final String HALF_OPEN_VAR = "halfOpen"; private static final String IGNORED_FOREIGN_ENDPOINTS_VAR = "ignoredForeignEndpoints"; private static final String MISMATCH_LINK_COST_VAR = "mismatchLinkCost"; private SortedMap<String, SortedMap<String, SortedMap<IpPair, OspfNeighborSummary>>> _allOspfNeighbors; private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> _broken; private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> _halfOpen; private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> _ignoredForeignEndpoints; private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> _mismatchLinkCost; public OspfSessionCheckAnswerElement() { _allOspfNeighbors = new TreeMap<>(); _broken = new TreeMap<>(); _halfOpen = new TreeMap<>(); _ignoredForeignEndpoints = new TreeMap<>(); _mismatchLinkCost = new TreeMap<>(); } public void add( SortedMap<String, SortedMap<String, SortedSet<IpPair>>> neighborsByHostname, String hostname, String vrf, OspfNeighborSummary ospfNeighbor) { SortedMap<String, SortedSet<IpPair>> neighborsByVrf = neighborsByHostname .get(hostname); if (neighborsByVrf == null) { neighborsByVrf = new TreeMap<>(); neighborsByHostname.put(hostname, neighborsByVrf); } SortedSet<IpPair> neighbors = neighborsByVrf.get(vrf); if (neighbors == null) { neighbors = new TreeSet<>(); neighborsByVrf.put(vrf, neighbors); } IpPair ipPair = new IpPair(ospfNeighbor.getLocalIp(), ospfNeighbor.getRemoteIp()); neighbors.add(ipPair); } public void addToAll( SortedMap<String, SortedMap<String, SortedMap<IpPair, OspfNeighborSummary>>> neighborsByHostname, String hostname, String vrf, OspfNeighborSummary ospfNeighbor) { SortedMap<String, SortedMap<IpPair, OspfNeighborSummary>> neighborsByVrf = neighborsByHostname .get(hostname); if (neighborsByVrf == null) { neighborsByVrf = new TreeMap<>(); neighborsByHostname.put(hostname, neighborsByVrf); } SortedMap<IpPair, OspfNeighborSummary> neighborsByIp = neighborsByVrf .get(vrf); if (neighborsByIp == null) { neighborsByIp = new TreeMap<>(); neighborsByVrf.put(vrf, neighborsByIp); } IpPair ipPair = new IpPair(ospfNeighbor.getLocalIp(), ospfNeighbor.getRemoteIp()); neighborsByIp.put(ipPair, ospfNeighbor); } @JsonProperty(ALL_OSPF_NEIGHBORS_VAR) public SortedMap<String, SortedMap<String, SortedMap<IpPair, OspfNeighborSummary>>> getAllOspfNeighbors() { return _allOspfNeighbors; } @JsonProperty(BROKEN_VAR) private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> getBroken() { return _broken; } @JsonProperty(HALF_OPEN_VAR) private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> getHalfOpen() { return _halfOpen; } @JsonProperty(IGNORED_FOREIGN_ENDPOINTS_VAR) private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> getIgnoredForeignEndpoints() { return _ignoredForeignEndpoints; } @JsonProperty(MISMATCH_LINK_COST_VAR) private SortedMap<String, SortedMap<String, SortedSet<IpPair>>> getMismatchLinkCost() { return _mismatchLinkCost; } @Override public String prettyPrint() { StringBuilder sb = new StringBuilder(); sb.append(prettyPrintCategory(_halfOpen, HALF_OPEN_VAR)); sb.append( prettyPrintCategory(_mismatchLinkCost, MISMATCH_LINK_COST_VAR)); return sb.toString(); } private CharSequence prettyPrintCategory( SortedMap<String, SortedMap<String, SortedSet<IpPair>>> category, String name) { StringBuilder sb = new StringBuilder(); if (!category.isEmpty()) { sb.append(name + ":\n"); for (Entry<String, SortedMap<String, SortedSet<IpPair>>> e1 : category .entrySet()) { String hostname = e1.getKey(); sb.append(" " + hostname + ":\n"); SortedMap<String, SortedSet<IpPair>> neighborsByHostname = e1 .getValue(); for (Entry<String, SortedSet<IpPair>> e2 : neighborsByHostname .entrySet()) { String vrf = e2.getKey(); sb.append(" " + vrf + ":\n"); SortedSet<IpPair> neighbors = e2.getValue(); for (IpPair ipPair : neighbors) { OspfNeighborSummary summary = _allOspfNeighbors .get(hostname).get(vrf).get(ipPair); Ip remoteIp = summary.getRemoteIp(); Ip localIp = summary.getLocalIp(); sb.append(String.format( " remoteIp: %-15s localIp: %-15s\n", remoteIp, localIp)); } } } } return sb; } @JsonProperty(ALL_OSPF_NEIGHBORS_VAR) public void setAllOspfNeighbors( SortedMap<String, SortedMap<String, SortedMap<IpPair, OspfNeighborSummary>>> allOspfNeighbors) { _allOspfNeighbors = allOspfNeighbors; } @JsonProperty(BROKEN_VAR) public void setBroken( SortedMap<String, SortedMap<String, SortedSet<IpPair>>> broken) { _broken = broken; } @JsonProperty(HALF_OPEN_VAR) public void setHalfOpen( SortedMap<String, SortedMap<String, SortedSet<IpPair>>> halfOpen) { _halfOpen = halfOpen; } @JsonProperty(IGNORED_FOREIGN_ENDPOINTS_VAR) public void setIgnoredForeignEndpoints( SortedMap<String, SortedMap<String, SortedSet<IpPair>>> ignoredForeignEndpoints) { _ignoredForeignEndpoints = ignoredForeignEndpoints; } @JsonProperty(MISMATCH_LINK_COST_VAR) public void setMismatchLinkCost( SortedMap<String, SortedMap<String, SortedSet<IpPair>>> mismatchLinkCost) { _mismatchLinkCost = mismatchLinkCost; } } public static class OspfSessionCheckAnswerer extends Answerer { public OspfSessionCheckAnswerer(Question question, IBatfish batfish) { super(question, batfish); } @Override public AnswerElement answer() { OspfSessionCheckQuestion question = (OspfSessionCheckQuestion) _question; Pattern node1Regex; Pattern node2Regex; try { node1Regex = Pattern.compile(question.getNode1Regex()); node2Regex = Pattern.compile(question.getNode2Regex()); } catch (PatternSyntaxException e) { throw new BatfishException(String.format( "One of the supplied regexes (%s OR %s) is not a valid java regex.", question.getNode1Regex(), question.getNode2Regex()), e); } _batfish.checkConfigurations(); Map<String, Configuration> configurations = _batfish .loadConfigurations(); PrefixTrie _foreignPrefixTrie = new PrefixTrie(); if (question._foreignOspfNetworks != null) { _foreignPrefixTrie.addAll(question._foreignOspfNetworks); } OspfSessionCheckAnswerElement answerElement = new OspfSessionCheckAnswerElement(); Set<Ip> allInterfaceIps = new HashSet<>(); Set<Ip> loopbackIps = new HashSet<>(); Map<Ip, Set<String>> ipOwners = new HashMap<>(); for (Configuration c : configurations.values()) { for (Interface i : c.getInterfaces().values()) { if (i.getActive()) { if (i.getPrefix() != null) { for (Prefix prefix : i.getAllPrefixes()) { Ip address = prefix.getAddress(); if (i.isLoopback(c.getConfigurationFormat())) { loopbackIps.add(address); } allInterfaceIps.add(address); Set<String> currentIpOwners = ipOwners.get(address); if (currentIpOwners == null) { currentIpOwners = new HashSet<>(); ipOwners.put(address, currentIpOwners); } currentIpOwners.add(c.getHostname()); } } } } } Topology topology = _batfish.computeTopology(configurations); _batfish.initRemoteOspfNeighbors(configurations, ipOwners, topology); for (Configuration co : configurations.values()) { String hostname = co.getHostname(); if (!node1Regex.matcher(co.getHostname()).matches()) { continue; } for (Vrf vrf : co.getVrfs().values()) { String vrfName = vrf.getName(); OspfProcess proc = vrf.getOspfProcess(); if (proc != null) { for (OspfNeighbor ospfNeighbor : proc.getOspfNeighbors() .values()) { OspfNeighborSummary ospfNeighborSummary = new OspfNeighborSummary( ospfNeighbor); answerElement.addToAll(answerElement.getAllOspfNeighbors(), hostname, vrfName, ospfNeighborSummary); boolean foreign = _foreignPrefixTrie .containsIp(ospfNeighbor.getRemoteIp()); if (foreign) { answerElement.add( answerElement.getIgnoredForeignEndpoints(), hostname, vrfName, ospfNeighborSummary); } else if (ospfNeighbor.getRemoteOspfNeighbor() == null) { if (!ospfNeighbor.getIface().getOspfPassive()) { // half-open answerElement.add(answerElement.getBroken(), hostname, vrfName, ospfNeighborSummary); answerElement.add(answerElement.getHalfOpen(), hostname, vrfName, ospfNeighborSummary); } } else { OspfNeighbor remoteOspfNeighbor = ospfNeighbor .getRemoteOspfNeighbor(); String remoteHostname = remoteOspfNeighbor.getOwner() .getHostname(); if (!node2Regex.matcher(remoteHostname).matches()) { continue; } // check link cost mismatch int linkCost = ospfNeighbor.getIface().getOspfCost(); int remoteLinkCost = remoteOspfNeighbor.getIface() .getOspfCost(); if (linkCost != remoteLinkCost) { answerElement.add( answerElement.getMismatchLinkCost(), hostname, vrfName, ospfNeighborSummary); } } } } } } return answerElement; } } // <question_page_comment> /** * Checks if OSPF sessions are correctly configured. * <p> * Details coming * * @type OspfSessionCheck multifile * * @param foreignOspfNetworks * Details coming. * @param node1Regex * Regular expression to match the nodes names for one end of the * sessions. Default is '.*' (all nodes). * @param node2Regex * Regular expression to match the nodes names for the other end of * the sessions. Default is '.*' (all nodes). * * @example bf_answer("OspfSessionCheck", node1Regex="as1.*", * node2Regex="as2.*") Checks all OSPF sessions between nodes that * start with as1 and those that start with as2. */ public static class OspfSessionCheckQuestion extends Question { private static final String FOREIGN_OSPF_NETWORKS_VAR = "foreignOspfNetworks"; private static final String NODE1_REGEX_VAR = "node1Regex"; private static final String NODE2_REGEX_VAR = "node2Regex"; private SortedSet<Prefix> _foreignOspfNetworks; private String _node1Regex; private String _node2Regex; public OspfSessionCheckQuestion() { _foreignOspfNetworks = new TreeSet<>(); _node1Regex = ".*"; _node2Regex = ".*"; } @Override public boolean getDataPlane() { return false; } @JsonProperty(FOREIGN_OSPF_NETWORKS_VAR) public SortedSet<Prefix> getForeignOspfNetworks() { return _foreignOspfNetworks; } @Override public String getName() { return "ospfsessioncheck"; } @JsonProperty(NODE1_REGEX_VAR) public String getNode1Regex() { return _node1Regex; } @JsonProperty(NODE2_REGEX_VAR) public String getNode2Regex() { return _node2Regex; } @Override public boolean getTraffic() { return false; } @JsonProperty(FOREIGN_OSPF_NETWORKS_VAR) public void setForeignOspfNetworks( SortedSet<Prefix> foreignOspfNetworks) { _foreignOspfNetworks = foreignOspfNetworks; } @JsonProperty(NODE1_REGEX_VAR) public void setNode1Regex(String regex) { _node1Regex = regex; } @JsonProperty(NODE2_REGEX_VAR) public void setNode2Regex(String regex) { _node2Regex = regex; } } @Override protected OspfSessionCheckAnswerer createAnswerer(Question question, IBatfish batfish) { return new OspfSessionCheckAnswerer(question, batfish); } @Override protected OspfSessionCheckQuestion createQuestion() { return new OspfSessionCheckQuestion(); } }