struct netdev_dev *netdev_dev_from_name(const char *name);
void netdev_dev_get_devices(const struct netdev_class *,
struct shash *device_list);
+bool netdev_dev_args_equal(const struct netdev_dev *netdev_dev,
+ const struct shash *args);
static inline void netdev_dev_assert_class(const struct netdev_dev *netdev_dev,
const struct netdev_class *class_)
*/
int (*set_config)(struct netdev_dev *netdev_dev, const struct shash *args);
+ /* Returns true if 'args' is equivalent to the "args" field in
+ * 'netdev_dev', otherwise false.
+ *
+ * If no special processing needs to be done beyond a simple
+ * shash comparison, this may be a null pointer.
+ */
+ bool (*config_equal)(const struct netdev_dev *netdev_dev,
+ const struct shash *args);
+
/* Attempts to open a network device. On success, sets 'netdevp'
* to the new network device.
*
int (*unparse_config)(const char *name, const char *type,
const struct nlattr *options, size_t options_len,
struct shash *args);
+ bool (*config_equal)(const struct shash *nd_args, const struct shash *args);
};
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
return error;
}
+static bool
+netdev_vport_config_equal(const struct netdev_dev *dev_,
+ const struct shash *args)
+{
+ const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
+ const struct vport_class *vport_class = vport_class_cast(netdev_class);
+
+ if (vport_class->config_equal) {
+ return vport_class->config_equal(&dev_->args, args);
+ } else {
+ return smap_equal(&dev_->args, args);
+ }
+}
+
static int
netdev_vport_send(struct netdev *netdev, const void *data, size_t size)
{
smap_add(args, "peer", nl_attr_get_string(a[ODP_PATCH_ATTR_PEER]));
return 0;
}
+
+/* Returns true if 'nd_args' is equivalent to 'args', otherwise false.
+ * Typically, 'nd_args' is the result of a call to unparse_tunnel_config()
+ * and 'args' is the original definition of the port.
+ *
+ * IPsec key configuration is handled by an external program, so it is not
+ * pushed down into the kernel module. Thus, when the "unparse_config"
+ * method is called on an existing IPsec-based vport, a simple
+ * comparison with the returned data will not match the original
+ * configuration. This function ignores configuration about keys when
+ * doing a comparison.
+ */
+static bool
+config_equal_ipsec(const struct shash *nd_args, const struct shash *args)
+{
+ struct shash tmp;
+ bool result;
+
+ smap_clone(&tmp, args);
+
+ shash_find_and_delete(&tmp, "psk");
+ shash_find_and_delete(&tmp, "peer_cert");
+ shash_find_and_delete(&tmp, "certificate");
+ shash_find_and_delete(&tmp, "private_key");
+ shash_find_and_delete(&tmp, "use_ssl_cert");
+
+ result = smap_equal(&tmp, nd_args);
+ smap_destroy(&tmp);
+
+ return result;
+}
\f
#define VPORT_FUNCTIONS(GET_STATUS) \
NULL, \
netdev_vport_create, \
netdev_vport_destroy, \
netdev_vport_set_config, \
+ netdev_vport_config_equal, \
\
netdev_vport_open, \
netdev_vport_close, \
static const struct vport_class vport_classes[] = {
{ ODP_VPORT_TYPE_GRE,
{ "gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
- parse_tunnel_config, unparse_tunnel_config },
+ parse_tunnel_config, unparse_tunnel_config, NULL },
{ ODP_VPORT_TYPE_GRE,
{ "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
- parse_tunnel_config, unparse_tunnel_config },
+ parse_tunnel_config, unparse_tunnel_config, config_equal_ipsec },
{ ODP_VPORT_TYPE_CAPWAP,
{ "capwap", VPORT_FUNCTIONS(netdev_vport_get_status) },
- parse_tunnel_config, unparse_tunnel_config },
+ parse_tunnel_config, unparse_tunnel_config, NULL },
{ ODP_VPORT_TYPE_PATCH,
{ "patch", VPORT_FUNCTIONS(NULL) },
- parse_patch_config, unparse_patch_config }
+ parse_patch_config, unparse_patch_config, NULL }
};
int i;
assert(netdev_dev->netdev_class == class);
} else if (!shash_is_empty(options->args) &&
- !smap_equal(&netdev_dev->args, options->args)) {
+ !netdev_dev_args_equal(netdev_dev, options->args)) {
VLOG_WARN("%s: attempted to open already open netdev with "
"different arguments", options->name);
}
if (netdev_dev->netdev_class->set_config) {
- if (!smap_equal(&netdev_dev->args, args)) {
+ if (!netdev_dev_args_equal(netdev_dev, args)) {
update_device_args(netdev_dev, args);
return netdev_dev->netdev_class->set_config(netdev_dev, args);
}
}
}
+/* Returns true if 'args' is equivalent to the "args" field in
+ * 'netdev_dev', otherwise false. */
+bool
+netdev_dev_args_equal(const struct netdev_dev *netdev_dev,
+ const struct shash *args)
+{
+ if (netdev_dev->netdev_class->config_equal) {
+ return netdev_dev->netdev_class->config_equal(netdev_dev, args);
+ } else {
+ return smap_equal(&netdev_dev->args, args);
+ }
+}
+
/* Initializes 'netdev' as a instance of the netdev_dev.
*
* This function adds 'netdev' to a netdev-owned linked list, so it is very