+ 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, OVS_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 smap *args)
+{
+ static const struct nl_policy ovs_patch_policy[] = {
+ [OVS_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
+ .max_len = IFNAMSIZ,
+ .optional = false }
+ };
+
+ struct nlattr *a[ARRAY_SIZE(ovs_patch_policy)];
+ struct ofpbuf buf;
+
+ ofpbuf_use_const(&buf, options, options_len);
+ if (!nl_policy_parse(&buf, 0, ovs_patch_policy,
+ a, ARRAY_SIZE(ovs_patch_policy))) {
+ return EINVAL;
+ }
+
+ smap_add(args, "peer", nl_attr_get_string(a[OVS_PATCH_ATTR_PEER]));
+ return 0;
+}
+\f
+#define VPORT_FUNCTIONS(GET_TUNNEL_CONFIG, GET_STATUS) \
+ NULL, \
+ netdev_vport_run, \
+ netdev_vport_wait, \
+ \
+ netdev_vport_create, \
+ netdev_vport_destroy, \
+ netdev_vport_get_config, \
+ netdev_vport_set_config, \
+ GET_TUNNEL_CONFIG, \
+ \
+ netdev_vport_open, \
+ netdev_vport_close, \
+ \
+ NULL, /* listen */ \
+ NULL, /* recv */ \
+ NULL, /* recv_wait */ \
+ NULL, /* drain */ \
+ \
+ NULL, /* send */ \
+ NULL, /* send_wait */ \
+ \
+ netdev_vport_set_etheraddr, \
+ netdev_vport_get_etheraddr, \
+ NULL, /* get_mtu */ \
+ NULL, /* set_mtu */ \
+ NULL, /* get_ifindex */ \
+ NULL, /* get_carrier */ \
+ NULL, /* get_carrier_resets */ \
+ NULL, /* get_miimon */ \
+ netdev_vport_get_stats, \
+ NULL, /* set_stats */ \
+ \
+ NULL, /* get_features */ \
+ NULL, /* set_advertisements */ \
+ \
+ 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
+
+#define TUNNEL_CLASS(NAME, VPORT_TYPE) \
+ { VPORT_TYPE, \
+ { NAME, VPORT_FUNCTIONS(get_netdev_tunnel_config, \
+ tunnel_get_status) }, \
+ parse_tunnel_config, unparse_tunnel_config }
+
+void
+netdev_vport_register(void)
+{
+ static const struct vport_class vport_classes[] = {
+ TUNNEL_CLASS("gre", OVS_VPORT_TYPE_GRE),
+ TUNNEL_CLASS("ipsec_gre", OVS_VPORT_TYPE_GRE),
+ TUNNEL_CLASS("gre64", OVS_VPORT_TYPE_GRE64),
+ TUNNEL_CLASS("ipsec_gre64", OVS_VPORT_TYPE_GRE64),
+ TUNNEL_CLASS("capwap", OVS_VPORT_TYPE_CAPWAP),
+ TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN),
+
+ { OVS_VPORT_TYPE_PATCH,
+ { "patch", VPORT_FUNCTIONS(NULL, NULL) },
+ parse_patch_config, unparse_patch_config }
+ };
+
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
+ netdev_register_provider(&vport_classes[i].netdev_class);
+ }