diff mbox

[v6,1/2] net: ipv6: Unduplicate UDP, raw, and L2TP sendmsg

Message ID 1399057868-3488-2-git-send-email-lorenzo@google.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Lorenzo Colitti May 2, 2014, 7:11 p.m. UTC
udpv6_sendmsg, rawv6_sendmsg and l2tp_ip6_sendmsg share ~100
lines of mostly identical code. Move this into a new
ipv6_datagram_send_common helper function.

In order to do so without having to pass around too many
parameters, define a new ip6_output_opts structure that holds
some of the information needed by ip6_append_data and retrieved
from userspace by ip6_datagram_send_ctl.

Tested: black-box functional testing using user-mode Linux. The
following were tested to work as expected:

- UDP sends using sendto and connect/send.
- Raw socket sends using sendto.
- Mark routing and oif routing using SO_BINDTODEVICE on UDP and
  raw sockets.
- IPv6 extension headers set on UDP packets via setsockopt()
- L2TP compiles with CONFIG_IPV6={m,y} and CONFIG_L2TP_IP={m,y}.

Untested:
- Setting hop limit, tclass and options via cmsg.

Performance: netperf UDP_STREAM results (1-byte and 1452-byte
payloads) shows impact is in the noise (+-1%).

Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
---
 include/net/ipv6.h       |  33 ++++++++++-
 include/net/transp_v6.h  |   7 ++-
 net/ipv6/datagram.c      | 145 +++++++++++++++++++++++++++++++++++++++++++++--
 net/ipv6/icmp.c          |  27 +++++----
 net/ipv6/ip6_flowlabel.c |   6 +-
 net/ipv6/ip6_output.c    |  11 ++--
 net/ipv6/ipv6_sockglue.c |   7 ++-
 net/ipv6/ping.c          |  11 ++--
 net/ipv6/raw.c           | 101 ++-------------------------------
 net/ipv6/udp.c           | 122 ++++-----------------------------------
 net/l2tp/l2tp_ip6.c      | 108 +++--------------------------------
 11 files changed, 230 insertions(+), 348 deletions(-)

Comments

Hannes Frederic Sowa May 5, 2014, 11:15 a.m. UTC | #1
Hi!

On Fri, May 2, 2014, at 12:11, Lorenzo Colitti wrote:
> +
> +struct ip6_output_opts {
> +       int hlimit: 9;
> +       int tclass: 9;
> +       int dontfrag: 2;
> +       unsigned connected: 1;
> +       struct ipv6_txoptions *opt;
> +};

I don't think the bitfields are useful, they can cause more RMW cycles
and at least on 64-bit systems the structure does not shrink in size if
you instead just use s16 and bool types because of the padding needed
for the pointer. On 32-bit it does save 4 bytes on the stack, but I
don't think it is worth it.

If you put the pointer above the other members, newly added fields at
the bottom can eat into padding space instead of leaving padding holes
in it.

Greetings,

  Hannes
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller May 5, 2014, 5:41 p.m. UTC | #2
From: Hannes Frederic Sowa <hannes@stressinduktion.org>
Date: Mon, 05 May 2014 04:15:57 -0700

> Hi!
> 
> On Fri, May 2, 2014, at 12:11, Lorenzo Colitti wrote:
>> +
>> +struct ip6_output_opts {
>> +       int hlimit: 9;
>> +       int tclass: 9;
>> +       int dontfrag: 2;
>> +       unsigned connected: 1;
>> +       struct ipv6_txoptions *opt;
>> +};
> 
> I don't think the bitfields are useful, they can cause more RMW cycles
> and at least on 64-bit systems the structure does not shrink in size if
> you instead just use s16 and bool types because of the padding needed
> for the pointer. On 32-bit it does save 4 bytes on the stack, but I
> don't think it is worth it.
> 
> If you put the pointer above the other members, newly added fields at
> the bottom can eat into padding space instead of leaving padding holes
> in it.

Furthermore, some architectures are just going to pass this in via two
registers anyways, so it doesn't save argument count in practice.

I apologize, but I still don't like these changes :-/
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 5b40ad2..eb69821 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -706,6 +706,28 @@  static inline u8 ip6_tclass(__be32 flowinfo)
 {
 	return ntohl(flowinfo & IPV6_TCLASS_MASK) >> IPV6_TCLASS_SHIFT;
 }
