2010-05-17 15:04:10 -07:00
|
|
|
|
/*
|
2011-01-12 14:55:18 -08:00
|
|
|
|
* Copyright (c) 2010, 2011 Nicira Networks.
|
2010-05-17 15:04:10 -07:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
2010-09-24 10:55:57 -07:00
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
2010-05-17 15:04:10 -07:00
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
|
|
|
|
#include "netdev-vport.h"
|
|
|
|
|
|
2010-05-17 15:04:10 -07:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
2010-12-21 16:26:21 -08:00
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <linux/rtnetlink.h>
|
2010-10-06 15:35:53 -07:00
|
|
|
|
#include <net/if.h>
|
2010-05-17 15:04:10 -07:00
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
2010-12-10 10:42:42 -08:00
|
|
|
|
#include "byte-order.h"
|
2010-12-21 16:26:21 -08:00
|
|
|
|
#include "hash.h"
|
|
|
|
|
#include "hmap.h"
|
2010-05-17 15:04:10 -07:00
|
|
|
|
#include "list.h"
|
2010-10-06 15:35:53 -07:00
|
|
|
|
#include "netdev-provider.h"
|
2010-12-21 16:26:21 -08:00
|
|
|
|
#include "netlink.h"
|
|
|
|
|
#include "netlink-socket.h"
|
|
|
|
|
#include "ofpbuf.h"
|
2010-05-17 15:04:10 -07:00
|
|
|
|
#include "openvswitch/datapath-protocol.h"
|
2010-10-06 15:35:53 -07:00
|
|
|
|
#include "openvswitch/tunnel.h"
|
|
|
|
|
#include "packets.h"
|
2010-12-21 16:26:21 -08:00
|
|
|
|
#include "rtnetlink.h"
|
2011-01-12 14:55:18 -08:00
|
|
|
|
#include "route-table.h"
|
2010-12-21 16:26:21 -08:00
|
|
|
|
#include "rtnetlink-link.h"
|
2010-05-17 15:04:10 -07:00
|
|
|
|
#include "shash.h"
|
|
|
|
|
#include "socket-util.h"
|
|
|
|
|
#include "vlog.h"
|
|
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(netdev_vport);
|
2010-07-16 11:02:49 -07:00
|
|
|
|
|
2010-12-21 16:26:21 -08:00
|
|
|
|
static struct hmap name_map;
|
|
|
|
|
static struct rtnetlink_notifier netdev_vport_link_notifier;
|
|
|
|
|
|
|
|
|
|
struct name_node {
|
|
|
|
|
struct hmap_node node; /* Node in name_map. */
|
|
|
|
|
uint32_t ifi_index; /* Kernel interface index. */
|
|
|
|
|
|
|
|
|
|
char ifname[IFNAMSIZ]; /* Interface name. */
|
|
|
|
|
};
|
|
|
|
|
|
2010-05-17 15:04:10 -07:00
|
|
|
|
struct netdev_vport_notifier {
|
|
|
|
|
struct netdev_notifier notifier;
|
|
|
|
|
struct list list_node;
|
2010-08-30 00:24:53 -07:00
|
|
|
|
struct shash_node *shash_node;
|
2010-05-17 15:04:10 -07:00
|
|
|
|
};
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
struct netdev_dev_vport {
|
|
|
|
|
struct netdev_dev netdev_dev;
|
2010-12-03 14:41:38 -08:00
|
|
|
|
uint64_t config[VPORT_CONFIG_SIZE / 8];
|
2010-10-06 15:35:53 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct netdev_vport {
|
|
|
|
|
struct netdev netdev;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct vport_class {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
struct netdev_class netdev_class;
|
|
|
|
|
int (*parse_config)(const struct netdev_dev *, const struct shash *args,
|
|
|
|
|
void *config);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
};
|
|
|
|
|
|
2010-05-17 15:04:10 -07:00
|
|
|
|
static struct shash netdev_vport_notifiers =
|
|
|
|
|
SHASH_INITIALIZER(&netdev_vport_notifiers);
|
|
|
|
|
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
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 *);
|
|
|
|
|
|
2010-12-21 16:26:21 -08:00
|
|
|
|
static void netdev_vport_tnl_iface_init(void);
|
|
|
|
|
static void netdev_vport_link_change(const struct rtnetlink_link_change *,
|
|
|
|
|
void *);
|
2011-01-05 11:51:15 -08:00
|
|
|
|
static const char *netdev_vport_get_tnl_iface(const struct netdev *netdev);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static bool
|
|
|
|
|
is_vport_class(const struct netdev_class *class)
|
2010-05-17 15:04:10 -07:00
|
|
|
|
{
|
2010-10-06 15:35:53 -07:00
|
|
|
|
return class->create == netdev_vport_create;
|
|
|
|
|
}
|
2010-05-17 15:04:10 -07:00
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static const struct vport_class *
|
|
|
|
|
vport_class_cast(const struct netdev_class *class)
|
|
|
|
|
{
|
|
|
|
|
assert(is_vport_class(class));
|
|
|
|
|
return CONTAINER_OF(class, struct vport_class, netdev_class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct netdev_dev_vport *
|
|
|
|
|
netdev_dev_vport_cast(const struct netdev_dev *netdev_dev)
|
|
|
|
|
{
|
|
|
|
|
assert(is_vport_class(netdev_dev_get_class(netdev_dev)));
|
|
|
|
|
return CONTAINER_OF(netdev_dev, struct netdev_dev_vport, netdev_dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct netdev_vport *
|
|
|
|
|
netdev_vport_cast(const struct netdev *netdev)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
|
|
|
|
|
assert(is_vport_class(netdev_dev_get_class(netdev_dev)));
|
|
|
|
|
return CONTAINER_OF(netdev, struct netdev_vport, netdev);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
/* 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)
|
2010-10-06 15:35:53 -07:00
|
|
|
|
{
|
2010-12-03 14:41:38 -08:00
|
|
|
|
const struct netdev_dev *dev = netdev_get_dev(netdev);
|
|
|
|
|
|
|
|
|
|
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);
|
2010-05-17 15:04:10 -07:00
|
|
|
|
}
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
2010-05-17 15:04:10 -07:00
|
|
|
|
|
2010-12-21 16:26:21 -08:00
|
|
|
|
static int
|
|
|
|
|
netdev_vport_init(void)
|
|
|
|
|
{
|
|
|
|
|
netdev_vport_tnl_iface_init();
|
2011-01-12 14:55:18 -08:00
|
|
|
|
route_table_register();
|
2010-12-21 16:26:21 -08:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static int
|
2010-12-03 14:41:38 -08:00
|
|
|
|
netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
|
|
|
|
|
const struct shash *args,
|
|
|
|
|
struct netdev_dev **netdev_devp)
|
2010-10-06 15:35:53 -07:00
|
|
|
|
{
|
2010-12-03 14:41:38 -08:00
|
|
|
|
const struct vport_class *vport_class = vport_class_cast(netdev_class);
|
|
|
|
|
struct netdev_dev_vport *dev;
|
|
|
|
|
int error;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
dev = xmalloc(sizeof *dev);
|
|
|
|
|
*netdev_devp = &dev->netdev_dev;
|
|
|
|
|
netdev_dev_init(&dev->netdev_dev, name, netdev_class);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
memset(dev->config, 0, sizeof dev->config);
|
|
|
|
|
error = vport_class->parse_config(&dev->netdev_dev, args, dev->config);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
netdev_dev_uninit(&dev->netdev_dev, true);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
2010-12-03 14:41:38 -08:00
|
|
|
|
return error;
|
2010-05-17 15:04:10 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static void
|
|
|
|
|
netdev_vport_destroy(struct netdev_dev *netdev_dev_)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_);
|
|
|
|
|
|
2011-01-12 14:55:18 -08:00
|
|
|
|
route_table_unregister();
|
2010-10-06 15:35:53 -07:00
|
|
|
|
free(netdev_dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
netdev_vport_open(struct netdev_dev *netdev_dev_, int ethertype OVS_UNUSED,
|
|
|
|
|
struct netdev **netdevp)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_vport *netdev;
|
|
|
|
|
|
|
|
|
|
netdev = xmalloc(sizeof *netdev);
|
|
|
|
|
netdev_init(&netdev->netdev, netdev_dev_);
|
|
|
|
|
|
|
|
|
|
*netdevp = &netdev->netdev;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
netdev_vport_close(struct netdev *netdev_)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_vport *netdev = netdev_vport_cast(netdev_);
|
|
|
|
|
free(netdev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2010-12-03 14:41:38 -08:00
|
|
|
|
netdev_vport_reconfigure(struct netdev_dev *dev_,
|
2010-10-06 15:35:53 -07:00
|
|
|
|
const struct shash *args)
|
|
|
|
|
{
|
2010-12-03 14:41:38 -08:00
|
|
|
|
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;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
memset(&port, 0, sizeof port);
|
|
|
|
|
strncpy(port.devname, netdev_dev_get_name(dev_), sizeof port.devname);
|
|
|
|
|
strncpy(port.type, netdev_dev_get_type(dev_), sizeof port.type);
|
|
|
|
|
error = vport_class->parse_config(dev_, args, port.config);
|
|
|
|
|
if (!error && memcmp(port.config, dev->config, sizeof dev->config)) {
|
|
|
|
|
error = netdev_vport_do_ioctl(ODP_VPORT_MOD, &port);
|
|
|
|
|
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);
|
|
|
|
|
}
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
2010-12-03 14:41:38 -08:00
|
|
|
|
return error;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2010-05-17 15:04:10 -07:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
memcpy(vport_ether.ether_addr, mac, ETH_ADDR_LEN);
|
|
|
|
|
|
|
|
|
|
err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_SET, &vport_ether);
|
|
|
|
|
if (err) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
netdev_vport_poll_notify(netdev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static int
|
2010-05-17 15:04:10 -07:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_GET, &vport_ether);
|
|
|
|
|
if (err) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(mac, vport_ether.ether_addr, ETH_ADDR_LEN);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static int
|
2010-05-17 15:04:10 -07:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
err = netdev_vport_do_ioctl(ODP_VPORT_MTU_GET, &vport_mtu);
|
|
|
|
|
if (err) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*mtup = vport_mtu.mtu;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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;
|
2010-11-09 13:48:57 -08:00
|
|
|
|
stats->multicast = ovsr.stats.multicast;
|
2010-05-17 15:04:10 -07:00
|
|
|
|
stats->collisions = ovsr.stats.collisions;
|
2010-11-09 13:48:57 -08:00
|
|
|
|
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;
|
2010-05-17 15:04:10 -07:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-09 12:54:34 -07:00
|
|
|
|
int
|
|
|
|
|
netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
|
|
|
|
|
{
|
|
|
|
|
struct odp_vport_stats_req ovsr;
|
|
|
|
|
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;
|
2010-11-09 13:48:57 -08:00
|
|
|
|
ovsr.stats.multicast = stats->multicast;
|
2010-06-09 12:54:34 -07:00
|
|
|
|
ovsr.stats.collisions = stats->collisions;
|
2010-11-09 13:48:57 -08:00
|
|
|
|
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;
|
2010-06-09 12:54:34 -07:00
|
|
|
|
|
|
|
|
|
err = netdev_vport_do_ioctl(ODP_VPORT_STATS_SET, &ovsr);
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
* called), it just means that it isn't attached and we'll be getting
|
|
|
|
|
* stats a different way. */
|
|
|
|
|
if (err == ENODEV) {
|
|
|
|
|
err = EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-05 11:51:15 -08:00
|
|
|
|
static int
|
|
|
|
|
netdev_vport_get_status(const struct netdev *netdev, struct shash *sh)
|
|
|
|
|
{
|
|
|
|
|
const char *iface = netdev_vport_get_tnl_iface(netdev);
|
|
|
|
|
|
|
|
|
|
if (iface) {
|
2011-01-19 14:50:01 -08:00
|
|
|
|
struct netdev *egress_netdev;
|
|
|
|
|
|
2011-01-05 11:51:15 -08:00
|
|
|
|
shash_add(sh, "tunnel_egress_iface", xstrdup(iface));
|
2011-01-19 14:50:01 -08:00
|
|
|
|
|
|
|
|
|
if (!netdev_open_default(iface, &egress_netdev)) {
|
|
|
|
|
shash_add(sh, "tunnel_egress_iface_carrier",
|
|
|
|
|
xstrdup(netdev_get_carrier(egress_netdev)
|
|
|
|
|
? "up" : "down"));
|
|
|
|
|
netdev_close(egress_netdev);
|
|
|
|
|
}
|
2011-01-05 11:51:15 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static int
|
2010-05-17 15:04:10 -07:00
|
|
|
|
netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED,
|
|
|
|
|
enum netdev_flags off, enum netdev_flags on OVS_UNUSED,
|
|
|
|
|
enum netdev_flags *old_flagsp)
|
|
|
|
|
{
|
|
|
|
|
if (off & (NETDEV_UP | NETDEV_PROMISC)) {
|
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*old_flagsp = NETDEV_UP | NETDEV_PROMISC;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
make_poll_name(const struct netdev *netdev)
|
|
|
|
|
{
|
|
|
|
|
return xasprintf("%s:%s", netdev_get_type(netdev), netdev_get_name(netdev));
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static int
|
2010-05-17 15:04:10 -07:00
|
|
|
|
netdev_vport_poll_add(struct netdev *netdev,
|
|
|
|
|
void (*cb)(struct netdev_notifier *), void *aux,
|
|
|
|
|
struct netdev_notifier **notifierp)
|
|
|
|
|
{
|
|
|
|
|
char *poll_name = make_poll_name(netdev);
|
|
|
|
|
struct netdev_vport_notifier *notifier;
|
|
|
|
|
struct list *list;
|
|
|
|
|
struct shash_node *shash_node;
|
|
|
|
|
|
|
|
|
|
shash_node = shash_find_data(&netdev_vport_notifiers, poll_name);
|
|
|
|
|
if (!shash_node) {
|
|
|
|
|
list = xmalloc(sizeof *list);
|
|
|
|
|
list_init(list);
|
2010-11-15 12:48:31 -08:00
|
|
|
|
shash_node = shash_add(&netdev_vport_notifiers, poll_name, list);
|
2010-05-17 15:04:10 -07:00
|
|
|
|
} else {
|
|
|
|
|
list = shash_node->data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
notifier = xmalloc(sizeof *notifier);
|
|
|
|
|
netdev_notifier_init(¬ifier->notifier, netdev, cb, aux);
|
|
|
|
|
list_push_back(list, ¬ifier->list_node);
|
|
|
|
|
notifier->shash_node = shash_node;
|
|
|
|
|
|
|
|
|
|
*notifierp = ¬ifier->notifier;
|
|
|
|
|
free(poll_name);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
static void
|
2010-05-17 15:04:10 -07:00
|
|
|
|
netdev_vport_poll_remove(struct netdev_notifier *notifier_)
|
|
|
|
|
{
|
|
|
|
|
struct netdev_vport_notifier *notifier =
|
|
|
|
|
CONTAINER_OF(notifier_, struct netdev_vport_notifier, notifier);
|
|
|
|
|
|
|
|
|
|
struct list *list;
|
|
|
|
|
|
|
|
|
|
list = list_remove(¬ifier->list_node);
|
|
|
|
|
if (list_is_empty(list)) {
|
|
|
|
|
shash_delete(&netdev_vport_notifiers, notifier->shash_node);
|
|
|
|
|
free(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(notifier);
|
|
|
|
|
}
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
netdev_vport_run(void)
|
|
|
|
|
{
|
|
|
|
|
rtnetlink_link_notifier_run();
|
2011-01-12 14:55:18 -08:00
|
|
|
|
route_table_run();
|
2010-12-21 16:26:21 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
netdev_vport_wait(void)
|
|
|
|
|
{
|
|
|
|
|
rtnetlink_link_notifier_wait();
|
2011-01-12 14:55:18 -08:00
|
|
|
|
route_table_wait();
|
2010-12-21 16:26:21 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get_tnl_iface() implementation. */
|
|
|
|
|
|
|
|
|
|
static struct name_node *
|
|
|
|
|
name_node_lookup(int ifi_index)
|
|
|
|
|
{
|
|
|
|
|
struct name_node *nn;
|
|
|
|
|
|
|
|
|
|
HMAP_FOR_EACH_WITH_HASH(nn, node, hash_int(ifi_index, 0), &name_map) {
|
|
|
|
|
if (nn->ifi_index == ifi_index) {
|
|
|
|
|
return nn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-12 14:55:18 -08:00
|
|
|
|
/* Queries the kernel for fresh data to populate the name map with. */
|
2010-12-21 16:26:21 -08:00
|
|
|
|
static int
|
2011-01-12 14:55:18 -08:00
|
|
|
|
netdev_vport_reset_names(void)
|
2010-12-21 16:26:21 -08:00
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
|
struct nl_dump dump;
|
|
|
|
|
struct rtgenmsg *rtmsg;
|
|
|
|
|
struct ofpbuf request, reply;
|
|
|
|
|
static struct nl_sock *rtnl_sock;
|
2011-01-12 14:55:18 -08:00
|
|
|
|
struct name_node *nn, *nn_next;
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
2011-01-12 14:55:18 -08:00
|
|
|
|
HMAP_FOR_EACH_SAFE(nn, nn_next, node, &name_map) {
|
|
|
|
|
hmap_remove(&name_map, &nn->node);
|
|
|
|
|
free(nn);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-09 16:57:45 -08:00
|
|
|
|
error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "Failed to create NETLINK_ROUTE socket");
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_init(&request, 0);
|
|
|
|
|
|
2011-01-12 14:55:18 -08:00
|
|
|
|
nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETLINK, NLM_F_REQUEST);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
|
|
|
|
rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
|
|
|
|
|
rtmsg->rtgen_family = AF_INET;
|
|
|
|
|
|
|
|
|
|
nl_dump_start(&dump, rtnl_sock, &request);
|
|
|
|
|
|
|
|
|
|
while (nl_dump_next(&dump, &reply)) {
|
2011-01-12 14:55:18 -08:00
|
|
|
|
struct rtnetlink_link_change change;
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
2011-01-12 14:55:18 -08:00
|
|
|
|
if (rtnetlink_link_parse(&reply, &change)) {
|
|
|
|
|
netdev_vport_link_change(&change, NULL);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nl_sock_destroy(rtnl_sock);
|
|
|
|
|
|
netlink-socket: Make dumping and doing transactions on same nl_sock safe.
It's not safe to use a single Netlink fd to do multiple operations in an
synchronous way. Some of the limitations are fundamental; for example, the
kernel only supports a single "dump" operation at a time. Others are
limitations imposed by the OVS coding style; for example, our Netlink
library is not callback based, so nothing can be done about incoming
messages that can't be handled immediately. Regardless, in OVS multicast
groups, transactions, and dumps cannot coexist on a single nl_sock.
This is only mildly irritating at the moment, but it will become much worse
later on, when dpif-linux shifts to using Netlink dumps for listing various
kinds of datapath entities. When that happens, a dump will be in progress
in situations where the dpif-linux client might want to do other
operations. For example, it is reasonable for the client to list flows
and, in the middle, look up information on vports mentioned in those flows.
It might be possible to simply ban and avoid such nested operations--I have
not even audited the source tree to find out whether we do anything like
that already--but that seems like an unnecessary cramp on our coding style.
Furthermore, it's difficult to explain and justify without understanding
the implementation.
This patch takes another approach, by improving the Netlink socket library
to avoid artificial constraints. When an operation, or a dump, or joining
a multicast group would cause a problem, this patch makes the library
transparently create a separate Netlink socket. This solves the problem
without putting any onerous restrictions on use.
This commit also slightly simplifies netdev_vport_reset_names(). It had
been written to destroy the dump object before the Netlink socket that it
used, but this is no longer necessary and doing it in the opposite order
saved a few lines of code.
Reviewed by Ethan Jackson <ethan@nicira.com>.
2011-01-22 15:23:10 -08:00
|
|
|
|
return nl_dump_done(&dump);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
netdev_vport_link_change(const struct rtnetlink_link_change *change,
|
|
|
|
|
void *aux OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (!change) {
|
|
|
|
|
netdev_vport_reset_names();
|
|
|
|
|
} else if (change->nlmsg_type == RTM_NEWLINK) {
|
|
|
|
|
struct name_node *nn;
|
|
|
|
|
|
|
|
|
|
if (name_node_lookup(change->ifi_index)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nn = xzalloc(sizeof *nn);
|
|
|
|
|
nn->ifi_index = change->ifi_index;
|
|
|
|
|
|
|
|
|
|
strncpy(nn->ifname, change->ifname, IFNAMSIZ);
|
|
|
|
|
nn->ifname[IFNAMSIZ - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
hmap_insert(&name_map, &nn->node, hash_int(nn->ifi_index, 0));
|
|
|
|
|
} else if (change->nlmsg_type == RTM_DELLINK) {
|
|
|
|
|
struct name_node *nn;
|
|
|
|
|
|
|
|
|
|
nn = name_node_lookup(change->ifi_index);
|
|
|
|
|
|
|
|
|
|
if (nn) {
|
|
|
|
|
hmap_remove(&name_map, &nn->node);
|
|
|
|
|
free(nn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
VLOG_WARN_RL(&rl, "Received unexpected rtnetlink message type %d",
|
|
|
|
|
change->nlmsg_type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
netdev_vport_tnl_iface_init(void)
|
|
|
|
|
{
|
|
|
|
|
static bool tnl_iface_is_init = false;
|
|
|
|
|
|
|
|
|
|
if (!tnl_iface_is_init) {
|
|
|
|
|
hmap_init(&name_map);
|
|
|
|
|
|
|
|
|
|
rtnetlink_link_notifier_register(&netdev_vport_link_notifier,
|
2011-01-12 14:55:18 -08:00
|
|
|
|
netdev_vport_link_change, NULL);
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
|
|
|
|
netdev_vport_reset_names();
|
|
|
|
|
tnl_iface_is_init = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
netdev_vport_get_tnl_iface(const struct netdev *netdev)
|
|
|
|
|
{
|
2011-01-12 14:55:18 -08:00
|
|
|
|
int ifindex;
|
2010-12-21 16:26:21 -08:00
|
|
|
|
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;
|
2011-01-12 14:55:18 -08:00
|
|
|
|
route = config->daddr;
|
2010-12-21 16:26:21 -08:00
|
|
|
|
|
2011-01-12 14:55:18 -08:00
|
|
|
|
if (route_table_get_ifindex(route, &ifindex)) {
|
2010-12-21 16:26:21 -08:00
|
|
|
|
struct name_node *nn;
|
2011-01-12 14:55:18 -08:00
|
|
|
|
HMAP_FOR_EACH_WITH_HASH(nn, node, hash_int(ifindex, 0), &name_map) {
|
|
|
|
|
if (nn->ifi_index == ifindex) {
|
2010-12-21 16:26:21 -08:00
|
|
|
|
return nn->ifname;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
|
|
|
|
/* Helper functions. */
|
2010-05-17 15:04:10 -07:00
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
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
|
2010-05-17 15:04:10 -07:00
|
|
|
|
netdev_vport_poll_notify(const struct netdev *netdev)
|
|
|
|
|
{
|
|
|
|
|
char *poll_name = make_poll_name(netdev);
|
|
|
|
|
struct list *list = shash_find_data(&netdev_vport_notifiers,
|
|
|
|
|
poll_name);
|
|
|
|
|
|
|
|
|
|
if (list) {
|
|
|
|
|
struct netdev_vport_notifier *notifier;
|
|
|
|
|
|
2010-09-17 10:33:10 -07:00
|
|
|
|
LIST_FOR_EACH (notifier, list_node, list) {
|
2010-05-17 15:04:10 -07:00
|
|
|
|
struct netdev_notifier *n = ¬ifier->notifier;
|
|
|
|
|
n->cb(n);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(poll_name);
|
|
|
|
|
}
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
|
|
|
|
/* Code specific to individual vport types. */
|
|
|
|
|
|
|
|
|
|
static int
|
2010-12-03 14:41:38 -08:00
|
|
|
|
parse_tunnel_config(const struct netdev_dev *dev, const struct shash *args,
|
|
|
|
|
void *configp)
|
2010-10-06 15:35:53 -07:00
|
|
|
|
{
|
2010-12-03 14:41:38 -08:00
|
|
|
|
const char *name = netdev_dev_get_name(dev);
|
|
|
|
|
const char *type = netdev_dev_get_type(dev);
|
2010-12-01 17:23:33 -08:00
|
|
|
|
bool is_gre = false;
|
|
|
|
|
bool is_ipsec = false;
|
2010-12-03 14:41:38 -08:00
|
|
|
|
struct tnl_port_config config;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
struct shash_node *node;
|
|
|
|
|
bool ipsec_mech_set = false;
|
|
|
|
|
|
2010-12-07 10:41:05 -08:00
|
|
|
|
memset(&config, 0, sizeof config);
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_PMTUD;
|
|
|
|
|
config.flags |= TNL_F_HDR_CACHE;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
2010-12-01 17:23:33 -08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-06 15:35:53 -07:00
|
|
|
|
SHASH_FOR_EACH (node, args) {
|
|
|
|
|
if (!strcmp(node->name, "remote_ip")) {
|
|
|
|
|
struct in_addr in_addr;
|
|
|
|
|
if (lookup_ip(node->data, &in_addr)) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.daddr = in_addr.s_addr;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "local_ip")) {
|
|
|
|
|
struct in_addr in_addr;
|
|
|
|
|
if (lookup_ip(node->data, &in_addr)) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
VLOG_WARN("%s: bad %s 'local_ip'", name, type);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.saddr = in_addr.s_addr;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "key") && is_gre) {
|
|
|
|
|
if (!strcmp(node->data, "flow")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_IN_KEY_MATCH;
|
|
|
|
|
config.flags |= TNL_F_OUT_KEY_ACTION;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-10 10:42:42 -08:00
|
|
|
|
uint64_t key = strtoull(node->data, NULL, 0);
|
|
|
|
|
config.out_key = config.in_key = htonll(key);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "in_key") && is_gre) {
|
|
|
|
|
if (!strcmp(node->data, "flow")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_IN_KEY_MATCH;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-10 10:42:42 -08:00
|
|
|
|
config.in_key = htonll(strtoull(node->data, NULL, 0));
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "out_key") && is_gre) {
|
|
|
|
|
if (!strcmp(node->data, "flow")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_OUT_KEY_ACTION;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-10 10:42:42 -08:00
|
|
|
|
config.out_key = htonll(strtoull(node->data, NULL, 0));
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "tos")) {
|
|
|
|
|
if (!strcmp(node->data, "inherit")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_TOS_INHERIT;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.tos = atoi(node->data);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "ttl")) {
|
|
|
|
|
if (!strcmp(node->data, "inherit")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_TTL_INHERIT;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.ttl = atoi(node->data);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "csum") && is_gre) {
|
|
|
|
|
if (!strcmp(node->data, "true")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags |= TNL_F_CSUM;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "pmtud")) {
|
|
|
|
|
if (!strcmp(node->data, "false")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags &= ~TNL_F_PMTUD;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "header_cache")) {
|
|
|
|
|
if (!strcmp(node->data, "false")) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
config.flags &= ~TNL_F_HDR_CACHE;
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
2010-12-15 23:44:41 -08:00
|
|
|
|
} else if (!strcmp(node->name, "peer_cert") && is_ipsec) {
|
|
|
|
|
if (shash_find(args, "certificate")) {
|
|
|
|
|
ipsec_mech_set = true;
|
|
|
|
|
} else {
|
2010-12-27 17:44:33 -08:00
|
|
|
|
const char *use_ssl_cert;
|
|
|
|
|
|
|
|
|
|
/* If the "use_ssl_cert" is true, then "certificate" and
|
|
|
|
|
* "private_key" will be pulled from the SSL table. The
|
|
|
|
|
* use of this option is strongly discouraged, since it
|
|
|
|
|
* will like be removed when multiple SSL configurations
|
|
|
|
|
* are supported by OVS.
|
|
|
|
|
*/
|
|
|
|
|
use_ssl_cert = shash_find_data(args, "use_ssl_cert");
|
|
|
|
|
if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) {
|
|
|
|
|
VLOG_WARN("%s: 'peer_cert' requires 'certificate' argument",
|
|
|
|
|
name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
ipsec_mech_set = true;
|
2010-12-15 23:44:41 -08:00
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(node->name, "psk") && is_ipsec) {
|
2010-10-06 15:35:53 -07:00
|
|
|
|
ipsec_mech_set = true;
|
2010-12-21 16:26:21 -08:00
|
|
|
|
} else if (is_ipsec
|
2010-12-15 23:44:41 -08:00
|
|
|
|
&& (!strcmp(node->name, "certificate")
|
2010-12-27 17:44:33 -08:00
|
|
|
|
|| !strcmp(node->name, "private_key")
|
|
|
|
|
|| !strcmp(node->name, "use_ssl_cert"))) {
|
2010-12-15 23:44:41 -08:00
|
|
|
|
/* Ignore options not used by the netdev. */
|
2010-10-06 15:35:53 -07:00
|
|
|
|
} else {
|
|
|
|
|
VLOG_WARN("%s: unknown %s argument '%s'",
|
2010-12-03 14:41:38 -08:00
|
|
|
|
name, type, node->name);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-15 23:44:41 -08:00
|
|
|
|
if (is_ipsec) {
|
|
|
|
|
if (shash_find(args, "peer_cert") && shash_find(args, "psk")) {
|
|
|
|
|
VLOG_WARN("%s: cannot define both 'peer_cert' and 'psk'", name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ipsec_mech_set) {
|
|
|
|
|
VLOG_WARN("%s: IPsec requires an 'peer_cert' or psk' argument",
|
|
|
|
|
name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
if (!config.daddr) {
|
2010-10-06 15:35:53 -07:00
|
|
|
|
VLOG_WARN("%s: %s type requires valid 'remote_ip' argument",
|
2010-12-03 14:41:38 -08:00
|
|
|
|
name, type);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
BUILD_ASSERT(sizeof config <= VPORT_CONFIG_SIZE);
|
|
|
|
|
memcpy(configp, &config, sizeof config);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2010-12-03 14:41:38 -08:00
|
|
|
|
parse_patch_config(const struct netdev_dev *dev, const struct shash *args,
|
|
|
|
|
void *configp)
|
2010-10-06 15:35:53 -07:00
|
|
|
|
{
|
2010-12-03 14:41:38 -08:00
|
|
|
|
const char *name = netdev_dev_get_name(dev);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
const char *peer;
|
|
|
|
|
|
|
|
|
|
peer = shash_find_data(args, "peer");
|
|
|
|
|
if (!peer) {
|
|
|
|
|
VLOG_WARN("%s: patch type requires valid 'peer' argument", name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shash_count(args) > 1) {
|
|
|
|
|
VLOG_WARN("%s: patch type takes only a 'peer' argument", name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
if (strlen(peer) >= MIN(IFNAMSIZ, VPORT_CONFIG_SIZE)) {
|
2010-10-06 15:35:53 -07:00
|
|
|
|
VLOG_WARN("%s: patch 'peer' arg too long", name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strcmp(name, peer)) {
|
|
|
|
|
VLOG_WARN("%s: patch peer must not be self", name);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
strncpy(configp, peer, VPORT_CONFIG_SIZE);
|
2010-10-06 15:35:53 -07:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-05 11:51:15 -08:00
|
|
|
|
#define VPORT_FUNCTIONS(GET_STATUS) \
|
2010-12-21 16:26:21 -08:00
|
|
|
|
netdev_vport_init, \
|
|
|
|
|
netdev_vport_run, \
|
|
|
|
|
netdev_vport_wait, \
|
2010-10-06 15:35:53 -07:00
|
|
|
|
\
|
|
|
|
|
netdev_vport_create, \
|
|
|
|
|
netdev_vport_destroy, \
|
|
|
|
|
netdev_vport_reconfigure, \
|
|
|
|
|
\
|
|
|
|
|
netdev_vport_open, \
|
|
|
|
|
netdev_vport_close, \
|
|
|
|
|
\
|
|
|
|
|
NULL, /* enumerate */ \
|
|
|
|
|
\
|
|
|
|
|
NULL, /* recv */ \
|
|
|
|
|
NULL, /* recv_wait */ \
|
|
|
|
|
NULL, /* drain */ \
|
|
|
|
|
\
|
|
|
|
|
NULL, /* send */ \
|
|
|
|
|
NULL, /* send_wait */ \
|
|
|
|
|
\
|
|
|
|
|
netdev_vport_set_etheraddr, \
|
|
|
|
|
netdev_vport_get_etheraddr, \
|
|
|
|
|
netdev_vport_get_mtu, \
|
|
|
|
|
NULL, /* get_ifindex */ \
|
2010-10-27 15:29:16 -07:00
|
|
|
|
NULL, /* get_carrier */ \
|
2011-01-07 16:22:34 -08:00
|
|
|
|
NULL, /* get_miimon */ \
|
2010-10-06 15:35:53 -07:00
|
|
|
|
netdev_vport_get_stats, \
|
|
|
|
|
netdev_vport_set_stats, \
|
|
|
|
|
\
|
|
|
|
|
NULL, /* get_features */ \
|
|
|
|
|
NULL, /* set_advertisements */ \
|
|
|
|
|
NULL, /* get_vlan_vid */ \
|
|
|
|
|
\
|
|
|
|
|
NULL, /* set_policing */ \
|
|
|
|
|
NULL, /* get_qos_types */ \
|
|
|
|
|
NULL, /* get_qos_capabilities */ \
|
|
|
|
|
NULL, /* get_qos */ \
|
|
|
|
|
NULL, /* set_qos */ \
|
|
|
|
|
NULL, /* get_queue */ \
|
|
|
|
|
NULL, /* set_queue */ \
|
|
|
|
|
NULL, /* delete_queue */ \
|
|
|
|
|
NULL, /* get_queue_stats */ \
|
|
|
|
|
NULL, /* dump_queues */ \
|
|
|
|
|
NULL, /* dump_queue_stats */ \
|
|
|
|
|
\
|
|
|
|
|
NULL, /* get_in4 */ \
|
|
|
|
|
NULL, /* set_in4 */ \
|
|
|
|
|
NULL, /* get_in6 */ \
|
|
|
|
|
NULL, /* add_router */ \
|
|
|
|
|
NULL, /* get_next_hop */ \
|
2011-01-05 11:51:15 -08:00
|
|
|
|
GET_STATUS, \
|
2010-10-06 15:35:53 -07:00
|
|
|
|
NULL, /* arp_lookup */ \
|
|
|
|
|
\
|
|
|
|
|
netdev_vport_update_flags, \
|
|
|
|
|
\
|
|
|
|
|
netdev_vport_poll_add, \
|
|
|
|
|
netdev_vport_poll_remove,
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
netdev_vport_register(void)
|
|
|
|
|
{
|
2010-12-03 14:41:38 -08:00
|
|
|
|
static const struct vport_class vport_classes[] = {
|
2011-01-05 11:51:15 -08:00
|
|
|
|
{ { "gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
|
2010-12-21 16:26:21 -08:00
|
|
|
|
parse_tunnel_config },
|
2011-01-05 11:51:15 -08:00
|
|
|
|
{ { "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
|
2010-12-21 16:26:21 -08:00
|
|
|
|
parse_tunnel_config },
|
2011-01-05 11:51:15 -08:00
|
|
|
|
{ { "capwap", VPORT_FUNCTIONS(netdev_vport_get_status) },
|
2010-12-21 16:26:21 -08:00
|
|
|
|
parse_tunnel_config },
|
|
|
|
|
{ { "patch", VPORT_FUNCTIONS(NULL) }, parse_patch_config }
|
2010-12-03 14:41:38 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
|
|
|
|
|
netdev_register_provider(&vport_classes[i].netdev_class);
|
|
|
|
|
}
|
2010-10-06 15:35:53 -07:00
|
|
|
|
}
|