// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
#include <linux/bitfield.h>
#include <linux/mpls.h>
#include <net/pkt_cls.h>
#include <net/tc_act/tc_csum.h>
#include <net/tc_act/tc_gact.h>
#include <net/tc_act/tc_mirred.h>
#include <net/tc_act/tc_mpls.h>
#include <net/tc_act/tc_pedit.h>
#include <net/tc_act/tc_vlan.h>
#include <net/tc_act/tc_tunnel_key.h>
#include "cmsg.h"
#include "main.h"
#include "../nfp_net_repr.h"
/* The kernel versions of TUNNEL_* are not ABI and therefore vulnerable
* to change. Such changes will break our FW ABI.
*/
#define NFP_FL_TUNNEL_CSUM cpu_to_be16(0x01)
#define NFP_FL_TUNNEL_KEY cpu_to_be16(0x04)
#define NFP_FL_TUNNEL_GENEVE_OPT cpu_to_be16(0x0800)
#define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS (IP_TUNNEL_INFO_TX | \
IP_TUNNEL_INFO_IPV6)
#define NFP_FL_SUPPORTED_UDP_TUN_FLAGS (NFP_FL_TUNNEL_CSUM | \
NFP_FL_TUNNEL_KEY | \
NFP_FL_TUNNEL_GENEVE_OPT)
static int
nfp_fl_push_mpls(struct nfp_fl_push_mpls *push_mpls,
const struct flow_action_entry *act,
struct netlink_ext_ack *extack)
{
size_t act_size = sizeof(struct nfp_fl_push_mpls);
u32 mpls_lse = 0;
push_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_MPLS;
push_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
/* BOS is optional in the TC action but required for offload. */
if (act->mpls_push.bos != ACT_MPLS_BOS_NOT_SET) {
mpls_lse |= act->mpls_push.bos << MPLS_LS_S_SHIFT;
} else {
NL_SET_ERR_MSG_MOD(extack, "unsupported offload: BOS field must explicitly be set for MPLS push");
return -EOPNOTSUPP;
}
/* Leave MPLS TC as a default value of 0 if not explicitly set. */
if (act->mpls_push.tc != ACT_MPLS_TC_NOT_SET)
mpls_lse |= act->mpls_push.tc << MPLS_LS_TC_SHIFT;
/* Proto, label and TTL are enforced and verified for MPLS push. */
mpls_lse |= act->mpls_push.label << MPLS_LS_LABEL_SHIFT;
mpls_lse |= act->mpls_push.ttl << MPLS_LS_TTL_SHIFT;
push_mpls->ethtype = act->mpls_push.proto;
push_mpls->lse = cpu_to_be32(mpls_lse);
return 0;
}
static void
nfp_fl_pop_mpls(struct nfp_fl_pop_mpls *pop_mpls,
const struct flow_action_entry *act)
{
size_t act_size = sizeof(struct nfp_fl_pop_mpls);
pop_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_POP_MPLS;
pop_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
pop_mpls->ethtype = act->mpls_pop.proto;
}
static void
nfp_fl_set_mpls(struct nfp_fl_set_mpls *set_mpls,
const struct flow_action_entry *act)
{
size_t act_size = sizeof(struct nfp_fl_set_mpls);
u32 mpls_lse = 0, mpls_mask = 0;
set_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_SET_MPLS;
set_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
if (act->mpls_mangle.label != ACT_MPLS_LABEL_NOT_SET) {
mpls_lse |= act->mpls_mangle.label <<