summaryrefslogtreecommitdiff
path: root/ovs_build/patches/0001-ovs-vxlan-gpe-vxlan-extension-to-support-vxlan-gpe-t.patch
blob: c172320c746a423679bc013978ef091a56aa7158 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
From 5d79831435ec4e5bea20cc36c3f83eacf6fd065c Mon Sep 17 00:00:00 2001
From: Yi Yang <yi.y.yang@intel.com>
Date: Mon, 11 Apr 2016 15:58:14 +0800
Subject: [PATCH 1/6] ovs-vxlan-gpe: vxlan extension to support vxlan-gpe
 tunnel port

Signed-off-by: Mengke Liu <mengke.liu@intel.com>
Signed-off-by: Ricky Li <ricky.li@intel.com>
Signed-off-by: Johnson Li <johnson.li@intel.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
---
 datapath/flow_netlink.c                           |  8 +-
 datapath/linux/compat/include/linux/openvswitch.h |  1 +
 datapath/linux/compat/include/net/vxlan.h         | 73 +++++++++++++++++++
 datapath/linux/compat/vxlan.c                     | 30 ++++++++
 datapath/vport-vxlan.c                            | 15 ++++
 lib/flow.c                                        |  8 ++
 lib/match.c                                       | 34 +++++++++
 lib/match.h                                       |  4 +
 lib/meta-flow.c                                   | 36 +++++++++
 lib/meta-flow.h                                   | 28 +++++++
 lib/netdev-vport.c                                |  2 +
 lib/nx-match.c                                    |  4 +
 lib/odp-util.c                                    | 89 ++++++++++++++++++++++-
 lib/packets.h                                     |  4 +-
 tests/ofproto.at                                  |  4 +-
 tests/ovs-ofctl.at                                |  4 +
 16 files changed, 340 insertions(+), 4 deletions(-)

diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 6ffcc53..351a504 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -309,6 +309,7 @@ size_t ovs_key_attr_size(void)
 
 static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = {
 	[OVS_VXLAN_EXT_GBP]	    = { .len = sizeof(u32) },
+	[OVS_VXLAN_EXT_GPE]	    = { .len = sizeof(u32) },
 };
 
 static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
@@ -521,6 +522,9 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
 		case OVS_VXLAN_EXT_GBP:
 			opts.gbp = nla_get_u32(a);
 			break;
+		case OVS_VXLAN_EXT_GPE:
+			opts.gpe = nla_get_u32(a);
+			break;
 		default:
 			OVS_NLERR(log, "Unknown VXLAN extension attribute %d",
 				  type);
@@ -677,7 +681,9 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb,
 	if (!nla)
 		return -EMSGSIZE;
 
-	if (nla_put_u32(skb, OVS_VXLAN_EXT_GBP, opts->gbp) < 0)
+	if (opts->gbp && nla_put_u32(skb, OVS_VXLAN_EXT_GBP, opts->gbp) < 0)
+		return -EMSGSIZE;
+	else if (opts->gpe && nla_put_u32(skb, OVS_VXLAN_EXT_GPE, opts->gpe) < 0)
 		return -EMSGSIZE;
 
 	nla_nest_end(skb, nla);
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 3b39ebb..44adb81 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -287,6 +287,7 @@ enum ovs_vport_attr {
 enum {
 	OVS_VXLAN_EXT_UNSPEC,
 	OVS_VXLAN_EXT_GBP,      /* Flag or __u32 */
+	OVS_VXLAN_EXT_GPE,
 	__OVS_VXLAN_EXT_MAX,
 };
 
diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
index 75a5a7a..2bfc3f8 100644
--- a/datapath/linux/compat/include/net/vxlan.h
+++ b/datapath/linux/compat/include/net/vxlan.h
@@ -84,6 +84,75 @@ struct vxlanhdr_gbp {
 #define VXLAN_GBP_POLICY_APPLIED	(BIT(3) << 16)
 #define VXLAN_GBP_ID_MASK		(0xFFFF)
 
+/*
+ * VXLAN Generic Protocol Extension Extension:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|  Next Proto   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                VXLAN Network Identifier (VNI) |   Reserved    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Ver = Version. Indicates VXLAN GPE protocol version.  The initial
+ *      version is 0.  If a receiver does not support the version
+ *      indicated it MUST drop the packet.
+ *
+ * I = Instance Bit. The I bit MUST be set to indicate a valid VNI.
+ *
+ * P = Next Protocol Bit. The P bit is set to indicate that the
+ *     Next Protocol field is present.
+ *
+ * O = OAM Flag Bit. The O bit is set to indicate that the packet
+ *     is an OAM packet.
+ *
+ * [1] https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-01.txt
+ */
+
+struct vxlanhdr_gpe {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+       uint8_t    oam_flag:1;
+       uint8_t    reserved_flags1:1;
+       uint8_t    np_applied:1;
+       uint8_t    instance_applied:1;
+       uint8_t    gpe_version:2;
+       uint8_t    reserved_flags2:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+       uint8_t    reserved_flags2:2;
+       uint8_t    gpe_version:2;
+       uint8_t    instance_applied:1;
+       uint8_t    np_applied:1;
+       uint8_t    reserved_flags1:1;
+       uint8_t    oam_flag:1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+    uint8_t    reserved_flags3;
+    uint8_t    reserved_flags4;
+    uint8_t    next_proto;
+    __be32        vx_vni;
+};
+
+/* VxLAN-GPE Header Next Protocol */
+#define VXLAN_GPE_NP_IPV4        0x01
+#define VXLAN_GPE_NP_IPV6        0x02
+#define VXLAN_GPE_NP_ETHERNET    0x03
+#define VXLAN_GPE_NP_NSH        0x04
+
+/* skb->mark mapping
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|  Next Proto   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define VXLAN_GPE_OAM_FLAG                     (BIT(0) << 24)
+#define VXLAN_GPE_NP_APPLIED           (BIT(0) << 26)
+#define VXLAN_GPE_INSTANCE_APPLIED     (BIT(0) << 27)
+#define VXLAN_GPE_VERSION                      ((BIT(0) << 28) | (BIT(0) << 29))
+
+#define VXLAN_GPE_NP_MASK                      (0xFF)
+
+#define VXLAN_GPE_USED_BITS (VXLAN_GPE_OAM_FLAG | VXLAN_GPE_NP_APPLIED \
+                           | VXLAN_GPE_INSTANCE_APPLIED | VXLAN_GPE_VERSION | 0xFF)
+
 /* VXLAN protocol header:
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  * |G|R|R|R|I|R|R|C|               Reserved                        |
@@ -104,6 +173,7 @@ struct vxlanhdr {
 #define VXLAN_HF_RCO BIT(21)
 #define VXLAN_HF_VNI BIT(27)
 #define VXLAN_HF_GBP BIT(31)
+#define VXLAN_HF_GPE BIT(26)
 
 /* Remote checksum offload header option */
 #define VXLAN_RCO_MASK  0x7f    /* Last byte of vni field */
@@ -120,6 +190,7 @@ struct vxlanhdr {
 struct vxlan_metadata {
 	__be32          vni;
 	u32             gbp;
+	u32             gpe;
 };
 
 #define VNI_HASH_BITS	10
@@ -205,11 +276,13 @@ struct vxlan_dev {
 #define VXLAN_F_GBP			0x800
 #define VXLAN_F_REMCSUM_NOPARTIAL	0x1000
 #define VXLAN_F_COLLECT_METADATA	0x2000
+#define VXLAN_F_GPE                     0x4000
 
 /* Flags that are used in the receive path. These flags must match in
  * order for a socket to be shareable
  */
 #define VXLAN_F_RCV_FLAGS		(VXLAN_F_GBP |			\
+                                         VXLAN_F_GPE |                  \
 					 VXLAN_F_UDP_ZERO_CSUM6_RX |	\
 					 VXLAN_F_REMCSUM_RX |		\
 					 VXLAN_F_REMCSUM_NOPARTIAL |	\
diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
index 4faa18f..7ef051c 100644
--- a/datapath/linux/compat/vxlan.c
+++ b/datapath/linux/compat/vxlan.c
@@ -971,6 +971,18 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 			md->gbp |= VXLAN_GBP_POLICY_APPLIED;
 
 		flags &= ~VXLAN_GBP_USED_BITS;
+	} else if ((flags & VXLAN_HF_GPE) && (vs->flags & VXLAN_F_GPE)) {
+		struct vxlanhdr_gpe *gpe;
+
+		gpe = (struct vxlanhdr_gpe *)vxh;
+		md->gpe = ntohs(gpe->next_proto);
+
+		buf.dst.u.tun_info.key.tun_flags |= TUNNEL_VXLAN_OPT;
+
+		if (gpe->oam_flag)
+			md->gpe |= VXLAN_GPE_OAM_FLAG;
+
+		flags &= ~VXLAN_GPE_USED_BITS;
 	}
 
 	if (flags || vni & ~VXLAN_VNI_MASK) {
@@ -1023,6 +1035,22 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags,
 	gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK);
 }
 
+static void vxlan_build_gpe_hdr(struct vxlanhdr *vxh, u32 vxflags,
+                                struct vxlan_metadata *md)
+{
+       struct vxlanhdr_gpe *gpe;
+
+       if (!md->gpe)
+               return;
+
+       gpe = (struct vxlanhdr_gpe*)vxh;
+       vxh->vx_flags |= htonl(VXLAN_HF_GPE);
+
+       if (md->gpe & VXLAN_GPE_OAM_FLAG)
+               gpe->oam_flag = 1;
+       gpe->next_proto = md->gpe & VXLAN_GPE_NP_MASK;
+}
+
 #if IS_ENABLED(CONFIG_IPV6)
 static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,
 			   struct sk_buff *skb,
@@ -1106,6 +1134,8 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,
 
 	if (vxflags & VXLAN_F_GBP)
 		vxlan_build_gbp_hdr(vxh, vxflags, md);
+	else if (vxflags & VXLAN_F_GPE)
+		vxlan_build_gpe_hdr(vxh, vxflags, md);
 
 	ovs_skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index c05f5d4..5d775cc 100644
--- a/datapath/vport-vxlan.c
+++ b/datapath/vport-vxlan.c
@@ -52,6 +52,18 @@ static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
 			return -EMSGSIZE;
 
 		nla_nest_end(skb, exts);
+	} else if (vxlan->flags & VXLAN_F_GPE) {
+		struct nlattr *exts;
+
+		exts = nla_nest_start(skb, OVS_TUNNEL_ATTR_EXTENSION);
+		if (!exts)
+			return -EMSGSIZE;
+
+		if (vxlan->flags & VXLAN_F_GPE &&
+		    nla_put_flag(skb, OVS_VXLAN_EXT_GPE))
+			return -EMSGSIZE;
+
+		nla_nest_end(skb, exts);
 	}
 
 	return 0;
@@ -59,6 +71,7 @@ static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
 
 static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = {
 	[OVS_VXLAN_EXT_GBP]	= { .type = NLA_FLAG, },
+	[OVS_VXLAN_EXT_GPE]	= { .type = NLA_FLAG, },
 };
 
 static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr,
@@ -76,6 +89,8 @@ static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr,
 
 	if (exts[OVS_VXLAN_EXT_GBP])
 		conf->flags |= VXLAN_F_GBP;
+        else if (exts[OVS_VXLAN_EXT_GPE])
+                conf->flags |= VXLAN_F_GPE;
 
 	return 0;
 }
diff --git a/lib/flow.c b/lib/flow.c
index b9ce331..d24bdc9 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -870,6 +870,12 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
     if (flow->tunnel.gbp_flags) {
         match_set_tun_gbp_flags(flow_metadata, flow->tunnel.gbp_flags);
     }
+    if (flow->tunnel.gpe_np != htons(0)) {
+        match_set_tun_gpe_np(flow_metadata, flow->tunnel.gpe_np);
+    }
+    if (flow->tunnel.gpe_flags) {
+        match_set_tun_gpe_flags(flow_metadata, flow->tunnel.gpe_flags);
+    }
     tun_metadata_get_fmd(&flow->tunnel, flow_metadata);
     if (flow->metadata != htonll(0)) {
         match_set_metadata(flow_metadata, flow->metadata);
@@ -1265,6 +1271,8 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
         WC_MASK_FIELD(wc, tunnel.tp_dst);
         WC_MASK_FIELD(wc, tunnel.gbp_id);
         WC_MASK_FIELD(wc, tunnel.gbp_flags);
+        WC_MASK_FIELD(wc, tunnel.gpe_np);
+        WC_MASK_FIELD(wc, tunnel.gpe_flags);
 
         if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
             if (flow->tunnel.metadata.present.map) {
diff --git a/lib/match.c b/lib/match.c
index fd571d9..52437c9 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -289,6 +289,32 @@ match_set_tun_gbp_flags(struct match *match, uint8_t flags)
 }
 
 void
+match_set_tun_gpe_np_masked(struct match *match, uint8_t np, uint8_t mask)
+{
+    match->wc.masks.tunnel.gpe_np = mask;
+    match->flow.tunnel.gpe_np = np & mask;
+}
+
+void
+match_set_tun_gpe_np(struct match *match, uint8_t np)
+{
+    match_set_tun_gpe_np_masked(match, np, UINT8_MAX);
+}
+
+void
+match_set_tun_gpe_flags_masked(struct match *match, uint8_t flags, uint8_t mask)
+{
+    match->wc.masks.tunnel.gpe_flags = mask;
+    match->flow.tunnel.gpe_flags = flags & mask;
+}
+
+void
+match_set_tun_gpe_flags(struct match *match, uint8_t flags)
+{
+    match_set_tun_gpe_flags_masked(match, flags, UINT8_MAX);
+}
+
+void
 match_set_in_port(struct match *match, ofp_port_t ofp_port)
 {
     match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
@@ -1013,6 +1039,14 @@ format_flow_tunnel(struct ds *s, const struct match *match)
         ds_put_format(s, "tun_gbp_flags=%#"PRIx8",", tnl->gbp_flags);
     }
 
+    if (wc->masks.tunnel.gpe_np) {
+        ds_put_format(s, "tun_gpe_np=%#"PRIx8",", tnl->gpe_np);
+    }
+
+    if (wc->masks.tunnel.gpe_flags) {
+        ds_put_format(s, "tun_gpe_flags=%#"PRIx8",", tnl->gpe_flags);
+    }
+
     if (wc->masks.tunnel.ip_tos) {
         ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
     }
diff --git a/lib/match.h b/lib/match.h
index 0a6ac29..48aa0b1 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -86,6 +86,10 @@ void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16
 void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id);
 void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask);
 void match_set_tun_gbp_flags(struct match *match, uint8_t flags);
+void match_set_tun_gpe_np_masked(struct match *match, uint8_t gpe_np, uint8_t mask);
+void match_set_tun_gpe_np(struct match *match, uint8_t gpe_np);
+void match_set_tun_gpe_flags_masked(struct match *match, uint8_t flags, uint8_t mask);
+void match_set_tun_gpe_flags(struct match *match, uint8_t flags);
 void match_set_in_port(struct match *, ofp_port_t ofp_port);
 void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
 void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 721152c..ab77fca 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -213,6 +213,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
         return !wc->masks.tunnel.gbp_id;
     case MFF_TUN_GBP_FLAGS:
         return !wc->masks.tunnel.gbp_flags;
+    case MFF_TUN_GPE_NP:
+        return !wc->masks.tunnel.gpe_np;
+    case MFF_TUN_GPE_FLAGS:
+        return !wc->masks.tunnel.gpe_flags;
     CASE_MFF_TUN_METADATA:
         return !ULLONG_GET(wc->masks.tunnel.metadata.present.map,
                            mf->id - MFF_TUN_METADATA0);
@@ -515,6 +519,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_TUN_TTL:
     case MFF_TUN_GBP_ID:
     case MFF_TUN_GBP_FLAGS:
+    case MFF_TUN_GPE_NP:
+    case MFF_TUN_GPE_FLAGS:
     CASE_MFF_TUN_METADATA:
     case MFF_METADATA:
     case MFF_IN_PORT:
