Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <u9012063@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

William Tu and committed by
David S. Miller
f551c91d 1d7e2ed2

+216 -18
+118 -2
include/net/erspan.h
··· 24 24 * | Reserved | Index | 25 25 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 26 * 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 + * 27 46 * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB 28 47 */ 29 48 30 49 #define ERSPAN_VERSION 0x1 /* ERSPAN type II */ 31 - 32 50 #define VER_MASK 0xf000 33 51 #define VLAN_MASK 0x0fff 34 52 #define COS_MASK 0xe000 ··· 54 36 #define T_MASK 0x0400 55 37 #define ID_MASK 0x03ff 56 38 #define INDEX_MASK 0xfffff 39 + 40 + #define ERSPAN_VERSION2 0x2 /* ERSPAN type III*/ 41 + #define BSO_MASK EN_MASK 42 + #define SGT_MASK 0xffff0000 43 + #define P_MASK 0x8000 44 + #define FT_MASK 0x7c00 45 + #define HWID_MASK 0x03f0 46 + #define DIR_MASK 0x0008 47 + #define GRA_MASK 0x0006 48 + #define O_MASK 0x0001 49 + 50 + /* ERSPAN version 2 metadata header */ 51 + struct erspan_md2 { 52 + __be32 timestamp; 53 + __be16 sgt; /* security group tag */ 54 + __be16 flags; 55 + #define P_OFFSET 15 56 + #define FT_OFFSET 10 57 + #define HWID_OFFSET 4 58 + #define DIR_OFFSET 3 59 + #define GRA_OFFSET 1 60 + }; 57 61 58 62 enum erspan_encap_type { 59 63 ERSPAN_ENCAP_NOVLAN = 0x0, /* originally without VLAN tag */ ··· 88 48 #define ERSPAN_V2_MDSIZE 8 89 49 struct erspan_metadata { 90 50 union { 91 - __be32 index; /* Version 1 (type II)*/ 51 + __be32 index; /* Version 1 (type II)*/ 52 + struct erspan_md2 md2; /* Version 2 (type III) */ 92 53 } u; 54 + int version; 93 55 }; 94 56 95 57 struct erspan_base_hdr { ··· 100 58 __be16 session_id; 101 59 #define COS_OFFSET 13 102 60 #define EN_OFFSET 11 61 + #define BSO_OFFSET EN_OFFSET 103 62 #define T_OFFSET 10 104 63 }; 105 64 ··· 164 121 /* Build metadata */ 165 122 ersmd = (struct erspan_metadata *)(ershdr + 1); 166 123 ersmd->u.index = htonl(index & INDEX_MASK); 124 + } 125 + 126 + /* ERSPAN GRA: timestamp granularity 127 + * 00b --> granularity = 100 microseconds 128 + * 01b --> granularity = 100 nanoseconds 129 + * 10b --> granularity = IEEE 1588 130 + * Here we only support 100 microseconds. 131 + */ 132 + static inline __be32 erspan_get_timestamp(void) 133 + { 134 + u64 h_usecs; 135 + ktime_t kt; 136 + 137 + kt = ktime_get_real(); 138 + h_usecs = ktime_divns(kt, 100 * NSEC_PER_USEC); 139 + 140 + /* ERSPAN base header only has 32-bit, 141 + * so it wraps around 4 days. 142 + */ 143 + return htonl((u32)h_usecs); 144 + } 145 + 146 + static inline void erspan_build_header_v2(struct sk_buff *skb, 147 + __be32 id, u8 direction, u16 hwid, 148 + bool truncate, bool is_ipv4) 149 + { 150 + struct ethhdr *eth = eth_hdr(skb); 151 + struct erspan_base_hdr *ershdr; 152 + struct erspan_metadata *md; 153 + struct qtag_prefix { 154 + __be16 eth_type; 155 + __be16 tci; 156 + } *qp; 157 + u16 vlan_tci = 0; 158 + u16 session_id; 159 + u8 gra = 0; /* 100 usec */ 160 + u8 bso = 0; /* Bad/Short/Oversized */ 161 + u8 sgt = 0; 162 + u8 tos; 163 + 164 + tos = is_ipv4 ? ip_hdr(skb)->tos : 165 + (ipv6_hdr(skb)->priority << 4) + 166 + (ipv6_hdr(skb)->flow_lbl[0] >> 4); 167 + 168 + /* Unlike v1, v2 does not have En field, 169 + * so only extract vlan tci field. 170 + */ 171 + if (eth->h_proto == htons(ETH_P_8021Q)) { 172 + qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN); 173 + vlan_tci = ntohs(qp->tci); 174 + } 175 + 176 + skb_push(skb, sizeof(*ershdr) + ERSPAN_V2_MDSIZE); 177 + ershdr = (struct erspan_base_hdr *)skb->data; 178 + memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE); 179 + 180 + /* Build base header */ 181 + ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) | 182 + (ERSPAN_VERSION2 << VER_OFFSET)); 183 + session_id = (u16)(ntohl(id) & ID_MASK) | 184 + ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) | 185 + (bso << BSO_OFFSET & BSO_MASK) | 186 + ((truncate << T_OFFSET) & T_MASK); 187 + ershdr->session_id = htons(session_id); 188 + 189 + /* Build metadata */ 190 + md = (struct erspan_metadata *)(ershdr + 1); 191 + md->u.md2.timestamp = erspan_get_timestamp(); 192 + md->u.md2.sgt = htons(sgt); 193 + md->u.md2.flags = htons(((1 << P_OFFSET) & P_MASK) | 194 + ((hwid << HWID_OFFSET) & HWID_MASK) | 195 + ((direction << DIR_OFFSET) & DIR_MASK) | 196 + ((gra << GRA_OFFSET) & GRA_MASK)); 167 197 } 168 198 169 199 #endif
+4 -1
include/net/ip_tunnels.h
··· 116 116 u32 o_seqno; /* The last output seqno */ 117 117 int tun_hlen; /* Precalculated header length */ 118 118 119 - /* This field used only by ERSPAN */ 119 + /* These four fields used only by ERSPAN */ 120 120 u32 index; /* ERSPAN type II index */ 121 + u8 erspan_ver; /* ERSPAN version */ 122 + u8 dir; /* ERSPAN direction */ 123 + u16 hwid; /* ERSPAN hardware ID */ 121 124 122 125 struct dst_cache dst_cache; 123 126
+1
include/uapi/linux/if_ether.h
··· 47 47 #define ETH_P_PUP 0x0200 /* Xerox PUP packet */ 48 48 #define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ 49 49 #define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */ 50 + #define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */ 50 51 #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 51 52 #define ETH_P_X25 0x0805 /* CCITT X.25 */ 52 53 #define ETH_P_ARP 0x0806 /* Address Resolution packet */
+3
include/uapi/linux/if_tunnel.h
··· 137 137 IFLA_GRE_IGNORE_DF, 138 138 IFLA_GRE_FWMARK, 139 139 IFLA_GRE_ERSPAN_INDEX, 140 + IFLA_GRE_ERSPAN_VER, 141 + IFLA_GRE_ERSPAN_DIR, 142 + IFLA_GRE_ERSPAN_HWID, 140 143 __IFLA_GRE_MAX, 141 144 }; 142 145
+90 -15
net/ipv4/ip_gre.c
··· 315 315 return PACKET_REJECT; 316 316 317 317 memcpy(md, pkt_md, sizeof(*md)); 318 + md->version = ver; 319 + 318 320 info = &tun_dst->u.tun_info; 319 321 info->key.tun_flags |= TUNNEL_ERSPAN_OPT; 320 322 info->options_len = sizeof(*md); 321 323 } 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 + 323 338 } 324 339 325 340 skb_reset_mac_header(skb); ··· 428 413 if (hdr_len < 0) 429 414 goto drop; 430 415 431 - if (unlikely(tpi.proto == htons(ETH_P_ERSPAN))) { 416 + if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) || 417 + tpi.proto == htons(ETH_P_ERSPAN2))) { 432 418 if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD) 433 419 return 0; 434 420 } ··· 584 568 bool truncate = false; 585 569 struct flowi4 fl; 586 570 int tunnel_hlen; 571 + int version; 587 572 __be16 df; 588 573 589 574 tun_info = skb_tunnel_info(skb); ··· 593 576 goto err_free_skb; 594 577 595 578 key = &tun_info->key; 579 + md = ip_tunnel_info_opts(tun_info); 580 + if (!md) 581 + goto err_free_rt; 596 582 597 583 /* ERSPAN has fixed 8 byte GRE header */ 598 - tunnel_hlen = 8 + sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE; 584 + version = md->version; 585 + tunnel_hlen = 8 + erspan_hdr_len(version); 599 586 600 587 rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen); 601 588 if (!rt) ··· 613 592 truncate = true; 614 593 } 615 594 616 - md = ip_tunnel_info_opts(tun_info); 617 - if (!md) 618 - goto err_free_rt; 595 + if (version == 1) { 596 + erspan_build_header(skb, tunnel_id_to_key32(key->tun_id), 597 + ntohl(md->u.index), truncate, true); 598 + } else if (version == 2) { 599 + u16 md2_flags; 600 + u8 direction; 601 + u16 hwid; 619 602 620 - erspan_build_header(skb, tunnel_id_to_key32(key->tun_id), 621 - ntohl(md->u.index), truncate, true); 603 + md2_flags = ntohs(md->u.md2.flags); 604 + direction = (md2_flags & DIR_MASK) >> DIR_OFFSET; 605 + hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET; 606 + 607 + erspan_build_header_v2(skb, tunnel_id_to_key32(key->tun_id), 608 + direction, hwid, truncate, true); 609 + } else { 610 + goto err_free_rt; 611 + } 622 612 623 613 gre_build_header(skb, 8, TUNNEL_SEQ, 624 614 htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++)); ··· 731 699 } 732 700 733 701 /* Push ERSPAN header */ 734 - erspan_build_header(skb, tunnel->parms.o_key, tunnel->index, 735 - truncate, true); 702 + if (tunnel->erspan_ver == 1) 703 + erspan_build_header(skb, tunnel->parms.o_key, tunnel->index, 704 + truncate, true); 705 + else 706 + erspan_build_header_v2(skb, tunnel->parms.o_key, 707 + tunnel->dir, tunnel->hwid, 708 + truncate, true); 709 + 736 710 tunnel->parms.o_flags &= ~TUNNEL_KEY; 737 711 __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_ERSPAN)); 738 712 return NETDEV_TX_OK; ··· 1210 1172 if (data[IFLA_GRE_FWMARK]) 1211 1173 *fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]); 1212 1174 1213 - if (data[IFLA_GRE_ERSPAN_INDEX]) { 1214 - t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); 1175 + if (data[IFLA_GRE_ERSPAN_VER]) { 1176 + t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); 1215 1177 1216 - if (t->index & ~INDEX_MASK) 1178 + if (t->erspan_ver != 1 && t->erspan_ver != 2) 1217 1179 return -EINVAL; 1180 + } 1181 + 1182 + if (t->erspan_ver == 1) { 1183 + if (data[IFLA_GRE_ERSPAN_INDEX]) { 1184 + t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); 1185 + if (t->index & ~INDEX_MASK) 1186 + return -EINVAL; 1187 + } 1188 + } else if (t->erspan_ver == 2) { 1189 + if (data[IFLA_GRE_ERSPAN_DIR]) { 1190 + t->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]); 1191 + if (t->dir & ~(DIR_MASK >> DIR_OFFSET)) 1192 + return -EINVAL; 1193 + } 1194 + if (data[IFLA_GRE_ERSPAN_HWID]) { 1195 + t->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]); 1196 + if (t->hwid & ~(HWID_MASK >> HWID_OFFSET)) 1197 + return -EINVAL; 1198 + } 1218 1199 } 1219 1200 1220 1201 return 0; ··· 1302 1245 tunnel->tun_hlen = 8; 1303 1246 tunnel->parms.iph.protocol = IPPROTO_GRE; 1304 1247 tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen + 1305 - sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE; 1248 + erspan_hdr_len(tunnel->erspan_ver); 1306 1249 t_hlen = tunnel->hlen + sizeof(struct iphdr); 1307 1250 1308 1251 dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4; ··· 1432 1375 nla_total_size(4) + 1433 1376 /* IFLA_GRE_ERSPAN_INDEX */ 1434 1377 nla_total_size(4) + 1378 + /* IFLA_GRE_ERSPAN_VER */ 1379 + nla_total_size(1) + 1380 + /* IFLA_GRE_ERSPAN_DIR */ 1381 + nla_total_size(1) + 1382 + /* IFLA_GRE_ERSPAN_HWID */ 1383 + nla_total_size(2) + 1435 1384 0; 1436 1385 } 1437 1386 ··· 1480 1417 goto nla_put_failure; 1481 1418 } 1482 1419 1483 - if (t->index) 1420 + if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver)) 1421 + goto nla_put_failure; 1422 + 1423 + if (t->erspan_ver == 1) { 1484 1424 if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index)) 1485 1425 goto nla_put_failure; 1426 + } else if (t->erspan_ver == 2) { 1427 + if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir)) 1428 + goto nla_put_failure; 1429 + if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid)) 1430 + goto nla_put_failure; 1431 + } 1486 1432 1487 1433 return 0; 1488 1434 ··· 1527 1455 [IFLA_GRE_IGNORE_DF] = { .type = NLA_U8 }, 1528 1456 [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, 1529 1457 [IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 }, 1458 + [IFLA_GRE_ERSPAN_VER] = { .type = NLA_U8 }, 1459 + [IFLA_GRE_ERSPAN_DIR] = { .type = NLA_U8 }, 1460 + [IFLA_GRE_ERSPAN_HWID] = { .type = NLA_U16 }, 1530 1461 }; 1531 1462 1532 1463 static struct rtnl_link_ops ipgre_link_ops __read_mostly = {