Skip to content

Commit

Permalink
openvswitch: 802.1AD Flow handling, actions, vlan parsing, netlink at…
Browse files Browse the repository at this point in the history
…tributes

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.

This is based on Thomas F Herbert's original v20 patch. I made some
small clean ups and bug fixes.

Signed-off-by: Thomas F Herbert <[email protected]>
Signed-off-by: Eric Garver <[email protected]>
Acked-by: Pravin B Shelar <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
erig0 authored and davem330 committed Sep 9, 2016
1 parent fe19c4f commit 018c1dd
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 124 deletions.
16 changes: 10 additions & 6 deletions net/openvswitch/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,20 +246,24 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
int err;

err = skb_vlan_pop(skb);
if (skb_vlan_tag_present(skb))
if (skb_vlan_tag_present(skb)) {
invalidate_flow_key(key);
else
key->eth.tci = 0;
} else {
key->eth.vlan.tci = 0;
key->eth.vlan.tpid = 0;
}
return err;
}

static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_action_push_vlan *vlan)
{
if (skb_vlan_tag_present(skb))
if (skb_vlan_tag_present(skb)) {
invalidate_flow_key(key);
else
key->eth.tci = vlan->vlan_tci;
} else {
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);
}
Expand Down
65 changes: 47 additions & 18 deletions net/openvswitch/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,24 +302,57 @@ 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 *key_vh)
{
struct qtag_prefix {
__be16 eth_type; /* ETH_P_8021Q */
__be16 tci;
};
struct qtag_prefix *qp;
struct vlan_head *vh = (struct vlan_head *)skb->data;

if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16)))
if (likely(!eth_type_vlan(vh->tpid)))
return 0;

if (unlikely(!pskb_may_pull(skb, 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 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));
vh = (struct vlan_head *)skb->data;
key_vh->tci = vh->tci | htons(VLAN_TAG_PRESENT);
key_vh->tpid = vh->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;
} else {
/* Parse outer vlan tag in the non-accelerated case. */
res = parse_vlan_tag(skb, &key->eth.vlan);
if (res <= 0)
return res;
}

/* Parse inner vlan tag. */
res = parse_vlan_tag(skb, &key->eth.cvlan);
if (res <= 0)
return res;

return 0;
}
Expand Down Expand Up @@ -480,12 +513,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)))
Expand Down
8 changes: 7 additions & 1 deletion net/openvswitch/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -69,7 +74,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 {
Expand Down
Loading

0 comments on commit 018c1dd

Please sign in to comment.