Mininet Assignment 3: Moving a TCP connection in multitrunk.py

Due: Friday, August 11

In this assignment we will use the following multitrunk.py configuration (N=1, K=2):

              s1--------------------s3
            /                          \
    h1----s5                            s6---h2

            \                          /
      
       s2--------------------s4

In this configuration there are two paths from h1 to h2. The existing code will set up a TCP path for a single connection from h1 to h2 via s1--s3. You are to change that connection so that it follows the s2--s4 trunk, without changing any other connections.

You will use the mininet python file trunk12.py (N=1, K=2). The corresponding controller file is multitrunkpox12.py. To start the latter, you need a relatively complex command line: in the top-level pox directory enter (perhaps by pasting) the following:

./pox.py openflow.discovery openflow.nicira --convert-packet-in log.level --WARNING forwarding.multitrunkpox12

What I do is put this into a file, such as mt12.sh, and make it executable, and run it.

A bit of notation: a Flow object represents a one-way TCP flow (so a TCP connection from h1 to h2 will involve two Flow objects: one for the h1→h2 flow and one for the return h2→h1 flow. You are only moving the first flow; the return path will be unchanged. Each Flow has an assigned path; paths are Python lists such as [h1,s5,s1,s3,s6,h2] or [h1,s5,s2,s4,s6,h2]. Those are the only two paths from h1 to h2, in fact. For a path p from h1 to h2, p[0] = h1, p[1]=s5, p[2] is the entry trunk switch s1 or s2, and so on.

It is possible to create TCP connections originating at h2 and connecting to h1, but we will ignore that. That way, we can assume all TCP connections start at h1. Typically these will be ssh connections: at the mininet prompt you will type xterm h1 (which you can do more than once), and then in the h1 xterm window you will type slogin 10.0.0.2. This creates one TCP connection. You can also use netcat to create continuous data-flow connections, but we'll get to those later.

Whenever a new TCP connection is created, the Pox controller assigns a path for each of the two unidirectional Flow objects, and creates appropriate flow entries at each switch along the path. Given a flow f, you can look up its path as flow_to_path[f]. The first TCP flow from h1 to h2 is assigned a path through s1, the second is assigned a path through s2, the third is assigned a path through s1 again, and back and forth. The choice of the first trunk switch -- and thus of the path-- is made by picktrunk().  All flows from h2 to h1 -- the reverse direction -- are assigned a path through s1 by picktrunk().

Each TCP flow from h1 to h2 is added to the list TCPflows. The first connection is thus TCPflows[0].

Some code has been added to multitrunkpox12.py's _handle_PacketIn() to help decide what connection to move. There is a call to moveTCPflow(), which is triggered by the second TCP connection. It calls moveTCPflow() on the first connection's h1→h2 flow. The first TCP connection's reverse (h2→h1) flow is left unchanged. The first h1→h2 flow always goes through s1, by assignment, and so the goal is to move it so as to be through s2 (switchmap[2]). Hence, the call is, in full, moveTCPflow(TCPflows[0], switchmap[2]).

Here is what moveTCPflow(flow,ts) has to do:

To delete flow entries, create a match object, match on all six flow attributes plus IPv4 and TCP (shown below), and set the command to of.OFPFC_DELETE. Then send it via the switch's connection() object.

    msg = nx.nx_flow_mod()
    msg.match = nx.nx_match()   # pld: see pox dox "Using nx_match"
    msg.table_id = 0
    msg.match.append(nx.NXM_OF_ETH_SRC(flow.ethsrc))
    msg.match.append(nx.NXM_OF_ETH_DST(flow.ethdst))
    msg.match.append(nx.NXM_OF_ETH_TYPE(pkt.ethernet.IP_TYPE))        # match that this is an IPv4 packet
    msg.match.append(nx.NXM_OF_IP_PROTO(pkt.ipv4.TCP_PROTOCOL))       # match that this is a TCP packet
    msg.match.append(nx.NXM_OF_IP_SRC(flow.srcip))
    msg.match.append(nx.NXM_OF_IP_DST(flow.dstip))
    msg.match.append(nx.NXM_OF_TCP_SRC(flow.srcport))
    msg.match.append(nx.NXM_OF_TCP_DST(flow.dstport))
    msg.command = of.OFPFC_DELETE
    s.connection().send(msg)

After the first connection, you should see the following with ovs-ofctl (consider ovs-ofctl dump-flows s | grep -i tcp)
After this first connection's h1→h2 flow has been moved, we should see the following:

Verify this.

If you want to use a continuous TCP flow, instead of ssh which is fundamentally intermittent, consider the following, where FILENAME is a text file of modest size:

while cat FILENAME; do : ; done | netcat 10.0.0.2 5432

Be careful with the punctuation. This continuously sends the named file over a netcat connection.

Getting ssh/slogin to work

It turns out that ssh is not set up by default (this is one of the things that got lost when we moved from 64-bit to 32-bit at the start of the semester). The ssh command itself works, but you need to configure things so that the root user is authorized without a password. Here is what to do. First, run the following command, as root:

ssh-keygen

Answer y to all the questions (or just Enter for the default). Then do the following:

cd /root/.ssh
cp id_rsa.pub authorized_keys

The file id_rsa.pub is root's public key; by putting this in authorized_keys you're saying that root is authorized to log in.

You will probably get warning messages the first time you connect, like
The authenticity of host 10.0.0.2 can’t be established.
RSA key fingerprint is da:2e:e3:94:84:6b:bf:6d:2f:e4:c3:76:68:72:a5:a0.
Are you sure you want to continue connecting (yes/no)?
Choose yes. This just means that, because this is the first time the public key has been seen, you have to agree to trust it.