/* callbacks used for customizing the creation and destruction of a behavior */ struct seg6_local_lwtunnel_ops { int (*build_state)(struct seg6_local_lwt *slwt, constvoid *cfg, struct netlink_ext_ack *extack); void (*destroy_state)(struct seg6_local_lwt *slwt);
};
struct seg6_action_desc { int action; unsignedlong attrs;
/* The optattrs field is used for specifying all the optional *attributessupportedbyaspecificbehavior. *Itmeansthatifoneoftheseattributesisnotprovidedinthe *netlinkmessageduringthebehaviorcreation,noerrorswillbe *returnedtotheuserspace. * *Eachattributecanbeonlyoftwotypes(mutuallyexclusive): *1)requiredor2)optional. *EveryuserMUSTobeytothisrule!Ifyousetanattributeas *requiredthesameattributeCANNOTbesetasoptionalandvice *versa.
*/ unsignedlong optattrs;
int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); int static_headroom;
/* default length values (expressed in bits) for both Locator-Block and *Locator-NodeFunction. * *BothSEG6_LOCAL_LCBLOCK_DBITSandSEG6_LOCAL_LCNODE_FN_DBITS*must*be: *i)greaterthan0; *ii)evenlydivisibleby8.Inotherterms,thelengthsofthe *Locator-BlockandLocator-NodeFunctionmustbebyte-aligned(wecan *relaxthisconstraintinthefutureifreallyneeded). * *Moreover,athirdconditionmusthold: *iii)SEG6_LOCAL_LCBLOCK_DBITS+SEG6_LOCAL_LCNODE_FN_DBITS<=128. * *ThecorrectnessofSEG6_LOCAL_LCBLOCK_DBITSandSEG6_LOCAL_LCNODE_FN_DBITS *valuesarecheckedduringthekernelcompilation.Ifthecompilationstops, *checkthevalueoftheseparameterstoseeiftheymeetconditions(i),(ii) *and(iii).
*/ #define SEG6_LOCAL_LCBLOCK_DBITS 32 #define SEG6_LOCAL_LCNODE_FN_DBITS 16
/* The following next_csid_chk_{cntr,lcblock,lcblock_fn}_bits macros can be *useddirectlytocheckwhetherthelengths(inbits)ofLocator-Blockand *Locator-NodeFunctionarevalidaccordingto(i),(ii),(iii).
*/ #define next_csid_chk_cntr_bits(blen, flen) \
((blen) + (flen) > 128)
struct net *net; /* VRF device associated to the routing table used by the SRv6 *End.DT4/DT6behaviorforroutingIPv4/IPv6packets.
*/ int vrf_ifindex; int vrf_table;
/* tunneled packet family (IPv4 or IPv6). *Protocolandheaderlengthareinferredfromfamily.
*/
u16 family;
};
int headroom; struct seg6_action_desc *desc; /* unlike the required attrs, we have to track the optional attributes *thathavebeeneffectivelyparsed.
*/ unsignedlong parsed_optattrs;
};
/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through *theflavorsframework.Thesebehaviorsmustreportthesubsetof(flavor) *operationstheycurrentlyimplement.Inthisway,ifauserspecifiesa *flavorcombinationthatisnotsupportedbyagivenEnd*behavior,the *kernelrefusestoinstantiatethetunnelreportingtheerror.
*/ staticint seg6_flv_supp_ops_by_action(int action, __u32 *fops)
{ switch (action) { case SEG6_LOCAL_ACTION_END:
*fops = SEG6_LOCAL_END_FLV_SUPP_OPS; break; case SEG6_LOCAL_ACTION_END_X:
*fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; break; default: return -EOPNOTSUPP;
}
return0;
}
/* We describe the packet state in relation to the absence/presence of the SRH *andtheSegmentLeft(SL)field. *Forourpurposes,itisnotnecessarytorecordtheexactvalueoftheSL *whentheSIDListconsistsoftwoormoresegments.
*/ enum seg6_local_pktinfo { /* the order really matters! */
SEG6_LOCAL_PKTINFO_NOHDR = 0,
SEG6_LOCAL_PKTINFO_SL_ZERO,
SEG6_LOCAL_PKTINFO_SL_ONE,
SEG6_LOCAL_PKTINFO_SL_MORE,
__SEG6_LOCAL_PKTINFO_MAX,
};
/* We compute the size of the action table by considering the RFC8986 flavors *actuallysupportedbythekernel.Inthisway,thesizeisautomatically *adjustedwhennewflavorsaresupported.
*/ #define FLV8986_ACT_TBL_SIZE \
roundup_pow_of_two(flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_MAX, \
SEG6_LOCAL_FLV8986_SUPP_OPS))
/* tbl_cfg(act, pf, fm) macro is used to easily configure the action *table;itaccepts3arguments: *i)@act,thesuffixfromSEG6_LOCAL_FLV_ACT_{act}representing *theactionthatshouldbeappliedonthepacket; *ii)@pf,thesuffixfromSEG6_LOCAL_PKTINFO_{pf}reportingthepacket *infoaboutthelack/presenceofSRH,SRHwithSL=0,etc; *iii)@fm,themaskofflavors.
*/ #define tbl_cfg(act, pf, fm) \
[flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_##pf, \
(fm))] = SEG6_LOCAL_FLV_ACT_##act
/* shorthand for improving readability */ #define F_PSP SEG6_F_LOCAL_FLV_PSP
/* The table contains, for each combination of the pktinfo data and *flavors,theactionthatshouldbetakenonapacket(e.g. *"standard"Endpointprocessing,PenultimateSegmentPop,etc). * *Bydefault,tableentriesnotexplicitlyconfiguredareinitializedwiththe *SEG6_LOCAL_FLV_ACT_UNSPECaction,whichgenerallyhastheeffectof *discardingtheprocessedpacket.
*/ staticconst u8 flv8986_act_tbl[FLV8986_ACT_TBL_SIZE] = { /* PSP variant for packet where SRH with SL = 1 */
tbl_cfg(PSP, SL_ONE, F_PSP), /* End for packet where the SRH with SL > 1*/
tbl_cfg(END, SL_MORE, F_PSP),
};
#undef F_PSP #undef tbl_cfg
/* For each flavor defined in RFC8986 (or a combination of them) an action is *performedonthepacket.Thespecificactiondependson: *-infoextractedfromthepacket(i.e.pktinfodata)regardingthe *lack/presenceoftheSRH,andiftheSRHisavailable,onthevalueof *SegmentLeftfield; *-themaskofflavorsconfiguredforthespecificSRv6End*behavior. * *Thefunctioncombinesboththepkinfoandtheflavorsmasktoevaluatethe *correspondingactiontobetakenonthepacket.
*/ staticenum seg6_local_flv_action
seg6_local_flv8986_act_lookup(enum seg6_local_pktinfo pinfo, __u32 flvmask)
{ unsignedlong index;
/* check if the provided mask of flavors is supported */ if (unlikely(flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS)) return SEG6_LOCAL_FLV_ACT_UNSPEC;
index = flv8986_act_tbl_idx(pinfo, flvmask); if (unlikely(index >= FLV8986_ACT_TBL_SIZE)) return SEG6_LOCAL_FLV_ACT_UNSPEC;
return flv8986_act_tbl[index];
}
/* skb->data must be aligned with skb->network_header */ staticbool seg6_pop_srh(struct sk_buff *skb, int srhoff)
{ struct ipv6_sr_hdr *srh; struct ipv6hdr *iph;
__u8 srh_nexthdr; int thoff = -1; int srhlen; int nhlen;
if (unlikely(srhoff < sizeof(*iph) ||
!pskb_may_pull(skb, srhoff + sizeof(*srh)))) returnfalse;
/* we are about to mangle the pkt, let's check if we can write on it */ if (unlikely(skb_ensure_writable(skb, srhoff + srhlen))) returnfalse;
/* skb_ensure_writable() may change skb pointers; evaluate srh again */
srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
srh_nexthdr = srh->nexthdr;
if (unlikely(!skb_transport_header_was_set(skb))) goto pull;
nhlen = skb_network_header_len(skb); /* we have to deal with the transport header: it could be set before *theSRH,aftertheSRH,orwithinit(whichisconsideredwrong, *however).
*/ if (likely(nhlen <= srhoff))
thoff = nhlen; elseif (nhlen >= srhoff + srhlen) /* transport_header is set after the SRH */
thoff = nhlen - srhlen; else /* transport_header falls inside the SRH; hence, we can't *restorethetransport_headerpointerproperlyafter *SRHremovingoperation.
*/ returnfalse;
pull: /* we need to pop the SRH: *1)firstofall,wepullouteverythingfromIPv6headeruptoSRH *(included)evaluatingalsothercsum; *2)weoverwrite(andthenremove)theSRHbyproperlymovingthe *IPv6alongwithanyextensionheaderthatprecedestheSRH; *3)Attheend,wepushbackthepulledheaders(exceptforSRH, *obviously).
*/
skb_pull_rcsum(skb, srhoff + srhlen);
memmove(skb_network_header(skb) + srhlen, skb_network_header(skb),
srhoff);
skb_push(skb, srhoff);
skb_reset_network_header(skb);
skb_mac_header_rebuild(skb); if (likely(thoff >= 0))
skb_set_transport_header(skb, thoff);
iph = ipv6_hdr(skb); if (iph->nexthdr == NEXTHDR_ROUTING) {
iph->nexthdr = srh_nexthdr;
} else { /* we must look for the extension header (EXTH, for short) that *immediatelyprecedestheSRHwehavejustremoved. *Then,weupdatethevalueoftheEXTHnexthdrwiththeone *containedintheSRHnexthdr.
*/ unsignedint off = sizeof(*iph); struct ipv6_opt_hdr *hp, _hdr;
__u8 nexthdr = iph->nexthdr;
for (;;) { if (unlikely(!ipv6_ext_hdr(nexthdr) ||
nexthdr == NEXTHDR_NONE)) returnfalse;
hp = skb_header_pointer(skb, off, sizeof(_hdr), &_hdr); if (unlikely(!hp)) returnfalse;
if (hp->nexthdr == NEXTHDR_ROUTING) {
hp->nexthdr = srh_nexthdr; break;
}
switch (nexthdr) { case NEXTHDR_FRAGMENT:
fallthrough; case NEXTHDR_AUTH: /* we expect SRH before FRAG and AUTH */ returnfalse; default:
off += ipv6_optlen(hp); break;
}
/* process the packet on the basis of the RFC8986 flavors set for the given *SRv6Endbehaviorinstance.
*/ staticint end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{ conststruct seg6_flavors_info *finfo = &slwt->flv_info; enum seg6_local_flv_action action; enum seg6_local_pktinfo pinfo; struct ipv6_sr_hdr *srh;
__u32 flvmask; int srhoff;
/* retrieve the action triggered by the combination of pktinfo data and *theflavorsmask.
*/
action = seg6_local_flv8986_act_lookup(pinfo, flvmask); switch (action) { case SEG6_LOCAL_FLV_ACT_END: /* process the packet as the "standard" End behavior */
advance_nextseg(srh, &ipv6_hdr(skb)->daddr); break; case SEG6_LOCAL_FLV_ACT_PSP:
advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
if (unlikely(!seg6_pop_srh(skb, srhoff))) goto drop; break; case SEG6_LOCAL_FLV_ACT_UNSPEC:
fallthrough; default: /* by default, we drop the packet since we could not find a *suitableaction.
*/ goto drop;
}
if (!fops) return input_action_end_core(skb, slwt);
/* check for the presence of NEXT-C-SID since it applies first */ if (seg6_next_csid_enabled(fops)) return end_next_csid_core(skb, slwt);
/* the specific processing function to be performed on the packet *dependsonthecombinationofflavorsdefinedinRFC8986andsome *informationextractedfromthepacket,e.g.presence/absenceofSRH, *SegmentLeft=0,etc.
*/ return end_flv8986_core(skb, slwt);
}
srh = get_and_validate_srh(skb); if (!srh) goto drop;
advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
seg6_lookup_nexthop(skb, NULL, slwt->table);
return dst_input(skb);
drop:
kfree_skb(skb); return -EINVAL;
}
/* decapsulate and forward inner L2 frame on specified interface */ staticint input_action_end_dx2(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{ struct net *net = dev_net(skb->dev); struct net_device *odev; struct ethhdr *eth;
if (!decap_and_validate(skb, IPPROTO_ETHERNET)) goto drop;
if (!pskb_may_pull(skb, ETH_HLEN)) goto drop;
skb_reset_mac_header(skb);
eth = (struct ethhdr *)skb->data;
/* To determine the frame's protocol, we assume it is 802.3. This avoids *acalltoeth_type_trans(),whichisnotreallyrelevantforour *usecase.
*/ if (!eth_proto_is_802_3(eth->h_proto)) goto drop;
odev = dev_get_by_index_rcu(net, slwt->oif); if (!odev) goto drop;
/* As we accept Ethernet frames, make sure the egress device is of *thecorrecttype.
*/ if (odev->type != ARPHRD_ETHER) goto drop;
if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) goto drop;
/* The inner packet is not associated to any local interface, *sowedonotcallnetif_rx(). * *Ifslwt->nh6issetto::,thenlookupthenexthopforthe *innerpacket'sDA.Otherwise,usethespecifiednexthop.
*/ if (!ipv6_addr_any(&slwt->nh6))
nhaddr = &slwt->nh6;
seg6_lookup_nexthop(skb, nhaddr, 0);
return dst_input(skb);
}
/* decapsulate and forward to specified nexthop */ staticint input_action_end_dx6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{ /* this function accepts IPv6 encapsulated packets, with either *anSRHwithSL=0,ornoSRH.
*/
if (!decap_and_validate(skb, IPPROTO_IPV6)) goto drop;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto drop;
/* note that vrf_table was already set by parse_nla_vrftable() */
vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net,
info->vrf_table); if (vrf_ifindex < 0) { if (vrf_ifindex == -EPERM) {
NL_SET_ERR_MSG(extack, "Strict mode for VRF is disabled");
} elseif (vrf_ifindex == -ENODEV) {
NL_SET_ERR_MSG(extack, "Table has no associated VRF device");
} else {
pr_debug("seg6local: SRv6 End.DT* creation error=%d\n",
vrf_ifindex);
}
return vrf_ifindex;
}
info->net = net;
info->vrf_ifindex = vrf_ifindex;
info->family = family;
info->mode = DT_VRF_MODE;
return0;
}
/* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and *routestheIPv4/IPv6packetbylookingattheconfiguredroutingtable. * *IntheSRv6End.DT4/DT6usecase,wecanreceivetraffic(IPv6+Segment *RoutingHeaderpackets)fromseveralinterfacesandtheouterIPv6 *destinationaddress(DA)isusedforretrievingthespecificinstanceofthe *End.DT4/DT6behaviorthatshouldprocessthepackets. * *However,theinnerIPv4/IPv6packetisnotreallyboundtoanyreceiving *interfaceandthustheEnd.DT4/DT6setstheVRF(associatedwiththe *correspondingroutingtable)asthe*receiving*interface. *Inotherwords,theEnd.DT4/DT6processesapacketasifithasbeenreceived *directlybytheVRF(andnotbyoneofitsslavedevices,ifany). *Inthisway,theVRFinterfaceisusedforroutingtheIPv4/IPv6packetin *accordingtotheroutingtableconfiguredbytheEnd.DT4/DT6instance. * *Thisdesignallowsyoutogetsomeinterestingfeatureslike: *1)thestatisticsonrxpackets; *2)thepossibilitytoinstallapacketsnifferonthereceivinginterface *(theVRFone)forlookingattheincomingpackets; *3)thepossibilitytoleveragethenetfilterpreroutinghookfortheinner *IPv4packet. * *Thisfunctionreturns: *-thesk_buff*whentheVRFrcvhandlerhasprocessedthepacketcorrectly; *-NULLwhentheskbisconsumedbytheVRFrcvhandler; *-apointerwhichencodesanegativeerrornumberincaseoferror. *Notethatinthiscase,thefunctiontakescareoffreeingtheskb.
*/ staticstruct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, struct net_device *dev)
{ /* based on l3mdev_ip_rcv; we are only interested in the master */ if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) goto drop;
if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) goto drop;
/* the decap packet IPv4/IPv6 does not come with any mac header info. *WemustunsetthemacheadertoallowtheVRFdevicetorebuildit, *justincasethereisasnifferattachedonthedevice.
*/
skb_unset_mac_header(skb);
skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); if (!skb) /* the skb buffer was consumed by the handler */ return NULL;
/* when a packet is received by a VRF or by one of its slaves, the *masterdevicereferenceissetintotheskb.
*/ if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) goto drop;
return skb;
drop:
kfree_skb(skb); return ERR_PTR(-EINVAL);
}
staticstruct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, struct seg6_end_dt_info *info)
{ int vrf_ifindex = info->vrf_ifindex; struct net *net = info->net;
if (unlikely(vrf_ifindex < 0)) goto error;
if (unlikely(!net_eq(dev_net(skb->dev), net))) goto error;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto drop;
#ifdef CONFIG_NET_L3_MASTER_DEV if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) goto legacy_mode;
/* DT6_VRF_MODE */
skb = end_dt_vrf_core(skb, slwt, AF_INET6); if (!skb) /* packet has been processed and consumed by the VRF */ return0;
if (IS_ERR(skb)) return PTR_ERR(skb);
/* note: this time we do not need to specify the table because the VRF *takescareofselectingthecorrecttable.
*/
seg6_lookup_any_nexthop(skb, NULL, 0, true, 0);
switch (nexthdr) { case IPPROTO_IPIP: return input_action_end_dt4(skb, slwt); case IPPROTO_IPV6: return input_action_end_dt6(skb, slwt);
}
drop:
kfree_skb(skb); return -EINVAL;
} #endif
/* push an SRH on top of the current one */ staticint input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{ struct ipv6_sr_hdr *srh; int err = -EINVAL;
srh = get_and_validate_srh(skb); if (!srh) goto drop;
err = seg6_do_srh_inline(skb, slwt->srh); if (err) goto drop;
ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX,
attrs[SEG6_LOCAL_COUNTERS],
seg6_local_counters_policy, NULL); if (ret < 0) return ret;
/* basic support for SRv6 Behavior counters requires at least: *packets,bytesanderrors.
*/ if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] ||
!tb[SEG6_LOCAL_CNT_ERRORS]) return -EINVAL;
/* counters are always zero initialized */
pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL); if (!pcounters) return -ENOMEM;
staticint cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{ /* a and b are equal if both have pcpu_counters set or not */ return (!!((unsignedlong)a->pcpu_counters)) ^
(!!((unsignedlong)b->pcpu_counters));
}
/* check whether the lengths of the Locator-Block and Locator-Node Function *arecompatiblewiththedimensionofaC-SIDcontainer.
*/ staticint seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len)
{ /* Locator-Block and Locator-Node Function cannot exceed 128 bits *(i.e.C-SIDcontainerlength).
*/ if (next_csid_chk_cntr_bits(block_len, func_len)) return -EINVAL;
/* Locator-Block length must be greater than zero and evenly divisible *by8.TheremustberoomforaLocator-NodeFunction,atleast.
*/ if (next_csid_chk_lcblock_bits(block_len)) return -EINVAL;
/* Locator-Node Function length must be greater than zero and evenly *divisibleby8.TheremustberoomfortheLocator-Block.
*/ if (next_csid_chk_lcnode_fn_bits(func_len)) return -EINVAL;
/* this attribute MUST always be present since it represents the Flavor *operation(s)tobecarriedout.
*/ if (!tb[SEG6_LOCAL_FLV_OPERATION]) return -EINVAL;
if (seg6_next_csid_enabled(fops)) { /* Locator-Block and Locator-Node Function lengths can be *providedbytheuserspace.Otherwise,defaultvaluesare *applied.
*/
rc = seg6_parse_nla_next_csid_cfg(tb, finfo, extack); if (rc < 0) return rc;
}
/* call the destroy() callback (if available) for each set attribute in *@parsed_attrs,startingfromthefirstattributeuptothe@max_parsed *(excluded)attribute.
*/ staticvoid __destroy_attrs(unsignedlong parsed_attrs, int max_parsed, struct seg6_local_lwt *slwt)
{ struct seg6_action_param *param; int i;
/* Every required seg6local attribute is identified by an ID which is *encodedasaflag(i.e:1<<ID)inthe'attrs'bitmask; * *Wescanthe'parsed_attrs'bitmask,startingfromthefirstattribute *uptothe@max_parsed(excluded)attribute. *Foreachsetattribute,weretrievethecorrespondingdestroy() *callback.Ifthecallbackisnotavailable,thenweskiptothenext *attribute;otherwise,wecallthedestroy()callback.
*/ for (i = SEG6_LOCAL_SRH; i < max_parsed; ++i) { if (!(parsed_attrs & SEG6_F_ATTR(i))) continue;
param = &seg6_action_params[i];
if (param->destroy)
param->destroy(slwt);
}
}
/* release all the resources that may have been acquired during parsing *operations.
*/ staticvoid destroy_attrs(struct seg6_local_lwt *slwt)
{ unsignedlong attrs = slwt->desc->attrs | slwt->parsed_optattrs;
/* current attribute has been correctly parsed */
parsed_optattrs |= SEG6_F_ATTR(i);
}
/* store in the tunnel state all the optional attributed successfully *parsed.
*/
slwt->parsed_optattrs = parsed_optattrs;
return0;
parse_optattrs_err:
__destroy_attrs(parsed_optattrs, i, slwt);
return err;
}
/* call the custom constructor of the behavior during its initialization phase *andafterthatallitsattributeshavebeenparsedsuccessfully.
*/ staticint
seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, constvoid *cfg, struct netlink_ext_ack *extack)
{ struct seg6_action_desc *desc = slwt->desc; struct seg6_local_lwtunnel_ops *ops;
ops = &desc->slwt_ops; if (!ops->build_state) return0;
return ops->build_state(slwt, cfg, extack);
}
/* call the custom destructor of the behavior which is invoked before the *tunnelisgoingtobedestroyed.
*/ staticvoid seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt)
{ struct seg6_action_desc *desc = slwt->desc; struct seg6_local_lwtunnel_ops *ops;
ops = &desc->slwt_ops; if (!ops->destroy_state) return;
/* Forcing the desc->optattrs *set* and the desc->attrs *set* to be *disjoined,thisallowustoreleaseacquiredresourcesbyoptional *attributesandbyrequiredattributesindependentlyfromeachother *withoutanyinterference. *Inotherterms,wearesurethatwedonotreleasesometheacquired *resourcestwice. * *Notethatifanattributeisconfiguredbothasrequiredandas *optional,itmeansthattheuserhasmessedsomethingupinthe *seg6_action_table.Therefore,thischeckisrequiredforSRv6 *behaviorstoworkproperly.
*/
invalid_attrs = desc->attrs & desc->optattrs; if (invalid_attrs) {
WARN_ONCE(1, "An attribute cannot be both required AND optional"); return -EINVAL;
}
/* parse the required attributes */ for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (desc->attrs & SEG6_F_ATTR(i)) { if (!attrs[i]) return -EINVAL;
for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (attrs_a & SEG6_F_ATTR(i)) {
param = &seg6_action_params[i]; if (param->cmp(slwt_a, slwt_b)) return1;
}
}
int __init seg6_local_init(void)
{ /* If the max total number of defined attributes is reached, then your *kernelbuildstopshere. * *Thischeckisrequiredtoavoidarithmeticoverflowswhenprocessing *behaviorattributesandthemaximumnumberofdefinedattributes *exceedstheallowedvalue.
*/
BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsignedlong));
/* Check whether the number of defined flavors exceeds the maximum *allowedvalue.
*/
BUILD_BUG_ON(SEG6_LOCAL_FLV_OP_MAX + 1 > BITS_PER_TYPE(__u32));
/* If the default NEXT-C-SID Locator-Block/Node Function lengths (in *bits)havebeenchangedwithinvalidvalues,kernelbuildstops *here.
*/
BUILD_BUG_ON(next_csid_chk_cntr_bits(SEG6_LOCAL_LCBLOCK_DBITS,
SEG6_LOCAL_LCNODE_FN_DBITS));
BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS));
BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS));
/* To be memory efficient, we use 'u8' to represent the different *actionsrelatedtoRFC8986flavors.Ifthekernelbuildstopshere, *itmeansthatitisnotpossibletocorrectlyencodetheseactions *withthedatatypechosenfortheactiontable.
*/
BUILD_BUG_ON(SEG6_LOCAL_FLV_ACT_MAX > (typeof(flv8986_act_tbl[0]))~0U);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.54Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.