William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 1 | #ifndef __LINUX_ERSPAN_H |
| 2 | #define __LINUX_ERSPAN_H |
| 3 | |
| 4 | /* |
| 5 | * GRE header for ERSPAN encapsulation (8 octets [34:41]) -- 8 bytes |
| 6 | * 0 1 2 3 |
| 7 | * 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 |
| 8 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 9 | * |0|0|0|1|0|00000|000000000|00000| Protocol Type for ERSPAN | |
| 10 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 11 | * | Sequence Number (increments per packet per session) | |
| 12 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 13 | * |
| 14 | * Note that in the above GRE header [RFC1701] out of the C, R, K, S, |
| 15 | * s, Recur, Flags, Version fields only S (bit 03) is set to 1. The |
| 16 | * other fields are set to zero, so only a sequence number follows. |
| 17 | * |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 18 | * ERSPAN Version 1 (Type II) header (8 octets [42:49]) |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 19 | * 0 1 2 3 |
| 20 | * 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 |
| 21 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 22 | * | Ver | VLAN | COS | En|T| Session ID | |
| 23 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 24 | * | Reserved | Index | |
| 25 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 26 | * |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 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 | * |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 46 | * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB |
| 47 | */ |
| 48 | |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 49 | #define ERSPAN_VERSION 0x1 /* ERSPAN type II */ |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 50 | #define VER_MASK 0xf000 |
| 51 | #define VLAN_MASK 0x0fff |
| 52 | #define COS_MASK 0xe000 |
| 53 | #define EN_MASK 0x1800 |
| 54 | #define T_MASK 0x0400 |
| 55 | #define ID_MASK 0x03ff |
| 56 | #define INDEX_MASK 0xfffff |
| 57 | |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 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 | |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 80 | enum erspan_encap_type { |
| 81 | ERSPAN_ENCAP_NOVLAN = 0x0, /* originally without VLAN tag */ |
| 82 | ERSPAN_ENCAP_ISL = 0x1, /* originally ISL encapsulated */ |
| 83 | ERSPAN_ENCAP_8021Q = 0x2, /* originally 802.1Q encapsulated */ |
| 84 | ERSPAN_ENCAP_INFRAME = 0x3, /* VLAN tag perserved in frame */ |
| 85 | }; |
| 86 | |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 87 | #define ERSPAN_V1_MDSIZE 4 |
| 88 | #define ERSPAN_V2_MDSIZE 8 |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 89 | struct erspan_metadata { |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 90 | union { |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 91 | __be32 index; /* Version 1 (type II)*/ |
| 92 | struct erspan_md2 md2; /* Version 2 (type III) */ |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 93 | } u; |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 94 | int version; |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 95 | }; |
| 96 | |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 97 | struct erspan_base_hdr { |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 98 | __be16 ver_vlan; |
| 99 | #define VER_OFFSET 12 |
| 100 | __be16 session_id; |
| 101 | #define COS_OFFSET 13 |
| 102 | #define EN_OFFSET 11 |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 103 | #define BSO_OFFSET EN_OFFSET |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 104 | #define T_OFFSET 10 |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 105 | }; |
| 106 | |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 107 | static inline int erspan_hdr_len(int version) |
| 108 | { |
| 109 | return sizeof(struct erspan_base_hdr) + |
| 110 | (version == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE); |
| 111 | } |
| 112 | |
William Tu | a3222dc | 2017-11-30 11:51:27 -0800 | [diff] [blame] | 113 | static inline u8 tos_to_cos(u8 tos) |
| 114 | { |
| 115 | u8 dscp, cos; |
| 116 | |
| 117 | dscp = tos >> 2; |
| 118 | cos = dscp >> 3; |
| 119 | return cos; |
| 120 | } |
| 121 | |
| 122 | static inline void erspan_build_header(struct sk_buff *skb, |
| 123 | __be32 id, u32 index, |
| 124 | bool truncate, bool is_ipv4) |
| 125 | { |
William Tu | b423d13 | 2018-01-23 17:01:29 -0800 | [diff] [blame^] | 126 | struct ethhdr *eth = (struct ethhdr *)skb->data; |
William Tu | a3222dc | 2017-11-30 11:51:27 -0800 | [diff] [blame] | 127 | enum erspan_encap_type enc_type; |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 128 | struct erspan_base_hdr *ershdr; |
| 129 | struct erspan_metadata *ersmd; |
William Tu | a3222dc | 2017-11-30 11:51:27 -0800 | [diff] [blame] | 130 | struct qtag_prefix { |
| 131 | __be16 eth_type; |
| 132 | __be16 tci; |
| 133 | } *qp; |
| 134 | u16 vlan_tci = 0; |
| 135 | u8 tos; |
| 136 | |
| 137 | tos = is_ipv4 ? ip_hdr(skb)->tos : |
| 138 | (ipv6_hdr(skb)->priority << 4) + |
| 139 | (ipv6_hdr(skb)->flow_lbl[0] >> 4); |
| 140 | |
| 141 | enc_type = ERSPAN_ENCAP_NOVLAN; |
| 142 | |
| 143 | /* If mirrored packet has vlan tag, extract tci and |
| 144 | * perserve vlan header in the mirrored frame. |
| 145 | */ |
| 146 | if (eth->h_proto == htons(ETH_P_8021Q)) { |
| 147 | qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN); |
| 148 | vlan_tci = ntohs(qp->tci); |
| 149 | enc_type = ERSPAN_ENCAP_INFRAME; |
| 150 | } |
| 151 | |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 152 | skb_push(skb, sizeof(*ershdr) + ERSPAN_V1_MDSIZE); |
| 153 | ershdr = (struct erspan_base_hdr *)skb->data; |
| 154 | memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE); |
William Tu | a3222dc | 2017-11-30 11:51:27 -0800 | [diff] [blame] | 155 | |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 156 | /* Build base header */ |
William Tu | a3222dc | 2017-11-30 11:51:27 -0800 | [diff] [blame] | 157 | ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) | |
| 158 | (ERSPAN_VERSION << VER_OFFSET)); |
| 159 | ershdr->session_id = htons((u16)(ntohl(id) & ID_MASK) | |
| 160 | ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) | |
| 161 | (enc_type << EN_OFFSET & EN_MASK) | |
| 162 | ((truncate << T_OFFSET) & T_MASK)); |
William Tu | 1d7e2ed | 2017-12-13 16:38:55 -0800 | [diff] [blame] | 163 | |
| 164 | /* Build metadata */ |
| 165 | ersmd = (struct erspan_metadata *)(ershdr + 1); |
| 166 | ersmd->u.index = htonl(index & INDEX_MASK); |
William Tu | a3222dc | 2017-11-30 11:51:27 -0800 | [diff] [blame] | 167 | } |
| 168 | |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 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 | { |
William Tu | b423d13 | 2018-01-23 17:01:29 -0800 | [diff] [blame^] | 193 | struct ethhdr *eth = (struct ethhdr *)skb->data; |
William Tu | f551c91 | 2017-12-13 16:38:56 -0800 | [diff] [blame] | 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 | |
William Tu | 84e54fe | 2017-08-22 09:40:28 -0700 | [diff] [blame] | 242 | #endif |