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();
}
}