123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- /* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/net.h>
- #include <linux/socket.h>
- #include <linux/errno.h>
- #include <linux/mm.h>
- #include <linux/poll.h>
- #include <linux/fcntl.h>
- #include <linux/gfp.h>
- #include <linux/msm_ipc.h>
- #include <linux/sched.h>
- #include <linux/thread_info.h>
- #include <linux/slab.h>
- #include <linux/kmemleak.h>
- #include <linux/ipc_logging.h>
- #include <linux/string.h>
- #include <linux/atomic.h>
- #include <linux/ipc_router.h>
- #include <net/sock.h>
- #include "ipc_router_private.h"
- #include "ipc_router_security.h"
- #define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
- #define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
- #ifndef SIZE_MAX
- #define SIZE_MAX ((size_t)-1)
- #endif
- static int sockets_enabled;
- static struct proto msm_ipc_proto;
- static const struct proto_ops msm_ipc_proto_ops;
- static RAW_NOTIFIER_HEAD(ipcrtr_af_init_chain);
- static DEFINE_MUTEX(ipcrtr_af_init_lock);
- static struct sk_buff_head *msm_ipc_router_build_msg(struct msghdr *m,
- size_t total_len)
- {
- struct sk_buff_head *msg_head;
- struct sk_buff *msg;
- int first = 1;
- int last = 1;
- size_t data_size = 0;
- size_t alloc_size, align_size;
- void *data;
- size_t total_copied_size = 0, copied_size;
- if (iov_iter_count(&m->msg_iter) == total_len)
- data_size = total_len;
- if (!data_size)
- return NULL;
- align_size = ALIGN_SIZE(data_size);
- msg_head = kmalloc(sizeof(*msg_head), GFP_KERNEL);
- if (!msg_head) {
- IPC_RTR_ERR("%s: cannot allocate skb_head\n", __func__);
- return NULL;
- }
- skb_queue_head_init(msg_head);
- while (total_copied_size < total_len) {
- alloc_size = data_size;
- if (first)
- alloc_size += IPC_ROUTER_HDR_SIZE;
- if (last)
- alloc_size += align_size;
- msg = alloc_skb(alloc_size, GFP_KERNEL);
- if (!msg) {
- if (alloc_size <= (PAGE_SIZE / 2)) {
- IPC_RTR_ERR("%s: cannot allocated skb\n",
- __func__);
- goto msg_build_failure;
- }
- data_size = data_size / 2;
- last = 0;
- continue;
- }
- if (first) {
- skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
- first = 0;
- }
- data = skb_put(msg, data_size);
- copied_size = copy_from_iter(msg->data, data_size,
- &m->msg_iter);
- if (copied_size != data_size) {
- IPC_RTR_ERR("%s: copy_from_iter failed %zu %zu %zu\n",
- __func__, alloc_size, data_size,
- copied_size);
- kfree_skb(msg);
- goto msg_build_failure;
- }
- skb_queue_tail(msg_head, msg);
- total_copied_size += data_size;
- data_size = total_len - total_copied_size;
- last = 1;
- }
- return msg_head;
- msg_build_failure:
- while (!skb_queue_empty(msg_head)) {
- msg = skb_dequeue(msg_head);
- kfree_skb(msg);
- }
- kfree(msg_head);
- return NULL;
- }
- static int msm_ipc_router_extract_msg(struct msghdr *m,
- struct rr_packet *pkt)
- {
- struct sockaddr_msm_ipc *addr;
- struct rr_header_v1 *hdr;
- struct sk_buff *temp;
- union rr_control_msg *ctl_msg;
- int offset = 0, data_len = 0, copy_len, copied_len;
- if (!m || !pkt) {
- IPC_RTR_ERR("%s: Invalid pointers passed\n", __func__);
- return -EINVAL;
- }
- addr = (struct sockaddr_msm_ipc *)m->msg_name;
- hdr = &pkt->hdr;
- if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
- temp = skb_peek(pkt->pkt_fragment_q);
- if (!temp || !temp->data) {
- IPC_RTR_ERR("%s: Invalid skb\n", __func__);
- return -EINVAL;
- }
- ctl_msg = (union rr_control_msg *)(temp->data);
- memset(addr, 0x0, sizeof(*addr));
- addr->family = AF_MSM_IPC;
- addr->address.addrtype = MSM_IPC_ADDR_ID;
- addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id;
- addr->address.addr.port_addr.port_id = ctl_msg->cli.port_id;
- m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
- return offset;
- }
- if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_DATA)) {
- memset(addr, 0x0, sizeof(*addr));
- addr->family = AF_MSM_IPC;
- addr->address.addrtype = MSM_IPC_ADDR_ID;
- addr->address.addr.port_addr.node_id = hdr->src_node_id;
- addr->address.addr.port_addr.port_id = hdr->src_port_id;
- m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
- }
- data_len = hdr->size;
- skb_queue_walk(pkt->pkt_fragment_q, temp) {
- copy_len = data_len < temp->len ? data_len : temp->len;
- copied_len = copy_to_iter(temp->data, copy_len, &m->msg_iter);
- if (copy_len != copied_len) {
- IPC_RTR_ERR("%s: Copy to user failed\n", __func__);
- return -EFAULT;
- }
- offset += copy_len;
- data_len -= copy_len;
- }
- return offset;
- }
- static int msm_ipc_router_create(struct net *net,
- struct socket *sock,
- int protocol,
- int kern)
- {
- struct sock *sk;
- struct msm_ipc_port *port_ptr;
- if (unlikely(protocol != 0)) {
- IPC_RTR_ERR("%s: Protocol not supported\n", __func__);
- return -EPROTONOSUPPORT;
- }
- switch (sock->type) {
- case SOCK_DGRAM:
- break;
- default:
- IPC_RTR_ERR("%s: Protocol type not supported\n", __func__);
- return -EPROTOTYPE;
- }
- sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto, kern);
- if (!sk) {
- IPC_RTR_ERR("%s: sk_alloc failed\n", __func__);
- return -ENOMEM;
- }
- sock->ops = &msm_ipc_proto_ops;
- sock_init_data(sock, sk);
- sk->sk_data_ready = NULL;
- sk->sk_write_space = ipc_router_dummy_write_space;
- sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
- sk->sk_sndtimeo = DEFAULT_SND_TIMEO;
- port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
- if (!port_ptr) {
- IPC_RTR_ERR("%s: port_ptr alloc failed\n", __func__);
- sock_put(sk);
- sock->sk = NULL;
- return -ENOMEM;
- }
- port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
- msm_ipc_sk(sk)->port = port_ptr;
- msm_ipc_sk(sk)->default_node_vote_info = NULL;
- return 0;
- }
- int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
- int uaddr_len)
- {
- struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr;
- int ret;
- if (!sk)
- return -EINVAL;
- if (!check_permissions()) {
- IPC_RTR_ERR("%s: %s Do not have permissions\n",
- __func__, current->comm);
- return -EPERM;
- }
- if (!uaddr_len) {
- IPC_RTR_ERR("%s: Invalid address length\n", __func__);
- return -EINVAL;
- }
- if (addr->family != AF_MSM_IPC) {
- IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
- return -EAFNOSUPPORT;
- }
- if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
- IPC_RTR_ERR("%s: Address type is incorrect\n", __func__);
- return -EINVAL;
- }
- port_ptr = msm_ipc_sk_port(sk);
- if (!port_ptr)
- return -ENODEV;
- if (!msm_ipc_sk(sk)->default_node_vote_info)
- msm_ipc_sk(sk)->default_node_vote_info =
- msm_ipc_load_default_node();
- lock_sock(sk);
- ret = msm_ipc_router_register_server(port_ptr, &addr->address);
- release_sock(sk);
- return ret;
- }
- static int ipc_router_connect(struct socket *sock, struct sockaddr *uaddr,
- int uaddr_len, int flags)
- {
- struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr;
- int ret;
- if (!sk)
- return -EINVAL;
- if (uaddr_len <= 0) {
- IPC_RTR_ERR("%s: Invalid address length\n", __func__);
- return -EINVAL;
- }
- if (!addr) {
- IPC_RTR_ERR("%s: Invalid address\n", __func__);
- return -EINVAL;
- }
- if (addr->family != AF_MSM_IPC) {
- IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
- return -EAFNOSUPPORT;
- }
- port_ptr = msm_ipc_sk_port(sk);
- if (!port_ptr)
- return -ENODEV;
- lock_sock(sk);
- ret = ipc_router_set_conn(port_ptr, &addr->address);
- release_sock(sk);
- return ret;
- }
- static int msm_ipc_router_sendmsg(struct socket *sock,
- struct msghdr *m, size_t total_len)
- {
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
- struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
- struct sk_buff_head *msg;
- int ret;
- struct msm_ipc_addr dest_addr = {0};
- long timeout;
- if (dest) {
- if (m->msg_namelen < sizeof(*dest) ||
- dest->family != AF_MSM_IPC)
- return -EINVAL;
- memcpy(&dest_addr, &dest->address, sizeof(dest_addr));
- } else {
- if (port_ptr->conn_status == NOT_CONNECTED)
- return -EDESTADDRREQ;
- if (port_ptr->conn_status < CONNECTION_RESET)
- return -ENETRESET;
- memcpy(&dest_addr.addr.port_addr, &port_ptr->dest_addr,
- sizeof(struct msm_ipc_port_addr));
- dest_addr.addrtype = MSM_IPC_ADDR_ID;
- }
- if (total_len > MAX_IPC_PKT_SIZE)
- return -EINVAL;
- lock_sock(sk);
- timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
- msg = msm_ipc_router_build_msg(m, total_len);
- if (!msg) {
- IPC_RTR_ERR("%s: Msg build failure\n", __func__);
- ret = -ENOMEM;
- goto out_sendmsg;
- }
- kmemleak_not_leak(msg);
- if (port_ptr->type == CLIENT_PORT)
- wait_for_irsc_completion();
- ret = msm_ipc_router_send_to(port_ptr, msg, &dest_addr, timeout);
- if (ret != total_len) {
- if (ret < 0) {
- if (ret != -EAGAIN)
- IPC_RTR_ERR("%s: Send_to failure %d\n",
- __func__, ret);
- msm_ipc_router_free_skb(msg);
- } else if (ret >= 0) {
- ret = -EFAULT;
- }
- }
- out_sendmsg:
- release_sock(sk);
- return ret;
- }
- static int msm_ipc_router_recvmsg(struct socket *sock,
- struct msghdr *m, size_t buf_len, int flags)
- {
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
- struct rr_packet *pkt;
- long timeout;
- int ret;
- lock_sock(sk);
- if (!buf_len) {
- if (flags & MSG_PEEK)
- ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
- else
- ret = -EINVAL;
- release_sock(sk);
- return ret;
- }
- timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
- ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
- if (ret) {
- release_sock(sk);
- if (ret == -ENOMSG)
- m->msg_namelen = 0;
- return ret;
- }
- ret = msm_ipc_router_read(port_ptr, &pkt, buf_len);
- if (ret <= 0 || !pkt) {
- release_sock(sk);
- return ret;
- }
- ret = msm_ipc_router_extract_msg(m, pkt);
- release_pkt(pkt);
- release_sock(sk);
- return ret;
- }
- static int msm_ipc_router_ioctl(struct socket *sock,
- unsigned int cmd, unsigned long arg)
- {
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr;
- struct server_lookup_args server_arg;
- struct msm_ipc_server_info *srv_info = NULL;
- unsigned int n;
- size_t srv_info_sz = 0;
- int ret;
- if (!sk)
- return -EINVAL;
- lock_sock(sk);
- port_ptr = msm_ipc_sk_port(sock->sk);
- if (!port_ptr) {
- release_sock(sk);
- return -EINVAL;
- }
- switch (cmd) {
- case IPC_ROUTER_IOCTL_GET_VERSION:
- n = IPC_ROUTER_V1;
- ret = put_user(n, (unsigned int *)arg);
- break;
- case IPC_ROUTER_IOCTL_GET_MTU:
- n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
- ret = put_user(n, (unsigned int *)arg);
- break;
- case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
- ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
- break;
- case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
- if (!msm_ipc_sk(sk)->default_node_vote_info)
- msm_ipc_sk(sk)->default_node_vote_info =
- msm_ipc_load_default_node();
- ret = copy_from_user(&server_arg, (void *)arg,
- sizeof(server_arg));
- if (ret) {
- ret = -EFAULT;
- break;
- }
- if (server_arg.num_entries_in_array < 0) {
- ret = -EINVAL;
- break;
- }
- if (server_arg.num_entries_in_array) {
- if (server_arg.num_entries_in_array >
- (SIZE_MAX / sizeof(*srv_info))) {
- IPC_RTR_ERR("%s: Integer Overflow %zu * %d\n",
- __func__, sizeof(*srv_info),
- server_arg.num_entries_in_array);
- ret = -EINVAL;
- break;
- }
- srv_info_sz = server_arg.num_entries_in_array *
- sizeof(*srv_info);
- srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
- if (!srv_info) {
- ret = -ENOMEM;
- break;
- }
- }
- ret = msm_ipc_router_lookup_server_name
- (&server_arg.port_name, srv_info,
- server_arg.num_entries_in_array,
- server_arg.lookup_mask);
- if (ret < 0) {
- IPC_RTR_ERR("%s: Server not found\n", __func__);
- ret = -ENODEV;
- kfree(srv_info);
- break;
- }
- server_arg.num_entries_found = ret;
- ret = copy_to_user((void *)arg, &server_arg,
- sizeof(server_arg));
- n = min(server_arg.num_entries_found,
- server_arg.num_entries_in_array);
- if (ret == 0 && n) {
- ret = copy_to_user((void *)(arg + sizeof(server_arg)),
- srv_info, n * sizeof(*srv_info));
- }
- if (ret)
- ret = -EFAULT;
- kfree(srv_info);
- break;
- case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
- ret = msm_ipc_router_bind_control_port(port_ptr);
- break;
- case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
- ret = msm_ipc_config_sec_rules((void *)arg);
- if (ret != -EPERM)
- port_ptr->type = IRSC_PORT;
- break;
- default:
- ret = -EINVAL;
- }
- release_sock(sk);
- return ret;
- }
- static unsigned int msm_ipc_router_poll(struct file *file, struct socket *sock,
- poll_table *wait)
- {
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr;
- u32 mask = 0;
- if (!sk)
- return -EINVAL;
- port_ptr = msm_ipc_sk_port(sk);
- if (!port_ptr)
- return -EINVAL;
- poll_wait(file, &port_ptr->port_rx_wait_q, wait);
- if (!list_empty(&port_ptr->port_rx_q))
- mask |= (POLLRDNORM | POLLIN);
- if (port_ptr->conn_status == CONNECTION_RESET)
- mask |= (POLLHUP | POLLERR);
- return mask;
- }
- static int msm_ipc_router_close(struct socket *sock)
- {
- struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr;
- int ret;
- if (!sk)
- return -EINVAL;
- lock_sock(sk);
- port_ptr = msm_ipc_sk_port(sk);
- if (!port_ptr) {
- release_sock(sk);
- return -EINVAL;
- }
- ret = msm_ipc_router_close_port(port_ptr);
- msm_ipc_unload_default_node(msm_ipc_sk(sk)->default_node_vote_info);
- release_sock(sk);
- sock_put(sk);
- sock->sk = NULL;
- return ret;
- }
- /**
- * register_ipcrtr_af_init_notifier() - Register for ipc router socket
- * address family initialization callback
- * @nb: Notifier block which will be notified when address family is
- * initialized.
- *
- * Return: 0 on success, standard error code otherwise.
- */
- int register_ipcrtr_af_init_notifier(struct notifier_block *nb)
- {
- int ret;
- if (!nb)
- return -EINVAL;
- mutex_lock(&ipcrtr_af_init_lock);
- if (sockets_enabled)
- nb->notifier_call(nb, IPCRTR_AF_INIT, NULL);
- ret = raw_notifier_chain_register(&ipcrtr_af_init_chain, nb);
- mutex_unlock(&ipcrtr_af_init_lock);
- return ret;
- }
- EXPORT_SYMBOL(register_ipcrtr_af_init_notifier);
- /**
- * unregister_ipcrtr_af_init_notifier() - Unregister for ipc router socket
- * address family initialization callback
- * @nb: Notifier block which will be notified once address family is
- * initialized.
- *
- * Return: 0 on success, standard error code otherwise.
- */
- int unregister_ipcrtr_af_init_notifier(struct notifier_block *nb)
- {
- int ret;
- if (!nb)
- return -EINVAL;
- ret = raw_notifier_chain_unregister(&ipcrtr_af_init_chain, nb);
- return ret;
- }
- EXPORT_SYMBOL(unregister_ipcrtr_af_init_notifier);
- static const struct net_proto_family msm_ipc_family_ops = {
- .owner = THIS_MODULE,
- .family = AF_MSM_IPC,
- .create = msm_ipc_router_create
- };
- static const struct proto_ops msm_ipc_proto_ops = {
- .family = AF_MSM_IPC,
- .owner = THIS_MODULE,
- .release = msm_ipc_router_close,
- .bind = msm_ipc_router_bind,
- .connect = ipc_router_connect,
- .socketpair = sock_no_socketpair,
- .accept = sock_no_accept,
- .getname = sock_no_getname,
- .poll = msm_ipc_router_poll,
- .ioctl = msm_ipc_router_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = msm_ipc_router_ioctl,
- #endif
- .listen = sock_no_listen,
- .shutdown = sock_no_shutdown,
- .setsockopt = sock_no_setsockopt,
- .getsockopt = sock_no_getsockopt,
- #ifdef CONFIG_COMPAT
- .compat_setsockopt = sock_no_setsockopt,
- .compat_getsockopt = sock_no_getsockopt,
- #endif
- .sendmsg = msm_ipc_router_sendmsg,
- .recvmsg = msm_ipc_router_recvmsg,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
- };
- static struct proto msm_ipc_proto = {
- .name = "MSM_IPC",
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct msm_ipc_sock),
- };
- int msm_ipc_router_init_sockets(void)
- {
- int ret;
- ret = proto_register(&msm_ipc_proto, 1);
- if (ret) {
- IPC_RTR_ERR("%s: Failed to register MSM_IPC protocol type\n",
- __func__);
- goto out_init_sockets;
- }
- ret = sock_register(&msm_ipc_family_ops);
- if (ret) {
- IPC_RTR_ERR("%s: Failed to register MSM_IPC socket type\n",
- __func__);
- proto_unregister(&msm_ipc_proto);
- goto out_init_sockets;
- }
- mutex_lock(&ipcrtr_af_init_lock);
- sockets_enabled = 1;
- raw_notifier_call_chain(&ipcrtr_af_init_chain,
- IPCRTR_AF_INIT, NULL);
- mutex_unlock(&ipcrtr_af_init_lock);
- out_init_sockets:
- return ret;
- }
- void msm_ipc_router_exit_sockets(void)
- {
- if (!sockets_enabled)
- return;
- sock_unregister(msm_ipc_family_ops.family);
- proto_unregister(&msm_ipc_proto);
- mutex_lock(&ipcrtr_af_init_lock);
- sockets_enabled = 0;
- raw_notifier_call_chain(&ipcrtr_af_init_chain,
- IPCRTR_AF_DEINIT, NULL);
- mutex_unlock(&ipcrtr_af_init_lock);
- }
|