+ 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 == ODP_VPORT_TYPE_INTERNAL;
+}
+
+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 odp_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 odp_vport_policy[] = {
+ [ODP_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32 },
+ [ODP_VPORT_ATTR_TYPE] = { .type = NL_A_U32 },
+ [ODP_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
+ [ODP_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
+ .min_len = sizeof(struct rtnl_link_stats64),
+ .max_len = sizeof(struct rtnl_link_stats64),
+ .optional = true },
+ [ODP_VPORT_ATTR_ADDRESS] = { .type = NL_A_UNSPEC,
+ .min_len = ETH_ADDR_LEN,
+ .max_len = ETH_ADDR_LEN,
+ .optional = true },
+ [ODP_VPORT_ATTR_MTU] = { .type = NL_A_U32, .optional = true },
+ [ODP_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
+ [ODP_VPORT_ATTR_IFINDEX] = { .type = NL_A_U32, .optional = true },
+ [ODP_VPORT_ATTR_IFLINK] = { .type = NL_A_U32, .optional = true },
+ };
+
+ struct nlattr *a[ARRAY_SIZE(odp_vport_policy)];
+ struct odp_header *odp_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);
+ odp_header = ofpbuf_try_pull(&b, sizeof *odp_header);
+ if (!nlmsg || !genl || !odp_header
+ || nlmsg->nlmsg_type != odp_vport_family
+ || !nl_policy_parse(&b, 0, odp_vport_policy, a,
+ ARRAY_SIZE(odp_vport_policy))) {
+ return EINVAL;
+ }
+
+ vport->cmd = genl->cmd;
+ vport->dp_ifindex = odp_header->dp_ifindex;
+ vport->port_no = nl_attr_get_u32(a[ODP_VPORT_ATTR_PORT_NO]);
+ vport->type = nl_attr_get_u32(a[ODP_VPORT_ATTR_TYPE]);
+ vport->name = nl_attr_get_string(a[ODP_VPORT_ATTR_NAME]);
+ if (a[ODP_VPORT_ATTR_STATS]) {
+ vport->stats = nl_attr_get(a[ODP_VPORT_ATTR_STATS]);
+ }
+ if (a[ODP_VPORT_ATTR_ADDRESS]) {
+ vport->address = nl_attr_get(a[ODP_VPORT_ATTR_ADDRESS]);
+ }
+ if (a[ODP_VPORT_ATTR_MTU]) {
+ vport->mtu = nl_attr_get_u32(a[ODP_VPORT_ATTR_MTU]);
+ } else {
+ vport->mtu = INT_MAX;
+ }
+ if (a[ODP_VPORT_ATTR_OPTIONS]) {
+ vport->options = nl_attr_get(a[ODP_VPORT_ATTR_OPTIONS]);
+ vport->options_len = nl_attr_get_size(a[ODP_VPORT_ATTR_OPTIONS]);
+ }
+ if (a[ODP_VPORT_ATTR_IFINDEX]) {
+ vport->ifindex = nl_attr_get_u32(a[ODP_VPORT_ATTR_IFINDEX]);
+ }
+ if (a[ODP_VPORT_ATTR_IFLINK]) {
+ vport->iflink = nl_attr_get_u32(a[ODP_VPORT_ATTR_IFLINK]);
+ }
+ return 0;
+}
+
+/* Appends to 'buf' (which must initially be empty) a "struct odp_header"
+ * followed by Netlink attributes corresponding to 'vport'. */
+static void
+dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *vport,
+ struct ofpbuf *buf)
+{
+ struct odp_header *odp_header;
+
+ nl_msg_put_genlmsghdr(buf, 0, odp_vport_family, NLM_F_REQUEST | NLM_F_ECHO,
+ vport->cmd, 1);
+
+ odp_header = ofpbuf_put_uninit(buf, sizeof *odp_header);
+ odp_header->dp_ifindex = vport->dp_ifindex;
+
+ if (vport->port_no != UINT32_MAX) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_PORT_NO, vport->port_no);