Joe and I spent some time today discussing our options.  This is pretty tricky 
to get right and most of the options that come immediately to mind have subtle 
corner cases.  We're planning to whiteboard more options tomorrow, but I wanted 
to get down what's my personal favorite and see what people think of its 
shortcoming.  We're planning to document the other options that we've 
considered and the problems that they have, which we'll share with the group.

The idea is to essentially implement a mark and sweep algorithm.  Assuming that 
we have a lowest priority "drop" flow, we'll add an action that sets a 
"drop_flow" bit (e.g., 0x1) in the conntrack label.  In the next table, we'll 
have a flow that matches on this label bit and drops traffic.  Here's a psuedo 
set of flows to implement allowing stateful traffic to port 22 and 80:

        1) table=0, ip, actions=ct(table=1)
        2) table=1, priority=10, ct_state=-rpl, tcp, tp_dst=22, 
actions=ct(commit,table=2)
        3) table=1, priority=10, ct_state=-rpl, tcp, tp_dst=80, 
actions=ct(commit,table=2)
        4) table=1, priority=0, ct_state=-rpl, actions=ct(set_ct_label=0x1),drop
        5) table=1, priority=10, ct_state=+rpl, ct_label=0x1, actions=drop
        6) table=1, priority=0, ct_state=+rpl+est, actions=goto_table:2
        7) table=2, priority=0, actions= /* Continue logical forwarding 
pipeline. */

Here's an explanation of the flows:

        1) Send all IP traffic to the connection tracker and then go to table 1.
        2) If the destination TCP port is 22 in the request direction, commit 
it to the connection tracker and continue to table 2.
        3) Same as flow 2, but with TCP port 80 traffic.
        4) Traffic in the request direction that doesn't match flows 2 or 3 get 
the conntrack label set to 0x1 (the "drop_flow" bit) and the traffic gets 
dropped.  It's important to note that there's no "commit" here, so that this 
will mark an existing conntrack entry with that label, but won't create a new 
entry for it.
        5) Drop traffic in the reply direction with the "drop_flow" bit set.
        6) Send any reply traffic that has an existing conntrack entry (and the 
"drop_flow" bit not set) to table 2.
        7) Continue the logical forwarding pipeline (ie, the ACL allowed the 
traffic)

If traffic is initiated to port 23, it will be dropped by flow 4, but there 
won't be an entry in the conntrack table since no one committed it.  If traffic 
is initiated to port 22, the connection will be allowed and committed to the 
conntrack table by flow 2.  Similarly for traffic initiated to port 80, it will 
be allowed and committed by flow 3.  The reply direction traffic to 22 and 80 
will be allowed by flow 6.

Now let's say that flow 2 is removed because we don't want to allow port 22 
traffic anymore.  There will still be a conntrack entry from that previous 
connection.  Now when the initiator sends traffic to port 22, it will get 
dropped by flow 4, but we'll also set the existing conntrack entry's flow label 
to 0x1.  When the reply traffic comes back, it will now match flow 5, since the 
ct_label value will be 0x1 and the flow will be dropped.  Traffic to port 80 
will be unaffected.

The nice thing about this approach is that it's not very heavy duty: it doesn't 
cause a lot of flow churn, it doesn't make worse megaflows, it doesn't cause 
race conditions between updating the OVS flow table and conntrack entries, we 
don't have to write (and debug) another flow classifier in ovn-controller, it's 
straight-forward to implement, and it's instantaneous in application--mostly.

That "mostly" is it's drawback, though.  It instantly corrects traffic in both 
directions once a packet is sent in the initiating direction.  However, until 
that happens, reply traffic will continue to flow.  I doubt this will be a big 
problem in practice, since you'd need to have traffic that is largely 
unidirectional without any sort of acknowledgement.  ACKs would take care of 
this for TCP, so it wouldn't be a real problem (there could be a few packets 
that are let through, but policy updates aren't going to be instantaneous 
coming down from the CMS, anyway).  There could be UDP-based protocols that 
don't use any sort of positive acknowledgement, but I don't know of any off the 
top of my head.

As I mentioned, Joe and I will try to come up with a document that describes 
the different approaches that occur to us along with their strengths and 
weaknesses.  I think that will be helpful to have a more fruitful discussion 
about alternatives.

In the meantime, I'd be curious to hear what people think about the above 
proposal.  In the meantime, I think this would be a reasonable approach, since 
it covers most of the use-cases nicely and it wouldn't be hard to implement.

--Justin


> On Feb 1, 2016, at 1:07 PM, Russell Bryant <russ...@ovn.org> wrote:
> 
> We had a bug filed against the OpenStack+OVN integration
> (networking-ovn) that Neutron security group changes are not applied to
> existing connections.  The existing OVS integration in Neutron does this
> by deleting conntrack state entries by running the conntrack tool from a
> Python agent running on every hypervisor.  The OVN integration is
> expected to provide the same behavior.
> 
> https://bugs.launchpad.net/networking-ovn/+bug/1536080
> 
> I've been thinking about this a bit and trying to think of how to deal
> with it.  I don't have any great answers, so I wanted to put out a call
> for ideas.
> 
> I started playing a bit today and tweaked the logical flows to get a bit
> closer, but I don't have a complete solution.
> 
> Has anyone else thought about this?
> 
> -- 
> Russell Bryant

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to