diff mbox series

[net-next] l2tp: centralise parsing of sockaddr_pppol2tp*

Message ID fa923854b6bdb86990cc26c8e90e14a2dc906559.1524670082.git.g.nault@alphalink.fr
State Deferred, archived
Delegated to: David Miller
Headers show
Series [net-next] l2tp: centralise parsing of sockaddr_pppol2tp* | expand

Commit Message

Guillaume Nault April 25, 2018, 3:50 p.m. UTC
'sockaddr_len' is checked against various sockaddr length when entering
pppol2tp_connect(), in order to ensure that is has a valid size. It is
also used again later, to find out which sockaddr structure was passed
from user space. This patch combines these two operations into one new
function in order to simplify pppol2tp_connect().

A new structure, l2tp_connect_info, is used to pass sockaddr data back
to pppol2tp_connect(), to avoid passing too many parameters to
l2tp_sockaddr_get_info(). Also, the first parameter is void* in order
to avoid casting between all sockaddr_* structures manually.

Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
---
 net/l2tp/l2tp_ppp.c | 169 ++++++++++++++++++++++++++------------------
 1 file changed, 101 insertions(+), 68 deletions(-)

This patch originates from an attempt to remove the
'/* bad socket address */' else clause that became obsolete with
eb1c28c05894 ("l2tp: check sockaddr length in pppol2tp_connect()").
Doing so made gcc warn about unused variables as it didn't realise that
invalid values of sockaddr_len were already filtered out at the
beginning of the function.

@DaveM, I have some know bugs in pppol2tp_connect() that I'm going to
work on soon. That's probably going to create some conflicts between
net and net-next. They should be easy to resolve, but if you prefer, I
can resend this patch later, after all known issues get fixed.

Comments

David Miller April 27, 2018, 3:03 p.m. UTC | #1
From: Guillaume Nault <g.nault@alphalink.fr>
Date: Wed, 25 Apr 2018 17:50:31 +0200

> @DaveM, I have some know bugs in pppol2tp_connect() that I'm going to
> work on soon. That's probably going to create some conflicts between
> net and net-next. They should be easy to resolve, but if you prefer, I
> can resend this patch later, after all known issues get fixed.

Let's fix the bugs in net first, then do this cleanup on top after those
fixes make it into net-next.

Thanks!
diff mbox series

Patch

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 1fd9e145076a..41a25d009574 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -596,38 +596,111 @@  static void pppol2tp_session_init(struct l2tp_session *session)
 	}
 }
 
