Skip to content

Commit f551c91

Browse files
williamtudavem330
authored andcommitted
net: erspan: introduce erspan v2 for ip_gre
The patch adds support for erspan version 2. Not all features are supported in this patch. The SGT (security group tag), GRA (timestamp granularity), FT (frame type) are set to fixed value. Only hardware ID and direction are configurable. Optional subheader is also not supported. Signed-off-by: William Tu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1d7e2ed commit f551c91

File tree

5 files changed

+216
-18
lines changed

5 files changed

+216
-18
lines changed

include/net/erspan.h

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,29 @@
2424
* | Reserved | Index |
2525
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2626
*
27+
*
28+
* ERSPAN Version 2 (Type III) header (12 octets [42:49])
29+
* 0 1 2 3
30+
* 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
31+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32+
* | Ver | VLAN | COS |BSO|T| Session ID |
33+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34+
* | Timestamp |
35+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36+
* | SGT |P| FT | Hw ID |D|Gra|O|
37+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38+
*
39+
* Platform Specific SubHeader (8 octets, optional)
40+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41+
* | Platf ID | Platform Specific Info |
42+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43+
* | Platform Specific Info |
44+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45+
*
2746
* GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
2847
*/
2948

3049
#define ERSPAN_VERSION 0x1 /* ERSPAN type II */
31-
3250
#define VER_MASK 0xf000
3351
#define VLAN_MASK 0x0fff
3452
#define COS_MASK 0xe000
@@ -37,6 +55,28 @@
3755
#define ID_MASK 0x03ff
3856
#define INDEX_MASK 0xfffff
3957

