[ovs-dev,net-next,V17,3/3] 802.1AD: Flow handling, actions, vlan parsing and netlink attributes
diff mbox

Message ID 1445130748-27671-4-git-send-email-thomasfherbert@gmail.com
State Not Applicable
Headers show

Commit Message

Thomas F Herbert Oct. 18, 2015, 1:12 a.m. UTC
Add support for 802.1ad including the ability to push and pop double
tagged vlans. Add support for 802.1ad to netlink parsing and flow
conversion. Uses double nested encap attributes to represent double
tagged vlan. Inner TPID encoded along with ctci in nested attributes.

Signed-off-by: Thomas F Herbert <thomasfherbert@gmail.com>
---
 net/openvswitch/actions.c      |   6 +-
 net/openvswitch/flow.c         |  76 +++++++++++++-----
 net/openvswitch/flow.h         |   8 +-
 net/openvswitch/flow_netlink.c | 172 +++++++++++++++++++++++++++++++++++++----
 net/openvswitch/vport-netdev.c |   4 +-
 5 files changed, 227 insertions(+), 39 deletions(-)

Comments

Pravin B Shelar Oct. 19, 2015, 6:28 p.m. UTC | #1
On Sat, Oct 17, 2015 at 6:12 PM, Thomas F Herbert
<thomasfherbert@gmail.com> wrote:
> Add support for 802.1ad including the ability to push and pop double
> tagged vlans. Add support for 802.1ad to netlink parsing and flow
> conversion. Uses double nested encap attributes to represent double
> tagged vlan. Inner TPID encoded along with ctci in nested attributes.
>
> Signed-off-by: Thomas F Herbert <thomasfherbert@gmail.com>
> ---
>  net/openvswitch/actions.c      |   6 +-
>  net/openvswitch/flow.c         |  76 +++++++++++++-----
>  net/openvswitch/flow.h         |   8 +-
>  net/openvswitch/flow_netlink.c | 172 +++++++++++++++++++++++++++++++++++++----
>  net/openvswitch/vport-netdev.c |   4 +-
>  5 files changed, 227 insertions(+), 39 deletions(-)
>
> diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
> index 315f533..09cc1c9 100644
...

> diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
> index c92d6a2..97a6d12 100644
> --- a/net/openvswitch/flow_netlink.c
> +++ b/net/openvswitch/flow_netlink.c
...

> @@ -1320,6 +1443,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
>  {
>         struct ovs_key_ethernet *eth_key;
>         struct nlattr *nla, *encap;
> +       struct nlattr *in_encap = NULL;
>
>         if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
>                 goto nla_put_failure;
> @@ -1368,17 +1492,29 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
>         ether_addr_copy(eth_key->eth_src, output->eth.src);
>         ether_addr_copy(eth_key->eth_dst, output->eth.dst);
>
> -       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
> -               __be16 eth_type;
> -               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
> -               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
> -                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
> +       if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
> +               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
> +                                output->eth.vlan.tpid) ||
> +                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.vlan.tci))
>                         goto nla_put_failure;
>                 encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
> -               if (!swkey->eth.tci)
> +               if (!swkey->eth.vlan.tci)
>                         goto unencap;
> -       } else
> +               if (swkey->eth.cvlan.tci) {
> +                       /* Customer tci is nested but uses same key attribute.
> +                        */
> +                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
> +                                        output->eth.cvlan.tpid) ||
> +                           nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
> +                                        output->eth.cvlan.tci))
> +                               goto nla_put_failure;
> +                       in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
> +                       if (!swkey->eth.cvlan.tci)
> +                               goto unencap;
> +               }
> +       } else {
>                 encap = NULL;
> +       }
After the vlan parsing changes, we need to encode cvlan in outer
netlink attribute and then encode regular vlan. Currently we are
reversing netlink encoding while sending back the vlan attributes.
cvlan and vlan is independent, therefore we need to check
swkey->eth.cvlan.tc outside of swkey->eth.vlan.tci block. Also
redundant check for swkey->eth.cvlan.tci should be removed.

Can you also post changes for userspace vswitchd so that I can try next patch.
Thomas F Herbert Oct. 20, 2015, 2:26 p.m. UTC | #2
On 10/19/15 2:28 PM, Pravin Shelar wrote:
> On Sat, Oct 17, 2015 at 6:12 PM, Thomas F Herbert
> <thomasfherbert@gmail.com> wrote:
>> Add support for 802.1ad including the ability to push and pop double
>> tagged vlans. Add support for 802.1ad to netlink parsing and flow
>> conversion. Uses double nested encap attributes to represent double
>> tagged vlan. Inner TPID encoded along with ctci in nested attributes.
>>
>> Signed-off-by: Thomas F Herbert <thomasfherbert@gmail.com>
>> ---
>>   net/openvswitch/actions.c      |   6 +-
>>   net/openvswitch/flow.c         |  76 +++++++++++++-----
>>   net/openvswitch/flow.h         |   8 +-
>>   net/openvswitch/flow_netlink.c | 172 +++++++++++++++++++++++++++++++++++++----
>>   net/openvswitch/vport-netdev.c |   4 +-
>>   5 files changed, 227 insertions(+), 39 deletions(-)
>>
>> diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
>> index 315f533..09cc1c9 100644
> ...
>
>> diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
>> index c92d6a2..97a6d12 100644
>> --- a/net/openvswitch/flow_netlink.c
>> +++ b/net/openvswitch/flow_netlink.c
> ...
>
>> @@ -1320,6 +1443,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
>>   {
>>          struct ovs_key_ethernet *eth_key;
>>          struct nlattr *nla, *encap;
>> +       struct nlattr *in_encap = NULL;
>>
>>          if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
>>                  goto nla_put_failure;
>> @@ -1368,17 +1492,29 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
>>          ether_addr_copy(eth_key->eth_src, output->eth.src);
>>          ether_addr_copy(eth_key->eth_dst, output->eth.dst);
>>
>> -       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
>> -               __be16 eth_type;
>> -               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
>> -               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
>> -                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
>> +       if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
>> +               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>> +                                output->eth.vlan.tpid) ||
>> +                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.vlan.tci))
>>                          goto nla_put_failure;
>>                  encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>> -               if (!swkey->eth.tci)
>> +               if (!swkey->eth.vlan.tci)
>>                          goto unencap;
>> -       } else
>> +               if (swkey->eth.cvlan.tci) {
>> +                       /* Customer tci is nested but uses same key attribute.
>> +                        */
>> +                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>> +                                        output->eth.cvlan.tpid) ||
>> +                           nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>> +                                        output->eth.cvlan.tci))
>> +                               goto nla_put_failure;
>> +                       in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>> +                       if (!swkey->eth.cvlan.tci)
>> +                               goto unencap;
>> +               }
>> +       } else {
>>                  encap = NULL;
>> +       }
> After the vlan parsing changes, we need to encode cvlan in outer
> netlink attribute and then encode regular vlan.
I don't understand this. cvlan is inner vlan, why would it be encoded in 
outer vlan without the 2nd layer of encapsulation?