+
+struct ip6_output_opts {
+	int hlimit: 9;
+	int tclass: 9;
+	int dontfrag: 2;
+	unsigned connected: 1;
+	struct ipv6_txoptions *opt;
+};
+#define IP6_OUTOPTS_INIT { -1, -1, -1, 0, NULL }
+
+static inline void ip6_output_opts_sk_dst_init(struct ip6_output_opts *outopts,
+					       struct ipv6_pinfo *np,
+					       struct flowi6 *fl6,
+					       struct dst_entry *dst)
+{
+	outopts->hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
+	outopts->tclass = np->tclass;
+	outopts->dontfrag = np->dontfrag;
+	outopts->connected = 0;
+	outopts->opt = np->opt;
+}
+
 /*
  *	Prototypes exported by ipv6
  */
@@ -730,9 +752,10 @@  int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
 int ip6_append_data(struct sock *sk,
 		    int getfrag(void *from, char *to, int offset, int len,
 				int odd, struct sk_buff *skb),
-		    void *from, int length, int transhdrlen, int hlimit,
-		    int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
-		    struct rt6_info *rt, unsigned int flags, int dontfrag);
+		    void *from, int length, int transhdrlen,
+		    struct flowi6 *fl6, struct rt6_info *rt,
+		    const struct ip6_output_opts *outopts,
+		    unsigned int flags);
 
 int ip6_push_pending_frames(struct sock *sk);
 
@@ -804,6 +827,10 @@  int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
 int ip6_datagram_connect(struct sock *sk, struct sockaddr *addr, int addr_len);
 int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *addr,
 				 int addr_len);
+int ip6_datagram_send_common(struct sock *sk, struct msghdr *msg,
+			     struct flowi6 *fl6, struct dst_entry **dstp,
+			     struct ip6_output_opts *opt,
+			     struct ipv6_txoptions *opt_space);
 
 int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len,
 		    int *addr_len);
diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h
index b927413..c2b8324 100644
--- a/include/net/transp_v6.h
+++ b/include/net/transp_v6.h
@@ -11,6 +11,7 @@  extern struct proto tcpv6_prot;
 extern struct proto pingv6_prot;
 
 struct flowi6;
+struct ip6_output_opts;
 
 /* extension headers */
 int ipv6_exthdrs_init(void);
@@ -40,9 +41,9 @@  void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
 void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 				    struct sk_buff *skb);
 
-int ip6_datagram_send_ctl(struct net *net, struct sock *sk, struct msghdr *msg,
-			  struct flowi6 *fl6, struct ipv6_txoptions *opt,
-			  int *hlimit, int *tclass, int *dontfrag);
+int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
+			  struct msghdr *msg, struct flowi6 *fl6,
+			  struct ip6_output_opts *outopts);
 
 void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
 			     __u16 srcp, __u16 destp, int bucket);
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index c3bf2d2..68b8009 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -640,13 +640,13 @@  EXPORT_SYMBOL_GPL(ip6_datagram_recv_ctl);
 
 int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
 			  struct msghdr *msg, struct flowi6 *fl6,
-			  struct ipv6_txoptions *opt,
-			  int *hlimit, int *tclass, int *dontfrag)
+			  struct ip6_output_opts *outopts)
 {
 	struct in6_pktinfo *src_info;
 	struct cmsghdr *cmsg;
 	struct ipv6_rt_hdr *rthdr;
 	struct ipv6_opt_hdr *hdr;
+	struct ipv6_txoptions *opt = outopts->opt;
 	int len;
 	int err = 0;
 
@@ -854,18 +854,23 @@  int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
 
 		case IPV6_2292HOPLIMIT:
 		case IPV6_HOPLIMIT:
+		    {
+			int hlimit;
+
 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
 				err = -EINVAL;
 				goto exit_f;
 			}
 
-			*hlimit = *(int *)CMSG_DATA(cmsg);
-			if (*hlimit < -1 || *hlimit > 0xff) {
+			hlimit = *(int *)CMSG_DATA(cmsg);
+			if (hlimit < -1 || hlimit > 0xff) {
 				err = -EINVAL;
 				goto exit_f;
 			}
+			outopts->hlimit = hlimit;
 
 			break;
+		    }
 
 		case IPV6_TCLASS:
 		    {
@@ -880,7 +885,7 @@  int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
 				goto exit_f;
 
 			err = 0;
-			*tclass = tc;
+			outopts->tclass = tc;
 
 			break;
 		    }
@@ -898,7 +903,7 @@  int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
 				goto exit_f;
 
 			err = 0;
-			*dontfrag = df;
+			outopts->dontfrag = df;
 
 			break;
 		    }
@@ -915,6 +920,134 @@  exit_f:
 }
 EXPORT_SYMBOL_GPL(ip6_datagram_send_ctl);
 
