Skip to content

Commit bdf3c0b

Browse files
skorpion17Paolo Abeni
authored andcommitted
seg6: add PSP flavor support for SRv6 End behavior
The "flavors" framework defined in RFC8986 [1] represents additional operations that can modify or extend a subset of existing behaviors such as SRv6 End, End.X and End.T. We report these flavors hereafter: - Penultimate Segment Pop (PSP); - Ultimate Segment Pop (USP); - Ultimate Segment Decapsulation (USD). Depending on how the Segment Routing Header (SRH) has to be handled, an SRv6 End* behavior can support these flavors either individually or in combinations. In this patch, we only consider the PSP flavor for the SRv6 End behavior. A PSP enabled SRv6 End behavior is used by the Source/Ingress SR node (i.e., the one applying the SRv6 Policy) when it needs to instruct the penultimate SR Endpoint node listed in the SID List (carried by the SRH) to remove the SRH from the IPv6 header. Specifically, a PSP enabled SRv6 End behavior processes the SRH by: i) decreasing the Segment Left (SL) from 1 to 0; ii) copying the Last Segment IDentifier (SID) into the IPv6 Destination Address (DA); iii) removing (i.e., popping) the outer SRH from the extension headers following the IPv6 header. It is important to note that PSP operation (steps i, ii, iii) takes place only at a penultimate SR Segment Endpoint node (i.e., when the SL=1) and does not happen at non-penultimate Endpoint nodes. Indeed, when a SID of PSP flavor is processed at a non-penultimate SR Segment Endpoint node, the PSP operation is not performed because it would not be possible to decrease the SL from 1 to 0. SL=2 SL=1 SL=0 | | | For example, given the SRv6 policy (SID List := < X, Y, Z >): - a PSP enabled SRv6 End behavior bound to SID "Y" will apply the PSP operation as Segment Left (SL) is 1, corresponding to the Penultimate Segment of the SID List; - a PSP enabled SRv6 End behavior bound to SID "X" will *NOT* apply the PSP operation as the Segment Left is 2. This behavior instance will apply the "standard" End packet processing, ignoring the configured PSP flavor at all. [1] - RFC8986: https://datatracker.ietf.org/doc/html/rfc8986 Signed-off-by: Andrea Mayer <[email protected]> Reviewed-by: David Ahern <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 525c65f commit bdf3c0b

File tree

1 file changed

+333
-3
lines changed

1 file changed

+333
-3
lines changed

net/ipv6/seg6_local.c

Lines changed: 333 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,15 @@ struct bpf_lwt_prog {
109109
#define next_csid_chk_lcnode_fn_bits(flen) \
110110
next_csid_chk_lcblock_bits(flen)
111111

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+
112118
/* 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)
114121

115122
struct seg6_flavors_info {
116123
/* Flavor operations */
@@ -409,15 +416,331 @@ static bool seg6_next_csid_enabled(__u32 fops)
409416
return fops & BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID);
410417
}
411418

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+
412725
/* regular endpoint function */
413726
static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
414727
{
415728
const struct seg6_flavors_info *finfo = &slwt->flv_info;
729+
__u32 fops = finfo->flv_ops;
416730

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))
418736
return end_next_csid_core(skb, slwt);
419737

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);
421744
}
422745

423746
/* regular endpoint, and forward to specified nexthop */
@@ -2304,6 +2627,13 @@ int __init seg6_local_init(void)
23042627
BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS));
23052628
BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS));
23062629

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+
23072637
return lwtunnel_encap_add_ops(&seg6_local_ops,
23082638
LWTUNNEL_ENCAP_SEG6_LOCAL);
23092639
}

0 commit comments

Comments
 (0)