linux/net/ipv6/seg6_local.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  SR-IPv6 implementation
 *
 *  Authors:
 *  David Lebrun <[email protected]>
 *  eBPF support: Mathieu Xhonneux <[email protected]>
 */

#include <linux/filter.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/net.h>
#include <linux/module.h>
#include <net/ip.h>
#include <net/lwtunnel.h>
#include <net/netevent.h>
#include <net/netns/generic.h>
#include <net/ip6_fib.h>
#include <net/route.h>
#include <net/seg6.h>
#include <linux/seg6.h>
#include <linux/seg6_local.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/dst_cache.h>
#include <net/ip_tunnels.h>
#ifdef CONFIG_IPV6_SEG6_HMAC
#include <net/seg6_hmac.h>
#endif
#include <net/seg6_local.h>
#include <linux/etherdevice.h>
#include <linux/bpf.h>
#include <linux/netfilter.h>

#define SEG6_F_ATTR(i)

struct seg6_local_lwt;

/* callbacks used for customizing the creation and destruction of a behavior */
struct seg6_local_lwtunnel_ops {};

struct seg6_action_desc {};

struct bpf_lwt_prog {};

/* default length values (expressed in bits) for both Locator-Block and
 * Locator-Node Function.
 *
 * Both SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS *must* be:
 *    i) greater than 0;
 *   ii) evenly divisible by 8. In other terms, the lengths of the
 *	 Locator-Block and Locator-Node Function must be byte-aligned (we can
 *	 relax this constraint in the future if really needed).
 *
 * Moreover, a third condition must hold:
 *  iii) SEG6_LOCAL_LCBLOCK_DBITS + SEG6_LOCAL_LCNODE_FN_DBITS <= 128.
 *
 * The correctness of SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS
 * values are checked during the kernel compilation. If the compilation stops,
 * check the value of these parameters to see if they meet conditions (i), (ii)
 * and (iii).
 */
#define SEG6_LOCAL_LCBLOCK_DBITS
#define SEG6_LOCAL_LCNODE_FN_DBITS

/* The following next_csid_chk_{cntr,lcblock,lcblock_fn}_bits macros can be
 * used directly to check whether the lengths (in bits) of Locator-Block and
 * Locator-Node Function are valid according to (i), (ii), (iii).
 */
#define next_csid_chk_cntr_bits(blen, flen)

#define next_csid_chk_lcblock_bits(blen)

#define next_csid_chk_lcnode_fn_bits(flen)

/* flag indicating that flavors are set up for a given End* behavior */
#define SEG6_F_LOCAL_FLAVORS

#define SEG6_F_LOCAL_FLV_OP(flvname)
#define SEG6_F_LOCAL_FLV_NEXT_CSID
#define SEG6_F_LOCAL_FLV_PSP

/* Supported RFC8986 Flavor operations are reported in this bitmask */
#define SEG6_LOCAL_FLV8986_SUPP_OPS

#define SEG6_LOCAL_END_FLV_SUPP_OPS
#define SEG6_LOCAL_END_X_FLV_SUPP_OPS

struct seg6_flavors_info {};

enum seg6_end_dt_mode {};

struct seg6_end_dt_info {};

struct pcpu_seg6_local_counters {};

/* This struct groups all the SRv6 Behavior counters supported so far.
 *
 * put_nla_counters() makes use of this data structure to collect all counter
 * values after the per-CPU counter evaluation has been performed.
 * Finally, each counter value (in seg6_local_counters) is stored in the
 * corresponding netlink attribute and sent to user space.
 *
 * NB: we don't want to expose this structure to user space!
 */
struct seg6_local_counters {};

#define seg6_local_alloc_pcpu_counters(__gfp)

#define SEG6_F_LOCAL_COUNTERS

struct seg6_local_lwt {};

static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt)
{}

static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb)
{}

static bool decap_and_validate(struct sk_buff *skb, int proto)
{}

static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr)
{}

static int
seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
			u32 tbl_id, bool local_delivery)
{}

int seg6_lookup_nexthop(struct sk_buff *skb,
			struct in6_addr *nhaddr, u32 tbl_id)
{}

static __u8 seg6_flv_lcblock_octects(const struct seg6_flavors_info *finfo)
{}

static __u8 seg6_flv_lcnode_func_octects(const struct seg6_flavors_info *finfo)
{}

static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr,
				       const struct seg6_flavors_info *finfo)
{}

/* assume that DA.Argument length > 0 */
static void seg6_next_csid_advance_arg(struct in6_addr *addr,
				       const struct seg6_flavors_info *finfo)
{}

static int input_action_end_finish(struct sk_buff *skb,
				   struct seg6_local_lwt *slwt)
{}

static int input_action_end_core(struct sk_buff *skb,
				 struct seg6_local_lwt *slwt)
{}

static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int input_action_end_x_finish(struct sk_buff *skb,
				     struct seg6_local_lwt *slwt)
{}

static int input_action_end_x_core(struct sk_buff *skb,
				   struct seg6_local_lwt *slwt)
{}

static int end_x_next_csid_core(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

static bool seg6_next_csid_enabled(__u32 fops)
{}

/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through
 * the flavors framework. These behaviors must report the subset of (flavor)
 * operations they currently implement. In this way, if a user specifies a
 * flavor combination that is not supported by a given End* behavior, the
 * kernel refuses to instantiate the tunnel reporting the error.
 */
static int seg6_flv_supp_ops_by_action(int action, __u32 *fops)
{}

/* We describe the packet state in relation to the absence/presence of the SRH
 * and the Segment Left (SL) field.
 * For our purposes, it is not necessary to record the exact value of the SL
 * when the SID List consists of two or more segments.
 */
enum seg6_local_pktinfo {};

#define SEG6_LOCAL_PKTINFO_MAX

static enum seg6_local_pktinfo seg6_get_srh_pktinfo(struct ipv6_sr_hdr *srh)
{}

enum seg6_local_flv_action {};

#define SEG6_LOCAL_FLV_ACT_MAX

/* The action table for RFC8986 flavors (see the flv8986_act_tbl below)
 * contains the actions (i.e. processing operations) to be applied on packets
 * when flavors are configured for an End* behavior.
 * By combining the pkinfo data and from the flavors mask, the macro
 * computes the index used to access the elements (actions) stored in the
 * action table. The index is structured as follows:
 *
 *                     index
 *       _______________/\________________
 *      /                                 \
 *      +----------------+----------------+
 *      |        pf      |      afm       |
 *      +----------------+----------------+
 *        ph-1 ... p1 p0   fk-1 ... f1 f0
 *     MSB                               LSB
 *
 * where:
 *  - 'afm' (adjusted flavor mask) is the mask containing a combination of the
 *     RFC8986 flavors currently supported. 'afm' corresponds to the @fm
 *     argument of the macro whose value is righ-shifted by 1 bit. By doing so,
 *     we discard the SEG6_LOCAL_FLV_OP_UNSPEC flag (bit 0 in @fm) which is
 *     never used here;
 *  - 'pf' encodes the packet info (pktinfo) regarding the presence/absence of
 *    the SRH, SL = 0, etc. 'pf' is set with the value of @pf provided as
 *    argument to the macro.
 */
#define flv8986_act_tbl_idx(pf, fm)

/* We compute the size of the action table by considering the RFC8986 flavors
 * actually supported by the kernel. In this way, the size is automatically
 * adjusted when new flavors are supported.
 */
#define FLV8986_ACT_TBL_SIZE

/* tbl_cfg(act, pf, fm) macro is used to easily configure the action
 * table; it accepts 3 arguments:
 *     i) @act, the suffix from SEG6_LOCAL_FLV_ACT_{act} representing
 *        the action that should be applied on the packet;
 *    ii) @pf, the suffix from SEG6_LOCAL_PKTINFO_{pf} reporting the packet
 *        info about the lack/presence of SRH, SRH with SL = 0, etc;
 *   iii) @fm, the mask of flavors.
 */
#define tbl_cfg

/* shorthand for improving readability */
#define F_PSP

/* The table contains, for each combination of the pktinfo data and
 * flavors, the action that should be taken on a packet (e.g.
 * "standard" Endpoint processing, Penultimate Segment Pop, etc).
 *
 * By default, table entries not explicitly configured are initialized with the
 * SEG6_LOCAL_FLV_ACT_UNSPEC action, which generally has the effect of
 * discarding the processed packet.
 */
static const u8 flv8986_act_tbl[FLV8986_ACT_TBL_SIZE] =;

#undef F_PSP
#undef tbl_cfg

/* For each flavor defined in RFC8986 (or a combination of them) an action is
 * performed on the packet. The specific action depends on:
 *  - info extracted from the packet (i.e. pktinfo data) regarding the
 *    lack/presence of the SRH, and if the SRH is available, on the value of
 *    Segment Left field;
 *  - the mask of flavors configured for the specific SRv6 End* behavior.
 *
 * The function combines both the pkinfo and the flavors mask to evaluate the
 * corresponding action to be taken on the packet.
 */
static enum seg6_local_flv_action
seg6_local_flv8986_act_lookup(enum seg6_local_pktinfo pinfo, __u32 flvmask)
{}

/* skb->data must be aligned with skb->network_header */
static bool seg6_pop_srh(struct sk_buff *skb, int srhoff)
{}

/* process the packet on the basis of the RFC8986 flavors set for the given
 * SRv6 End behavior instance.
 */
static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

/* regular endpoint function */
static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

/* regular endpoint, and forward to specified nexthop */
static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

/* decapsulate and forward inner L2 frame on specified interface */
static int input_action_end_dx2(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

static int input_action_end_dx6_finish(struct net *net, struct sock *sk,
				       struct sk_buff *skb)
{}

/* decapsulate and forward to specified nexthop */
static int input_action_end_dx6(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

static int input_action_end_dx4_finish(struct net *net, struct sock *sk,
				       struct sk_buff *skb)
{}

static int input_action_end_dx4(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

#ifdef CONFIG_NET_L3_MASTER_DEV
static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg)
{}

static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg,
				   u16 family, struct netlink_ext_ack *extack)
{}

/* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and
 * routes the IPv4/IPv6 packet by looking at the configured routing table.
 *
 * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment
 * Routing Header packets) from several interfaces and the outer IPv6
 * destination address (DA) is used for retrieving the specific instance of the
 * End.DT4/DT6 behavior that should process the packets.
 *
 * However, the inner IPv4/IPv6 packet is not really bound to any receiving
 * interface and thus the End.DT4/DT6 sets the VRF (associated with the
 * corresponding routing table) as the *receiving* interface.
 * In other words, the End.DT4/DT6 processes a packet as if it has been received
 * directly by the VRF (and not by one of its slave devices, if any).
 * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in
 * according to the routing table configured by the End.DT4/DT6 instance.
 *
 * This design allows you to get some interesting features like:
 *  1) the statistics on rx packets;
 *  2) the possibility to install a packet sniffer on the receiving interface
 *     (the VRF one) for looking at the incoming packets;
 *  3) the possibility to leverage the netfilter prerouting hook for the inner
 *     IPv4 packet.
 *
 * This function returns:
 *  - the sk_buff* when the VRF rcv handler has processed the packet correctly;
 *  - NULL when the skb is consumed by the VRF rcv handler;
 *  - a pointer which encodes a negative error number in case of error.
 *    Note that in this case, the function takes care of freeing the skb.
 */
static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family,
				      struct net_device *dev)
{}

static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb,
					     struct seg6_end_dt_info *info)
{}

