summaryrefslogtreecommitdiff
path: root/ovs_build/ovs_nsh_patches/v2.6.1/0001-Enable-current-VxLAN-gpe-work-and-meet-NSH-requireme.patch
blob: 63b75972a80ada3f4d2e0c5105d51d74fd2328dc (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
From 9f7148f9dc03053a4a3231a7cf819c447a178a17 Mon Sep 17 00:00:00 2001
From: Yi Yang <yi.y.yang@intel.com>
Date: Fri, 11 Nov 2016 11:13:18 +0800
Subject: [PATCH 1/8] Enable current VxLAN-gpe work and meet NSH requirements

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         |  1 +
 datapath/linux/compat/vxlan.c                     |  4 +-
 datapath/vport-netdev.c                           |  3 +-
 datapath/vport-vxlan.c                            | 15 ++++
 include/openvswitch/match.h                       |  4 +
 include/openvswitch/meta-flow.h                   | 28 +++++++
 include/openvswitch/packets.h                     |  4 +-
 lib/flow.c                                        |  8 ++
 lib/match.c                                       | 34 +++++++++
 lib/meta-flow.c                                   | 36 +++++++++
 lib/netdev-vport.c                                |  2 +
 lib/nx-match.c                                    |  4 +
 lib/odp-util.c                                    | 89 ++++++++++++++++++++++-
 tests/ofproto.at                                  |  4 +-
 tests/ovs-ofctl.at                                |  4 +
 17 files changed, 241 insertions(+), 8 deletions(-)

diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 0f32664..2dcae07 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] = {
@@ -523,6 +524,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);
@@ -709,7 +713,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 12260d8..44b7ce4 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -291,6 +291,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 5bc8969..5a994b2 100644
--- a/datapath/linux/compat/include/net/vxlan.h
+++ b/datapath/linux/compat/include/net/vxlan.h
@@ -201,6 +201,7 @@ reserved_flags2:2;
 
 struct vxlan_metadata {
 	u32		gbp;
+	u32		gpe;
 };
 
 /* per UDP socket information */
diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
index d5dbe8d..a80610b 100644
--- a/datapath/linux/compat/vxlan.c
+++ b/datapath/linux/compat/vxlan.c
@@ -705,7 +705,6 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 	if (vs->flags & VXLAN_F_GPE) {
 		if (!vxlan_parse_gpe_hdr(&unparsed, &protocol, skb, vs->flags))
 			goto drop;
-		raw_proto = true;
 	}
 
 	if (__iptunnel_pull_header(skb, VXLAN_HLEN, protocol, raw_proto,
@@ -896,10 +895,9 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst,
 	if (vxflags & VXLAN_F_GBP)
 		vxlan_build_gbp_hdr(vxh, vxflags, md);
 	if (vxflags & VXLAN_F_GPE) {
-		err = vxlan_build_gpe_hdr(vxh, vxflags, skb->protocol);
+		err = vxlan_build_gpe_hdr(vxh, vxflags, inner_protocol);
 		if (err < 0)
 			goto out_free;
-		inner_protocol = skb->protocol;
 	}
 
 	ovs_skb_set_inner_protocol(skb, inner_protocol);
diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c
index 970f7d3..0ee076b 100644
--- a/datapath/vport-netdev.c
+++ b/datapath/vport-netdev.c
@@ -102,7 +102,8 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name)
 	}
 
 	if (vport->dev->flags & IFF_LOOPBACK ||
-	    vport->dev->type != ARPHRD_ETHER ||
+	    (vport->dev->type != ARPHRD_ETHER &&
+             vport->dev->type != ARPHRD_NONE) ||
 	    ovs_is_internal_dev(vport->dev)) {
 		err = -EINVAL;
 		goto error_put;
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index 11965c0..e32c970 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/include/openvswitch/match.h b/include/openvswitch/match.h
index 3b7f32f..93af1b8 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -89,6 +89,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/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index b091c1b..9e569ef 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -493,6 +493,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(200) 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(201) since v2.4.
+     * OXM: none.
+     */
+    MFF_TUN_GPE_FLAGS,
+
 #if TUN_METADATA_NUM_OPTS == 64
     /* "tun_metadata<N>".
      *
diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
index 5d97309..1e82df0 100644
--- a/include/openvswitch/packets.h
+++ b/include/openvswitch/packets.h
@@ -34,7 +34,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/lib/flow.c b/lib/flow.c
index ba4f8c7..375979b 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -897,6 +897,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);
@@ -1292,6 +1298,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 d78e6a1..f19648d 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -305,6 +305,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);
@@ -1029,6 +1055,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/meta-flow.c b/lib/meta-flow.c
index d07f927..5d0721f 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);
@@ -434,6 +438,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:
@@ -568,6 +574,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;
@@ -823,6 +835,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;
@@ -1161,6 +1179,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;
@@ -1484,6 +1508,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;
@@ -1793,6 +1823,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/netdev-vport.c b/lib/netdev-vport.c
index ac31da6..cff0f5c 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -526,6 +526,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 b03ccf2..65d7ee3 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1036,6 +1036,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 6d29b67..b9e8aa7 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1755,6 +1755,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] = {
@@ -1916,7 +1917,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)];
 
@@ -1930,6 +1934,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;
         }
@@ -2016,6 +2026,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);
 
@@ -2410,6 +2427,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);
@@ -3705,6 +3742,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;
@@ -3831,6 +3902,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_;
@@ -4066,6 +4152,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/tests/ofproto.at b/tests/ofproto.at
index 6e55270..7b7f02b 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2190,7 +2190,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 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 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:
@@ -2205,6 +2205,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 cbb818e..2267be0 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' \
-- 
2.1.0