58+
#define ERSPAN_VERSION2 0x2 /* ERSPAN type III*/
59+
#define BSO_MASK EN_MASK
60+
#define SGT_MASK 0xffff0000
61+
#define P_MASK 0x8000
62+
#define FT_MASK 0x7c00
63+
#define HWID_MASK 0x03f0
64+
#define DIR_MASK 0x0008
65+
#define GRA_MASK 0x0006
66+
#define O_MASK 0x0001
67+
68+
/* ERSPAN version 2 metadata header */
69+
struct erspan_md2 {
70+
__be32 timestamp;
71+
__be16 sgt; /* security group tag */
72+
__be16 flags;
73+
#define P_OFFSET 15
74+
#define FT_OFFSET 10
75+
#define HWID_OFFSET 4
76+
#define DIR_OFFSET 3
77+
#define GRA_OFFSET 1
78+
};
79+
4080
enum erspan_encap_type {
4181
ERSPAN_ENCAP_NOVLAN = 0x0, /* originally without VLAN tag */
4282
ERSPAN_ENCAP_ISL = 0x1, /* originally ISL encapsulated */
@@ -48,8 +88,10 @@ enum erspan_encap_type {
4888
#define ERSPAN_V2_MDSIZE 8
4989
struct erspan_metadata {
5090
union {
51-
__be32 index; /* Version 1 (type II)*/
91+
__be32 index; /* Version 1 (type II)*/
92+
struct erspan_md2 md2; /* Version 2 (type III) */
5293
} u;
94+
int version;
5395
};
5496

5597
struct erspan_base_hdr {
@@ -58,6 +100,7 @@ struct erspan_base_hdr {
58100
__be16 session_id;
59101
#define COS_OFFSET 13
60102
#define EN_OFFSET 11
103+
#define BSO_OFFSET EN_OFFSET
61104
#define T_OFFSET 10
62105
};
63106

@@ -123,4 +166,77 @@ static inline void erspan_build_header(struct sk_buff *skb,
123166
ersmd->u.index = htonl(index & INDEX_MASK);
124167
}
125168

169+
/* ERSPAN GRA: timestamp granularity
170+
* 00b --> granularity = 100 microseconds
171+
* 01b --> granularity = 100 nanoseconds
172+
* 10b --> granularity = IEEE 1588
173+
* Here we only support 100 microseconds.
174+
*/
175+
static inline __be32 erspan_get_timestamp(void)
176+
{
177+
u64 h_usecs;
178+
ktime_t kt;
179+
180+
kt = ktime_get_real();
181+
h_usecs = ktime_divns(kt, 100 * NSEC_PER_USEC);
182+
183+
/* ERSPAN base header only has 32-bit,
184+
* so it wraps around 4 days.
185+
*/
186+
return htonl((u32)h_usecs);
187+
}
188+
189+
static inline void erspan_build_header_v2(struct sk_buff *skb,
190+
__be32 id, u8 direction, u16 hwid,
191+
bool truncate, bool is_ipv4)
192+
{
193+
struct ethhdr *eth = eth_hdr(skb);
194+
struct erspan_base_hdr *ershdr;
195+
struct erspan_metadata *md;
196+
struct qtag_prefix {
197+
__be16 eth_type;
198+
__be16 tci;
199+
} *qp;
200+
u16 vlan_tci = 0;
201+
u16 session_id;
202+
u8 gra = 0; /* 100 usec */
203+
u8 bso = 0; /* Bad/Short/Oversized */
204+
u8 sgt = 0;
205+
u8 tos;
206+
207+
tos = is_ipv4 ? ip_hdr(skb)->tos :
208+
(ipv6_hdr(skb)->priority << 4) +
209+
(ipv6_hdr(skb)->flow_lbl[0] >> 4);
210+
211+
/* Unlike v1, v2 does not have En field,
212+
* so only extract vlan tci field.
213+
*/
214+
if (eth->h_proto == htons(ETH_P_8021Q)) {
215+
qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN);
216+
vlan_tci = ntohs(qp->tci);
217+
}
218+
219+
skb_push(skb, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
220+
ershdr = (struct erspan_base_hdr *)skb->data;
221+
memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
222+
223+
/* Build base header */
224+
ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) |
225+
(ERSPAN_VERSION2 << VER_OFFSET));
226+
session_id = (u16)(ntohl(id) & ID_MASK) |
227+
((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) |
228+
(bso << BSO_OFFSET & BSO_MASK) |
229+
((truncate << T_OFFSET) & T_MASK);
230+
ershdr->session_id = htons(session_id);
231+
232+
/* Build metadata */
233+
md = (struct erspan_metadata *)(ershdr + 1);
234+
md->u.md2.timestamp = erspan_get_timestamp();
235+
md->u.md2.sgt = htons(sgt);
236+
md->u.md2.flags = htons(((1 << P_OFFSET) & P_MASK) |
237+
((hwid << HWID_OFFSET) & HWID_MASK) |
238+
((direction << DIR_OFFSET) & DIR_MASK) |
239+
((gra << GRA_OFFSET) & GRA_MASK));
240+
}
241+
126242
#endif

include/net/ip_tunnels.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ struct ip_tunnel {
116116
u32 o_seqno; /* The last output seqno */
117117
int tun_hlen; /* Precalculated header length */
118118

119-
/* This field used only by ERSPAN */
119+
/* These four fields used only by ERSPAN */
120120
u32 index; /* ERSPAN type II index */
121+
u8 erspan_ver; /* ERSPAN version */
122+
u8 dir; /* ERSPAN direction */
123+
u16 hwid; /* ERSPAN hardware ID */
121124

122125
struct dst_cache dst_cache;
123126

include/uapi/linux/if_ether.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#define ETH_P_PUP 0x0200 /* Xerox PUP packet */
4848
#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */
4949
#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */
50+
#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */
5051
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
5152
#define ETH_P_X25 0x0805 /* CCITT X.25 */
5253
#define ETH_P_ARP 0x0806 /* Address Resolution packet */

include/uapi/linux/if_tunnel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ enum {
137137
IFLA_GRE_IGNORE_DF,
138138
IFLA_GRE_FWMARK,
139139
IFLA_GRE_ERSPAN_INDEX,
140+
IFLA_GRE_ERSPAN_VER,
141+
IFLA_GRE_ERSPAN_DIR,
142+
IFLA_GRE_ERSPAN_HWID,
140143
__IFLA_GRE_MAX,
141144
};
142145

net/ipv4/ip_gre.c

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,26 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
315315
return PACKET_REJECT;
316316

317317
memcpy(md, pkt_md, sizeof(*md));
318+
md->version = ver;
319+
318320
info = &tun_dst->u.tun_info;
319321
info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
320322
info->options_len = sizeof(*md);
321323
} else {
322-
tunnel->index = ntohl(pkt_md->u.index);
324+
tunnel->erspan_ver = ver;
325+
if (ver == 1) {
326+
tunnel->index = ntohl(pkt_md->u.index);
327+
} else {
328+
u16 md2_flags;
329+
u16 dir, hwid;
330+
331+
md2_flags = ntohs(pkt_md->u.md2.flags);
332+
dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
333+
hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
334+
tunnel->dir = dir;
335+
tunnel->hwid = hwid;
336+
}
337+
323338
}
324339

325340
skb_reset_mac_header(skb);
@@ -413,7 +428,8 @@ static int gre_rcv(struct sk_buff *skb)
413428
if (hdr_len < 0)
414429
goto drop;
415430

416-
if (unlikely(tpi.proto == htons(ETH_P_ERSPAN))) {
431+
if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
432+
tpi.proto == htons(ETH_P_ERSPAN2))) {
417433
if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
418434
return 0;
419435
}
@@ -568,6 +584,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
568584
bool truncate = false;
569585
struct flowi4 fl;
570586
int tunnel_hlen;
587+
int version;
571588
__be16 df;
572589

573590
tun_info = skb_tunnel_info(skb);
@@ -576,9 +593,13 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
576593
goto err_free_skb;
577594

578595
key = &tun_info->key;
596+
md = ip_tunnel_info_opts(tun_info);
597+
if (!md)
598+
goto err_free_rt;
579599

580600
/* ERSPAN has fixed 8 byte GRE header */
581-
tunnel_hlen = 8 + sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
601+
version = md->version;
602+
tunnel_hlen = 8 + erspan_hdr_len(version);
582603

583604
rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen);
584605
if (!rt)
@@ -592,12 +613,23 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
592613
truncate = true;
593614
}
594615

