[PATCH v4 07/11] net: qrtr: Allow sendmsg to target an endpoint

From: Mihai Moldovan
Date: Mon Jul 28 2025 - 12:48:15 EST


From: Denis Kenzior <denkenz@xxxxxxxxx>

Allow QIPCRTR family sockets to include QRTR_ENDPOINT auxiliary data
as part of the sendmsg system call. By including this parameter, the
client can ask the kernel to route the message to a given endpoint, in
situations where multiple endpoints with conflicting node identifier
sets exist in the system.

For legacy clients, or clients that do not include QRTR_ENDPOINT data,
the endpoint is looked up, as before, by only using the node identifier
of the destination qrtr socket address.

Signed-off-by: Denis Kenzior <denkenz@xxxxxxxxx>
Reviewed-by: Marcel Holtmann <marcel@xxxxxxxxxxxx>
Reviewed-by: Andy Gross <agross@xxxxxxxxxx>
Signed-off-by: Mihai Moldovan <ionic@xxxxxxxx>

---

v4:
- no changes
- Link to v3: https://msgid.link/4b812aeb566819045dfca401bd06656ea612a4ec.1753312999.git.ionic@xxxxxxxx

v3:
- rebase against current master
- port [endpoint ID|node ID] key usage in qrtr_node_lookup() to the generic
solution already established for the [node ID|port number] usage
- Link to v2: https://msgid.link/fc510e5f0bae7e2d2cc5c0349ee7c166840b9154.1752947108.git.ionic@xxxxxxxx

v2:
- rebase against current master
- no action on review comment regarding initializing out_endpoint_id,
since that's rightfully already being done
- Link to v1: https://msgid.link/20241018181842.1368394-7-denkenz@xxxxxxxxx
---
net/qrtr/af_qrtr.c | 89 ++++++++++++++++++++++++++++++++++------------
net/qrtr/qrtr.h | 2 ++
2 files changed, 68 insertions(+), 23 deletions(-)

diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index 8ddaebbd76d2..fa88a8ed4d8c 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -106,6 +106,36 @@ static inline struct qrtr_sock *qrtr_sk(struct sock *sk)
return container_of(sk, struct qrtr_sock, sk);
}

+int qrtr_msg_get_endpoint(struct msghdr *msg, u32 *out_endpoint_id)
+{
+ struct cmsghdr *cmsg;
+ u32 endpoint_id = 0;
+
+ for_each_cmsghdr(cmsg, msg) {
+ if (!CMSG_OK(msg, cmsg))
+ return -EINVAL;
+
+ if (cmsg->cmsg_level != SOL_QRTR)
+ continue;
+
+ if (cmsg->cmsg_type != QRTR_ENDPOINT)
+ return -EINVAL;
+
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
+ return -EINVAL;
+
+ /* Endpoint ids start at 1 */
+ endpoint_id = *(u32 *)CMSG_DATA(cmsg);
+ if (!endpoint_id)
+ return -EINVAL;
+ }
+
+ if (out_endpoint_id)
+ *out_endpoint_id = endpoint_id;
+
+ return 0;
+}
+
static unsigned int qrtr_local_nid = 1;

/* for node ids */
@@ -456,14 +486,23 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
*
* callers must release with qrtr_node_release()
*/
-static struct qrtr_node *qrtr_node_lookup(unsigned int nid)
+static struct qrtr_node *qrtr_node_lookup(unsigned int endpoint_id,
+ unsigned int nid)
{
- struct qrtr_node *node;
+ struct qrtr_node *node = NULL;
unsigned long flags;
+ unsigned long key = 0;
+
+ if (endpoint_id > QRTR_INDEX_HALF_UNSIGNED_MAX ||
+ nid > QRTR_INDEX_HALF_UNSIGNED_MAX)
+ return node;
+
+ key = ((unsigned long)(endpoint_id) << QRTR_INDEX_HALF_BITS) |
+ ((unsigned long)(nid) & QRTR_INDEX_HALF_UNSIGNED_MAX);

mutex_lock(&qrtr_node_lock);
spin_lock_irqsave(&qrtr_nodes_lock, flags);
- node = radix_tree_lookup(&qrtr_nodes, nid);
+ node = radix_tree_lookup(&qrtr_nodes, key);
node = qrtr_node_acquire(node);
spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
mutex_unlock(&qrtr_node_lock);
@@ -1028,6 +1067,7 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
struct qrtr_sock *ipc = qrtr_sk(sock->sk);
struct sock *sk = sock->sk;
struct qrtr_node *node;
+ u32 msg_endpoint_id;
u32 endpoint_id = qrtr_local_nid;
struct sk_buff *skb;
size_t plen;
@@ -1040,46 +1080,48 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
if (len > 65535)
return -EMSGSIZE;

+ rc = qrtr_msg_get_endpoint(msg, &msg_endpoint_id);
+ if (rc < 0)
+ return rc;
+
lock_sock(sk);

if (addr) {
- if (msg->msg_namelen < sizeof(*addr)) {
- release_sock(sk);
- return -EINVAL;
- }
+ rc = -EINVAL;

- if (addr->sq_family != AF_QIPCRTR) {
- release_sock(sk);
- return -EINVAL;
- }
+ if (msg->msg_namelen < sizeof(*addr))
+ goto release_sock;
+
+ if (addr->sq_family != AF_QIPCRTR)
+ goto release_sock;

rc = qrtr_autobind(sock);
- if (rc) {
- release_sock(sk);
- return rc;
- }
+ if (rc)
+ goto release_sock;
} else if (sk->sk_state == TCP_ESTABLISHED) {
addr = &ipc->peer;
} else {
- release_sock(sk);
- return -ENOTCONN;
+ rc = -ENOTCONN;
+ goto release_sock;
}

node = NULL;
if (addr->sq_node == QRTR_NODE_BCAST) {
if (addr->sq_port != QRTR_PORT_CTRL &&
qrtr_local_nid != QRTR_NODE_BCAST) {
- release_sock(sk);
- return -ENOTCONN;
+ rc = -ENOTCONN;
+ goto release_sock;
}
enqueue_fn = qrtr_bcast_enqueue;
} else if (addr->sq_node == ipc->us.sq_node) {
enqueue_fn = qrtr_local_enqueue;
} else {
- node = qrtr_node_lookup(addr->sq_node);
+ endpoint_id = msg_endpoint_id;
+
+ node = qrtr_node_lookup(endpoint_id, addr->sq_node);
if (!node) {
- release_sock(sk);
- return -ECONNRESET;
+ rc = endpoint_id ? -ENXIO : -ECONNRESET;
+ goto release_sock;
}
enqueue_fn = qrtr_node_enqueue;
}
@@ -1118,6 +1160,7 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)

out_node:
qrtr_node_release(node);
+release_sock:
release_sock(sk);

return rc;
@@ -1132,7 +1175,7 @@ static int qrtr_send_resume_tx(struct qrtr_cb *cb)
struct sk_buff *skb;
int ret;

- node = qrtr_node_lookup(remote.sq_node);
+ node = qrtr_node_lookup(cb->endpoint_id, remote.sq_node);
if (!node)
return -EINVAL;

diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h
index 11b897af05e6..22fcecbf8de2 100644
--- a/net/qrtr/qrtr.h
+++ b/net/qrtr/qrtr.h
@@ -34,4 +34,6 @@ int qrtr_ns_init(void);

void qrtr_ns_remove(void);

+int qrtr_msg_get_endpoint(struct msghdr *msg, u32 *out_endpoint_id);
+
#endif
--
2.50.0