Commit 018c1dda authored by Eric Garver's avatar Eric Garver Committed by David S. Miller

openvswitch: 802.1AD Flow handling, actions, vlan parsing, netlink attributes

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: default avatarThomas F Herbert <thomasfherbert@gmail.com>
Signed-off-by: default avatarEric Garver <e@erig.me>
Acked-by: default avatarPravin B Shelar <pshelar@ovn.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fe19c4f9
...@@ -246,20 +246,24 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) ...@@ -246,20 +246,24 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
int err; int err;
err = skb_vlan_pop(skb); err = skb_vlan_pop(skb);
if (skb_vlan_tag_present(skb)) if (skb_vlan_tag_present(skb)) {
invalidate_flow_key(key); invalidate_flow_key(key);
else } else {
key->eth.tci = 0; key->eth.vlan.tci = 0;
key->eth.vlan.tpid = 0;
}
return err; return err;
} }
static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_action_push_vlan *vlan) const struct ovs_action_push_vlan *vlan)
{ {
if (skb_vlan_tag_present(skb)) if (skb_vlan_tag_present(skb)) {
invalidate_flow_key(key); invalidate_flow_key(key);
else } 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, return skb_vlan_push(skb, vlan->vlan_tpid,
ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
} }
......
...@@ -302,24 +302,57 @@ static bool icmp6hdr_ok(struct sk_buff *skb) ...@@ -302,24 +302,57 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
sizeof(struct icmp6hdr)); 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 { struct vlan_head *vh = (struct vlan_head *)skb->data;
__be16 eth_type; /* ETH_P_8021Q */
__be16 tci;
};
struct qtag_prefix *qp;
if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16))) if (likely(!eth_type_vlan(vh->tpid)))
return 0; return 0;
if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) + if (unlikely(skb->len < sizeof(struct vlan_head) + sizeof(__be16)))
sizeof(__be16)))) return 0;
if (unlikely(!pskb_may_pull(skb, sizeof(struct vlan_head) +
sizeof(__be16))))
return -ENOMEM; return -ENOMEM;
qp = (struct qtag_prefix *) skb->data; vh = (struct vlan_head *)skb->data;
key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT); key_vh->tci = vh->tci | htons(VLAN_TAG_PRESENT);
__skb_pull(skb, sizeof(struct qtag_prefix)); 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; return 0;
} }
...@@ -480,12 +513,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) ...@@ -480,12 +513,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
* update skb->csum here. * update skb->csum here.
*/ */
key->eth.tci = 0; if (unlikely(parse_vlan(skb, key)))
if (skb_vlan_tag_present(skb)) return -ENOMEM;
key->eth.tci = htons(skb->vlan_tci);
else if (eth->h_proto == htons(ETH_P_8021Q))
if (unlikely(parse_vlan(skb, key)))
return -ENOMEM;
key->eth.type = parse_ethertype(skb); key->eth.type = parse_ethertype(skb);
if (unlikely(key->eth.type == htons(0))) if (unlikely(key->eth.type == htons(0)))
......
...@@ -50,6 +50,11 @@ struct ovs_tunnel_info { ...@@ -50,6 +50,11 @@ struct ovs_tunnel_info {
struct metadata_dst *tun_dst; 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 \ #define OVS_SW_FLOW_KEY_METADATA_SIZE \
(offsetof(struct sw_flow_key, recirc_id) + \ (offsetof(struct sw_flow_key, recirc_id) + \
FIELD_SIZEOF(struct sw_flow_key, recirc_id)) FIELD_SIZEOF(struct sw_flow_key, recirc_id))
...@@ -69,7 +74,8 @@ struct sw_flow_key { ...@@ -69,7 +74,8 @@ struct sw_flow_key {
struct { struct {
u8 src[ETH_ALEN]; /* Ethernet source address. */ u8 src[ETH_ALEN]; /* Ethernet source address. */
u8 dst[ETH_ALEN]; /* Ethernet destination 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. */ __be16 type; /* Ethernet frame type. */
} eth; } eth;
union { union {
......
This diff is collapsed.
...@@ -485,9 +485,14 @@ static unsigned int packet_length(const struct sk_buff *skb) ...@@ -485,9 +485,14 @@ static unsigned int packet_length(const struct sk_buff *skb)
{ {
unsigned int length = skb->len - ETH_HLEN; unsigned int length = skb->len - ETH_HLEN;
if (skb->protocol == htons(ETH_P_8021Q)) if (skb_vlan_tagged(skb))
length -= VLAN_HLEN; length -= VLAN_HLEN;
/* Don't subtract for multiple VLAN tags. Most (all?) drivers allow
* (ETH_LEN + VLAN_HLEN) in addition to the mtu value, but almost none
* account for 802.1ad. e.g. is_skb_forwardable().
*/
return length; return length;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment