2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 13:58:14 +00:00

datapath: Convert datapath operations to use Netlink framing.

Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
This commit is contained in:
Ben Pfaff
2011-01-26 15:42:00 -08:00
parent 9c52546b52
commit d656937779
6 changed files with 1455 additions and 991 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -399,15 +399,15 @@ int flow_cmp(const struct tbl_node *node, void *key2_)
/** /**
* flow_from_nlattrs - parses Netlink attributes into a flow key. * flow_from_nlattrs - parses Netlink attributes into a flow key.
* @swkey: receives the extracted flow key. * @swkey: receives the extracted flow key.
* @key: start of %ODP_KEY_ATTR_* Netlink attribute sequence. * @key: Netlink attribute holding nested %ODP_KEY_ATTR_* Netlink attribute
* @key_len: number of bytes in @key. * sequence.
* *
* This state machine accepts the following forms, with [] for optional * This state machine accepts the following forms, with [] for optional
* elements and | for alternatives: * elements and | for alternatives:
* *
* [tun_id] in_port ethernet [8021q] [ethertype [IP [TCP|UDP|ICMP] | ARP] * [tun_id] in_port ethernet [8021q] [ethertype [IP [TCP|UDP|ICMP] | ARP]
*/ */
static int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *key, u32 key_len) int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
{ {
const struct nlattr *nla; const struct nlattr *nla;
u16 prev_type; u16 prev_type;
@@ -417,7 +417,7 @@ static int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *key
swkey->dl_type = htons(ETH_P_802_2); swkey->dl_type = htons(ETH_P_802_2);
prev_type = ODP_KEY_ATTR_UNSPEC; prev_type = ODP_KEY_ATTR_UNSPEC;
nla_for_each_attr(nla, key, key_len, rem) { nla_for_each_nested(nla, attr, rem) {
static const u32 key_lens[ODP_KEY_ATTR_MAX + 1] = { static const u32 key_lens[ODP_KEY_ATTR_MAX + 1] = {
[ODP_KEY_ATTR_TUN_ID] = 8, [ODP_KEY_ATTR_TUN_ID] = 8,
[ODP_KEY_ATTR_IN_PORT] = 4, [ODP_KEY_ATTR_IN_PORT] = 4,
@@ -572,39 +572,43 @@ static int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *key
return -EINVAL; return -EINVAL;
} }
u32 flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
{ {
struct odp_key_ethernet *eth_key; struct odp_key_ethernet *eth_key;
struct nlattr *nla;
if (skb_tailroom(skb) < FLOW_BUFSIZE)
return -EMSGSIZE;
if (swkey->tun_id != cpu_to_be64(0)) if (swkey->tun_id != cpu_to_be64(0))
nla_put_be64(skb, ODP_KEY_ATTR_TUN_ID, swkey->tun_id); NLA_PUT_BE64(skb, ODP_KEY_ATTR_TUN_ID, swkey->tun_id);
nla_put_u32(skb, ODP_KEY_ATTR_IN_PORT, swkey->in_port); NLA_PUT_U32(skb, ODP_KEY_ATTR_IN_PORT, swkey->in_port);
eth_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_ETHERNET, sizeof(*eth_key))); nla = nla_reserve(skb, ODP_KEY_ATTR_ETHERNET, sizeof(*eth_key));
if (!nla)
goto nla_put_failure;
eth_key = nla_data(nla);
memcpy(eth_key->eth_src, swkey->dl_src, ETH_ALEN); memcpy(eth_key->eth_src, swkey->dl_src, ETH_ALEN);
memcpy(eth_key->eth_dst, swkey->dl_dst, ETH_ALEN); memcpy(eth_key->eth_dst, swkey->dl_dst, ETH_ALEN);
if (swkey->dl_tci != htons(0)) { if (swkey->dl_tci != htons(0)) {
struct odp_key_8021q *q_key; struct odp_key_8021q q_key;
q_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_8021Q, sizeof(*q_key))); q_key.q_tpid = htons(ETH_P_8021Q);
q_key->q_tpid = htons(ETH_P_8021Q); q_key.q_tci = swkey->dl_tci & ~htons(VLAN_TAG_PRESENT);
q_key->q_tci = swkey->dl_tci & ~htons(VLAN_TAG_PRESENT); NLA_PUT(skb, ODP_KEY_ATTR_8021Q, sizeof(q_key), &q_key);
} }
if (swkey->dl_type == htons(ETH_P_802_2)) if (swkey->dl_type == htons(ETH_P_802_2))
goto exit; return 0;
nla_put_be16(skb, ODP_KEY_ATTR_ETHERTYPE, swkey->dl_type); NLA_PUT_BE16(skb, ODP_KEY_ATTR_ETHERTYPE, swkey->dl_type);
if (swkey->dl_type == htons(ETH_P_IP)) { if (swkey->dl_type == htons(ETH_P_IP)) {
struct odp_key_ipv4 *ipv4_key; struct odp_key_ipv4 *ipv4_key;
ipv4_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_IPV4, sizeof(*ipv4_key))); nla = nla_reserve(skb, ODP_KEY_ATTR_IPV4, sizeof(*ipv4_key));
if (!nla)
goto nla_put_failure;
ipv4_key = nla_data(nla);
ipv4_key->ipv4_src = swkey->nw_src; ipv4_key->ipv4_src = swkey->nw_src;
ipv4_key->ipv4_dst = swkey->nw_dst; ipv4_key->ipv4_dst = swkey->nw_dst;
ipv4_key->ipv4_proto = swkey->nw_proto; ipv4_key->ipv4_proto = swkey->nw_proto;
@@ -613,63 +617,47 @@ u32 flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
if (swkey->nw_proto == IPPROTO_TCP) { if (swkey->nw_proto == IPPROTO_TCP) {
struct odp_key_tcp *tcp_key; struct odp_key_tcp *tcp_key;
tcp_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_TCP, sizeof(*tcp_key))); nla = nla_reserve(skb, ODP_KEY_ATTR_TCP, sizeof(*tcp_key));
if (!nla)
goto nla_put_failure;
tcp_key = nla_data(nla);
tcp_key->tcp_src = swkey->tp_src; tcp_key->tcp_src = swkey->tp_src;
tcp_key->tcp_dst = swkey->tp_dst; tcp_key->tcp_dst = swkey->tp_dst;
} else if (swkey->nw_proto == IPPROTO_UDP) { } else if (swkey->nw_proto == IPPROTO_UDP) {
struct odp_key_udp *udp_key; struct odp_key_udp *udp_key;
udp_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_UDP, sizeof(*udp_key))); nla = nla_reserve(skb, ODP_KEY_ATTR_UDP, sizeof(*udp_key));
if (!nla)
goto nla_put_failure;
udp_key = nla_data(nla);
udp_key->udp_src = swkey->tp_src; udp_key->udp_src = swkey->tp_src;
udp_key->udp_dst = swkey->tp_dst; udp_key->udp_dst = swkey->tp_dst;
} else if (swkey->nw_proto == IPPROTO_ICMP) { } else if (swkey->nw_proto == IPPROTO_ICMP) {
struct odp_key_icmp *icmp_key; struct odp_key_icmp *icmp_key;
icmp_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_ICMP, sizeof(*icmp_key))); nla = nla_reserve(skb, ODP_KEY_ATTR_ICMP, sizeof(*icmp_key));
if (!nla)
goto nla_put_failure;
icmp_key = nla_data(nla);
icmp_key->icmp_type = ntohs(swkey->tp_src); icmp_key->icmp_type = ntohs(swkey->tp_src);
icmp_key->icmp_code = ntohs(swkey->tp_dst); icmp_key->icmp_code = ntohs(swkey->tp_dst);
} }
} else if (swkey->dl_type == htons(ETH_P_ARP)) { } else if (swkey->dl_type == htons(ETH_P_ARP)) {
struct odp_key_arp *arp_key; struct odp_key_arp *arp_key;
arp_key = nla_data(__nla_reserve(skb, ODP_KEY_ATTR_ARP, sizeof(*arp_key))); nla = nla_reserve(skb, ODP_KEY_ATTR_ARP, sizeof(*arp_key));
if (!nla)
goto nla_put_failure;
arp_key = nla_data(nla);
arp_key->arp_sip = swkey->nw_src; arp_key->arp_sip = swkey->nw_src;
arp_key->arp_tip = swkey->nw_dst; arp_key->arp_tip = swkey->nw_dst;
arp_key->arp_op = htons(swkey->nw_proto); arp_key->arp_op = htons(swkey->nw_proto);
} }
exit: return 0;
return skb->len;
}
int flow_copy_from_user(struct sw_flow_key *swkey, const struct nlattr __user *ukey, u32 ukey_len) nla_put_failure:
{ return -EMSGSIZE;
char key[FLOW_BUFSIZE] __aligned(NLA_ALIGNTO);
if (ukey_len > FLOW_BUFSIZE || ukey_len % NLA_ALIGNTO)
return -EINVAL;
if (copy_from_user(key, ukey, ukey_len))
return -EFAULT;
return flow_from_nlattrs(swkey, (const struct nlattr *)key, ukey_len);
}
int flow_copy_to_user(struct nlattr __user *ukey, const struct sw_flow_key *swkey, u32 ukey_len)
{
struct sk_buff *skb;
int retval;
skb = alloc_skb(FLOW_BUFSIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
retval = flow_to_nlattrs(swkey, skb);
if (copy_to_user(ukey, skb->data, min(skb->len, ukey_len)))
retval = -EFAULT;
kfree_skb(skb);
return retval;
} }
/* Initializes the flow module. /* Initializes the flow module.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009, 2010 Nicira Networks. * Copyright (c) 2009, 2010, 2011 Nicira Networks.
* Distributed under the terms of the GNU GPL version 2. * Distributed under the terms of the GNU GPL version 2.
* *
* Significant portions of this file may be copied from parts of the Linux * Significant portions of this file may be copied from parts of the Linux
@@ -100,9 +100,8 @@ int flow_cmp(const struct tbl_node *, void *target);
*/ */
#define FLOW_BUFSIZE 96 #define FLOW_BUFSIZE 96
u32 flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *); int flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
int flow_copy_from_user(struct sw_flow_key *, const struct nlattr __user *ukey, u32 key_len); int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *);
int flow_copy_to_user(struct nlattr __user *ukey, const struct sw_flow_key *, u32 key_len);
static inline struct sw_flow *flow_cast(const struct tbl_node *node) static inline struct sw_flow *flow_cast(const struct tbl_node *node)
{ {

View File

@@ -15,32 +15,7 @@
#include "openvswitch/datapath-protocol.h" #include "openvswitch/datapath-protocol.h"
#include <linux/compat.h> #include <linux/compat.h>
#define ODP_FLOW_GET32 _IOWR('O', 13, struct compat_odp_flow)
#define ODP_FLOW_PUT32 _IOWR('O', 14, struct compat_odp_flow)
#define ODP_FLOW_DUMP32 _IOWR('O', 15, struct compat_odp_flow_dump)
#define ODP_FLOW_DEL32 _IOWR('O', 17, struct compat_odp_flow)
#define ODP_EXECUTE32 _IOR('O', 18, struct compat_odp_execute) #define ODP_EXECUTE32 _IOR('O', 18, struct compat_odp_execute)
#define ODP_FLOW_DEL32 _IOWR('O', 17, struct compat_odp_flow)
struct compat_odp_flow {
uint32_t dp_idx;
struct odp_flow_stats stats;
compat_uptr_t key;
u32 key_len;
compat_uptr_t actions;
u32 actions_len;
};
struct compat_odp_flow_put {
struct compat_odp_flow flow;
u32 flags;
};
struct compat_odp_flow_dump {
uint32_t dp_idx;
compat_uptr_t flow;
uint32_t state[2];
};
struct compat_odp_execute { struct compat_odp_execute {
uint32_t dp_idx; uint32_t dp_idx;

View File

@@ -70,14 +70,11 @@
#include <linux/if_link.h> #include <linux/if_link.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#define ODP_MAX 256 /* Maximum number of datapaths. */ #define ODP_DP_NEW _IOWR('O', 0, struct odp_datapath)
#define ODP_DP_DEL _IOR('O', 1, struct odp_datapath)
#define ODP_DP_CREATE _IO('O', 0) #define ODP_DP_GET _IOWR('O', 2, struct odp_datapath)
#define ODP_DP_DESTROY _IO('O', 1) #define ODP_DP_SET _IOWR('O', 3, struct odp_datapath)
#define ODP_DP_STATS _IOW('O', 2, struct odp_stats) #define ODP_DP_DUMP _IOWR('O', 4, struct odp_datapath)
#define ODP_GET_DROP_FRAGS _IOW('O', 3, int)
#define ODP_SET_DROP_FRAGS _IOR('O', 4, int)
#define ODP_GET_LISTEN_MASK _IOW('O', 5, int) #define ODP_GET_LISTEN_MASK _IOW('O', 5, int)
#define ODP_SET_LISTEN_MASK _IOR('O', 6, int) #define ODP_SET_LISTEN_MASK _IOR('O', 6, int)
@@ -88,16 +85,48 @@
#define ODP_VPORT_SET _IOR('O', 22, struct odp_vport) #define ODP_VPORT_SET _IOR('O', 22, struct odp_vport)
#define ODP_VPORT_DUMP _IOWR('O', 10, struct odp_vport) #define ODP_VPORT_DUMP _IOWR('O', 10, struct odp_vport)
#define ODP_FLOW_GET _IOWR('O', 13, struct odp_flow) #define ODP_FLOW_NEW _IOWR('O', 13, struct odp_flow)
#define ODP_FLOW_PUT _IOWR('O', 14, struct odp_flow) #define ODP_FLOW_DEL _IOWR('O', 14, struct odp_flow)
#define ODP_FLOW_DUMP _IOWR('O', 15, struct odp_flow_dump) #define ODP_FLOW_GET _IOWR('O', 15, struct odp_flow)
#define ODP_FLOW_FLUSH _IO('O', 16) #define ODP_FLOW_SET _IOWR('O', 16, struct odp_flow)
#define ODP_FLOW_DEL _IOWR('O', 17, struct odp_flow) #define ODP_FLOW_DUMP _IOWR('O', 17, struct odp_flow)
#define ODP_FLOW_FLUSH _IO('O', 19)
#define ODP_EXECUTE _IOR('O', 18, struct odp_execute) #define ODP_EXECUTE _IOR('O', 18, struct odp_execute)
#define ODP_SET_SFLOW_PROBABILITY _IOR('O', 19, int) /**
#define ODP_GET_SFLOW_PROBABILITY _IOW('O', 20, int) * struct odp_datapath - header with basic information about a datapath.
* @dp_idx: Datapath index (-1 to make a request not specific to a datapath).
* @len: Length of this structure plus the Netlink attributes following it.
* @total_len: Total space available for kernel reply to request.
*
* Followed by &struct nlattr attributes, whose types are drawn from
* %ODP_DP_ATTR_*, up to a length of @len bytes including the &struct
* odp_datapath header.
*/
struct odp_datapath {
int32_t dp_idx;
uint32_t len;
uint32_t total_len;
};
enum odp_datapath_type {
ODP_DP_ATTR_UNSPEC,
ODP_DP_ATTR_NAME, /* name of dp_ifidx netdev */
ODP_DP_ATTR_STATS, /* struct odp_stats */
ODP_DP_ATTR_IPV4_FRAGS, /* 32-bit enum odp_frag_handling */
ODP_DP_ATTR_SAMPLING, /* 32-bit fraction of packets to sample. */
__ODP_DP_ATTR_MAX
};
#define ODP_DP_ATTR_MAX (__ODP_DP_ATTR_MAX - 1)
/* Values for ODP_DP_ATTR_IPV4_FRAGS. */
enum odp_frag_handling {
ODP_DP_FRAG_UNSPEC,
ODP_DP_FRAG_ZERO, /* Treat IP fragments as transport port 0. */
ODP_DP_FRAG_DROP /* Drop IP fragments. */
};
struct odp_stats { struct odp_stats {
uint64_t n_frags; /* Number of dropped IP fragments. */ uint64_t n_frags; /* Number of dropped IP fragments. */
@@ -210,10 +239,6 @@ enum {
struct odp_flow_stats { struct odp_flow_stats {
uint64_t n_packets; /* Number of matched packets. */ uint64_t n_packets; /* Number of matched packets. */
uint64_t n_bytes; /* Number of matched bytes. */ uint64_t n_bytes; /* Number of matched bytes. */
uint64_t used_sec; /* Time last used, in system monotonic time. */
uint32_t used_nsec;
uint8_t tcp_flags;
uint8_t reserved;
}; };
enum odp_key_type { enum odp_key_type {
@@ -271,42 +296,37 @@ struct odp_key_arp {
ovs_be16 arp_op; ovs_be16 arp_op;
}; };
struct odp_flow { /**
uint32_t dp_idx; * struct odp_flow - header with basic information about a flow.
struct odp_flow_stats stats; * @dp_idx: Datapath index.
struct nlattr *key; * @len: Length of this structure plus the Netlink attributes following it.
uint32_t key_len; * @total_len: Total space available for kernel reply to request.
struct nlattr *actions;
uint32_t actions_len;
};
/* Flags for ODP_FLOW_PUT. */
#define ODPPF_CREATE (1 << 0) /* Allow creating a new flow. */
#define ODPPF_MODIFY (1 << 1) /* Allow modifying an existing flow. */
#define ODPPF_ZERO_STATS (1 << 2) /* Zero the stats of an existing flow. */
/* ODP_FLOW_PUT argument. */
struct odp_flow_put {
struct odp_flow flow;
uint32_t flags;
};
/* ODP_FLOW_DUMP argument.
* *
* This is used to iterate through the flow table flow-by-flow. Each * Followed by &struct nlattr attributes, whose types are drawn from
* ODP_FLOW_DUMP call either stores a new odp_flow into 'flow' or stores 0 into * %ODP_FLOW_ATTR_*, up to a length of @len bytes including the &struct
* flow->key_len to indicate that the end of the table has been reached, and * odp_flow header.
* updates 'state' in-place.
*
* Before the first call, zero 'state'. The format of 'state' is otherwise
* unspecified.
*/ */
struct odp_flow_dump { struct odp_flow {
uint32_t nlmsg_flags;
uint32_t dp_idx; uint32_t dp_idx;
struct odp_flow *flow; uint32_t len;
uint32_t state[2]; uint32_t total_len;
}; };
enum odp_flow_type {
ODP_FLOW_ATTR_UNSPEC,
ODP_FLOW_ATTR_KEY, /* Sequence of ODP_KEY_ATTR_* attributes. */
ODP_FLOW_ATTR_ACTIONS, /* Sequence of nested ODPAT_* attributes. */
ODP_FLOW_ATTR_STATS, /* struct odp_flow_stats. */
ODP_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */
ODP_FLOW_ATTR_USED, /* u64 msecs last used in monotonic time. */
ODP_FLOW_ATTR_CLEAR, /* Flag to clear stats, tcp_flags, used. */
ODP_FLOW_ATTR_STATE, /* u64 state for ODP_FLOW_DUMP. */
__ODP_FLOW_ATTR_MAX
};
#define ODP_FLOW_ATTR_MAX (__ODP_FLOW_ATTR_MAX - 1)
/* Action types. */ /* Action types. */
enum odp_action_type { enum odp_action_type {
ODPAT_UNSPEC, ODPAT_UNSPEC,

View File

@@ -47,11 +47,64 @@
#include "rtnetlink-link.h" #include "rtnetlink-link.h"
#include "shash.h" #include "shash.h"
#include "svec.h" #include "svec.h"
#include "unaligned.h"
#include "util.h" #include "util.h"
#include "vlog.h" #include "vlog.h"
VLOG_DEFINE_THIS_MODULE(dpif_linux); VLOG_DEFINE_THIS_MODULE(dpif_linux);
struct dpif_linux_dp {
/* ioctl command argument. */
int cmd;
/* struct odp_datapath header. */
uint32_t dp_idx;
/* Attributes. */
const char *name; /* ODP_DP_ATTR_NAME. */
struct odp_stats stats; /* ODP_DP_ATTR_STATS. */
enum odp_frag_handling ipv4_frags; /* ODP_DP_ATTR_IPV4_FRAGS. */
const uint32_t *sampling; /* ODP_DP_ATTR_SAMPLING. */
};
static void dpif_linux_dp_init(struct dpif_linux_dp *);
static int dpif_linux_dp_transact(const struct dpif_linux_dp *request,
struct dpif_linux_dp *reply,
struct ofpbuf **bufp);
static int dpif_linux_dp_get(const struct dpif *, struct dpif_linux_dp *reply,
struct ofpbuf **bufp);
struct dpif_linux_flow {
/* ioctl command argument. */
int cmd;
/* struct odp_flow header. */
unsigned int nlmsg_flags;
uint32_t dp_idx;
/* Attributes.
*
* The 'stats', 'used', and 'state' members point to 64-bit data that might
* only be aligned on 32-bit boundaries, so get_unaligned_u64() should be
* used to access their values. */
const struct nlattr *key; /* ODP_FLOW_ATTR_KEY. */
size_t key_len;
const struct nlattr *actions; /* ODP_FLOW_ATTR_ACTIONS. */
size_t actions_len;
const struct odp_flow_stats *stats; /* ODP_FLOW_ATTR_STATS. */
const uint8_t *tcp_flags; /* ODP_FLOW_ATTR_TCP_FLAGS. */
const uint64_t *used; /* ODP_FLOW_ATTR_USED. */
bool clear; /* ODP_FLOW_ATTR_CLEAR. */
const uint64_t *state; /* ODP_FLOW_ATTR_STATE. */
};
static void dpif_linux_flow_init(struct dpif_linux_flow *);
static int dpif_linux_flow_transact(const struct dpif_linux_flow *request,
struct dpif_linux_flow *reply,
struct ofpbuf **bufp);
static void dpif_linux_flow_get_stats(const struct dpif_linux_flow *,
struct dpif_flow_stats *);
/* Datapath interface for the openvswitch Linux kernel module. */ /* Datapath interface for the openvswitch Linux kernel module. */
struct dpif_linux { struct dpif_linux {
struct dpif dpif; struct dpif dpif;
@@ -74,7 +127,6 @@ static int do_ioctl(const struct dpif *, int cmd, const void *arg);
static int open_dpif(const struct dpif_linux_vport *local_vport, static int open_dpif(const struct dpif_linux_vport *local_vport,
struct dpif **); struct dpif **);
static int get_openvswitch_major(void); static int get_openvswitch_major(void);
static int create_minor(const char *name, int minor);
static int open_minor(int minor, int *fdp); static int open_minor(int minor, int *fdp);
static int make_openvswitch_device(int minor, char **fnp); static int make_openvswitch_device(int minor, char **fnp);
static void dpif_linux_port_changed(const struct rtnetlink_link_change *, static void dpif_linux_port_changed(const struct rtnetlink_link_change *,
@@ -90,9 +142,8 @@ dpif_linux_cast(const struct dpif *dpif)
static int static int
dpif_linux_enumerate(struct svec *all_dps) dpif_linux_enumerate(struct svec *all_dps)
{ {
uint32_t dp_idx;
int major; int major;
int error;
int i;
/* Check that the Open vSwitch module is loaded. */ /* Check that the Open vSwitch module is loaded. */
major = get_openvswitch_major(); major = get_openvswitch_major();
@@ -100,22 +151,28 @@ dpif_linux_enumerate(struct svec *all_dps)
return -major; return -major;
} }
error = 0; dp_idx = 0;
for (i = 0; i < ODP_MAX; i++) { for (;;) {
struct dpif *dpif; struct dpif_linux_dp request, reply;
struct ofpbuf *buf;
char devname[16]; char devname[16];
int retval; int error;
sprintf(devname, "dp%d", i); dpif_linux_dp_init(&request);
retval = dpif_open(devname, "system", &dpif); request.dp_idx = dp_idx;
if (!retval) { request.cmd = ODP_DP_DUMP;
svec_add(all_dps, devname);
dpif_uninit(dpif, true); error = dpif_linux_dp_transact(&request, &reply, &buf);
} else if (retval != ENODEV && !error) { if (error) {
error = retval; return error == ENODEV ? 0 : error;
} }
ofpbuf_delete(buf);
sprintf(devname, "dp%d", reply.dp_idx);
svec_add(all_dps, devname);
dp_idx = reply.dp_idx + 1;
} }
return error;
} }
static int static int
@@ -130,27 +187,20 @@ dpif_linux_open(const struct dpif_class *class OVS_UNUSED, const char *name,
minor = !strncmp(name, "dp", 2) minor = !strncmp(name, "dp", 2)
&& isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1; && isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1;
if (create) { if (create) {
if (minor >= 0) { struct dpif_linux_dp request, reply;
error = create_minor(name, minor); struct ofpbuf *buf;
if (error) { int error;
return error;
}
} else {
/* Scan for unused minor number. */
for (minor = 0; ; minor++) {
if (minor >= ODP_MAX) {
/* All datapath numbers in use. */
return ENOBUFS;
}
error = create_minor(name, minor); dpif_linux_dp_init(&request);
if (!error) { request.cmd = ODP_DP_NEW;
break; request.dp_idx = minor;
} else if (error != EBUSY) { request.name = name;
return error; error = dpif_linux_dp_transact(&request, &reply, &buf);
} if (error) {
} return error;
} }
minor = reply.dp_idx;
ofpbuf_delete(buf);
} }
dpif_linux_vport_init(&request); dpif_linux_vport_init(&request);
@@ -245,25 +295,41 @@ dpif_linux_get_all_names(const struct dpif *dpif_, struct svec *all_names)
static int static int
dpif_linux_destroy(struct dpif *dpif_) dpif_linux_destroy(struct dpif *dpif_)
{ {
return do_ioctl(dpif_, ODP_DP_DESTROY, NULL); struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct dpif_linux_dp dp;
dpif_linux_dp_init(&dp);
dp.cmd = ODP_DP_DEL;
dp.dp_idx = dpif->minor;
return dpif_linux_dp_transact(&dp, NULL, NULL);
} }
static int static int
dpif_linux_get_stats(const struct dpif *dpif_, struct odp_stats *stats) dpif_linux_get_stats(const struct dpif *dpif_, struct odp_stats *stats)
{ {
memset(stats, 0, sizeof *stats); struct dpif_linux_dp dp;
return do_ioctl(dpif_, ODP_DP_STATS, stats); struct ofpbuf *buf;
int error;
error = dpif_linux_dp_get(dpif_, &dp, &buf);
if (!error) {
*stats = dp.stats;
ofpbuf_delete(buf);
}
return error;
} }
static int static int
dpif_linux_get_drop_frags(const struct dpif *dpif_, bool *drop_fragsp) dpif_linux_get_drop_frags(const struct dpif *dpif_, bool *drop_fragsp)
{ {
int drop_frags; struct dpif_linux_dp dp;
struct ofpbuf *buf;
int error; int error;
error = do_ioctl(dpif_, ODP_GET_DROP_FRAGS, &drop_frags); error = dpif_linux_dp_get(dpif_, &dp, &buf);
if (!error) { if (!error) {
*drop_fragsp = drop_frags & 1; *drop_fragsp = dp.ipv4_frags == ODP_DP_FRAG_DROP;
ofpbuf_delete(buf);
} }
return error; return error;
} }
@@ -271,8 +337,14 @@ dpif_linux_get_drop_frags(const struct dpif *dpif_, bool *drop_fragsp)
static int static int
dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags) dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags)
{ {
int drop_frags_int = drop_frags; struct dpif_linux *dpif = dpif_linux_cast(dpif_);
return do_ioctl(dpif_, ODP_SET_DROP_FRAGS, &drop_frags_int); struct dpif_linux_dp dp;
dpif_linux_dp_init(&dp);
dp.cmd = ODP_DP_SET;
dp.dp_idx = dpif->minor;
dp.ipv4_frags = drop_frags ? ODP_DP_FRAG_DROP : ODP_DP_FRAG_ZERO;
return dpif_linux_dp_transact(&dp, NULL, NULL);
} }
static int static int
@@ -460,48 +532,32 @@ dpif_linux_port_poll_wait(const struct dpif *dpif_)
} }
} }
static void
odp_flow_stats_to_dpif_flow_stats(const struct odp_flow_stats *ofs,
struct dpif_flow_stats *dfs)
{
dfs->n_packets = ofs->n_packets;
dfs->n_bytes = ofs->n_bytes;
dfs->used = ofs->used_sec * 1000 + ofs->used_nsec / 1000000;
dfs->tcp_flags = ofs->tcp_flags;
}
static int static int
dpif_linux_flow_get(const struct dpif *dpif_, dpif_linux_flow_get(const struct dpif *dpif_,
const struct nlattr *key, size_t key_len, const struct nlattr *key, size_t key_len,
struct ofpbuf **actionsp, struct dpif_flow_stats *stats) struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
{ {
struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct ofpbuf *actions = NULL; struct dpif_linux_flow request, reply;
struct odp_flow odp_flow; struct ofpbuf *buf;
int error; int error;
memset(&odp_flow, 0, sizeof odp_flow); dpif_linux_flow_init(&request);
odp_flow.dp_idx = dpif->minor; request.cmd = ODP_FLOW_GET;
odp_flow.key = (struct nlattr *) key; request.dp_idx = dpif->minor;
odp_flow.key_len = key_len; request.key = key;
if (actionsp) { request.key_len = key_len;
actions = *actionsp = ofpbuf_new(65536); error = dpif_linux_flow_transact(&request, &reply, &buf);
odp_flow.actions = actions->base;
odp_flow.actions_len = actions->allocated;
}
error = do_ioctl(dpif_, ODP_FLOW_GET, &odp_flow);
if (!error) { if (!error) {
if (stats) { if (stats) {
odp_flow_stats_to_dpif_flow_stats(&odp_flow.stats, stats); dpif_linux_flow_get_stats(&reply, stats);
} }
if (actions) { if (actionsp) {
actions->size = odp_flow.actions_len; buf->data = (void *) reply.actions;
ofpbuf_trim(actions); buf->size = reply.actions_len;
} *actionsp = buf;
} else { } else {
if (actions) { ofpbuf_delete(buf);
ofpbuf_delete(actions);
} }
} }
return error; return error;
@@ -514,28 +570,27 @@ dpif_linux_flow_put(struct dpif *dpif_, enum dpif_flow_put_flags flags,
struct dpif_flow_stats *stats) struct dpif_flow_stats *stats)
{ {
struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct odp_flow_put put; struct dpif_linux_flow request, reply;
struct ofpbuf *buf;
int error; int error;
memset(&put, 0, sizeof put); dpif_linux_flow_init(&request);
put.flow.dp_idx = dpif->minor; request.cmd = flags & DPIF_FP_CREATE ? ODP_FLOW_NEW : ODP_FLOW_SET;
put.flow.key = (struct nlattr *) key; request.dp_idx = dpif->minor;
put.flow.key_len = key_len; request.key = key;
put.flow.actions = (struct nlattr *) actions; request.key_len = key_len;
put.flow.actions_len = actions_len; request.actions = actions;
put.flags = 0; request.actions_len = actions_len;
if (flags & DPIF_FP_CREATE) {
put.flags |= ODPPF_CREATE;
}
if (flags & DPIF_FP_MODIFY) {
put.flags |= ODPPF_MODIFY;
}
if (flags & DPIF_FP_ZERO_STATS) { if (flags & DPIF_FP_ZERO_STATS) {
put.flags |= ODPPF_ZERO_STATS; request.clear = true;
} }
error = do_ioctl(dpif_, ODP_FLOW_PUT, &put); request.nlmsg_flags = flags & DPIF_FP_MODIFY ? 0 : NLM_F_CREATE;
error = dpif_linux_flow_transact(&request,
stats ? &reply : NULL,
stats ? &buf : NULL);
if (!error && stats) { if (!error && stats) {
odp_flow_stats_to_dpif_flow_stats(&put.flow.stats, stats); dpif_linux_flow_get_stats(&reply, stats);
ofpbuf_delete(buf);
} }
return error; return error;
} }
@@ -546,84 +601,81 @@ dpif_linux_flow_del(struct dpif *dpif_,
struct dpif_flow_stats *stats) struct dpif_flow_stats *stats)
{ {
struct dpif_linux *dpif = dpif_linux_cast(dpif_); struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct odp_flow odp_flow; struct dpif_linux_flow request, reply;
struct ofpbuf *buf;
int error; int error;
memset(&odp_flow, 0, sizeof odp_flow); dpif_linux_flow_init(&request);
odp_flow.dp_idx = dpif->minor; request.cmd = ODP_FLOW_DEL;
odp_flow.key = (struct nlattr *) key; request.dp_idx = dpif->minor;
odp_flow.key_len = key_len; request.key = key;
error = do_ioctl(dpif_, ODP_FLOW_DEL, &odp_flow); request.key_len = key_len;
error = dpif_linux_flow_transact(&request,
stats ? &reply : NULL,
stats ? &buf : NULL);
if (!error && stats) { if (!error && stats) {
odp_flow_stats_to_dpif_flow_stats(&odp_flow.stats, stats); dpif_linux_flow_get_stats(&reply, stats);
ofpbuf_delete(buf);
} }
return error; return error;
} }
struct dpif_linux_flow_state { struct dpif_linux_flow_state {
struct odp_flow_dump dump; struct dpif_linux_flow flow;
struct odp_flow flow; struct ofpbuf *buf;
uint32_t keybuf[ODPUTIL_FLOW_KEY_U32S];
uint32_t actionsbuf[65536 / sizeof(uint32_t)];
struct dpif_flow_stats stats; struct dpif_flow_stats stats;
}; };
static int static int
dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep) dpif_linux_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
{ {
struct dpif_linux *dpif = dpif_linux_cast(dpif_); *statep = xzalloc(sizeof(struct dpif_linux_flow_state));
struct dpif_linux_flow_state *state;
*statep = state = xmalloc(sizeof *state);
state->dump.dp_idx = dpif->minor;
state->dump.state[0] = 0;
state->dump.state[1] = 0;
state->dump.flow = &state->flow;
return 0; return 0;
} }
static int static int
dpif_linux_flow_dump_next(const struct dpif *dpif, void *state_, dpif_linux_flow_dump_next(const struct dpif *dpif_, void *state_,
const struct nlattr **key, size_t *key_len, const struct nlattr **key, size_t *key_len,
const struct nlattr **actions, size_t *actions_len, const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats) const struct dpif_flow_stats **stats)
{ {
struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct dpif_linux_flow_state *state = state_; struct dpif_linux_flow_state *state = state_;
struct ofpbuf *old_buf = state->buf;
struct dpif_linux_flow request;
int error; int error;
memset(&state->flow, 0, sizeof state->flow); dpif_linux_flow_init(&request);
state->flow.key = (struct nlattr *) state->keybuf; request.cmd = ODP_FLOW_DUMP;
state->flow.key_len = sizeof state->keybuf; request.dp_idx = dpif->minor;
if (actions) { request.state = state->flow.state;
state->flow.actions = (struct nlattr *) state->actionsbuf; error = dpif_linux_flow_transact(&request, &state->flow, &state->buf);
state->flow.actions_len = sizeof state->actionsbuf; ofpbuf_delete(old_buf);
}
error = do_ioctl(dpif, ODP_FLOW_DUMP, &state->dump);
if (!error) { if (!error) {
if (!state->flow.key_len) {
return EOF;
}
if (key) { if (key) {
*key = (const struct nlattr *) state->keybuf; *key = state->flow.key;
*key_len = state->flow.key_len; *key_len = state->flow.key_len;
} }
if (actions) { if (actions) {
*actions = (const struct nlattr *) state->actionsbuf; *actions = state->flow.actions;
*actions_len = state->flow.actions_len; *actions_len = state->flow.actions_len;
} }
if (stats) { if (stats) {
odp_flow_stats_to_dpif_flow_stats(&state->flow.stats, dpif_linux_flow_get_stats(&state->flow, &state->stats);
&state->stats);
*stats = &state->stats; *stats = &state->stats;
} }
} }
return error; return error == ENODEV ? EOF : error;
} }
static int static int
dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state) dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
{ {
struct dpif_linux_flow_state *state = state_;
ofpbuf_delete(state->buf);
free(state); free(state);
return 0; return 0;
} }
@@ -661,13 +713,29 @@ static int
dpif_linux_get_sflow_probability(const struct dpif *dpif_, dpif_linux_get_sflow_probability(const struct dpif *dpif_,
uint32_t *probability) uint32_t *probability)
{ {
return do_ioctl(dpif_, ODP_GET_SFLOW_PROBABILITY, probability); struct dpif_linux_dp dp;
struct ofpbuf *buf;
int error;
error = dpif_linux_dp_get(dpif_, &dp, &buf);
if (!error) {
*probability = dp.sampling ? *dp.sampling : 0;
ofpbuf_delete(buf);
}
return error;
} }
static int static int
dpif_linux_set_sflow_probability(struct dpif *dpif_, uint32_t probability) dpif_linux_set_sflow_probability(struct dpif *dpif_, uint32_t probability)
{ {
return do_ioctl(dpif_, ODP_SET_SFLOW_PROBABILITY, &probability); struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct dpif_linux_dp dp;
dpif_linux_dp_init(&dp);
dp.cmd = ODP_DP_SET;
dp.dp_idx = dpif->minor;
dp.sampling = &probability;
return dpif_linux_dp_transact(&dp, NULL, NULL);
} }
static int static int
@@ -985,22 +1053,6 @@ get_major(const char *target)
return -ENODEV; return -ENODEV;
} }
static int
create_minor(const char *name, int minor)
{
int error;
int fd;
error = open_minor(minor, &fd);
if (error) {
return error;
}
error = ioctl(fd, ODP_DP_CREATE, name) ? errno : 0;
close(fd);
return error;
}
static int static int
open_minor(int minor, int *fdp) open_minor(int minor, int *fdp)
{ {
@@ -1042,6 +1094,24 @@ dpif_linux_port_changed(const struct rtnetlink_link_change *change,
dpif->change_error = true; dpif->change_error = true;
} }
} }
static int
get_dp0_fd(int *dp0_fdp)
{
static int dp0_fd = -1;
if (dp0_fd < 0) {
int error;
int fd;
error = open_minor(0, &fd);
if (error) {
return error;
}
dp0_fd = fd;
}
*dp0_fdp = dp0_fd;
return 0;
}
/* Parses the contents of 'buf', which contains a "struct odp_vport" followed /* Parses the contents of 'buf', which contains a "struct odp_vport" followed
* by Netlink attributes, into 'vport'. Returns 0 if successful, otherwise a * by Netlink attributes, into 'vport'. Returns 0 if successful, otherwise a
@@ -1183,25 +1253,21 @@ dpif_linux_vport_transact(const struct dpif_linux_vport *request,
struct dpif_linux_vport *reply, struct dpif_linux_vport *reply,
struct ofpbuf **bufp) struct ofpbuf **bufp)
{ {
static int dp0_fd = -1;
struct ofpbuf *buf = NULL; struct ofpbuf *buf = NULL;
int error; int error;
int fd;
assert((reply != NULL) == (bufp != NULL)); assert((reply != NULL) == (bufp != NULL));
if (dp0_fd < 0) {
int fd;
error = open_minor(0, &fd); error = get_dp0_fd(&fd);
if (error) { if (error) {
goto error; goto error;
}
dp0_fd = fd;
} }
buf = ofpbuf_new(1024); buf = ofpbuf_new(1024);
dpif_linux_vport_to_ofpbuf(request, buf); dpif_linux_vport_to_ofpbuf(request, buf);
error = ioctl(dp0_fd, request->cmd, buf->data) ? errno : 0; error = ioctl(fd, request->cmd, buf->data) ? errno : 0;
if (error) { if (error) {
goto error; goto error;
} }
@@ -1242,4 +1308,322 @@ dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
return dpif_linux_vport_transact(&request, reply, bufp); return dpif_linux_vport_transact(&request, reply, bufp);
} }
/* Parses the contents of 'buf', which contains a "struct odp_datapath"
* followed by Netlink attributes, into 'dp'. Returns 0 if successful,
* otherwise a positive errno value.
*
* 'dp' will contain pointers into 'buf', so the caller should not free 'buf'
* while 'dp' is still in use. */
static int
dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, const struct ofpbuf *buf)
{
static const struct nl_policy odp_datapath_policy[] = {
[ODP_DP_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
[ODP_DP_ATTR_STATS] = { .type = NL_A_UNSPEC,
.min_len = sizeof(struct odp_stats),
.max_len = sizeof(struct odp_stats),
.optional = true },
[ODP_DP_ATTR_IPV4_FRAGS] = { .type = NL_A_U32, .optional = true },
[ODP_DP_ATTR_SAMPLING] = { .type = NL_A_U32, .optional = true },
};
struct odp_datapath *odp_dp;
struct nlattr *a[ARRAY_SIZE(odp_datapath_policy)];
dpif_linux_dp_init(dp);
if (!nl_policy_parse(buf, sizeof *odp_dp, odp_datapath_policy,
a, ARRAY_SIZE(odp_datapath_policy))) {
return EINVAL;
}
odp_dp = buf->data;
dp->dp_idx = odp_dp->dp_idx;
dp->name = nl_attr_get_string(a[ODP_DP_ATTR_NAME]);
if (a[ODP_DP_ATTR_STATS]) {
/* Can't use structure assignment because Netlink doesn't ensure
* sufficient alignment for 64-bit members. */
memcpy(&dp->stats, nl_attr_get(a[ODP_DP_ATTR_STATS]),
sizeof dp->stats);
}
if (a[ODP_DP_ATTR_IPV4_FRAGS]) {
dp->ipv4_frags = nl_attr_get_u32(a[ODP_DP_ATTR_IPV4_FRAGS]);
}
if (a[ODP_DP_ATTR_SAMPLING]) {
dp->sampling = nl_attr_get(a[ODP_DP_ATTR_SAMPLING]);
}
return 0;
}
/* Appends to 'buf' (which must initially be empty) a "struct odp_datapath"
* followed by Netlink attributes corresponding to 'dp'. */
static void
dpif_linux_dp_to_ofpbuf(const struct dpif_linux_dp *dp, struct ofpbuf *buf)
{
struct odp_datapath *odp_dp;
ofpbuf_reserve(buf, sizeof odp_dp);
if (dp->name) {
nl_msg_put_string(buf, ODP_DP_ATTR_NAME, dp->name);
}
/* Skip ODP_DP_ATTR_STATS since we never have a reason to serialize it. */
if (dp->ipv4_frags) {
nl_msg_put_u32(buf, ODP_DP_ATTR_IPV4_FRAGS, dp->ipv4_frags);
}
if (dp->sampling) {
nl_msg_put_u32(buf, ODP_DP_ATTR_SAMPLING, *dp->sampling);
}
odp_dp = ofpbuf_push_uninit(buf, sizeof *odp_dp);
odp_dp->dp_idx = dp->dp_idx;
odp_dp->len = buf->size;
odp_dp->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
}
/* Clears 'dp' to "empty" values. */
void
dpif_linux_dp_init(struct dpif_linux_dp *dp)
{
memset(dp, 0, sizeof *dp);
dp->dp_idx = -1;
}
/* Executes 'request' in the kernel datapath. If the command fails, returns a
* positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0
* without doing anything else. If 'reply' and 'bufp' are nonnull, then the
* result of the command is expected to be an odp_datapath also, which is
* decoded and stored in '*reply' and '*bufp'. The caller must free '*bufp'
* when the reply is no longer needed ('reply' will contain pointers into
* '*bufp'). */
int
dpif_linux_dp_transact(const struct dpif_linux_dp *request,
struct dpif_linux_dp *reply, struct ofpbuf **bufp)
{
struct ofpbuf *buf = NULL;
int error;
int fd;
assert((reply != NULL) == (bufp != NULL));
error = get_dp0_fd(&fd);
if (error) {
goto error;
}
buf = ofpbuf_new(1024);
dpif_linux_dp_to_ofpbuf(request, buf);
error = ioctl(fd, request->cmd, buf->data) ? errno : 0;
if (error) {
goto error;
}
if (bufp) {
buf->size = ((struct odp_datapath *) buf->data)->len;
error = dpif_linux_dp_from_ofpbuf(reply, buf);
if (error) {
goto error;
}
*bufp = buf;
} else {
ofpbuf_delete(buf);
}
return 0;
error:
ofpbuf_delete(buf);
if (bufp) {
memset(reply, 0, sizeof *reply);
*bufp = NULL;
}
return error;
}
/* Obtains information about 'dpif_' and stores it into '*reply' and '*bufp'.
* The caller must free '*bufp' when the reply is no longer needed ('reply'
* will contain pointers into '*bufp'). */
int
dpif_linux_dp_get(const struct dpif *dpif_, struct dpif_linux_dp *reply,
struct ofpbuf **bufp)
{
struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct dpif_linux_dp request;
dpif_linux_dp_init(&request);
request.cmd = ODP_DP_GET;
request.dp_idx = dpif->minor;
return dpif_linux_dp_transact(&request, reply, bufp);
}
/* Parses the contents of 'buf', which contains a "struct odp_flow" followed by
* Netlink attributes, into 'flow'. Returns 0 if successful, otherwise a
* positive errno value.
*
* 'flow' will contain pointers into 'buf', so the caller should not free 'buf'
* while 'flow' is still in use. */
static int
dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
const struct ofpbuf *buf)
{
static const struct nl_policy odp_flow_policy[] = {
[ODP_FLOW_ATTR_KEY] = { .type = NL_A_NESTED },
[ODP_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
[ODP_FLOW_ATTR_STATS] = { .type = NL_A_UNSPEC,
.min_len = sizeof(struct odp_flow_stats),
.max_len = sizeof(struct odp_flow_stats),
.optional = true },
[ODP_FLOW_ATTR_TCP_FLAGS] = { .type = NL_A_U8, .optional = true },
[ODP_FLOW_ATTR_USED] = { .type = NL_A_U64, .optional = true },
/* The kernel never uses ODP_FLOW_ATTR_CLEAR. */
[ODP_FLOW_ATTR_STATE] = { .type = NL_A_U64, .optional = true },
};
struct odp_flow *odp_flow;
struct nlattr *a[ARRAY_SIZE(odp_flow_policy)];
dpif_linux_flow_init(flow);
if (!nl_policy_parse(buf, sizeof *odp_flow, odp_flow_policy,
a, ARRAY_SIZE(odp_flow_policy))) {
return EINVAL;
}
odp_flow = buf->data;
flow->nlmsg_flags = odp_flow->nlmsg_flags;
flow->dp_idx = odp_flow->dp_idx;
flow->key = nl_attr_get(a[ODP_FLOW_ATTR_KEY]);
flow->key_len = nl_attr_get_size(a[ODP_FLOW_ATTR_KEY]);
if (a[ODP_FLOW_ATTR_ACTIONS]) {
flow->actions = nl_attr_get(a[ODP_FLOW_ATTR_ACTIONS]);
flow->actions_len = nl_attr_get_size(a[ODP_FLOW_ATTR_ACTIONS]);
}
if (a[ODP_FLOW_ATTR_STATS]) {
flow->stats = nl_attr_get(a[ODP_FLOW_ATTR_STATS]);
}
if (a[ODP_FLOW_ATTR_TCP_FLAGS]) {
flow->tcp_flags = nl_attr_get(a[ODP_FLOW_ATTR_TCP_FLAGS]);
}
if (a[ODP_FLOW_ATTR_STATE]) {
flow->state = nl_attr_get(a[ODP_FLOW_ATTR_STATE]);
}
return 0;
}
/* Appends to 'buf' (which must initially be empty) a "struct odp_flow"
* followed by Netlink attributes corresponding to 'flow'. */
static void
dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow *flow,
struct ofpbuf *buf)
{
struct odp_flow *odp_flow;
ofpbuf_reserve(buf, sizeof odp_flow);
if (flow->key_len) {
nl_msg_put_unspec(buf, ODP_FLOW_ATTR_KEY, flow->key, flow->key_len);
}
if (flow->actions_len) {
nl_msg_put_unspec(buf, ODP_FLOW_ATTR_ACTIONS,
flow->actions, flow->actions_len);
}
/* We never need to send these to the kernel. */
assert(!flow->stats);
assert(!flow->tcp_flags);
assert(!flow->used);
if (flow->clear) {
nl_msg_put_flag(buf, ODP_FLOW_ATTR_CLEAR);
}
if (flow->state) {
nl_msg_put_u64(buf, ODP_FLOW_ATTR_STATE,
get_unaligned_u64(flow->state));
}
odp_flow = ofpbuf_push_uninit(buf, sizeof *odp_flow);
odp_flow->nlmsg_flags = flow->nlmsg_flags;
odp_flow->dp_idx = flow->dp_idx;
odp_flow->len = buf->size;
odp_flow->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
}
/* Clears 'flow' to "empty" values. */
void
dpif_linux_flow_init(struct dpif_linux_flow *flow)
{
memset(flow, 0, sizeof *flow);
}
/* Executes 'request' in the kernel datapath. If the command fails, returns a
* positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0
* without doing anything else. If 'reply' and 'bufp' are nonnull, then the
* result of the command is expected to be an odp_flow also, which is decoded
* and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the
* reply is no longer needed ('reply' will contain pointers into '*bufp'). */
int
dpif_linux_flow_transact(const struct dpif_linux_flow *request,
struct dpif_linux_flow *reply, struct ofpbuf **bufp)
{
struct ofpbuf *buf = NULL;
int error;
int fd;
assert((reply != NULL) == (bufp != NULL));
error = get_dp0_fd(&fd);
if (error) {
goto error;
}
buf = ofpbuf_new(1024);
dpif_linux_flow_to_ofpbuf(request, buf);
error = ioctl(fd, request->cmd, buf->data) ? errno : 0;
if (error) {
goto error;
}
if (bufp) {
buf->size = ((struct odp_flow *) buf->data)->len;
error = dpif_linux_flow_from_ofpbuf(reply, buf);
if (error) {
goto error;
}
*bufp = buf;
} else {
ofpbuf_delete(buf);
}
return 0;
error:
ofpbuf_delete(buf);
if (bufp) {
memset(reply, 0, sizeof *reply);
*bufp = NULL;
}
return error;
}
static void
dpif_linux_flow_get_stats(const struct dpif_linux_flow *flow,
struct dpif_flow_stats *stats)
{
if (flow->stats) {
stats->n_packets = get_unaligned_u64(&flow->stats->n_packets);
stats->n_bytes = get_unaligned_u64(&flow->stats->n_bytes);
} else {
stats->n_packets = 0;
stats->n_bytes = 0;
}
stats->used = flow->used ? get_unaligned_u64(flow->used) : 0;
stats->tcp_flags = flow->tcp_flags ? *flow->tcp_flags : 0;
}