+tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
+ struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1])
+{
+ static const struct nl_policy ovs_tunnel_policy[] = {
+ [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32 },
+ [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32 },
+ [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
+ [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
+ [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
+ [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
+ [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
+ };
+ struct ofpbuf buf;
+
+ ofpbuf_use_const(&buf, options, options_len);
+ if (!nl_policy_parse(&buf, 0, ovs_tunnel_policy,
+ a, ARRAY_SIZE(ovs_tunnel_policy))) {
+ return EINVAL;
+ }
+ return 0;
+}
+
+static uint64_t
+get_be64_or_zero(const struct nlattr *a)
+{
+ return a ? ntohll(nl_attr_get_be64(a)) : 0;
+}
+
+static int
+unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
+ const struct nlattr *options, size_t options_len,
+ struct smap *args)
+{
+ struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
+ ovs_be32 daddr;
+ uint32_t flags;
+ int error;
+
+ error = tnl_port_config_from_nlattr(options, options_len, a);
+ if (error) {
+ return error;
+ }
+
+ flags = nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]);
+ if (!(flags & TNL_F_HDR_CACHE) == !(flags & TNL_F_IPSEC)) {
+ smap_add(args, "header_cache",
+ flags & TNL_F_HDR_CACHE ? "true" : "false");
+ }
+
+ daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
+ smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(&daddr));
+
+ if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
+ ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
+ smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(&saddr));
+ }
+
+ if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
+ smap_add(args, "key", "flow");
+ } else {
+ uint64_t in_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_IN_KEY]);
+ uint64_t out_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_OUT_KEY]);
+
+ if (in_key && in_key == out_key) {
+ smap_add_format(args, "key", "%"PRIu64, in_key);
+ } else {
+ if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
+ smap_add(args, "in_key", "flow");
+ } else if (in_key) {
+ smap_add_format(args, "in_key", "%"PRIu64, in_key);
+ }
+
+ if (!a[OVS_TUNNEL_ATTR_OUT_KEY]) {
+ smap_add(args, "out_key", "flow");
+ } else if (out_key) {
+ smap_add_format(args, "out_key", "%"PRIu64, out_key);
+ }
+ }
+ }
+
+ if (flags & TNL_F_TTL_INHERIT) {
+ smap_add(args, "tos", "inherit");
+ } else if (a[OVS_TUNNEL_ATTR_TTL]) {
+ int ttl = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
+ smap_add_format(args, "tos", "%d", ttl);
+ }
+
+ if (flags & TNL_F_TOS_INHERIT) {
+ smap_add(args, "tos", "inherit");
+ } else if (a[OVS_TUNNEL_ATTR_TOS]) {
+ int tos = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
+ smap_add_format(args, "tos", "0x%x", tos);
+ }
+
+ if (flags & TNL_F_CSUM) {
+ smap_add(args, "csum", "true");
+ }
+ if (flags & TNL_F_DF_INHERIT) {
+ smap_add(args, "df_inherit", "true");
+ }
+ if (!(flags & TNL_F_DF_DEFAULT)) {
+ smap_add(args, "df_default", "false");
+ }
+ if (!(flags & TNL_F_PMTUD)) {
+ smap_add(args, "pmtud", "false");
+ }
+
+ return 0;
+}
+
+static int
+parse_patch_config(const char *name, const char *type OVS_UNUSED,
+ const struct smap *args, struct ofpbuf *options)