One think I see I should have done is check for eth_type_vlan() on the 
inner tag as well.
> Currently we are
> reversing netlink encoding while sending back the vlan attributes.
> cvlan and vlan is independent,
Are you talking about a corner case where the incoming packet has an 
inner vlan but no outer vlN?
> therefore we need to check
> swkey->eth.cvlan.tc outside of swkey->eth.vlan.tci block. Also
> redundant check for swkey->eth.cvlan.tci should be removed
I must be missing something because I don't understand this either. 
First my patch encodes the outer vlan and then sets the encap bit and 
then encodes the inner vlan. The encoding shows up correctly in user space.
>
> Can you also post changes for userspace vswitchd so that I can try next patch.
I posted a patch for user space (V14) to ovs dev on October 2nd. I 
haven't made any changes since then.
http://openvswitch.org/pipermail/dev/2015-October/060874.html
Pravin B Shelar Oct. 20, 2015, 8:34 p.m. UTC | #3
On Tue, Oct 20, 2015 at 7:26 AM, Thomas F Herbert
<thomasfherbert@gmail.com> wrote:
> On 10/19/15 2:28 PM, Pravin Shelar wrote:
>>
>> On Sat, Oct 17, 2015 at 6:12 PM, Thomas F Herbert
>> <thomasfherbert@gmail.com> wrote:
>>>
>>> Add support for 802.1ad including the ability to push and pop double
>>> tagged vlans. Add support for 802.1ad to netlink parsing and flow
>>> conversion. Uses double nested encap attributes to represent double
>>> tagged vlan. Inner TPID encoded along with ctci in nested attributes.
>>>
>>> Signed-off-by: Thomas F Herbert <thomasfherbert@gmail.com>
>>> ---
>>>   net/openvswitch/actions.c      |   6 +-
>>>   net/openvswitch/flow.c         |  76 +++++++++++++-----
>>>   net/openvswitch/flow.h         |   8 +-
>>>   net/openvswitch/flow_netlink.c | 172
>>> +++++++++++++++++++++++++++++++++++++----
>>>   net/openvswitch/vport-netdev.c |   4 +-
>>>   5 files changed, 227 insertions(+), 39 deletions(-)
>>>
>>> diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
>>> index 315f533..09cc1c9 100644
>>
>> ...
>>
>>> diff --git a/net/openvswitch/flow_netlink.c
>>> b/net/openvswitch/flow_netlink.c
>>> index c92d6a2..97a6d12 100644
>>> --- a/net/openvswitch/flow_netlink.c
>>> +++ b/net/openvswitch/flow_netlink.c
>>
>> ...
>>
>>> @@ -1320,6 +1443,7 @@ static int __ovs_nla_put_key(const struct
>>> sw_flow_key *swkey,
>>>   {
>>>          struct ovs_key_ethernet *eth_key;
>>>          struct nlattr *nla, *encap;
>>> +       struct nlattr *in_encap = NULL;
>>>
>>>          if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
>>>                  goto nla_put_failure;
>>> @@ -1368,17 +1492,29 @@ static int __ovs_nla_put_key(const struct
>>> sw_flow_key *swkey,
>>>          ether_addr_copy(eth_key->eth_src, output->eth.src);
>>>          ether_addr_copy(eth_key->eth_dst, output->eth.dst);
>>>
>>> -       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
>>> -               __be16 eth_type;
>>> -               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
>>> -               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type)
>>> ||
>>> -                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>> output->eth.tci))
>>> +       if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
>>> +               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>> +                                output->eth.vlan.tpid) ||
>>> +                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>> output->eth.vlan.tci))
>>>                          goto nla_put_failure;
>>>                  encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>>> -               if (!swkey->eth.tci)
>>> +               if (!swkey->eth.vlan.tci)
>>>                          goto unencap;
>>> -       } else
>>> +               if (swkey->eth.cvlan.tci) {
>>> +                       /* Customer tci is nested but uses same key
>>> attribute.
>>> +                        */
>>> +                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>> +                                        output->eth.cvlan.tpid) ||
>>> +                           nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>> +                                        output->eth.cvlan.tci))
>>> +                               goto nla_put_failure;
>>> +                       in_encap = nla_nest_start(skb,
>>> OVS_KEY_ATTR_ENCAP);
>>> +                       if (!swkey->eth.cvlan.tci)
>>> +                               goto unencap;
>>> +               }
>>> +       } else {
>>>                  encap = NULL;
>>> +       }
>>
>> After the vlan parsing changes, we need to encode cvlan in outer
>> netlink attribute and then encode regular vlan.
>
> I don't understand this. cvlan is inner vlan, why would it be encoded in
> outer vlan without the 2nd layer of encapsulation?
>
Lets start with double tagged packet.
packet: eth, vlan 10, inner vlan 20, ip.
flow key would be:
flow_key: vlan = 10, cvlan = 20
That would get serialize over netlink like:
eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x8100),vlan(vid=20,pcp=0),
encap(eth_type(0x0800),ipv4(frag=no))...
So far looks good.

Now userspace sends back same key and installs flow in kernel
datapath. But ovs_nla_get_match() parses netink key and sets vlan in
reverse order. After parsing netlink in ovs_nla_get_match() vlans
flow-key would look like:
flow_key: vlan = 20, cvlan = 10. This is not what we started with.

Now I think rather than fixing __ovs_nla_put_key (), we need to fix
ovs_nla_get_match() to keep vlan order.
I also noticed that eth.vlan.tpid is never initialized from netlink attributes.
Call to parse_flow_nlattrs  in parse_vlan_from_nlattrs() has no effect
so it should be removed.


