CS1950Y Lecture 23:
April 6th, 2018


Announcements

If you've chosen to work with epistemic logic for the final project, there is a great resource available online from the Brown library! Check out the book One Hundred Prisoners and a Light Bulb, by Ditmarsch and Kooi.

We recently discovered an anonymization issue in the way we were storing the data collected during the course labs; this error resulted in your logins getting stored along with the data we had intended to collect. We are working on wiping out this portion of the record. If you have any questions or concerns about this unintended collection, please bring them up with Tim.

An application for Alloy!

Today, we'll be discussing a practical application of a modeling tool; specifically, we'll be bringing the power of Alloy to bear on the problem of router configuration.

A router has a few crucial determinations it has to make as it manages your network:

Ensuring that a particular router configuration will answer the above questions correctly for every packet it recieves is a tough job, but it turns out that we can make it a little easier with the help of Alloy!

A router configuration file is formatted as a list of rules which describe which packets are permitted to travel through the network, where a given packet should be sent next, and if and how the packet should be modified. We can write a compiler that takes in a router config file and generates an Alloy spec from it. The spec contains three predicates describing the behaviors required by the config file:

The translation of router config code into this Alloy specification is trivial, because the "list of rules" format of the config file is quite similar to Alloy code. If the config file specifies that a packet should be permitted in if it comes from IP address A or it it comes from IP address B, we can specify permit(p) thus:


all p: Packet | permit(p) iff p.sourceIP == A or p.sourceIP == B

As the rule get grows, we can just expand this disjunction with each new rule. We'll gloss over exactly how we write route(p, next) and nat(p, p'), but they are similarly simple.

Now, put yourself in the shoes of an overworked sysadmin. With access to the spec above, what kinds of things could you check about your router configuration? Using assert statements, you could verify that particular types of bad packets never enter your system (by using permit(p)), that a particular packet always gets sent to the right place (using route(p, next)), or any of a number of other important system invariants.

But let's think even bigger. Imagine you get paged at 3am by your angry traveling boss who wants to know why they can't access the nextwork from an internet cafe in Paris. You wake up in a hurry, drive in to work, add a new line to your enormous and complicated config file, and your hand is hovering over the commit button. You're pretty sure you fixed your boss' problem, but want to make sure there wasn't any unexpected interaction between the fix and the other 500 lines of the configuration.

You can ensure that no unexpected changes have occurred using the following assert:


assert noDiff {
    all p: Packet | not bossPacket(p) implies (permit_new(p) iff permit_old(p))
}

That is, any packet that isn't your boss' ought to be treated the same way under the new router configuration as it was under the old. If the assert fails, Alloy will provide you with a specific counterexample of how it fails, and you'll have a chance to debug before pushing your problematic change.

One way to think about this type of assert is that it is like running a semantic diff between the old config and the new config. Rather than just point out the line that you changed (which is what the standard syntactic diff would do) this assert actually looks for the changes in system behavior that resulted from the added line of code.

This is all well and good, but when we try to check noDiff Alloy will force us to specify bounds for the check. How can we be sure that we are providing enough distinct packets and IP addresses to find a counterexample, if indeed there is one?

As it turns out, automatically determining sufficient bounds for an arbitrary Alloy spec is undecidable, but for some particular formulations, it can be done. In the early 20th century, Bernays, Schoenfinkel and Ramsey proved that if you have a series of n existentially quantified variables, followed by a series of m universally quantified variables, and the body of the quantifier only refers to your n+m variables and no others, the entire expression is satisfiable iff it is satisfied by some instance of size n. That is, if you have a formula of this form:


    some x1,x2...xn | all y1,y2...ym | expression containing no other vars

There exists a satisfying instance of size n iff the formula is satisfiable at all.