Using Satisfiability Solvers to Test Network Configurations1 A comparison of three solvers’ performances Mendy Fisch (mendy@princeton.edu) Dr. Gary Levin, Telcordia Technologies, Inc. (glevin@telcordia.com) Contents Summary – 1 Description of the problem – 2 Input methods for network configurations – 3 Running times for each solver – 3 Acknowledgements – 6 References - 6 Summary This brief report describes the performance of various satisfiability solvers when used to test configurations of network addresses. The satisfiability solvers were fed files detailing a desired configuration of network addresses and asked to determine if the configuration were possible. Given a possible configuration, the solvers were asked to output numerical assignments for the network addresses that would satisfy the configuration requirements, if they were capable of doing so. This research was conducted with the eventual goal of finding a solver that could solve large instances of this problem using reasonable time and space. This could have useful commercial applications as part of a program that checks existing network configurations or configures network configurations according to desired parameters. All of the solvers tested appeared to take at least O(N^3) time to solve test configurations that were fed to them, where N is the number of subnets in the configuration. Physical memory was a major limiting factor. Running on a machine with 1536 MB of RAM, the solvers ran out of memory when testing large instances of the problem – which caused thrashing and page faults. The solvers tested included the Kodkod constraint solver for relational logic (http://web.mit.edu/emina/www/kodkod.html) utilizing both the MiniSat and MiniSatProver SAT solvers (http://minisat.se/), CVC3, an automatic theorem prover for SMT problems (http://www.cs.nyu.edu/acsys/cvc3/), and Boolector, an SMT solver for bitvectors (http://fmv.jku.at/boolector/), which was the winner in the bitvector division of the 2008 Satisfiability Modulo Theories Competition (http://smtcomp.org/). Kodkod and Boolector performed similarly in tests – both took about O(N^3) time and caused the machine to thrash or run out of memory for files containing 150 1 This material is based upon work supported by Air Force Rome Laboratories (AFRL) under contract FA8750-07-C-0030, funded by Dr. Carl Landwehr. Any opinions, findings and conclusions or recommendations expressed in this material are those of the authors and do not necessarily reflect the views of Air Force Rome Laboratories. 1 or more subnets. CVC began running out of memory on files with 50 or more subnets, so it was difficult to get an estimate for the order of the runtime of the problem under CVC. While cubic runtime is certainly not optimal, it seems that the first bottleneck is physical memory. A program that drains the machine’s memory will not evaluate in any amount of time. The first challenge for SAT solver developers should be to optimize the solvers for space; runtime can be addressed afterwards. Description of the problem The aim of the project is to utilize satisfiability solvers to determine whether given configurations of network addresses are valid, and generate numerical assignments for unassigned network addresses if the configuration is valid. This is accomplished by expressing the desired hierarchy of IP addresses as a logical statement and running the statement through a satisfiability solver to generate a set of addresses that will satisfy the configuration. The most important operation in the problem is checking whether a given IP address falls within the range of a given subnet address – that is, whether it would be possible for this IP address to be below the subnet address in the network hierarchy and hence “contained” in the subnet. Linking “contained” operations together with the logical operators AND, NOT, and EQUALS allows us to describe a hierarchical network of IP addresses, some representing large subnets, others representing smaller subnets, and still others representing individual machines. One way to check if one IP address is “contained” within a subnet involves performing bitvector operations on the address and the subnet. Given a subnet address A and a mask M that indicates which bits in A are host-specific, as well as an IP address B and a mask N that indicates which bits in B are host specific, the “contained” statement can be expressed as: (M ≥ N) AND A & (-1 << M) = B & (-1 << M) where A and B are 32-bit integers, M and N are numbers between 0 (no bits are significant) and 32 (all bits are significant), & is a bitvector AND operation, and (-1 << M) is an instruction to left-shift a 32-bit integer with all bits on by M bits. An advantage of this method is that many satisfiability solvers are optimized for bitvector operations - or, in the case of Boolector, are only able to work with bitvectors. Another way to implement “contained” would be to avoid using bit-whacking operations and stick to integer comparisons. In this case, we would need to transform the subnet address (A) and the address we want to test (B) into network ids by computing A – (A modulo M) and B – (B modulo N), where M is A’s mask and N is B’s mask. Then, we could express the contained statement as: networkId(A) ≤ networkId(B) AND networkId(A) + 2M ≥ networkdId(B) + 2N Though we tried to program this method in CVC3, we failed because there was no way to implement the modulo function in CVC. Running a sample file through CVC (see http://www.princeton.edu/~mendy/CVC/SMT-002.txt) generated the following error: “*** fatal error in theory_core.cpp:325 (false) 2 Equivalence classes didn't get merged.” Input methods for network configurations Network configurations were expressed in postfix notation, and translated to the appropriate format for each satisfiability solver. For an example of a small file in the postfix notation (Qunatifier Free Format), see http://www.princeton.edu/~mendy/QFF/QFF-002.txt. (For more examples, see the directory http://www.princeton.edu/~mendy/QFF.) The java program http://www.princeton.edu/~mendy/parser_bitvector.java translates QFF files to CVC3’s presentation input language, implementing “contained” using the bitwhacking method. All of the BIN-* files in the directory http://www.princeton.edu/~mendy/CVC/ were generated using this translator. The translator http://www.princeton.edu/~mendy/parser_smt.java translates QFF files to CVC3’s presentation input language, but implements “contained” using integer comparisons. An example of a file generated using this translator is http://www.princeton.edu/~mendy/CVC/SMT-002.txt. CVC fails to evaluate this file, for the reasons described above. To prepare files for Boolector, we translate files first to CVC’s presentation input language, and then use CVC’s translate function to translate the file to the SMT-LIB format that Boolector understands. The file http://www.princeton.edu/~mendy/parser_bitvector_noint.java outputs files that can be translated by CVC to SMT-LIB format. It differs from parser_bitvector.java because it ensures all bitvectors used are of the same size and changes all integer constraints (i.e. 4 = 4) into bitvector constraints. Files in http://www.princeton.edu/~mendy/Boolector/ were generated by running the QFF files through the parser_bitvector_noint translator and feeding the results to CVC3’s translator (cvc3 +translate –output-lang smt). Writing a more direct translator may have been more efficient, but was unnecessary since we are seeking to compare solvers rather than come up with efficient translation methods. Running times for each solver Following is a table summarizing the performance of each solver on the given input files. The solvers were run on a VMware virtual machine running Red Hat Enterprise Linux 4, utilizing 1536 megabytes of RAM and 2 2.4 GHZ Intel Core Duo processors. For programs requiring Java, Java was assigned 1500 MB of RAM (using –Xmx1500m). Where it appeared that memory limitations were causing a significant slowdown in the program, this fact is noted. The files used can be found using the links provided above. Each filename indicates how many subnets it is checking. Files ending in const4 replace all of the mask variables with 4, and files ending in eq4 append the condition that each mask variable equal 4. This makes for a faster runtime. Boolector File 002 Solving 0.039 3 050-const4 050-eq4 050 100-const4 100-eq4 100 150-const4 150-eq4 150 200-const4 200-eq4 200 1.739 1.612 10.888 5.822 6.003 91.441 12.590 14.728 * out of memory error ([btormem] out of memory in 'btor_realloc') 46.826 * observed memory limitations using vmstat 594.158 * observed memory limitations using vmstat * out of memory error ([btormem] out of memory in 'btor_realloc') KodKod - MiniSat File 002 050-const4 050-eq4 050 100-const4 100-eq4 100 150-const4 150-eq4 150 200-const4 200-eq4 200 Solving 0.008 0.212 0.935 5.728 0.559 4.850 55.950 1.150 10.204 1945.287 * observed memory limitations using vmstat 3.319 *** stopped after 30 minutes, observed memory limitations *** stopped after 30 minutes, observed memory limitations KodKod - MiniSatProver File Solving 002 0.012 050-const4 0.205 050-eq4 2.076 050 162.813 * observed memory limitations using vmstat 100-const4 1.026 100-eq4 10.717 100 *** stopped after 30 minutes, observed memory limitations 150-const4 2.249 150-eq4 *** stopped after 30 minutes, got out of memory error message 150 *** stopped after 30 minutes, got out of memory error message 200-const4 76.358 200-eq4 *** stopped after 30 minutes, got out of memory error message 200 *** stopped after 30 minutes, got out of memory error message 4 CVC3 002 050-const4 050-eq4 050 100-const4 11.624 662.809 * observed memory limitations *** stopped after 30 minutes, observed memory limitations *** stopped after 30 minutes, observed memory limitations *** stopped after 30 minutes, observed memory limitations I was able to observe memory limitations by running the vmstat command on the Linux machine. If there were large numbers in the “swpd” column (for virtual memory usage), the “si” column (for page-ins), and the “so” column (for page outs), there was likely not enough physical memory, and paging was probably causing the runtime of the program to become significantly slower. Following is a sample vmstat printout indicating low memory. Notice the large values in the “swpd,” “si,” and “so” fields. If the program were not demanding more memory than was available, these fields would probably have contained zeroes. procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu---r b swpd free buff cache si so bi bo in cs us sy id wa 3 1 229444 17464 488 21288 46 116 151 288 519 168 8 57 25 9 4 0 239292 14456 216 19864 363 1020 906 1057 999 222 4 73 4 19 3 0 240636 14400 220 19472 922 466 1786 507 1037 259 2 44 12 41 0 4 253172 15672 224 21456 1218 1289 2337 1397 1071 327 2 35 12 51 2 6 258496 14656 232 18044 835 557 1570 675 1068 298 2 56 8 35 Occasionally, Java or Boolector generated an error message when it reached a memory limit. Kodkod did this when solving 200-eq4 and 200 with MiniSatProver. The error message read: “Free swap: 393216 pages of RAM 163824 pages of HIGHMEM 4593 reserved pages 6151 pages shared 501 pages swap cached Out of Memory: Killed process 19695 (java)” Each time I received this message, my machine froze and I needed to restart it. Boolector also outputted an error message for 200 and 150, “[btormem] out of memory in 'btor_realloc.'” Looking at the times that were not affected by memory limitations (we can really only do this for Kodkod with MiniSat and Boolector), it seems that the running time is cubic for the regular files (with unassigned mask variables) and quadratic for the files with assigned mask variables. T(100)/T(50) for Boolector = 9.441/10.888 = 8.4, and T(100)/T(50) for Kodkod = 55.950/5.728 = 9.77. Both of these ratios are 5 around 9, indicating cubic time. Most of time ratios for doubling files with assigned mask variables are around 4, indicating quadratic time. Though using cubic or quadratic time is often a cause for concern, it is clear that the biggest problem at the moment is the amount of memory taken up by the solvers. Either the solvers should be adjusted to consume less memory per variable, or the configuration files should be rewritten to contain fewer variables. Acknowledgements This work was performed under guidance of Professor Sharad Malik at Princeton, Professor Clark Barrett at New York University and Dr. Sanjai Narain at Telcordia. The idea of using Kodkod for network configuration was developed in Telcordia's ConfigAssure project. The Kodkod system was created by Emina Torlak at MIT under guidance of Professor Daniel Jackson. References 1. S. Narain, G. Levin, V. Kaul, S. Malik. “Declarative Infrastructure Configuration Synthesis and Debugging.” Journal of Network Systems and Management, Special Issue on Security Configuration, eds. Ehab Al-Shaer, Charles Kalmanek, Felix Wu. 2008. Available online: http://www.argreenhouse.com/papers/narain/DeclarativeConfiguration.pdf 2. Alloy: http://alloy.mit.edu/ 3. Kodkod: http://web.mit.edu/emina/www/kodkod.html 4. Chaff: http://www.princeton.edu/~chaff 5. MiniSat: http://minisat.se/ 6. Y. Mahajan, Z. Fu, S. Malik. “Zchaff2004, An Efficient SAT Solver.” Proceedings of 7th International Conference on Theory and Applications of Satisfiability Testing (SAT). 2004. 6