> One think I see I should have done is check for eth_type_vlan() on the inner
> tag as well.
>>
>> Currently we are
>> reversing netlink encoding while sending back the vlan attributes.
>> cvlan and vlan is independent,
>
> Are you talking about a corner case where the incoming packet has an inner
> vlan but no outer vlN?
>>
nope, this is usual case.

>> therefore we need to check
>> swkey->eth.cvlan.tc outside of swkey->eth.vlan.tci block. Also
>> redundant check for swkey->eth.cvlan.tci should be removed
>
> I must be missing something because I don't understand this either. First my
> patch encodes the outer vlan and then sets the encap bit and then encodes
> the inner vlan. The encoding shows up correctly in user space.
>>
Once ovs_nla_get_match() is fixed, there is no need to change this
function to fix that ordering.

I was talking about following redundant check:

                if (swkey->eth.cvlan.tci) {
                        /* Customer tci is nested but uses same key attribute.
                         */
                        if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
                                         output->eth.cvlan.tpid) ||
                            nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
                                         output->eth.cvlan.tci))
                                goto nla_put_failure;
                        in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>>>>>>>>               if (!swkey->eth.cvlan.tci)  <<<<<<<<<<<<<
                                goto unencap;
                }

>>
>> Can you also post changes for userspace vswitchd so that I can try next
>> patch.
>
> I posted a patch for user space (V14) to ovs dev on October 2nd. I haven't
> made any changes since then.
> http://openvswitch.org/pipermail/dev/2015-October/060874.html
>
That patch does not apply on OVS master. You could also send me link
to ovs userpsace github repo with these patches applied so that I can
try kernel datapath changes.
Thomas F Herbert Oct. 21, 2015, 2:39 p.m. UTC | #4
On 10/20/15 4:34 PM, Pravin Shelar wrote:
> On Tue, Oct 20, 2015 at 7:26 AM, Thomas F Herbert
> <thomasfherbert@gmail.com>  wrote:
>> On 10/19/15 2:28 PM, Pravin Shelar wrote:
>>> On Sat, Oct 17, 2015 at 6:12 PM, Thomas F Herbert
>>> <thomasfherbert@gmail.com>  wrote:
>>>> Add support for 802.1ad including the ability to push and pop double
>>>> tagged vlans. Add support for 802.1ad to netlink parsing and flow
>>>> conversion. Uses double nested encap attributes to represent double
>>>> tagged vlan. Inner TPID encoded along with ctci in nested attributes.
>>>>
>>>> Signed-off-by: Thomas F Herbert<thomasfherbert@gmail.com>
>>>> ---
>>>>    net/openvswitch/actions.c      |   6 +-
>>>>    net/openvswitch/flow.c         |  76 +++++++++++++-----
>>>>    net/openvswitch/flow.h         |   8 +-
>>>>    net/openvswitch/flow_netlink.c | 172
>>>> +++++++++++++++++++++++++++++++++++++----
>>>>    net/openvswitch/vport-netdev.c |   4 +-
>>>>    5 files changed, 227 insertions(+), 39 deletions(-)
>>>>
>>>> diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
>>>> index 315f533..09cc1c9 100644
>>> ...
>>>
>>>> diff --git a/net/openvswitch/flow_netlink.c
>>>> b/net/openvswitch/flow_netlink.c
>>>> index c92d6a2..97a6d12 100644
>>>> --- a/net/openvswitch/flow_netlink.c
>>>> +++ b/net/openvswitch/flow_netlink.c
>>> ...
>>>
>>>> @@ -1320,6 +1443,7 @@ static int __ovs_nla_put_key(const struct
>>>> sw_flow_key *swkey,
>>>>    {
>>>>           struct ovs_key_ethernet *eth_key;
>>>>           struct nlattr *nla, *encap;
>>>> +       struct nlattr *in_encap = NULL;
>>>>
>>>>           if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
>>>>                   goto nla_put_failure;
>>>> @@ -1368,17 +1492,29 @@ static int __ovs_nla_put_key(const struct
>>>> sw_flow_key *swkey,
>>>>           ether_addr_copy(eth_key->eth_src, output->eth.src);
>>>>           ether_addr_copy(eth_key->eth_dst, output->eth.dst);
>>>>
>>>> -       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
>>>> -               __be16 eth_type;
>>>> -               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
>>>> -               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type)
>>>> ||
>>>> -                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>> output->eth.tci))
>>>> +       if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
>>>> +               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>>> +                                output->eth.vlan.tpid) ||
>>>> +                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>> output->eth.vlan.tci))
>>>>                           goto nla_put_failure;
>>>>                   encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>>>> -               if (!swkey->eth.tci)
>>>> +               if (!swkey->eth.vlan.tci)
>>>>                           goto unencap;
>>>> -       } else
>>>> +               if (swkey->eth.cvlan.tci) {
>>>> +                       /* Customer tci is nested but uses same key
>>>> attribute.
>>>> +                        */
>>>> +                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>>> +                                        output->eth.cvlan.tpid) ||
>>>> +                           nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>> +                                        output->eth.cvlan.tci))
>>>> +                               goto nla_put_failure;
>>>> +                       in_encap = nla_nest_start(skb,
>>>> OVS_KEY_ATTR_ENCAP);
>>>> +                       if (!swkey->eth.cvlan.tci)
>>>> +                               goto unencap;
>>>> +               }
>>>> +       } else {
>>>>                   encap = NULL;
>>>> +       }
>>> After the vlan parsing changes, we need to encode cvlan in outer
>>> netlink attribute and then encode regular vlan.
>> I don't understand this. cvlan is inner vlan, why would it be encoded in
>> outer vlan without the 2nd layer of encapsulation?
>>
> Lets start with double tagged packet.
> packet: eth, vlan 10, inner vlan 20, ip.
> flow key would be:
> flow_key: vlan = 10, cvlan = 20
> That would get serialize over netlink like:
> eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x8100),vlan(vid=20,pcp=0),
> encap(eth_type(0x0800),ipv4(frag=no))...
> So far looks good.
>
> Now userspace sends back same key and installs flow in kernel
> datapath. But ovs_nla_get_match() parses netink key and sets vlan in
> reverse order. After parsing netlink in ovs_nla_get_match() vlans
> flow-key would look like:
> flow_key: vlan = 20, cvlan = 10. This is not what we started with.
>
> Now I think rather than fixing __ovs_nla_put_key (), we need to fix
> ovs_nla_get_match() to keep vlan order.
> I also noticed that eth.vlan.tpid is never initialized from netlink attributes.
Yes, you are right. The code in master never encoded the tpid in the key 
and I didn't add it when I modified the swkey for the addition of the 
vlan struct.