+int ip6_datagram_send_common(struct sock *sk, struct msghdr *msg,
+			     struct flowi6 *fl6, struct dst_entry **dstp,
+			     struct ip6_output_opts *outopts,
+			     struct ipv6_txoptions *opt_space)
+{
+	struct ipv6_txoptions *opt = NULL;
+	struct ip6_flowlabel *flowlabel = NULL;
+	struct in6_addr *final_p, final;
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct in6_addr *daddr;
+	struct dst_entry *dst;
+	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
+	int addr_len = msg->msg_namelen;
+	int err;
+	int connected;
+
+	if (sin6) {
+		daddr = &sin6->sin6_addr;
+
+		if (np->sndflow) {
+			fl6->flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+			if (fl6->flowlabel&IPV6_FLOWLABEL_MASK) {
+				flowlabel = fl6_sock_lookup(sk, fl6->flowlabel);
+				if (flowlabel == NULL)
+					return -EINVAL;
+			}
+		}
+
+		/* Otherwise it will be difficult to maintain
+		 * sk->sk_dst_cache.
+		 */
+		if (sk->sk_state == TCP_ESTABLISHED &&
+		    ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
+			daddr = &sk->sk_v6_daddr;
+
+		if (addr_len >= sizeof(struct sockaddr_in6) &&
+		    sin6->sin6_scope_id &&
+		    __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr)))
+			fl6->flowi6_oif = sin6->sin6_scope_id;
+
+		connected = 0;
+	} else {
+		if (sk->sk_state != TCP_ESTABLISHED)
+			return -EDESTADDRREQ;
+
+		daddr = &sk->sk_v6_daddr;
+		fl6->flowlabel = np->flow_label;
+		connected = 1;
+	}
+
+	if (!fl6->flowi6_oif)
+		fl6->flowi6_oif = sk->sk_bound_dev_if;
+
+	if (!fl6->flowi6_oif)
+		fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
+
+	fl6->flowi6_mark = sk->sk_mark;
+
+	if (msg->msg_controllen) {
+		opt = opt_space;
+		memset(opt, 0, sizeof(*opt));
+		opt->tot_len = sizeof(*opt);
+
+		err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, fl6,
+					    outopts);
+		if (err < 0) {
+			fl6_sock_release(flowlabel);
+			return err;
+		}
+		if ((fl6->flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
+			flowlabel = fl6_sock_lookup(sk, fl6->flowlabel);
+			if (flowlabel == NULL)
+				return -EINVAL;
+		}
+		if (!(opt->opt_nflen|opt->opt_flen))
+			opt = NULL;
+		connected = 0;
+	}
+	if (opt == NULL)
+		opt = np->opt;
+	if (flowlabel)
+		opt = fl6_merge_options(opt_space, flowlabel, opt);
+	opt = ipv6_fixup_options(opt_space, opt);
+
+	fl6_sock_release(flowlabel);
+
+	if (!ipv6_addr_any(daddr))
+		fl6->daddr = *daddr;
+	else
+		fl6->daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */
+
+	if (ipv6_addr_any(&fl6->saddr) && !ipv6_addr_any(&np->saddr))
+		fl6->saddr = np->saddr;
+
+	final_p = fl6_update_dst(fl6, opt, &final);
+	if (final_p)
+		connected = 0;
+
+	if (!fl6->flowi6_oif && ipv6_addr_is_multicast(&fl6->daddr)) {
+		fl6->flowi6_oif = np->mcast_oif;
+		connected = 0;
+	} else if (!fl6->flowi6_oif)
+		fl6->flowi6_oif = np->ucast_oif;
+
+	security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
+
+	dst = ip6_sk_dst_lookup_flow(sk, fl6, final_p);
+	if (IS_ERR(dst))
+		return PTR_ERR(dst);
+
+	if (outopts->hlimit < 0)
+		outopts->hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
+
+	if (outopts->tclass < 0)
+		outopts->tclass = np->tclass;
+
+	if (outopts->dontfrag < 0)
+		outopts->dontfrag = np->dontfrag;
+
+	outopts->opt = opt;
+	outopts->connected = connected;
+
+	*dstp = dst;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ip6_datagram_send_common);
+
 void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
 			     __u16 srcp, __u16 destp, int bucket)
 {
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 3b0905b..ebcefa9 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -398,8 +398,8 @@  static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
 	int iif = 0;
 	int addr_type = 0;
 	int len;
-	int hlimit;
 	int err = 0;
+	struct ip6_output_opts outopts;
 
 	if ((u8 *)hdr < skb->head ||
 	    (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb))
@@ -493,7 +493,7 @@  static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
 	if (IS_ERR(dst))
 		goto out;
 
-	hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
+	ip6_output_opts_sk_dst_init(&outopts, np, &fl6, dst);
 
 	msg.skb = skb;
 	msg.offset = skb_network_offset(skb);
@@ -511,9 +511,9 @@  static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
 
 	err = ip6_append_data(sk, icmpv6_getfrag, &msg,
 			      len + sizeof(struct icmp6hdr),
-			      sizeof(struct icmp6hdr), hlimit,
-			      np->tclass, NULL, &fl6, (struct rt6_info *)dst,
-			      MSG_DONTWAIT, np->dontfrag);
+			      sizeof(struct icmp6hdr),
+			      &fl6, (struct rt6_info *)dst, &outopts,
+			      MSG_DONTWAIT);
 	if (err) {
 		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS);
 		ip6_flush_pending_frames(sk);
