@@ -109,8 +109,15 @@ struct bpf_lwt_prog {
109
109
#define next_csid_chk_lcnode_fn_bits (flen ) \
110
110
next_csid_chk_lcblock_bits(flen)
111
111
112
+ #define SEG6_F_LOCAL_FLV_OP (flvname ) BIT(SEG6_LOCAL_FLV_OP_##flvname)
113
+ #define SEG6_F_LOCAL_FLV_PSP SEG6_F_LOCAL_FLV_OP(PSP)
114
+
115
+ /* Supported RFC8986 Flavor operations are reported in this bitmask */
116
+ #define SEG6_LOCAL_FLV8986_SUPP_OPS SEG6_F_LOCAL_FLV_PSP
117
+
112
118
/* Supported Flavor operations are reported in this bitmask */
113
- #define SEG6_LOCAL_FLV_SUPP_OPS (BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID))
119
+ #define SEG6_LOCAL_FLV_SUPP_OPS (SEG6_F_LOCAL_FLV_OP(NEXT_CSID) | \
120
+ SEG6_LOCAL_FLV8986_SUPP_OPS)
114
121
115
122
struct seg6_flavors_info {
116
123
/* Flavor operations */
@@ -409,15 +416,331 @@ static bool seg6_next_csid_enabled(__u32 fops)
409
416
return fops & BIT (SEG6_LOCAL_FLV_OP_NEXT_CSID );
410
417
}
411
418
419
+ /* We describe the packet state in relation to the absence/presence of the SRH
420
+ * and the Segment Left (SL) field.
421
+ * For our purposes, it is not necessary to record the exact value of the SL
422
+ * when the SID List consists of two or more segments.
423
+ */
424
+ enum seg6_local_pktinfo {
425
+ /* the order really matters! */
426
+ SEG6_LOCAL_PKTINFO_NOHDR = 0 ,
427
+ SEG6_LOCAL_PKTINFO_SL_ZERO ,
428
+ SEG6_LOCAL_PKTINFO_SL_ONE ,
429
+ SEG6_LOCAL_PKTINFO_SL_MORE ,
430
+ __SEG6_LOCAL_PKTINFO_MAX ,
431
+ };
432
+
433
+ #define SEG6_LOCAL_PKTINFO_MAX (__SEG6_LOCAL_PKTINFO_MAX - 1)
434
+
435
+ static enum seg6_local_pktinfo seg6_get_srh_pktinfo (struct ipv6_sr_hdr * srh )
436
+ {
437
+ __u8 sgl ;
438
+
439
+ if (!srh )
440
+ return SEG6_LOCAL_PKTINFO_NOHDR ;
441
+
442
+ sgl = srh -> segments_left ;
443
+ if (sgl < 2 )
444
+ return SEG6_LOCAL_PKTINFO_SL_ZERO + sgl ;
445
+
446
+ return SEG6_LOCAL_PKTINFO_SL_MORE ;
447
+ }
448
+
449
+ enum seg6_local_flv_action {
450
+ SEG6_LOCAL_FLV_ACT_UNSPEC = 0 ,
451
+ SEG6_LOCAL_FLV_ACT_END ,
452
+ SEG6_LOCAL_FLV_ACT_PSP ,
453
+ SEG6_LOCAL_FLV_ACT_USP ,
454
+ SEG6_LOCAL_FLV_ACT_USD ,
455
+ __SEG6_LOCAL_FLV_ACT_MAX
456
+ };
457
+
458
+ #define SEG6_LOCAL_FLV_ACT_MAX (__SEG6_LOCAL_FLV_ACT_MAX - 1)
459
+
460
+ /* The action table for RFC8986 flavors (see the flv8986_act_tbl below)
461
+ * contains the actions (i.e. processing operations) to be applied on packets
462
+ * when flavors are configured for an End* behavior.
463
+ * By combining the pkinfo data and from the flavors mask, the macro
464
+ * computes the index used to access the elements (actions) stored in the
465
+ * action table. The index is structured as follows:
466
+ *
467
+ * index
468
+ * _______________/\________________
469
+ * / \
470
+ * +----------------+----------------+
471
+ * | pf | afm |
472
+ * +----------------+----------------+
473
+ * ph-1 ... p1 p0 fk-1 ... f1 f0
474
+ * MSB LSB
475
+ *
476
+ * where:
477
+ * - 'afm' (adjusted flavor mask) is the mask containing a combination of the
478
+ * RFC8986 flavors currently supported. 'afm' corresponds to the @fm
479
+ * argument of the macro whose value is righ-shifted by 1 bit. By doing so,
480
+ * we discard the SEG6_LOCAL_FLV_OP_UNSPEC flag (bit 0 in @fm) which is
481
+ * never used here;
482
+ * - 'pf' encodes the packet info (pktinfo) regarding the presence/absence of
483
+ * the SRH, SL = 0, etc. 'pf' is set with the value of @pf provided as
484
+ * argument to the macro.
485
+ */
486
+ #define flv8986_act_tbl_idx (pf , fm ) \
487
+ ((((pf) << bits_per(SEG6_LOCAL_FLV8986_SUPP_OPS)) | \
488
+ ((fm) & SEG6_LOCAL_FLV8986_SUPP_OPS)) >> SEG6_LOCAL_FLV_OP_PSP)
489
+
490
+ /* We compute the size of the action table by considering the RFC8986 flavors
491
+ * actually supported by the kernel. In this way, the size is automatically
492
+ * adjusted when new flavors are supported.
493
+ */
494
+ #define FLV8986_ACT_TBL_SIZE \
495
+ roundup_pow_of_two(flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_MAX, \
496
+ SEG6_LOCAL_FLV8986_SUPP_OPS))
497
+
498
+ /* tbl_cfg(act, pf, fm) macro is used to easily configure the action
499
+ * table; it accepts 3 arguments:
500
+ * i) @act, the suffix from SEG6_LOCAL_FLV_ACT_{act} representing
501
+ * the action that should be applied on the packet;
502
+ * ii) @pf, the suffix from SEG6_LOCAL_PKTINFO_{pf} reporting the packet
503
+ * info about the lack/presence of SRH, SRH with SL = 0, etc;
504
+ * iii) @fm, the mask of flavors.
505
+ */
506
+ #define tbl_cfg (act , pf , fm ) \
507
+ [flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_##pf, \
508
+ (fm))] = SEG6_LOCAL_FLV_ACT_##act
509
+
510
+ /* shorthand for improving readability */
511
+ #define F_PSP SEG6_F_LOCAL_FLV_PSP
512
+
513
+ /* The table contains, for each combination of the pktinfo data and
514
+ * flavors, the action that should be taken on a packet (e.g.
515
+ * "standard" Endpoint processing, Penultimate Segment Pop, etc).
516
+ *
517
+ * By default, table entries not explicitly configured are initialized with the
518
+ * SEG6_LOCAL_FLV_ACT_UNSPEC action, which generally has the effect of
519
+ * discarding the processed packet.
520
+ */
521
+ static const u8 flv8986_act_tbl [FLV8986_ACT_TBL_SIZE ] = {
522
+ /* PSP variant for packet where SRH with SL = 1 */
523
+ tbl_cfg (PSP , SL_ONE , F_PSP ),
524
+ /* End for packet where the SRH with SL > 1*/
525
+ tbl_cfg (END , SL_MORE , F_PSP ),
526
+ };
527
+
528
+ #undef F_PSP
529
+ #undef tbl_cfg
530
+
531
+ /* For each flavor defined in RFC8986 (or a combination of them) an action is
532
+ * performed on the packet. The specific action depends on:
533
+ * - info extracted from the packet (i.e. pktinfo data) regarding the
534
+ * lack/presence of the SRH, and if the SRH is available, on the value of
535
+ * Segment Left field;
536
+ * - the mask of flavors configured for the specific SRv6 End* behavior.
537
+ *
538
+ * The function combines both the pkinfo and the flavors mask to evaluate the
539
+ * corresponding action to be taken on the packet.
540
+ */
541
+ static enum seg6_local_flv_action
542
+ seg6_local_flv8986_act_lookup (enum seg6_local_pktinfo pinfo , __u32 flvmask )
543
+ {
544
+ unsigned long index ;
545
+
546
+ /* check if the provided mask of flavors is supported */
547
+ if (unlikely (flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS ))
548
+ return SEG6_LOCAL_FLV_ACT_UNSPEC ;
549
+
550
+ index = flv8986_act_tbl_idx (pinfo , flvmask );
551
+ if (unlikely (index >= FLV8986_ACT_TBL_SIZE ))
552
+ return SEG6_LOCAL_FLV_ACT_UNSPEC ;
553
+
554
+ return flv8986_act_tbl [index ];
555
+ }
556
+
557
+ /* skb->data must be aligned with skb->network_header */
558
+ static bool seg6_pop_srh (struct sk_buff * skb , int srhoff )
559
+ {
560
+ struct ipv6_sr_hdr * srh ;
561
+ struct ipv6hdr * iph ;
562
+ __u8 srh_nexthdr ;
563
+ int thoff = -1 ;
564
+ int srhlen ;
565
+ int nhlen ;
566
+
567
+ if (unlikely (srhoff < sizeof (* iph ) ||
568
+ !pskb_may_pull (skb , srhoff + sizeof (* srh ))))
569
+ return false;
570
+
571
+ srh = (struct ipv6_sr_hdr * )(skb -> data + srhoff );
572
+ srhlen = ipv6_optlen (srh );
573
+
574
+ /* we are about to mangle the pkt, let's check if we can write on it */
575
+ if (unlikely (skb_ensure_writable (skb , srhoff + srhlen )))
576
+ return false;
577
+
578
+ /* skb_ensure_writable() may change skb pointers; evaluate srh again */
579
+ srh = (struct ipv6_sr_hdr * )(skb -> data + srhoff );
580
+ srh_nexthdr = srh -> nexthdr ;
581
+
582
+ if (unlikely (!skb_transport_header_was_set (skb )))
583
+ goto pull ;
584
+
585
+ nhlen = skb_network_header_len (skb );
586
+ /* we have to deal with the transport header: it could be set before
587
+ * the SRH, after the SRH, or within it (which is considered wrong,
588
+ * however).
589
+ */
590
+ if (likely (nhlen <= srhoff ))
591
+ thoff = nhlen ;
592
+ else if (nhlen >= srhoff + srhlen )
593
+ /* transport_header is set after the SRH */
594
+ thoff = nhlen - srhlen ;
595
+ else
596
+ /* transport_header falls inside the SRH; hence, we can't
597
+ * restore the transport_header pointer properly after
598
+ * SRH removing operation.
599
+ */
600
+ return false;
601
+ pull :
602
+ /* we need to pop the SRH:
603
+ * 1) first of all, we pull out everything from IPv6 header up to SRH
604
+ * (included) evaluating also the rcsum;
605
+ * 2) we overwrite (and then remove) the SRH by properly moving the
606
+ * IPv6 along with any extension header that precedes the SRH;
607
+ * 3) At the end, we push back the pulled headers (except for SRH,
608
+ * obviously).
609
+ */
610
+ skb_pull_rcsum (skb , srhoff + srhlen );
611
+ memmove (skb_network_header (skb ) + srhlen , skb_network_header (skb ),
612
+ srhoff );
613
+ skb_push (skb , srhoff );
614
+
615
+ skb_reset_network_header (skb );
616
+ skb_mac_header_rebuild (skb );
617
+ if (likely (thoff >= 0 ))
618
+ skb_set_transport_header (skb , thoff );
619
+
620
+ iph = ipv6_hdr (skb );
621
+ if (iph -> nexthdr == NEXTHDR_ROUTING ) {
622
+ iph -> nexthdr = srh_nexthdr ;
623
+ } else {
624
+ /* we must look for the extension header (EXTH, for short) that
625
+ * immediately precedes the SRH we have just removed.
626
+ * Then, we update the value of the EXTH nexthdr with the one
627
+ * contained in the SRH nexthdr.
628
+ */
629
+ unsigned int off = sizeof (* iph );
630
+ struct ipv6_opt_hdr * hp , _hdr ;
631
+ __u8 nexthdr = iph -> nexthdr ;
632
+
633
+ for (;;) {
634
+ if (unlikely (!ipv6_ext_hdr (nexthdr ) ||
635
+ nexthdr == NEXTHDR_NONE ))
636
+ return false;
637
+
638
+ hp = skb_header_pointer (skb , off , sizeof (_hdr ), & _hdr );
639
+ if (unlikely (!hp ))
640
+ return false;
641
+
642
+ if (hp -> nexthdr == NEXTHDR_ROUTING ) {
643
+ hp -> nexthdr = srh_nexthdr ;
644
+ break ;
645
+ }
646
+
647
+ switch (nexthdr ) {
648
+ case NEXTHDR_FRAGMENT :
649
+ fallthrough ;
650
+ case NEXTHDR_AUTH :
651
+ /* we expect SRH before FRAG and AUTH */
652
+ return false;
653
+ default :
654
+ off += ipv6_optlen (hp );
655
+ break ;
656
+ }
657
+
658
+ nexthdr = hp -> nexthdr ;
659
+ }
660
+ }
661
+
662
+ iph -> payload_len = htons (skb -> len - sizeof (struct ipv6hdr ));
663
+
664
+ skb_postpush_rcsum (skb , iph , srhoff );
665
+
666
+ return true;
667
+ }
668
+
669
+ /* process the packet on the basis of the RFC8986 flavors set for the given
670
+ * SRv6 End behavior instance.
671
+ */
672
+ static int end_flv8986_core (struct sk_buff * skb , struct seg6_local_lwt * slwt )
673
+ {
674
+ const struct seg6_flavors_info * finfo = & slwt -> flv_info ;
675
+ enum seg6_local_flv_action action ;
676
+ enum seg6_local_pktinfo pinfo ;
677
+ struct ipv6_sr_hdr * srh ;
678
+ __u32 flvmask ;
679
+ int srhoff ;
680
+
681
+ srh = seg6_get_srh (skb , 0 );
682
+ srhoff = srh ? ((unsigned char * )srh - skb -> data ) : 0 ;
683
+ pinfo = seg6_get_srh_pktinfo (srh );
684
+ #ifdef CONFIG_IPV6_SEG6_HMAC
685
+ if (srh && !seg6_hmac_validate_skb (skb ))
686
+ goto drop ;
687
+ #endif
688
+ flvmask = finfo -> flv_ops ;
689
+ if (unlikely (flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS )) {
690
+ pr_warn_once ("seg6local: invalid RFC8986 flavors\n" );
691
+ goto drop ;
692
+ }
693
+
694
+ /* retrieve the action triggered by the combination of pktinfo data and
695
+ * the flavors mask.
696
+ */
697
+ action = seg6_local_flv8986_act_lookup (pinfo , flvmask );
698
+ switch (action ) {
699
+ case SEG6_LOCAL_FLV_ACT_END :
700
+ /* process the packet as the "standard" End behavior */
701
+ advance_nextseg (srh , & ipv6_hdr (skb )-> daddr );
702
+ break ;
703
+ case SEG6_LOCAL_FLV_ACT_PSP :
704
+ advance_nextseg (srh , & ipv6_hdr (skb )-> daddr );
705
+
706
+ if (unlikely (!seg6_pop_srh (skb , srhoff )))
707
+ goto drop ;
708
+ break ;
709
+ case SEG6_LOCAL_FLV_ACT_UNSPEC :
710
+ fallthrough ;
711
+ default :
712
+ /* by default, we drop the packet since we could not find a
713
+ * suitable action.
714
+ */
715
+ goto drop ;
716
+ }
717
+
718
+ return input_action_end_finish (skb , slwt );
719
+
720
+ drop :
721
+ kfree_skb (skb );
722
+ return - EINVAL ;
723
+ }
724
+
412
725
/* regular endpoint function */
413
726
static int input_action_end (struct sk_buff * skb , struct seg6_local_lwt * slwt )
414
727
{
415
728
const struct seg6_flavors_info * finfo = & slwt -> flv_info ;
729
+ __u32 fops = finfo -> flv_ops ;
416
730
417
- if (seg6_next_csid_enabled (finfo -> flv_ops ))
731
+ if (!fops )
732
+ return input_action_end_core (skb , slwt );
733
+
734
+ /* check for the presence of NEXT-C-SID since it applies first */
735
+ if (seg6_next_csid_enabled (fops ))
418
736
return end_next_csid_core (skb , slwt );
419
737
420
- return input_action_end_core (skb , slwt );
738
+ /* the specific processing function to be performed on the packet
739
+ * depends on the combination of flavors defined in RFC8986 and some
740
+ * information extracted from the packet, e.g. presence/absence of SRH,
741
+ * Segment Left = 0, etc.
742
+ */
743
+ return end_flv8986_core (skb , slwt );
421
744
}
422
745
423
746
/* regular endpoint, and forward to specified nexthop */
@@ -2304,6 +2627,13 @@ int __init seg6_local_init(void)
2304
2627
BUILD_BUG_ON (next_csid_chk_lcblock_bits (SEG6_LOCAL_LCBLOCK_DBITS ));
2305
2628
BUILD_BUG_ON (next_csid_chk_lcnode_fn_bits (SEG6_LOCAL_LCNODE_FN_DBITS ));
2306
2629
2630
+ /* To be memory efficient, we use 'u8' to represent the different
2631
+ * actions related to RFC8986 flavors. If the kernel build stops here,
2632
+ * it means that it is not possible to correctly encode these actions
2633
+ * with the data type chosen for the action table.
2634
+ */
2635
+ BUILD_BUG_ON (SEG6_LOCAL_FLV_ACT_MAX > (typeof (flv8986_act_tbl [0 ]))~0U );
2636
+
2307
2637
return lwtunnel_encap_add_ops (& seg6_local_ops ,
2308
2638
LWTUNNEL_ENCAP_SEG6_LOCAL );
2309
2639
}
0 commit comments