Yes, you are right the code is wrong. The intention was to leave the old 
code which left the outer tci in the key for later processing. In the 
code in master, after checking proper vlan the function 
ovs_key_from_nlattrs() encoded the tci.

Now I see that I must refactor the code in ovs_nla_get_match() and 
parse_vlan_from_nlattrs to explicitly encode the outer vlan including 
tpid before the inner vlan.
> Call to parse_flow_nlattrs  in parse_vlan_from_nlattrs() has no effect
> so it should be removed
parse_flow_nlattrs() is called for each level of encapsulation. It sets 
v_attrs with the keys found in the flow key. This is to insure that I am 
looking only at the keys for the current level of encapsulation. Then it 
is called a final time to parse out the l3 attributes for encoding.
>
>> One think I see I should have done is check for eth_type_vlan() on the inner
>> tag as well.
>>> Currently we are
>>> reversing netlink encoding while sending back the vlan attributes.
>>> cvlan and vlan is independent,
>> Are you talking about a corner case where the incoming packet has an inner
>> vlan but no outer vlN?
> nope, this is usual case.
>
>>> therefore we need to check
>>> swkey->eth.cvlan.tc outside of swkey->eth.vlan.tci block. Also
>>> redundant check for swkey->eth.cvlan.tci should be removed
>> I must be missing something because I don't understand this either. First my
>> patch encodes the outer vlan and then sets the encap bit and then encodes
>> the inner vlan. The encoding shows up correctly in user space.
> Once ovs_nla_get_match() is fixed, there is no need to change this
> function to fix that ordering.
>
> I was talking about following redundant check:
>
>                  if (swkey->eth.cvlan.tci) {
I think this should be the fix: Maybe it should be: if 
((eth_type_vlan(swkey->eth.cvlan.tpid) || (eth.cvlan.tci)) {
>                          /* Customer tci is nested but uses same key attribute.
>                           */
>                          if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>                                           output->eth.cvlan.tpid) ||
>                              nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>                                           output->eth.cvlan.tci))
>                                  goto nla_put_failure;
>                          in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>>>>>>>>>                if (!swkey->eth.cvlan.tci)  <<<<<<<<<<<<<
OK, I see what you mean. It looks redundant because it checks for 
cvlan.tci before the encap and then checks again after the encap. I got 
this from the original code which checks for 8021Q || eth.tci. Then 
after the encap, if it sees a zero tci, it unencaps. I guess this was to 
make sure it didn't encode a zero tci. See pseudo-code above.
>                                  goto unencap;
>                  }
>
>>> Can you also post changes for userspace vswitchd so that I can try next
>>> patch.
>> I posted a patch for user space (V14) to ovs dev on October 2nd. I haven't
>> made any changes since then.
>> http://openvswitch.org/pipermail/dev/2015-October/060874.html
>>
> That patch does not apply on OVS master. You could also send me link
> to ovs userpsace github repo with these patches applied so that I can
> try kernel datapath changes.
https://github.com/tfherbert/ovs_8021ad/tree/8021AD
Pravin B Shelar Oct. 23, 2015, 7:26 p.m. UTC | #5
On Wed, Oct 21, 2015 at 7:39 AM, Thomas F Herbert <therbert@redhat.com> wrote:
>
>
> On 10/20/15 4:34 PM, Pravin Shelar wrote:
>>
>> On Tue, Oct 20, 2015 at 7:26 AM, Thomas F Herbert
>> <thomasfherbert@gmail.com>  wrote:
>>>
>>> On 10/19/15 2:28 PM, Pravin Shelar wrote:
>>>>
>>>> On Sat, Oct 17, 2015 at 6:12 PM, Thomas F Herbert
>>>> <thomasfherbert@gmail.com>  wrote:
>>>>>
>>>>> Add support for 802.1ad including the ability to push and pop double
>>>>> tagged vlans. Add support for 802.1ad to netlink parsing and flow
>>>>> conversion. Uses double nested encap attributes to represent double
>>>>> tagged vlan. Inner TPID encoded along with ctci in nested attributes.
>>>>>
>>>>> Signed-off-by: Thomas F Herbert<thomasfherbert@gmail.com>
>>>>> ---
>>>>>    net/openvswitch/actions.c      |   6 +-
>>>>>    net/openvswitch/flow.c         |  76 +++++++++++++-----
>>>>>    net/openvswitch/flow.h         |   8 +-
>>>>>    net/openvswitch/flow_netlink.c | 172
>>>>> +++++++++++++++++++++++++++++++++++++----
>>>>>    net/openvswitch/vport-netdev.c |   4 +-
>>>>>    5 files changed, 227 insertions(+), 39 deletions(-)
>>>>>
>>>>> diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
>>>>> index 315f533..09cc1c9 100644
>>>>
>>>> ...
>>>>
>>>>> diff --git a/net/openvswitch/flow_netlink.c
>>>>> b/net/openvswitch/flow_netlink.c
>>>>> index c92d6a2..97a6d12 100644
>>>>> --- a/net/openvswitch/flow_netlink.c
>>>>> +++ b/net/openvswitch/flow_netlink.c
>>>>
>>>> ...
>>>>
>>>>> @@ -1320,6 +1443,7 @@ static int __ovs_nla_put_key(const struct
>>>>> sw_flow_key *swkey,
>>>>>    {
>>>>>           struct ovs_key_ethernet *eth_key;
>>>>>           struct nlattr *nla, *encap;
>>>>> +       struct nlattr *in_encap = NULL;
>>>>>
>>>>>           if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID,
>>>>> output->recirc_id))
>>>>>                   goto nla_put_failure;
>>>>> @@ -1368,17 +1492,29 @@ static int __ovs_nla_put_key(const struct
>>>>> sw_flow_key *swkey,
>>>>>           ether_addr_copy(eth_key->eth_src, output->eth.src);
>>>>>           ether_addr_copy(eth_key->eth_dst, output->eth.dst);
>>>>>
>>>>> -       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
>>>>> -               __be16 eth_type;
>>>>> -               eth_type = !is_mask ? htons(ETH_P_8021Q) :
>>>>> htons(0xffff);
>>>>> -               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type)
>>>>> ||
>>>>> -                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>>> output->eth.tci))
>>>>> +       if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
>>>>> +               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>>>> +                                output->eth.vlan.tpid) ||
>>>>> +                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>>> output->eth.vlan.tci))
>>>>>                           goto nla_put_failure;
>>>>>                   encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>>>>> -               if (!swkey->eth.tci)
>>>>> +               if (!swkey->eth.vlan.tci)
>>>>>                           goto unencap;
>>>>> -       } else
>>>>> +               if (swkey->eth.cvlan.tci) {
>>>>> +                       /* Customer tci is nested but uses same key
>>>>> attribute.
>>>>> +                        */
>>>>> +                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>>>> +                                        output->eth.cvlan.tpid) ||
>>>>> +                           nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>>> +                                        output->eth.cvlan.tci))
>>>>> +                               goto nla_put_failure;
>>>>> +                       in_encap = nla_nest_start(skb,
>>>>> OVS_KEY_ATTR_ENCAP);
>>>>> +                       if (!swkey->eth.cvlan.tci)
>>>>> +                               goto unencap;
>>>>> +               }
>>>>> +       } else {
>>>>>                   encap = NULL;
>>>>> +       }
>>>>
>>>> After the vlan parsing changes, we need to encode cvlan in outer
>>>> netlink attribute and then encode regular vlan.
>>>
>>> I don't understand this. cvlan is inner vlan, why would it be encoded in
>>> outer vlan without the 2nd layer of encapsulation?
>>>
>> Lets start with double tagged packet.
>> packet: eth, vlan 10, inner vlan 20, ip.
>> flow key would be:
>> flow_key: vlan = 10, cvlan = 20
>> That would get serialize over netlink like:
>>
>> eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x8100),vlan(vid=20,pcp=0),
>> encap(eth_type(0x0800),ipv4(frag=no))...
>> So far looks good.
>>
>> Now userspace sends back same key and installs flow in kernel
>> datapath. But ovs_nla_get_match() parses netink key and sets vlan in
>> reverse order. After parsing netlink in ovs_nla_get_match() vlans
>> flow-key would look like:
>> flow_key: vlan = 20, cvlan = 10. This is not what we started with.
>>
>> Now I think rather than fixing __ovs_nla_put_key (), we need to fix
>> ovs_nla_get_match() to keep vlan order.
>> I also noticed that eth.vlan.tpid is never initialized from netlink
>> attributes.
>
> Yes, you are right. The code in master never encoded the tpid in the key and
> I didn't add it when I modified the swkey for the addition of the vlan
> struct.
>
> Yes, you are right the code is wrong. The intention was to leave the old
> code which left the outer tci in the key for later processing. In the code
> in master, after checking proper vlan the function ovs_key_from_nlattrs()
> encoded the tci.
>
> Now I see that I must refactor the code in ovs_nla_get_match() and
> parse_vlan_from_nlattrs to explicitly encode the outer vlan including tpid
> before the inner vlan.
>>
>> Call to parse_flow_nlattrs  in parse_vlan_from_nlattrs() has no effect
>> so it should be removed
>
> parse_flow_nlattrs() is called for each level of encapsulation. It sets
> v_attrs with the keys found in the flow key. This is to insure that I am
> looking only at the keys for the current level of encapsulation. Then it is
> called a final time to parse out the l3 attributes for encoding.