@@ -546,11 +546,10 @@  static void icmpv6_echo_reply(struct sk_buff *skb)
 	struct icmp6hdr *icmph = icmp6_hdr(skb);
 	struct icmp6hdr tmp_hdr;
 	struct flowi6 fl6;
+	struct ip6_output_opts outopts;
 	struct icmpv6_msg msg;
 	struct dst_entry *dst;
 	int err = 0;
-	int hlimit;
-	u8 tclass;
 
 	saddr = &ipv6_hdr(skb)->daddr;
 
@@ -588,19 +587,19 @@  static void icmpv6_echo_reply(struct sk_buff *skb)
 	if (IS_ERR(dst))
 		goto out;
 
-	hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
-
 	idev = __in6_dev_get(skb->dev);
 
 	msg.skb = skb;
 	msg.offset = 0;
 	msg.type = ICMPV6_ECHO_REPLY;
 
-	tclass = ipv6_get_dsfield(ipv6_hdr(skb));
-	err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
-				sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl6,
-				(struct rt6_info *)dst, MSG_DONTWAIT,
-				np->dontfrag);
+	ip6_output_opts_sk_dst_init(&outopts, np, &fl6, dst);
+	outopts.tclass = ipv6_get_dsfield(ipv6_hdr(skb));
+
+	err = ip6_append_data(sk, icmpv6_getfrag, &msg,
+			      skb->len + sizeof(struct icmp6hdr),
+			      sizeof(struct icmp6hdr), &fl6,
+			      (struct rt6_info *)dst, &outopts, MSG_DONTWAIT);
 
 	if (err) {
 		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 4052694..444595c 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -374,7 +374,7 @@  fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
 	if (olen > 0) {
 		struct msghdr msg;
 		struct flowi6 flowi6;
-		int junk;
+		struct ip6_output_opts outopts;
 
 		err = -ENOMEM;
 		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
@@ -391,10 +391,10 @@  fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
 		msg.msg_control = (void*)(fl->opt+1);
 		memset(&flowi6, 0, sizeof(flowi6));
 
-		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt,
-					    &junk, &junk, &junk);
+		err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, &outopts);
 		if (err)
 			goto done;
+		fl->opt = outopts.opt;
 		err = -EINVAL;
 		if (fl->opt->opt_flen)
 			goto done;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 40e7581..d358ddd 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1123,8 +1123,9 @@  static void ip6_append_data_mtu(unsigned int *mtu,
 int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 	int offset, int len, int odd, struct sk_buff *skb),
 	void *from, int length, int transhdrlen,
-	int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
-	struct rt6_info *rt, unsigned int flags, int dontfrag)
+	struct flowi6 *fl6, struct rt6_info *rt,
+	const struct ip6_output_opts *outopts,
+	unsigned int flags)
 {
 	struct inet_sock *inet = inet_sk(sk);
 	struct ipv6_pinfo *np = inet6_sk(sk);
@@ -1138,6 +1139,8 @@  int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 	int err;
 	int offset = 0;
 	__u8 tx_flags = 0;
+	struct ipv6_txoptions *opt = outopts->opt;
+	int dontfrag = outopts->dontfrag;
 
 	if (flags&MSG_PROBE)
 		return 0;
@@ -1183,8 +1186,8 @@  int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		dst_hold(&rt->dst);
 		cork->dst = &rt->dst;
 		inet->cork.fl.u.ip6 = *fl6;
-		np->cork.hop_limit = hlimit;
-		np->cork.tclass = tclass;
+		np->cork.hop_limit = outopts->hlimit;
+		np->cork.tclass = outopts->tclass;
 		if (rt->dst.flags & DST_XFRM_TUNNEL)
 			mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
 			      rt->dst.dev->mtu : dst_mtu(&rt->dst);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index edb58af..e514ad8 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -443,10 +443,10 @@  sticky_done:
 
 	case IPV6_2292PKTOPTIONS:
 	{
+		struct ip6_output_opts outopts;
 		struct ipv6_txoptions *opt = NULL;
 		struct msghdr msg;
 		struct flowi6 fl6;
-		int junk;
 
 		memset(&fl6, 0, sizeof(fl6));
 		fl6.flowi6_oif = sk->sk_bound_dev_if;
@@ -476,13 +476,14 @@  sticky_done:
 		msg.msg_controllen = optlen;
 		msg.msg_control = (void*)(opt+1);
 
-		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, opt, &junk,
-					     &junk, &junk);
+		outopts.opt = opt;
+		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &outopts);
 		if (retv)
 			goto done;
 update:
 		retv = 0;
 		opt = ipv6_update_options(sk, opt);