static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
				       struct seg6_local_lwt *slwt, u16 family)
{}

static int input_action_end_dt4(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg,
			      struct netlink_ext_ack *extack)
{}

static enum
seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt)
{}

static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt)
{}

static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg,
			      struct netlink_ext_ack *extack)
{}
#endif

static int input_action_end_dt6(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

#ifdef CONFIG_NET_L3_MASTER_DEV
static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg,
			       struct netlink_ext_ack *extack)
{}

static int input_action_end_dt46(struct sk_buff *skb,
				 struct seg6_local_lwt *slwt)
{}
#endif

/* push an SRH on top of the current one */
static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

/* encapsulate within an outer IPv6 header and a specified SRH */
static int input_action_end_b6_encap(struct sk_buff *skb,
				     struct seg6_local_lwt *slwt)
{}

DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states) =;

bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
{}

static int input_action_end_bpf(struct sk_buff *skb,
				struct seg6_local_lwt *slwt)
{}

static struct seg6_action_desc seg6_action_table[] =;

static struct seg6_action_desc *__get_action_desc(int action)
{}

static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt)
{}

static void seg6_local_update_counters(struct seg6_local_lwt *slwt,
				       unsigned int len, int err)
{}

static int seg6_local_input_core(struct net *net, struct sock *sk,
				 struct sk_buff *skb)
{}

static int seg6_local_input(struct sk_buff *skb)
{}

static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] =;

static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			 struct netlink_ext_ack *extack)
{}

static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static void destroy_attr_srh(struct seg6_local_lwt *slwt)
{}

static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			   struct netlink_ext_ack *extack)
{}

static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static struct
seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt)
{}

static int parse_nla_vrftable(struct nlattr **attrs,
			      struct seg6_local_lwt *slwt,
			      struct netlink_ext_ack *extack)
{}

static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			 struct netlink_ext_ack *extack)
{}

static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			 struct netlink_ext_ack *extack)
{}

static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			 struct netlink_ext_ack *extack)
{}

static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			 struct netlink_ext_ack *extack)
{}

static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

#define MAX_PROG_NAME
static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] =;

static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			 struct netlink_ext_ack *extack)
{}

static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static void destroy_attr_bpf(struct seg6_local_lwt *slwt)
{}

static const struct
nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] =;

static int parse_nla_counters(struct nlattr **attrs,
			      struct seg6_local_lwt *slwt,
			      struct netlink_ext_ack *extack)
{}

static int seg6_local_fill_nla_counters(struct sk_buff *skb,
					struct seg6_local_counters *counters)
{}

static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static void destroy_attr_counters(struct seg6_local_lwt *slwt)
{}

static const
struct nla_policy seg6_local_flavors_policy[SEG6_LOCAL_FLV_MAX + 1] =;

/* check whether the lengths of the Locator-Block and Locator-Node Function
 * are compatible with the dimension of a C-SID container.
 */
static int seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len)
{}

static int seg6_parse_nla_next_csid_cfg(struct nlattr **tb,
					struct seg6_flavors_info *finfo,
					struct netlink_ext_ack *extack)
{}

static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			     struct netlink_ext_ack *extack)
{}

static int seg6_fill_nla_next_csid_cfg(struct sk_buff *skb,
				       struct seg6_flavors_info *finfo)
{}

static int put_nla_flavors(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{}

static int seg6_cmp_nla_next_csid_cfg(struct seg6_flavors_info *finfo_a,
				      struct seg6_flavors_info *finfo_b)
{}

static int cmp_nla_flavors(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
{}

static int encap_size_flavors(struct seg6_local_lwt *slwt)
{}

struct seg6_action_param {};

static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] =;

/* call the destroy() callback (if available) for each set attribute in
 * @parsed_attrs, starting from the first attribute up to the @max_parsed
 * (excluded) attribute.
 */
static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed,
			    struct seg6_local_lwt *slwt)
{}

/* release all the resources that may have been acquired during parsing
 * operations.
 */
static void destroy_attrs(struct seg6_local_lwt *slwt)
{}

static int parse_nla_optional_attrs(struct nlattr **attrs,
				    struct seg6_local_lwt *slwt,
				    struct netlink_ext_ack *extack)
{}

/* call the custom constructor of the behavior during its initialization phase
 * and after that all its attributes have been parsed successfully.
 */
static int
seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg,
				struct netlink_ext_ack *extack)
{}

/* call the custom destructor of the behavior which is invoked before the
 * tunnel is going to be destroyed.
 */
static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt)
{}

static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt,
			    struct netlink_ext_ack *extack)
{}

static int seg6_local_build_state(struct net *net, struct nlattr *nla,
				  unsigned int family, const void *cfg,
				  struct lwtunnel_state **ts,
				  struct netlink_ext_ack *extack)
{}

static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
{}

static int seg6_local_fill_encap(struct sk_buff *skb,
				 struct lwtunnel_state *lwt)
{}

static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
{}

static int seg6_local_cmp_encap(struct lwtunnel_state *a,
				struct lwtunnel_state *b)
{}

static const struct lwtunnel_encap_ops seg6_local_ops =;

int __init seg6_local_init(void)
{}

void seg6_local_exit(void)
{}