Skip to content

Commit 1a66a83

Browse files
williamtudavem330
authored andcommitted
gre: add collect_md mode to ERSPAN tunnel
Similar to gre, vxlan, geneve, ipip tunnels, allow ERSPAN tunnels to operate in 'collect metadata' mode. bpf_skb_[gs]et_tunnel_key() helpers can make use of it right away. OVS can use it as well in the future. Signed-off-by: William Tu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 862a03c commit 1a66a83

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

include/net/ip_tunnels.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,10 @@ struct ip_tunnel {
154154
#define TUNNEL_GENEVE_OPT __cpu_to_be16(0x0800)
155155
#define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000)
156156
#define TUNNEL_NOCACHE __cpu_to_be16(0x2000)
157+
#define TUNNEL_ERSPAN_OPT __cpu_to_be16(0x4000)
157158

158-
#define TUNNEL_OPTIONS_PRESENT (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT)
159+
#define TUNNEL_OPTIONS_PRESENT \
160+
(TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
159161

160162
struct tnl_ptk_info {
161163
__be16 flags;

net/ipv4/ip_gre.c

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
113113

114114
static struct rtnl_link_ops ipgre_link_ops __read_mostly;
115115
static int ipgre_tunnel_init(struct net_device *dev);
116+
static void erspan_build_header(struct sk_buff *skb,
117+
__be32 id, u32 index, bool truncate);
116118

117119
static unsigned int ipgre_net_id __read_mostly;
118120
static unsigned int gre_tap_net_id __read_mostly;
@@ -287,7 +289,33 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
287289
false, false) < 0)
288290
goto drop;
289291

290-
tunnel->index = ntohl(index);
292+
if (tunnel->collect_md) {
293+
struct ip_tunnel_info *info;
294+
struct erspan_metadata *md;
295+
__be64 tun_id;
296+
__be16 flags;
297+
298+
tpi->flags |= TUNNEL_KEY;
299+
flags = tpi->flags;
300+
tun_id = key32_to_tunnel_id(tpi->key);
301+
302+
tun_dst = ip_tun_rx_dst(skb, flags,
303+
tun_id, sizeof(*md));
304+
if (!tun_dst)
305+
return PACKET_REJECT;
306+
307+
md = ip_tunnel_info_opts(&tun_dst->u.tun_info);
308+
if (!md)
309+
return PACKET_REJECT;
310+
311+
md->index = index;
312+
info = &tun_dst->u.tun_info;
313+
info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
314+
info->options_len = sizeof(*md);
315+
} else {
316+
tunnel->index = ntohl(index);
317+
}
318+
291319
skb_reset_mac_header(skb);
292320
ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
293321
return PACKET_RCVD;
@@ -523,6 +551,64 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
523551
dev->stats.tx_dropped++;
524552
}
525553

554+
static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
555+
__be16 proto)
556+
{
557+
struct ip_tunnel *tunnel = netdev_priv(dev);
558+
struct ip_tunnel_info *tun_info;
559+
const struct ip_tunnel_key *key;
560+
struct erspan_metadata *md;
561+
struct rtable *rt = NULL;
562+
bool truncate = false;
563+
struct flowi4 fl;
564+
int tunnel_hlen;
565+
__be16 df;
566+
567+
tun_info = skb_tunnel_info(skb);
568+
if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) ||
569+
ip_tunnel_info_af(tun_info) != AF_INET))
570+
goto err_free_skb;
571+
572+
key = &tun_info->key;
573+
574+
/* ERSPAN has fixed 8 byte GRE header */
575+
tunnel_hlen = 8 + sizeof(struct erspanhdr);
576+
577+
rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen);
578+
if (!rt)
579+
return;
580+
581+
if (gre_handle_offloads(skb, false))
582+
goto err_free_rt;
583+
584+
if (skb->len > dev->mtu) {
585+
pskb_trim(skb, dev->mtu);
586+
truncate = true;
587+
}
588+
589+
md = ip_tunnel_info_opts(tun_info);
590+
if (!md)
591+
goto err_free_rt;
592+
593+
erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
594+
ntohl(md->index), truncate);
595+
596+
gre_build_header(skb, 8, TUNNEL_SEQ,
597+
htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++));
598+
599+
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
600+
601+
iptunnel_xmit(skb->sk, rt, skb, fl.saddr, key->u.ipv4.dst, IPPROTO_GRE,
602+
key->tos, key->ttl, df, false);
603+
return;
604+
605+
err_free_rt:
606+
ip_rt_put(rt);
607+
err_free_skb:
608+
kfree_skb(skb);
609+
dev->stats.tx_dropped++;
610+
}
611+
526612
static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
527613
{
528614
struct ip_tunnel_info *info = skb_tunnel_info(skb);
@@ -636,6 +722,11 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
636722
struct ip_tunnel *tunnel = netdev_priv(dev);
637723
bool truncate = false;
638724

725+
if (tunnel->collect_md) {
726+
erspan_fb_xmit(skb, dev, skb->protocol);
727+
return NETDEV_TX_OK;
728+
}
729+
639730
if (gre_handle_offloads(skb, false))
640731
goto free_skb;
641732

@@ -998,9 +1089,12 @@ static int erspan_validate(struct nlattr *tb[], struct nlattr *data[],
9981089
return ret;
9991090

10001091
/* ERSPAN should only have GRE sequence and key flag */
1001-
flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
1002-
flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
1003-
if (flags != (GRE_SEQ | GRE_KEY))
1092+
if (data[IFLA_GRE_OFLAGS])
1093+
flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
1094+
if (data[IFLA_GRE_IFLAGS])
1095+
flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
1096+
if (!data[IFLA_GRE_COLLECT_METADATA] &&
1097+
flags != (GRE_SEQ | GRE_KEY))
10041098
return -EINVAL;
10051099

10061100
/* ERSPAN Session ID only has 10-bit. Since we reuse

0 commit comments

Comments
 (0)