+ if (strlen(peer) >= IFNAMSIZ) {
+ VLOG_ERR("%s: patch 'peer' arg too long", name);
+ return EINVAL;
+ }
+
+ if (!strcmp(name, peer)) {
+ VLOG_ERR("%s: patch peer must not be self", name);
+ return EINVAL;
+ }
+
+ 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 struct nlattr *options, size_t options_len,
+ struct shash *args)
+{
+ 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;
+
+ 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;
+}
+
+/* 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 tmp1, tmp2;
+ bool result;
+
+ smap_clone(&tmp1, nd_args);
+ smap_clone(&tmp2, args);
+
+ shash_find_and_delete(&tmp1, "psk");
+ shash_find_and_delete(&tmp2, "psk");
+ shash_find_and_delete(&tmp1, "peer_cert");
+ shash_find_and_delete(&tmp2, "peer_cert");
+ shash_find_and_delete(&tmp1, "certificate");
+ shash_find_and_delete(&tmp2, "certificate");
+ shash_find_and_delete(&tmp1, "private_key");
+ shash_find_and_delete(&tmp2, "private_key");
+ shash_find_and_delete(&tmp1, "use_ssl_cert");
+ shash_find_and_delete(&tmp2, "use_ssl_cert");
+
+ result = smap_equal(&tmp1, &tmp2);
+ smap_destroy(&tmp1);
+ smap_destroy(&tmp2);
+
+ return result;
+}
+\f
+#define VPORT_FUNCTIONS(GET_STATUS) \
+ NULL, \
+ netdev_vport_run, \
+ netdev_vport_wait, \
+ \
+ netdev_vport_create, \
+ netdev_vport_destroy, \
+ netdev_vport_set_config, \
+ netdev_vport_config_equal, \
+ \
+ netdev_vport_open, \
+ netdev_vport_close, \
+ \
+ NULL, /* enumerate */ \
+ \
+ NULL, /* recv */ \
+ NULL, /* recv_wait */ \
+ NULL, /* drain */ \
+ \
+ netdev_vport_send, /* send */ \
+ NULL, /* send_wait */ \
+ \
+ netdev_vport_set_etheraddr, \
+ netdev_vport_get_etheraddr, \
+ netdev_vport_get_mtu, \
+ NULL, /* get_ifindex */ \
+ NULL, /* get_carrier */ \
+ NULL, /* get_miimon */ \
+ 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 */ \
+ GET_STATUS, \
+ NULL, /* arp_lookup */ \
+ \
+ netdev_vport_update_flags, \
+ \
+ netdev_vport_change_seq
+
+void
+netdev_vport_register(void)
+{
+ static const struct vport_class vport_classes[] = {
+ { ODP_VPORT_TYPE_GRE,
+ { "gre", VPORT_FUNCTIONS(netdev_vport_get_status) },
+ 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, config_equal_ipsec },
+
+ { ODP_VPORT_TYPE_CAPWAP,
+ { "capwap", VPORT_FUNCTIONS(netdev_vport_get_status) },
+ parse_tunnel_config, unparse_tunnel_config, NULL },
+
+ { ODP_VPORT_TYPE_PATCH,
+ { "patch", VPORT_FUNCTIONS(NULL) },
+ parse_patch_config, unparse_patch_config, NULL }
+ };
+
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
+ netdev_register_provider(&vport_classes[i].netdev_class);
+ }