static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
			  struct netlink_ext_ack *extack,
			  struct geneve_config *cfg, bool changelink)
{
	struct ip_tunnel_info *info = &cfg->info;
	int attrtype;

	if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) {
		NL_SET_ERR_MSG(extack,
			       "Cannot specify both IPv4 and IPv6 Remote addresses");
		return -EINVAL;
	}

	if (data[IFLA_GENEVE_REMOTE]) {
		if (changelink && (ip_tunnel_info_af(info) == AF_INET6)) {
			attrtype = IFLA_GENEVE_REMOTE;
			goto change_notsup;
		}

		info->key.u.ipv4.dst =
			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);

		if (ipv4_is_multicast(info->key.u.ipv4.dst)) {
			NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE],
					    "Remote IPv4 address cannot be Multicast");
			return -EINVAL;
		}
	}

	if (data[IFLA_GENEVE_REMOTE6]) {
#if IS_ENABLED(CONFIG_IPV6)
		if (changelink && (ip_tunnel_info_af(info) == AF_INET)) {
			attrtype = IFLA_GENEVE_REMOTE6;
			goto change_notsup;
		}

		info->mode = IP_TUNNEL_INFO_IPV6;
		info->key.u.ipv6.dst =
			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);

		if (ipv6_addr_type(&info->key.u.ipv6.dst) &
		    IPV6_ADDR_LINKLOCAL) {
			NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6],
					    "Remote IPv6 address cannot be link-local");
			return -EINVAL;
		}
		if (ipv6_addr_is_multicast(&info->key.u.ipv6.dst)) {
			NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6],
					    "Remote IPv6 address cannot be Multicast");
			return -EINVAL;
		}
		info->key.tun_flags |= TUNNEL_CSUM;
		cfg->use_udp6_rx_checksums = true;
#else
		NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6],
				    "IPv6 support not enabled in the kernel");
		return -EPFNOSUPPORT;
#endif
	}

	if (data[IFLA_GENEVE_ID]) {
		__u32 vni;
		__u8 tvni[3];
		__be64 tunid;

		vni = nla_get_u32(data[IFLA_GENEVE_ID]);
		tvni[0] = (vni & 0x00ff0000) >> 16;
		tvni[1] = (vni & 0x0000ff00) >> 8;
		tvni[2] =  vni & 0x000000ff;

		tunid = vni_to_tunnel_id(tvni);
		if (changelink && (tunid != info->key.tun_id)) {
			attrtype = IFLA_GENEVE_ID;
			goto change_notsup;
		}
		info->key.tun_id = tunid;
	}

	if (data[IFLA_GENEVE_TTL_INHERIT]) {
		if (nla_get_u8(data[IFLA_GENEVE_TTL_INHERIT]))
			cfg->ttl_inherit = true;
		else
			cfg->ttl_inherit = false;
	} else if (data[IFLA_GENEVE_TTL]) {
		info->key.ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
		cfg->ttl_inherit = false;
	}

	if (data[IFLA_GENEVE_TOS])
		info->key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]);

	if (data[IFLA_GENEVE_DF])
		cfg->df = nla_get_u8(data[IFLA_GENEVE_DF]);

	if (data[IFLA_GENEVE_LABEL]) {
		info->key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) &
				  IPV6_FLOWLABEL_MASK;
		if (info->key.label && (!(info->mode & IP_TUNNEL_INFO_IPV6))) {
			NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LABEL],
					    "Label attribute only applies for IPv6 Geneve devices");
			return -EINVAL;
		}
	}

	if (data[IFLA_GENEVE_PORT]) {
		if (changelink) {
			attrtype = IFLA_GENEVE_PORT;
			goto change_notsup;
		}
		info->key.tp_dst = nla_get_be16(data[IFLA_GENEVE_PORT]);
	}

	if (data[IFLA_GENEVE_COLLECT_METADATA]) {
		if (changelink) {
			attrtype = IFLA_GENEVE_COLLECT_METADATA;
			goto change_notsup;
		}
		cfg->collect_md = true;
	}

	if (data[IFLA_GENEVE_UDP_CSUM]) {
		if (changelink) {
			attrtype = IFLA_GENEVE_UDP_CSUM;
			goto change_notsup;
		}
		if (nla_get_u8(data[IFLA_GENEVE_UDP_CSUM]))
			info->key.tun_flags |= TUNNEL_CSUM;
	}

	if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) {
#if IS_ENABLED(CONFIG_IPV6)
		if (changelink) {
			attrtype = IFLA_GENEVE_UDP_ZERO_CSUM6_TX;
			goto change_notsup;
		}
		if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
			info->key.tun_flags &= ~TUNNEL_CSUM;
#else
		NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX],
				    "IPv6 support not enabled in the kernel");
		return -EPFNOSUPPORT;
#endif
	}

	if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) {
#if IS_ENABLED(CONFIG_IPV6)
		if (changelink) {
			attrtype = IFLA_GENEVE_UDP_ZERO_CSUM6_RX;
			goto change_notsup;
		}
		if (nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
			cfg->use_udp6_rx_checksums = false;
#else
		NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX],
				    "IPv6 support not enabled in the kernel");
		return -EPFNOSUPPORT;
#endif
	}

	return 0;
change_notsup:
	NL_SET_ERR_MSG_ATTR(extack, data[attrtype],
			    "Changing VNI, Port, endpoint IP address family, external, and UDP checksum attributes are not supported");
	return -EOPNOTSUPP;
}