ovs_nla_get_match() calls parse_flow_nlattrs() and then again in
parse_vlan_from_nlattrs(), second parse_flow_nlattr() can potentially
overwrite vlan attributes. Therefore it should called after the outer
vlan attributes are read.
Thomas F Herbert Oct. 23, 2015, 7:42 p.m. UTC | #6
On 10/23/15 3:26 PM, Pravin Shelar wrote:
> On Wed, Oct 21, 2015 at 7:39 AM, Thomas F Herbert <therbert@redhat.com> wrote:
>>
>> On 10/20/15 4:34 PM, Pravin Shelar wrote:
>>> On Tue, Oct 20, 2015 at 7:26 AM, Thomas F Herbert
>>> <thomasfherbert@gmail.com>  wrote:
>>>> On 10/19/15 2:28 PM, Pravin Shelar wrote:
>>>>> On Sat, Oct 17, 2015 at 6:12 PM, Thomas F Herbert
>>>>> <thomasfherbert@gmail.com>  wrote:
>>>>>> Add support for 802.1ad including the ability to push and pop double
>>>>>> tagged vlans. Add support for 802.1ad to netlink parsing and flow
>>>>>> conversion. Uses double nested encap attributes to represent double
>>>>>> tagged vlan. Inner TPID encoded along with ctci in nested attributes.
>>>>>>
>>>>>> Signed-off-by: Thomas F Herbert<thomasfherbert@gmail.com>
>>>>>> ---
>>>>>>     net/openvswitch/actions.c      |   6 +-
>>>>>>     net/openvswitch/flow.c         |  76 +++++++++++++-----
>>>>>>     net/openvswitch/flow.h         |   8 +-
>>>>>>     net/openvswitch/flow_netlink.c | 172
>>>>>> +++++++++++++++++++++++++++++++++++++----
>>>>>>     net/openvswitch/vport-netdev.c |   4 +-
>>>>>>     5 files changed, 227 insertions(+), 39 deletions(-)
>>>>>>
>>>>>> diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
>>>>>> index 315f533..09cc1c9 100644
>>>>> ...
>>>>>
>>>>>> diff --git a/net/openvswitch/flow_netlink.c
>>>>>> b/net/openvswitch/flow_netlink.c
>>>>>> index c92d6a2..97a6d12 100644
>>>>>> --- a/net/openvswitch/flow_netlink.c
>>>>>> +++ b/net/openvswitch/flow_netlink.c
>>>>> ...
>>>>>
>>>>>> @@ -1320,6 +1443,7 @@ static int __ovs_nla_put_key(const struct
>>>>>> sw_flow_key *swkey,
>>>>>>     {
>>>>>>            struct ovs_key_ethernet *eth_key;
>>>>>>            struct nlattr *nla, *encap;
>>>>>> +       struct nlattr *in_encap = NULL;
>>>>>>
>>>>>>            if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID,
>>>>>> output->recirc_id))
>>>>>>                    goto nla_put_failure;
>>>>>> @@ -1368,17 +1492,29 @@ static int __ovs_nla_put_key(const struct
>>>>>> sw_flow_key *swkey,
>>>>>>            ether_addr_copy(eth_key->eth_src, output->eth.src);
>>>>>>            ether_addr_copy(eth_key->eth_dst, output->eth.dst);
>>>>>>
>>>>>> -       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
>>>>>> -               __be16 eth_type;
>>>>>> -               eth_type = !is_mask ? htons(ETH_P_8021Q) :
>>>>>> htons(0xffff);
>>>>>> -               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type)
>>>>>> ||
>>>>>> -                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>>>> output->eth.tci))
>>>>>> +       if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
>>>>>> +               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>>>>> +                                output->eth.vlan.tpid) ||
>>>>>> +                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>>>> output->eth.vlan.tci))
>>>>>>                            goto nla_put_failure;
>>>>>>                    encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
>>>>>> -               if (!swkey->eth.tci)
>>>>>> +               if (!swkey->eth.vlan.tci)
>>>>>>                            goto unencap;
>>>>>> -       } else
>>>>>> +               if (swkey->eth.cvlan.tci) {
>>>>>> +                       /* Customer tci is nested but uses same key
>>>>>> attribute.
>>>>>> +                        */
>>>>>> +                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
>>>>>> +                                        output->eth.cvlan.tpid) ||
>>>>>> +                           nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
>>>>>> +                                        output->eth.cvlan.tci))
>>>>>> +                               goto nla_put_failure;
>>>>>> +                       in_encap = nla_nest_start(skb,
>>>>>> OVS_KEY_ATTR_ENCAP);
>>>>>> +                       if (!swkey->eth.cvlan.tci)
>>>>>> +                               goto unencap;
>>>>>> +               }
>>>>>> +       } else {
>>>>>>                    encap = NULL;
>>>>>> +       }
>>>>> After the vlan parsing changes, we need to encode cvlan in outer
>>>>> netlink attribute and then encode regular vlan.
>>>> I don't understand this. cvlan is inner vlan, why would it be encoded in
>>>> outer vlan without the 2nd layer of encapsulation?
>>>>
>>> Lets start with double tagged packet.
>>> packet: eth, vlan 10, inner vlan 20, ip.
>>> flow key would be:
>>> flow_key: vlan = 10, cvlan = 20
>>> That would get serialize over netlink like:
>>>
>>> eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x8100),vlan(vid=20,pcp=0),
>>> encap(eth_type(0x0800),ipv4(frag=no))...
>>> So far looks good.
>>>
>>> Now userspace sends back same key and installs flow in kernel
>>> datapath. But ovs_nla_get_match() parses netink key and sets vlan in
>>> reverse order. After parsing netlink in ovs_nla_get_match() vlans
>>> flow-key would look like:
>>> flow_key: vlan = 20, cvlan = 10. This is not what we started with.
>>>
>>> Now I think rather than fixing __ovs_nla_put_key (), we need to fix
>>> ovs_nla_get_match() to keep vlan order.
>>> I also noticed that eth.vlan.tpid is never initialized from netlink
>>> attributes.
>> Yes, you are right. The code in master never encoded the tpid in the key and
>> I didn't add it when I modified the swkey for the addition of the vlan
>> struct.
>>
>> Yes, you are right the code is wrong. The intention was to leave the old
>> code which left the outer tci in the key for later processing. In the code
>> in master, after checking proper vlan the function ovs_key_from_nlattrs()
>> encoded the tci.
>>
>> Now I see that I must refactor the code in ovs_nla_get_match() and
>> parse_vlan_from_nlattrs to explicitly encode the outer vlan including tpid
>> before the inner vlan.
>>> Call to parse_flow_nlattrs  in parse_vlan_from_nlattrs() has no effect
>>> so it should be removed
>> parse_flow_nlattrs() is called for each level of encapsulation. It sets
>> v_attrs with the keys found in the flow key. This is to insure that I am
>> looking only at the keys for the current level of encapsulation. Then it is
>> called a final time to parse out the l3 attributes for encoding.
> ovs_nla_get_match() calls parse_flow_nlattrs() and then again in
> parse_vlan_from_nlattrs(), second parse_flow_nlattr() can potentially
> overwrite vlan attributes. Therefore it should called after the outer
> vlan attributes are read.
Thanks! Yes, I agree. I am fixing this with the refactoring I am doing now.

