+/* Obtains information about the kernel vport named 'name' and stores it into
+ * '*reply' and '*bufp'. The caller must free '*bufp' when the reply is no
+ * longer needed ('reply' will contain pointers into '*bufp'). */
+int
+dpif_linux_vport_get(const char *name, struct dpif_linux_vport *reply,
+ struct ofpbuf **bufp)
+{
+ struct dpif_linux_vport request;
+
+ dpif_linux_vport_init(&request);
+ request.cmd = OVS_VPORT_CMD_GET;
+ request.name = name;
+
+ return dpif_linux_vport_transact(&request, reply, bufp);
+}
+\f
+/* Parses the contents of 'buf', which contains a "struct ovs_header" followed
+ * by Netlink attributes, into 'dp'. Returns 0 if successful, otherwise a
+ * positive errno value.
+ *
+ * 'dp' will contain pointers into 'buf', so the caller should not free 'buf'
+ * while 'dp' is still in use. */
+static int
+dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, const struct ofpbuf *buf)
+{
+ static const struct nl_policy ovs_datapath_policy[] = {
+ [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
+ [OVS_DP_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_dp_stats),
+ .optional = true },
+ };
+
+ struct nlattr *a[ARRAY_SIZE(ovs_datapath_policy)];
+ struct ovs_header *ovs_header;
+ struct nlmsghdr *nlmsg;
+ struct genlmsghdr *genl;
+ struct ofpbuf b;
+
+ dpif_linux_dp_init(dp);
+
+ 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_datapath_family
+ || !nl_policy_parse(&b, 0, ovs_datapath_policy, a,
+ ARRAY_SIZE(ovs_datapath_policy))) {
+ return EINVAL;
+ }
+
+ dp->cmd = genl->cmd;
+ dp->dp_ifindex = ovs_header->dp_ifindex;
+ dp->name = nl_attr_get_string(a[OVS_DP_ATTR_NAME]);
+ if (a[OVS_DP_ATTR_STATS]) {
+ /* Can't use structure assignment because Netlink doesn't ensure
+ * sufficient alignment for 64-bit members. */
+ memcpy(&dp->stats, nl_attr_get(a[OVS_DP_ATTR_STATS]),
+ sizeof dp->stats);
+ }
+
+ return 0;
+}
+
+/* Appends to 'buf' the Generic Netlink message described by 'dp'. */
+static void
+dpif_linux_dp_to_ofpbuf(const struct dpif_linux_dp *dp, struct ofpbuf *buf)
+{
+ struct ovs_header *ovs_header;
+
+ nl_msg_put_genlmsghdr(buf, 0, ovs_datapath_family,
+ NLM_F_REQUEST | NLM_F_ECHO, dp->cmd,
+ OVS_DATAPATH_VERSION);
+
+ ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header);
+ ovs_header->dp_ifindex = dp->dp_ifindex;
+
+ if (dp->name) {
+ nl_msg_put_string(buf, OVS_DP_ATTR_NAME, dp->name);
+ }
+
+ if (dp->upcall_pid) {
+ nl_msg_put_u32(buf, OVS_DP_ATTR_UPCALL_PID, *dp->upcall_pid);
+ }
+
+ /* Skip OVS_DP_ATTR_STATS since we never have a reason to serialize it. */
+}
+
+/* Clears 'dp' to "empty" values. */
+static void
+dpif_linux_dp_init(struct dpif_linux_dp *dp)
+{
+ memset(dp, 0, sizeof *dp);
+}
+
+static void
+dpif_linux_dp_dump_start(struct nl_dump *dump)
+{
+ struct dpif_linux_dp request;
+ struct ofpbuf *buf;
+
+ dpif_linux_dp_init(&request);
+ request.cmd = OVS_DP_CMD_GET;
+
+ buf = ofpbuf_new(1024);
+ dpif_linux_dp_to_ofpbuf(&request, buf);
+ nl_dump_start(dump, genl_sock, buf);
+ ofpbuf_delete(buf);
+}
+
+/* Executes 'request' in the kernel datapath. If the command fails, returns a
+ * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0
+ * without doing anything else. If 'reply' and 'bufp' are nonnull, then the
+ * result of the command is expected to be of the same form, which is decoded
+ * and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the
+ * reply is no longer needed ('reply' will contain pointers into '*bufp'). */
+static int
+dpif_linux_dp_transact(const struct dpif_linux_dp *request,
+ struct dpif_linux_dp *reply, struct ofpbuf **bufp)
+{
+ struct ofpbuf *request_buf;
+ int error;
+
+ assert((reply != NULL) == (bufp != NULL));
+
+ request_buf = ofpbuf_new(1024);
+ dpif_linux_dp_to_ofpbuf(request, request_buf);
+ error = nl_sock_transact(genl_sock, request_buf, bufp);
+ ofpbuf_delete(request_buf);
+
+ if (reply) {
+ if (!error) {
+ error = dpif_linux_dp_from_ofpbuf(reply, *bufp);
+ }
+ if (error) {
+ dpif_linux_dp_init(reply);
+ ofpbuf_delete(*bufp);
+ *bufp = NULL;
+ }
+ }