llc: Check netns in llc_estab_match() and llc_listener_match().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Tue, 18 Jul 2023 17:41:50 +0000 (10:41 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 20 Jul 2023 08:46:28 +0000 (10:46 +0200)
We will remove this restriction in llc_rcv() in the following patch,
which means that the protocol handler must be aware of netns.

        if (!net_eq(dev_net(dev), &init_net))
                goto drop;

llc_rcv() fetches llc_type_handlers[llc_pdu_type(skb) - 1] and calls it
if not NULL.

If the PDU type is LLC_DEST_CONN, llc_conn_handler() is called to pass
skb to corresponding sockets.  Then, we must look up a proper socket in
the same netns with skb->dev.

llc_conn_handler() calls __llc_lookup() to look up a established or
litening socket by __llc_lookup_established() and llc_lookup_listener().

Both functions iterate on a list and call llc_estab_match() or
llc_listener_match() to check if the socket is the correct destination.
However, these functions do not check netns.

Also, bind() and connect() call llc_establish_connection(), which
finally calls __llc_lookup_established(), to check if there is a
conflicting socket.

Let's test netns in llc_estab_match() and llc_listener_match().

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/llc_conn.h
net/llc/af_llc.c
net/llc/llc_conn.c
net/llc/llc_if.c

index 2c1ea3414640523a3efc20ad626ae9896a418d1b..374411b3066ca52e7391c80493dfc6c6be9d73da 100644 (file)
@@ -111,7 +111,7 @@ void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit);
 void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit);
 int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr, u16 *how_many_unacked);
 struct sock *llc_lookup_established(struct llc_sap *sap, struct llc_addr *daddr,
-                                   struct llc_addr *laddr);
+                                   struct llc_addr *laddr, const struct net *net);
 void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk);
 void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk);
 
index 57c35c960b2c795ebefcf8e3e3e6458177edf298..9b06c380866b53bcb395bf255587279db025d11d 100644 (file)
@@ -402,7 +402,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
                memcpy(laddr.mac, addr->sllc_mac, IFHWADDRLEN);
                laddr.lsap = addr->sllc_sap;
                rc = -EADDRINUSE; /* mac + sap clash. */
-               ask = llc_lookup_established(sap, &daddr, &laddr);
+               ask = llc_lookup_established(sap, &daddr, &laddr, &init_net);
                if (ask) {
                        sock_put(ask);
                        goto out_put;
index 912aa9bd5e29a04e296e020a9776082cbbdadf0a..d037009ee10f36a6aa8681f1e5bf752c4ebdcc69 100644 (file)
@@ -453,11 +453,13 @@ static int llc_exec_conn_trans_actions(struct sock *sk,
 static inline bool llc_estab_match(const struct llc_sap *sap,
                                   const struct llc_addr *daddr,
                                   const struct llc_addr *laddr,
-                                  const struct sock *sk)
+                                  const struct sock *sk,
+                                  const struct net *net)
 {
        struct llc_sock *llc = llc_sk(sk);
 
-       return llc->laddr.lsap == laddr->lsap &&
+       return net_eq(sock_net(sk), net) &&
+               llc->laddr.lsap == laddr->lsap &&
                llc->daddr.lsap == daddr->lsap &&
                ether_addr_equal(llc->laddr.mac, laddr->mac) &&
                ether_addr_equal(llc->daddr.mac, daddr->mac);
@@ -468,6 +470,7 @@ static inline bool llc_estab_match(const struct llc_sap *sap,
  *     @sap: SAP
  *     @daddr: address of remote LLC (MAC + SAP)
  *     @laddr: address of local LLC (MAC + SAP)
+ *     @net: netns to look up a socket in
  *
  *     Search connection list of the SAP and finds connection using the remote
  *     mac, remote sap, local mac, and local sap. Returns pointer for
@@ -476,7 +479,8 @@ static inline bool llc_estab_match(const struct llc_sap *sap,
  */
 static struct sock *__llc_lookup_established(struct llc_sap *sap,
                                             struct llc_addr *daddr,
-                                            struct llc_addr *laddr)
+                                            struct llc_addr *laddr,
+                                            const struct net *net)
 {
        struct sock *rc;
        struct hlist_nulls_node *node;
@@ -486,12 +490,12 @@ static struct sock *__llc_lookup_established(struct llc_sap *sap,
        rcu_read_lock();
 again:
        sk_nulls_for_each_rcu(rc, node, laddr_hb) {
-               if (llc_estab_match(sap, daddr, laddr, rc)) {
+               if (llc_estab_match(sap, daddr, laddr, rc, net)) {
                        /* Extra checks required by SLAB_TYPESAFE_BY_RCU */
                        if (unlikely(!refcount_inc_not_zero(&rc->sk_refcnt)))
                                goto again;
                        if (unlikely(llc_sk(rc)->sap != sap ||
-                                    !llc_estab_match(sap, daddr, laddr, rc))) {
+                                    !llc_estab_match(sap, daddr, laddr, rc, net))) {
                                sock_put(rc);
                                continue;
                        }
@@ -513,29 +517,33 @@ found:
 
 struct sock *llc_lookup_established(struct llc_sap *sap,
                                    struct llc_addr *daddr,
-                                   struct llc_addr *laddr)
+                                   struct llc_addr *laddr,
+                                   const struct net *net)
 {
        struct sock *sk;
 
        local_bh_disable();
-       sk = __llc_lookup_established(sap, daddr, laddr);
+       sk = __llc_lookup_established(sap, daddr, laddr, net);
        local_bh_enable();
        return sk;
 }
 
 static inline bool llc_listener_match(const struct llc_sap *sap,
                                      const struct llc_addr *laddr,
-                                     const struct sock *sk)
+                                     const struct sock *sk,
+                                     const struct net *net)
 {
        struct llc_sock *llc = llc_sk(sk);
 
-       return sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN &&
+       return net_eq(sock_net(sk), net) &&
+               sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN &&
                llc->laddr.lsap == laddr->lsap &&
                ether_addr_equal(llc->laddr.mac, laddr->mac);
 }
 
 static struct sock *__llc_lookup_listener(struct llc_sap *sap,
-                                         struct llc_addr *laddr)
+                                         struct llc_addr *laddr,
+                                         const struct net *net)
 {
        struct sock *rc;
        struct hlist_nulls_node *node;
@@ -545,12 +553,12 @@ static struct sock *__llc_lookup_listener(struct llc_sap *sap,
        rcu_read_lock();
 again:
        sk_nulls_for_each_rcu(rc, node, laddr_hb) {
-               if (llc_listener_match(sap, laddr, rc)) {
+               if (llc_listener_match(sap, laddr, rc, net)) {
                        /* Extra checks required by SLAB_TYPESAFE_BY_RCU */
                        if (unlikely(!refcount_inc_not_zero(&rc->sk_refcnt)))
                                goto again;
                        if (unlikely(llc_sk(rc)->sap != sap ||
-                                    !llc_listener_match(sap, laddr, rc))) {
+                                    !llc_listener_match(sap, laddr, rc, net))) {
                                sock_put(rc);
                                continue;
                        }
@@ -574,6 +582,7 @@ found:
  *     llc_lookup_listener - Finds listener for local MAC + SAP
  *     @sap: SAP
  *     @laddr: address of local LLC (MAC + SAP)
+ *     @net: netns to look up a socket in
  *
  *     Search connection list of the SAP and finds connection listening on
  *     local mac, and local sap. Returns pointer for parent socket found,
@@ -581,24 +590,26 @@ found:
  *     Caller has to make sure local_bh is disabled.
  */
 static struct sock *llc_lookup_listener(struct llc_sap *sap,
-                                       struct llc_addr *laddr)
+                                       struct llc_addr *laddr,
+                                       const struct net *net)
 {
+       struct sock *rc = __llc_lookup_listener(sap, laddr, net);
        static struct llc_addr null_addr;
-       struct sock *rc = __llc_lookup_listener(sap, laddr);
 
        if (!rc)
-               rc = __llc_lookup_listener(sap, &null_addr);
+               rc = __llc_lookup_listener(sap, &null_addr, net);
 
        return rc;
 }
 
 static struct sock *__llc_lookup(struct llc_sap *sap,
                                 struct llc_addr *daddr,
-                                struct llc_addr *laddr)
+                                struct llc_addr *laddr,
+                                const struct net *net)
 {
-       struct sock *sk = __llc_lookup_established(sap, daddr, laddr);
+       struct sock *sk = __llc_lookup_established(sap, daddr, laddr, net);
 
-       return sk ? : llc_lookup_listener(sap, laddr);
+       return sk ? : llc_lookup_listener(sap, laddr, net);
 }
 
 /**
@@ -776,7 +787,7 @@ void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb)
        llc_pdu_decode_da(skb, daddr.mac);
        llc_pdu_decode_dsap(skb, &daddr.lsap);
 
-       sk = __llc_lookup(sap, &saddr, &daddr);
+       sk = __llc_lookup(sap, &saddr, &daddr, dev_net(skb->dev));
        if (!sk)
                goto drop;
 
index dde9bf08a593efa7d4882ce509e47873d109408b..58a5f419adc6b8906b3d47ce708c557dadf0c9ff 100644 (file)
@@ -92,7 +92,7 @@ int llc_establish_connection(struct sock *sk, const u8 *lmac, u8 *dmac, u8 dsap)
        daddr.lsap = dsap;
        memcpy(daddr.mac, dmac, sizeof(daddr.mac));
        memcpy(laddr.mac, lmac, sizeof(laddr.mac));
-       existing = llc_lookup_established(llc->sap, &daddr, &laddr);
+       existing = llc_lookup_established(llc->sap, &daddr, &laddr, sock_net(sk));
        if (existing) {
                if (existing->sk_state == TCP_ESTABLISHED) {
                        sk = existing;