]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ipv{4,6}/raw: fix output xfrm lookup wrt protocol
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Mon, 22 May 2023 12:08:20 +0000 (14:08 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 5 Jun 2023 06:17:33 +0000 (08:17 +0200)
commit 3632679d9e4f879f49949bb5b050e0de553e4739 upstream.

With a raw socket bound to IPPROTO_RAW (ie with hdrincl enabled), the
protocol field of the flow structure, build by raw_sendmsg() /
rawv6_sendmsg()),  is set to IPPROTO_RAW. This breaks the ipsec policy
lookup when some policies are defined with a protocol in the selector.

For ipv6, the sin6_port field from 'struct sockaddr_in6' could be used to
specify the protocol. Just accept all values for IPPROTO_RAW socket.

For ipv4, the sin_port field of 'struct sockaddr_in' could not be used
without breaking backward compatibility (the value of this field was never
checked). Let's add a new kind of control message, so that the userland
could specify which protocol is used.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
CC: stable@vger.kernel.org
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Link: https://lore.kernel.org/r/20230522120820.1319391-1-nicolas.dichtel@6wind.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/ip.h
include/uapi/linux/in.h
net/ipv4/ip_sockglue.c
net/ipv4/raw.c
net/ipv6/raw.c

index db841ab388c0e45f3984d3e60bbee58abc442100..0ebe5385efcaeb717277d0df6dcc4001b4c04673 100644 (file)
@@ -73,6 +73,7 @@ struct ipcm_cookie {
        __be32                  addr;
        int                     oif;
        struct ip_options_rcu   *opt;
+       __u8                    protocol;
        __u8                    ttl;
        __s16                   tos;
        char                    priority;
@@ -93,6 +94,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm,
        ipcm->sockc.tsflags = inet->sk.sk_tsflags;
        ipcm->oif = inet->sk.sk_bound_dev_if;
        ipcm->addr = inet->inet_saddr;
+       ipcm->protocol = inet->inet_num;
 }
 
 #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))
index 60e1241d4b77b45bda3dfa669732933fb9f0f698..f5ac290c2250951a6b3a6e6118a1f68c8ab98734 100644 (file)
@@ -154,6 +154,8 @@ struct in_addr {
 #define MCAST_MSFILTER                 48
 #define IP_MULTICAST_ALL               49
 #define IP_UNICAST_IF                  50
+#define IP_LOCAL_PORT_RANGE            51
+#define IP_PROTOCOL                    52
 
 #define MCAST_EXCLUDE  0
 #define MCAST_INCLUDE  1
index aa3fd61818c478602f0e72d20dd7a688f8900d83..0c528f642eb865014a0cfbd7e970bde6433d00b1 100644 (file)
@@ -316,7 +316,14 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
                        ipc->tos = val;
                        ipc->priority = rt_tos2priority(ipc->tos);
                        break;
-
+               case IP_PROTOCOL:
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
+                               return -EINVAL;
+                       val = *(int *)CMSG_DATA(cmsg);
+                       if (val < 1 || val > 255)
+                               return -EINVAL;
+                       ipc->protocol = val;
+                       break;
                default:
                        return -EINVAL;
                }
@@ -1524,6 +1531,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        case IP_MINTTL:
                val = inet->min_ttl;
                break;
+       case IP_PROTOCOL:
+               val = inet_sk(sk)->inet_num;
+               break;
        default:
                release_sock(sk);
                return -ENOPROTOOPT;
index ddc24e57dc555f3d440b205d0a0d1532f73f8df6..1044f4985922f252b0b4bade8b1040eb8572a777 100644 (file)
@@ -558,6 +558,9 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        }
 
        ipcm_init_sk(&ipc, inet);
+       /* Keep backward compat */
+       if (hdrincl)
+               ipc.protocol = IPPROTO_RAW;
 
        if (msg->msg_controllen) {
                err = ip_cmsg_send(sk, msg, &ipc, false);
@@ -625,7 +628,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 
        flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos,
                           RT_SCOPE_UNIVERSE,
-                          hdrincl ? IPPROTO_RAW : sk->sk_protocol,
+                          hdrincl ? ipc.protocol : sk->sk_protocol,
                           inet_sk_flowi_flags(sk) |
                            (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
                           daddr, saddr, 0, 0, sk->sk_uid);
index bf052d68e1f5a39b40a8b420184832144e029b8b..731485b18de3175fb1012736b779515e0dd30fc0 100644 (file)
@@ -828,7 +828,8 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 
                if (!proto)
                        proto = inet->inet_num;
-               else if (proto != inet->inet_num)
+               else if (proto != inet->inet_num &&
+                        inet->inet_num != IPPROTO_RAW)
                        return -EINVAL;
 
                if (proto > 255)