+
 done:
 		if (opt)
 			sock_kfree_s(sk, opt, opt->tot_len);
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index a2a1d80..ee633a6 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -83,13 +83,13 @@  int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 {
 	struct inet_sock *inet = inet_sk(sk);
 	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct ip6_output_opts outopts;
 	struct icmp6hdr user_icmph;
 	int addr_type;
 	struct in6_addr *daddr;
 	int iif = 0;
 	struct flowi6 fl6;
 	int err;
-	int hlimit;
 	struct dst_entry *dst;
 	struct rt6_info *rt;
 	struct pingfakehdr pfh;
@@ -168,13 +168,12 @@  int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 	pfh.wcheck = 0;
 	pfh.family = AF_INET6;
 
-	hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
+
+	ip6_output_opts_sk_dst_init(&outopts, np, &fl6, dst);
 
 	lock_sock(sk);
-	err = ip6_append_data(sk, ping_getfrag, &pfh, len,
-			      0, hlimit,
-			      np->tclass, NULL, &fl6, rt,
-			      MSG_DONTWAIT, np->dontfrag);
+	err = ip6_append_data(sk, ping_getfrag, &pfh, len, 0,
+			      &outopts, &fl6, rt, MSG_DONTWAIT);
 
 	if (err) {
 		ICMP6_INC_STATS(sock_net(sk), rt->rt6i_idev,
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index dddfb5f..146faca 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -739,18 +739,12 @@  static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 {
 	struct ipv6_txoptions opt_space;
 	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
-	struct in6_addr *daddr, *final_p, final;
 	struct inet_sock *inet = inet_sk(sk);
-	struct ipv6_pinfo *np = inet6_sk(sk);
 	struct raw6_sock *rp = raw6_sk(sk);
-	struct ipv6_txoptions *opt = NULL;
-	struct ip6_flowlabel *flowlabel = NULL;
+	struct ip6_output_opts outopts = IP6_OUTOPTS_INIT;
 	struct dst_entry *dst = NULL;
 	struct flowi6 fl6;
 	int addr_len = msg->msg_namelen;
-	int hlimit = -1;
-	int tclass = -1;
-	int dontfrag = -1;
 	u16 proto;
 	int err;
 
@@ -769,8 +763,6 @@  static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	 */
 	memset(&fl6, 0, sizeof(fl6));
 
-	fl6.flowi6_mark = sk->sk_mark;
-
 	if (sin6) {
 		if (addr_len < SIN6_LEN_RFC2133)
 			return -EINVAL;
@@ -788,99 +780,19 @@  static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 
 		if (proto > 255)
 			return -EINVAL;
-
-		daddr = &sin6->sin6_addr;
-		if (np->sndflow) {
-			fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
-			if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
-				flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-				if (flowlabel == NULL)
-					return -EINVAL;
-			}
-		}
-
-		/*
-		 * Otherwise it will be difficult to maintain
-		 * sk->sk_dst_cache.
-		 */
-		if (sk->sk_state == TCP_ESTABLISHED &&
-		    ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
-			daddr = &sk->sk_v6_daddr;
-
-		if (addr_len >= sizeof(struct sockaddr_in6) &&
-		    sin6->sin6_scope_id &&
-		    __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr)))
-			fl6.flowi6_oif = sin6->sin6_scope_id;
 	} else {
-		if (sk->sk_state != TCP_ESTABLISHED)
-			return -EDESTADDRREQ;
-
 		proto = inet->inet_num;
-		daddr = &sk->sk_v6_daddr;
-		fl6.flowlabel = np->flow_label;
-	}
-
-	if (fl6.flowi6_oif == 0)
-		fl6.flowi6_oif = sk->sk_bound_dev_if;
-
-	if (msg->msg_controllen) {
-		opt = &opt_space;
-		memset(opt, 0, sizeof(struct ipv6_txoptions));
-		opt->tot_len = sizeof(struct ipv6_txoptions);
-
-		err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
-					    &hlimit, &tclass, &dontfrag);
-		if (err < 0) {
-			fl6_sock_release(flowlabel);
-			return err;
-		}
-		if ((fl6.flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
-			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-			if (flowlabel == NULL)
-				return -EINVAL;
-		}
-		if (!(opt->opt_nflen|opt->opt_flen))
-			opt = NULL;
 	}
-	if (opt == NULL)
-		opt = np->opt;
-	if (flowlabel)
-		opt = fl6_merge_options(&opt_space, flowlabel, opt);
-	opt = ipv6_fixup_options(&opt_space, opt);
 
 	fl6.flowi6_proto = proto;
 	err = rawv6_probe_proto_opt(&fl6, msg);
 	if (err)
 		goto out;
 
-	if (!ipv6_addr_any(daddr))
-		fl6.daddr = *daddr;
-	else
-		fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */
-	if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr))
-		fl6.saddr = np->saddr;
-
-	final_p = fl6_update_dst(&fl6, opt, &final);
-
-	if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
-		fl6.flowi6_oif = np->mcast_oif;
-	else if (!fl6.flowi6_oif)
-		fl6.flowi6_oif = np->ucast_oif;
-	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
-
-	dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
-	if (IS_ERR(dst)) {
-		err = PTR_ERR(dst);
+	err = ip6_datagram_send_common(sk, msg, &fl6, &dst,
+				       &outopts, &opt_space);
+	if (err)
 		goto out;
-	}
-	if (hlimit < 0)
-		hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
-
-	if (tclass < 0)
-		tclass = np->tclass;
-
-	if (dontfrag < 0)
-		dontfrag = np->dontfrag;
 
 	if (msg->msg_flags&MSG_CONFIRM)
 		goto do_confirm;