Patch
diff mbox

diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 315f533..09cc1c9 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -236,7 +236,8 @@  static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 	if (skb_vlan_tag_present(skb))
 		invalidate_flow_key(key);
 	else
-		key->eth.tci = 0;
+		key->eth.vlan.tci = 0;
+		key->eth.vlan.tpid = 0;
 	return err;
 }
 
@@ -246,7 +247,8 @@  static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
 	if (skb_vlan_tag_present(skb))
 		invalidate_flow_key(key);
 	else
-		key->eth.tci = vlan->vlan_tci;
+		key->eth.vlan.tci = vlan->vlan_tci;
+		key->eth.vlan.tpid = vlan->vlan_tpid;
 	return skb_vlan_push(skb, vlan->vlan_tpid,
 			     ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
 }
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index c8db44a..ed19e2b 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -302,24 +302,68 @@  static bool icmp6hdr_ok(struct sk_buff *skb)
 				  sizeof(struct icmp6hdr));
 }
 
-static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
+/* Parse vlan tag from vlan header.
+ * Returns ERROR on memory error.
+ * Returns 0 if it encounters a non-vlan or incomplete packet.
+ * Returns 1 after successfully parsing vlan tag.
+ */
+
+static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *vlan)
 {
-	struct qtag_prefix {
-		__be16 eth_type; /* ETH_P_8021Q */
-		__be16 tci;
-	};
-	struct qtag_prefix *qp;
+	struct vlan_head *qp = (struct vlan_head *)skb->data;
+
+	if (likely(!eth_type_vlan(qp->tpid)))
+		return 0;
 
-	if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16)))
+	if (unlikely(skb->len < sizeof(struct vlan_head) + sizeof(__be16)))
 		return 0;
 
-	if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) +
-					 sizeof(__be16))))
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct vlan_head) +
+				 sizeof(__be16))))
 		return -ENOMEM;
 
