+ 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 bool
+dpif_linux_nln_parse(struct ofpbuf *buf, void *vport_)
+{
+ struct dpif_linux_vport *vport = vport_;
+ return dpif_linux_vport_from_ofpbuf(vport, buf) == 0;
+}
+
+static void
+dpif_linux_port_changed(const void *vport_, void *dpif_)
+{
+ const struct dpif_linux_vport *vport = vport_;
+ struct dpif_linux *dpif = dpif_;
+
+ if (vport) {
+ if (vport->dp_ifindex == dpif->dp_ifindex
+ && (vport->cmd == OVS_VPORT_CMD_NEW
+ || vport->cmd == OVS_VPORT_CMD_DEL
+ || vport->cmd == OVS_VPORT_CMD_SET)) {
+ VLOG_DBG("port_changed: dpif:%s vport:%s cmd:%"PRIu8,
+ dpif->dpif.full_name, vport->name, vport->cmd);
+ sset_add(&dpif->changed_ports, vport->name);
+ }
+ } 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 ovs_vport_stats),
+ .max_len = sizeof(struct ovs_vport_stats),
+ .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_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
+ [OVS_VPORT_ATTR_IFINDEX] = { .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]);
+ }
+ if (a[OVS_VPORT_ATTR_ADDRESS]) {
+ vport->address = nl_attr_get(a[OVS_VPORT_ATTR_ADDRESS]);
+ }
+ if (a[OVS_VPORT_ATTR_OPTIONS]) {
+ vport->options = nl_attr_get(a[OVS_VPORT_ATTR_OPTIONS]);
+ vport->options_len = nl_attr_get_size(a[OVS_VPORT_ATTR_OPTIONS]);
+ }
+ if (a[OVS_VPORT_ATTR_IFINDEX]) {
+ vport->ifindex = nl_attr_get_u32(a[OVS_VPORT_ATTR_IFINDEX]);