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 *******************************************************************************/
9 package it.polito.verigraph.service;
11 import java.io.IOException;
12 import java.util.Date;
13 import java.util.ArrayList;
14 import java.util.Calendar;
15 import java.util.HashMap;
16 import java.util.List;
18 import java.util.logging.Logger;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21 import java.util.stream.Collectors;
22 import javax.xml.bind.JAXBException;
23 import com.fasterxml.jackson.core.JsonParseException;
24 import com.fasterxml.jackson.databind.JsonMappingException;
25 import com.microsoft.z3.*;
26 import it.polito.neo4j.exceptions.MyInvalidDirectionException;
27 import it.polito.neo4j.jaxb.Paths;
28 import it.polito.neo4j.manager.Neo4jDBManager;
29 import it.polito.neo4j.manager.Neo4jLibrary;
30 import it.polito.neo4j.translator.*;
31 import it.polito.verigraph.exception.BadRequestException;
32 import it.polito.verigraph.exception.DataNotFoundException;
33 import it.polito.verigraph.exception.ForbiddenException;
34 import it.polito.verigraph.model.Graph;
35 import it.polito.verigraph.model.Node;
36 import it.polito.verigraph.model.Test;
37 import it.polito.verigraph.model.Verification;
38 import it.polito.verigraph.resources.beans.VerificationBean;
39 import it.polito.verigraph.solver.GeneratorSolver;
40 import it.polito.verigraph.solver.Scenario;
42 public class VerificationService {
44 private Neo4jDBManager manager=new Neo4jDBManager();
45 //private final static Logger LOGGER = Logger.getLogger(VerigraphLogger.class.getName());
46 public static VerigraphLogger vlogger = VerigraphLogger.getVerigraphlogger();
48 public VerificationService() {}
50 private Paths getPaths(Graph graph, Node sourceNode, Node destinationNode) throws MyInvalidDirectionException {
52 String source = sourceNode.getName();
53 String destination = destinationNode.getName();
55 paths=manager.getPath(graph.getId(), source, destination, "outgoing");
59 private List<List<String>> sanitizePaths(Paths paths) {
60 List<List<String>> sanitizedPaths = new ArrayList<List<String>>();
61 for (String path : paths.getPath()) {
62 List<String> newPath = extractPath(path);
63 sanitizedPaths.add(newPath);
65 return sanitizedPaths;
68 private List<String> extractPath(String path) {
69 List<String> newPath = new ArrayList<String>();
70 // find all nodes, i.e. all names between parentheses
71 Matcher m = Pattern.compile("\\(([^)]+)\\)").matcher(path);
73 String node = m.group(1);
79 private void printListsOfStrings(String message, List<List<String>> lists) {
80 vlogger.logger.info(message);
81 for (List<String> element : lists) {
82 StringBuilder paths= new StringBuilder();
83 for(String s : element){
86 vlogger.logger.info(paths.toString());
87 //System.out.println(element);
91 public Verification verify(long graphId, VerificationBean verificationBean) throws MyInvalidDirectionException, JsonParseException, JsonMappingException, JAXBException, IOException {
93 throw new ForbiddenException("Illegal graph id: " + graphId);
95 GraphService graphService = new GraphService();
96 Graph graph = graphService.getGraph(graphId);
98 throw new DataNotFoundException("Graph with id " + graphId + " not found");
100 String source = verificationBean.getSource();
101 String destination = verificationBean.getDestination();
102 String type = verificationBean.getType();
103 if (source == null || source.equals("")) {
104 throw new BadRequestException("Please specify the 'source' parameter in your request");
106 if (destination == null || destination.equals("")) {
107 throw new BadRequestException("Please specify the 'destination' parameter in your request");
109 if (type == null || type.equals("")) {
110 throw new BadRequestException("Please specify the 'type' parameter in your request");
113 Node sourceNode = graph.searchNodeByName(verificationBean.getSource());
114 Node destinationNode = graph.searchNodeByName(verificationBean.getDestination());
116 if (sourceNode == null) {
117 throw new BadRequestException("The 'source' parameter '" + source + "' is not valid, please insert the name of an existing node");
119 if (destinationNode == null) {
120 throw new BadRequestException("The 'destination' parameter '" + destination + "' is not valid, please insert the name of an existing node");
122 if ((!type.equals("reachability")) && (!type.equals("isolation")) && (!type.equals("traversal"))) {
123 throw new BadRequestException("The 'type' parameter '"+ type
124 + "' is not valid: valid types are: 'reachability', 'isolation' and 'traversal'");
127 Verification v = null;
132 v = reachabilityVerification(graph, sourceNode, destinationNode);
135 middlebox = verificationBean.getMiddlebox();
136 if (middlebox == null || middlebox.equals("")) {
137 throw new BadRequestException("Please specify the 'middlebox' parameter in your request");
140 middleboxNode = graph.searchNodeByName(middlebox);
141 if (middleboxNode == null) {
142 throw new BadRequestException("The 'middlebox' parameter '" + middlebox + "' is not valid, please insert the name of an existing node");
144 if (middleboxNode.getFunctional_type().equals("endpoint")) {
145 throw new BadRequestException("'"+ middlebox
146 + "' is of type 'endpoint', please choose a valid middlebox");
148 v = isolationVerification(graph, sourceNode, destinationNode, middleboxNode);
151 middlebox = verificationBean.getMiddlebox();
152 if (middlebox == null || middlebox.equals("")) {
153 throw new BadRequestException("Please specify the 'middlebox' parameter in your request");
156 middleboxNode = graph.searchNodeByName(middlebox);
157 if (middleboxNode == null) {
158 throw new BadRequestException("The 'middlebox' parameter '" + middlebox + "' is not valid, please insert the name of an existing node");
160 if (middleboxNode.getFunctional_type().equals("endpoint")) {
161 throw new BadRequestException("'"+ middlebox
162 + "' is of type 'endpoint', please choose a valid middlebox");
164 v = traversalVerification(graph, sourceNode, destinationNode, middleboxNode);
172 private Verification isolationVerification(Graph graph, Node sourceNode, Node destinationNode, Node middleboxNode) throws MyInvalidDirectionException {
173 Long time_isolation=(long) 0;
174 Calendar cal_isolation = Calendar.getInstance();
175 Date start_time_isolation = cal_isolation.getTime();
176 Paths paths = getPaths(graph, sourceNode, destinationNode);
177 if (paths.getPath().size() == 0) {
178 return new Verification("UNSAT",
179 "There are no available paths between '"+ sourceNode.getName() + "' and '"
180 + destinationNode.getName() + "'");
183 List<List<String>> sanitizedPaths = sanitizePaths(paths);
184 List<Test> tests = extractTestsFromPaths(graph, sanitizedPaths, "UNKNWON");
186 //printListsOfStrings("sanitizedPaths", sanitizedPaths);
188 if (sanitizedPaths.isEmpty()) {
189 return new Verification("UNSAT",
190 "There are no available paths between '"+ sourceNode.getName() + "' and '"
191 + destinationNode.getName() + "'");
193 extractPathsWithoutMiddlebox(sanitizedPaths, middleboxNode.getName());
195 if (sanitizedPaths.isEmpty()) {
196 return new Verification("UNSAT",
198 "There are no available paths between '"+ sourceNode.getName() + "' and '"
199 + destinationNode.getName() + "' which no traverse middlebox '"
200 + middleboxNode.getName() + "'. See below all the available paths.");
203 //printListsOfStrings("Paths with middlebox '" + middleboxNode.getName() + "'", sanitizedPaths);
205 Map<Integer, GeneratorSolver> scenarios=createScenarios(sanitizedPaths, graph);
207 tests = run(graph, scenarios, sourceNode.getName(), destinationNode.getName());
208 Verification isolation=evaluateIsolationResults(tests, sourceNode.getName(),
209 destinationNode.getName(),
210 middleboxNode.getName());
211 Calendar cal_isolation_stop = Calendar.getInstance();
212 time_isolation = time_isolation +(cal_isolation_stop.getTime().getTime() - start_time_isolation.getTime());
213 vlogger.logger.info("Time to check reachability policy: " + time_isolation);
214 //System.out.println("Time to check reachability policy: " + time_isolation);
218 private List<Test> extractTestsFromPaths(Graph graph, List<List<String>> paths, String result) {
219 List<Test> tests = new ArrayList<Test>();
220 for (List<String> path : paths) {
221 List<Node> nodes = new ArrayList<Node>();
222 for (String nodeName : path) {
223 nodes.add(graph.searchNodeByName(nodeName));
225 tests.add(new Test(nodes, result));
230 private Verification evaluateIsolationResults(List<Test> tests, String source, String destination,
232 Verification v = new Verification();
233 boolean isSat = false;
234 int unsatCounter = 0;
235 for (Test t : tests) {
237 if (t.getResult().equals("SAT")) {
240 else if (t.getResult().equals("UNKNOWN")) {
241 v.setResult("UNKNOWN");
242 v.setComment("Isolation property with source '"+ source + "', destination '" + destination
243 + "' and middlebox '" + middlebox + "' is UNKNOWN because although '" + source
244 + "' cannot reach '" + middlebox + "' in any path from '" + source + "' to '"
245 + destination + "' which traverses middlebox '" + middlebox
246 + "' at least one reachability test between '" + source + "' and '" + middlebox
247 + "' returned UNKNOWN (see below all the paths that have been checked)");
249 else if (t.getResult().equals("UNSAT")) {
255 v.setComment("Isolation property with source '"+ source + "', destination '" + destination
256 + "' and middlebox '" + middlebox + "' is SATISFIED because reachability between '" + source
257 + "' and '" + middlebox + "' is UNSATISFIED in all paths between '" + source + "' and '"
258 + destination + "' which traverse middlebox '" + middlebox
259 + "' (see below all the paths that have been checked)");
262 else if (unsatCounter == tests.size()) {
263 v.setResult("UNSAT");
264 v.setComment("Isolation property with source '"+ source + "', destination '" + destination
265 + "' and middlebox '" + middlebox + "' is UNSATISFIED because reachability between '"
266 + source + "' and '" + middlebox + "' is SATISFIED in at least one path between '" + source
267 + "' and '" + destination + "' which traverses middlebox '" + middlebox
268 + "' (see below all the paths that have been checked)");
273 private void extractPathsWithMiddlebox(List<List<String>> sanitizedPaths, String middleboxName) {
274 List<List<String>> pathsToBeRemoved = new ArrayList<List<String>>();
275 for (List<String> path : sanitizedPaths) {
276 boolean middleboxFound = false;
277 for (String node : path) {
278 if (node.equals(middleboxName)) {
279 middleboxFound = true;
283 if (!middleboxFound) {
284 pathsToBeRemoved.add(path);
287 for (List<String> path : pathsToBeRemoved) {
288 sanitizedPaths.remove(path);
293 private void extractPathsWithoutMiddlebox(List<List<String>> sanitizedPaths, String middleboxName) {
294 List<List<String>> pathsToBeRemoved = new ArrayList<List<String>>();
295 for (List<String> path : sanitizedPaths) {
296 boolean middleboxFound = false;
297 for (String node : path) {
298 if (node.equals(middleboxName)) {
299 middleboxFound = true;
303 if (middleboxFound) {
304 pathsToBeRemoved.add(path);
307 for (List<String> path : pathsToBeRemoved) {
308 sanitizedPaths.remove(path);
312 private Verification traversalVerification(Graph graph, Node sourceNode, Node destinationNode, Node middleboxNode) throws MyInvalidDirectionException {
313 Long time_traversal=(long) 0;
314 Calendar cal_traversal = Calendar.getInstance();
315 Date start_time_traversal = cal_traversal.getTime();
316 Paths paths = getPaths(graph, sourceNode, destinationNode);
317 if (paths.getPath().size() == 0) {
318 return new Verification("UNSAT",
319 "There are no available paths between '"+ sourceNode.getName() + "' and '"
320 + destinationNode.getName() + "'");
323 List<List<String>> pathsBetweenSourceAndDestination = sanitizePaths(paths);
325 //printListsOfStrings("Paths", pathsBetweenSourceAndDestination);
327 if (pathsBetweenSourceAndDestination.isEmpty()) {
328 return new Verification("UNSAT",
329 "There are no available paths between '"+ sourceNode.getName() + "' and '"
330 + destinationNode.getName() + "'");
333 List<Test> tests = extractTestsFromPaths(graph, pathsBetweenSourceAndDestination, "UNKNOWN");
335 List<List<String>> pathsWithMiddlebox = new ArrayList<List<String>>();
336 for (List<String> path : pathsBetweenSourceAndDestination) {
337 pathsWithMiddlebox.add(path);
340 extractPathsWithMiddlebox(pathsWithMiddlebox, middleboxNode.getName());
342 if (pathsWithMiddlebox.isEmpty()) {
343 return new Verification("UNSAT",
345 "There are no paths between '"+ sourceNode.getName() + "' and '"
346 + destinationNode.getName() + "' which traverse middlebox '"
347 + middleboxNode.getName() + "'. See below all the available paths");
350 //printListsOfStrings("Paths with middlebox '" + middleboxNode.getName() + "'", pathsWithMiddlebox);
352 Map<Integer, GeneratorSolver> scenarios=createScenarios(pathsWithMiddlebox, graph);
354 tests = run(graph, scenarios, sourceNode.getName(), destinationNode.getName());
356 for (Test t : tests) {
357 if (t.getResult().equals("UNSAT")) {
358 return new Verification("UNSAT",
360 "There is at least a path between '"+ sourceNode.getName() + "' and '"
361 + destinationNode.getName() + "' traversing middlebox '"
362 + middleboxNode.getName() + "' where '" + sourceNode.getName()
363 + "' cannot reach '" + destinationNode.getName()
364 + "'. See below the paths that have been checked");
366 if (t.getResult().equals("UNKNOWN")) {
367 return new Verification("UNKNOWN",
369 "There is at least a path between '"+ sourceNode.getName() + "' and '"
370 + destinationNode.getName() + "' traversing middlebox '"
371 + middleboxNode.getName() + "' where it is not guaranteed that '"
372 + sourceNode.getName() + "' can effectively reach '"
373 + destinationNode.getName()
374 + "'. See below the paths that have been checked");
378 extractPathsWithoutMiddlebox(pathsBetweenSourceAndDestination, middleboxNode.getName());
379 printListsOfStrings("Paths without middlebox '" + middleboxNode.getName() + "'", pathsBetweenSourceAndDestination);
381 if (pathsBetweenSourceAndDestination.isEmpty()) {
382 return new Verification("SAT",
384 "All the paths between node '"+ sourceNode.getName() + "' and '"
385 + destinationNode.getName() + "' traverse middlebox '"
386 + middleboxNode.getName() + "'");
390 Map<Integer, GeneratorSolver> scenarios2=createScenarios( pathsBetweenSourceAndDestination, graph);
392 tests = run(graph, scenarios2, sourceNode.getName(), destinationNode.getName());
394 Verification traversal= evaluateTraversalResults(tests,
395 sourceNode.getName(),
396 destinationNode.getName(),
397 middleboxNode.getName());
399 Calendar cal_traversal_stop = Calendar.getInstance();
400 time_traversal = time_traversal +(cal_traversal_stop.getTime().getTime() - start_time_traversal.getTime());
401 vlogger.logger.info("Time to check traversal policy: " + time_traversal);
402 //System.out.println("Time to check traversal policy: " + time_traversal);
406 private Verification evaluateTraversalResults(List<Test> tests, String source, String destination,
408 Verification v = new Verification();
409 boolean isSat = false;
410 int unsatCounter = 0;
411 for (Test t : tests) {
414 if (t.getResult().equals("SAT")) {
417 else if (t.getResult().equals("UNKNOWN")) {
418 v.setResult("UNKNWON");
419 v.setComment("There is at least one path from '"+ source + "' to '" + destination
420 + "' that doesn't traverse middlebox '" + middlebox
421 + "' (see below all the paths that have been checked)");
423 else if (t.getResult().equals("UNSAT")) {
428 v.setResult("UNSAT");
429 v.setComment("There is at least one path from '"+ source + "' to '" + destination
430 + "' that doesn't traverse middlebox '" + middlebox
431 + "' (see below all the paths that have been checked)");
433 else if (unsatCounter == tests.size()) {
435 v.setComment("The only available paths from '"+ source + "' to '" + destination
436 + "' are those that traverse middlebox '" + middlebox
437 + "' (see below the alternative paths that have been checked and are unusable)");
442 private Verification reachabilityVerification(Graph graph, Node sourceNode, Node destinationNode) throws MyInvalidDirectionException {
443 Long time_reachability=(long) 0;
444 Calendar cal_reachability = Calendar.getInstance();
445 Date start_time_reachability = cal_reachability.getTime();
447 Paths paths = getPaths(graph, sourceNode, destinationNode);
449 if (paths.getPath().size() == 0) {
450 return new Verification("UNSAT",
451 "There are no available paths between '"+ sourceNode.getName() + "' and '"
452 + destinationNode.getName() + "'");
455 List<List<String>> sanitizedPaths = sanitizePaths(paths);
457 printListsOfStrings("Paths", sanitizedPaths);
459 if (sanitizedPaths.isEmpty()) {
460 return new Verification("UNSAT",
461 "There are no available paths between '"+ sourceNode.getName() + "' and '"
462 + destinationNode.getName() + "'");
465 Map<Integer, GeneratorSolver> scenarios=createScenarios(sanitizedPaths, graph);
467 List<Test> tests = run(graph, scenarios, sourceNode.getName(), destinationNode.getName());
469 Calendar cal_reachability_after_run = Calendar.getInstance();
470 time_reachability = time_reachability +(cal_reachability_after_run.getTime().getTime() - start_time_reachability.getTime());
471 vlogger.logger.info("Time reachability after run: " + time_reachability);
472 //System.out.println("Time reachability after run: " + time_reachability);
474 Verification reachability= evaluateReachabilityResult(tests, sourceNode.getName(), destinationNode.getName());
476 Calendar cal_reachability_stop = Calendar.getInstance();
477 time_reachability = time_reachability +(cal_reachability_stop.getTime().getTime() - start_time_reachability.getTime());
478 vlogger.logger.info("Time to check reachability policy: " + time_reachability);
479 //System.out.println("Time to check reachability policy: " + time_reachability);
484 private List<Test> run(Graph graph, Map<Integer, GeneratorSolver> scenarios, String src, String dst) {
485 List<Test> tests = new ArrayList<Test>();
489 //Long time=(long) 0;
490 //Calendar cal = Calendar.getInstance();
491 //Date start_time = cal.getTime();
493 for(Map.Entry<Integer, GeneratorSolver> t : scenarios.entrySet()){
495 result=t.getValue().run(src, dst);
497 List<Node> path = new ArrayList<Node>();
498 for (String nodeString : t.getValue().getPaths()) {
499 Node node = graph.searchNodeByName(nodeString);
502 Test test = new Test(path, result);
506 //Calendar cal2 = Calendar.getInstance();
507 //time = time +(cal2.getTime().getTime() - start_time.getTime());
508 //System.out.println("Time occur to run: " + time);
513 private Map<Integer, GeneratorSolver> createScenarios(List<List<String>> sanitizedPaths, Graph graph) {
515 Map<Integer, GeneratorSolver> scenarios=new HashMap<Integer, GeneratorSolver>();
516 for(List<String> s : sanitizedPaths){
517 Scenario tmp=new Scenario(graph, s);
518 tmp.createScenario();
519 GeneratorSolver gs=new GeneratorSolver(tmp, s);
521 scenarios.put(index++, gs);
526 private Verification evaluateReachabilityResult(List<Test> tests, String source, String destination) {
527 Verification v = new Verification();
530 for (Test t : tests) {
532 if (t.getResult().equals("SAT")) {
536 else if (t.getResult().equals("UNKNOWN")) {
537 v.setResult("UNKNWON");
538 v.setComment("Reachability from '"+ source + "' to '" + destination
539 + "' is unknown. See all the checked paths below");
542 else if (t.getResult().equals("UNSAT")) {
549 v.setComment("There is at least one path '"+ source + "' can use to reach '" + destination
550 + "'. See all the available paths below");
553 else if (unsat == tests.size()) {
554 v.setResult("UNSAT");
555 v.setComment("There isn't any path '"+ source + "' can use to reach '" + destination
556 + "'. See all the checked paths below");
563 public List<List<Node>> getPaths(long graphId, String source, String destination) throws JsonParseException, JsonMappingException, JAXBException, IOException, MyInvalidDirectionException {
565 throw new ForbiddenException("Illegal graph id: " + graphId);
567 GraphService graphService = new GraphService();
568 Graph graph = graphService.getGraph(graphId);
570 throw new DataNotFoundException("Graph with id " + graphId + " not found");
573 if (source == null || source.equals("")) {
574 throw new BadRequestException("Please specify the 'source' parameter in your request");
576 if (destination == null || destination.equals("")) {
577 throw new BadRequestException("Please specify the 'destination' parameter in your request");
580 Node sourceNode = graph.searchNodeByName(source);
581 Node destinationNode = graph.searchNodeByName(destination);
583 if (sourceNode == null) {
584 throw new BadRequestException("The 'source' parameter '" + source + "' is not valid, please insert the name of an existing node");
586 if (destinationNode == null) {
587 throw new BadRequestException("The 'destination' parameter '" + destination + "' is not valid, please insert the name of an existing node");
590 Paths all_paths = getPaths(graph, sourceNode, destinationNode);
592 if (all_paths.getPath().size() == 0) {
593 vlogger.logger.info("No path available");
594 //System.out.println("No path available");
598 List<List<String>> sanitizedPaths = sanitizePaths(all_paths);
600 printListsOfStrings("Paths", sanitizedPaths);
602 if (sanitizedPaths.isEmpty()) {
606 List<List<Node>> paths=new ArrayList<List<Node>>();
607 List<Node> p= new ArrayList<Node>();
609 for(int i=0; i<sanitizedPaths.size(); i++){
610 List<String> name=sanitizedPaths.get(i);
611 for(int j=0; j<name.size(); j++){
612 Node n=graph.searchNodeByName(name.get(j));