static struct datapath __rcu *dps[ODP_MAX];
static DEFINE_MUTEX(dp_mutex);
-static int new_vport(struct datapath *, struct odp_port *, int port_no);
+static struct vport *new_vport(const struct vport_parms *);
/* Must be called with rcu_read_lock or dp_mutex. */
struct datapath *get_dp(int dp_idx)
static int create_dp(int dp_idx, const char __user *devnamep)
{
- struct odp_port internal_dev_port;
+ struct vport_parms parms;
char devname[IFNAMSIZ];
+ struct vport *vport;
struct datapath *dp;
int err;
int i;
goto err_free_dp;
/* Set up our datapath device. */
- BUILD_BUG_ON(sizeof(internal_dev_port.devname) != sizeof(devname));
- strcpy(internal_dev_port.devname, devname);
- internal_dev_port.type = ODP_VPORT_TYPE_INTERNAL;
- err = new_vport(dp, &internal_dev_port, ODPP_LOCAL);
- if (err) {
+ parms.name = devname;
+ parms.type = ODP_VPORT_TYPE_INTERNAL;
+ parms.options = NULL;
+ parms.dp = dp;
+ parms.port_no = ODPP_LOCAL;
+ vport = new_vport(&parms);
+ if (IS_ERR(vport)) {
+ err = PTR_ERR(vport);
if (err == -EBUSY)
err = -EEXIST;
}
/* Called with RTNL lock and dp->mutex. */
-static int new_vport(struct datapath *dp, struct odp_port *odp_port, int port_no)
+static struct vport *new_vport(const struct vport_parms *parms)
{
- struct vport_parms parms;
struct vport *vport;
- parms.name = odp_port->devname;
- parms.type = odp_port->type;
- parms.config = odp_port->config;
- parms.dp = dp;
- parms.port_no = port_no;
-
vport_lock();
- vport = vport_add(&parms);
- vport_unlock();
+ vport = vport_add(parms);
+ if (!IS_ERR(vport)) {
+ struct datapath *dp = parms->dp;
- if (IS_ERR(vport))
- return PTR_ERR(vport);
-
- rcu_assign_pointer(dp->ports[port_no], vport);
- list_add_rcu(&vport->node, &dp->port_list);
- dp->n_ports++;
+ rcu_assign_pointer(dp->ports[parms->port_no], vport);
+ list_add_rcu(&vport->node, &dp->port_list);
+ dp->n_ports++;
- dp_ifinfo_notify(RTM_NEWLINK, vport);
+ dp_ifinfo_notify(RTM_NEWLINK, vport);
+ }
+ vport_unlock();
- return 0;
-}
-
-static int attach_port(int dp_idx, struct odp_port __user *portp)
-{
- struct datapath *dp;
- struct odp_port port;
- int port_no;
- int err;
-
- err = -EFAULT;
- if (copy_from_user(&port, portp, sizeof(port)))
- goto out;
- port.devname[IFNAMSIZ - 1] = '\0';
-
- rtnl_lock();
- dp = get_dp_locked(dp_idx);
- err = -ENODEV;
- if (!dp)
- goto out_unlock_rtnl;
-
- for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
- if (!dp->ports[port_no])
- goto got_port_no;
- err = -EFBIG;
- goto out_unlock_dp;
-
-got_port_no:
- err = new_vport(dp, &port, port_no);
- if (err)
- goto out_unlock_dp;
-
- set_internal_devs_mtu(dp);
- dp_sysfs_add_if(get_vport_protected(dp, port_no));
-
- err = put_user(port_no, &portp->port);
-
-out_unlock_dp:
- mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
- rtnl_unlock();
-out:
- return err;
+ return vport;
}
int dp_detach_port(struct vport *p)
return err;
}
-static int detach_port(int dp_idx, int port_no)
-{
- struct vport *p;
- struct datapath *dp;
- int err;
-
- err = -EINVAL;
- if (port_no < 0 || port_no >= DP_MAX_PORTS || port_no == ODPP_LOCAL)
- goto out;
-
- rtnl_lock();
- dp = get_dp_locked(dp_idx);
- err = -ENODEV;
- if (!dp)
- goto out_unlock_rtnl;
-
- p = get_vport_protected(dp, port_no);
- err = -ENOENT;
- if (!p)
- goto out_unlock_dp;
-
- err = dp_detach_port(p);
-
-out_unlock_dp:
- mutex_unlock(&dp->mutex);
-out_unlock_rtnl:
- rtnl_unlock();
-out:
- return err;
-}
-
/* Must be called with rcu_read_lock. */
void dp_process_received_packet(struct vport *p, struct sk_buff *skb)
{
}
}
-static void compose_odp_port(const struct vport *vport, struct odp_port *odp_port)
+static int get_listen_mask(const struct file *f)
+{
+ return (long)f->private_data;
+}
+
+static void set_listen_mask(struct file *f, int listen_mask)
{
+ f->private_data = (void*)(long)listen_mask;
+}
+
+static const struct nla_policy vport_policy[ODP_VPORT_ATTR_MAX + 1] = {
+ [ODP_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+ [ODP_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
+ [ODP_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
+ [ODP_VPORT_ATTR_STATS] = { .len = sizeof(struct rtnl_link_stats64) },
+ [ODP_VPORT_ATTR_ADDRESS] = { .len = ETH_ALEN },
+ [ODP_VPORT_ATTR_MTU] = { .type = NLA_U32 },
+ [ODP_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
+};
+
+static int copy_vport_to_user(void __user *dst, struct vport *vport, uint32_t total_len)
+{
+ struct odp_vport *odp_vport;
+ struct sk_buff *skb;
+ struct nlattr *nla;
+ int ifindex, iflink;
+ int err;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ err = -ENOMEM;
+ if (!skb)
+ goto exit;
+
rcu_read_lock();
- strncpy(odp_port->devname, vport_get_name(vport), sizeof(odp_port->devname));
- odp_port->type = vport_get_type(vport);
- vport_get_config(vport, odp_port->config);
- odp_port->port = vport->port_no;
- odp_port->dp_idx = vport->dp->dp_idx;
+ odp_vport = (struct odp_vport*)__skb_put(skb, sizeof(struct odp_vport));
+ odp_vport->dp_idx = vport->dp->dp_idx;
+ odp_vport->total_len = total_len;
+
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_TYPE, vport_get_type(vport));
+ NLA_PUT_STRING(skb, ODP_VPORT_ATTR_NAME, vport_get_name(vport));
+
+ nla = nla_reserve(skb, ODP_VPORT_ATTR_STATS, sizeof(struct rtnl_link_stats64));
+ if (!nla)
+ goto nla_put_failure;
+ if (vport_get_stats(vport, nla_data(nla)))
+ __skb_trim(skb, skb->len - nla->nla_len);
+
+ NLA_PUT(skb, ODP_VPORT_ATTR_ADDRESS, ETH_ALEN, vport_get_addr(vport));
+
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_MTU, vport_get_mtu(vport));
+
+ err = vport_get_options(vport, skb);
+
+ ifindex = vport_get_ifindex(vport);
+ if (ifindex > 0)
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_IFINDEX, ifindex);
+
+ iflink = vport_get_iflink(vport);
+ if (iflink > 0)
+ NLA_PUT_U32(skb, ODP_VPORT_ATTR_IFLINK, iflink);
+
+ err = -EMSGSIZE;
+ if (skb->len > total_len)
+ goto exit_unlock;
+
+ odp_vport->len = skb->len;
+ err = copy_to_user(dst, skb->data, skb->len) ? -EFAULT : 0;
+ goto exit_unlock;
+
+nla_put_failure:
+ err = -EMSGSIZE;
+exit_unlock:
rcu_read_unlock();
+ kfree_skb(skb);
+exit:
+ return err;
}
-static int query_port(int dp_idx, struct odp_port __user *uport)
+static struct sk_buff *copy_vport_from_user(struct odp_vport __user *uodp_vport,
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
{
- struct odp_port port;
+ struct odp_vport *odp_vport;
+ struct sk_buff *skb;
+ u32 len;
+ int err;
- if (copy_from_user(&port, uport, sizeof(port)))
- return -EFAULT;
+ if (get_user(len, &uodp_vport->len))
+ return ERR_PTR(-EFAULT);
+ if (len < sizeof(struct odp_vport))
+ return ERR_PTR(-EINVAL);
- if (port.devname[0]) {
- struct vport *vport;
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
- port.devname[IFNAMSIZ - 1] = '\0';
+ err = -EFAULT;
+ if (copy_from_user(__skb_put(skb, len), uodp_vport, len))
+ goto error_free_skb;
- vport_lock();
- vport = vport_locate(port.devname);
- if (vport)
- compose_odp_port(vport, &port);
- vport_unlock();
+ odp_vport = (struct odp_vport *)skb->data;
+ err = -EINVAL;
+ if (odp_vport->len != len)
+ goto error_free_skb;
- if (!vport)
- return -ENODEV;
- } else {
- struct vport *vport;
- struct datapath *dp;
+ err = nla_parse(a, ODP_VPORT_ATTR_MAX, (struct nlattr *)(skb->data + sizeof(struct odp_vport)),
+ skb->len - sizeof(struct odp_vport), vport_policy);
+ if (err)
+ goto error_free_skb;
- if (port.port >= DP_MAX_PORTS)
- return -EINVAL;
+ err = VERIFY_NUL_STRING(a[ODP_VPORT_ATTR_NAME], IFNAMSIZ - 1);
+ if (err)
+ goto error_free_skb;
+
+ return skb;
+
+error_free_skb:
+ kfree_skb(skb);
+ return ERR_PTR(err);
+}
+
+
+/* Called without any locks (or with RTNL lock).
+ * Returns holding vport->dp->mutex.
+ */
+static struct vport *lookup_vport(struct odp_vport *odp_vport,
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
+{
+ struct datapath *dp;
+ struct vport *vport;
+
+ if (a[ODP_VPORT_ATTR_NAME]) {
+ int dp_idx, port_no;
+
+ retry:
+ vport_lock();
+ vport = vport_locate(nla_data(a[ODP_VPORT_ATTR_NAME]));
+ if (!vport) {
+ vport_unlock();
+ return ERR_PTR(-ENODEV);
+ }
+ dp_idx = vport->dp->dp_idx;
+ port_no = vport->port_no;
+ vport_unlock();
dp = get_dp_locked(dp_idx);
if (!dp)
- return -ENODEV;
+ goto retry;
- vport = get_vport_protected(dp, port.port);
- if (vport)
- compose_odp_port(vport, &port);
- mutex_unlock(&dp->mutex);
+ vport = get_vport_protected(dp, port_no);
+ if (!vport ||
+ strcmp(vport_get_name(vport), nla_data(a[ODP_VPORT_ATTR_NAME]))) {
+ mutex_unlock(&dp->mutex);
+ goto retry;
+ }
- if (!vport)
- return -ENOENT;
- }
+ return vport;
+ } else if (a[ODP_VPORT_ATTR_PORT_NO]) {
+ u32 port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+
+ if (port_no >= DP_MAX_PORTS)
+ return ERR_PTR(-EINVAL);
+
+ dp = get_dp_locked(odp_vport->dp_idx);
+ if (!dp)
+ return ERR_PTR(-ENODEV);
- return copy_to_user(uport, &port, sizeof(struct odp_port));
+ vport = get_vport_protected(dp, port_no);
+ if (!vport) {
+ mutex_unlock(&dp->mutex);
+ return ERR_PTR(-ENOENT);
+ }
+ return vport;
+ } else
+ return ERR_PTR(-EINVAL);
}
-static int do_dump_port(struct datapath *dp, struct odp_vport_dump *dump)
+static int change_vport(struct vport *vport, struct nlattr *a[ODP_VPORT_ATTR_MAX + 1])
{
+ int err = 0;
+ if (a[ODP_VPORT_ATTR_STATS])
+ err = vport_set_stats(vport, nla_data(a[ODP_VPORT_ATTR_STATS]));
+ if (!err && a[ODP_VPORT_ATTR_ADDRESS])
+ err = vport_set_addr(vport, nla_data(a[ODP_VPORT_ATTR_ADDRESS]));
+ if (!err && a[ODP_VPORT_ATTR_MTU])
+ err = vport_set_mtu(vport, nla_get_u32(a[ODP_VPORT_ATTR_MTU]));
+ return err;
+}
+
+static int attach_vport(struct odp_vport __user *uodp_vport)
+{
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct odp_vport *odp_vport;
+ struct vport_parms parms;
+ struct vport *vport;
+ struct sk_buff *skb;
+ struct datapath *dp;
u32 port_no;
+ int err;
- for (port_no = dump->port_no; port_no < DP_MAX_PORTS; port_no++) {
- struct vport *vport = get_vport_protected(dp, port_no);
- if (vport) {
- struct odp_port odp_port;
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+ odp_vport = (struct odp_vport *)skb->data;
+
+ err = -EINVAL;
+ if (!a[ODP_VPORT_ATTR_NAME] || !a[ODP_VPORT_ATTR_TYPE])
+ goto exit_kfree_skb;
+
+ rtnl_lock();
+
+ dp = get_dp_locked(odp_vport->dp_idx);
+ err = -ENODEV;
+ if (!dp)
+ goto exit_unlock_rtnl;
+
+ if (a[ODP_VPORT_ATTR_PORT_NO]) {
+ port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
- compose_odp_port(vport, &odp_port);
- return copy_to_user((struct odp_port __force __user*)dump->port, &odp_port, sizeof(struct odp_port));
+ err = -EFBIG;
+ if (port_no >= DP_MAX_PORTS)
+ goto exit_unlock_dp;
+
+ vport = get_vport_protected(dp, port_no);
+ err = -EBUSY;
+ if (vport)
+ goto exit_unlock_dp;
+ } else {
+ for (port_no = 1; ; port_no++) {
+ if (port_no >= DP_MAX_PORTS) {
+ err = -EFBIG;
+ goto exit_unlock_dp;
+ }
+ vport = get_vport_protected(dp, port_no);
+ if (!vport)
+ break;
}
}
- return put_user('\0', (char __force __user*)&dump->port->devname[0]);
+ parms.name = nla_data(a[ODP_VPORT_ATTR_NAME]);
+ parms.type = nla_get_u32(a[ODP_VPORT_ATTR_TYPE]);
+ parms.options = a[ODP_VPORT_ATTR_OPTIONS];
+ parms.dp = dp;
+ parms.port_no = port_no;
+
+ vport = new_vport(&parms);
+ err = PTR_ERR(vport);
+ if (IS_ERR(vport))
+ goto exit_unlock_dp;
+
+ set_internal_devs_mtu(dp);
+ dp_sysfs_add_if(vport);
+
+ err = change_vport(vport, a);
+ if (err) {
+ dp_detach_port(vport);
+ goto exit_unlock_dp;
+ }
+
+ err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+
+exit_unlock_dp:
+ mutex_unlock(&dp->mutex);
+exit_unlock_rtnl:
+ rtnl_unlock();
+exit_kfree_skb:
+ kfree_skb(skb);
+exit:
+ return err;
}
-static int dump_port(struct datapath *dp, struct odp_vport_dump __user *udump)
+static int set_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
{
- struct odp_vport_dump dump;
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct vport *vport;
+ struct sk_buff *skb;
+ int err;
- if (copy_from_user(&dump, udump, sizeof(dump)))
- return -EFAULT;
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+
+ rtnl_lock();
+ vport = lookup_vport((struct odp_vport *)skb->data, a);
+ err = PTR_ERR(vport);
+ if (IS_ERR(vport))
+ goto exit_free;
- return do_dump_port(dp, &dump);
+ err = 0;
+ if (a[ODP_VPORT_ATTR_OPTIONS])
+ err = vport_set_options(vport, a[ODP_VPORT_ATTR_OPTIONS]);
+ if (!err)
+ err = change_vport(vport, a);
+
+ mutex_unlock(&vport->dp->mutex);
+exit_free:
+ kfree_skb(skb);
+ rtnl_unlock();
+exit:
+ return err;
}
-static int get_listen_mask(const struct file *f)
+static int del_vport(unsigned int cmd, struct odp_vport __user *uodp_vport)
{
- return (long)f->private_data;
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct datapath *dp;
+ struct vport *vport;
+ struct sk_buff *skb;
+ int err;
+
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+
+ rtnl_lock();
+ vport = lookup_vport((struct odp_vport *)skb->data, a);
+ err = PTR_ERR(vport);
+ if (IS_ERR(vport))
+ goto exit_free;
+ dp = vport->dp;
+
+ err = -EINVAL;
+ if (vport->port_no == ODPP_LOCAL)
+ goto exit_free;
+
+ err = dp_detach_port(vport);
+ mutex_unlock(&dp->mutex);
+exit_free:
+ kfree_skb(skb);
+ rtnl_unlock();
+exit:
+ return err;
}
-static void set_listen_mask(struct file *f, int listen_mask)
+static int get_vport(struct odp_vport __user *uodp_vport)
{
- f->private_data = (void*)(long)listen_mask;
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct odp_vport *odp_vport;
+ struct vport *vport;
+ struct sk_buff *skb;
+ int err;
+
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+ odp_vport = (struct odp_vport *)skb->data;
+
+ vport = lookup_vport(odp_vport, a);
+ err = PTR_ERR(vport);
+ if (IS_ERR(vport))
+ goto exit_free;
+
+ err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+ mutex_unlock(&vport->dp->mutex);
+exit_free:
+ kfree_skb(skb);
+exit:
+ return err;
+}
+
+static int dump_vport(struct odp_vport __user *uodp_vport)
+{
+ struct nlattr *a[ODP_VPORT_ATTR_MAX + 1];
+ struct odp_vport *odp_vport;
+ struct sk_buff *skb;
+ struct datapath *dp;
+ u32 port_no;
+ int err;
+
+ skb = copy_vport_from_user(uodp_vport, a);
+ err = PTR_ERR(skb);
+ if (IS_ERR(skb))
+ goto exit;
+ odp_vport = (struct odp_vport *)skb->data;
+
+ dp = get_dp_locked(odp_vport->dp_idx);
+ err = -ENODEV;
+ if (!dp)
+ goto exit_free;
+
+ port_no = 0;
+ if (a[ODP_VPORT_ATTR_PORT_NO])
+ port_no = nla_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+ for (; port_no < DP_MAX_PORTS; port_no++) {
+ struct vport *vport = get_vport_protected(dp, port_no);
+ if (vport) {
+ err = copy_vport_to_user(uodp_vport, vport, odp_vport->total_len);
+ goto exit_unlock_dp;
+ }
+ }
+ err = -ENODEV;
+
+exit_unlock_dp:
+ mutex_unlock(&dp->mutex);
+exit_free:
+ kfree_skb(skb);
+exit:
+ return err;
}
static long openvswitch_ioctl(struct file *f, unsigned int cmd,
{
int dp_idx = iminor(f->f_dentry->d_inode);
struct datapath *dp;
- int drop_frags, listeners, port_no;
+ int drop_frags, listeners;
unsigned int sflow_probability;
int err;
err = destroy_dp(dp_idx);
goto exit;
- case ODP_VPORT_ATTACH:
- err = attach_port(dp_idx, (struct odp_port __user *)argp);
+ case ODP_VPORT_NEW:
+ err = attach_vport((struct odp_vport __user *)argp);
goto exit;
- case ODP_VPORT_DETACH:
- err = get_user(port_no, (int __user *)argp);
- if (!err)
- err = detach_port(dp_idx, port_no);
- goto exit;
-
- case ODP_VPORT_QUERY:
- err = query_port(dp_idx, (struct odp_port __user *)argp);
- goto exit;
-
- case ODP_VPORT_MOD:
- err = vport_user_mod((struct odp_port __user *)argp);
- goto exit;
-
- case ODP_VPORT_STATS_GET:
- err = vport_user_stats_get((struct odp_vport_stats_req __user *)argp);
+ case ODP_VPORT_GET:
+ err = get_vport((struct odp_vport __user *)argp);
goto exit;
- case ODP_VPORT_STATS_SET:
- err = vport_user_stats_set((struct odp_vport_stats_req __user *)argp);
+ case ODP_VPORT_DEL:
+ err = del_vport(cmd, (struct odp_vport __user *)argp);
goto exit;
- case ODP_VPORT_ETHER_GET:
- err = vport_user_ether_get((struct odp_vport_ether __user *)argp);
+ case ODP_VPORT_SET:
+ err = set_vport(cmd, (struct odp_vport __user *)argp);
goto exit;
- case ODP_VPORT_ETHER_SET:
- err = vport_user_ether_set((struct odp_vport_ether __user *)argp);
- goto exit;
-
- case ODP_VPORT_MTU_GET:
- err = vport_user_mtu_get((struct odp_vport_mtu __user *)argp);
- goto exit;
-
- case ODP_VPORT_MTU_SET:
- err = vport_user_mtu_set((struct odp_vport_mtu __user *)argp);
+ case ODP_VPORT_DUMP:
+ err = dump_vport((struct odp_vport __user *)argp);
goto exit;
}
dp->sflow_probability = sflow_probability;
break;
- case ODP_VPORT_DUMP:
- err = dump_port(dp, (struct odp_vport_dump __user *)argp);
- break;
-
case ODP_FLOW_FLUSH:
err = flush_flows(dp);
break;
}
#ifdef CONFIG_COMPAT
-static int compat_dump_port(struct datapath *dp, struct compat_odp_vport_dump __user *compat)
-{
- struct odp_vport_dump dump;
- compat_uptr_t port;
-
- if (!access_ok(VERIFY_READ, compat, sizeof(struct compat_odp_vport_dump)) ||
- __get_user(port, &compat->port) ||
- __get_user(dump.port_no, &compat->port_no))
- return -EFAULT;
-
- dump.port = (struct odp_port __force *)compat_ptr(port);
- return do_dump_port(dp, &dump);
-}
-
static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow __user *compat)
{
compat_uptr_t key, actions;
return openvswitch_ioctl(f, cmd, argp);
case ODP_DP_CREATE:
- case ODP_VPORT_ATTACH:
- case ODP_VPORT_DETACH:
- case ODP_VPORT_MOD:
- case ODP_VPORT_MTU_SET:
- case ODP_VPORT_MTU_GET:
- case ODP_VPORT_ETHER_SET:
- case ODP_VPORT_ETHER_GET:
- case ODP_VPORT_STATS_SET:
- case ODP_VPORT_STATS_GET:
+ case ODP_VPORT_NEW:
+ case ODP_VPORT_DEL:
+ case ODP_VPORT_GET:
+ case ODP_VPORT_SET:
+ case ODP_VPORT_DUMP:
case ODP_DP_STATS:
case ODP_GET_DROP_FRAGS:
case ODP_SET_DROP_FRAGS:
case ODP_GET_LISTEN_MASK:
case ODP_SET_SFLOW_PROBABILITY:
case ODP_GET_SFLOW_PROBABILITY:
- case ODP_VPORT_QUERY:
/* Ioctls that just need their pointer argument extended. */
return openvswitch_ioctl(f, cmd, (unsigned long)compat_ptr(argp));
}
goto exit;
switch (cmd) {
- case ODP_VPORT_DUMP32:
- err = compat_dump_port(dp, compat_ptr(argp));
- break;
-
case ODP_FLOW_PUT32:
err = compat_put_flow(dp, compat_ptr(argp));
break;
#include "openvswitch/datapath-protocol.h"
#include <linux/compat.h>
-#define ODP_VPORT_DUMP32 _IOWR('O', 10, struct compat_odp_vport_dump)
#define ODP_FLOW_GET32 _IOWR('O', 13, struct compat_odp_flowvec)
#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_EXECUTE32 _IOR('O', 18, struct compat_odp_execute)
#define ODP_FLOW_DEL32 _IOWR('O', 17, struct compat_odp_flow)
-struct compat_odp_vport_dump {
- compat_uptr_t port;
- u32 port_no;
-};
-
struct compat_odp_flow {
struct odp_flow_stats stats;
compat_uptr_t key;
static unsigned int *find_port_pool(const struct tnl_mutable_config *mutable)
{
- if (mutable->port_config.flags & TNL_F_IN_KEY_MATCH) {
- if (mutable->port_config.saddr)
+ if (mutable->flags & TNL_F_IN_KEY_MATCH) {
+ if (mutable->saddr)
return &local_remote_ports;
else
return &remote_ports;
} else {
- if (mutable->port_config.saddr)
+ if (mutable->saddr)
return &key_local_remote_ports;
else
return &key_remote_ports;
lookup->mutable = rcu_dereference_rtnl(tnl_vport->mutable);
return (lookup->mutable->tunnel_type == lookup->tunnel_type &&
- lookup->mutable->port_config.daddr == lookup->daddr &&
- lookup->mutable->port_config.in_key == lookup->key &&
- lookup->mutable->port_config.saddr == lookup->saddr);
+ lookup->mutable->daddr == lookup->daddr &&
+ lookup->mutable->in_key == lookup->key &&
+ lookup->mutable->saddr == lookup->saddr);
}
static u32 port_hash(struct port_lookup_key *k)
{
struct port_lookup_key lookup;
- lookup.saddr = mutable->port_config.saddr;
- lookup.daddr = mutable->port_config.daddr;
- lookup.key = mutable->port_config.in_key;
+ lookup.saddr = mutable->saddr;
+ lookup.daddr = mutable->daddr;
+ lookup.key = mutable->in_key;
lookup.tunnel_type = mutable->tunnel_type;
return port_hash(&lookup);
* not symmetric then PMTUD needs to be disabled since we won't have
* any way of synthesizing packets.
*/
- if ((mutable->port_config.flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) ==
+ if ((mutable->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) ==
(TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
OVS_CB(nskb)->tun_id = flow_key;
int mtu;
__be16 frag_off;
- frag_off = (mutable->port_config.flags & TNL_F_PMTUD) ? htons(IP_DF) : 0;
+ frag_off = (mutable->flags & TNL_F_PMTUD) ? htons(IP_DF) : 0;
if (frag_off)
mtu = dst_mtu(&rt_dst(rt))
- ETH_HLEN
iph->ihl = sizeof(struct iphdr) >> 2;
iph->frag_off = htons(IP_DF);
iph->protocol = tnl_vport->tnl_ops->ipproto;
- iph->tos = mutable->port_config.tos;
+ iph->tos = mutable->tos;
iph->daddr = rt->rt_dst;
iph->saddr = rt->rt_src;
- iph->ttl = mutable->port_config.ttl;
+ iph->ttl = mutable->ttl;
if (!iph->ttl)
iph->ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
void *cache_data;
int cache_len;
- if (!(mutable->port_config.flags & TNL_F_HDR_CACHE))
+ if (!(mutable->flags & TNL_F_HDR_CACHE))
return NULL;
/*
*cache = NULL;
tos = RT_TOS(tos);
- if (likely(tos == mutable->port_config.tos &&
- check_cache_valid(cur_cache, mutable))) {
+ if (likely(tos == mutable->tos && check_cache_valid(cur_cache, mutable))) {
*cache = cur_cache;
return cur_cache->rt;
} else {
struct rtable *rt;
struct flowi fl = { .nl_u = { .ip4_u =
- { .daddr = mutable->port_config.daddr,
- .saddr = mutable->port_config.saddr,
+ { .daddr = mutable->daddr,
+ .saddr = mutable->saddr,
.tos = tos } },
.proto = tnl_vport->tnl_ops->ipproto };
if (unlikely(ip_route_output_key(&init_net, &rt, &fl)))
return NULL;
- if (likely(tos == mutable->port_config.tos))
+ if (likely(tos == mutable->tos))
*cache = build_cache(vport, mutable, rt);
return rt;
else
inner_tos = 0;
- if (mutable->port_config.flags & TNL_F_TOS_INHERIT)
+ if (mutable->flags & TNL_F_TOS_INHERIT)
tos = inner_tos;
else
- tos = mutable->port_config.tos;
+ tos = mutable->tos;
tos = INET_ECN_encapsulate(tos, inner_tos);
}
/* TTL */
- ttl = mutable->port_config.ttl;
+ ttl = mutable->ttl;
if (!ttl)
ttl = dst_metric(&rt_dst(rt), RTAX_HOPLIMIT);
- if (mutable->port_config.flags & TNL_F_TTL_INHERIT) {
+ if (mutable->flags & TNL_F_TTL_INHERIT) {
if (skb->protocol == htons(ETH_P_IP))
ttl = ip_hdr(skb)->ttl;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
return sent_len;
}
-static int tnl_set_config(const void *config, const struct tnl_ops *tnl_ops,
+static const struct nla_policy tnl_policy[ODP_TUNNEL_ATTR_MAX + 1] = {
+ [ODP_TUNNEL_ATTR_FLAGS] = { .type = NLA_U32 },
+ [ODP_TUNNEL_ATTR_DST_IPV4] = { .type = NLA_U32 },
+ [ODP_TUNNEL_ATTR_SRC_IPV4] = { .type = NLA_U32 },
+ [ODP_TUNNEL_ATTR_OUT_KEY] = { .type = NLA_U64 },
+ [ODP_TUNNEL_ATTR_IN_KEY] = { .type = NLA_U64 },
+ [ODP_TUNNEL_ATTR_TOS] = { .type = NLA_U8 },
+ [ODP_TUNNEL_ATTR_TTL] = { .type = NLA_U8 },
+};
+
+/* Sets ODP_TUNNEL_ATTR_* fields in 'mutable', which must initially be zeroed. */
+static int tnl_set_config(struct nlattr *options, const struct tnl_ops *tnl_ops,
const struct vport *cur_vport,
struct tnl_mutable_config *mutable)
{
const struct vport *old_vport;
const struct tnl_mutable_config *old_mutable;
+ struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
+ int err;
- mutable->port_config = *(struct tnl_port_config *)config;
-
- if (mutable->port_config.daddr == 0)
+ if (!options)
return -EINVAL;
- if (mutable->port_config.tos != RT_TOS(mutable->port_config.tos))
+ err = nla_parse_nested(a, ODP_TUNNEL_ATTR_MAX, options, tnl_policy);
+ if (err)
+ return err;
+
+ if (!a[ODP_TUNNEL_ATTR_FLAGS] || !a[ODP_TUNNEL_ATTR_DST_IPV4])
return -EINVAL;
- mutable->tunnel_hlen = tnl_ops->hdr_len(&mutable->port_config);
+ mutable->flags = nla_get_u32(a[ODP_TUNNEL_ATTR_FLAGS]) & TNL_F_PUBLIC;
+
+ if (a[ODP_TUNNEL_ATTR_SRC_IPV4])
+ mutable->saddr = nla_get_be32(a[ODP_TUNNEL_ATTR_SRC_IPV4]);
+ mutable->daddr = nla_get_be32(a[ODP_TUNNEL_ATTR_DST_IPV4]);
+
+ if (a[ODP_TUNNEL_ATTR_TOS]) {
+ mutable->tos = nla_get_u8(a[ODP_TUNNEL_ATTR_TOS]);
+ if (mutable->tos != RT_TOS(mutable->tos))
+ return -EINVAL;
+ }
+
+ if (a[ODP_TUNNEL_ATTR_TTL])
+ mutable->ttl = nla_get_u8(a[ODP_TUNNEL_ATTR_TTL]);
+
+ mutable->tunnel_hlen = tnl_ops->hdr_len(mutable);
if (mutable->tunnel_hlen < 0)
return mutable->tunnel_hlen;
mutable->tunnel_hlen += sizeof(struct iphdr);
mutable->tunnel_type = tnl_ops->tunnel_type;
- if (mutable->port_config.flags & TNL_F_IN_KEY_MATCH) {
+ if (!a[ODP_TUNNEL_ATTR_IN_KEY]) {
mutable->tunnel_type |= TNL_T_KEY_MATCH;
- mutable->port_config.in_key = 0;
- } else
+ mutable->flags |= TNL_F_IN_KEY_MATCH;
+ } else {
mutable->tunnel_type |= TNL_T_KEY_EXACT;
+ mutable->in_key = nla_get_be64(a[ODP_TUNNEL_ATTR_IN_KEY]);
+ }
+
+ if (!a[ODP_TUNNEL_ATTR_OUT_KEY])
+ mutable->flags |= TNL_F_OUT_KEY_ACTION;
+ else
+ mutable->out_key = nla_get_be64(a[ODP_TUNNEL_ATTR_OUT_KEY]);
- old_vport = tnl_find_port(mutable->port_config.saddr,
- mutable->port_config.daddr,
- mutable->port_config.in_key,
- mutable->tunnel_type,
+ old_vport = tnl_find_port(mutable->saddr, mutable->daddr,
+ mutable->in_key, mutable->tunnel_type,
&old_mutable);
if (old_vport && old_vport != cur_vport)
return -EEXIST;
- if (mutable->port_config.flags & TNL_F_OUT_KEY_ACTION)
- mutable->port_config.out_key = 0;
-
return 0;
}
get_random_bytes(&initial_frag_id, sizeof(int));
atomic_set(&tnl_vport->frag_id, initial_frag_id);
- err = tnl_set_config(parms->config, tnl_ops, NULL, mutable);
+ err = tnl_set_config(parms->options, tnl_ops, NULL, mutable);
if (err)
goto error_free_mutable;
return ERR_PTR(err);
}
-int tnl_modify(struct vport *vport, struct odp_port *port)
+int tnl_set_options(struct vport *vport, struct nlattr *options)
{
struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+ const struct tnl_mutable_config *old_mutable;
struct tnl_mutable_config *mutable;
int err;
- mutable = kmemdup(rtnl_dereference(tnl_vport->mutable),
- sizeof(struct tnl_mutable_config), GFP_KERNEL);
+ mutable = kzalloc(sizeof(struct tnl_mutable_config), GFP_KERNEL);
if (!mutable) {
err = -ENOMEM;
goto error;
}
- err = tnl_set_config(port->config, tnl_vport->tnl_ops, vport, mutable);
+ /* Copy fields whose values should be retained. */
+ old_mutable = rtnl_dereference(tnl_vport->mutable);
+ mutable->seq = old_mutable->seq + 1;
+ memcpy(mutable->eth_addr, old_mutable->eth_addr, ETH_ALEN);
+ mutable->mtu = old_mutable->mtu;
+
+ /* Parse the others configured by userspace. */
+ err = tnl_set_config(options, tnl_vport->tnl_ops, vport, mutable);
if (err)
goto error_free;
- mutable->seq++;
-
err = move_port(vport, mutable);
if (err)
goto error_free;
return err;
}
+int tnl_get_options(const struct vport *vport, struct sk_buff *skb)
+{
+ const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+ const struct tnl_mutable_config *mutable = rcu_dereference_rtnl(tnl_vport->mutable);
+
+ NLA_PUT_U32(skb, ODP_TUNNEL_ATTR_FLAGS, mutable->flags & TNL_F_PUBLIC);
+ NLA_PUT_BE32(skb, ODP_TUNNEL_ATTR_DST_IPV4, mutable->daddr);
+
+ if (!(mutable->flags & TNL_F_IN_KEY_MATCH))
+ NLA_PUT_BE64(skb, ODP_TUNNEL_ATTR_IN_KEY, mutable->in_key);
+ if (!(mutable->flags & TNL_F_OUT_KEY_ACTION))
+ NLA_PUT_BE64(skb, ODP_TUNNEL_ATTR_OUT_KEY, mutable->out_key);
+ if (mutable->saddr)
+ NLA_PUT_BE32(skb, ODP_TUNNEL_ATTR_SRC_IPV4, mutable->saddr);
+ if (mutable->tos)
+ NLA_PUT_U8(skb, ODP_TUNNEL_ATTR_TOS, mutable->tos);
+ if (mutable->ttl)
+ NLA_PUT_U8(skb, ODP_TUNNEL_ATTR_TTL, mutable->ttl);
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
static void free_port_rcu(struct rcu_head *rcu)
{
struct tnl_vport *tnl_vport = container_of(rcu,
mutable = rtnl_dereference(tnl_vport->mutable);
- if (vport == tnl_find_port(mutable->port_config.saddr,
- mutable->port_config.daddr, mutable->port_config.in_key,
- mutable->tunnel_type, &old_mutable))
+ if (vport == tnl_find_port(mutable->saddr, mutable->daddr,
+ mutable->in_key, mutable->tunnel_type,
+ &old_mutable))
del_port(vport);
call_rcu(&tnl_vport->rcu, free_port_rcu);
return rcu_dereference_rtnl(tnl_vport->mutable)->eth_addr;
}
-void tnl_get_config(const struct vport *vport, void *config)
-{
- const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_port_config *port_config;
-
- port_config = &rcu_dereference_rtnl(tnl_vport->mutable)->port_config;
- memcpy(config, port_config, sizeof(*port_config));
-}
-
int tnl_get_mtu(const struct vport *vport)
{
const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
/*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
* Distributed under the terms of the GNU GPL version 2.
*
* Significant portions of this file may be copied from parts of the Linux
#define TNL_T_KEY_MATCH (1 << 11)
#define TNL_T_KEY_EITHER (TNL_T_KEY_EXACT | TNL_T_KEY_MATCH)
+/* Private flags not exposed to userspace in this form. */
+#define TNL_F_IN_KEY_MATCH (1 << 16) /* Store the key in tun_id to match in flow table. */
+#define TNL_F_OUT_KEY_ACTION (1 << 17) /* Get the key from a SET_TUNNEL action. */
+
+/* All public tunnel flags. */
+#define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
+ TNL_F_PMTUD | TNL_F_HDR_CACHE | TNL_F_IPSEC)
+
+/**
+ * struct tnl_mutable_config - modifiable configuration for a tunnel.
+ * @rcu: RCU callback head for deferred destruction.
+ * @seq: Sequence number for distinguishing configuration versions.
+ * @tunnel_type: Set of TNL_T_* flags that define lookup.
+ * @tunnel_hlen: Tunnel header length.
+ * @eth_addr: Source address for packets generated by tunnel itself
+ * (e.g. ICMP fragmentation needed messages).
+ * @mtu: MTU of tunnel.
+ * @in_key: Key to match on input, 0 for wildcard.
+ * @out_key: Key to use on output, 0 if this tunnel has no fixed output key.
+ * @flags: TNL_F_* flags.
+ * @saddr: IPv4 source address to match, 0 to accept any source address.
+ * @daddr: IPv4 destination of tunnel.
+ * @tos: IPv4 TOS value to use for tunnel, 0 if no fixed TOS.
+ * @ttl: IPv4 TTL value to use for tunnel, 0 if no fixed TTL.
+ */
struct tnl_mutable_config {
struct rcu_head rcu;
- unsigned seq; /* Sequence number to identify this config. */
+ unsigned seq;
- u32 tunnel_type; /* Set of TNL_T_* flags that define lookup. */
- unsigned tunnel_hlen; /* Tunnel header length. */
+ u32 tunnel_type;
+ unsigned tunnel_hlen;
unsigned char eth_addr[ETH_ALEN];
unsigned mtu;
- struct tnl_port_config port_config;
+ /* Configured via ODP_TUNNEL_ATTR_* attributes. */
+ __be64 in_key;
+ __be64 out_key;
+ u32 flags;
+ __be32 saddr;
+ __be32 daddr;
+ u8 tos;
+ u8 ttl;
};
struct tnl_ops {
* build_header() (i.e. excludes the IP header). Returns a negative
* error code if the configuration is invalid.
*/
- int (*hdr_len)(const struct tnl_port_config *);
+ int (*hdr_len)(const struct tnl_mutable_config *);
/*
* Builds the static portion of the tunnel header, which is stored in
struct vport *tnl_create(const struct vport_parms *, const struct vport_ops *,
const struct tnl_ops *);
-int tnl_modify(struct vport *, struct odp_port *);
int tnl_destroy(struct vport *);
+
+int tnl_set_options(struct vport *, struct nlattr *);
+int tnl_get_options(const struct vport *, struct sk_buff *);
+
int tnl_set_mtu(struct vport *vport, int mtu);
int tnl_set_addr(struct vport *vport, const unsigned char *addr);
const char *tnl_get_name(const struct vport *vport);
const unsigned char *tnl_get_addr(const struct vport *vport);
-void tnl_get_config(const struct vport *vport, void *config);
int tnl_get_mtu(const struct vport *vport);
int tnl_send(struct vport *vport, struct sk_buff *skb);
void tnl_rcv(struct vport *vport, struct sk_buff *skb);
static struct socket *capwap_rcv_socket;
-static int capwap_hdr_len(const struct tnl_port_config *port_config)
+static int capwap_hdr_len(const struct tnl_mutable_config *mutable)
{
- /* CAPWAP has neither checksums nor keys, so reject ports with those. */
- if (port_config->flags & (TNL_F_CSUM | TNL_F_IN_KEY_MATCH |
- TNL_F_OUT_KEY_ACTION))
+ /* CAPWAP has no checksums. */
+ if (mutable->flags & TNL_F_CSUM)
return -EINVAL;
- if (port_config->in_key != 0 || port_config->out_key != 0)
+ /* CAPWAP has no keys, so check that the configuration for keys is the
+ * default if no key-specific attributes are used.
+ */
+ if ((mutable->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) !=
+ (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
return -EINVAL;
return CAPWAP_HLEN;
.init = capwap_init,
.exit = capwap_exit,
.create = capwap_create,
- .modify = tnl_modify,
.destroy = tnl_destroy,
.set_mtu = tnl_set_mtu,
.set_addr = tnl_set_addr,
.get_name = tnl_get_name,
.get_addr = tnl_get_addr,
- .get_config = tnl_get_config,
+ .get_options = tnl_get_options,
+ .set_options = tnl_set_options,
.get_dev_flags = vport_gen_get_dev_flags,
.is_running = vport_gen_is_running,
.get_operstate = vport_gen_get_operstate,
__be16 protocol;
};
-static int gre_hdr_len(const struct tnl_port_config *port_config)
+static int gre_hdr_len(const struct tnl_mutable_config *mutable)
{
int len;
len = GRE_HEADER_SECTION;
- if (port_config->flags & TNL_F_CSUM)
+ if (mutable->flags & TNL_F_CSUM)
len += GRE_HEADER_SECTION;
- if (port_config->out_key ||
- port_config->flags & TNL_F_OUT_KEY_ACTION)
+ if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
len += GRE_HEADER_SECTION;
return len;
greh->protocol = htons(ETH_P_TEB);
greh->flags = 0;
- if (mutable->port_config.flags & TNL_F_CSUM) {
+ if (mutable->flags & TNL_F_CSUM) {
greh->flags |= GRE_CSUM;
*options = 0;
options++;
}
- if (mutable->port_config.out_key ||
- mutable->port_config.flags & TNL_F_OUT_KEY_ACTION)
+ if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
greh->flags |= GRE_KEY;
- if (mutable->port_config.out_key)
- *options = be64_get_low32(mutable->port_config.out_key);
+ if (mutable->out_key)
+ *options = be64_get_low32(mutable->out_key);
}
static struct sk_buff *gre_update_header(const struct vport *vport,
- GRE_HEADER_SECTION);
/* Work backwards over the options so the checksum is last. */
- if (mutable->port_config.flags & TNL_F_OUT_KEY_ACTION) {
+ if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
*options = be64_get_low32(OVS_CB(skb)->tun_id);
options--;
}
- if (mutable->port_config.flags & TNL_F_CSUM)
+ if (mutable->flags & TNL_F_CSUM)
*(__sum16 *)options = csum_fold(skb_checksum(skb,
skb_transport_offset(skb),
skb->len - skb_transport_offset(skb),
* out key as if it were the in key and then check to see if the input
* and output keys are the same.
*/
- if (mutable->port_config.in_key != mutable->port_config.out_key)
+ if (mutable->in_key != mutable->out_key)
return;
- if (!!(mutable->port_config.flags & TNL_F_IN_KEY_MATCH) !=
- !!(mutable->port_config.flags & TNL_F_OUT_KEY_ACTION))
+ if (!!(mutable->flags & TNL_F_IN_KEY_MATCH) !=
+ !!(mutable->flags & TNL_F_OUT_KEY_ACTION))
return;
- if ((mutable->port_config.flags & TNL_F_CSUM) && !(flags & GRE_CSUM))
+ if ((mutable->flags & TNL_F_CSUM) && !(flags & GRE_CSUM))
return;
tunnel_hdr_len += iph->ihl << 2;
goto error;
}
- if (mutable->port_config.flags & TNL_F_IN_KEY_MATCH)
+ if (mutable->flags & TNL_F_IN_KEY_MATCH)
OVS_CB(skb)->tun_id = key;
else
OVS_CB(skb)->tun_id = 0;
.init = gre_init,
.exit = gre_exit,
.create = gre_create,
- .modify = tnl_modify,
.destroy = tnl_destroy,
.set_mtu = tnl_set_mtu,
.set_addr = tnl_set_addr,
.get_name = tnl_get_name,
.get_addr = tnl_get_addr,
- .get_config = tnl_get_config,
+ .get_options = tnl_get_options,
+ .set_options = tnl_set_options,
.get_dev_flags = vport_gen_get_dev_flags,
.is_running = vport_gen_is_running,
.get_operstate = vport_gen_get_operstate,
kfree(peer_table);
}
-static int patch_set_config(struct vport *vport, const void *config,
+static const struct nla_policy patch_policy[ODP_PATCH_ATTR_MAX + 1] = {
+#ifdef HAVE_NLA_NUL_STRING
+ [ODP_PATCH_ATTR_PEER] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+#endif
+};
+
+static int patch_set_config(struct vport *vport, const struct nlattr *options,
struct patch_config *patchconf)
{
struct patch_vport *patch_vport = patch_vport_priv(vport);
- char peer_name[IFNAMSIZ];
+ struct nlattr *a[ODP_PATCH_ATTR_MAX + 1];
+ const char *peer_name;
+ int err;
+
+ if (!options)
+ return -EINVAL;
- strlcpy(peer_name, config, IFNAMSIZ);
+ err = nla_parse_nested(a, ODP_PATCH_ATTR_MAX, options, patch_policy);
+ if (err)
+ return err;
+
+ if (!a[ODP_PATCH_ATTR_PEER] || VERIFY_NUL_STRING(a[ODP_PATCH_ATTR_PEER], IFNAMSIZ - 1))
+ return -EINVAL;
+ peer_name = nla_data(a[ODP_PATCH_ATTR_PEER]);
if (!strcmp(patch_vport->name, peer_name))
return -EINVAL;
goto error_free_vport;
}
- err = patch_set_config(vport, parms->config, patchconf);
+ err = patch_set_config(vport, parms->options, patchconf);
if (err)
goto error_free_patchconf;
return ERR_PTR(err);
}
-static int patch_modify(struct vport *vport, struct odp_port *port)
+static void free_port_rcu(struct rcu_head *rcu)
+{
+ struct patch_vport *patch_vport = container_of(rcu,
+ struct patch_vport, rcu);
+
+ kfree((struct patch_config __force *)patch_vport->patchconf);
+ vport_free(vport_from_priv(patch_vport));
+}
+
+static int patch_destroy(struct vport *vport)
+{
+ struct patch_vport *patch_vport = patch_vport_priv(vport);
+
+ update_peers(patch_vport->name, NULL);
+ hlist_del(&patch_vport->hash_node);
+ call_rcu(&patch_vport->rcu, free_port_rcu);
+
+ return 0;
+}
+
+static int patch_set_options(struct vport *vport, struct nlattr *options)
{
struct patch_vport *patch_vport = patch_vport_priv(vport);
struct patch_config *patchconf;
goto error;
}
- err = patch_set_config(vport, port->config, patchconf);
+ err = patch_set_config(vport, options, patchconf);
if (err)
goto error_free;
assign_config_rcu(vport, patchconf);
-
+
hlist_del(&patch_vport->hash_node);
rcu_assign_pointer(patch_vport->peer, vport_locate(patchconf->peer_name));
return err;
}
-static void free_port_rcu(struct rcu_head *rcu)
-{
- struct patch_vport *patch_vport = container_of(rcu,
- struct patch_vport, rcu);
-
- kfree((struct patch_config __force *)patch_vport->patchconf);
- vport_free(vport_from_priv(patch_vport));
-}
-
-static int patch_destroy(struct vport *vport)
-{
- struct patch_vport *patch_vport = patch_vport_priv(vport);
-
- update_peers(patch_vport->name, NULL);
- hlist_del(&patch_vport->hash_node);
- call_rcu(&patch_vport->rcu, free_port_rcu);
-
- return 0;
-}
-
static void update_peers(const char *name, struct vport *vport)
{
struct hlist_head *bucket = hash_bucket(name);
return rcu_dereference_rtnl(patch_vport->patchconf)->eth_addr;
}
-static void patch_get_config(const struct vport *vport, void *config)
+static int patch_get_options(const struct vport *vport, struct sk_buff *skb)
{
- const struct patch_vport *patch_vport = patch_vport_priv(vport);
- const char *peer_name;
+ struct patch_vport *patch_vport = patch_vport_priv(vport);
+ struct patch_config *patchconf = rcu_dereference_rtnl(patch_vport->patchconf);
- peer_name = rcu_dereference_rtnl(patch_vport->patchconf)->peer_name;
- strlcpy(config, peer_name, VPORT_CONFIG_SIZE);
+ return nla_put_string(skb, ODP_PATCH_ATTR_PEER, patchconf->peer_name);
}
static int patch_get_mtu(const struct vport *vport)
.init = patch_init,
.exit = patch_exit,
.create = patch_create,
- .modify = patch_modify,
.destroy = patch_destroy,
.set_mtu = patch_set_mtu,
.set_addr = patch_set_addr,
.get_name = patch_get_name,
.get_addr = patch_get_addr,
- .get_config = patch_get_config,
+ .get_options = patch_get_options,
+ .set_options = patch_set_options,
.get_dev_flags = vport_gen_get_dev_flags,
.is_running = vport_gen_is_running,
.get_operstate = vport_gen_get_operstate,
kfree(dev_table);
}
-/**
- * vport_user_mod - modify existing vport device (for userspace callers)
- *
- * @uport: New configuration for vport
- *
- * Modifies an existing device with the specified configuration (which is
- * dependent on device type). This function is for userspace callers and
- * assumes no locks are held.
- */
-int vport_user_mod(const struct odp_port __user *uport)
-{
- struct odp_port port;
- struct vport *vport;
- int err;
-
- if (copy_from_user(&port, uport, sizeof(port)))
- return -EFAULT;
-
- port.devname[IFNAMSIZ - 1] = '\0';
-
- rtnl_lock();
-
- vport = vport_locate(port.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- vport_lock();
- err = vport_mod(vport, &port);
- vport_unlock();
-
-out:
- rtnl_unlock();
- return err;
-}
-
-/**
- * vport_user_stats_get - retrieve device stats (for userspace callers)
- *
- * @ustats_req: Stats request parameters.
- *
- * Retrieves transmit, receive, and error stats for the given device. This
- * function is for userspace callers and assumes no locks are held.
- */
-int vport_user_stats_get(struct odp_vport_stats_req __user *ustats_req)
-{
- struct odp_vport_stats_req stats_req;
- struct vport *vport;
- int err;
-
- if (copy_from_user(&stats_req, ustats_req, sizeof(struct odp_vport_stats_req)))
- return -EFAULT;
-
- stats_req.devname[IFNAMSIZ - 1] = '\0';
-
- vport_lock();
-
- vport = vport_locate(stats_req.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- err = vport_get_stats(vport, &stats_req.stats);
-
-out:
- vport_unlock();
-
- if (!err)
- if (copy_to_user(ustats_req, &stats_req, sizeof(struct odp_vport_stats_req)))
- err = -EFAULT;
-
- return err;
-}
-
-/**
- * vport_user_stats_set - sets offset device stats (for userspace callers)
- *
- * @ustats_req: Stats set parameters.
- *
- * Provides a set of transmit, receive, and error stats to be added as an
- * offset to the collect data when stats are retreived. Some devices may not
- * support setting the stats, in which case the result will always be
- * -EOPNOTSUPP. This function is for userspace callers and assumes no locks
- * are held.
- */
-int vport_user_stats_set(struct odp_vport_stats_req __user *ustats_req)
-{
- struct odp_vport_stats_req stats_req;
- struct vport *vport;
- int err;
-
- if (copy_from_user(&stats_req, ustats_req, sizeof(struct odp_vport_stats_req)))
- return -EFAULT;
-
- stats_req.devname[IFNAMSIZ - 1] = '\0';
-
- rtnl_lock();
- vport_lock();
-
- vport = vport_locate(stats_req.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- err = vport_set_stats(vport, &stats_req.stats);
-
-out:
- vport_unlock();
- rtnl_unlock();
- return err;
-}
-
-
-/**
- * vport_user_ether_get - retrieve device Ethernet address (for userspace callers)
- *
- * @uvport_ether: Ethernet address request parameters.
- *
- * Retrieves the Ethernet address of the given device. This function is for
- * userspace callers and assumes no locks are held.
- */
-int vport_user_ether_get(struct odp_vport_ether __user *uvport_ether)
-{
- struct odp_vport_ether vport_ether;
- struct vport *vport;
- int err = 0;
-
- if (copy_from_user(&vport_ether, uvport_ether, sizeof(struct odp_vport_ether)))
- return -EFAULT;
-
- vport_ether.devname[IFNAMSIZ - 1] = '\0';
-
- vport_lock();
-
- vport = vport_locate(vport_ether.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- rcu_read_lock();
- memcpy(vport_ether.ether_addr, vport_get_addr(vport), ETH_ALEN);
- rcu_read_unlock();
-
-out:
- vport_unlock();
-
- if (!err)
- if (copy_to_user(uvport_ether, &vport_ether, sizeof(struct odp_vport_ether)))
- err = -EFAULT;
-
- return err;
-}
-
-/**
- * vport_user_ether_set - set device Ethernet address (for userspace callers)
- *
- * @uvport_ether: Ethernet address request parameters.
- *
- * Sets the Ethernet address of the given device. Some devices may not support
- * setting the Ethernet address, in which case the result will always be
- * -EOPNOTSUPP. This function is for userspace callers and assumes no locks
- * are held.
- */
-int vport_user_ether_set(struct odp_vport_ether __user *uvport_ether)
-{
- struct odp_vport_ether vport_ether;
- struct vport *vport;
- int err;
-
- if (copy_from_user(&vport_ether, uvport_ether, sizeof(struct odp_vport_ether)))
- return -EFAULT;
-
- vport_ether.devname[IFNAMSIZ - 1] = '\0';
-
- rtnl_lock();
- vport_lock();
-
- vport = vport_locate(vport_ether.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- err = vport_set_addr(vport, vport_ether.ether_addr);
-
-out:
- vport_unlock();
- rtnl_unlock();
- return err;
-}
-
-/**
- * vport_user_mtu_get - retrieve device MTU (for userspace callers)
- *
- * @uvport_mtu: MTU request parameters.
- *
- * Retrieves the MTU of the given device. This function is for userspace
- * callers and assumes no locks are held.
- */
-int vport_user_mtu_get(struct odp_vport_mtu __user *uvport_mtu)
-{
- struct odp_vport_mtu vport_mtu;
- struct vport *vport;
- int err = 0;
-
- if (copy_from_user(&vport_mtu, uvport_mtu, sizeof(struct odp_vport_mtu)))
- return -EFAULT;
-
- vport_mtu.devname[IFNAMSIZ - 1] = '\0';
-
- vport_lock();
-
- vport = vport_locate(vport_mtu.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- vport_mtu.mtu = vport_get_mtu(vport);
-
-out:
- vport_unlock();
-
- if (!err)
- if (copy_to_user(uvport_mtu, &vport_mtu, sizeof(struct odp_vport_mtu)))
- err = -EFAULT;
-
- return err;
-}
-
-/**
- * vport_user_mtu_set - set device MTU (for userspace callers)
- *
- * @uvport_mtu: MTU request parameters.
- *
- * Sets the MTU of the given device. Some devices may not support setting the
- * MTU, in which case the result will always be -EOPNOTSUPP. This function is
- * for userspace callers and assumes no locks are held.
- */
-int vport_user_mtu_set(struct odp_vport_mtu __user *uvport_mtu)
-{
- struct odp_vport_mtu vport_mtu;
- struct vport *vport;
- int err;
-
- if (copy_from_user(&vport_mtu, uvport_mtu, sizeof(struct odp_vport_mtu)))
- return -EFAULT;
-
- vport_mtu.devname[IFNAMSIZ - 1] = '\0';
-
- rtnl_lock();
- vport_lock();
-
- vport = vport_locate(vport_mtu.devname);
- if (!vport) {
- err = -ENODEV;
- goto out;
- }
-
- err = vport_set_mtu(vport, vport_mtu.mtu);
-
-out:
- vport_unlock();
- rtnl_unlock();
- return err;
-}
-
static struct hlist_head *hash_bucket(const char *name)
{
unsigned int hash = full_name_hash(name, strlen(name));
}
/**
- * vport_mod - modify existing vport device (for kernel callers)
+ * vport_set_options - modify existing vport device (for kernel callers)
*
* @vport: vport to modify.
* @port: New configuration.
*
* Modifies an existing device with the specified configuration (which is
- * dependent on device type). Both RTNL and vport locks must be held.
+ * dependent on device type). RTNL lock must be held.
*/
-int vport_mod(struct vport *vport, struct odp_port *port)
+int vport_set_options(struct vport *vport, struct nlattr *options)
{
ASSERT_RTNL();
- ASSERT_VPORT();
- if (vport->ops->modify)
- return vport->ops->modify(vport, port);
- else
+ if (!vport->ops->set_options)
return -EOPNOTSUPP;
+ return vport->ops->set_options(vport, options);
}
/**
}
/**
- * vport_get_config - retrieve device configuration
+ * vport_get_options - retrieve device options
*
- * @vport: vport from which to retrieve the configuration.
- * @config: buffer to store config, which must be at least the length
- * of VPORT_CONFIG_SIZE.
+ * @vport: vport from which to retrieve the options.
+ * @skb: sk_buff where options should be appended.
*
- * Retrieves the configuration of the given device. Either RTNL lock or
- * rcu_read_lock must be held.
+ * Retrieves the configuration of the given device, appending an
+ * %ODP_VPORT_ATTR_OPTIONS attribute that in turn contains nested
+ * vport-specific attributes to @skb. Either RTNL lock or rcu_read_lock must
+ * be held.
+ *
+ * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room, or another
+ * negative error code if a real error occurred.
*/
-void vport_get_config(const struct vport *vport, void *config)
+int vport_get_options(const struct vport *vport, struct sk_buff *skb)
{
- if (vport->ops->get_config)
- vport->ops->get_config(vport, config);
+ struct nlattr *nla;
+
+ nla = nla_nest_start(skb, ODP_VPORT_ATTR_OPTIONS);
+ if (!nla)
+ return -EMSGSIZE;
+
+ if (vport->ops->get_options) {
+ int err = vport->ops->get_options(vport, skb);
+ if (err)
+ return err;
+ }
+
+ nla_nest_end(skb, nla);
+ return 0;
}
/**
/* The following definitions are for users of the vport subsytem: */
-int vport_user_mod(const struct odp_port __user *);
-
-int vport_user_stats_get(struct odp_vport_stats_req __user *);
-int vport_user_stats_set(struct odp_vport_stats_req __user *);
-int vport_user_ether_get(struct odp_vport_ether __user *);
-int vport_user_ether_set(struct odp_vport_ether __user *);
-int vport_user_mtu_get(struct odp_vport_mtu __user *);
-int vport_user_mtu_set(struct odp_vport_mtu __user *);
-
void vport_lock(void);
void vport_unlock(void);
void vport_exit(void);
struct vport *vport_add(const struct vport_parms *);
-int vport_mod(struct vport *, struct odp_port *);
int vport_del(struct vport *);
struct vport *vport_locate(const char *name);
int vport_get_iflink(const struct vport *);
int vport_get_mtu(const struct vport *);
-void vport_get_config(const struct vport *, void *);
+
+int vport_set_options(struct vport *, struct nlattr *options);
+int vport_get_options(const struct vport *, struct sk_buff *);
int vport_send(struct vport *, struct sk_buff *);
*
* @name: New vport's name.
* @type: New vport's type.
- * @config: Kernel copy of 'config' member of &struct odp_port describing
- * configuration for new port. Exactly %VPORT_CONFIG_SIZE bytes.
+ * @options: %ODP_VPORT_ATTR_OPTIONS attribute from Netlink message, %NULL if
+ * none was supplied.
* @dp: New vport's datapath.
* @port_no: New vport's port number.
*/
struct vport_parms {
const char *name;
enum odp_vport_type type;
- const void *config;
+ struct nlattr *options;
/* For vport_alloc(). */
struct datapath *dp;
* @exit: Called at module unload.
* @create: Create a new vport configured as specified. On success returns
* a new vport allocated with vport_alloc(), otherwise an ERR_PTR() value.
- * @modify: Modify the configuration of an existing vport. May be null if
- * modification is not supported.
* @destroy: Detach and destroy a vport.
+ * @set_options: Modify the configuration of an existing vport. May be %NULL
+ * if modification is not supported.
+ * @get_options: Appends vport-specific attributes for the configuration of an
+ * existing vport to a &struct sk_buff. May be %NULL for a vport that does not
+ * have any configuration.
* @set_mtu: Set the device's MTU. May be null if not supported.
* @set_addr: Set the device's MAC address. May be null if not supported.
* @get_name: Get the device's name.
/* Called with RTNL lock. */
struct vport *(*create)(const struct vport_parms *);
- int (*modify)(struct vport *, struct odp_port *);
int (*destroy)(struct vport *);
+ int (*set_options)(struct vport *, struct nlattr *);
+ int (*get_options)(const struct vport *, struct sk_buff *);
+
int (*set_mtu)(struct vport *, int mtu);
int (*set_addr)(struct vport *, const unsigned char *);
#define ODP_GET_LISTEN_MASK _IOW('O', 5, int)
#define ODP_SET_LISTEN_MASK _IOR('O', 6, int)
-#define ODP_VPORT_ATTACH _IOR('O', 7, struct odp_port)
-#define ODP_VPORT_DETACH _IOR('O', 8, int)
-#define ODP_VPORT_QUERY _IOWR('O', 9, struct odp_port)
-#define ODP_VPORT_DUMP _IOWR('O', 10, struct odp_vport_dump)
+#define ODP_VPORT_NEW _IOR('O', 7, struct odp_vport)
+#define ODP_VPORT_DEL _IOR('O', 8, struct odp_vport)
+#define ODP_VPORT_GET _IOWR('O', 9, 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_FLOW_GET _IOWR('O', 13, struct odp_flowvec)
#define ODP_FLOW_PUT _IOWR('O', 14, struct odp_flow)
#define ODP_SET_SFLOW_PROBABILITY _IOR('O', 19, int)
#define ODP_GET_SFLOW_PROBABILITY _IOW('O', 20, int)
-#define ODP_VPORT_MOD _IOR('O', 22, struct odp_port)
-#define ODP_VPORT_STATS_GET _IOWR('O', 24, struct odp_vport_stats_req)
-#define ODP_VPORT_ETHER_GET _IOWR('O', 25, struct odp_vport_ether)
-#define ODP_VPORT_ETHER_SET _IOW('O', 26, struct odp_vport_ether)
-#define ODP_VPORT_MTU_GET _IOWR('O', 27, struct odp_vport_mtu)
-#define ODP_VPORT_MTU_SET _IOW('O', 28, struct odp_vport_mtu)
-#define ODP_VPORT_STATS_SET _IOWR('O', 29, struct odp_vport_stats_req)
struct odp_stats {
/* Flows. */
uint32_t len;
};
-#define VPORT_CONFIG_SIZE 32
-struct odp_port {
- char devname[16]; /* IFNAMSIZ */
- uint32_t type; /* One of ODP_VPORT_TYPE_*. */
- uint16_t port;
- uint16_t dp_idx;
- uint32_t reserved2;
- __aligned_u64 config[VPORT_CONFIG_SIZE / 8]; /* type-specific */
-};
-
enum odp_vport_type {
ODP_VPORT_TYPE_UNSPEC,
ODP_VPORT_TYPE_NETDEV, /* network device */
#define ODP_VPORT_TYPE_MAX (__ODP_VPORT_TYPE_MAX - 1)
/**
- * struct odp_vport_dump - ODP_VPORT_DUMP argument.
- * @port: Points to port structure to fill in.
- * @port_no: Minimum port number of interest.
+ * struct odp_vport - header with basic information about a virtual port.
+ * @dp_idx: Number of datapath to which the vport belongs.
+ * @len: Length of this structure plus the Netlink attributes following it.
+ * @total_len: Total space available for kernel reply to request.
*
- * Used to iterate through vports one at a time. The kernel fills in @port
- * with the information for the configured port with the smallest port number
- * greater than or equal to @port_no. If there is no such port, it sets
- * @port->devname to the empty string.
+ * Followed by &struct nlattr attributes, whose types are drawn from
+ * %ODP_VPORT_ATTR_*, up to a length of @len bytes including the &struct
+ * odp_vport header.
*/
-struct odp_vport_dump {
- struct odp_port *port;
- uint32_t port_no;
+struct odp_vport {
+ uint32_t dp_idx;
+ uint32_t len;
+ uint32_t total_len;
+};
+
+enum {
+ ODP_VPORT_ATTR_UNSPEC,
+ ODP_VPORT_ATTR_PORT_NO, /* port number within datapath */
+ ODP_VPORT_ATTR_TYPE, /* 32-bit ODP_VPORT_TYPE_* constant. */
+ ODP_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */
+ ODP_VPORT_ATTR_STATS, /* struct rtnl_link_stats64 */
+ ODP_VPORT_ATTR_ADDRESS, /* hardware address */
+ ODP_VPORT_ATTR_MTU, /* 32-bit maximum transmission unit */
+ ODP_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
+ ODP_VPORT_ATTR_IFINDEX, /* 32-bit ifindex of backing netdev */
+ ODP_VPORT_ATTR_IFLINK, /* 32-bit ifindex on which packets are sent */
+ __ODP_VPORT_ATTR_MAX
};
+#define ODP_VPORT_ATTR_MAX (__ODP_VPORT_ATTR_MAX - 1)
+
+/* ODP_VPORT_ATTR_OPTIONS attributes for patch vports. */
+enum {
+ ODP_PATCH_ATTR_UNSPEC,
+ ODP_PATCH_ATTR_PEER, /* name of peer vport, as a string */
+ __ODP_PATCH_ATTR_MAX
+};
+
+#define ODP_PATCH_ATTR_MAX (__ODP_PATCH_ATTR_MAX - 1)
+
struct odp_flow_stats {
uint64_t n_packets; /* Number of matched packets. */
uint64_t n_bytes; /* Number of matched bytes. */
uint32_t length;
};
-#define VPORT_TYPE_SIZE 16
-struct odp_vport_add {
- char port_type[VPORT_TYPE_SIZE];
- char devname[16]; /* IFNAMSIZ */
- void *config;
-};
-
-struct odp_vport_mod {
- char devname[16]; /* IFNAMSIZ */
- void *config;
-};
-
-struct odp_vport_stats_req {
- char devname[16]; /* IFNAMSIZ */
- struct rtnl_link_stats64 stats;
-};
-
-struct odp_vport_ether {
- char devname[16]; /* IFNAMSIZ */
- unsigned char ether_addr[6];
-};
-
-struct odp_vport_mtu {
- char devname[16]; /* IFNAMSIZ */
- uint16_t mtu;
-};
-
#endif /* openvswitch/datapath-protocol.h */
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
*
* This file is offered under your choice of two licenses: Apache 2.0 or GNU
* GPL 2.0 or later. The permission statements for each of these licenses is
#include <linux/types.h>
#include "openvswitch/datapath-protocol.h"
-#define TNL_F_CSUM (1 << 1) /* Checksum packets. */
-#define TNL_F_IN_KEY_MATCH (1 << 2) /* Store the key in tun_id to match in flow table. */
-#define TNL_F_OUT_KEY_ACTION (1 << 3) /* Get the key from a SET_TUNNEL action. */
-#define TNL_F_TOS_INHERIT (1 << 4) /* Inherit the ToS from the inner packet. */
-#define TNL_F_TTL_INHERIT (1 << 5) /* Inherit the TTL from the inner packet. */
-#define TNL_F_PMTUD (1 << 6) /* Enable path MTU discovery. */
-#define TNL_F_HDR_CACHE (1 << 7) /* Enable tunnel header caching. */
-#define TNL_F_IPSEC (1 << 8) /* Traffic is IPsec encrypted. */
-
-/* This goes in the "config" member of struct odp_port for tunnel vports. */
-struct tnl_port_config {
- __aligned_be64 in_key;
- __aligned_be64 out_key;
- __u32 flags;
- __be32 saddr;
- __be32 daddr;
- __u8 tos;
- __u8 ttl;
+/* ODP_VPORT_ATTR_OPTIONS attributes for tunnels.
+ *
+ * ODP_TUNNEL_ATTR_FLAGS and ODP_TUNNEL_ATTR_DST_IPV4 are required. All other
+ * attributes are optional.
+ */
+enum {
+ ODP_TUNNEL_ATTR_UNSPEC,
+ ODP_TUNNEL_ATTR_FLAGS, /* 32-bit TNL_F_*. */
+ ODP_TUNNEL_ATTR_DST_IPV4, /* IPv4 destination address. */
+ ODP_TUNNEL_ATTR_SRC_IPV4, /* IPv4 source address. */
+ ODP_TUNNEL_ATTR_OUT_KEY, /* __be64 key to use on output. */
+ ODP_TUNNEL_ATTR_IN_KEY, /* __be64 key to match on input. */
+ ODP_TUNNEL_ATTR_TOS, /* 8-bit TOS value. */
+ ODP_TUNNEL_ATTR_TTL, /* 8-bit TTL value. */
+ __ODP_TUNNEL_ATTR_MAX
};
+#define ODP_TUNNEL_ATTR_MAX (__ODP_TUNNEL_ATTR_MAX - 1)
+
+#define TNL_F_CSUM (1 << 0) /* Checksum packets. */
+#define TNL_F_TOS_INHERIT (1 << 1) /* Inherit the ToS from the inner packet. */
+#define TNL_F_TTL_INHERIT (1 << 2) /* Inherit the TTL from the inner packet. */
+#define TNL_F_PMTUD (1 << 3) /* Enable path MTU discovery. */
+#define TNL_F_HDR_CACHE (1 << 4) /* Enable tunnel header caching. */
+#define TNL_F_IPSEC (1 << 5) /* Traffic is IPsec encrypted. */
+
#endif /* openvswitch/tunnel.h */
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
static int do_ioctl(const struct dpif *, int cmd, const void *arg);
-static int lookup_internal_device(const char *name, int *dp_idx, int *port_no);
-static int lookup_minor(const char *name, int *minor);
-static int finish_open(struct dpif *, const char *local_ifname);
+static int open_dpif(const struct dpif_linux_vport *local_vport,
+ struct dpif **);
static int get_openvswitch_major(void);
-static int create_minor(const char *name, int minor, struct dpif **dpifp);
-static int open_minor(int minor, struct dpif **dpifp);
+static int create_minor(const char *name, int minor);
+static int open_minor(int minor, int *fdp);
static int make_openvswitch_device(int minor, char **fnp);
static void dpif_linux_port_changed(const struct rtnetlink_link_change *,
void *dpif);
dpif_linux_open(const struct dpif_class *class OVS_UNUSED, const char *name,
bool create, struct dpif **dpifp)
{
+ struct dpif_linux_vport request, reply;
+ struct ofpbuf *buf;
int minor;
+ int error;
minor = !strncmp(name, "dp", 2)
&& isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1;
if (create) {
if (minor >= 0) {
- return create_minor(name, minor, dpifp);
+ error = create_minor(name, minor);
+ if (error) {
+ return error;
+ }
} else {
/* Scan for unused minor number. */
- for (minor = 0; minor < ODP_MAX; minor++) {
- int error = create_minor(name, minor, dpifp);
- if (error != EBUSY) {
+ for (minor = 0; ; minor++) {
+ if (minor >= ODP_MAX) {
+ /* All datapath numbers in use. */
+ return ENOBUFS;
+ }
+
+ error = create_minor(name, minor);
+ if (!error) {
+ break;
+ } else if (error != EBUSY) {
return error;
}
}
-
- /* All datapath numbers in use. */
- return ENOBUFS;
}
+ }
+
+ dpif_linux_vport_init(&request);
+ request.cmd = ODP_VPORT_GET;
+ request.port_no = ODPP_LOCAL;
+ if (minor >= 0) {
+ request.dp_idx = minor;
} else {
- struct dpif_linux *dpif;
- struct odp_port port;
- int error;
+ request.name = name;
+ }
- if (minor < 0) {
- error = lookup_minor(name, &minor);
- if (error) {
- return error;
- }
- }
+ error = dpif_linux_vport_transact(&request, &reply, &buf);
+ if (error) {
+ return error;
+ } else if (reply.port_no != ODPP_LOCAL) {
+ /* This is an Open vSwitch device but not the local port. We
+ * intentionally support only using the name of the local port as the
+ * name of a datapath; otherwise, it would be too difficult to
+ * enumerate all the names of a datapath. */
+ error = EOPNOTSUPP;
+ } else {
+ error = open_dpif(&reply, dpifp);
+ }
- error = open_minor(minor, dpifp);
- if (error) {
- return error;
- }
- dpif = dpif_linux_cast(*dpifp);
-
- /* We need the local port's ifindex for the poll function. Start by
- * getting the local port's name. */
- memset(&port, 0, sizeof port);
- port.port = ODPP_LOCAL;
- if (ioctl(dpif->fd, ODP_VPORT_QUERY, &port)) {
- error = errno;
- if (error != ENODEV) {
- VLOG_WARN("%s: probe returned unexpected error: %s",
- dpif_name(*dpifp), strerror(error));
- }
- dpif_uninit(*dpifp, true);
- return error;
- }
+ ofpbuf_delete(buf);
+ return error;
+}
+
+static int
+open_dpif(const struct dpif_linux_vport *local_vport, struct dpif **dpifp)
+{
+ int dp_idx = local_vport->dp_idx;
+ struct dpif_linux *dpif;
+ char *name;
+ int error;
+ int fd;
+
+ error = open_minor(dp_idx, &fd);
+ if (error) {
+ goto error;
+ }
- /* Then use that to finish up opening. */
- return finish_open(&dpif->dpif, port.devname);
+ dpif = xmalloc(sizeof *dpif);
+ error = rtnetlink_link_notifier_register(&dpif->port_notifier,
+ dpif_linux_port_changed, dpif);
+ if (error) {
+ goto error_free;
}
+
+ name = xasprintf("dp%d", dp_idx);
+ dpif_init(&dpif->dpif, &dpif_linux_class, name, dp_idx, dp_idx);
+ free(name);
+
+ dpif->fd = fd;
+ dpif->local_ifname = xstrdup(local_vport->name);
+ dpif->local_ifindex = local_vport->ifindex;
+ dpif->minor = dp_idx;
+ shash_init(&dpif->changed_ports);
+ dpif->change_error = false;
+ *dpifp = &dpif->dpif;
+
+ return 0;
+
+error_free:
+ free(dpif);
+ close(fd);
+error:
+ return error;
}
static void
return do_ioctl(dpif_, ODP_SET_DROP_FRAGS, &drop_frags_int);
}
-static const char *
-vport_type_to_netdev_type(const struct odp_port *odp_port)
-{
- struct tnl_port_config tnl_config;
-
- switch (odp_port->type) {
- case ODP_VPORT_TYPE_UNSPEC:
- break;
-
- case ODP_VPORT_TYPE_NETDEV:
- return "system";
-
- case ODP_VPORT_TYPE_INTERNAL:
- return "internal";
-
- case ODP_VPORT_TYPE_PATCH:
- return "patch";
-
- case ODP_VPORT_TYPE_GRE:
- memcpy(&tnl_config, odp_port->config, sizeof tnl_config);
- return tnl_config.flags & TNL_F_IPSEC ? "ipsec_gre" : "gre";
-
- case ODP_VPORT_TYPE_CAPWAP:
- return "capwap";
-
- case __ODP_VPORT_TYPE_MAX:
- break;
- }
-
- VLOG_WARN_RL(&error_rl, "dp%d: port `%s' has unsupported type %"PRIu32,
- odp_port->dp_idx, odp_port->devname, odp_port->type);
- return "unknown";
-}
-
-static enum odp_vport_type
-netdev_type_to_vport_type(const char *type)
-{
- return (!strcmp(type, "system") ? ODP_VPORT_TYPE_NETDEV
- : !strcmp(type, "internal") ? ODP_VPORT_TYPE_INTERNAL
- : !strcmp(type, "patch") ? ODP_VPORT_TYPE_PATCH
- : (!strcmp(type, "gre")
- || !strcmp(type, "ipsec_gre")) ? ODP_VPORT_TYPE_GRE
- : !strcmp(type, "capwap") ? ODP_VPORT_TYPE_CAPWAP
- : ODP_VPORT_TYPE_UNSPEC);
-}
-
static int
-dpif_linux_port_add(struct dpif *dpif, struct netdev *netdev,
+dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
uint16_t *port_nop)
{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
const char *name = netdev_get_name(netdev);
const char *type = netdev_get_type(netdev);
- struct odp_port port;
+ struct dpif_linux_vport request, reply;
+ const struct ofpbuf *options;
+ struct ofpbuf *buf;
int error;
- memset(&port, 0, sizeof port);
- strncpy(port.devname, name, sizeof port.devname);
- netdev_vport_get_config(netdev, port.config);
-
- port.type = netdev_type_to_vport_type(type);
- if (port.type == ODP_VPORT_TYPE_UNSPEC) {
+ dpif_linux_vport_init(&request);
+ request.cmd = ODP_VPORT_NEW;
+ request.dp_idx = dpif->minor;
+ request.type = netdev_vport_get_vport_type(netdev);
+ if (request.type == ODP_VPORT_TYPE_UNSPEC) {
VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
"unsupported type `%s'",
- dpif_name(dpif), name, type);
+ dpif_name(dpif_), name, type);
return EINVAL;
}
+ request.name = name;
- error = do_ioctl(dpif, ODP_VPORT_ATTACH, &port);
+ options = netdev_vport_get_options(netdev);
+ if (options && options->size) {
+ request.options = options->data;
+ request.options_len = options->size;
+ }
+
+ error = dpif_linux_vport_transact(&request, &reply, &buf);
if (!error) {
- *port_nop = port.port;
+ *port_nop = reply.port_no;
+ ofpbuf_delete(buf);
}
return error;
}
static int
-dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no_)
+dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no)
{
- int port_no = port_no_; /* Kernel expects an "int". */
- return do_ioctl(dpif_, ODP_VPORT_DETACH, &port_no);
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct dpif_linux_vport vport;
+
+ dpif_linux_vport_init(&vport);
+ vport.cmd = ODP_VPORT_DEL;
+ vport.dp_idx = dpif->minor;
+ vport.port_no = port_no;
+ return dpif_linux_vport_transact(&vport, NULL, NULL);
}
static int
dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
const char *port_name, struct dpif_port *dpif_port)
{
- struct odp_port odp_port;
+ struct dpif_linux_vport request;
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
int error;
- memset(&odp_port, 0, sizeof odp_port);
- odp_port.port = port_no;
- strncpy(odp_port.devname, port_name, sizeof odp_port.devname);
+ dpif_linux_vport_init(&request);
+ request.cmd = ODP_VPORT_GET;
+ request.dp_idx = dpif_linux_cast(dpif)->minor;
+ request.port_no = port_no;
+ request.name = port_name;
- error = do_ioctl(dpif, ODP_VPORT_QUERY, &odp_port);
- if (error) {
- return error;
- } else if (odp_port.dp_idx != dpif_linux_cast(dpif)->minor) {
- /* A vport named 'port_name' exists but in some other datapath. */
- return ENOENT;
- } else {
- dpif_port->name = xstrdup(odp_port.devname);
- dpif_port->type = xstrdup(vport_type_to_netdev_type(&odp_port));
- dpif_port->port_no = odp_port.port;
- return 0;
+ error = dpif_linux_vport_transact(&request, &reply, &buf);
+ if (!error) {
+ dpif_port->name = xstrdup(reply.name);
+ dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
+ dpif_port->port_no = reply.port_no;
+ ofpbuf_delete(buf);
}
+ return error;
}
static int
dpif_linux_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
struct dpif_port *dpif_port)
{
- return dpif_linux_port_query__(dpif, port_no, "", dpif_port);
+ return dpif_linux_port_query__(dpif, port_no, NULL, dpif_port);
}
static int
return do_ioctl(dpif_, ODP_FLOW_FLUSH, NULL);
}
+struct dpif_linux_port_state {
+ struct ofpbuf *buf;
+ uint32_t next;
+};
+
static int
dpif_linux_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
{
- *statep = xzalloc(sizeof(struct odp_port));
+ *statep = xzalloc(sizeof(struct dpif_linux_port_state));
return 0;
}
static int
-dpif_linux_port_dump_next(const struct dpif *dpif, void *state,
+dpif_linux_port_dump_next(const struct dpif *dpif, void *state_,
struct dpif_port *dpif_port)
{
- struct odp_port *odp_port = state;
- struct odp_vport_dump dump;
+ struct dpif_linux_port_state *state = state_;
+ struct dpif_linux_vport request, reply;
+ struct ofpbuf *buf;
int error;
- dump.port = odp_port;
- dump.port_no = odp_port->port;
- error = do_ioctl(dpif, ODP_VPORT_DUMP, &dump);
+ ofpbuf_delete(state->buf);
+ state->buf = NULL;
+
+ dpif_linux_vport_init(&request);
+ request.cmd = ODP_VPORT_DUMP;
+ request.dp_idx = dpif_linux_cast(dpif)->minor;
+ request.port_no = state->next;
+ error = dpif_linux_vport_transact(&request, &reply, &buf);
if (error) {
- return error;
- } else if (odp_port->devname[0] == '\0') {
- return EOF;
+ return error == ENODEV ? EOF : error;
} else {
- dpif_port->name = odp_port->devname;
- dpif_port->type = (char *) vport_type_to_netdev_type(odp_port);
- dpif_port->port_no = odp_port->port;
- odp_port->port++;
+ dpif_port->name = (char *) reply.name;
+ dpif_port->type = (char *) netdev_vport_get_netdev_type(&reply);
+ dpif_port->port_no = reply.port_no;
+ state->buf = buf;
+ state->next = reply.port_no + 1;
return 0;
}
}
static int
-dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state)
+dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
{
+ struct dpif_linux_port_state *state = state_;
+ ofpbuf_delete(state->buf);
free(state);
return 0;
}
return ioctl(dpif->fd, cmd, arg) ? errno : 0;
}
-static int
-lookup_internal_device(const char *name, int *dp_idx, int *port_no)
-{
- struct odp_port odp_port;
- static int dp0_fd = -1;
-
- if (dp0_fd < 0) {
- int error;
- char *fn;
-
- error = make_openvswitch_device(0, &fn);
- if (error) {
- return error;
- }
-
- dp0_fd = open(fn, O_RDONLY | O_NONBLOCK);
- if (dp0_fd < 0) {
- VLOG_WARN_RL(&error_rl, "%s: open failed (%s)",
- fn, strerror(errno));
- free(fn);
- return errno;
- }
- free(fn);
- }
-
- memset(&odp_port, 0, sizeof odp_port);
- strncpy(odp_port.devname, name, sizeof odp_port.devname);
- if (ioctl(dp0_fd, ODP_VPORT_QUERY, &odp_port)) {
- if (errno != ENODEV) {
- VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
- name, strerror(errno));
- }
- return errno;
- } else if (odp_port.type == ODP_VPORT_TYPE_INTERNAL) {
- *dp_idx = odp_port.dp_idx;
- *port_no = odp_port.port;
- return 0;
- } else {
- return EINVAL;
- }
-}
-
-static int
-lookup_minor(const char *name, int *minorp)
+bool
+dpif_linux_is_internal_device(const char *name)
{
- int minor, port_no;
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
int error;
- error = lookup_internal_device(name, &minor, &port_no);
- if (error) {
- return error;
- } else if (port_no != ODPP_LOCAL) {
- /* This is an Open vSwitch device but not the local port. We
- * intentionally support only using the name of the local port as the
- * name of a datapath; otherwise, it would be too difficult to
- * enumerate all the names of a datapath. */
- return EOPNOTSUPP;
- } else {
- *minorp = minor;
- return 0;
+ error = dpif_linux_vport_get(name, &reply, &buf);
+ if (!error) {
+ ofpbuf_delete(buf);
+ } else if (error != ENODEV) {
+ VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
+ name, strerror(error));
}
-}
-bool
-dpif_linux_is_internal_device(const char *name)
-{
- int minor, port_no;
-
- return !lookup_internal_device(name, &minor, &port_no);
+ return reply.type == ODP_VPORT_TYPE_INTERNAL;
}
static int
}
static int
-finish_open(struct dpif *dpif_, const char *local_ifname)
+create_minor(const char *name, int minor)
{
- struct dpif_linux *dpif = dpif_linux_cast(dpif_);
- dpif->local_ifname = xstrdup(local_ifname);
- dpif->local_ifindex = if_nametoindex(local_ifname);
- if (!dpif->local_ifindex) {
- int error = errno;
- dpif_uninit(dpif_, true);
- VLOG_WARN("could not get ifindex of %s device: %s",
- local_ifname, strerror(errno));
+ int error;
+ int fd;
+
+ error = open_minor(minor, &fd);
+ if (error) {
return error;
}
- return 0;
-}
-static int
-create_minor(const char *name, int minor, struct dpif **dpifp)
-{
- int error = open_minor(minor, dpifp);
- if (!error) {
- error = do_ioctl(*dpifp, ODP_DP_CREATE, name);
- if (!error) {
- error = finish_open(*dpifp, name);
- } else {
- dpif_uninit(*dpifp, true);
- }
- }
+ error = ioctl(fd, ODP_DP_CREATE, name) ? errno : 0;
+ close(fd);
return error;
}
static int
-open_minor(int minor, struct dpif **dpifp)
+open_minor(int minor, int *fdp)
{
int error;
char *fn;
- int fd;
error = make_openvswitch_device(minor, &fn);
if (error) {
return error;
}
- fd = open(fn, O_RDONLY | O_NONBLOCK);
- if (fd >= 0) {
- struct dpif_linux *dpif = xmalloc(sizeof *dpif);
- error = rtnetlink_link_notifier_register(&dpif->port_notifier,
- dpif_linux_port_changed,
- dpif);
- if (!error) {
- char *name;
-
- name = xasprintf("dp%d", minor);
- dpif_init(&dpif->dpif, &dpif_linux_class, name, minor, minor);
- free(name);
-
- dpif->fd = fd;
- dpif->local_ifname = NULL;
- dpif->minor = minor;
- dpif->local_ifindex = 0;
- shash_init(&dpif->changed_ports);
- dpif->change_error = false;
- *dpifp = &dpif->dpif;
- } else {
- free(dpif);
- }
- } else {
+ *fdp = open(fn, O_RDONLY | O_NONBLOCK);
+ if (*fdp < 0) {
error = errno;
VLOG_WARN("%s: open failed (%s)", fn, strerror(error));
+ free(fn);
+ return error;
}
free(fn);
-
- return error;
+ return 0;
}
static void
dpif->change_error = true;
}
}
+\f
+/* Parses the contents of 'buf', which contains a "struct odp_vport" followed
+ * by Netlink attributes, into 'vport'. Returns 0 if successful, otherwise a
+ * positive errno value.
+ *
+ * 'vport' will contain pointers into 'buf', so the caller should not free
+ * 'buf' while 'vport' is still in use. */
+static int
+dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
+ const struct ofpbuf *buf)
+{
+ static const struct nl_policy odp_vport_policy[] = {
+ [ODP_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32 },
+ [ODP_VPORT_ATTR_TYPE] = { .type = NL_A_U32 },
+ [ODP_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
+ [ODP_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
+ .min_len = sizeof(struct rtnl_link_stats64),
+ .max_len = sizeof(struct rtnl_link_stats64),
+ .optional = true },
+ [ODP_VPORT_ATTR_ADDRESS] = { .type = NL_A_UNSPEC,
+ .min_len = ETH_ADDR_LEN,
+ .max_len = ETH_ADDR_LEN,
+ .optional = true },
+ [ODP_VPORT_ATTR_MTU] = { .type = NL_A_U32, .optional = true },
+ [ODP_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
+ [ODP_VPORT_ATTR_IFINDEX] = { .type = NL_A_U32, .optional = true },
+ [ODP_VPORT_ATTR_IFLINK] = { .type = NL_A_U32, .optional = true },
+ };
+
+ struct odp_vport *odp_vport;
+ struct nlattr *a[ARRAY_SIZE(odp_vport_policy)];
+
+ dpif_linux_vport_init(vport);
+
+ if (!nl_policy_parse(buf, sizeof *odp_vport, odp_vport_policy,
+ a, ARRAY_SIZE(odp_vport_policy))) {
+ return EINVAL;
+ }
+ odp_vport = buf->data;
+
+ vport->dp_idx = odp_vport->dp_idx;
+ vport->port_no = nl_attr_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+ vport->type = nl_attr_get_u32(a[ODP_VPORT_ATTR_TYPE]);
+ vport->name = nl_attr_get_string(a[ODP_VPORT_ATTR_NAME]);
+ if (a[ODP_VPORT_ATTR_STATS]) {
+ vport->stats = nl_attr_get(a[ODP_VPORT_ATTR_STATS]);
+ }
+ if (a[ODP_VPORT_ATTR_ADDRESS]) {
+ vport->address = nl_attr_get(a[ODP_VPORT_ATTR_ADDRESS]);
+ }
+ if (a[ODP_VPORT_ATTR_MTU]) {
+ vport->mtu = nl_attr_get_u32(a[ODP_VPORT_ATTR_MTU]);
+ }
+ if (a[ODP_VPORT_ATTR_OPTIONS]) {
+ vport->options = nl_attr_get(a[ODP_VPORT_ATTR_OPTIONS]);
+ vport->options_len = nl_attr_get_size(a[ODP_VPORT_ATTR_OPTIONS]);
+ }
+ if (a[ODP_VPORT_ATTR_IFINDEX]) {
+ vport->ifindex = nl_attr_get_u32(a[ODP_VPORT_ATTR_IFINDEX]);
+ }
+ if (a[ODP_VPORT_ATTR_IFLINK]) {
+ vport->iflink = nl_attr_get_u32(a[ODP_VPORT_ATTR_IFLINK]);
+ }
+ return 0;
+}
+
+/* Appends to 'buf' (which must initially be empty) a "struct odp_vport"
+ * followed by Netlink attributes corresponding to 'vport'. */
+static void
+dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *vport,
+ struct ofpbuf *buf)
+{
+ struct odp_vport *odp_vport;
+
+ ofpbuf_reserve(buf, sizeof odp_vport);
+
+ if (vport->port_no != UINT32_MAX) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
+ }
+
+ if (vport->type != ODP_VPORT_TYPE_UNSPEC) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_TYPE, vport->type);
+ }
+
+ if (vport->name) {
+ nl_msg_put_string(buf, ODP_VPORT_ATTR_NAME, vport->name);
+ }
+
+ if (vport->stats) {
+ nl_msg_put_unspec(buf, ODP_VPORT_ATTR_STATS,
+ vport->stats, sizeof *vport->stats);
+ }
+
+ if (vport->address) {
+ nl_msg_put_unspec(buf, ODP_VPORT_ATTR_ADDRESS,
+ vport->address, ETH_ADDR_LEN);
+ }
+
+ if (vport->mtu) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_MTU, vport->mtu);
+ }
+
+ if (vport->options) {
+ nl_msg_put_nested(buf, ODP_VPORT_ATTR_OPTIONS,
+ vport->options, vport->options_len);
+ }
+
+ if (vport->ifindex) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFINDEX, vport->ifindex);
+ }
+
+ if (vport->iflink) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFLINK, vport->iflink);
+ }
+
+ odp_vport = ofpbuf_push_uninit(buf, sizeof *odp_vport);
+ odp_vport->dp_idx = vport->dp_idx;
+ odp_vport->len = buf->size;
+ odp_vport->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
+}
+
+/* Clears 'vport' to "empty" values. */
+void
+dpif_linux_vport_init(struct dpif_linux_vport *vport)
+{
+ memset(vport, 0, sizeof *vport);
+ vport->dp_idx = UINT32_MAX;
+ vport->port_no = UINT32_MAX;
+}
+
+/* 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_vport 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_vport_transact(const struct dpif_linux_vport *request,
+ struct dpif_linux_vport *reply,
+ struct ofpbuf **bufp)
+{
+ static int dp0_fd = -1;
+ struct ofpbuf *buf = NULL;
+ int error;
+
+ assert((reply != NULL) == (bufp != NULL));
+ if (dp0_fd < 0) {
+ int fd;
+
+ error = open_minor(0, &fd);
+ if (error) {
+ goto error;
+ }
+ dp0_fd = fd;
+ }
+
+ buf = ofpbuf_new(1024);
+ dpif_linux_vport_to_ofpbuf(request, buf);
+
+ error = ioctl(dp0_fd, request->cmd, buf->data) ? errno : 0;
+ if (error) {
+ goto error;
+ }
+
+ if (bufp) {
+ buf->size = ((struct odp_vport *) buf->data)->len;
+ error = dpif_linux_vport_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 the kernel vport named 'name' 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_vport_get(const char *name, struct dpif_linux_vport *reply,
+ struct ofpbuf **bufp)
+{
+ struct dpif_linux_vport request;
+
+ dpif_linux_vport_init(&request);
+ request.cmd = ODP_VPORT_GET;
+ request.name = name;
+
+ return dpif_linux_vport_transact(&request, reply, bufp);
+}
+
/*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define DPIF_LINUX_H 1
#include <stdbool.h>
+#include "openvswitch/datapath-protocol.h"
+
+struct ofpbuf;
+
+struct dpif_linux_vport {
+ /* ioctl command argument. */
+ int cmd;
+
+ /* odp_vport header. */
+ uint32_t dp_idx;
+ uint32_t port_no; /* UINT32_MAX if unknown. */
+ enum odp_vport_type type;
+
+ /* Attributes. */
+ const char *name; /* ODP_VPORT_ATTR_NAME. */
+ const struct rtnl_link_stats64 *stats; /* ODP_VPORT_ATTR_STATS. */
+ const uint8_t *address; /* ODP_VPORT_ATTR_ADDRESS. */
+ int mtu; /* ODP_VPORT_ATTR_MTU. */
+ const struct nlattr *options; /* ODP_VPORT_ATTR_OPTIONS. */
+ size_t options_len;
+ int ifindex; /* ODP_VPORT_ATTR_IFINDEX. */
+ int iflink; /* ODP_VPORT_ATTR_IFLINK. */
+};
+
+void dpif_linux_vport_init(struct dpif_linux_vport *);
+
+int dpif_linux_vport_transact(const struct dpif_linux_vport *request,
+ struct dpif_linux_vport *reply,
+ struct ofpbuf **bufp);
+int dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
+ struct ofpbuf **bufp);
bool dpif_linux_is_internal_device(const char *name);
#include <sys/ioctl.h>
#include "byte-order.h"
+#include "dpif-linux.h"
#include "hash.h"
#include "hmap.h"
#include "list.h"
#include "openvswitch/datapath-protocol.h"
#include "openvswitch/tunnel.h"
#include "packets.h"
-#include "rtnetlink.h"
#include "route-table.h"
+#include "rtnetlink.h"
#include "rtnetlink-link.h"
#include "shash.h"
#include "socket-util.h"
struct netdev_dev_vport {
struct netdev_dev netdev_dev;
- uint64_t config[VPORT_CONFIG_SIZE / 8];
+ struct ofpbuf *options;
};
struct netdev_vport {
enum odp_vport_type type;
struct netdev_class netdev_class;
int (*parse_config)(const char *name, const char *type,
- const struct shash *args, void *config);
+ const struct shash *args, struct ofpbuf *options);
int (*unparse_config)(const char *name, const char *type,
- const void *config, struct shash *args);
+ const struct nlattr *options, size_t options_len,
+ struct shash *args);
};
static struct shash netdev_vport_notifiers =
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-static int netdev_vport_do_ioctl(int cmd, void *arg);
static int netdev_vport_create(const struct netdev_class *, const char *,
const struct shash *, struct netdev_dev **);
static void netdev_vport_poll_notify(const struct netdev *);
+static int tnl_port_config_from_nlattr(const struct nlattr *options,
+ size_t options_len,
+ struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1]);
static void netdev_vport_tnl_iface_init(void);
static void netdev_vport_link_change(const struct rtnetlink_link_change *,
return CONTAINER_OF(netdev, struct netdev_vport, netdev);
}
-/* If 'netdev' is a vport netdev, copies its kernel configuration into
- * 'config'. Otherwise leaves 'config' untouched. */
-void
-netdev_vport_get_config(const struct netdev *netdev, void *config)
+/* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink
+ * options to include in ODP_VPORT_ATTR_OPTIONS for configuring that vport.
+ * Otherwise returns NULL. */
+const struct ofpbuf *
+netdev_vport_get_options(const struct netdev *netdev)
+{
+ const struct netdev_dev *dev = netdev_get_dev(netdev);
+
+ return (is_vport_class(netdev_dev_get_class(dev))
+ ? netdev_dev_vport_cast(dev)->options
+ : NULL);
+}
+
+enum odp_vport_type
+netdev_vport_get_vport_type(const struct netdev *netdev)
{
const struct netdev_dev *dev = netdev_get_dev(netdev);
+ const struct netdev_class *class = netdev_dev_get_class(dev);
+
+ return (is_vport_class(class) ? vport_class_cast(class)->type
+ : class == &netdev_internal_class ? ODP_VPORT_TYPE_INTERNAL
+ : class == &netdev_linux_class ? ODP_VPORT_TYPE_NETDEV
+ : ODP_VPORT_TYPE_UNSPEC);
+}
+
+const char *
+netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
+{
+ struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
+
+ switch (vport->type) {
+ case ODP_VPORT_TYPE_UNSPEC:
+ break;
+
+ case ODP_VPORT_TYPE_NETDEV:
+ return "system";
+
+ case ODP_VPORT_TYPE_INTERNAL:
+ return "internal";
- if (is_vport_class(netdev_dev_get_class(dev))) {
- const struct netdev_dev_vport *vport = netdev_dev_vport_cast(dev);
- memcpy(config, vport->config, VPORT_CONFIG_SIZE);
+ case ODP_VPORT_TYPE_PATCH:
+ return "patch";
+
+ case ODP_VPORT_TYPE_GRE:
+ if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
+ a)) {
+ break;
+ }
+ return (nl_attr_get_u32(a[ODP_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
+ ? "ipsec_gre" : "gre");
+
+ case ODP_VPORT_TYPE_CAPWAP:
+ return "capwap";
+
+ case __ODP_VPORT_TYPE_MAX:
+ break;
}
+
+ VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
+ vport->dp_idx, vport->name, (unsigned int) vport->type);
+ return "unknown";
}
static int
struct netdev_dev **netdev_devp)
{
const struct vport_class *vport_class = vport_class_cast(netdev_class);
- uint64_t config[VPORT_CONFIG_SIZE / 8];
+ struct ofpbuf *options = NULL;
struct shash fetched_args;
int error;
- memset(config, 0, sizeof config);
shash_init(&fetched_args);
if (!shash_is_empty(args)) {
/* Parse the provided configuration. */
+ options = ofpbuf_new(64);
error = vport_class->parse_config(name, netdev_class->type,
- args, config);
+ args, options);
} else {
/* Fetch an existing configuration from the kernel.
*
* This case could be ambiguous with initializing a new vport with an
* empty configuration, but none of the existing vport classes accept
* an empty configuration. */
- struct odp_port odp_port;
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
- memset(&odp_port, 0, sizeof odp_port);
- strncpy(odp_port.devname, name, sizeof odp_port.devname);
- error = netdev_vport_do_ioctl(ODP_VPORT_QUERY, &odp_port);
+ error = dpif_linux_vport_get(name, &reply, &buf);
if (!error) {
/* XXX verify correct type */
- memcpy(config, odp_port.config, sizeof config);
error = vport_class->unparse_config(name, netdev_class->type,
- odp_port.config,
+ reply.options,
+ reply.options_len,
&fetched_args);
if (error) {
VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)",
name, strerror(error));
+ } else {
+ options = ofpbuf_clone_data(reply.options, reply.options_len);
}
+ ofpbuf_delete(buf);
} else {
VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
name, strerror(error));
netdev_dev_init(&dev->netdev_dev, name,
shash_is_empty(&fetched_args) ? args : &fetched_args,
netdev_class);
- memcpy(dev->config, config, sizeof dev->config);
+ dev->options = options;
*netdev_devp = &dev->netdev_dev;
+ } else {
+ ofpbuf_delete(options);
}
shash_destroy(&fetched_args);
const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
const struct vport_class *vport_class = vport_class_cast(netdev_class);
struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
- struct odp_port port;
+ const char *name = netdev_dev_get_name(dev_);
+ struct ofpbuf *options;
int error;
- memset(&port, 0, sizeof port);
- strncpy(port.devname, netdev_dev_get_name(dev_), sizeof port.devname);
- port.type = vport_class->type;
- error = vport_class->parse_config(netdev_dev_get_name(dev_),
- netdev_dev_get_type(dev_),
- args, port.config);
- if (!error && memcmp(port.config, dev->config, sizeof dev->config)) {
- error = netdev_vport_do_ioctl(ODP_VPORT_MOD, &port);
+ options = ofpbuf_new(64);
+ error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
+ args, options);
+ if (!error
+ && (options->size != dev->options->size
+ || memcmp(options->data, dev->options->data, options->size))) {
+ struct dpif_linux_vport vport;
+
+ dpif_linux_vport_init(&vport);
+ vport.cmd = ODP_VPORT_SET;
+ vport.name = name;
+ vport.options = options->data;
+ vport.options_len = options->size;
+ error = dpif_linux_vport_transact(&vport, NULL, NULL);
if (!error || error == ENODEV) {
/* Either reconfiguration succeeded or this vport is not installed
* in the kernel (e.g. it hasn't been added to a dpif yet with
* dpif_port_add()). */
- memcpy(dev->config, port.config, sizeof dev->config);
+ ofpbuf_delete(dev->options);
+ dev->options = options;
+ options = NULL;
+ error = 0;
}
}
+ ofpbuf_delete(options);
+
return error;
}
netdev_vport_set_etheraddr(struct netdev *netdev,
const uint8_t mac[ETH_ADDR_LEN])
{
- struct odp_vport_ether vport_ether;
- int err;
-
- ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev),
- sizeof vport_ether.devname);
+ struct dpif_linux_vport vport;
+ int error;
- memcpy(vport_ether.ether_addr, mac, ETH_ADDR_LEN);
+ dpif_linux_vport_init(&vport);
+ vport.cmd = ODP_VPORT_SET;
+ vport.name = netdev_get_name(netdev);
+ vport.address = mac;
- err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_SET, &vport_ether);
- if (err) {
- return err;
+ error = dpif_linux_vport_transact(&vport, NULL, NULL);
+ if (!error) {
+ netdev_vport_poll_notify(netdev);
}
-
- netdev_vport_poll_notify(netdev);
- return 0;
+ return error;
}
static int
netdev_vport_get_etheraddr(const struct netdev *netdev,
uint8_t mac[ETH_ADDR_LEN])
{
- struct odp_vport_ether vport_ether;
- int err;
-
- ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev),
- sizeof vport_ether.devname);
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
+ int error;
- err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_GET, &vport_ether);
- if (err) {
- return err;
+ error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+ if (!error) {
+ if (reply.address) {
+ memcpy(mac, reply.address, ETH_ADDR_LEN);
+ } else {
+ error = EOPNOTSUPP;
+ }
+ ofpbuf_delete(buf);
}
-
- memcpy(mac, vport_ether.ether_addr, ETH_ADDR_LEN);
- return 0;
+ return error;
}
static int
netdev_vport_get_mtu(const struct netdev *netdev, int *mtup)
{
- struct odp_vport_mtu vport_mtu;
- int err;
-
- ovs_strlcpy(vport_mtu.devname, netdev_get_name(netdev),
- sizeof vport_mtu.devname);
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
+ int error;
- err = netdev_vport_do_ioctl(ODP_VPORT_MTU_GET, &vport_mtu);
- if (err) {
- return err;
+ error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+ if (!error) {
+ *mtup = reply.mtu;
+ ofpbuf_delete(buf);
}
-
- *mtup = vport_mtu.mtu;
- return 0;
+ return error;
}
int
netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
{
- const char *name = netdev_get_name(netdev);
- struct odp_vport_stats_req ovsr;
- int err;
+ struct dpif_linux_vport reply;
+ struct ofpbuf *buf;
+ int error;
+
+ error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+ if (error) {
+ return error;
+ } else if (!reply.stats) {
+ ofpbuf_delete(buf);
+ return EOPNOTSUPP;
+ }
- ovs_strlcpy(ovsr.devname, name, sizeof ovsr.devname);
- err = netdev_vport_do_ioctl(ODP_VPORT_STATS_GET, &ovsr);
- if (err) {
- return err;
- }
-
- stats->rx_packets = ovsr.stats.rx_packets;
- stats->tx_packets = ovsr.stats.tx_packets;
- stats->rx_bytes = ovsr.stats.rx_bytes;
- stats->tx_bytes = ovsr.stats.tx_bytes;
- stats->rx_errors = ovsr.stats.rx_errors;
- stats->tx_errors = ovsr.stats.tx_errors;
- stats->rx_dropped = ovsr.stats.rx_dropped;
- stats->tx_dropped = ovsr.stats.tx_dropped;
- stats->multicast = ovsr.stats.multicast;
- stats->collisions = ovsr.stats.collisions;
- stats->rx_length_errors = ovsr.stats.rx_length_errors;
- stats->rx_over_errors = ovsr.stats.rx_over_errors;
- stats->rx_crc_errors = ovsr.stats.rx_crc_errors;
- stats->rx_frame_errors = ovsr.stats.rx_frame_errors;
- stats->rx_fifo_errors = ovsr.stats.rx_fifo_errors;
- stats->rx_missed_errors = ovsr.stats.rx_missed_errors;
- stats->tx_aborted_errors = ovsr.stats.tx_aborted_errors;
- stats->tx_carrier_errors = ovsr.stats.tx_carrier_errors;
- stats->tx_fifo_errors = ovsr.stats.tx_fifo_errors;
- stats->tx_heartbeat_errors = ovsr.stats.tx_heartbeat_errors;
- stats->tx_window_errors = ovsr.stats.tx_window_errors;
+ stats->rx_packets = reply.stats->rx_packets;
+ stats->tx_packets = reply.stats->tx_packets;
+ stats->rx_bytes = reply.stats->rx_bytes;
+ stats->tx_bytes = reply.stats->tx_bytes;
+ stats->rx_errors = reply.stats->rx_errors;
+ stats->tx_errors = reply.stats->tx_errors;
+ stats->rx_dropped = reply.stats->rx_dropped;
+ stats->tx_dropped = reply.stats->tx_dropped;
+ stats->multicast = reply.stats->multicast;
+ stats->collisions = reply.stats->collisions;
+ stats->rx_length_errors = reply.stats->rx_length_errors;
+ stats->rx_over_errors = reply.stats->rx_over_errors;
+ stats->rx_crc_errors = reply.stats->rx_crc_errors;
+ stats->rx_frame_errors = reply.stats->rx_frame_errors;
+ stats->rx_fifo_errors = reply.stats->rx_fifo_errors;
+ stats->rx_missed_errors = reply.stats->rx_missed_errors;
+ stats->tx_aborted_errors = reply.stats->tx_aborted_errors;
+ stats->tx_carrier_errors = reply.stats->tx_carrier_errors;
+ stats->tx_fifo_errors = reply.stats->tx_fifo_errors;
+ stats->tx_heartbeat_errors = reply.stats->tx_heartbeat_errors;
+ stats->tx_window_errors = reply.stats->tx_window_errors;
+
+ ofpbuf_delete(buf);
return 0;
}
int
netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
{
- struct odp_vport_stats_req ovsr;
+ struct rtnl_link_stats64 rtnl_stats;
+ struct dpif_linux_vport vport;
int err;
- ovs_strlcpy(ovsr.devname, netdev_get_name(netdev), sizeof ovsr.devname);
-
- ovsr.stats.rx_packets = stats->rx_packets;
- ovsr.stats.tx_packets = stats->tx_packets;
- ovsr.stats.rx_bytes = stats->rx_bytes;
- ovsr.stats.tx_bytes = stats->tx_bytes;
- ovsr.stats.rx_errors = stats->rx_errors;
- ovsr.stats.tx_errors = stats->tx_errors;
- ovsr.stats.rx_dropped = stats->rx_dropped;
- ovsr.stats.tx_dropped = stats->tx_dropped;
- ovsr.stats.multicast = stats->multicast;
- ovsr.stats.collisions = stats->collisions;
- ovsr.stats.rx_length_errors = stats->rx_length_errors;
- ovsr.stats.rx_over_errors = stats->rx_over_errors;
- ovsr.stats.rx_crc_errors = stats->rx_crc_errors;
- ovsr.stats.rx_frame_errors = stats->rx_frame_errors;
- ovsr.stats.rx_fifo_errors = stats->rx_fifo_errors;
- ovsr.stats.rx_missed_errors = stats->rx_missed_errors;
- ovsr.stats.tx_aborted_errors = stats->tx_aborted_errors;
- ovsr.stats.tx_carrier_errors = stats->tx_carrier_errors;
- ovsr.stats.tx_fifo_errors = stats->tx_fifo_errors;
- ovsr.stats.tx_heartbeat_errors = stats->tx_heartbeat_errors;
- ovsr.stats.tx_window_errors = stats->tx_window_errors;
-
- err = netdev_vport_do_ioctl(ODP_VPORT_STATS_SET, &ovsr);
+ rtnl_stats.rx_packets = stats->rx_packets;
+ rtnl_stats.tx_packets = stats->tx_packets;
+ rtnl_stats.rx_bytes = stats->rx_bytes;
+ rtnl_stats.tx_bytes = stats->tx_bytes;
+ rtnl_stats.rx_errors = stats->rx_errors;
+ rtnl_stats.tx_errors = stats->tx_errors;
+ rtnl_stats.rx_dropped = stats->rx_dropped;
+ rtnl_stats.tx_dropped = stats->tx_dropped;
+ rtnl_stats.multicast = stats->multicast;
+ rtnl_stats.collisions = stats->collisions;
+ rtnl_stats.rx_length_errors = stats->rx_length_errors;
+ rtnl_stats.rx_over_errors = stats->rx_over_errors;
+ rtnl_stats.rx_crc_errors = stats->rx_crc_errors;
+ rtnl_stats.rx_frame_errors = stats->rx_frame_errors;
+ rtnl_stats.rx_fifo_errors = stats->rx_fifo_errors;
+ rtnl_stats.rx_missed_errors = stats->rx_missed_errors;
+ rtnl_stats.tx_aborted_errors = stats->tx_aborted_errors;
+ rtnl_stats.tx_carrier_errors = stats->tx_carrier_errors;
+ rtnl_stats.tx_fifo_errors = stats->tx_fifo_errors;
+ rtnl_stats.tx_heartbeat_errors = stats->tx_heartbeat_errors;
+ rtnl_stats.tx_window_errors = stats->tx_window_errors;
+
+ dpif_linux_vport_init(&vport);
+ vport.cmd = ODP_VPORT_SET;
+ vport.name = netdev_get_name(netdev);
+ vport.stats = &rtnl_stats;
+
+ err = dpif_linux_vport_transact(&vport, NULL, NULL);
/* If the vport layer doesn't know about the device, that doesn't mean it
* doesn't exist (after all were able to open it when netdev_open() was
static const char *
netdev_vport_get_tnl_iface(const struct netdev *netdev)
{
+ struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
int ifindex;
uint32_t route;
struct netdev_dev_vport *ndv;
- struct tnl_port_config *config;
ndv = netdev_dev_vport_cast(netdev_get_dev(netdev));
- config = (struct tnl_port_config *) ndv->config;
- route = config->daddr;
+ if (tnl_port_config_from_nlattr(ndv->options->data, ndv->options->size,
+ a)) {
+ return NULL;
+ }
+ route = nl_attr_get_be32(a[ODP_TUNNEL_ATTR_DST_IPV4]);
if (route_table_get_ifindex(route, &ifindex)) {
struct name_node *nn;
\f
/* Helper functions. */
-static int
-netdev_vport_do_ioctl(int cmd, void *arg)
-{
- static int ioctl_fd = -1;
-
- if (ioctl_fd < 0) {
- ioctl_fd = open("/dev/net/dp0", O_RDONLY | O_NONBLOCK);
- if (ioctl_fd < 0) {
- VLOG_ERR_RL(&rl, "failed to open ioctl fd: %s", strerror(errno));
- return errno;
- }
- }
-
- return ioctl(ioctl_fd, cmd, arg) ? errno : 0;
-}
-
static void
netdev_vport_poll_notify(const struct netdev *netdev)
{
\f
/* Code specific to individual vport types. */
+static void
+set_key(const struct shash *args, const char *name, uint16_t type,
+ struct ofpbuf *options)
+{
+ const char *s;
+
+ s = shash_find_data(args, name);
+ if (!s) {
+ s = shash_find_data(args, "key");
+ if (!s) {
+ s = "0";
+ }
+ }
+
+ if (!strcmp(s, "flow")) {
+ /* This is the default if no attribute is present. */
+ } else {
+ nl_msg_put_be64(options, type, htonll(strtoull(s, NULL, 0)));
+ }
+}
+
static int
parse_tunnel_config(const char *name, const char *type,
- const struct shash *args, void *configp)
+ const struct shash *args, struct ofpbuf *options)
{
bool is_gre = false;
bool is_ipsec = false;
- struct tnl_port_config config;
struct shash_node *node;
bool ipsec_mech_set = false;
+ ovs_be32 daddr = htonl(0);
+ uint32_t flags;
- memset(&config, 0, sizeof config);
- config.flags |= TNL_F_PMTUD;
- config.flags |= TNL_F_HDR_CACHE;
-
+ flags = TNL_F_PMTUD | TNL_F_HDR_CACHE;
if (!strcmp(type, "gre")) {
is_gre = true;
} else if (!strcmp(type, "ipsec_gre")) {
is_gre = true;
is_ipsec = true;
-
- config.flags |= TNL_F_IPSEC;
-
- /* IPsec doesn't work when header caching is enabled. */
- config.flags &= ~TNL_F_HDR_CACHE;
+ flags |= TNL_F_IPSEC;
+ flags &= ~TNL_F_HDR_CACHE;
}
SHASH_FOR_EACH (node, args) {
if (lookup_ip(node->data, &in_addr)) {
VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
} else {
- config.daddr = in_addr.s_addr;
+ daddr = in_addr.s_addr;
}
} else if (!strcmp(node->name, "local_ip")) {
struct in_addr in_addr;
if (lookup_ip(node->data, &in_addr)) {
VLOG_WARN("%s: bad %s 'local_ip'", name, type);
} else {
- config.saddr = in_addr.s_addr;
- }
- } else if (!strcmp(node->name, "key") && is_gre) {
- if (!strcmp(node->data, "flow")) {
- config.flags |= TNL_F_IN_KEY_MATCH;
- config.flags |= TNL_F_OUT_KEY_ACTION;
- } else {
- uint64_t key = strtoull(node->data, NULL, 0);
- config.out_key = config.in_key = htonll(key);
- }
- } else if (!strcmp(node->name, "in_key") && is_gre) {
- if (!strcmp(node->data, "flow")) {
- config.flags |= TNL_F_IN_KEY_MATCH;
- } else {
- config.in_key = htonll(strtoull(node->data, NULL, 0));
- }
- } else if (!strcmp(node->name, "out_key") && is_gre) {
- if (!strcmp(node->data, "flow")) {
- config.flags |= TNL_F_OUT_KEY_ACTION;
- } else {
- config.out_key = htonll(strtoull(node->data, NULL, 0));
+ nl_msg_put_be32(options, ODP_TUNNEL_ATTR_SRC_IPV4,
+ in_addr.s_addr);
}
} else if (!strcmp(node->name, "tos")) {
if (!strcmp(node->data, "inherit")) {
- config.flags |= TNL_F_TOS_INHERIT;
+ flags |= TNL_F_TOS_INHERIT;
} else {
- config.tos = atoi(node->data);
+ nl_msg_put_u8(options, ODP_TUNNEL_ATTR_TOS, atoi(node->data));
}
} else if (!strcmp(node->name, "ttl")) {
if (!strcmp(node->data, "inherit")) {
- config.flags |= TNL_F_TTL_INHERIT;
+ flags |= TNL_F_TTL_INHERIT;
} else {
- config.ttl = atoi(node->data);
+ nl_msg_put_u8(options, ODP_TUNNEL_ATTR_TTL, atoi(node->data));
}
} else if (!strcmp(node->name, "csum") && is_gre) {
if (!strcmp(node->data, "true")) {
- config.flags |= TNL_F_CSUM;
+ flags |= TNL_F_CSUM;
}
} else if (!strcmp(node->name, "pmtud")) {
if (!strcmp(node->data, "false")) {
- config.flags &= ~TNL_F_PMTUD;
+ flags &= ~TNL_F_PMTUD;
}
} else if (!strcmp(node->name, "header_cache")) {
if (!strcmp(node->data, "false")) {
- config.flags &= ~TNL_F_HDR_CACHE;
+ flags &= ~TNL_F_HDR_CACHE;
}
} else if (!strcmp(node->name, "peer_cert") && is_ipsec) {
if (shash_find(args, "certificate")) {
|| !strcmp(node->name, "private_key")
|| !strcmp(node->name, "use_ssl_cert"))) {
/* Ignore options not used by the netdev. */
+ } else if (is_gre && (!strcmp(node->name, "key") &&
+ !strcmp(node->name, "in_key") &&
+ !strcmp(node->name, "out_key"))) {
+ /* Handled separately below. */
} else {
- VLOG_WARN("%s: unknown %s argument '%s'",
- name, type, node->name);
+ VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->name);
}
}
}
}
- if (!config.daddr) {
+ if (is_gre) {
+ set_key(args, "in_key", ODP_TUNNEL_ATTR_IN_KEY, options);
+ set_key(args, "out_key", ODP_TUNNEL_ATTR_OUT_KEY, options);
+ }
+
+ if (!daddr) {
VLOG_WARN("%s: %s type requires valid 'remote_ip' argument",
name, type);
return EINVAL;
}
+ nl_msg_put_be32(options, ODP_TUNNEL_ATTR_DST_IPV4, daddr);
+
+ nl_msg_put_u32(options, ODP_TUNNEL_ATTR_FLAGS, flags);
- BUILD_ASSERT(sizeof config <= VPORT_CONFIG_SIZE);
- memcpy(configp, &config, sizeof config);
return 0;
}
+static int
+tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
+ struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1])
+{
+ static const struct nl_policy odp_tunnel_policy[] = {
+ [ODP_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32 },
+ [ODP_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32 },
+ [ODP_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
+ [ODP_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
+ [ODP_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
+ [ODP_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
+ [ODP_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
+ };
+ struct ofpbuf buf;
+
+ ofpbuf_use_const(&buf, options, options_len);
+ if (!nl_policy_parse(&buf, 0, odp_tunnel_policy,
+ a, ARRAY_SIZE(odp_tunnel_policy))) {
+ return EINVAL;
+ }
+ return 0;
+}
+
+static uint64_t
+get_be64_or_zero(const struct nlattr *a)
+{
+ return a ? ntohll(nl_attr_get_be64(a)) : 0;
+}
+
static int
unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
- const void *config_, struct shash *args)
+ const struct nlattr *options, size_t options_len,
+ struct shash *args)
{
- const struct tnl_port_config *config = config_;
+ struct nlattr *a[ODP_TUNNEL_ATTR_MAX + 1];
+ ovs_be32 daddr;
+ uint32_t flags;
+ int error;
+
+ error = tnl_port_config_from_nlattr(options, options_len, a);
+ if (error) {
+ return error;
+ }
- if (!(config->flags & TNL_F_HDR_CACHE) == !(config->flags & TNL_F_IPSEC)) {
+ flags = nl_attr_get_u32(a[ODP_TUNNEL_ATTR_FLAGS]);
+ if (!(flags & TNL_F_HDR_CACHE) == !(flags & TNL_F_IPSEC)) {
smap_add(args, "header_cache",
- config->flags & TNL_F_HDR_CACHE ? "true" : "false");
+ flags & TNL_F_HDR_CACHE ? "true" : "false");
}
- shash_add(args, "remote_ip", xasprintf(IP_FMT, IP_ARGS(&config->daddr)));
- if (config->saddr) {
- shash_add(args, "local_ip",
- xasprintf(IP_FMT, IP_ARGS(&config->saddr)));
+
+ daddr = nl_attr_get_be32(a[ODP_TUNNEL_ATTR_DST_IPV4]);
+ shash_add(args, "remote_ip", xasprintf(IP_FMT, IP_ARGS(&daddr)));
+
+ if (a[ODP_TUNNEL_ATTR_SRC_IPV4]) {
+ ovs_be32 saddr = nl_attr_get_be32(a[ODP_TUNNEL_ATTR_SRC_IPV4]);
+ shash_add(args, "local_ip", xasprintf(IP_FMT, IP_ARGS(&saddr)));
}
- if ((config->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
- == (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) {
+ if (!a[ODP_TUNNEL_ATTR_IN_KEY] && !a[ODP_TUNNEL_ATTR_OUT_KEY]) {
smap_add(args, "key", "flow");
- } else if (config->in_key && config->in_key == config->out_key) {
- shash_add(args, "key",
- xasprintf("%"PRIu64, ntohll(config->in_key)));
} else {
- if (config->flags & TNL_F_IN_KEY_MATCH) {
- smap_add(args, "in_key", "flow");
- } else if (config->in_key) {
- shash_add(args, "in_key",
- xasprintf("%"PRIu64, ntohll(config->in_key)));
- }
+ uint64_t in_key = get_be64_or_zero(a[ODP_TUNNEL_ATTR_IN_KEY]);
+ uint64_t out_key = get_be64_or_zero(a[ODP_TUNNEL_ATTR_OUT_KEY]);
+
+ if (in_key && in_key == out_key) {
+ shash_add(args, "key", xasprintf("%"PRIu64, in_key));
+ } else {
+ if (!a[ODP_TUNNEL_ATTR_IN_KEY]) {
+ smap_add(args, "in_key", "flow");
+ } else if (in_key) {
+ shash_add(args, "in_key", xasprintf("%"PRIu64, in_key));
+ }
- if (config->flags & TNL_F_OUT_KEY_ACTION) {
- smap_add(args, "out_key", "flow");
- } else if (config->out_key) {
- shash_add(args, "out_key",
- xasprintf("%"PRIu64, ntohll(config->out_key)));
+ if (!a[ODP_TUNNEL_ATTR_OUT_KEY]) {
+ smap_add(args, "out_key", "flow");
+ } else if (out_key) {
+ shash_add(args, "out_key", xasprintf("%"PRIu64, out_key));
+ }
}
}
- if (config->flags & TNL_F_TTL_INHERIT) {
+ if (flags & TNL_F_TTL_INHERIT) {
+ smap_add(args, "tos", "inherit");
+ } else if (a[ODP_TUNNEL_ATTR_TTL]) {
+ int ttl = nl_attr_get_u8(a[ODP_TUNNEL_ATTR_TTL]);
+ shash_add(args, "tos", xasprintf("%d", ttl));
+ }
+
+ if (flags & TNL_F_TOS_INHERIT) {
smap_add(args, "tos", "inherit");
- } else if (config->ttl) {
- shash_add(args, "tos", xasprintf("%"PRIu8, config->ttl));
+ } else if (a[ODP_TUNNEL_ATTR_TOS]) {
+ int tos = nl_attr_get_u8(a[ODP_TUNNEL_ATTR_TOS]);
+ shash_add(args, "tos", xasprintf("%d", tos));
}
- if (config->flags & TNL_F_CSUM) {
+ if (flags & TNL_F_CSUM) {
smap_add(args, "csum", "true");
}
- if (!(config->flags & TNL_F_PMTUD)) {
+ if (!(flags & TNL_F_PMTUD)) {
smap_add(args, "pmtud", "false");
}
static int
parse_patch_config(const char *name, const char *type OVS_UNUSED,
- const struct shash *args, void *configp)
+ const struct shash *args, struct ofpbuf *options)
{
const char *peer;
return EINVAL;
}
- if (strlen(peer) >= MIN(IFNAMSIZ, VPORT_CONFIG_SIZE)) {
+ if (strlen(peer) >= IFNAMSIZ) {
VLOG_WARN("%s: patch 'peer' arg too long", name);
return EINVAL;
}
return EINVAL;
}
- strncpy(configp, peer, VPORT_CONFIG_SIZE);
+ nl_msg_put_string(options, ODP_PATCH_ATTR_PEER, peer);
return 0;
}
static int
unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
- const void *config_, struct shash *args)
+ const struct nlattr *options, size_t options_len,
+ struct shash *args)
{
- char peer[IFNAMSIZ];
+ static const struct nl_policy odp_patch_policy[] = {
+ [ODP_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
+ .max_len = IFNAMSIZ,
+ .optional = false }
+ };
+
+ struct nlattr *a[ARRAY_SIZE(odp_patch_policy)];
+ struct ofpbuf buf;
- ovs_strlcpy(peer, config_, MIN(sizeof peer, VPORT_CONFIG_SIZE));
- if (peer[0]) {
- smap_add(args, "peer", peer);
+ ofpbuf_use_const(&buf, options, options_len);
+ if (!nl_policy_parse(&buf, 0, odp_patch_policy,
+ a, ARRAY_SIZE(odp_patch_policy))) {
+ return EINVAL;
}
+ smap_add(args, "peer", nl_attr_get_string(a[ODP_PATCH_ATTR_PEER]));
return 0;
}
\f
/*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#ifndef NETDEV_VPORT_H
#define NETDEV_VPORT_H 1
+struct dpif_linux_vport;
struct netdev;
struct netdev_stats;
-struct odp_port;
-struct shash;
void netdev_vport_register(void);
-void netdev_vport_get_config(const struct netdev *, void *config);
+const struct ofpbuf *netdev_vport_get_options(const struct netdev *);
+
+enum odp_vport_type netdev_vport_get_vport_type(const struct netdev *);
+const char *netdev_vport_get_netdev_type(const struct dpif_linux_vport *);
int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
int netdev_vport_set_stats(struct netdev *, const struct netdev_stats *);