Skip to content
This repository has been archived by the owner on Jun 27, 2018. It is now read-only.

Language Basics

coxjacob edited this page Jul 3, 2015 · 21 revisions

The Policy: Pyretic's Foundation

Conceptually, a Pyretic policy is a function that takes a packet as input and returns a set of packets. This function describes what the network switches should do with incoming packets. For example a function that takes any packet and returns the empty set when applied as the overall network policy will cause the network to drop all packets. Likewise a function that takes any packet arriving at a given location (switch and port) and returns the set of packets which are identical to the original but located respectively at the ports at that switch which lie on the network spanning tree, applied as the overall network policy will cause the network to flood all packets. However, Pyretic policies can be built from other policies using combinators such as parallel and sequential composition, so policies such as the ones above can be building blocks of the overall network policy, as opposed to only being used as the overall network policy itself. The MAC learner module provides an example of just such a policy.

As Pyretic is embedded in Python, we've chosen to implement these conceptual policy functions as instances of classes that descend from the Policy class defined in language.py. While these classes have lots of interesting implementation details (feel free to poke about the code), the member function 'eval' of a class comprises the conceptual function that takes a packet and returns a set of packets.

Summary of Basic Policies

POLICY SYNTAX SEMANTICS EXAMPLE
match match(f=v) returns set containing packet if packet's field f matches value v, empty set otherwise match(dstmac=EthAddr('00:00:00:00:00:01'))
drop drop returns empty set drop
identity identity returns set containing copy of packet identity
modify modify(f=v) returns set containing copy of packet where field f is set to value v modify(srcmac=EthAddr('00:00:00:00:00:01'))
forward fwd(a) returns set containing copy of packet where outport field is set to a fwd(1)
flood flood() returns set containing one copy of packet for each port on the spanning tree flood()
parallel composition A + B returns the union of A's output and B's output fwd(1) + fwd(2)
sequential composition A >> B returns B's output where A's output is B's input modify(dstip=IPAddr('10.0.0.2')) >> fwd(2) match(switch=1) >> flood()
negation ~A returns logical negation of filter* policies ~match(switch=1)
* Note: unlike other basic policy types, drop and identity don't require a constructor call "()". This is because they are singletons - there is only one drop policy and only one identity policy - unlike say the fwd policy type which can have many different policy instantiations [e.g., fwd(1), fwd(2), …].

Filter Policies

Filter policies (or "filters" for short) are policies that don't change the packet - either a set containing just the packet is returned or the empty set is returned. match, drop, and identity are filters. The negation (~), conjunction (&), and disjunction (|) are only defined on filter policies - you will get a type error if you attempt to write something like "match(switch=1) & fwd(1)" or "~flood()". Negation is defined above. Conjunction (&) and disjunction (|) evaluate in exactly the same way as the sequential (>>) and parallel (+) composition operators, respectively. The only difference between

condition1 = match(dstmac=EthAddr(00:00:00:00:00:01)) & match(srcmac=EthAddr(00:00:00:00:00:02))

and

condition2 = match(dstmac=EthAddr(00:00:00:00:00:01)) >> match(srcmac=EthAddr(00:00:00:00:00:02))

is that the output of the former (condition1) is a FilterPolicy and the output of the latter (condition2) is just a Policy. condition1 and condition2 will produce identical output, but since condition2 isn't a subclass of FilterPolicy writing

~condition2

will result in a type error, while writing

~condition1

will work out just fine.

Conditional Execution

Filter policies are exactly what you want to conditionally execute code. For example, if you want to write a new policy 'split' that forwards traffic to destination IP 10.0.0.1 out port 1 and traffic to any other destination out port 2, then you can write this using the basic combinators discussed above as:

split = (match(dstip=IPAddr('10.0.0.1')) >> fwd(1)) + (~match(dstip=IPAddr('10.0.0.1')) >> fwd(2))

alternately, you can use the convenience 'if_' policy to write this more concisely as

split = if_(match(dstip=IPAddr('10.0.0.1')),fwd(1),fwd(2))

Both of these encodings are semantically identical - they mean the exact same thing and produce the exact same behavior. And in both cases 'split' is just a subclass of Policy which when eval'd on a packet will produce a set of packets.