@@ -648,6 +654,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_TUN_GBP_FLAGS:
         value->u8 = flow->tunnel.gbp_flags;
         break;
+    case MFF_TUN_GPE_NP:
+        value->u8 = flow->tunnel.gpe_np;
+        break;
+    case MFF_TUN_GPE_FLAGS:
+        value->u8 = flow->tunnel.gpe_flags;
+        break;
     case MFF_TUN_TTL:
         value->u8 = flow->tunnel.ip_ttl;
         break;
@@ -899,6 +911,12 @@ mf_set_value(const struct mf_field *mf,
     case MFF_TUN_GBP_FLAGS:
          match_set_tun_gbp_flags(match, value->u8);
          break;
+    case MFF_TUN_GPE_NP:
+         match_set_tun_gpe_np(match, value->u8);
+         break;
+    case MFF_TUN_GPE_FLAGS:
+         match_set_tun_gpe_flags(match, value->u8);
+         break;
     case MFF_TUN_TOS:
         match_set_tun_tos(match, value->u8);
         break;
@@ -1216,6 +1234,12 @@ mf_set_flow_value(const struct mf_field *mf,
     case MFF_TUN_GBP_FLAGS:
         flow->tunnel.gbp_flags = value->u8;
         break;
+    case MFF_TUN_GPE_NP:
+        flow->tunnel.gpe_np= value->u8;
+        break;
+    case MFF_TUN_GPE_FLAGS:
+        flow->tunnel.gpe_flags= value->u8;
+        break;
     case MFF_TUN_TOS:
         flow->tunnel.ip_tos = value->u8;
         break;
@@ -1535,6 +1559,12 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
     case MFF_TUN_GBP_FLAGS:
         match_set_tun_gbp_flags_masked(match, 0, 0);
         break;
+    case MFF_TUN_GPE_NP:
+        match_set_tun_gpe_np_masked(match, 0, 0);
+        break;
+    case MFF_TUN_GPE_FLAGS:
+        match_set_tun_gpe_flags_masked(match, 0, 0);
+        break;
     case MFF_TUN_TOS:
         match_set_tun_tos_masked(match, 0, 0);
         break;
@@ -1838,6 +1868,12 @@ mf_set(const struct mf_field *mf,
     case MFF_TUN_GBP_FLAGS:
         match_set_tun_gbp_flags_masked(match, value->u8, mask->u8);
         break;
+    case MFF_TUN_GPE_NP:
+        match_set_tun_gpe_np_masked(match, value->u8, mask->u8);
+        break;
+    case MFF_TUN_GPE_FLAGS:
+        match_set_tun_gpe_flags_masked(match, value->u8, mask->u8);
+        break;
     case MFF_TUN_TTL:
         match_set_tun_ttl_masked(match, value->u8, mask->u8);
         break;
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index c73a1af..4bd9ff6 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -491,6 +491,34 @@ enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_TUN_GBP_FLAGS,
 
+     /* "tun_gpe_np".
+     *
+     * VXLAN Generic Protocol Extension next_proto
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_TUN_GPE_NP(111) since v2.4.
+     * OXM: none.
+     */
+    MFF_TUN_GPE_NP,
+
+     /* "tun_gpe_flags".
+     *
+     * VXLAN Generic Protocol Extension flag
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_TUN_GPE_FLAGS(112) since v2.4.
+     * OXM: none.
+     */
+    MFF_TUN_GPE_FLAGS,
+
 #if TUN_METADATA_NUM_OPTS == 64
     /* "tun_metadata<N>".
      *
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index e398562..92ceec1 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -583,6 +583,8 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
             while (ext) {
                 if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) {
                     tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
+                } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) {
+                    tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
                 } else {
                     VLOG_WARN("%s: unknown extension '%s'", name, ext);
                 }
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 9f0f452..0eecac7 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1037,6 +1037,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
                 flow->tunnel.gbp_id, match->wc.masks.tunnel.gbp_id);
     nxm_put_8m(b, MFF_TUN_GBP_FLAGS, oxm,
                flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags);
+    nxm_put_8m(b, MFF_TUN_GPE_NP, oxm,
+               flow->tunnel.gpe_np, match->wc.masks.tunnel.gpe_np);
+    nxm_put_8m(b, MFF_TUN_GPE_FLAGS, oxm,
+               flow->tunnel.gpe_flags, match->wc.masks.tunnel.gpe_flags);
     tun_metadata_to_nx_match(b, oxm, match);
 
     /* Registers. */
diff --git a/lib/odp-util.c b/lib/odp-util.c
index b4689cc..7983720 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1727,6 +1727,7 @@ odp_actions_from_string(const char *s, const struct simap *port_names,
 
 static const struct attr_len_tbl ovs_vxlan_ext_attr_lens[OVS_VXLAN_EXT_MAX + 1] = {
     [OVS_VXLAN_EXT_GBP]                 = { .len = 4 },
+    [OVS_VXLAN_EXT_GPE]                 = { .len = 4 },
 };
 
 static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
@@ -1888,7 +1889,10 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
             break;
         case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: {
             static const struct nl_policy vxlan_opts_policy[] = {
-                [OVS_VXLAN_EXT_GBP] = { .type = NL_A_U32 },
+                [OVS_VXLAN_EXT_GBP] = { .type = NL_A_U32 ,
+                                        .optional = true },
+                [OVS_VXLAN_EXT_GPE] = { .type = NL_A_U32 ,
+                                        .optional = true },
             };
             struct nlattr *ext[ARRAY_SIZE(vxlan_opts_policy)];
 
@@ -1902,6 +1906,12 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
                 tun->gbp_id = htons(gbp & 0xFFFF);
                 tun->gbp_flags = (gbp >> 16) & 0xFF;
             }
+            if (ext[OVS_VXLAN_EXT_GPE]) {
+                uint32_t gpe = nl_attr_get_u32(ext[OVS_VXLAN_EXT_GPE]);
+
+                tun->gpe_np = gpe & 0xFF;
+                tun->gpe_flags = gpe >> 24;
+            }
 
             break;
         }
@@ -1988,6 +1998,13 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
         nl_msg_put_u32(a, OVS_VXLAN_EXT_GBP,
                        (tun_key->gbp_flags << 16) | ntohs(tun_key->gbp_id));
         nl_msg_end_nested(a, vxlan_opts_ofs);
+    } else if (tun_key->gpe_flags || tun_key->gpe_np) {
+        size_t vxlan_opts_ofs;
+
+        vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
+        nl_msg_put_u32(a, OVS_VXLAN_EXT_GPE,
+                (tun_key->gpe_flags << 24) | (tun_key->gpe_np));
+        nl_msg_end_nested(a, vxlan_opts_ofs);
     }
     tun_metadata_to_geneve_nlattr(tun_key, tun_flow_key, key_buf, a);
 
@@ -2383,6 +2400,26 @@ format_odp_tun_vxlan_opt(const struct nlattr *attr,
             ds_put_cstr(ds, "),");
             break;
         }
+        case OVS_VXLAN_EXT_GPE: {
+            uint32_t key = nl_attr_get_u32(a);
+            uint8_t np, np_mask;
+            uint8_t flags, flags_mask;
+
+            np = key & 0xFF;
+            flags = (key >> 24) & 0xFF;
+            if (ma) {
+                uint32_t mask = nl_attr_get_u32(ma);
+                np_mask = mask & 0xFF;
+                flags_mask = (mask >> 24) & 0xFF;
+            }
+
+            ds_put_cstr(ds, "gpe(");
+            format_u8x(ds, "np", np, ma ? &np_mask : NULL, verbose);
+            format_u8x(ds, "flags", flags, ma ? &flags_mask : NULL, verbose);
+            ds_chomp(ds, ',');
+            ds_put_cstr(ds, "),");
+            break;
+        }
 
         default:
             format_unknown_key(ds, a, ma);
@@ -3670,6 +3707,40 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
 }
 
 static int
+scan_vxlan_gpe(const char *s, uint32_t *key, uint32_t *mask)
+{
+    const char *s_base = s;
+    uint8_t np = 0, np_mask = 0;
+    uint8_t flags = 0, flags_mask = 0;
+
+    if (!strncmp(s, "np=", 3)) {
+        s += 3;
+        s += scan_u8(s, &np, mask ? &np_mask : NULL);
+    }
+
+    if (s[0] == ',') {
+        s++;
+    }
+    if (!strncmp(s, "flags=", 6)) {
+        s += 6;
+        s += scan_u8(s, &flags, mask ? &flags_mask : NULL);
+    }
+
+    if (!strncmp(s, "))", 2)) {
+        s += 2;
+
+        *key = (flags << 24) | np;
+        if (mask) {
+            *mask = (flags_mask << 24) | np_mask;
+        }
+
+        return s - s_base;
+    }
+
+    return 0;
+}
+
+static int
 scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
 {
     const char *s_base = s;
@@ -3796,6 +3867,21 @@ vxlan_gbp_to_attr(struct ofpbuf *a, const void *data_)
 }
 
 static void
+vxlan_gpe_to_attr(struct ofpbuf *a, const void *data_)
+{
+    const uint32_t *gpe = data_;
+
+    if (*gpe) {
+        size_t vxlan_opts_ofs;
+
+        vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
+        nl_msg_put_u32(a, OVS_VXLAN_EXT_GPE, *gpe);
+        nl_msg_end_nested(a, vxlan_opts_ofs);
+    }
+}
+
+
+static void
 geneve_to_attr(struct ofpbuf *a, const void *data_)
 {
     const struct geneve_scan *geneve = data_;
@@ -4031,6 +4117,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         SCAN_FIELD_NESTED("tp_src=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_SRC);
         SCAN_FIELD_NESTED("tp_dst=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_DST);
         SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
+        SCAN_FIELD_NESTED_FUNC("vxlan(gpe(", uint32_t, vxlan_gpe, vxlan_gpe_to_attr);
         SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
                                geneve_to_attr);
         SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
diff --git a/lib/packets.h b/lib/packets.h
index a8ea24b..dc97333 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -49,7 +49,9 @@ struct flow_tnl {
     ovs_be16 tp_dst;
     ovs_be16 gbp_id;
     uint8_t  gbp_flags;
-    uint8_t  pad1[5];        /* Pad to 64 bits. */
+    uint8_t  gpe_np;
+    uint8_t  gpe_flags;
+    uint8_t  pad1[3];        /* Pad to 64 bits. */
     struct tun_metadata metadata;
 };
 
diff --git a/tests/ofproto.at b/tests/ofproto.at
index fbb6d71..6c7217d 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1775,7 +1775,7 @@ head_table () {
       instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
       Write-Actions and Apply-Actions features:
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
-        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl
+        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_gpe_np tun_gpe_flags tun_metadata0 dnl
 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
 metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll
     matching:
@@ -1790,6 +1790,8 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
       tun_flags: arbitrary mask
       tun_gbp_id: arbitrary mask
       tun_gbp_flags: arbitrary mask
+      tun_gpe_np: arbitrary mask
+      tun_gpe_flags: arbitrary mask
       tun_metadata0: arbitrary mask
       tun_metadata1: arbitrary mask
       tun_metadata2: arbitrary mask
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index f26f622..dde603d 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -17,6 +17,10 @@ for test_case in \
     'tun_gbp_id=0/0x1                            NXM,OXM' \
     'tun_gbp_flags=0                             NXM,OXM' \
     'tun_gbp_flags=0/0x1                         NXM,OXM' \
+    'tun_gpe_np=0                                NXM,OXM' \
+    'tun_gpe_np=0/0x1                            NXM,OXM' \
+    'tun_gpe_flags=0                             NXM,OXM' \
+    'tun_gpe_flags=0/0x1                         NXM,OXM' \
     'tun_metadata0=0                             NXM,OXM' \
     'tun_metadata0=0/0x1                         NXM,OXM' \
     'tun_metadata0                               NXM,OXM' \
-- 
1.9.3