595-
md = ip_tunnel_info_opts(tun_info);
596-
if (!md)
597-
goto err_free_rt;
616+
if (version == 1) {
617+
erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
618+
ntohl(md->u.index), truncate, true);
619+
} else if (version == 2) {
620+
u16 md2_flags;
621+
u8 direction;
622+
u16 hwid;
598623

599-
erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
600-
ntohl(md->u.index), truncate, true);
624+
md2_flags = ntohs(md->u.md2.flags);
625+
direction = (md2_flags & DIR_MASK) >> DIR_OFFSET;
626+
hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
627+
628+
erspan_build_header_v2(skb, tunnel_id_to_key32(key->tun_id),
629+
direction, hwid, truncate, true);
630+
} else {
631+
goto err_free_rt;
632+
}
601633

602634
gre_build_header(skb, 8, TUNNEL_SEQ,
603635
htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++));
@@ -699,8 +731,14 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
699731
}
700732

701733
/* Push ERSPAN header */
702-
erspan_build_header(skb, tunnel->parms.o_key, tunnel->index,
703-
truncate, true);
734+
if (tunnel->erspan_ver == 1)
735+
erspan_build_header(skb, tunnel->parms.o_key, tunnel->index,
736+
truncate, true);
737+
else
738+
erspan_build_header_v2(skb, tunnel->parms.o_key,
739+
tunnel->dir, tunnel->hwid,
740+
truncate, true);
741+
704742
tunnel->parms.o_flags &= ~TUNNEL_KEY;
705743
__gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_ERSPAN));
706744
return NETDEV_TX_OK;
@@ -1172,13 +1210,32 @@ static int ipgre_netlink_parms(struct net_device *dev,
11721210
if (data[IFLA_GRE_FWMARK])
11731211
*fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
11741212

1175-
if (data[IFLA_GRE_ERSPAN_INDEX]) {
1176-
t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
1213+
if (data[IFLA_GRE_ERSPAN_VER]) {
1214+
t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
11771215

1178-
if (t->index & ~INDEX_MASK)
1216+
if (t->erspan_ver != 1 && t->erspan_ver != 2)
11791217
return -EINVAL;
11801218
}
11811219

1220+
if (t->erspan_ver == 1) {
1221+
if (data[IFLA_GRE_ERSPAN_INDEX]) {
1222+
t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
1223+
if (t->index & ~INDEX_MASK)
1224+
return -EINVAL;
1225+
}
1226+
} else if (t->erspan_ver == 2) {
1227+
if (data[IFLA_GRE_ERSPAN_DIR]) {
1228+
t->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
1229+
if (t->dir & ~(DIR_MASK >> DIR_OFFSET))
1230+
return -EINVAL;
1231+
}
1232+
if (data[IFLA_GRE_ERSPAN_HWID]) {
1233+
t->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
1234+
if (t->hwid & ~(HWID_MASK >> HWID_OFFSET))
1235+
return -EINVAL;
1236+
}
1237+
}
1238+
11821239
return 0;
11831240
}
11841241

@@ -1245,7 +1302,7 @@ static int erspan_tunnel_init(struct net_device *dev)
12451302
tunnel->tun_hlen = 8;
12461303
tunnel->parms.iph.protocol = IPPROTO_GRE;
12471304
tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
1248-
sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
1305+
erspan_hdr_len(tunnel->erspan_ver);
12491306
t_hlen = tunnel->hlen + sizeof(struct iphdr);
12501307

12511308
dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4;
@@ -1375,6 +1432,12 @@ static size_t ipgre_get_size(const struct net_device *dev)
13751432
nla_total_size(4) +
13761433
/* IFLA_GRE_ERSPAN_INDEX */
13771434
nla_total_size(4) +
1435+
/* IFLA_GRE_ERSPAN_VER */
1436+
nla_total_size(1) +
1437+
/* IFLA_GRE_ERSPAN_DIR */
1438+
nla_total_size(1) +
1439+
/* IFLA_GRE_ERSPAN_HWID */
1440+
nla_total_size(2) +
13781441
0;
13791442
}
13801443

@@ -1417,9 +1480,18 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
14171480
goto nla_put_failure;
14181481
}
14191482

1420-
if (t->index)
1483+
if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
1484+
goto nla_put_failure;
1485+
1486+
if (t->erspan_ver == 1) {
14211487
if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index))
14221488
goto nla_put_failure;
1489+
} else if (t->erspan_ver == 2) {
1490+
if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir))
1491+
goto nla_put_failure;
1492+
if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid))
1493+
goto nla_put_failure;
1494+
}
14231495

14241496
return 0;
14251497

@@ -1455,6 +1527,9 @@ static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = {
14551527
[IFLA_GRE_IGNORE_DF] = { .type = NLA_U8 },
14561528
[IFLA_GRE_FWMARK] = { .type = NLA_U32 },
14571529
[IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 },
1530+
[IFLA_GRE_ERSPAN_VER] = { .type = NLA_U8 },
1531+
[IFLA_GRE_ERSPAN_DIR] = { .type = NLA_U8 },
1532+
[IFLA_GRE_ERSPAN_HWID] = { .type = NLA_U16 },
14581533
};
14591534

14601535
static struct rtnl_link_ops ipgre_link_ops __read_mostly = {

0 commit comments

Comments
 (0)