* s, Recur, Flags, Version fields only S (bit 03) is set to 1. The
* other fields are set to zero, so only a sequence number follows.
*
- * ERSPAN Type II header (8 octets [42:49])
+ * ERSPAN Version 1 (Type II) header (8 octets [42:49])
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
*/
-#define ERSPAN_VERSION 0x1
+#define ERSPAN_VERSION 0x1 /* ERSPAN type II */
#define VER_MASK 0xf000
#define VLAN_MASK 0x0fff
ERSPAN_ENCAP_INFRAME = 0x3, /* VLAN tag perserved in frame */
};
+#define ERSPAN_V1_MDSIZE 4
+#define ERSPAN_V2_MDSIZE 8
struct erspan_metadata {
- __be32 index; /* type II */
+ union {
+ __be32 index; /* Version 1 (type II)*/
+ } u;
};
-struct erspanhdr {
+struct erspan_base_hdr {
__be16 ver_vlan;
#define VER_OFFSET 12
__be16 session_id;
#define COS_OFFSET 13
#define EN_OFFSET 11
#define T_OFFSET 10
- struct erspan_metadata md;
};
+static inline int erspan_hdr_len(int version)
+{
+ return sizeof(struct erspan_base_hdr) +
+ (version == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE);
+}
+
static inline u8 tos_to_cos(u8 tos)
{
u8 dscp, cos;
{
struct ethhdr *eth = eth_hdr(skb);
enum erspan_encap_type enc_type;
- struct erspanhdr *ershdr;
+ struct erspan_base_hdr *ershdr;
+ struct erspan_metadata *ersmd;
struct qtag_prefix {
__be16 eth_type;
__be16 tci;
enc_type = ERSPAN_ENCAP_INFRAME;
}
- skb_push(skb, sizeof(*ershdr));
- ershdr = (struct erspanhdr *)skb->data;
- memset(ershdr, 0, sizeof(*ershdr));
+ skb_push(skb, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
+ ershdr = (struct erspan_base_hdr *)skb->data;
+ memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
+ /* Build base header */
ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) |
(ERSPAN_VERSION << VER_OFFSET));
ershdr->session_id = htons((u16)(ntohl(id) & ID_MASK) |
((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) |
(enc_type << EN_OFFSET & EN_MASK) |
((truncate << T_OFFSET) & T_MASK));
- ershdr->md.index = htonl(index & INDEX_MASK);
+
+ /* Build metadata */
+ ersmd = (struct erspan_metadata *)(ershdr + 1);
+ ersmd->u.index = htonl(index & INDEX_MASK);
}
#endif
{
struct net *net = dev_net(skb->dev);
struct metadata_dst *tun_dst = NULL;
+ struct erspan_base_hdr *ershdr;
+ struct erspan_metadata *pkt_md;
struct ip_tunnel_net *itn;
struct ip_tunnel *tunnel;
- struct erspanhdr *ershdr;
const struct iphdr *iph;
- __be32 index;
+ int ver;
int len;
itn = net_generic(net, erspan_net_id);
len = gre_hdr_len + sizeof(*ershdr);
+ /* Check based hdr len */
if (unlikely(!pskb_may_pull(skb, len)))
return -ENOMEM;
iph = ip_hdr(skb);
- ershdr = (struct erspanhdr *)(skb->data + gre_hdr_len);
+ ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
+ ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET;
/* The original GRE header does not have key field,
* Use ERSPAN 10-bit session ID as key.
*/
tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
- index = ershdr->md.index;
+ pkt_md = (struct erspan_metadata *)(ershdr + 1);
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
tpi->flags | TUNNEL_KEY,
iph->saddr, iph->daddr, tpi->key);
if (tunnel) {
+ len = gre_hdr_len + erspan_hdr_len(ver);
+ if (unlikely(!pskb_may_pull(skb, len)))
+ return -ENOMEM;
+
if (__iptunnel_pull_header(skb,
- gre_hdr_len + sizeof(*ershdr),
+ len,
htons(ETH_P_TEB),
false, false) < 0)
goto drop;
if (!md)
return PACKET_REJECT;
- md->index = index;
+ memcpy(md, pkt_md, sizeof(*md));
info = &tun_dst->u.tun_info;
info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
info->options_len = sizeof(*md);
} else {
- tunnel->index = ntohl(index);
+ tunnel->index = ntohl(pkt_md->u.index);
}
skb_reset_mac_header(skb);
key = &tun_info->key;
/* ERSPAN has fixed 8 byte GRE header */
- tunnel_hlen = 8 + sizeof(struct erspanhdr);
+ tunnel_hlen = 8 + sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen);
if (!rt)
goto err_free_rt;
erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
- ntohl(md->index), truncate, true);
+ ntohl(md->u.index), truncate, true);
gre_build_header(skb, 8, TUNNEL_SEQ,
htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++));
tunnel->tun_hlen = 8;
tunnel->parms.iph.protocol = IPPROTO_GRE;
tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
- sizeof(struct erspanhdr);
+ sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
t_hlen = tunnel->hlen + sizeof(struct iphdr);
dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4;
static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
struct tnl_ptk_info *tpi)
{
+ struct erspan_base_hdr *ershdr;
+ struct erspan_metadata *pkt_md;
const struct ipv6hdr *ipv6h;
- struct erspanhdr *ershdr;
struct ip6_tnl *tunnel;
- __be32 index;
+ u8 ver;
ipv6h = ipv6_hdr(skb);
- ershdr = (struct erspanhdr *)skb->data;
+ ershdr = (struct erspan_base_hdr *)skb->data;
if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
return PACKET_REJECT;
+ ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET;
tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
- index = ershdr->md.index;
+ pkt_md = (struct erspan_metadata *)(ershdr + 1);
tunnel = ip6gre_tunnel_lookup(skb->dev,
&ipv6h->saddr, &ipv6h->daddr, tpi->key,
tpi->proto);
if (tunnel) {
- if (__iptunnel_pull_header(skb, sizeof(*ershdr),
+ int len = erspan_hdr_len(ver);
+
+ if (unlikely(!pskb_may_pull(skb, len)))
+ return -ENOMEM;
+
+ if (__iptunnel_pull_header(skb, len,
htons(ETH_P_TEB),
false, false) < 0)
return PACKET_REJECT;
if (!md)
return PACKET_REJECT;
- md->index = index;
+ memcpy(md, pkt_md, sizeof(*md));
info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
info->options_len = sizeof(*md);
ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
} else {
- tunnel->parms.index = ntohl(index);
+ tunnel->parms.index = ntohl(pkt_md->u.index);
ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
}
goto tx_err;
erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
- ntohl(md->index), truncate, false);
+ ntohl(md->u.index), truncate, false);
} else {
switch (skb->protocol) {
tunnel->tun_hlen = 8;
tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
- sizeof(struct erspanhdr);
+ sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
dev->hard_header_len = LL_MAX_HEADER + t_hlen;
BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts));
memset(&opts, 0, sizeof(opts));
- opts.index = nla_get_be32(attr);
+ opts.u.index = nla_get_be32(attr);
/* Index has only 20-bit */
- if (ntohl(opts.index) & ~INDEX_MASK) {
+ if (ntohl(opts.u.index) & ~INDEX_MASK) {
OVS_NLERR(log, "ERSPAN index number %x too large.",
- ntohl(opts.index));
+ ntohl(opts.u.index));
return -EINVAL;
}
return -EMSGSIZE;
else if (output->tun_flags & TUNNEL_ERSPAN_OPT &&
nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,
- ((struct erspan_metadata *)tun_opts)->index))
+ ((struct erspan_metadata *)tun_opts)->u.index))
return -EMSGSIZE;
}