@@ -891,8 +803,8 @@  back_from_confirm:
 	else {
 		lock_sock(sk);
 		err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
-			len, 0, hlimit, tclass, opt, &fl6, (struct rt6_info*)dst,
-			msg->msg_flags, dontfrag);
+			len, 0, &fl6, (struct rt6_info *)dst, &outopts,
+			msg->msg_flags);
 
 		if (err)
 			ip6_flush_pending_frames(sk);
@@ -903,7 +815,6 @@  back_from_confirm:
 done:
 	dst_release(dst);
 out:
-	fl6_sock_release(flowlabel);
 	return err<0?err:len;
 do_confirm:
 	dst_confirm(dst);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index d8d6ca0..0f7baff 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -39,6 +39,7 @@ 
 
 #include <net/ndisc.h>
 #include <net/protocol.h>
+#include <net/ipv6.h>
 #include <net/transp_v6.h>
 #include <net/ip6_route.h>
 #include <net/raw.h>
@@ -1044,19 +1045,14 @@  int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	struct inet_sock *inet = inet_sk(sk);
 	struct ipv6_pinfo *np = inet6_sk(sk);
 	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
-	struct in6_addr *daddr, *final_p, final;
-	struct ipv6_txoptions *opt = NULL;
-	struct ip6_flowlabel *flowlabel = NULL;
+	struct in6_addr *daddr;
+	struct ip6_output_opts outopts = IP6_OUTOPTS_INIT;
 	struct flowi6 fl6;
-	struct dst_entry *dst;
+	struct dst_entry *dst = NULL;
 	int addr_len = msg->msg_namelen;
 	int ulen = len;
-	int hlimit = -1;
-	int tclass = -1;
-	int dontfrag = -1;
 	int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
 	int err;
-	int connected = 0;
 	int is_udplite = IS_UDPLITE(sk);
 	int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
 
@@ -1123,7 +1119,7 @@  do_udp_sendmsg:
 				release_sock(sk);
 				return -EAFNOSUPPORT;
 			}
-			dst = NULL;
+			outopts.dontfrag = np->dontfrag;
 			goto do_append_data;
 		}
 		release_sock(sk);
@@ -1131,112 +1127,22 @@  do_udp_sendmsg:
 	ulen += sizeof(struct udphdr);
 
 	memset(&fl6, 0, sizeof(fl6));