-	qp = (struct qtag_prefix *) skb->data;
-	key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT);
-	__skb_pull(skb, sizeof(struct qtag_prefix));
+	vlan->tci = qp->tci | htons(VLAN_TAG_PRESENT);
+	vlan->tpid = qp->tpid;
+
+	__skb_pull(skb, sizeof(struct vlan_head));
+	return 1;
+}
+
+static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
+{
+	int res;
+
+	key->eth.vlan.tci = 0;
+	key->eth.vlan.tpid = 0;
+	key->eth.cvlan.tci = 0;
+	key->eth.cvlan.tpid = 0;
+
+	if (likely(skb_vlan_tag_present(skb))) {
+		key->eth.vlan.tci = htons(skb->vlan_tci);
+		key->eth.vlan.tpid = skb->vlan_proto;
+
+		/* Case where ingress processing has already stripped
+		 * the outer vlan tag.
+		 */
+		res = parse_vlan_tag(skb, &key->eth.cvlan);
+		if (res < 0)
+			return res;
+		/* For inner tag, return 0 because neither
+		 * non-existent nor partial inner tag is an error.
+		 */
+		return 0;
+	}
+	res = parse_vlan_tag(skb, &key->eth.vlan);
+	if (res <= 0)
+		/* This is an outer tag in the non-accelerated VLAN
+		 * case. Return error unless it is a complete vlan tag.
+		 */
+		return res;
+
+	/* Parse inner vlan tag if present for non-accelerated case. */
+	res = parse_vlan_tag(skb, &key->eth.cvlan);
+	if (res <= 0)
+		return res;
 
 	return 0;
 }
@@ -480,12 +524,8 @@  static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
 	 * update skb->csum here.
 	 */
 
-	key->eth.tci = 0;
-	if (skb_vlan_tag_present(skb))
-		key->eth.tci = htons(skb->vlan_tci);
-	else if (eth->h_proto == htons(ETH_P_8021Q))
-		if (unlikely(parse_vlan(skb, key)))
-			return -ENOMEM;
+	if (unlikely(parse_vlan(skb, key)))
+		return -ENOMEM;
 
 	key->eth.type = parse_ethertype(skb);
 	if (unlikely(key->eth.type == htons(0)))
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index fe527d2..7ea8deb 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -50,6 +50,11 @@  struct ovs_tunnel_info {
 	struct metadata_dst	*tun_dst;
 };
 
+struct vlan_head {
+	__be16 tpid;	/* Vlan type. Generally 802.1q or 802.1ad.*/
+	__be16 tci;	/* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
+};
+
 #define OVS_SW_FLOW_KEY_METADATA_SIZE			\
 	(offsetof(struct sw_flow_key, recirc_id) +	\
 	FIELD_SIZEOF(struct sw_flow_key, recirc_id))
@@ -68,7 +73,8 @@  struct sw_flow_key {
 	struct {
 		u8     src[ETH_ALEN];	/* Ethernet source address. */
 		u8     dst[ETH_ALEN];	/* Ethernet destination address. */
-		__be16 tci;		/* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
+		struct vlan_head vlan;
+		struct vlan_head cvlan;
 		__be16 type;		/* Ethernet frame type. */
 	} eth;
 	union {
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index c92d6a2..97a6d12 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -811,6 +811,36 @@  static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
 	return 0;
 }
 
+static int cust_vlan_from_nlattrs(struct sw_flow_match *match,
+				  const struct nlattr *a[],
+				  bool is_mask, bool log)
+{
+	__be16 ctci = 0;
+	__be16 c_tpid = 0;
+
+	if (a[OVS_KEY_ATTR_VLAN])
+		ctci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+
+	if (a[OVS_KEY_ATTR_ETHERTYPE])
+		c_tpid = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+
+	if (is_mask && c_tpid != htons(0xffff)) {
+		OVS_NLERR(log, "VLAN frames must have an exact match on the CTPID (mask=%x).",
+			  ntohs(c_tpid));
+		return -EINVAL;
+	}
+	if (!(ctci & htons(VLAN_TAG_PRESENT))) {
+		if (is_mask)
+			OVS_NLERR(log, "VLAN CTCI mask does not have exact match for VLAN_TAG_PRESENT bit.");
+		else
+			OVS_NLERR(log, "VLAN CTCI does not have VLAN_TAG_PRESENT bit set.");
+		return -EINVAL;
+	}
+	SW_FLOW_KEY_PUT(match, eth.cvlan.tpid, c_tpid, is_mask);
+	SW_FLOW_KEY_PUT(match, eth.cvlan.tci, ctci, is_mask);
+	return 0;
+}
+
 static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
 				u64 attrs, const struct nlattr **a,
 				bool is_mask, bool log)
@@ -845,7 +875,7 @@  static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
 			return -EINVAL;
 		}
 
-		SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
+		SW_FLOW_KEY_PUT(match, eth.vlan.tci, tci, is_mask);
 		attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
 	}
 
@@ -1064,6 +1094,92 @@  static void mask_set_nlattr(struct nlattr *attr, u8 val)
 	nlattr_set(attr, val, ovs_key_lens);
 }
 
+static int __parse_vlan_from_nlattrs(const struct nlattr **nla,
+				     struct sw_flow_match *match,
+				     u64 *key_attrs, u64 v_attrs,
+				     const struct nlattr **a, bool is_mask,
+				     bool log)
+{
+	int err;
+	const struct nlattr *encap;
+
+	err = cust_vlan_from_nlattrs(match, nla, is_mask, log);
+	if (err)
+		return err;
+
+	encap = nla[OVS_KEY_ATTR_ENCAP];
+	*nla = encap;
+	v_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
+
+	/* Insure that tci key attribute isn't
+	 * overwritten by encapsulated customer tci.
+	 * Ethertype is cleared because it is c_tpid.
+	 */
+	v_attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
+	v_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
+
+	*key_attrs |= v_attrs;
+
+	if (!is_mask)
+		err = parse_flow_nlattrs(*nla, a, key_attrs, log);
+	else
+		err = parse_flow_mask_nlattrs(*nla, a, key_attrs, log);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int parse_vlan_from_nlattrs(const struct nlattr **nla,
+				   struct sw_flow_match *match,
+				   u64 *key_attrs, bool *ie_valid,
+				   const struct nlattr **a, bool is_mask,
+				   bool log)
+{
+	int err;
+	u64 v_attrs = 0;
+
+	if (!is_mask) {
+		err = parse_flow_nlattrs(*nla, a, &v_attrs, log);
+		if (err)
+			return err;
+		/* Another encap attribute here indicates
+		 * the presence of a double tagged vlan.
+		 */
+		if ((v_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
+		    eth_type_vlan(nla_get_be16(nla[OVS_KEY_ATTR_ETHERTYPE]))) {
+			if (!((v_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
+			      (v_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
+				OVS_NLERR(log, "Invalid Inner VLAN frame");
+				return -EINVAL;
+			}
+			*ie_valid = true;
+			err = __parse_vlan_from_nlattrs(nla, match, key_attrs,
+							v_attrs, a, is_mask,
+							log);
+			if (err)
+				return err;
+		}
+	} else {
+		err = parse_flow_mask_nlattrs(*nla, a, &v_attrs, log);
+		if (err)
+			return err;
+
+		if (v_attrs & 1 << OVS_KEY_ATTR_ENCAP) {
+			if (!*ie_valid) {
+				OVS_NLERR(log, "Encap mask attribute is set for non-CVLAN frame.");
+				return -EINVAL;
+			}
+			err = __parse_vlan_from_nlattrs(nla, match, key_attrs,
+							v_attrs, a, is_mask,
+							log);
+			if (err)
+				return err;
+		}
+	}
+	return 0;
+}
+
 /**
  * ovs_nla_get_match - parses Netlink attributes into a flow key and
  * mask. In case the 'mask' is NULL, the flow is treated as exact match
@@ -1091,6 +1207,7 @@  int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
 	u64 key_attrs = 0;
 	u64 mask_attrs = 0;
 	bool encap_valid = false;
+	bool i_encap_valid = false;
 	int err;
 
 	err = parse_flow_nlattrs(nla_key, a, &key_attrs, log);
@@ -1099,11 +1216,11 @@  int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
 
 	if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) &&
 	    (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
-	    (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
+	    eth_type_vlan(nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]))) {
 		__be16 tci;
 
-		if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
-		      (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
+		if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) &&
+		      (key_attrs & (1ULL << OVS_KEY_ATTR_ENCAP)))) {
 			OVS_NLERR(log, "Invalid Vlan frame.");
 			return -EINVAL;
 		}
@@ -1115,9 +1232,12 @@  int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
 		encap_valid = true;
 
 		if (tci & htons(VLAN_TAG_PRESENT)) {
-			err = parse_flow_nlattrs(encap, a, &key_attrs, log);
+			err = parse_vlan_from_nlattrs(&encap, match, &key_attrs,
+						      &i_encap_valid, a, false,
+						      log);
 			if (err)
 				return err;
+
 		} else if (!tci) {
 			/* Corner case for truncated 802.1Q header. */
 			if (nla_len(encap)) {
@@ -1169,7 +1289,7 @@  int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
 			goto free_newmask;
 
 		/* Always match on tci. */
-		SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
+		SW_FLOW_KEY_PUT(match, eth.vlan.tci, htons(0xffff), true);
 
 		if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) {
 			__be16 eth_type = 0;
@@ -1188,10 +1308,13 @@  int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
 			if (eth_type == htons(0xffff)) {
 				mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
 				encap = a[OVS_KEY_ATTR_ENCAP];
-				err = parse_flow_mask_nlattrs(encap, a,
-							      &mask_attrs, log);
+				err = parse_vlan_from_nlattrs(&encap, match,
+							      &mask_attrs,
+							      &i_encap_valid,
+							      a, true, log);
 				if (err)
 					goto free_newmask;
+
 			} else {
 				OVS_NLERR(log, "VLAN frames must have an exact match on the TPID (mask=%x).",
 					  ntohs(eth_type));
@@ -1320,6 +1443,7 @@  static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
 {
 	struct ovs_key_ethernet *eth_key;
 	struct nlattr *nla, *encap;
+	struct nlattr *in_encap = NULL;
 
 	if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
 		goto nla_put_failure;
@@ -1368,17 +1492,29 @@  static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
 	ether_addr_copy(eth_key->eth_src, output->eth.src);
 	ether_addr_copy(eth_key->eth_dst, output->eth.dst);
 
-	if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
-		__be16 eth_type;
-		eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
-		if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
-		    nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
+	if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
+		if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
+				 output->eth.vlan.tpid) ||
+		    nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.vlan.tci))
 			goto nla_put_failure;
 		encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
-		if (!swkey->eth.tci)
+		if (!swkey->eth.vlan.tci)
 			goto unencap;
-	} else
+		if (swkey->eth.cvlan.tci) {
+			/* Customer tci is nested but uses same key attribute.
+			 */
+			if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
+					 output->eth.cvlan.tpid) ||
+			    nla_put_be16(skb, OVS_KEY_ATTR_VLAN,
+					 output->eth.cvlan.tci))
+				goto nla_put_failure;
+			in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
+			if (!swkey->eth.cvlan.tci)
+				goto unencap;
+		}
+	} else {
 		encap = NULL;
+	}
 
 	if (swkey->eth.type == htons(ETH_P_802_2)) {
 		/*
@@ -1523,6 +1659,8 @@  static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
 	}
 
 unencap:
+	if (in_encap)
+		nla_nest_end(skb, in_encap);
 	if (encap)
 		nla_nest_end(skb, encap);
 
@@ -2174,7 +2312,7 @@  static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
 
 		case OVS_ACTION_ATTR_PUSH_VLAN:
 			vlan = nla_data(a);
-			if (vlan->vlan_tpid != htons(ETH_P_8021Q))
+			if (!eth_type_vlan(vlan->vlan_tpid))
 				return -EINVAL;
 			if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
 				return -EINVAL;
@@ -2279,7 +2417,7 @@  int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
 
 	(*sfa)->orig_len = nla_len(attr);
 	err = __ovs_nla_copy_actions(net, attr, key, 0, sfa, key->eth.type,
-				     key->eth.tci, log);
+				     key->eth.vlan.tci, log);
 	if (err)
 		ovs_nla_free_flow_actions(*sfa);
 
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index f7e8dcc..d2581b7 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -194,7 +194,9 @@  static unsigned int packet_length(const struct sk_buff *skb)
 {
 	unsigned int length = skb->len - ETH_HLEN;
 
-	if (skb->protocol == htons(ETH_P_8021Q))
+	if (eth_type_vlan(skb->protocol))
+		length -= VLAN_HLEN;
+	if (skb->protocol == htons(ETH_P_8021AD))
 		length -= VLAN_HLEN;
 
 	return length;