diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index bf6e593f..6aa4d8f 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -529,6 +529,13 @@ struct ipv6_txoptions *txoptions_from_opt(struct sock *sk,
 					  unsigned int max_len,
 					  unsigned int max_cnt);
 
+struct ipv6_opt_hdr *txoptions_from_tlv_opt(struct sock *sk,
+					    struct tlv_param_table
+						*tlv_param_table,
+					    struct ipv6_txoptions *opt,
+					    int optname, char __user *optval,
+					    unsigned int optlen, int *which);
+
 static inline bool ipv6_accept_ra(struct inet6_dev *idev)
 {
 	/* If forwarding is enabled, RA are not accepted unless the special
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 1c79361..018fc6f 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -289,6 +289,15 @@ struct in6_flowlabel_req {
 #define IPV6_RECVFRAGSIZE	77
 #define IPV6_FREEBIND		78
 
+/* API to set single Destination or Hop-by-Hop options */
+
+#define IPV6_HOPOPTS_TLV               79
+#define IPV6_RTHDRDSTOPTS_TLV          80
+#define IPV6_DSTOPTS_TLV               81
+#define IPV6_HOPOPTS_DEL_TLV           82
+#define IPV6_RTHDRDSTOPTS_DEL_TLV      83
+#define IPV6_DSTOPTS_DEL_TLV           84
+
 /*
  * Multicast Routing:
  * see include/uapi/linux/mroute6.h.
diff --git a/net/ipv6/exthdrs_common.c b/net/ipv6/exthdrs_common.c
index 8acff49..9e9ad85 100644
--- a/net/ipv6/exthdrs_common.c
+++ b/net/ipv6/exthdrs_common.c
@@ -1272,6 +1272,76 @@ struct ipv6_txoptions *txoptions_from_opt(struct sock *sk,
 }
 EXPORT_SYMBOL(txoptions_from_opt);
 
+struct ipv6_opt_hdr *txoptions_from_tlv_opt(struct sock *sk,
+					    struct tlv_param_table
+							*tlv_param_table,
+					     struct ipv6_txoptions *opt,
+					     int optname, char __user *optval,
+					     unsigned int optlen, int *whichp)
+{
+	struct ipv6_opt_hdr *old = NULL, *new = NULL;
+	struct net *net = sock_net(sk);
+	bool deleting = false;
+	void *new_opt = NULL;
+	int which = -1, retv;
+	bool admin;
+
+	new_opt = memdup_user(optval, optlen);
+	if (IS_ERR(new_opt))
+		return new_opt;
+
+	switch (optname) {
+	case IPV6_HOPOPTS_DEL_TLV:
+		deleting = true;
+		/* Fallthrough */
+	case IPV6_HOPOPTS_TLV:
+		if (opt)
+			old = opt->hopopt;
+		which = IPV6_HOPOPTS;
+		break;
+	case IPV6_RTHDRDSTOPTS_DEL_TLV:
+		deleting = true;
+		/* Fallthrough */
+	case IPV6_RTHDRDSTOPTS_TLV:
+		if (opt)
+			old = opt->dst0opt;
+		which = IPV6_RTHDRDSTOPTS;
+		break;
+	case IPV6_DSTOPTS_DEL_TLV:
+		deleting = true;
+		/* Fallthrough */
+	case IPV6_DSTOPTS_TLV:
+		if (opt)
+			old = opt->dst1opt;
+		which = IPV6_DSTOPTS;
+		break;
+	}
+
+	*whichp = which;
+
+	admin = ns_capable(net->user_ns, CAP_NET_RAW);
+
+	retv = ipv6_opt_validate_single_tlv(net, tlv_param_table, which,
+					    new_opt, optlen, deleting, admin);
+	if (retv < 0)
+		return ERR_PTR(retv);
+
+	if (deleting) {
+		if (!old)
+			return NULL;
+		new = ipv6_opt_tlv_delete(net, tlv_param_table, old, new_opt,
+					  admin);
+	} else {
+		new = ipv6_opt_tlv_insert(net, tlv_param_table, old, which,
+					  new_opt, admin);
+	}
+
+	kfree(new_opt);
+
+	return new;
+}
+EXPORT_SYMBOL(txoptions_from_tlv_opt);
+
 const struct nla_policy tlv_nl_policy[IPV6_TLV_ATTR_MAX + 1] = {
 	[IPV6_TLV_ATTR_TYPE] =		{ .type = NLA_U8, },
 	[IPV6_TLV_ATTR_ORDER] =		{ .type = NLA_U8, },
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b8ef0ea..3e4c0eb 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -476,6 +476,33 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 		break;
 	}
 
+	case IPV6_HOPOPTS_TLV:
+	case IPV6_RTHDRDSTOPTS_TLV:
+	case IPV6_DSTOPTS_TLV:
+	case IPV6_HOPOPTS_DEL_TLV:
+	case IPV6_RTHDRDSTOPTS_DEL_TLV:
+	case IPV6_DSTOPTS_DEL_TLV:
+	{
+		struct ipv6_txoptions *opt;
+		struct ipv6_opt_hdr *new;
+		int which;
+
+		opt = rcu_dereference_protected(np->opt,
+						lockdep_sock_is_held(sk));
+		new = txoptions_from_tlv_opt(sk, &ipv6_tlv_param_table, opt,
+					     optname, optval, optlen, &which);
+		if (IS_ERR(new)) {
+			retv = PTR_ERR(new);
+			break;
+		}
+
+		retv = ipv6_opt_update(sk, opt, which, new);
+
+		kfree(new);
+
+		break;
+	}
+
 	case IPV6_PKTINFO:
 	{
 		struct in6_pktinfo pkt;