+	fl6.flowi6_proto = sk->sk_protocol;
 
 	if (sin6) {
 		if (sin6->sin6_port == 0)
 			return -EINVAL;
 
 		fl6.fl6_dport = sin6->sin6_port;
-		daddr = &sin6->sin6_addr;
-
-		if (np->sndflow) {
-			fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
-			if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
-				flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-				if (flowlabel == NULL)
-					return -EINVAL;
-			}
-		}
-
-		/*
-		 * Otherwise it will be difficult to maintain
-		 * sk->sk_dst_cache.
-		 */
-		if (sk->sk_state == TCP_ESTABLISHED &&
-		    ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
-			daddr = &sk->sk_v6_daddr;
-
-		if (addr_len >= sizeof(struct sockaddr_in6) &&
-		    sin6->sin6_scope_id &&
-		    __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr)))
-			fl6.flowi6_oif = sin6->sin6_scope_id;
 	} else {
-		if (sk->sk_state != TCP_ESTABLISHED)
-			return -EDESTADDRREQ;
-
 		fl6.fl6_dport = inet->inet_dport;
-		daddr = &sk->sk_v6_daddr;
-		fl6.flowlabel = np->flow_label;
-		connected = 1;
-	}
-
-	if (!fl6.flowi6_oif)
-		fl6.flowi6_oif = sk->sk_bound_dev_if;
-
-	if (!fl6.flowi6_oif)
-		fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
-
-	fl6.flowi6_mark = sk->sk_mark;
-
-	if (msg->msg_controllen) {
-		opt = &opt_space;
-		memset(opt, 0, sizeof(struct ipv6_txoptions));
-		opt->tot_len = sizeof(*opt);
-
-		err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
-					    &hlimit, &tclass, &dontfrag);
-		if (err < 0) {
-			fl6_sock_release(flowlabel);
-			return err;
-		}
-		if ((fl6.flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
-			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-			if (flowlabel == NULL)
-				return -EINVAL;
-		}
-		if (!(opt->opt_nflen|opt->opt_flen))
-			opt = NULL;
-		connected = 0;
 	}
-	if (opt == NULL)
-		opt = np->opt;
-	if (flowlabel)
-		opt = fl6_merge_options(&opt_space, flowlabel, opt);
-	opt = ipv6_fixup_options(&opt_space, opt);
-
-	fl6.flowi6_proto = sk->sk_protocol;
-	if (!ipv6_addr_any(daddr))
-		fl6.daddr = *daddr;
-	else
-		fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */
-	if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr))
-		fl6.saddr = np->saddr;
 	fl6.fl6_sport = inet->inet_sport;
 
-	final_p = fl6_update_dst(&fl6, opt, &final);
-	if (final_p)
-		connected = 0;
-
-	if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) {
-		fl6.flowi6_oif = np->mcast_oif;
-		connected = 0;
-	} else if (!fl6.flowi6_oif)
-		fl6.flowi6_oif = np->ucast_oif;
-
-	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
-
-	dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p);
-	if (IS_ERR(dst)) {
-		err = PTR_ERR(dst);
-		dst = NULL;
+	err = ip6_datagram_send_common(sk, msg, &fl6, &dst,
+				       &outopts, &opt_space);
+	if (err)
 		goto out;
-	}
-
-	if (hlimit < 0)
-		hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
-
-	if (tclass < 0)
-		tclass = np->tclass;
 
 	if (msg->msg_flags&MSG_CONFIRM)
 		goto do_confirm;
@@ -1256,14 +1162,11 @@  back_from_confirm:
 	up->pending = AF_INET6;
 
 do_append_data:
-	if (dontfrag < 0)
-		dontfrag = np->dontfrag;
 	up->len += ulen;
 	getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;
 	err = ip6_append_data(sk, getfrag, msg->msg_iov, ulen,
-		sizeof(struct udphdr), hlimit, tclass, opt, &fl6,
-		(struct rt6_info*)dst,
-		corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags, dontfrag);
+		sizeof(struct udphdr), &fl6, (struct rt6_info*)dst, &outopts,
+		corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
 	if (err)
 		udp_v6_flush_pending_frames(sk);
 	else if (!corkreq)
