| 1 | #ifndef __LINUX_ERSPAN_H
|
| 2 | #define __LINUX_ERSPAN_H
|
| 3 |
|
| 4 | |
| 5 | * GRE header for ERSPAN type I encapsulation (4 octets [34:37])
|
| 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|0|0|00000|000000000|00000| Protocol Type for ERSPAN |
|
| 10 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 11 | *
|
| 12 | * The Type I ERSPAN frame format is based on the barebones IP + GRE
|
| 13 | * encapsulation (as described above) on top of the raw mirrored frame.
|
| 14 | * There is no extra ERSPAN header.
|
| 15 | *
|
| 16 | *
|
| 17 | * GRE header for ERSPAN type II and II encapsulation (8 octets [34:41])
|
| 18 | * 0 1 2 3
|
| 19 | * 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
|
| 20 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 21 | * |0|0|0|1|0|00000|000000000|00000| Protocol Type for ERSPAN |
|
| 22 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 23 | * | Sequence Number (increments per packet per session) |
|
| 24 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 25 | *
|
| 26 | * Note that in the above GRE header [RFC1701] out of the C, R, K, S,
|
| 27 | * s, Recur, Flags, Version fields only S (bit 03) is set to 1. The
|
| 28 | * other fields are set to zero, so only a sequence number follows.
|
| 29 | *
|
| 30 | * ERSPAN Version 1 (Type II) header (8 octets [42:49])
|
| 31 | * 0 1 2 3
|
| 32 | * 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
|
| 33 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 34 | * | Ver | VLAN | COS | En|T| Session ID |
|
| 35 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 36 | * | Reserved | Index |
|
| 37 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 38 | *
|
| 39 | *
|
| 40 | * ERSPAN Version 2 (Type III) header (12 octets [42:49])
|
| 41 | * 0 1 2 3
|
| 42 | * 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
|
| 43 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 44 | * | Ver | VLAN | COS |BSO|T| Session ID |
|
| 45 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 46 | * | Timestamp |
|
| 47 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 48 | * | SGT |P| FT | Hw ID |D|Gra|O|
|
| 49 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 50 | *
|
| 51 | * Platform Specific SubHeader (8 octets, optional)
|
| 52 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 53 | * | Platf ID | Platform Specific Info |
|
| 54 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 55 | * | Platform Specific Info |
|
| 56 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| 57 | *
|
| 58 | * GRE proto ERSPAN type I/II = 0x88BE, type III = 0x22EB
|
| 59 |
|
| 60 |
|
| 61 | #include <linux/ip.h>
|
| 62 | #include <linux/ipv6.h>
|
| 63 | #include <linux/skbuff.h>
|
| 64 | #include <uapi/linux/erspan.h>
|
| 65 |
|
| 66 | #define ERSPAN_VERSION 0x1
|
| 67 | #define VER_MASK 0xf000
|
| 68 | #define VLAN_MASK 0x0fff
|
| 69 | #define COS_MASK 0xe000
|
| 70 | #define EN_MASK 0x1800
|
| 71 | #define T_MASK 0x0400
|
| 72 | #define ID_MASK 0x03ff
|
| 73 | #define INDEX_MASK 0xfffff
|
| 74 |
|
| 75 | #define ERSPAN_VERSION2 0x2
|
| 76 | #define BSO_MASK EN_MASK
|
| 77 | #define SGT_MASK 0xffff0000
|
| 78 | #define P_MASK 0x8000
|
| 79 | #define FT_MASK 0x7c00
|
| 80 | #define HWID_MASK 0x03f0
|
| 81 | #define DIR_MASK 0x0008
|
| 82 | #define GRA_MASK 0x0006
|
| 83 | #define O_MASK 0x0001
|
| 84 |
|
| 85 | #define HWID_OFFSET 4
|
| 86 | #define DIR_OFFSET 3
|
| 87 |
|
| 88 | enum erspan_encap_type {
|
| 89 | ERSPAN_ENCAP_NOVLAN = 0x0,
|
| 90 | ERSPAN_ENCAP_ISL = 0x1,
|
| 91 | ERSPAN_ENCAP_8021Q = 0x2,
|
| 92 | ERSPAN_ENCAP_INFRAME = 0x3,
|
| 93 | };
|
| 94 |
|
| 95 | #define ERSPAN_V1_MDSIZE 4
|
| 96 | #define ERSPAN_V2_MDSIZE 8
|
| 97 |
|
| 98 | struct erspan_base_hdr {
|
| 99 | #if defined(__LITTLE_ENDIAN_BITFIELD)
|
| 100 | __u8 vlan_upper:4,
|
| 101 | ver:4;
|
| 102 | __u8 vlan:8;
|
| 103 | __u8 session_id_upper:2,
|
| 104 | t:1,
|
| 105 | en:2,
|
| 106 | cos:3;
|
| 107 | __u8 session_id:8;
|
| 108 | #elif defined(__BIG_ENDIAN_BITFIELD)
|
| 109 | __u8 ver: 4,
|
| 110 | vlan_upper:4;
|
| 111 | __u8 vlan:8;
|
| 112 | __u8 cos:3,
|
| 113 | en:2,
|
| 114 | t:1,
|
| 115 | session_id_upper:2;
|
| 116 | __u8 session_id:8;
|
| 117 | #else
|
| 118 | #error "Please fix <asm/byteorder.h>"
|
| 119 | #endif
|
| 120 | };
|
| 121 |
|
| 122 | static inline void set_session_id(struct erspan_base_hdr *ershdr, u16 id)
|
| 123 | {
|
| 124 | ershdr->session_id = id & 0xff;
|
| 125 | ershdr->session_id_upper = (id >> 8) & 0x3;
|
| 126 | }
|
| 127 |
|
| 128 | static inline u16 get_session_id(const struct erspan_base_hdr *ershdr)
|
| 129 | {
|
| 130 | return (ershdr->session_id_upper << 8) + ershdr->session_id;
|
| 131 | }
|
| 132 |
|
| 133 | static inline void set_vlan(struct erspan_base_hdr *ershdr, u16 vlan)
|
| 134 | {
|
| 135 | ershdr->vlan = vlan & 0xff;
|
| 136 | ershdr->vlan_upper = (vlan >> 8) & 0xf;
|
| 137 | }
|
| 138 |
|
| 139 | static inline u16 get_vlan(const struct erspan_base_hdr *ershdr)
|
| 140 | {
|
| 141 | return (ershdr->vlan_upper << 8) + ershdr->vlan;
|
| 142 | }
|
| 143 |
|
| 144 | static inline void set_hwid(struct erspan_md2 *md2, u8 hwid)
|
| 145 | {
|
| 146 | md2->hwid = hwid & 0xf;
|
| 147 | md2->hwid_upper = (hwid >> 4) & 0x3;
|
| 148 | }
|
| 149 |
|
| 150 | static inline u8 get_hwid(const struct erspan_md2 *md2)
|
| 151 | {
|
| 152 | return (md2->hwid_upper << 4) + md2->hwid;
|
| 153 | }
|
| 154 |
|
| 155 | static inline int erspan_hdr_len(int version)
|
| 156 | {
|
| 157 | if (version == 0)
|
| 158 | return 0;
|
| 159 |
|
| 160 | return sizeof(struct erspan_base_hdr) +
|
| 161 | (version == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE);
|
| 162 | }
|
| 163 |
|
| 164 | static inline u8 tos_to_cos(u8 tos)
|
| 165 | {
|
| 166 | u8 dscp, cos;
|
| 167 |
|
| 168 | dscp = tos >> 2;
|
| 169 | cos = dscp >> 3;
|
| 170 | return cos;
|
| 171 | }
|
| 172 |
|
| 173 | static inline void erspan_build_header(struct sk_buff *skb,
|
| 174 | u32 id, u32 index,
|
| 175 | bool truncate, bool is_ipv4)
|
| 176 | {
|
| 177 | struct ethhdr *eth = (struct ethhdr *)skb->data;
|
| 178 | enum erspan_encap_type enc_type;
|
| 179 | struct erspan_base_hdr *ershdr;
|
| 180 | struct qtag_prefix {
|
| 181 | __be16 eth_type;
|
| 182 | __be16 tci;
|
| 183 | } *qp;
|
| 184 | u16 vlan_tci = 0;
|
| 185 | u8 tos;
|
| 186 | __be32 *idx;
|
| 187 |
|
| 188 | tos = is_ipv4 ? ip_hdr(skb)->tos :
|
| 189 | (ipv6_hdr(skb)->priority << 4) +
|
| 190 | (ipv6_hdr(skb)->flow_lbl[0] >> 4);
|
| 191 |
|
| 192 | enc_type = ERSPAN_ENCAP_NOVLAN;
|
| 193 |
|
| 194 | |
| 195 | * preserve vlan header in the mirrored frame.
|
| 196 |
|
| 197 | if (eth->h_proto == htons(ETH_P_8021Q)) {
|
| 198 | qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN);
|
| 199 | vlan_tci = ntohs(qp->tci);
|
| 200 | enc_type = ERSPAN_ENCAP_INFRAME;
|
| 201 | }
|
| 202 |
|
| 203 | skb_push(skb, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
|
| 204 | ershdr = (struct erspan_base_hdr *)skb->data;
|
| 205 | memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
|
| 206 |
|
| 207 |
|
| 208 | ershdr->ver = ERSPAN_VERSION;
|
| 209 | ershdr->cos = tos_to_cos(tos);
|
| 210 | ershdr->en = enc_type;
|
| 211 | ershdr->t = truncate;
|
| 212 | set_vlan(ershdr, vlan_tci);
|
| 213 | set_session_id(ershdr, id);
|
| 214 |
|
| 215 |
|
| 216 | idx = (__be32 *)(ershdr + 1);
|
| 217 | *idx = htonl(index & INDEX_MASK);
|
| 218 | }
|
| 219 |
|
| 220 | |
| 221 | * 00b --> granularity = 100 microseconds
|
| 222 | * 01b --> granularity = 100 nanoseconds
|
| 223 | * 10b --> granularity = IEEE 1588
|
| 224 | * Here we only support 100 microseconds.
|
| 225 |
|
| 226 | static inline __be32 erspan_get_timestamp(void)
|
| 227 | {
|
| 228 | u64 h_usecs;
|
| 229 | ktime_t kt;
|
| 230 |
|
| 231 | kt = ktime_get_real();
|
| 232 | h_usecs = ktime_divns(kt, 100 * NSEC_PER_USEC);
|
| 233 |
|
| 234 | |
| 235 | * so it wraps around 4 days.
|
| 236 |
|
| 237 | return htonl((u32)h_usecs);
|
| 238 | }
|
| 239 |
|
| 240 | |
| 241 | * 00b --> Good frame with no error, or unknown integrity
|
| 242 | * 01b --> Payload is a Short Frame
|
| 243 | * 10b --> Payload is an Oversized Frame
|
| 244 | * 11b --> Payload is a Bad Frame with CRC or Alignment Error
|
| 245 |
|
| 246 | enum erspan_bso {
|
| 247 | BSO_NOERROR = 0x0,
|
| 248 | BSO_SHORT = 0x1,
|
| 249 | BSO_OVERSIZED = 0x2,
|
| 250 | BSO_BAD = 0x3,
|
| 251 | };
|
| 252 |
|
| 253 | static inline u8 erspan_detect_bso(struct sk_buff *skb)
|
| 254 | {
|
| 255 | |
| 256 | * or alignment error information is in FCS.
|
| 257 |
|
| 258 | if (skb->len < ETH_ZLEN)
|
| 259 | return BSO_SHORT;
|
| 260 |
|
| 261 | if (skb->len > ETH_FRAME_LEN)
|
| 262 | return BSO_OVERSIZED;
|
| 263 |
|
| 264 | return BSO_NOERROR;
|
| 265 | }
|
| 266 |
|
| 267 | static inline void erspan_build_header_v2(struct sk_buff *skb,
|
| 268 | u32 id, u8 direction, u16 hwid,
|
| 269 | bool truncate, bool is_ipv4)
|
| 270 | {
|
| 271 | struct ethhdr *eth = (struct ethhdr *)skb->data;
|
| 272 | struct erspan_base_hdr *ershdr;
|
| 273 | struct erspan_md2 *md2;
|
| 274 | struct qtag_prefix {
|
| 275 | __be16 eth_type;
|
| 276 | __be16 tci;
|
| 277 | } *qp;
|
| 278 | u16 vlan_tci = 0;
|
| 279 | u8 gra = 0;
|
| 280 | u8 bso = 0;
|
| 281 | u8 sgt = 0;
|
| 282 | u8 tos;
|
| 283 |
|
| 284 | tos = is_ipv4 ? ip_hdr(skb)->tos :
|
| 285 | (ipv6_hdr(skb)->priority << 4) +
|
| 286 | (ipv6_hdr(skb)->flow_lbl[0] >> 4);
|
| 287 |
|
| 288 | |
| 289 | * so only extract vlan tci field.
|
| 290 |
|
| 291 | if (eth->h_proto == htons(ETH_P_8021Q)) {
|
| 292 | qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN);
|
| 293 | vlan_tci = ntohs(qp->tci);
|
| 294 | }
|
| 295 |
|
| 296 | bso = erspan_detect_bso(skb);
|
| 297 | skb_push(skb, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
|
| 298 | ershdr = (struct erspan_base_hdr *)skb->data;
|
| 299 | memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
|
| 300 |
|
| 301 |
|
| 302 | ershdr->ver = ERSPAN_VERSION2;
|
| 303 | ershdr->cos = tos_to_cos(tos);
|
| 304 | ershdr->en = bso;
|
| 305 | ershdr->t = truncate;
|
| 306 | set_vlan(ershdr, vlan_tci);
|
| 307 | set_session_id(ershdr, id);
|
| 308 |
|
| 309 |
|
| 310 | md2 = (struct erspan_md2 *)(ershdr + 1);
|
| 311 | md2->timestamp = erspan_get_timestamp();
|
| 312 | md2->sgt = htons(sgt);
|
| 313 | md2->p = 1;
|
| 314 | md2->ft = 0;
|
| 315 | md2->dir = direction;
|
| 316 | md2->gra = gra;
|
| 317 | md2->o = 0;
|
| 318 | set_hwid(md2, hwid);
|
| 319 | }
|
| 320 |
|
| 321 | #endif
|