As of Linux 4.18, custom SRv6 network functions can be implemented in eBPF and installed in the kernel. eBPF (for extended Berkeley Packet Filter), is a general-purpose 64 bits RISC-like virtual machine included in Linux. It provides a programmable interface to adapt kernel components at run-time to user-specific behaviours. eBPF programs are written in C and compiled to eBPF bytecode using LLVM, and then can be attached to predetermined hooks in the kernel. The eBPF program is then executed for each packet going through the datapath associated to its hook. The program can read and, for some hooks, modify the packet.
A BPF hook has been introduced in the seg6local infrastructure as a regular seg6local action, called End.BPF. Using End.BPF, network operators can implement their own SRv6 functions. Moreover, SRv6-specific BPF helpers are provided and allow End.BPF functions to leverage advanced SRv6 features such as executing basic SRv6 actions (End.X, End.T, ...) or adding TLVs.
This page describes the intrinsic properties of End.BPF, the explications described here assume that the reader is familiar with BPF. For more information regarding all the capabilities of BPF, see the BPF reference guide.
Every instance of an End.BPF action is bound to a given eBPF program. As its name implies, End.BPF behaves as an endpoint, i.e. it advances the SRH (Segment Routing Header) to the next segment, and subsequently executes the associated eBPF code. Only IPv6 packets containing a SRH with Segments Left > 0
are accepted. Segments Left
is decremented and the next segment to be processed is copied to the IPv6 Destination Address
field by the action before the eBPF function is executed.
End.BPF programs must return one of the three following return values. These values indicate to End.BPF the final step of the SRv6 processing that must be applied on the packet:
BPF_OK
: a regular FIB lookup must be performed on the next segment, the packet will be forwarded on the egress interface returned by the lookup.
BPF_DROP
: the packet must be dropped.
BPF_REDIRECT
: the default endpoint lookup must not be performed, and the packet must be forwarded to the destination already set in the packet metadata. To be used altogether with the bpf_lwt_seg6_action
helper.
Furthermore, this action has been designed with two key principles in mind:
As a consequence of the first principle, direct write access by BPF code is prohibited. Only specific fields of the outermost SRH can be modified using the bpf_lwt_seg6_store_bytes
helper. To enable End.BPF functions to leverage SRv6 features, two others seg6 helpers are available. Mainly, bpf_lwt_seg6_action
allows to use classic SRv6 actions (End.X, End.T, End.B6 and End.B6.Encaps) inside End.BPF. The specifications of these helpers are given below.
Installing an End.BPF program for a given SID can be performed using iproute2, much like other seg6local actions:
ip -6 route add dead::beef encap seg6local action End.BPF endpoint object my_code.o section my_function dev eth0
my_code.o must contain proper eBPF bytecode and my_function corresponds to the section in the code to execute.
End.BPF can be used to implement various custom SRv6 network functions (advanced routing policies, firewalls, load balancers, OAM functions, etc). An exhaustive study analyzing the implementation of three SRv6 End.BPF functions is available at https://github.com/Zashas/Thesis-SRv6-BPF, along with the source codes released under GNU GPL v3.
End.BPF only processes IPv6 packets with a SRH. However, one might also need to implement custom SRv6 transit behaviors, mainly to implement specifc SRv6 encapsulation policies. To this end, the bpf_lwt_push_encap
helper has been implemented and is available within the LWT BPF in
hook. It allows to encapsulate an SRH into any IPv6 packets, either inline or with an outer IPv6 header. The specification of this helper is given below.
int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len) * Description * Encapsulate the packet associated to *skb* within a Layer 3 * protocol header. This header is provided in the buffer at * address *hdr*, with *len* its size in bytes. *type* indicates * the protocol of the header and can be one of: * * **BPF_LWT_ENCAP_SEG6** * IPv6 encapsulation with Segment Routing Header * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, * the IPv6 header is computed by the kernel. * **BPF_LWT_ENCAP_SEG6_INLINE** * Only works if *skb* contains an IPv6 packet. Insert a * Segment Routing Header (**struct ipv6_sr_hdr**) inside * the IPv6 header. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. Only the flags, tag and TLVs * inside the outermost IPv6 Segment Routing Header can be * modified through this helper. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta) * Description * Adjust the size allocated to TLVs in the outermost IPv6 * Segment Routing Header contained in the packet associated to * *skb*, at position *offset* by *delta* bytes. Only offsets * after the segments are accepted. *delta* can be as well * positive (growing) as negative (shrinking). * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len) * Description * Apply an IPv6 Segment Routing action of type *action* to the * packet associated to *skb*. Each action takes a parameter * contained at address *param*, and of length *param_len* bytes. * *action* can be one of: * * **SEG6_LOCAL_ACTION_END_X** * End.X action: Endpoint with Layer-3 cross-connect. * Type of *param*: **struct in6_addr**. * **SEG6_LOCAL_ACTION_END_T** * End.T action: Endpoint with specific IPv6 table lookup. * Type of *param*: **int**. * **SEG6_LOCAL_ACTION_END_B6** * End.B6 action: Endpoint bound to an SRv6 policy. * Type of param: **struct ipv6_sr_hdr**. * **SEG6_LOCAL_ACTION_END_B6_ENCAP** * End.B6.Encap action: Endpoint bound to an SRv6 * encapsulation policy. * Type of param: **struct ipv6_sr_hdr**. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure.