+ error = dpif_linux_vport_get(name, &reply, &buf);
+ if (!error) {
+ ofpbuf_delete(buf);
+ } else if (error != ENODEV && error != ENOENT) {
+ VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
+ name, strerror(error));
+ }
+
+ return reply.type == OVS_VPORT_TYPE_INTERNAL;
+}
+
+int
+dpif_linux_vport_send(int dp_ifindex, uint32_t port_no,
+ const void *data, size_t size)
+{
+ struct ofpbuf actions, key, packet;
+ struct odputil_keybuf keybuf;
+ struct flow flow;
+ uint64_t action;
+
+ ofpbuf_use_const(&packet, data, size);
+ flow_extract(&packet, htonll(0), 0, &flow);
+
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&key, &flow);
+
+ ofpbuf_use_stack(&actions, &action, sizeof action);
+ nl_msg_put_u32(&actions, OVS_ACTION_ATTR_OUTPUT, port_no);
+
+ return dpif_linux_execute__(dp_ifindex, key.data, key.size,
+ actions.data, actions.size, &packet);
+}
+
+static void
+dpif_linux_port_changed(const struct rtnetlink_link_change *change,
+ void *dpif_)
+{
+ struct dpif_linux *dpif = dpif_;
+
+ if (change) {
+ if (change->master_ifindex == dpif->dp_ifindex
+ && (change->nlmsg_type == RTM_NEWLINK
+ || change->nlmsg_type == RTM_DELLINK))
+ {
+ /* Our datapath changed, either adding a new port or deleting an
+ * existing one. */
+ sset_add(&dpif->changed_ports, change->ifname);
+ }
+ } else {
+ dpif->change_error = true;
+ }
+}
+\f
+/* Parses the contents of 'buf', which contains a "struct ovs_header" 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 ovs_vport_policy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32 },
+ [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32 },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
+ [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
+ .min_len = sizeof(struct rtnl_link_stats64),
+ .max_len = sizeof(struct rtnl_link_stats64),
+ .optional = true },
+ [OVS_VPORT_ATTR_ADDRESS] = { .type = NL_A_UNSPEC,
+ .min_len = ETH_ADDR_LEN,
+ .max_len = ETH_ADDR_LEN,
+ .optional = true },
+ [OVS_VPORT_ATTR_MTU] = { .type = NL_A_U32, .optional = true },
+ [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
+ [OVS_VPORT_ATTR_IFINDEX] = { .type = NL_A_U32, .optional = true },
+ [OVS_VPORT_ATTR_IFLINK] = { .type = NL_A_U32, .optional = true },
+ };
+
+ struct nlattr *a[ARRAY_SIZE(ovs_vport_policy)];
+ struct ovs_header *ovs_header;
+ struct nlmsghdr *nlmsg;
+ struct genlmsghdr *genl;
+ struct ofpbuf b;
+
+ dpif_linux_vport_init(vport);
+
+ ofpbuf_use_const(&b, buf->data, buf->size);
+ nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
+ genl = ofpbuf_try_pull(&b, sizeof *genl);
+ ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header);
+ if (!nlmsg || !genl || !ovs_header
+ || nlmsg->nlmsg_type != ovs_vport_family
+ || !nl_policy_parse(&b, 0, ovs_vport_policy, a,
+ ARRAY_SIZE(ovs_vport_policy))) {
+ return EINVAL;
+ }
+
+ vport->cmd = genl->cmd;
+ vport->dp_ifindex = ovs_header->dp_ifindex;
+ vport->port_no = nl_attr_get_u32(a[OVS_VPORT_ATTR_PORT_NO]);
+ vport->type = nl_attr_get_u32(a[OVS_VPORT_ATTR_TYPE]);
+ vport->name = nl_attr_get_string(a[OVS_VPORT_ATTR_NAME]);
+ if (a[OVS_VPORT_ATTR_STATS]) {
+ vport->stats = nl_attr_get(a[OVS_VPORT_ATTR_STATS]);