@@ -1272,7 +1175,7 @@  do_append_data:
 		up->pending = 0;
 
 	if (dst) {
-		if (connected) {
+		if (outopts.connected) {
 			ip6_dst_store(sk, dst,
 				      ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ?
 				      &sk->sk_v6_daddr : NULL,
@@ -1292,7 +1195,6 @@  do_append_data:
 	release_sock(sk);
 out:
 	dst_release(dst);
-	fl6_sock_release(flowlabel);
 	if (!err)
 		return len;
 	/*
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index e472d44..fe4d931 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -485,16 +485,10 @@  static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
 {
 	struct ipv6_txoptions opt_space;
 	DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name);
-	struct in6_addr *daddr, *final_p, final;
-	struct ipv6_pinfo *np = inet6_sk(sk);
-	struct ipv6_txoptions *opt = NULL;
-	struct ip6_flowlabel *flowlabel = NULL;
+	struct ip6_output_opts outopts = IP6_OUTOPTS_INIT;
 	struct dst_entry *dst = NULL;
 	struct flowi6 fl6;
 	int addr_len = msg->msg_namelen;
-	int hlimit = -1;
-	int tclass = -1;
-	int dontfrag = -1;
 	int transhdrlen = 4; /* zero session-id */
 	int ulen = len + transhdrlen;
 	int err;
@@ -514,7 +508,7 @@  static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
 	 */
 	memset(&fl6, 0, sizeof(fl6));
 
-	fl6.flowi6_mark = sk->sk_mark;
+	fl6.flowi6_proto = sk->sk_protocol;
 
 	if (lsa) {
 		if (addr_len < SIN6_LEN_RFC2133)
@@ -522,97 +516,12 @@  static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
 
 		if (lsa->l2tp_family && lsa->l2tp_family != AF_INET6)
 			return -EAFNOSUPPORT;
-
-		daddr = &lsa->l2tp_addr;
-		if (np->sndflow) {
-			fl6.flowlabel = lsa->l2tp_flowinfo & IPV6_FLOWINFO_MASK;
-			if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
-				flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-				if (flowlabel == NULL)
-					return -EINVAL;
-			}
-		}
-
-		/*
-		 * Otherwise it will be difficult to maintain
-		 * sk->sk_dst_cache.
-		 */
-		if (sk->sk_state == TCP_ESTABLISHED &&
-		    ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
-			daddr = &sk->sk_v6_daddr;
-
-		if (addr_len >= sizeof(struct sockaddr_in6) &&
-		    lsa->l2tp_scope_id &&
-		    ipv6_addr_type(daddr) & IPV6_ADDR_LINKLOCAL)
-			fl6.flowi6_oif = lsa->l2tp_scope_id;
-	} else {
-		if (sk->sk_state != TCP_ESTABLISHED)
-			return -EDESTADDRREQ;
-
-		daddr = &sk->sk_v6_daddr;
-		fl6.flowlabel = np->flow_label;
 	}
 
-	if (fl6.flowi6_oif == 0)
-		fl6.flowi6_oif = sk->sk_bound_dev_if;
-
-	if (msg->msg_controllen) {
-		opt = &opt_space;
-		memset(opt, 0, sizeof(struct ipv6_txoptions));
-		opt->tot_len = sizeof(struct ipv6_txoptions);
-
-		err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
-					    &hlimit, &tclass, &dontfrag);
-		if (err < 0) {
-			fl6_sock_release(flowlabel);
-			return err;
-		}
-		if ((fl6.flowlabel & IPV6_FLOWLABEL_MASK) && !flowlabel) {
-			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-			if (flowlabel == NULL)
-				return -EINVAL;
-		}
-		if (!(opt->opt_nflen|opt->opt_flen))
-			opt = NULL;
-	}
-
-	if (opt == NULL)
-		opt = np->opt;
-	if (flowlabel)
-		opt = fl6_merge_options(&opt_space, flowlabel, opt);
-	opt = ipv6_fixup_options(&opt_space, opt);
-
-	fl6.flowi6_proto = sk->sk_protocol;
-	if (!ipv6_addr_any(daddr))
-		fl6.daddr = *daddr;
-	else
-		fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */
-	if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr))
-		fl6.saddr = np->saddr;
-
-	final_p = fl6_update_dst(&fl6, opt, &final);
-
-	if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
-		fl6.flowi6_oif = np->mcast_oif;
-	else if (!fl6.flowi6_oif)
-		fl6.flowi6_oif = np->ucast_oif;
-
-	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
-
-	dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
-	if (IS_ERR(dst)) {
-		err = PTR_ERR(dst);
+	err = ip6_datagram_send_common(sk, msg, &fl6, &dst,
+				       &outopts, &opt_space);
+	if (err)
 		goto out;
-	}
-
-	if (hlimit < 0)
-		hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
-
-	if (tclass < 0)
-		tclass = np->tclass;
-
-	if (dontfrag < 0)
-		dontfrag = np->dontfrag;
 
 	if (msg->msg_flags & MSG_CONFIRM)
 		goto do_confirm;
@@ -620,9 +529,8 @@  static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
 back_from_confirm:
 	lock_sock(sk);
 	err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
-			      ulen, transhdrlen, hlimit, tclass, opt,
-			      &fl6, (struct rt6_info *)dst,
-			      msg->msg_flags, dontfrag);
+			      ulen, transhdrlen, &fl6, (struct rt6_info *)dst,
+			      &outopts, msg->msg_flags);
 	if (err)
 		ip6_flush_pending_frames(sk);
 	else if (!(msg->msg_flags & MSG_MORE))
@@ -631,8 +539,6 @@  back_from_confirm:
 done:
 	dst_release(dst);
 out:
-	fl6_sock_release(flowlabel);
-
 	return err < 0 ? err : len;
 
 do_confirm: