1 /*******************************************************************************
2 * Copyright (c) 2017 Politecnico di Torino and others.
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Apache License, Version 2.0
6 * which accompanies this distribution, and is available at
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *******************************************************************************/
10 package it.polito.escape.verify.service;
12 import java.io.BufferedReader;
14 import java.io.FileWriter;
15 import java.io.FilenameFilter;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.io.PrintWriter;
19 import java.io.StringWriter;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Locale;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 import java.util.stream.Collectors;
30 import javax.tools.Diagnostic;
31 import javax.tools.DiagnosticCollector;
32 import javax.tools.JavaCompiler;
33 import javax.tools.JavaFileObject;
34 import javax.tools.StandardJavaFileManager;
35 import javax.tools.ToolProvider;
36 import javax.ws.rs.ProcessingException;
37 import javax.xml.bind.JAXBException;
39 import org.json.simple.JSONArray;
40 import org.json.simple.JSONObject;
42 import com.fasterxml.jackson.databind.JsonNode;
44 import it.polito.escape.verify.client.Neo4jManagerClient;
45 import it.polito.escape.verify.exception.BadRequestException;
46 import it.polito.escape.verify.exception.DataNotFoundException;
47 import it.polito.escape.verify.exception.ForbiddenException;
48 import it.polito.escape.verify.exception.InternalServerErrorException;
49 import it.polito.escape.verify.model.Configuration;
50 import it.polito.escape.verify.model.Entry;
51 import it.polito.escape.verify.model.Graph;
52 import it.polito.escape.verify.model.Neighbour;
53 import it.polito.escape.verify.model.Node;
54 import it.polito.escape.verify.model.Test;
55 import it.polito.escape.verify.model.Verification;
56 import it.polito.escape.verify.resources.beans.VerificationBean;
57 import it.polito.nffg.neo4j.jaxb.Paths;
58 import qj.util.ReflectUtil;
59 import qj.util.lang.DynamicClassLoader;
61 public class VerificationService {
63 private static final String generatorFolder = System.getProperty("catalina.base")
64 + "/webapps/verify/WEB-INF/classes/tests/j-verigraph-generator";
66 private static final String generatorFolderForGrpc = "service/src/tests/j-verigraph-generator";
68 private String testClassGenerator = generatorFolder + "/test_class_generator.py";
70 private String testGenerator = generatorFolder + "/test_generator.py";
72 public VerificationService() {
76 private Paths getPaths(Graph graph, Node sourceNode, Node destinationNode) {
78 String source = sourceNode.getName() + "_" + sourceNode.getId();
79 String destination = destinationNode.getName() + "_" + destinationNode.getId();
81 List<String> endpoints = new ArrayList<>();
82 List<String> firewalls = new ArrayList<>();
83 Map<String, List<Entry>> routingTable = new HashMap<>();
85 for (Node node : graph.getNodes().values()) {
87 if (node.getFunctional_type().equals("NF")) {
88 // add 2 connection points to RT
89 routingTable.put(node.getName() + "_" + node.getId() + "_in", new ArrayList<Entry>());
90 routingTable.put(node.getName() + "_" + node.getId() + "_out", new ArrayList<Entry>());
91 // add node to firewalls
92 firewalls.add(node.getName() + "_" + node.getId());
94 for (Neighbour neighbour : node.getNeighbours().values()) {
95 // check if neighbour is a firewall
96 Node hop = graph.searchNodeByName(neighbour.getName());
97 // if neighbour is a firewall connect to its input port
98 if (hop.getFunctional_type().equals("NF"))
99 routingTable.get(node.getName() + "_" + node.getId() + "_out")
100 .add(new Entry("output", neighbour.getName() + "_" + hop.getId() + "_in"));
105 routingTable.get(node.getName() + "_" + node.getId()
107 .add(new Entry( "output",
108 neighbour.getName() + "_"
114 // add endpoint to RT
115 routingTable.put(node.getName() + "_" + node.getId(), new ArrayList<Entry>());
117 endpoints.add(node.getName() + "_" + node.getId());
119 for (Neighbour neighbour : node.getNeighbours().values()) {
120 // check if neighbour is a firewall
121 Node hop = graph.searchNodeByName(neighbour.getName());
122 // if neighbour is a firewall connect to its input port
123 if (hop.getFunctional_type().equals("NF"))
124 routingTable.get(node.getName() + "_" + node.getId())
125 .add(new Entry("output", neighbour.getName() + "_" + hop.getId() + "_in"));
130 routingTable.get(node.getName() + "_" + node.getId())
131 .add(new Entry("output", neighbour.getName() + "_" + hop.getId()));
139 System.out.println("Endpoints:");
140 for (String endpoint : endpoints) {
141 System.out.println(endpoint);
143 System.out.println("Firewalls:");
144 for (String firewall : firewalls) {
145 System.out.println(firewall);
147 System.out.println("Source: " + source);
148 System.out.println("Destination: " + destination);
149 for (String key : routingTable.keySet()) {
150 System.out.println("RT for node " + key);
151 for (Entry entry : routingTable.get(key)) {
152 System.out.println("\t" + entry.getDirection() + "->" + entry.getDestination());
157 Neo4jManagerClient client = new Neo4jManagerClient( "http://localhost:8080/neo4jmanager/rest/",
166 paths = client.getPaths();
168 catch (JAXBException e) {
169 throw new InternalServerErrorException("Error generating input for neo4jmanager: " + e.getMessage());
171 catch (ProcessingException e) {
172 throw new InternalServerErrorException("Response of neo4jmanager doesn't contain any path: "
175 catch (IllegalStateException e) {
176 throw new InternalServerErrorException("Error getting a response from neo4jmanager, no input stream for paths or input stream already consumed: "
179 catch (Exception e) {
180 throw new InternalServerErrorException("Unable to continue due to a neo4jmanager error: " + e.getMessage());
187 private List<String> sanitizePath(String path) {
188 List<String> newPath = new ArrayList<String>();
189 // find all nodes, i.e. all names between parentheses
190 Matcher m = Pattern.compile("\\(([^)]+)\\)").matcher(path);
192 String node = m.group(1);
194 int spaceIndex = node.lastIndexOf("_");
195 if (spaceIndex != -1) {
196 node = node.substring(0, spaceIndex);
204 private List<List<String>> sanitizePaths(Paths paths) {
205 List<List<String>> sanitizedPaths = new ArrayList<List<String>>();
206 for (String path : paths.getPath()) {
207 System.out.println("Original path: " + path);
208 List<String> newPath = sanitizePath(path);
209 sanitizedPaths.add(newPath);
211 return sanitizedPaths;
214 static private Map<String, Long> toMap(List<String> lst) {
215 return lst.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting()));
218 private void eliminateLoopsInPaths(List<List<String>> sanitizedPaths) {
219 List<List<String>> pathsToBeRemoved = new ArrayList<List<String>>();
221 for (List<String> path : sanitizedPaths) {
222 Map<String, Long> occurrencesMap = toMap(path);
223 for (long occurrences : occurrencesMap.values()) {
224 if (occurrences > 1) {
225 pathsToBeRemoved.add(path);
230 for (List<String> path : pathsToBeRemoved) {
231 sanitizedPaths.remove(path);
235 private void printListsOfStrings(String message, List<List<String>> lists) {
236 System.out.println(message);
237 for (List<String> element : lists) {
238 System.out.println(element);
242 private static File createTempDir(String prefix) throws IOException {
243 String tmpDirStr = System.getProperty("java.io.tmpdir");
244 if (tmpDirStr == null) {
245 throw new IOException("System property 'java.io.tmpdir' does not specify a tmp dir");
248 File tmpDir = new File(tmpDirStr);
249 if (!tmpDir.exists()) {
250 boolean created = tmpDir.mkdirs();
252 throw new IOException("Unable to create tmp dir " + tmpDir);
256 File resultDir = null;
257 int suffix = (int) System.currentTimeMillis();
258 int failureCount = 0;
260 resultDir = new File(tmpDir, prefix + suffix % 10000);
263 } while (resultDir.exists() && failureCount < 50);
265 if (resultDir.exists()) {
266 throw new IOException(failureCount
267 + " attempts to generate a non-existent directory name failed, giving up");
269 boolean created = resultDir.mkdir();
271 throw new IOException("Failed to create tmp directory");
277 @SuppressWarnings("unchecked")
278 private void generateChainsFile(Graph graph, List<List<String>> sanitizedPaths, String chainsFile) {
279 JSONObject root = new JSONObject();
280 JSONArray chains = new JSONArray();
282 int chainCounter = 0;
284 for (List<String> path : sanitizedPaths) {
285 Iterator<String> pathsIterator = path.iterator();
286 JSONObject chain = new JSONObject();
287 chain.put("id", ++chainCounter);
288 chain.put("flowspace", "tcp=80");
289 JSONArray nodes = new JSONArray();
290 while (pathsIterator.hasNext()) {
291 String nodeName = (String) pathsIterator.next();
292 Node currentNode = graph.searchNodeByName(nodeName);
293 if (currentNode == null) {
294 throw new InternalServerErrorException("Unable to generate 'chains.json' for neo4jmanager: node "
295 + nodeName + " not found");
297 JSONObject node = new JSONObject();
298 node.put("name", currentNode.getName());
299 // if(currentNode.getFunctional_type().equals("firewall"))
300 // node.put("address", "ip_nat");
302 node.put("address", "ip_" + currentNode.getName());
303 node.put("functional_type", currentNode.getFunctional_type());
305 chain.put("nodes", nodes);
309 root.put("chains", chains);
311 try (FileWriter file = new FileWriter(chainsFile)) {
312 file.write(root.toJSONString());
313 System.out.println("Successfully created 'chains.json' with the following content:");
314 System.out.println(root);
316 catch (IOException e) {
317 throw new InternalServerErrorException("Error saving 'chains.json' for neo4jmanager");
322 @SuppressWarnings("unchecked")
323 private void generateConfigFile(Graph graph, String configFile) {
324 JSONObject root = new JSONObject();
325 JSONArray nodes = new JSONArray();
327 for (Node n : graph.getNodes().values()) {
328 JSONObject node = new JSONObject();
329 // JSONArray configuration = new JSONArray();
330 Configuration nodeConfig = n.getConfiguration();
331 JsonNode configuration = nodeConfig.getConfiguration();
333 node.put("configuration", configuration);
334 node.put("id", nodeConfig.getId());
335 node.put("description", nodeConfig.getDescription());
340 root.put("nodes", nodes);
342 try (FileWriter file = new FileWriter(configFile)) {
343 file.write(root.toJSONString());
344 System.out.println("Successfully created 'config.json' with the following content:");
345 System.out.println(root);
347 catch (IOException e) {
348 throw new InternalServerErrorException("Error saving 'config.json' for neo4jmanager");
353 private void printCommand(String[] cmd) {
354 for (String c : cmd) {
355 System.out.printf(c + " ");
357 System.out.println("");
360 private String platfromIndependentPath(String path) {
361 path = path.replaceAll("/", Matcher.quoteReplacement(Character.toString(File.separatorChar)));
365 private void generateTestScenarios(String chainsFile, String configFile, String scenarioFile) {
367 String[] cmd = { "python", platfromIndependentPath(testClassGenerator), "-c",
368 platfromIndependentPath(chainsFile), "-f", platfromIndependentPath(configFile), "-o",
369 platfromIndependentPath(scenarioFile) };
372 ProcessBuilder pb = new ProcessBuilder(cmd);
373 pb.redirectErrorStream(true);
376 process = pb.start();
378 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
380 while ((line = reader.readLine()) != null)
381 System.out.println("test_class_generator.py: " + line);
383 if (process.exitValue() != 0) {
384 throw new InternalServerErrorException("Unable to generate test scenario file for the verification request: test_class_generator returned "
385 + process.exitValue());
388 catch (IOException e) {
389 throw new InternalServerErrorException("Error generating tests for Z3: unable to execute generator");
391 catch (InterruptedException e) {
392 throw new InternalServerErrorException("Error generating tests for Z3: generator got interrupted during execution");
397 private void generateTests( int scenariosCounter, String scenariosBasename, String source, String destination,
398 String testsBasename) {
400 List<String> scenarios = new ArrayList<String>();
401 List<String> tests = new ArrayList<String>();
402 for (int i = 0; i < scenariosCounter; i++) {
403 scenarios.add(scenariosBasename + "_" + (i + 1) + ".java");
404 tests.add(testsBasename + "_" + (i + 1) + ".java");
407 for (int i = 0; i < scenariosCounter; i++) {
408 String[] cmd = { "python", platfromIndependentPath(testGenerator), "-i",
409 platfromIndependentPath(scenarios.get(i)), "-o", platfromIndependentPath(tests.get(i)),
410 "-s", source, "-d", destination };
413 ProcessBuilder pb = new ProcessBuilder(cmd);
414 pb.redirectErrorStream(true);
417 process = pb.start();
419 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
421 while ((line = reader.readLine()) != null)
422 System.out.println("test_generator.py: " + line);
424 if (process.exitValue() != 0) {
425 throw new InternalServerErrorException("Unable to generate test file for the verification request: test_generator returned "
426 + process.exitValue());
429 catch (IOException e) {
430 throw new InternalServerErrorException("Error generating tests for Z3: unable to execute generator");
432 catch (InterruptedException e) {
433 throw new InternalServerErrorException("Error generating tests for Z3: generator got interrupted during execution");
440 private void prepareForCompilationAndExecution( int scenariosCounter, String scenarioBasename, String testBasename,
441 List<File> sourceFiles, List<File> classFiles) {
442 for (int i = 0; i < scenariosCounter; i++) {
443 String scenario = scenarioBasename + "_" + (i + 1) + ".java";
444 sourceFiles.add(new File(scenario));
445 System.out.println("Scenario file " + scenario + " added to compilation");
447 String testSource = testBasename + "_" + (i + 1) + ".java";
448 String testClass = testBasename + "_" + (i + 1);
450 sourceFiles.add(new File(testSource));
451 System.out.println("Test file " + testSource + " added to copilation");
452 classFiles.add(new File(testClass));
453 System.out.println("Test file " + testClass + " added to execution");
457 private void compileFiles(List<File> files, String folder) {
459 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
460 if (compiler == null) {
461 throw new InternalServerErrorException("Error getting the Java compiler: JDK >= 1.8 required");
463 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
466 // fileManager.setLocation(StandardLocation.CLASS_OUTPUT,
467 // Arrays.asList(new File(projectFolder)));
469 // String z3 = "/usr/lib/com.microsoft.z3.jar";
470 // List<String> optionList = new ArrayList<String>();
471 // optionList.add("-classpath");
472 // optionList.add(System.getProperty("java.class.path") + ":" + z3);
473 List<String> optionList = new ArrayList<String>();
474 optionList.add("-d");
475 optionList.add(folder);
476 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
478 boolean success = compiler
484 fileManager.getJavaFileObjectsFromFiles(files))
487 Locale myLocale = Locale.getDefault();
488 StringBuilder msg = new StringBuilder();
489 msg.append("Error compiling Z3 test files: ");
490 for (Diagnostic<? extends JavaFileObject> err : diagnostics.getDiagnostics()) {
492 msg.append(err.getKind());
494 if (err.getSource() != null) {
495 msg.append(err.getSource().getName());
498 msg.append(err.getLineNumber());
500 msg.append(err.getMessage(myLocale));
502 throw new InternalServerErrorException(msg.toString());
507 catch (IOException e) {
508 throw new InternalServerErrorException("Unable to set the location of the Z3 test files to be compiled");
513 private int runIt(File filename, String folder) {
514 int endIndex = filename.getName().lastIndexOf(".");
515 String filenameNoExtension;
516 if (endIndex == -1) {
517 filenameNoExtension = filename.getName();
520 filenameNoExtension = filename.getName().substring(0, endIndex);
521 if (!filenameNoExtension.matches("\\w+")) {
522 filenameNoExtension = filename.getName();
526 System.out.println("Filename is: " + filenameNoExtension);
528 Class<?> userClass = new DynamicClassLoader(folder).load("tests." + filenameNoExtension);
529 Object context = ReflectUtil.newInstance(userClass);
530 Object result = ReflectUtil.invoke("run", context);
533 catch (Exception e) {
534 StringWriter errors = new StringWriter();
535 e.printStackTrace(new PrintWriter(errors));
536 throw new InternalServerErrorException("Error executing Z3 tests: " + e.getMessage()
537 + ". There are errors in the Z3 model.");
541 private List<Test> runFiles(String folder, List<List<String>> paths, Graph graph, List<File> files) {
542 List<Test> tests = new ArrayList<Test>();
543 for (int i = 0; i < files.size(); i++) {
544 System.out.println("Running test file \"" + files.get(i).getAbsolutePath() + "\"");
545 int result = runIt(files.get(i), folder);
546 System.out.println("Execution returned: " + result);
548 List<Node> path = new ArrayList<Node>();
549 for (String nodeString : paths.get(i)) {
550 Node node = graph.searchNodeByName(nodeString);
553 Test t = new Test(path, result);
560 @SuppressWarnings("unused")
561 private static boolean deleteDir(File dir) {
562 if (dir.isDirectory()) {
563 String[] children = dir.list();
564 for (int i = 0; i < children.length; i++) {
565 boolean success = deleteDir(new File(dir, children[i]));
576 @SuppressWarnings("unused")
577 private void deleteFilesWithPrefix(String directory, String prefix, String extension) {
578 final File scenarioFolder = new File(directory);
579 final File[] scenarioFiles = scenarioFolder.listFiles(new FilenameFilter() {
582 public boolean accept(final File dir, final String name) {
583 return name.matches(prefix + ".*\\." + extension);
586 for (final File file : scenarioFiles) {
587 if (!file.delete()) {
588 System.err.println("Can't remove " + file.getAbsolutePath());
591 System.out.println("Removed file " + file.getAbsolutePath());
596 public Verification verify(long graphId, VerificationBean verificationBean) {
598 throw new ForbiddenException("Illegal graph id: " + graphId);
600 GraphService graphService = new GraphService();
601 Graph graph = graphService.getGraph(graphId);
603 throw new DataNotFoundException("Graph with id " + graphId + " not found");
605 String source = verificationBean.getSource();
606 String destination = verificationBean.getDestination();
607 String type = verificationBean.getType();
608 if (source == null || source.equals("")) {
609 throw new BadRequestException("Please specify the 'source' parameter in your request");
611 if (destination == null || destination.equals("")) {
612 throw new BadRequestException("Please specify the 'destination' parameter in your request");
614 if (type == null || type.equals("")) {
615 throw new BadRequestException("Please specify the 'type' parameter in your request");
618 Node sourceNode = graph.searchNodeByName(verificationBean.getSource());
619 Node destinationNode = graph.searchNodeByName(verificationBean.getDestination());
621 if (sourceNode == null) {
622 throw new BadRequestException("The 'source' parameter '" + source + "' is not valid, please insert the name of an existing node");
624 if (destinationNode == null) {
625 throw new BadRequestException("The 'destination' parameter '" + destination + "' is not valid, please insert the name of an existing node");
627 if ((!type.equals("reachability")) && (!type.equals("isolation")) && (!type.equals("traversal"))) {
628 throw new BadRequestException("The 'type' parameter '" + type
629 + "' is not valid: valid types are: 'reachability', 'isolation' and 'traversal'");
632 Verification v = null;
637 v = reachabilityVerification(graph, sourceNode, destinationNode);
640 middlebox = verificationBean.getMiddlebox();
641 if (middlebox == null || middlebox.equals("")) {
642 throw new BadRequestException("Please specify the 'middlebox' parameter in your request");
645 middleboxNode = graph.searchNodeByName(middlebox);
646 if (middleboxNode == null) {
647 throw new BadRequestException("The 'middlebox' parameter '" + middlebox + "' is not valid, please insert the name of an existing node");
649 if (middleboxNode.getFunctional_type().equals("endpoint")) {
650 throw new BadRequestException("'" + middlebox
651 + "' is of type 'endpoint', please choose a valid middlebox");
653 v = isolationVerification(graph, sourceNode, destinationNode, middleboxNode);
656 middlebox = verificationBean.getMiddlebox();
657 if (middlebox == null || middlebox.equals("")) {
658 throw new BadRequestException("Please specify the 'middlebox' parameter in your request");
661 middleboxNode = graph.searchNodeByName(middlebox);
662 if (middleboxNode == null) {
663 throw new BadRequestException("The 'middlebox' parameter '" + middlebox + "' is not valid, please insert the name of an existing node");
665 if (middleboxNode.getFunctional_type().equals("endpoint")) {
666 throw new BadRequestException("'" + middlebox
667 + "' is of type 'endpoint', please choose a valid middlebox");
669 v = traversalVerification(graph, sourceNode, destinationNode, middleboxNode);
678 private Verification isolationVerification(Graph graph, Node sourceNode, Node destinationNode, Node middleboxNode) {
680 Paths paths = getPaths(graph, sourceNode, destinationNode);
681 if (paths.getPath().size() == 0) {
682 return new Verification("UNSAT",
683 "There are no available paths between '" + sourceNode.getName() + "' and '"
684 + destinationNode.getName() + "'");
687 List<List<String>> sanitizedPaths = sanitizePaths(paths);
689 printListsOfStrings("Before loops removal", sanitizedPaths);
691 eliminateLoopsInPaths(sanitizedPaths);
693 printListsOfStrings("After loops removal", sanitizedPaths);
695 if (sanitizedPaths.isEmpty()) {
696 return new Verification("UNSAT",
697 "There are no available paths between '" + sourceNode.getName() + "' and '"
698 + destinationNode.getName() + "'");
701 List<Test> tests = extractTestsFromPaths(graph, sanitizedPaths, "UNKNWON");
703 extractPathsWithMiddlebox(sanitizedPaths, middleboxNode.getName());
705 if (sanitizedPaths.isEmpty()) {
706 return new Verification("UNSAT",
708 "There are no available paths between '" + sourceNode.getName() + "' and '"
709 + destinationNode.getName() + "' which traverse middlebox '"
710 + middleboxNode.getName() + "'. See below all the available paths.");
713 printListsOfStrings("Paths with middlebox '" + middleboxNode.getName() + "'", sanitizedPaths);
718 tempDir = createTempDir("isolation");
720 catch (IOException e) {
721 throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage());
724 String chainsFile = tempDir.getAbsolutePath() + "/chains.json";
725 generateChainsFile(graph, sanitizedPaths, chainsFile);
727 String configFile = tempDir.getAbsolutePath() + "/config.json";
728 generateConfigFile(graph, configFile);
730 String isolationScenariosBasename = tempDir.getAbsolutePath() + "/IsolationScenario";
732 generateTestScenarios(chainsFile, configFile, isolationScenariosBasename);
733 }catch(Exception ex){
734 testClassGenerator = generatorFolderForGrpc + "/test_class_generator.py";
735 generateTestScenarios(chainsFile, configFile, isolationScenariosBasename);
738 String isolationTestsBasename = tempDir.getAbsolutePath() + "/IsolationTest";
740 generateTests( sanitizedPaths.size(),
741 isolationScenariosBasename,
742 sourceNode.getName(),
743 middleboxNode.getName(),
744 isolationTestsBasename);
745 }catch(InternalServerErrorException ex){
746 testGenerator = generatorFolderForGrpc + "/test_generator.py";
747 generateTests( sanitizedPaths.size(),
748 isolationScenariosBasename,
749 sourceNode.getName(),
750 middleboxNode.getName(),
751 isolationTestsBasename);
754 List<File> sourceFiles = new ArrayList<File>();
755 List<File> classFiles = new ArrayList<File>();
756 prepareForCompilationAndExecution( sanitizedPaths.size(),
757 isolationScenariosBasename,
758 isolationTestsBasename,
762 compileFiles(sourceFiles, tempDir.getAbsolutePath());
764 tests = runFiles(tempDir.getAbsolutePath(), sanitizedPaths, graph, classFiles);
766 return evaluateIsolationResults(tests,
767 sourceNode.getName(),
768 destinationNode.getName(),
769 middleboxNode.getName());
773 private List<Test> extractTestsFromPaths(Graph graph, List<List<String>> paths, String result) {
774 List<Test> tests = new ArrayList<Test>();
775 for (List<String> path : paths) {
776 List<Node> nodes = new ArrayList<Node>();
777 for (String nodeName : path) {
778 nodes.add(graph.searchNodeByName(nodeName));
780 tests.add(new Test(nodes, result));
785 private Verification evaluateIsolationResults( List<Test> tests, String source, String destination,
787 Verification v = new Verification();
788 boolean isSat = false;
789 int unsatCounter = 0;
790 for (Test t : tests) {
793 if (t.getResult().equals("SAT")) {
796 else if (t.getResult().equals("UNKNOWN")) {
797 v.setResult("UNKNWON");
798 v.setComment("Isolation property with source '" + source + "', destination '" + destination
799 + "' and middlebox '" + middlebox + "' is UNKNOWN because although '" + source
800 + "' cannot reach '" + middlebox + "' in any path from '" + source + "' to '"
801 + destination + "' which traverses middlebox '" + middlebox
802 + "' at least one reachability test between '" + source + "' and '" + middlebox
803 + "' returned UNKNOWN (see below all the paths that have been checked)");
805 else if (t.getResult().equals("UNSAT")) {
810 v.setResult("UNSAT");
811 v.setComment("Isolation property with source '" + source + "', destination '" + destination
812 + "' and middlebox '" + middlebox + "' is UNSATISFIED because reachability between '"
813 + source + "' and '" + middlebox + "' is SATISFIED in at least one path between '" + source
814 + "' and '" + destination + "' which traverses middlebox '" + middlebox
815 + "' (see below all the paths that have been checked)");
817 else if (unsatCounter == tests.size()) {
819 v.setComment("Isolation property with source '" + source + "', destination '" + destination
820 + "' and middlebox '" + middlebox + "' is SATISFIED because reachability between '" + source
821 + "' and '" + middlebox + "' is UNSATISFIED in all paths between '" + source + "' and '"
822 + destination + "' which traverse middlebox '" + middlebox
823 + "' (see below all the paths that have been checked)");
829 private void extractPathsWithMiddlebox(List<List<String>> sanitizedPaths, String middleboxName) {
830 List<List<String>> pathsToBeRemoved = new ArrayList<List<String>>();
831 for (List<String> path : sanitizedPaths) {
832 boolean middleboxFound = false;
833 for (String node : path) {
834 if (node.equals(middleboxName)) {
835 middleboxFound = true;
839 if (!middleboxFound) {
840 pathsToBeRemoved.add(path);
844 for (List<String> path : pathsToBeRemoved) {
845 sanitizedPaths.remove(path);
850 private void extractPathsWithoutMiddlebox(List<List<String>> sanitizedPaths, String middleboxName) {
851 List<List<String>> pathsToBeRemoved = new ArrayList<List<String>>();
852 for (List<String> path : sanitizedPaths) {
853 boolean middleboxFound = false;
854 for (String node : path) {
855 if (node.equals(middleboxName)) {
856 middleboxFound = true;
860 if (middleboxFound) {
861 pathsToBeRemoved.add(path);
865 for (List<String> path : pathsToBeRemoved) {
866 sanitizedPaths.remove(path);
871 private Verification traversalVerification(Graph graph, Node sourceNode, Node destinationNode, Node middleboxNode) {
873 Paths paths = getPaths(graph, sourceNode, destinationNode);
874 if (paths.getPath().size() == 0) {
875 return new Verification("UNSAT",
876 "There are no available paths between '" + sourceNode.getName() + "' and '"
877 + destinationNode.getName() + "'");
880 List<List<String>> pathsBetweenSourceAndDestination = sanitizePaths(paths);
882 printListsOfStrings("Before loops removal", pathsBetweenSourceAndDestination);
884 eliminateLoopsInPaths(pathsBetweenSourceAndDestination);
886 printListsOfStrings("After loops removal", pathsBetweenSourceAndDestination);
888 if (pathsBetweenSourceAndDestination.isEmpty()) {
889 return new Verification("UNSAT",
890 "There are no available paths between '" + sourceNode.getName() + "' and '"
891 + destinationNode.getName() + "'");
894 List<Test> tests = extractTestsFromPaths(graph, pathsBetweenSourceAndDestination, "UNKNOWN");
896 List<List<String>> pathsWithMiddlebox = new ArrayList<List<String>>();
897 for (List<String> path : pathsBetweenSourceAndDestination) {
898 pathsWithMiddlebox.add(path);
901 extractPathsWithMiddlebox(pathsWithMiddlebox, middleboxNode.getName());
903 if (pathsWithMiddlebox.isEmpty()) {
904 return new Verification("UNSAT",
906 "There are no paths between '" + sourceNode.getName() + "' and '"
907 + destinationNode.getName() + "' which traverse middlebox '"
908 + middleboxNode.getName() + "'. See below all the available paths");
911 printListsOfStrings("Paths with middlebox '" + middleboxNode.getName() + "'", pathsWithMiddlebox);
916 tempDir = createTempDir("traversal");
918 catch (IOException e) {
919 throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage());
922 String chainsFile = tempDir.getAbsolutePath() + "/chains.json";
923 generateChainsFile(graph, pathsWithMiddlebox, chainsFile);
925 String configFile = tempDir.getAbsolutePath() + "/config.json";
926 generateConfigFile(graph, configFile);
928 String traversalScenariosBasename = tempDir.getAbsolutePath() + "/TraversalScenario";
930 generateTestScenarios(chainsFile, configFile, traversalScenariosBasename);
931 }catch(Exception ex){
932 testClassGenerator = generatorFolderForGrpc + "/test_class_generator.py";
933 generateTestScenarios(chainsFile, configFile, traversalScenariosBasename);
936 String traversalTestsBasename = tempDir.getAbsolutePath() + "/TraversalTest";
938 generateTests( pathsWithMiddlebox.size(),
939 traversalScenariosBasename,
940 sourceNode.getName(),
941 destinationNode.getName(),
942 traversalTestsBasename);
943 }catch(InternalServerErrorException ex){
944 testGenerator = generatorFolderForGrpc + "/test_generator.py";
945 generateTests( pathsWithMiddlebox.size(),
946 traversalScenariosBasename,
947 sourceNode.getName(),
948 destinationNode.getName(),
949 traversalTestsBasename);
951 List<File> sourceFiles = new ArrayList<File>();
952 List<File> classFiles = new ArrayList<File>();
953 prepareForCompilationAndExecution( pathsWithMiddlebox.size(),
954 traversalScenariosBasename,
955 traversalTestsBasename,
959 compileFiles(sourceFiles, tempDir.getAbsolutePath());
961 tests = runFiles(tempDir.getAbsolutePath(), pathsWithMiddlebox, graph, classFiles);
963 for (Test t : tests) {
964 if (t.getResult().equals("UNSAT")) {
965 return new Verification("UNSAT",
967 "There is at least a path between '" + sourceNode.getName() + "' and '"
968 + destinationNode.getName() + "' traversing middlebox '"
969 + middleboxNode.getName() + "' where '" + sourceNode.getName()
970 + "' cannot reach '" + destinationNode.getName()
971 + "'. See below the paths that have been checked");
973 if (t.getResult().equals("UNKNOWN")) {
974 return new Verification("UNKNOWN",
976 "There is at least a path between '" + sourceNode.getName() + "' and '"
977 + destinationNode.getName() + "' traversing middlebox '"
978 + middleboxNode.getName() + "' where it is not guaranteed that '"
979 + sourceNode.getName() + "' can effectively reach '"
980 + destinationNode.getName()
981 + "'. See below the paths that have been checked");
985 extractPathsWithoutMiddlebox(pathsBetweenSourceAndDestination, middleboxNode.getName());
986 printListsOfStrings("Paths without middlebox '" + middleboxNode.getName() + "'", pathsBetweenSourceAndDestination);
988 if (pathsBetweenSourceAndDestination.isEmpty()) {
989 return new Verification("SAT",
991 "All the paths between node '" + sourceNode.getName() + "' and '"
992 + destinationNode.getName() + "' traverse middlebox '"
993 + middleboxNode.getName() + "'");
999 tempDir = createTempDir("traversal");
1001 catch (IOException e) {
1002 throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage());
1005 chainsFile = tempDir.getAbsolutePath() + "/chains.json";
1006 generateChainsFile(graph, pathsBetweenSourceAndDestination, chainsFile);
1008 configFile = tempDir.getAbsolutePath() + "/config.json";
1009 generateConfigFile(graph, configFile);
1011 traversalScenariosBasename = tempDir.getAbsolutePath() + "/TraversalScenario";
1012 generateTestScenarios(chainsFile, configFile, traversalScenariosBasename);
1014 traversalTestsBasename = tempDir.getAbsolutePath() + "/TraversalTest";
1015 generateTests( pathsBetweenSourceAndDestination.size(),
1016 traversalScenariosBasename,
1017 sourceNode.getName(),
1018 destinationNode.getName(),
1019 traversalTestsBasename);
1021 sourceFiles = new ArrayList<File>();
1022 classFiles = new ArrayList<File>();
1023 prepareForCompilationAndExecution( pathsBetweenSourceAndDestination.size(),
1024 traversalScenariosBasename,
1025 traversalTestsBasename,
1029 compileFiles(sourceFiles, tempDir.getAbsolutePath());
1031 tests = runFiles(tempDir.getAbsolutePath(), pathsBetweenSourceAndDestination, graph, classFiles);
1033 return evaluateTraversalResults(tests,
1034 sourceNode.getName(),
1035 destinationNode.getName(),
1036 middleboxNode.getName());
1040 private Verification evaluateTraversalResults( List<Test> tests, String source, String destination,
1042 Verification v = new Verification();
1043 boolean isSat = false;
1044 int unsatCounter = 0;
1045 for (Test t : tests) {
1046 v.getTests().add(t);
1048 if (t.getResult().equals("SAT")) {
1051 else if (t.getResult().equals("UNKNOWN")) {
1052 v.setResult("UNKNWON");
1053 v.setComment("There is at least one path from '" + source + "' to '" + destination
1054 + "' that doesn't traverse middlebox '" + middlebox
1055 + "' (see below all the paths that have been checked)");
1057 else if (t.getResult().equals("UNSAT")) {
1062 v.setResult("UNSAT");
1063 v.setComment("There is at least one path from '" + source + "' to '" + destination
1064 + "' that doesn't traverse middlebox '" + middlebox
1065 + "' (see below all the paths that have been checked)");
1067 else if (unsatCounter == tests.size()) {
1069 v.setComment("The only available paths from '" + source + "' to '" + destination
1070 + "' are those that traverse middlebox '" + middlebox
1071 + "' (see below the alternative paths that have been checked and are unusable)");
1076 private Verification reachabilityVerification(Graph graph, Node sourceNode, Node destinationNode) {
1077 Paths paths = getPaths(graph, sourceNode, destinationNode);
1079 if (paths.getPath().size() == 0) {
1080 return new Verification("UNSAT",
1081 "There are no available paths between '" + sourceNode.getName() + "' and '"
1082 + destinationNode.getName() + "'");
1085 List<List<String>> sanitizedPaths = sanitizePaths(paths);
1087 printListsOfStrings("Before loops removal", sanitizedPaths);
1089 eliminateLoopsInPaths(sanitizedPaths);
1091 printListsOfStrings("After loops removal", sanitizedPaths);
1093 if (sanitizedPaths.isEmpty()) {
1094 return new Verification("UNSAT",
1095 "There are no available paths between '" + sourceNode.getName() + "' and '"
1096 + destinationNode.getName() + "'");
1099 File tempDir = null;
1102 tempDir = createTempDir("reachability");
1104 catch (IOException e) {
1105 throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage());
1108 String chainsFile = tempDir.getAbsolutePath() + "/chains.json";
1109 generateChainsFile(graph, sanitizedPaths, chainsFile);
1111 String configFile = tempDir.getAbsolutePath() + "/config.json";
1112 generateConfigFile(graph, configFile);
1114 String reachabilityScenariosBasename = tempDir.getAbsolutePath() + "/ReachabilityScenario";
1116 generateTestScenarios(chainsFile, configFile, reachabilityScenariosBasename);
1117 }catch(InternalServerErrorException ex){
1118 testClassGenerator = generatorFolderForGrpc + "/test_class_generator.py";
1119 generateTestScenarios(chainsFile, configFile, reachabilityScenariosBasename);
1123 String reachabilityTestsBasename = tempDir.getAbsolutePath() + "/ReachabilityTest";
1125 generateTests( sanitizedPaths.size(),
1126 reachabilityScenariosBasename,
1127 sourceNode.getName(),
1128 destinationNode.getName(),
1129 reachabilityTestsBasename);
1130 }catch(InternalServerErrorException ex){
1131 testGenerator = generatorFolderForGrpc + "/test_generator.py";
1132 generateTests( sanitizedPaths.size(),
1133 reachabilityScenariosBasename,
1134 sourceNode.getName(),
1135 destinationNode.getName(),
1136 reachabilityTestsBasename);
1138 List<File> sourceFiles = new ArrayList<File>();
1139 List<File> classFiles = new ArrayList<File>();
1140 prepareForCompilationAndExecution( sanitizedPaths.size(),
1141 reachabilityScenariosBasename,
1142 reachabilityTestsBasename,
1146 compileFiles(sourceFiles, tempDir.getAbsolutePath());
1148 List<Test> tests = runFiles(tempDir.getAbsolutePath(), sanitizedPaths, graph, classFiles);
1150 return evaluateReachabilityResult(tests, sourceNode.getName(), destinationNode.getName());
1153 private Verification evaluateReachabilityResult(List<Test> tests, String source, String destination) {
1154 Verification v = new Verification();
1155 boolean sat = false;
1157 for (Test t : tests) {
1158 v.getTests().add(t);
1160 if (t.getResult().equals("SAT")) {
1163 else if (t.getResult().equals("UNKNOWN")) {
1164 v.setResult("UNKNWON");
1165 v.setComment("Reachability from '" + source + "' to '" + destination
1166 + "' is unknown. See all the checked paths below");
1168 else if (t.getResult().equals("UNSAT")) {
1174 v.setComment("There is at least one path '" + source + "' can use to reach '" + destination
1175 + "'. See all the available paths below");
1177 else if (unsat == tests.size()) {
1178 v.setResult("UNSAT");
1179 v.setComment("There isn't any path '" + source + "' can use to reach '" + destination
1180 + "'. See all the checked paths below");