@@ -251,7 +251,7 @@ erspan_opt_policy[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1] = {
251
251
};
252
252
253
253
static int ip_tun_parse_opts_geneve (struct nlattr * attr ,
254
- struct ip_tunnel_info * info ,
254
+ struct ip_tunnel_info * info , int opts_len ,
255
255
struct netlink_ext_ack * extack )
256
256
{
257
257
struct nlattr * tb [LWTUNNEL_IP_OPT_GENEVE_MAX + 1 ];
@@ -273,7 +273,7 @@ static int ip_tun_parse_opts_geneve(struct nlattr *attr,
273
273
return - EINVAL ;
274
274
275
275
if (info ) {
276
- struct geneve_opt * opt = ip_tunnel_info_opts (info );
276
+ struct geneve_opt * opt = ip_tunnel_info_opts (info ) + opts_len ;
277
277
278
278
memcpy (opt -> opt_data , nla_data (attr ), data_len );
279
279
opt -> length = data_len / 4 ;
@@ -288,7 +288,7 @@ static int ip_tun_parse_opts_geneve(struct nlattr *attr,
288
288
}
289
289
290
290
static int ip_tun_parse_opts_vxlan (struct nlattr * attr ,
291
- struct ip_tunnel_info * info ,
291
+ struct ip_tunnel_info * info , int opts_len ,
292
292
struct netlink_ext_ack * extack )
293
293
{
294
294
struct nlattr * tb [LWTUNNEL_IP_OPT_VXLAN_MAX + 1 ];
@@ -303,7 +303,8 @@ static int ip_tun_parse_opts_vxlan(struct nlattr *attr,
303
303
return - EINVAL ;
304
304
305
305
if (info ) {
306
- struct vxlan_metadata * md = ip_tunnel_info_opts (info );
306
+ struct vxlan_metadata * md =
307
+ ip_tunnel_info_opts (info ) + opts_len ;
307
308
308
309
attr = tb [LWTUNNEL_IP_OPT_VXLAN_GBP ];
309
310
md -> gbp = nla_get_u32 (attr );
@@ -314,7 +315,7 @@ static int ip_tun_parse_opts_vxlan(struct nlattr *attr,
314
315
}
315
316
316
317
static int ip_tun_parse_opts_erspan (struct nlattr * attr ,
317
- struct ip_tunnel_info * info ,
318
+ struct ip_tunnel_info * info , int opts_len ,
318
319
struct netlink_ext_ack * extack )
319
320
{
320
321
struct nlattr * tb [LWTUNNEL_IP_OPT_ERSPAN_MAX + 1 ];
@@ -329,7 +330,8 @@ static int ip_tun_parse_opts_erspan(struct nlattr *attr,
329
330
return - EINVAL ;
330
331
331
332
if (info ) {
332
- struct erspan_metadata * md = ip_tunnel_info_opts (info );
333
+ struct erspan_metadata * md =
334
+ ip_tunnel_info_opts (info ) + opts_len ;
333
335
334
336
attr = tb [LWTUNNEL_IP_OPT_ERSPAN_VER ];
335
337
md -> version = nla_get_u8 (attr );
@@ -356,30 +358,57 @@ static int ip_tun_parse_opts_erspan(struct nlattr *attr,
356
358
static int ip_tun_parse_opts (struct nlattr * attr , struct ip_tunnel_info * info ,
357
359
struct netlink_ext_ack * extack )
358
360
{
359
- struct nlattr * tb [ LWTUNNEL_IP_OPTS_MAX + 1 ] ;
360
- int err ;
361
+ int err , rem , opt_len , opts_len = 0 , type = 0 ;
362
+ struct nlattr * nla ;
361
363
362
364
if (!attr )
363
365
return 0 ;
364
366
365
- err = nla_parse_nested ( tb , LWTUNNEL_IP_OPTS_MAX , attr ,
366
- ip_opts_policy , extack );
367
+ err = nla_validate ( nla_data ( attr ), nla_len ( attr ), LWTUNNEL_IP_OPTS_MAX ,
368
+ ip_opts_policy , extack );
367
369
if (err )
368
370
return err ;
369
371
370
- if (tb [LWTUNNEL_IP_OPTS_GENEVE ])
371
- err = ip_tun_parse_opts_geneve (tb [LWTUNNEL_IP_OPTS_GENEVE ],
372
- info , extack );
373
- else if (tb [LWTUNNEL_IP_OPTS_VXLAN ])
374
- err = ip_tun_parse_opts_vxlan (tb [LWTUNNEL_IP_OPTS_VXLAN ],
375
- info , extack );
376
- else if (tb [LWTUNNEL_IP_OPTS_ERSPAN ])
377
- err = ip_tun_parse_opts_erspan (tb [LWTUNNEL_IP_OPTS_ERSPAN ],
378
- info , extack );
379
- else
380
- err = - EINVAL ;
372
+ nla_for_each_attr (nla , nla_data (attr ), nla_len (attr ), rem ) {
373
+ switch (nla_type (nla )) {
374
+ case LWTUNNEL_IP_OPTS_GENEVE :
375
+ if (type && type != TUNNEL_GENEVE_OPT )
376
+ return - EINVAL ;
377
+ opt_len = ip_tun_parse_opts_geneve (nla , info , opts_len ,
378
+ extack );
379
+ if (opt_len < 0 )
380
+ return opt_len ;
381
+ opts_len += opt_len ;
382
+ if (opts_len > IP_TUNNEL_OPTS_MAX )
383
+ return - EINVAL ;
384
+ type = TUNNEL_GENEVE_OPT ;
385
+ break ;
386
+ case LWTUNNEL_IP_OPTS_VXLAN :
387
+ if (type )
388
+ return - EINVAL ;
389
+ opt_len = ip_tun_parse_opts_vxlan (nla , info , opts_len ,
390
+ extack );
391
+ if (opt_len < 0 )
392
+ return opt_len ;
393
+ opts_len += opt_len ;
394
+ type = TUNNEL_VXLAN_OPT ;
395
+ break ;
396
+ case LWTUNNEL_IP_OPTS_ERSPAN :
397
+ if (type )
398
+ return - EINVAL ;
399
+ opt_len = ip_tun_parse_opts_erspan (nla , info , opts_len ,
400
+ extack );
401
+ if (opt_len < 0 )
402
+ return opt_len ;
403
+ opts_len += opt_len ;
404
+ type = TUNNEL_ERSPAN_OPT ;
405
+ break ;
406
+ default :
407
+ return - EINVAL ;
408
+ }
409
+ }
381
410
382
- return err ;
411
+ return opts_len ;
383
412
}
384
413
385
414
static int ip_tun_get_optlen (struct nlattr * attr ,
@@ -477,18 +506,23 @@ static int ip_tun_fill_encap_opts_geneve(struct sk_buff *skb,
477
506
{
478
507
struct geneve_opt * opt ;
479
508
struct nlattr * nest ;
509
+ int offset = 0 ;
480
510
481
511
nest = nla_nest_start_noflag (skb , LWTUNNEL_IP_OPTS_GENEVE );
482
512
if (!nest )
483
513
return - ENOMEM ;
484
514
485
- opt = ip_tunnel_info_opts (tun_info );
486
- if (nla_put_be16 (skb , LWTUNNEL_IP_OPT_GENEVE_CLASS , opt -> opt_class ) ||
487
- nla_put_u8 (skb , LWTUNNEL_IP_OPT_GENEVE_TYPE , opt -> type ) ||
488
- nla_put (skb , LWTUNNEL_IP_OPT_GENEVE_DATA , opt -> length * 4 ,
489
- opt -> opt_data )) {
490
- nla_nest_cancel (skb , nest );
491
- return - ENOMEM ;
515
+ while (tun_info -> options_len > offset ) {
516
+ opt = ip_tunnel_info_opts (tun_info ) + offset ;
517
+ if (nla_put_be16 (skb , LWTUNNEL_IP_OPT_GENEVE_CLASS ,
518
+ opt -> opt_class ) ||
519
+ nla_put_u8 (skb , LWTUNNEL_IP_OPT_GENEVE_TYPE , opt -> type ) ||
520
+ nla_put (skb , LWTUNNEL_IP_OPT_GENEVE_DATA , opt -> length * 4 ,
521
+ opt -> opt_data )) {
522
+ nla_nest_cancel (skb , nest );
523
+ return - ENOMEM ;
524
+ }
525
+ offset += sizeof (* opt ) + opt -> length * 4 ;
492
526
}
493
527
494
528
nla_nest_end (skb , nest );
@@ -602,13 +636,18 @@ static int ip_tun_opts_nlsize(struct ip_tunnel_info *info)
602
636
603
637
opt_len = nla_total_size (0 ); /* LWTUNNEL_IP_OPTS */
604
638
if (info -> key .tun_flags & TUNNEL_GENEVE_OPT ) {
605
- struct geneve_opt * opt = ip_tunnel_info_opts (info );
606
-
607
- opt_len += nla_total_size (0 ) /* LWTUNNEL_IP_OPTS_GENEVE */
608
- + nla_total_size (2 ) /* OPT_GENEVE_CLASS */
609
- + nla_total_size (1 ) /* OPT_GENEVE_TYPE */
610
- + nla_total_size (opt -> length * 4 );
611
- /* OPT_GENEVE_DATA */
639
+ struct geneve_opt * opt ;
640
+ int offset = 0 ;
641
+
642
+ opt_len += nla_total_size (0 ); /* LWTUNNEL_IP_OPTS_GENEVE */
643
+ while (info -> options_len > offset ) {
644
+ opt = ip_tunnel_info_opts (info ) + offset ;
645
+ opt_len += nla_total_size (2 ) /* OPT_GENEVE_CLASS */
646
+ + nla_total_size (1 ) /* OPT_GENEVE_TYPE */
647
+ + nla_total_size (opt -> length * 4 );
648
+ /* OPT_GENEVE_DATA */
649
+ offset += sizeof (* opt ) + opt -> length * 4 ;
650
+ }
612
651
} else if (info -> key .tun_flags & TUNNEL_VXLAN_OPT ) {
613
652
opt_len += nla_total_size (0 ) /* LWTUNNEL_IP_OPTS_VXLAN */
614
653
+ nla_total_size (4 ); /* OPT_VXLAN_GBP */
0 commit comments