+struct l2tp_connect_info {
+	u8 version;
+	int fd;
+	u32 tunnel_id;
+	u32 peer_tunnel_id;
+	u32 session_id;
+	u32 peer_session_id;
+};
+
+static int l2tp_sockaddr_get_info(const void *sa, int sa_len,
+				  struct l2tp_connect_info *info)
+{
+	switch (sa_len) {
+	case sizeof(struct sockaddr_pppol2tp):
+	{
+		const struct sockaddr_pppol2tp *sa_v2in4 = sa;
+
+		if (sa_v2in4->sa_protocol != PX_PROTO_OL2TP)
+			return -EINVAL;
+
+		info->version = 2;
+		info->fd = sa_v2in4->pppol2tp.fd;
+		info->tunnel_id = sa_v2in4->pppol2tp.s_tunnel;
+		info->peer_tunnel_id = sa_v2in4->pppol2tp.d_tunnel;
+		info->session_id = sa_v2in4->pppol2tp.s_session;
+		info->peer_session_id = sa_v2in4->pppol2tp.d_session;
+
+		break;
+	}
+	case sizeof(struct sockaddr_pppol2tpv3):
+	{
+		const struct sockaddr_pppol2tpv3 *sa_v3in4 = sa;
+
+		if (sa_v3in4->sa_protocol != PX_PROTO_OL2TP)
+			return -EINVAL;
+
+		info->version = 3;
+		info->fd = sa_v3in4->pppol2tp.fd;
+		info->tunnel_id = sa_v3in4->pppol2tp.s_tunnel;
+		info->peer_tunnel_id = sa_v3in4->pppol2tp.d_tunnel;
+		info->session_id = sa_v3in4->pppol2tp.s_session;
+		info->peer_session_id = sa_v3in4->pppol2tp.d_session;
+
+		break;
+	}
+	case sizeof(struct sockaddr_pppol2tpin6):
+	{
+		const struct sockaddr_pppol2tpin6 *sa_v2in6 = sa;
+
+		if (sa_v2in6->sa_protocol != PX_PROTO_OL2TP)
+			return -EINVAL;
+
+		info->version = 2;
+		info->fd = sa_v2in6->pppol2tp.fd;
+		info->tunnel_id = sa_v2in6->pppol2tp.s_tunnel;
+		info->peer_tunnel_id = sa_v2in6->pppol2tp.d_tunnel;
+		info->session_id = sa_v2in6->pppol2tp.s_session;
+		info->peer_session_id = sa_v2in6->pppol2tp.d_session;
+
+		break;
+	}
+	case sizeof(struct sockaddr_pppol2tpv3in6):
+	{
+		const struct sockaddr_pppol2tpv3in6 *sa_v3in6 = sa;
+
+		if (sa_v3in6->sa_protocol != PX_PROTO_OL2TP)
+			return -EINVAL;
+
+		info->version = 3;
+		info->fd = sa_v3in6->pppol2tp.fd;
+		info->tunnel_id = sa_v3in6->pppol2tp.s_tunnel;
+		info->peer_tunnel_id = sa_v3in6->pppol2tp.d_tunnel;
+		info->session_id = sa_v3in6->pppol2tp.s_session;
+		info->peer_session_id = sa_v3in6->pppol2tp.d_session;
+
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /* connect() handler. Attach a PPPoX socket to a tunnel UDP socket
  */
 static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 			    int sockaddr_len, int flags)
 {
 	struct sock *sk = sock->sk;
-	struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr;
 	struct pppox_sock *po = pppox_sk(sk);
 	struct l2tp_session *session = NULL;
+	struct l2tp_connect_info info;
 	struct l2tp_tunnel *tunnel;
 	struct pppol2tp_session *ps;
 	struct l2tp_session_cfg cfg = { 0, };
 	int error = 0;
-	u32 tunnel_id, peer_tunnel_id;
-	u32 session_id, peer_session_id;
 	bool drop_refcnt = false;
 	bool drop_tunnel = false;
-	int ver = 2;
-	int fd;
-
-	lock_sock(sk);
-
-	error = -EINVAL;
 
-	if (sockaddr_len != sizeof(struct sockaddr_pppol2tp) &&
-	    sockaddr_len != sizeof(struct sockaddr_pppol2tpv3) &&
-	    sockaddr_len != sizeof(struct sockaddr_pppol2tpin6) &&
-	    sockaddr_len != sizeof(struct sockaddr_pppol2tpv3in6))
-		goto end;
+	error = l2tp_sockaddr_get_info(uservaddr, sockaddr_len, &info);
+	if (error < 0)
+		return error;
 
-	if (sp->sa_protocol != PX_PROTO_OL2TP)
-		goto end;
+	lock_sock(sk);
 
 	/* Check for already bound sockets */
 	error = -EBUSY;
@@ -639,56 +712,12 @@  static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 	if (sk->sk_user_data)
 		goto end; /* socket is already attached */
 
-	/* Get params from socket address. Handle L2TPv2 and L2TPv3.
-	 * This is nasty because there are different sockaddr_pppol2tp
-	 * structs for L2TPv2, L2TPv3, over IPv4 and IPv6. We use
-	 * the sockaddr size to determine which structure the caller
-	 * is using.
-	 */
-	peer_tunnel_id = 0;
-	if (sockaddr_len == sizeof(struct sockaddr_pppol2tp)) {
-		fd = sp->pppol2tp.fd;
-		tunnel_id = sp->pppol2tp.s_tunnel;
-		peer_tunnel_id = sp->pppol2tp.d_tunnel;
-		session_id = sp->pppol2tp.s_session;
-		peer_session_id = sp->pppol2tp.d_session;
-	} else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpv3)) {
-		struct sockaddr_pppol2tpv3 *sp3 =
-			(struct sockaddr_pppol2tpv3 *) sp;
-		ver = 3;
-		fd = sp3->pppol2tp.fd;
-		tunnel_id = sp3->pppol2tp.s_tunnel;
-		peer_tunnel_id = sp3->pppol2tp.d_tunnel;
-		session_id = sp3->pppol2tp.s_session;
-		peer_session_id = sp3->pppol2tp.d_session;
-	} else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpin6)) {
-		struct sockaddr_pppol2tpin6 *sp6 =
-			(struct sockaddr_pppol2tpin6 *) sp;
-		fd = sp6->pppol2tp.fd;
-		tunnel_id = sp6->pppol2tp.s_tunnel;
-		peer_tunnel_id = sp6->pppol2tp.d_tunnel;
-		session_id = sp6->pppol2tp.s_session;
-		peer_session_id = sp6->pppol2tp.d_session;
-	} else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpv3in6)) {
-		struct sockaddr_pppol2tpv3in6 *sp6 =
-			(struct sockaddr_pppol2tpv3in6 *) sp;
-		ver = 3;
-		fd = sp6->pppol2tp.fd;
-		tunnel_id = sp6->pppol2tp.s_tunnel;
-		peer_tunnel_id = sp6->pppol2tp.d_tunnel;
-		session_id = sp6->pppol2tp.s_session;
-		peer_session_id = sp6->pppol2tp.d_session;
-	} else {
-		error = -EINVAL;
-		goto end; /* bad socket address */
-	}
-
 	/* Don't bind if tunnel_id is 0 */
 	error = -EINVAL;
-	if (tunnel_id == 0)
+	if (!info.tunnel_id)
 		goto end;
 
-	tunnel = l2tp_tunnel_get(sock_net(sk), tunnel_id);
+	tunnel = l2tp_tunnel_get(sock_net(sk), info.tunnel_id);
 	if (tunnel)
 		drop_tunnel = true;
 
@@ -696,13 +725,17 @@  static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 	 * peer_session_id is 0. Otherwise look up tunnel using supplied
 	 * tunnel id.
 	 */
-	if ((session_id == 0) && (peer_session_id == 0)) {
+	if (!info.session_id && !info.peer_session_id) {
 		if (tunnel == NULL) {
 			struct l2tp_tunnel_cfg tcfg = {
 				.encap = L2TP_ENCAPTYPE_UDP,
 				.debug = 0,
 			};
-			error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, &tcfg, &tunnel);
+			error = l2tp_tunnel_create(sock_net(sk), info.fd,
+						   info.version,
+						   info.tunnel_id,
+						   info.peer_tunnel_id, &tcfg,
+						   &tunnel);
 			if (error < 0)
 				goto end;
 
@@ -730,9 +763,9 @@  static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 		tunnel->recv_payload_hook = pppol2tp_recv_payload_hook;
 
 	if (tunnel->peer_tunnel_id == 0)
-		tunnel->peer_tunnel_id = peer_tunnel_id;
+		tunnel->peer_tunnel_id = info.peer_tunnel_id;
 
-	session = l2tp_session_get(sock_net(sk), tunnel, session_id);
+	session = l2tp_session_get(sock_net(sk), tunnel, info.session_id);
 	if (session) {
 		drop_refcnt = true;
 		ps = l2tp_session_priv(session);
@@ -753,8 +786,8 @@  static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 		cfg.mru = cfg.mtu;
 
 		session = l2tp_session_create(sizeof(struct pppol2tp_session),
-					      tunnel, session_id,
-					      peer_session_id, &cfg);
+					      tunnel, info.session_id,
+					      info.peer_session_id, &cfg);
 		if (IS_ERR(session)) {
 			error = PTR_ERR(session);
 			goto end;