// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2007-2017 Nicira, Inc.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/openvswitch.h>
#include <linux/sctp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in6.h>
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
#include <net/dst.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ip6_fib.h>
#include <net/checksum.h>
#include <net/dsfield.h>
#include <net/mpls.h>
#include <net/sctp/checksum.h>
#include "datapath.h"
#include "flow.h"
#include "conntrack.h"
#include "vport.h"
#include "flow_netlink.h"
struct deferred_action {
struct sk_buff *skb;
const struct nlattr *actions;
int actions_len;
/* Store pkt_key clone when creating deferred action. */
struct sw_flow_key pkt_key;
};
#define MAX_L2_LEN (VLAN_ETH_HLEN + 3 * MPLS_HLEN)
struct ovs_frag_data {
unsigned long dst;
struct vport *vport;
struct ovs_skb_cb cb;
__be16 inner_protocol;
u16 network_offset; /* valid only for MPLS */
u16 vlan_tci;
__be16 vlan_proto;
unsigned int l2_len;
u8 mac_proto;
u8 l2_data[MAX_L2_LEN];
};
static DEFINE_PER_CPU(struct ovs_frag_data, ovs_frag_data_storage);
#define DEFERRED_ACTION_FIFO_SIZE 10
#define OVS_RECURSION_LIMIT 5
#define OVS_DEFERRED_ACTION_THRESHOLD (OVS_RECURSION_LIMIT - 2)
struct action_fifo {
int head;
int tail;
/* Deferred action fifo queue storage. */
struct deferred_action fifo[DEFERRED_ACTION_FIFO_SIZE];
};
struct action_flow_keys {
struct sw_flow_key key[OVS_DEFERRED_ACTION_THRESHOLD];
};
static struct action_fifo __percpu *action_fifos;
static struct action_flow_keys __percpu *flow_keys;
static DEFINE_PER_CPU(int, exec_actions_level);
/* Make a clone of the 'key', using the pre-allocated percpu 'flow_keys'
* space. Return NULL if out of key spaces.
*/
static struct sw_flow_key *clone_key(const struct sw_flow_key *key_)
{
struct action_flow_keys *keys = this_cpu_ptr(flow_keys);
int level = this_cpu_read(exec_actions_level);
struct sw_flow_key *key = NULL;
if (level <= OVS_DEFERRED_ACTION_THRESHOLD) {
key = &keys->key[level - 1];
*key = *key_;
}
return key;
}
static void action_fifo_init(struct action_fifo *fifo)
{
fifo->head = 0;
fifo->tail = 0;
}
static bool action_fifo_is_empty(const struct action_fifo *fifo)
{
return (fifo->head == fifo->tail);
}
static struct deferred_action *action_fifo_get(struct action_fifo *fifo)
{
if (action_fifo_is_empty(fifo))
return NULL;
return &fifo->fifo[fifo->tail++];
}
static struct deferred_action *action_fifo_put(struct action_fifo *fifo)
{
if (fifo->head >= DEFERRED_ACTION_FIFO_SIZE - 1)
return NULL;
return &fifo->fifo[fifo->head++];
}
/* Return true if fifo is not full */
static struct deferred_action *add_deferred_actions(struct sk_buff *skb,
const struct sw_flow_key *key,
const struct nlattr *actions,
const int actions_len)
{
struct action_fifo *fifo;
struct deferred_action *da;
fifo = this_cpu_ptr(action_fifos);
da = action_fifo_put(fifo);
if (da) {
da->skb = skb;
da->actions = actions;
da->actions_len = actions_len;
da->pkt_key = *key;
}
return da;
}
static void invalidate_flow_key(struct sw_flow_key *key)
{
key->mac_proto |= SW_FLOW_KEY_INVALID;
}
static bool is_flow_key_valid(const struct sw_flow_key *key)
{
return !(key->mac_proto & SW_FLOW_KEY_INVALID);
}
static int clone_execute(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key,
u32 recirc_id,
const struct nlattr *actions, int len,
bool last, bool clone_flow_key);
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key,
const struct nlattr *attr, int len);
static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
__be32 mpls_lse, __be16 mpls_ethertype, __u16 mac_len)
{
int err;
err = skb_mpls_push(skb, mpls_lse, mpls_ethertype, mac_len, !!mac_len);
if (err)
return err;
if (!mac_len)
key->mac_proto = MAC_PROTO_NONE;
invalidate_flow_key(key);
return 0;
}
static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
const __be16 ethertype)
{
int err;
err = skb_mpls_pop(skb, ethertype, skb->mac_len,
ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET);
if (err)
return err;
if (ethertype == htons(ETH_P_TEB))
key->mac_proto = MAC_PROTO_ETHERNET;
inval
|