#include <linux/rculist.h>
#include <linux/dmi.h>
#include <net/inet_ecn.h>
+#include <net/genetlink.h>
#include <linux/compat.h>
#include "openvswitch/datapath-protocol.h"
OVS_CB(skb)->vport = p;
if (!OVS_CB(skb)->flow) {
- struct odp_flow_key key;
+ struct sw_flow_key key;
struct tbl_node *flow_node;
bool is_frag;
struct odp_flow_stats *stats)
{
struct tbl_node *flow_node;
+ struct sw_flow_key key;
struct sw_flow *flow;
struct tbl *table;
struct sw_flow_actions *acts = NULL;
int error;
u32 hash;
- hash = flow_hash(&uf->flow.key);
+ error = flow_copy_from_user(&key, (const struct nlattr __force __user *)uf->flow.key,
+ uf->flow.key_len);
+ if (error)
+ return error;
+
+ hash = flow_hash(&key);
table = get_table_protected(dp);
- flow_node = tbl_lookup(table, &uf->flow.key, hash, flow_cmp);
+ flow_node = tbl_lookup(table, &key, hash, flow_cmp);
if (!flow_node) {
/* No such flow. */
error = -ENOENT;
error = PTR_ERR(flow);
goto error;
}
- flow->key = uf->flow.key;
+ flow->key = key;
clear_stats(flow);
/* Obtain actions. */
&ufp->stats, actions, &ufp->actions_len);
}
-static struct sw_flow *do_del_flow(struct datapath *dp, struct odp_flow_key *key)
+static struct sw_flow *do_del_flow(struct datapath *dp, const struct nlattr __user *key, u32 key_len)
{
struct tbl *table = get_table_protected(dp);
struct tbl_node *flow_node;
+ struct sw_flow_key swkey;
int error;
- flow_node = tbl_lookup(table, key, flow_hash(key), flow_cmp);
+ error = flow_copy_from_user(&swkey, key, key_len);
+ if (error)
+ return ERR_PTR(error);
+
+ flow_node = tbl_lookup(table, &swkey, flow_hash(&swkey), flow_cmp);
if (!flow_node)
return ERR_PTR(-ENOENT);
if (copy_from_user(&uf, ufp, sizeof(uf)))
return -EFAULT;
- flow = do_del_flow(dp, &uf.key);
+ flow = do_del_flow(dp, (const struct nlattr __force __user *)uf.key, uf.key_len);
if (IS_ERR(flow))
return PTR_ERR(flow);
for (i = 0; i < flowvec->n_flows; i++) {
struct odp_flow __user *ufp = (struct odp_flow __user __force *)&flowvec->flows[i];
+ struct sw_flow_key key;
struct odp_flow uf;
struct tbl_node *flow_node;
int error;
if (copy_from_user(&uf, ufp, sizeof(uf)))
return -EFAULT;
- flow_node = tbl_lookup(table, &uf.key, flow_hash(&uf.key), flow_cmp);
+ error = flow_copy_from_user(&key, (const struct nlattr __force __user *)uf.key, uf.key_len);
+ if (error)
+ return error;
+
+ flow_node = tbl_lookup(table, &uf.key, flow_hash(&key), flow_cmp);
if (!flow_node)
error = put_user(ENOENT, &ufp->stats.error);
else
static int dump_flow(struct datapath *dp, struct odp_flow_dump __user *udumpp)
{
struct odp_flow __user *uflowp;
+ struct nlattr __user *ukey;
struct sw_flow *flow;
+ u32 key_len;
flow = do_dump_flow(dp, udumpp->state);
if (IS_ERR(flow))
if (!flow)
return put_user(ODPFF_EOF, &uflowp->flags);
- if (copy_to_user(&uflowp->key, &flow->key, sizeof(struct odp_flow_key)) ||
- put_user(0, &uflowp->flags))
+ if (put_user(0, &uflowp->flags) ||
+ get_user(ukey, (struct nlattr __user * __user*)&uflowp->key) ||
+ get_user(key_len, &uflowp->key_len))
return -EFAULT;
+
+ key_len = flow_copy_to_user(ukey, &flow->key, key_len);
+ if (key_len < 0)
+ return key_len;
+ if (put_user(key_len, &uflowp->key_len))
+ return -EFAULT;
+
return answer_query(dp, flow, 0, uflowp);
}
static int do_execute(struct datapath *dp, const struct odp_execute *execute)
{
- struct odp_flow_key key;
+ struct sw_flow_key key;
struct sk_buff *skb;
struct sw_flow_actions *actions;
struct ethhdr *eth;
static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow __user *compat)
{
- compat_uptr_t actions;
+ compat_uptr_t key, actions;
if (!access_ok(VERIFY_READ, compat, sizeof(struct compat_odp_flow)) ||
__copy_from_user(&flow->stats, &compat->stats, sizeof(struct odp_flow_stats)) ||
- __copy_from_user(&flow->key, &compat->key, sizeof(struct odp_flow_key)) ||
+ __get_user(key, &compat->key) ||
+ __get_user(flow->key_len, &compat->key_len) ||
__get_user(actions, &compat->actions) ||
__get_user(flow->actions_len, &compat->actions_len) ||
__get_user(flow->flags, &compat->flags))
return -EFAULT;
+ flow->key = (struct nlattr __force *)compat_ptr(key);
flow->actions = (struct nlattr __force *)compat_ptr(actions);
return 0;
}
if (compat_get_flow(&uf, ufp))
return -EFAULT;
- flow = do_del_flow(dp, &uf.key);
+ flow = do_del_flow(dp, (const struct nlattr __force __user *)uf.key, uf.key_len);
if (IS_ERR(flow))
return PTR_ERR(flow);
struct compat_odp_flow __user *ufp = &flows[i];
struct odp_flow uf;
struct tbl_node *flow_node;
+ struct sw_flow_key key;
int error;
if (compat_get_flow(&uf, ufp))
return -EFAULT;
- flow_node = tbl_lookup(table, &uf.key, flow_hash(&uf.key), flow_cmp);
+ error = flow_copy_from_user(&key, (const struct nlattr __force __user *) uf.key, uf.key_len);
+ if (error)
+ return error;
+
+ flow_node = tbl_lookup(table, &key, flow_hash(&key), flow_cmp);
if (!flow_node)
error = put_user(ENOENT, &ufp->stats.error);
else
struct compat_odp_flow __user *uflowp;
compat_uptr_t compat_ufp;
struct sw_flow *flow;
+ compat_uptr_t ukey;
+ u32 key_len;
flow = do_dump_flow(dp, udumpp->state);
if (IS_ERR(flow))
if (!flow)
return put_user(ODPFF_EOF, &uflowp->flags);
- if (copy_to_user(&uflowp->key, &flow->key, sizeof(struct odp_flow_key)) ||
- put_user(0, &uflowp->flags))
+ if (put_user(0, &uflowp->flags) ||
+ get_user(ukey, &uflowp->key) ||
+ get_user(key_len, &uflowp->key_len))
+ return -EFAULT;
+
+ key_len = flow_copy_to_user(compat_ptr(ukey), &flow->key, key_len);
+ if (key_len < 0)
+ return key_len;
+ if (put_user(key_len, &uflowp->key_len))
return -EFAULT;
+
return compat_answer_query(dp, flow, 0, uflowp);
}