+\f
+/* Parses the contents of 'buf', which contains a "struct odp_vport" 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 odp_vport *odp_vport;
+ struct nlattr *a[ARRAY_SIZE(odp_vport_policy)];
+
+ dpif_linux_vport_init(vport);
+
+ if (!nl_policy_parse(buf, sizeof *odp_vport, odp_vport_policy,
+ a, ARRAY_SIZE(odp_vport_policy))) {
+ return EINVAL;
+ }
+ odp_vport = buf->data;
+
+ vport->dp_idx = odp_vport->dp_idx;
+ 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]);
+ }
+ 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_vport"
+ * 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_vport *odp_vport;
+
+ ofpbuf_reserve(buf, sizeof odp_vport);
+
+ if (vport->port_no != UINT32_MAX) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_PORT_NO, vport->port_no);
+ }
+
+ if (vport->type != ODP_VPORT_TYPE_UNSPEC) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_TYPE, vport->type);
+ }
+
+ if (vport->name) {
+ nl_msg_put_string(buf, ODP_VPORT_ATTR_NAME, vport->name);
+ }
+
+ if (vport->stats) {
+ nl_msg_put_unspec(buf, ODP_VPORT_ATTR_STATS,
+ vport->stats, sizeof *vport->stats);
+ }
+
+ if (vport->address) {
+ nl_msg_put_unspec(buf, ODP_VPORT_ATTR_ADDRESS,
+ vport->address, ETH_ADDR_LEN);
+ }
+
+ if (vport->mtu) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_MTU, vport->mtu);
+ }
+
+ if (vport->options) {
+ nl_msg_put_nested(buf, ODP_VPORT_ATTR_OPTIONS,
+ vport->options, vport->options_len);
+ }
+
+ if (vport->ifindex) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFINDEX, vport->ifindex);
+ }
+
+ if (vport->iflink) {
+ nl_msg_put_u32(buf, ODP_VPORT_ATTR_IFLINK, vport->iflink);
+ }
+
+ odp_vport = ofpbuf_push_uninit(buf, sizeof *odp_vport);
+ odp_vport->dp_idx = vport->dp_idx;
+ odp_vport->len = buf->size;
+ odp_vport->total_len = (char *) ofpbuf_end(buf) - (char *) buf->data;
+}
+
+/* Clears 'vport' to "empty" values. */
+void
+dpif_linux_vport_init(struct dpif_linux_vport *vport)
+{
+ memset(vport, 0, sizeof *vport);
+ vport->dp_idx = UINT32_MAX;
+ vport->port_no = UINT32_MAX;
+}
+
+/* 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 an odp_vport also, 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'). */
+int
+dpif_linux_vport_transact(const struct dpif_linux_vport *request,
+ struct dpif_linux_vport *reply,
+ struct ofpbuf **bufp)
+{
+ static int dp0_fd = -1;
+ struct ofpbuf *buf = NULL;
+ int error;
+
+ assert((reply != NULL) == (bufp != NULL));
+ if (dp0_fd < 0) {
+ int fd;
+
+ error = open_minor(0, &fd);
+ if (error) {
+ goto error;
+ }
+ dp0_fd = fd;
+ }
+
+ buf = ofpbuf_new(1024);
+ dpif_linux_vport_to_ofpbuf(request, buf);
+
+ error = ioctl(dp0_fd, request->cmd, buf->data) ? errno : 0;
+ if (error) {
+ goto error;
+ }
+
+ if (bufp) {
+ buf->size = ((struct odp_vport *) buf->data)->len;
+ error = dpif_linux_vport_from_ofpbuf(reply, buf);
+ if (error) {
+ goto error;
+ }
+ *bufp = buf;
+ } else {
+ ofpbuf_delete(buf);
+ }
+ return 0;
+
+error:
+ ofpbuf_delete(buf);
+ if (bufp) {
+ memset(reply, 0, sizeof *reply);
+ *bufp = NULL;
+ }
+ return error;
+}
+
+/* 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 = ODP_VPORT_GET;
+ request.name = name;
+
+ return dpif_linux_vport_transact(&request, reply, bufp);
+}
+