net: ipc_router: Add snapshot of IPC Router

This snapshot is taken as of msm-4.4 'commit <d2afad6a903b>
("Merge "ext4 crypto: enable HW based encryption with ICE"")'.

Additionally fix the code style warnings and errors.

CRs-Fixed: 1079350
Change-Id: I0972e0a42d12659fc854dce2491df3016704d756
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
This commit is contained in:
Karthikeyan Ramasubramanian 2016-09-16 16:05:32 -06:00 • committed by Gerrit - the friendly Code Review server
commit 6a116d66c5
15 changed files with 6683 additions and 0 deletions

View file

@ -0,0 +1,404 @@
Introduction
============
Inter Process Communication(IPC) Message Router
IPC Router provides a connectionless message routing service between
multiple processes in a MSM setup. The communicating processes can
run either in the same processor or in a different processor within
the MSM setup. The IPC Router has been designed to
1) Route messages of any types
2) Support a broader network of processors
The IPC Router follows the same protocol as the existing RPC Router
in the kernel while communicating with its peer IPC Routers.
Hardware description
====================
The IPC Router doesn't implement any specific hardware driver.
The IPC Router uses the existing hardware drivers to transport messages
across to the peer router. IPC Router contains a XPRT interface layer to
handle the different types of transports/links. This interface layer
abstracts the underlying transport complexities from the router and
provides a packet/message interface with the transports.
Software description
====================
The IPC Router is designed to support a client-server model. The
clients and servers communicate with one another by exchanging data
units known as messages. A message is a byte string from 1 to 64K bytes
long. The IPC Router provides a connectionless message routing service
between the clients and servers i.e. any client/server can communicate
with any other client/server in the network of processors.
Network Topology Overview:
--------------------------
The system is organized as a network of nodes. Each processor in the
network is the most fundamental element called node. The complete
network is hierarchically structured i.e. the network is divided into
tiers and each tier is fully-meshed. The following figure shows an
example network topology.
---N1--- ---N4---
| | | |
| | | |
N2----N3-------N5-----N6
| |
| |
---N7----
| |
| |
N8------N9
Each node in the complete network is identified using a unique node id
(Nx in the example network). In the example network, nodes N1, N2 & N3
are fully-meshed to form a tier 1 network. Similarly nodes N4, N5 & N6
form another tier 1 network and nodes N7, N8 & N9 form third tier 1
network. These three tier 1 networks are fully-meshed to form a tier 2
network.
Each transport/link in the network is identified using a unique name/id
called XPRT id. This XPRT id is used by the nodes to identify the
link to be used while sending message to a specific destination.
In addition, each transport/link in the network is assigned a link id.
This link id is used to identify the tier to which the link belongs to.
This link marking is used to avoid the routing loops while forwarding
the broadcast messages. The incoming messages are only forwarded onto an
egress link which has a link id different from that of an ingress link.
IPC Router Addressing Overview:
-------------------------------
Each client/server in the network is identified using a unique
<Node_id:Port_id> combination. Node_id identifies the processor on which
a client/server is running. Port_id is a unique id within a node. This
Port_id is assigned by the IPC Router in that node when a client/server
comes up. The Node_id & Port_id are 32 bits each.
Port_id 0xFFFFFFFE is reserved for Router &
0xFFFFFFFF is reserved for broadcast messages.
Each server in the network can also be addressed using a service name.
The service name is of the form <service(32 bits):instance(32 bits)>.
When a server comes up, it binds itself with a service name. This name
information along with the <Node_id:Port_id> is broadcast onto the
entire network.
Control Path:
-------------
IPC Router uses the control messages to communicate and propagate the
system wide events to the other routers in the system. Some of the
events include:
1) Node Status
2) Server Status
3) Client Status
4) Flow Control Request/Confirmation
Message Header:
---------------
IPC Router prepends a header to every message that it communicates with
other IPC Router. The receiving IPC Routers use the header to identify
the source and destination of the message, size of the message, type
of the message and handle any flow control requests. The IPC Router
header format is as follows:
0 31
-------------------------------------------------
| Version |
-------------------------------------------------
| Message Type |
-------------------------------------------------
| Source Node ID |
-------------------------------------------------
| Source Port ID |
-------------------------------------------------
| Confirm RX |
-------------------------------------------------
| Payload Length |
-------------------------------------------------
| Destination Node ID |
-------------------------------------------------
| Destination Port ID |
-------------------------------------------------
Message Header v2(Optimized):
-----------------------------
The following optimization has been done to the IPC Router header to
make it concise, align with the system requirement and enable future
expansion:
0 8 16 24 31
-----------------------------------------------------------------
| Version | Message Type | Control Flag |
-----------------------------------------------------------------
| Payload Length |
-----------------------------------------------------------------
| Source Node ID | Source Port ID |
-----------------------------------------------------------------
| Destination Node ID | Destination Port ID |
-----------------------------------------------------------------
Control Flag:
0 14 15
------------------------------------------------------------------
| Reserved | Opt. Hdr. | Confirm RX |
------------------------------------------------------------------
IPC Router identifies and processes the header depending on the version
field. The Confirm RX field in message header v1 becomes part of the
control flag. All the other fields are reduced in size to align with the
system requirement.
Optional Header:
An optional header bit field is introduced in the control flag to handle
any unforeseen future requirement that this header cannot handle. When
that bit is set, an optional header follows the current header. The
optional header format is as follows:
0 8 16 31
-----------------------------------------------------------------
| Length(words) | Type | Control Flag |
-----------------------------------------------------------------
| |
| Optional Header Contents |
| |
-----------------------------------------------------------------
Design
======
The IPC Router is organized into 2 layers:
1) Router Core layer
2) Router - XPRT Interface layer
This organization allows the router to abstract the XPRT's complexities
from that of the core router functionalities. The Router Core layer
performs the following core functions:
1) Message Routing
2) Distributed name service
3) Flow control between ports
The Router core layer contains the following important data structures
to perform the core functions in their respective order:
1) Routing Table
2) Table of Active Servers
3) Table of Active Remote ports
All these data structures get updated based on the events passed through
the control path.
The Router - XPRT Interface layer hides the underlying transport
complexities and provides and abstracted packet interface to the
Router Core layer. The Router - XPRT Interface layer registers itself
with the Router Core layer upon complete initialization of the XPRT.
The Router - XPRT Interface layer upon registration exports the
following functionalities to the Router Core:
1) Read from the XPRT
2) # of bytes of data available to read
3) Write to the XPRT
4) Space available to write to the XPRT
5) Close the XPRT
The user behavioral model of the IPC Router should be
1) Create a port
2) If server, register a name to the port
3) If remote port not known, lookup through the name
4) Send messages over the port to the remote port
5) Receive messages along with the source info from the port
6) Repeat steps 3, 4 & 5 as required
7) If server, unregister the name from the port
8) Close the port
Power Management
================
IPC Message Router uses wakelocks to ensure that the system does not go
into suspend mode while there are pending messages to be handled. Once all
the messages are handled, IPC Message Router releases the wakelocks to
allow the system to go into suspend mode and comply with the system power
management requirement.
SMP/multi-core
==============
The IPC Router uses mutexes & spinlocks to protect the shared data
structures to be SMP/multi-core safe.
Security
========
None
Performance
===========
None
Interface
=========
Kernel-space APIs:
------------------
/*
* msm_ipc_router_create_port - Create a IPC Router port
*
* @msm_ipc_port_notify: notification function which will notify events
* like READ_DATA_AVAIL, WRITE_DONE etc.
* @priv: caller private context pointer, passed to the notify callback.
*
* @return: a valid port pointer on success, NULL on failure
*
*/
struct msm_ipc_port * msm_ipc_router_create_port(
void (*msm_ipc_port_notify)(unsigned event, void *data,
void *addr, void *priv),
void *priv)
/*
* msm_ipc_router_close_port - Close down the port
*
* @port: Port to be closed
*
* @return: 0 on success, -ve value on failure
*
*/
int msm_ipc_router_close_port(struct msm_ipc_port *port)
/*
* msm_ipc_router_send_to - Send data to a remote port
*
* @from_port: Source port of the message
* @data: Data to be sent
* @to_addr: Destination port name or address
*
* @return: number of bytes sent on success, -ve value on failure
*
*/
int msm_ipc_router_send_to(struct msm_ipc_port *from_port,
struct sk_buff_head *data,
struct msm_ipc_addr *to_addr)
/*
* msm_ipc_router_recv_from - Receive data over a port
*
* @port: Port from which the data has to be read
* @data: Pointer to the data
* @src_addr: If valid, filled with the source address of the data
* @timeout: Time to wait for the data, if already not present
*
* @return: number of bytes received on success, -ve value on failure
*
*/
int msm_ipc_router_recv_from(struct msm_ipc_port *port,
struct sk_buff_head **data,
struct msm_ipc_addr *src_addr,
unsigned long timeout)
/*
* msm_ipc_router_register_server - Bind a local port with a service
* name
*
* @server_port: Port to be bound with a service name
* @name: Name to bind with the port
*
* @return: 0 on success, -ve value on failure
*
*/
int msm_ipc_router_register_server(struct msm_ipc_port *server_port,
struct msm_ipc_addr *name)
/*
* msm_ipc_router_unregister_server - Unbind the local port from its
* service name
*
* @server_port: Port to be unbound from its service name
*
* @return: 0 on success, -ve value on failure
*
*/
int msm_ipc_router_unregister_server(struct msm_ipc_port *server_port)
/*
* msm_ipc_router_lookup_server - Lookup port address for the port name
*
* @name: Name to be looked up for
*
* @return: Port address corresponding to the service name on success,
* NULL on failure
*
*/
struct msm_ipc_addr * msm_ipc_router_lookup_server(
struct msm_ipc_addr *name)
User-space APIs:
----------------
User-space applications/utilities can use the socket APIs to interface
with the IPC Router. IPC Router, in order to support the socket APIs,
will register a new socket Address/Protocol Family with the kernel
Socket layer. The identity of the new Address/Protocol Family will be
defined using the macro AF_MSM_IPC/PF_MSM_IPC (hardcoded to 38) in
include/linux/socket.h file. Since IPC Router supports only message
oriented transfer, only SOCK_DGRAM type of sockets will be supported
by the IPC Router.
Driver parameters
=================
debug_mask - This module parameter is used to enable/disable Router
log messages in the kernel logs. This parameter can take any value
from 0 to 255.
Dependencies
============
Drivers in this project:
-----------------------
The following drivers are present in this project, listed in the
bottom - up order of the stack.
1a) Router - SMD XPRT Interface driver. This driver is used to interface
the Router with the SMD transport.
1b) Router - HSIC XPRT Interface driver. This driver is used to interface
the Router with the HSIC_IPC Bridge transport for off-chip communication.
2) Core Router driver. This driver performs the core functionalities
3) Socket - Router Interface driver. This driver enables the socket
interface to be used with the IPC Router.
In the write/send direction, these drivers interact by invoking the
exported APIs from the underlying drivers. In the read/receive
directions, these drivers interact by passing messages/events.
Drivers Needed:
---------------
1) SMD
2) Kernel Socket Layer
3) Platform Driver
4) HSIC IPC Bridge Driver
To do
=====
Improvements:
-------------
The IPC Router is designed to route any messages, as long as the system
follows the network architecture and addressing schemes. But the
implementation in progress will route only QMI messages. With few
additional enhancements, it can route existing RPC messages too.

View file

@ -0,0 +1,16 @@
Qualcomm Technologies, Inc. IPC Router
Required properties:
-compatible: should be "qcom,ipc_router"
-qcom,node-id: unique ID to identify the node in network
Optional properties:
-qcom,default-peripheral: String property to indicate the default peripheral
to communicate
Example:
qcom,ipc_router {
compatible = "qcom,ipc_router";
qcom,node-id = <1>;
qcom,default-peripheral = "modem";
};

346
include/linux/ipc_router.h Normal file
View file

@ -0,0 +1,346 @@
/* Copyright (c) 2012-2016, 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.
*/
#ifndef _IPC_ROUTER_H
#define _IPC_ROUTER_H
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/msm_ipc.h>
#include <linux/device.h>
#include <linux/kref.h>
/* Maximum Wakeup Source Name Size */
#define MAX_WS_NAME_SZ 32
#define IPC_RTR_ERR(buf, ...) \
pr_err("IPC_RTR: " buf, __VA_ARGS__)
/**
* enum msm_ipc_router_event - Events that will be generated by IPC Router
*/
enum msm_ipc_router_event {
IPC_ROUTER_CTRL_CMD_DATA = 1,
IPC_ROUTER_CTRL_CMD_HELLO,
IPC_ROUTER_CTRL_CMD_BYE,
IPC_ROUTER_CTRL_CMD_NEW_SERVER,
IPC_ROUTER_CTRL_CMD_REMOVE_SERVER,
IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT,
IPC_ROUTER_CTRL_CMD_RESUME_TX,
};
/**
* rr_control_msg - Control message structure
* @cmd: Command identifier for HELLO message in Version 1.
* @hello: Message structure for HELLO message in Version 2.
* @srv: Message structure for NEW_SERVER/REMOVE_SERVER events.
* @cli: Message structure for REMOVE_CLIENT event.
*/
union rr_control_msg {
uint32_t cmd;
struct {
uint32_t cmd;
uint32_t checksum;
uint32_t versions;
uint32_t capability;
uint32_t reserved;
} hello;
struct {
uint32_t cmd;
uint32_t service;
uint32_t instance;
uint32_t node_id;
uint32_t port_id;
} srv;
struct {
uint32_t cmd;
uint32_t node_id;
uint32_t port_id;
} cli;
};
struct comm_mode_info {
int mode;
void *xprt_info;
};
enum ipc_rtr_af_event_type {
IPCRTR_AF_INIT = 1,
IPCRTR_AF_DEINIT,
};
/**
* msm_ipc_port - Definition of IPC Router port
* @list: List(local/control ports) in which this port is present.
* @ref: Reference count for this port.
* @this_port: Contains port's node_id and port_id information.
* @port_name: Contains service & instance info if the port hosts a service.
* @type: Type of the port - Client, Service, Control or Security Config.
* @flags: Flags to identify the port state.
* @port_lock_lhc3: Lock to protect access to the port information.
* @mode_info: Communication mode of the port owner.
* @port_rx_q: Receive queue where incoming messages are queued.
* @port_rx_q_lock_lhc3: Lock to protect access to the port's rx_q.
* @rx_ws_name: Name of the receive wakeup source.
* @port_rx_ws: Wakeup source to prevent suspend until the rx_q is empty.
* @port_rx_wait_q: Wait queue to wait for the incoming messages.
* @restart_state: Flag to hold the restart state information.
* @restart_lock: Lock to protect access to the restart_state.
* @restart_wait: Wait Queue to wait for any restart events.
* @endpoint: Contains the information related to user-space interface.
* @notify: Function to notify the incoming events on the port.
* @check_send_permissions: Function to check access control from this port.
* @num_tx: Number of packets transmitted.
* @num_rx: Number of packets received.
* @num_tx_bytes: Number of bytes transmitted.
* @num_rx_bytes: Number of bytes received.
* @priv: Private information registered by the port owner.
*/
struct msm_ipc_port {
struct list_head list;
struct kref ref;
struct msm_ipc_port_addr this_port;
struct msm_ipc_port_name port_name;
uint32_t type;
unsigned int flags;
struct mutex port_lock_lhc3;
struct comm_mode_info mode_info;
struct msm_ipc_port_addr dest_addr;
int conn_status;
struct list_head port_rx_q;
struct mutex port_rx_q_lock_lhc3;
char rx_ws_name[MAX_WS_NAME_SZ];
struct wakeup_source *port_rx_ws;
wait_queue_head_t port_rx_wait_q;
wait_queue_head_t port_tx_wait_q;
int restart_state;
spinlock_t restart_lock;
wait_queue_head_t restart_wait;
void *rport_info;
void *endpoint;
void (*notify)(unsigned int event, void *oob_data,
size_t oob_data_len, void *priv);
int (*check_send_permissions)(void *data);
uint32_t num_tx;
uint32_t num_rx;
unsigned long num_tx_bytes;
unsigned long num_rx_bytes;
void *priv;
};
#ifdef CONFIG_IPC_ROUTER
/**
* msm_ipc_router_create_port() - Create a IPC Router port/endpoint
* @notify: Callback function to notify any event on the port.
* @event: Event ID to be handled.
* @oob_data: Any out-of-band data associated with the event.
* @oob_data_len: Size of the out-of-band data, if valid.
* @priv: Private data registered during the port creation.
* @priv: Private info to be passed while the notification is generated.
*
* @return: Pointer to the port on success, NULL on error.
*/
struct msm_ipc_port *msm_ipc_router_create_port(
void (*notify)(unsigned int event, void *oob_data,
size_t oob_data_len, void *priv),
void *priv);
/**
* msm_ipc_router_bind_control_port() - Bind a port as a control port
* @port_ptr: Port which needs to be marked as a control port.
*
* @return: 0 on success, standard Linux error codes on error.
*/
int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr);
/**
* msm_ipc_router_lookup_server_name() - Resolve server address
* @srv_name: Name<service:instance> of the server to be resolved.
* @srv_info: Buffer to hold the resolved address.
* @num_entries_in_array: Number of server info the buffer can hold.
* @lookup_mask: Mask to specify the range of instances to be resolved.
*
* @return: Number of server addresses resolved on success, < 0 on error.
*/
int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
struct msm_ipc_server_info *srv_info,
int num_entries_in_array,
uint32_t lookup_mask);
/**
* msm_ipc_router_send_msg() - Send a message/packet
* @src: Sender's address/port.
* @dest: Destination address.
* @data: Pointer to the data to be sent.
* @data_len: Length of the data to be sent.
*
* @return: 0 on success, < 0 on error.
*/
int msm_ipc_router_send_msg(struct msm_ipc_port *src,
struct msm_ipc_addr *dest,
void *data, unsigned int data_len);
/**
* msm_ipc_router_get_curr_pkt_size() - Get the packet size of the first
* packet in the rx queue
* @port_ptr: Port which owns the rx queue.
*
* @return: Returns the size of the first packet, if available.
* 0 if no packets available, < 0 on error.
*/
int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr);
/**
* msm_ipc_router_read_msg() - Read a message/packet
* @port_ptr: Receiver's port/address.
* @data: Pointer containing the address of the received data.
* @src: Address of the sender/source.
* @len: Length of the data being read.
*
* @return: 0 on success, < 0 on error.
*/
int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
struct msm_ipc_addr *src,
unsigned char **data,
unsigned int *len);
/**
* msm_ipc_router_close_port() - Close the port
* @port_ptr: Pointer to the port to be closed.
*
* @return: 0 on success, < 0 on error.
*/
int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr);
/**
* msm_ipc_router_register_server() - Register a service on a port
* @server_port: IPC Router port with which a service is registered.
* @name: Service name <service_id:instance_id> that gets registered.
*
* @return: 0 on success, standard Linux error codes on error.
*/
int msm_ipc_router_register_server(struct msm_ipc_port *server_port,
struct msm_ipc_addr *name);
/**
* msm_ipc_router_unregister_server() - Unregister a service from a port
* @server_port: Port with with a service is already registered.
*
* @return: 0 on success, standard Linux error codes on error.
*/
int msm_ipc_router_unregister_server(struct msm_ipc_port *server_port);
/**
* register_ipcrtr_af_init_notifier() - Register 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 register_ipcrtr_af_init_notifier(struct notifier_block *nb);
/**
* 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);
#else
struct msm_ipc_port *msm_ipc_router_create_port(
void (*notify)(unsigned int event, void *oob_data,
size_t oob_data_len, void *priv),
void *priv)
{
return NULL;
}
static inline int msm_ipc_router_bind_control_port(
struct msm_ipc_port *port_ptr)
{
return -ENODEV;
}
int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
struct msm_ipc_server_info *srv_info,
int num_entries_in_array,
uint32_t lookup_mask)
{
return -ENODEV;
}
int msm_ipc_router_send_msg(struct msm_ipc_port *src,
struct msm_ipc_addr *dest,
void *data, unsigned int data_len)
{
return -ENODEV;
}
int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
{
return -ENODEV;
}
int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
struct msm_ipc_addr *src,
unsigned char **data,
unsigned int *len)
{
return -ENODEV;
}
int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
{
return -ENODEV;
}
static inline int msm_ipc_router_register_server(
struct msm_ipc_port *server_port,
struct msm_ipc_addr *name)
{
return -ENODEV;
}
static inline int msm_ipc_router_unregister_server(
struct msm_ipc_port *server_port)
{
return -ENODEV;
}
int register_ipcrtr_af_init_notifier(struct notifier_block *nb)
{
return -ENODEV;
}
int unregister_ipcrtr_af_init_notifier(struct notifier_block *nb)
{
return -ENODEV;
}
#endif
#endif

View file

@ -0,0 +1,175 @@
/* Copyright (c) 2011-2016, 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.
*/
#ifndef _IPC_ROUTER_XPRT_H
#define _IPC_ROUTER_XPRT_H
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/msm_ipc.h>
#include <linux/ipc_router.h>
#include <linux/kref.h>
#define IPC_ROUTER_XPRT_EVENT_DATA 1
#define IPC_ROUTER_XPRT_EVENT_OPEN 2
#define IPC_ROUTER_XPRT_EVENT_CLOSE 3
#define FRAG_PKT_WRITE_ENABLE 0x1
/**
* rr_header_v1 - IPC Router header version 1
* @version: Version information.
* @type: IPC Router Message Type.
* @src_node_id: Source Node ID of the message.
* @src_port_id: Source Port ID of the message.
* @control_flag: Flag to indicate flow control.
* @size: Size of the IPC Router payload.
* @dst_node_id: Destination Node ID of the message.
* @dst_port_id: Destination Port ID of the message.
*/
struct rr_header_v1 {
uint32_t version;
uint32_t type;
uint32_t src_node_id;
uint32_t src_port_id;
uint32_t control_flag;
uint32_t size;
uint32_t dst_node_id;
uint32_t dst_port_id;
};
/**
* rr_header_v2 - IPC Router header version 2
* @version: Version information.
* @type: IPC Router Message Type.
* @control_flag: Flags to indicate flow control, optional header etc.
* @opt_len: Combined size of the all optional headers in units of words.
* @size: Size of the IPC Router payload.
* @src_node_id: Source Node ID of the message.
* @src_port_id: Source Port ID of the message.
* @dst_node_id: Destination Node ID of the message.
* @dst_port_id: Destination Port ID of the message.
*/
struct rr_header_v2 {
uint8_t version;
uint8_t type;
uint8_t control_flag;
uint8_t opt_len;
uint32_t size;
uint16_t src_node_id;
uint16_t src_port_id;
uint16_t dst_node_id;
uint16_t dst_port_id;
} __attribute__((__packed__));
union rr_header {
struct rr_header_v1 hdr_v1;
struct rr_header_v2 hdr_v2;
};
/**
* rr_opt_hdr - Optional header for IPC Router header version 2
* @len: Total length of the optional header.
* @data: Pointer to the actual optional header.
*/
struct rr_opt_hdr {
size_t len;
unsigned char *data;
};
#define IPC_ROUTER_HDR_SIZE sizeof(union rr_header)
#define IPCR_WORD_SIZE 4
/**
* rr_packet - Router to Router packet structure
* @list: Pointer to prev & next packets in a port's rx list.
* @hdr: Header information extracted from or prepended to a packet.
* @opt_hdr: Optinal header information.
* @pkt_fragment_q: Queue of SKBs containing payload.
* @length: Length of data in the chain of SKBs
* @ref: Reference count for the packet.
*/
struct rr_packet {
struct list_head list;
struct rr_header_v1 hdr;
struct rr_opt_hdr opt_hdr;
struct sk_buff_head *pkt_fragment_q;
uint32_t length;
struct kref ref;
};
/**
* msm_ipc_router_xprt - Structure to hold XPRT specific information
* @name: Name of the XPRT.
* @link_id: Network cluster ID to which the XPRT belongs to.
* @priv: XPRT's private data.
* @get_version: Method to get header version supported by the XPRT.
* @set_version: Method to set header version in XPRT.
* @get_option: Method to get XPRT specific options.
* @read_avail: Method to get data size available to be read from the XPRT.
* @read: Method to read data from the XPRT.
* @write_avail: Method to get write space available in the XPRT.
* @write: Method to write data to the XPRT.
* @close: Method to close the XPRT.
* @sft_close_done: Method to indicate to the XPRT that handling of reset
* event is complete.
*/
struct msm_ipc_router_xprt {
char *name;
uint32_t link_id;
void *priv;
int (*get_version)(struct msm_ipc_router_xprt *xprt);
int (*get_option)(struct msm_ipc_router_xprt *xprt);
void (*set_version)(struct msm_ipc_router_xprt *xprt,
unsigned int version);
int (*read_avail)(struct msm_ipc_router_xprt *xprt);
int (*read)(void *data, uint32_t len,
struct msm_ipc_router_xprt *xprt);
int (*write_avail)(struct msm_ipc_router_xprt *xprt);
int (*write)(void *data, uint32_t len,
struct msm_ipc_router_xprt *xprt);
int (*close)(struct msm_ipc_router_xprt *xprt);
void (*sft_close_done)(struct msm_ipc_router_xprt *xprt);
};
void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
unsigned int event,
void *data);
/**
* create_pkt() - Create a Router packet
* @data: SKB queue to be contained inside the packet.
*
* @return: pointer to packet on success, NULL on failure.
*/
struct rr_packet *create_pkt(struct sk_buff_head *data);
struct rr_packet *clone_pkt(struct rr_packet *pkt);
void release_pkt(struct rr_packet *pkt);
/**
* ipc_router_peek_pkt_size() - Peek into the packet header to get potential
* packet size
* @data: Starting address of the packet which points to router header.
*
* @returns: potential packet size on success, < 0 on error.
*
* This function is used by the underlying transport abstraction layer to
* peek into the potential packet size of an incoming packet. This information
* is used to perform link layer fragmentation and re-assembly
*/
int ipc_router_peek_pkt_size(char *data);
#endif

View file

@ -281,6 +281,7 @@ header-y += mroute.h
header-y += msdos_fs.h
header-y += msg.h
header-y += msm_ion.h
header-y += msm_ipc.h
header-y += msm_rmnet.h
header-y += mtio.h
header-y += nbd.h

View file

@ -0,0 +1,91 @@
#ifndef _UAPI_MSM_IPC_H_
#define _UAPI_MSM_IPC_H_
#include <linux/types.h>
#include <linux/ioctl.h>
struct msm_ipc_port_addr {
uint32_t node_id;
uint32_t port_id;
};
struct msm_ipc_port_name {
uint32_t service;
uint32_t instance;
};
struct msm_ipc_addr {
unsigned char addrtype;
union {
struct msm_ipc_port_addr port_addr;
struct msm_ipc_port_name port_name;
} addr;
};
#define MSM_IPC_WAIT_FOREVER (~0) /* timeout for permanent subscription */
/*
* Socket API
*/
#ifndef AF_MSM_IPC
#define AF_MSM_IPC 27
#endif
#ifndef PF_MSM_IPC
#define PF_MSM_IPC AF_MSM_IPC
#endif
#define MSM_IPC_ADDR_NAME 1
#define MSM_IPC_ADDR_ID 2
struct sockaddr_msm_ipc {
unsigned short family;
struct msm_ipc_addr address;
unsigned char reserved;
};
struct config_sec_rules_args {
int num_group_info;
uint32_t service_id;
uint32_t instance_id;
unsigned int reserved;
gid_t group_id[0];
};
#define IPC_ROUTER_IOCTL_MAGIC (0xC3)
#define IPC_ROUTER_IOCTL_GET_VERSION \
_IOR(IPC_ROUTER_IOCTL_MAGIC, 0, unsigned int)
#define IPC_ROUTER_IOCTL_GET_MTU \
_IOR(IPC_ROUTER_IOCTL_MAGIC, 1, unsigned int)
#define IPC_ROUTER_IOCTL_LOOKUP_SERVER \
_IOWR(IPC_ROUTER_IOCTL_MAGIC, 2, struct sockaddr_msm_ipc)
#define IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE \
_IOR(IPC_ROUTER_IOCTL_MAGIC, 3, unsigned int)
#define IPC_ROUTER_IOCTL_BIND_CONTROL_PORT \
_IOR(IPC_ROUTER_IOCTL_MAGIC, 4, unsigned int)
#define IPC_ROUTER_IOCTL_CONFIG_SEC_RULES \
_IOR(IPC_ROUTER_IOCTL_MAGIC, 5, struct config_sec_rules_args)
struct msm_ipc_server_info {
uint32_t node_id;
uint32_t port_id;
uint32_t service;
uint32_t instance;
};
struct server_lookup_args {
struct msm_ipc_port_name port_name;
int num_entries_in_array;
int num_entries_found;
uint32_t lookup_mask;
struct msm_ipc_server_info srv_info[0];
};
#endif

View file

@ -429,6 +429,8 @@ config MAY_USE_DEVLINK
on MAY_USE_DEVLINK to ensure they do not cause link errors when
devlink is a loadable module and the driver using it is built-in.
source "net/ipc_router/Kconfig"
endif # if NET
# Used by archs to tell that they support BPF JIT compiler plus which flavour.

View file

@ -82,3 +82,4 @@ endif
obj-$(CONFIG_QRTR) += qrtr/
obj-$(CONFIG_NET_NCSI) += ncsi/
obj-$(CONFIG_RMNET_DATA) += rmnet_data/
obj-$(CONFIG_IPC_ROUTER) += ipc_router/

25
net/ipc_router/Kconfig Normal file
View file

@ -0,0 +1,25 @@
#
# IPC_ROUTER Configuration
#
menuconfig IPC_ROUTER
bool "IPC Router support"
help
IPC Router provides a connectionless message routing service
between multiple modules within a System-on-Chip(SoC). The
communicating entities can run either in the same processor or
in a different processor within the SoC. The IPC Router has been
designed to route messages of any types and support a broader
network of processors.
If in doubt, say N.
config IPC_ROUTER_SECURITY
depends on IPC_ROUTER
bool "IPC Router Security support"
help
This feature of IPC Router will enforce security rules
configured by a security script from the user-space. IPC Router
once configured with the security rules will ensure that the
sender of the message to a service belongs to the relevant
Linux group as configured by the security script.

7
net/ipc_router/Makefile Normal file
View file

@ -0,0 +1,7 @@
#
# Makefile for the Linux IPC_ROUTER
#
obj-$(CONFIG_IPC_ROUTER) := ipc_router_core.o
obj-$(CONFIG_IPC_ROUTER) += ipc_router_socket.o
obj-$(CONFIG_IPC_ROUTER_SECURITY) += ipc_router_security.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,150 @@
/* Copyright (c) 2011-2016, 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.
*/
#ifndef _IPC_ROUTER_PRIVATE_H
#define _IPC_ROUTER_PRIVATE_H
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/msm_ipc.h>
#include <linux/ipc_router.h>
#include <linux/ipc_router_xprt.h>
#include <net/sock.h>
/* definitions for the R2R wire protocol */
#define IPC_ROUTER_V1 1
/* Ambiguous definition but will enable multiplexing IPC_ROUTER_V2 packets
* with an existing alternate transport in user-space, if needed.
*/
#define IPC_ROUTER_V2 3
#define IPC_ROUTER_VER_BITMASK ((BIT(IPC_ROUTER_V1)) | (BIT(IPC_ROUTER_V2)))
#define IPC_ROUTER_HELLO_MAGIC 0xE110
#define IPC_ROUTER_CHECKSUM_MASK 0xFFFF
#define IPC_ROUTER_ADDRESS 0x0000FFFF
#define IPC_ROUTER_NID_LOCAL 1
#define MAX_IPC_PKT_SIZE 66000
#define IPC_ROUTER_LOW_RX_QUOTA 5
#define IPC_ROUTER_HIGH_RX_QUOTA 10
#define IPC_ROUTER_INFINITY -1
#define DEFAULT_RCV_TIMEO IPC_ROUTER_INFINITY
#define DEFAULT_SND_TIMEO IPC_ROUTER_INFINITY
#define ALIGN_SIZE(x) ((4 - ((x) & 3)) & 3)
#define ALL_SERVICE 0xFFFFFFFF
#define ALL_INSTANCE 0xFFFFFFFF
#define CONTROL_FLAG_CONFIRM_RX 0x1
#define CONTROL_FLAG_OPT_HDR 0x2
enum {
CLIENT_PORT,
SERVER_PORT,
CONTROL_PORT,
IRSC_PORT,
};
enum {
NULL_MODE,
SINGLE_LINK_MODE,
MULTI_LINK_MODE,
};
enum {
CONNECTION_RESET = -1,
NOT_CONNECTED,
CONNECTED,
};
struct msm_ipc_sock {
struct sock sk;
struct msm_ipc_port *port;
void *default_node_vote_info;
};
/**
* msm_ipc_router_create_raw_port() - Create an IPC Router port
* @endpoint: User-space space socket information to be cached.
* @notify: Function to notify incoming events on the port.
* @event: Event ID to be handled.
* @oob_data: Any out-of-band data associated with the event.
* @oob_data_len: Size of the out-of-band data, if valid.
* @priv: Private data registered during the port creation.
* @priv: Private Data to be passed during the event notification.
*
* @return: Valid pointer to port on success, NULL on failure.
*
* This function is used to create an IPC Router port. The port is used for
* communication locally or outside the subsystem.
*/
struct msm_ipc_port *
msm_ipc_router_create_raw_port(void *endpoint,
void (*notify)(unsigned int event,
void *oob_data,
size_t oob_data_len, void *priv),
void *priv);
int msm_ipc_router_send_to(struct msm_ipc_port *src,
struct sk_buff_head *data,
struct msm_ipc_addr *dest,
long timeout);
int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
struct rr_packet **pkt,
size_t buf_len);
int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
struct rr_packet **pkt,
struct msm_ipc_addr *src_addr, long timeout);
int msm_ipc_router_register_server(struct msm_ipc_port *server_port,
struct msm_ipc_addr *name);
int msm_ipc_router_unregister_server(struct msm_ipc_port *server_port);
int msm_ipc_router_init_sockets(void);
void msm_ipc_router_exit_sockets(void);
void msm_ipc_sync_sec_rule(u32 service, u32 instance, void *rule);
void msm_ipc_sync_default_sec_rule(void *rule);
int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout);
void msm_ipc_router_free_skb(struct sk_buff_head *skb_head);
/**
* ipc_router_set_conn() - Set the connection by initializing dest address
* @port_ptr: Local port in which the connection has to be set.
* @addr: Destination address of the connection.
*
* @return: 0 on success, standard Linux error codes on failure.
*/
int ipc_router_set_conn(struct msm_ipc_port *port_ptr,
struct msm_ipc_addr *addr);
void *msm_ipc_load_default_node(void);
void msm_ipc_unload_default_node(void *pil);
/**
* ipc_router_dummy_write_space() - Dummy write space available callback
* @sk: Socket pointer for which the callback is called.
*/
void ipc_router_dummy_write_space(struct sock *sk);
#endif

View file

@ -0,0 +1,330 @@
/* Copyright (c) 2012-2014,2016 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/uaccess.h>
#include <linux/kernel.h>
#include <linux/msm_ipc.h>
#include <linux/rwsem.h>
#include <linux/uaccess.h>
#include <net/sock.h>
#include "ipc_router_private.h"
#include "ipc_router_security.h"
#define IRSC_COMPLETION_TIMEOUT_MS 30000
#define SEC_RULES_HASH_SZ 32
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
struct security_rule {
struct list_head list;
u32 service_id;
u32 instance_id;
unsigned int reserved;
int num_group_info;
kgid_t *group_id;
};
static DECLARE_RWSEM(security_rules_lock_lha4);
static struct list_head security_rules[SEC_RULES_HASH_SZ];
static DECLARE_COMPLETION(irsc_completion);
/**
* wait_for_irsc_completion() - Wait for IPC Router Security Configuration
* (IRSC) to complete
*/
void wait_for_irsc_completion(void)
{
unsigned long rem_jiffies;
do {
rem_jiffies = wait_for_completion_timeout
(&irsc_completion,
msecs_to_jiffies(IRSC_COMPLETION_TIMEOUT_MS));
if (rem_jiffies)
return;
pr_err("%s: waiting for IPC Security Conf.\n", __func__);
} while (1);
}
/**
* signal_irsc_completion() - Signal the completion of IRSC
*/
void signal_irsc_completion(void)
{
complete_all(&irsc_completion);
}
/**
* check_permisions() - Check whether the process has permissions to
* create an interface handle with IPC Router
*
* @return: true if the process has permissions, else false.
*/
int check_permissions(void)
{
int rc = 0;
if (capable(CAP_NET_RAW) || capable(CAP_NET_BIND_SERVICE))
rc = 1;
return rc;
}
EXPORT_SYMBOL(check_permissions);
/**
* msm_ipc_config_sec_rules() - Add a security rule to the database
* @arg: Pointer to the buffer containing the rule.
*
* @return: 0 if successfully added, < 0 for error.
*
* A security rule is defined using <Service_ID: Group_ID> tuple. The rule
* implies that a user-space process in order to send a QMI message to
* service Service_ID should belong to the Linux group Group_ID.
*/
int msm_ipc_config_sec_rules(void *arg)
{
struct config_sec_rules_args sec_rules_arg;
struct security_rule *rule, *temp_rule;
int key;
size_t kgroup_info_sz;
int ret;
size_t group_info_sz;
gid_t *group_id = NULL;
int loop;
if (!uid_eq(current_euid(), GLOBAL_ROOT_UID))
return -EPERM;
ret = copy_from_user(&sec_rules_arg, (void *)arg,
sizeof(sec_rules_arg));
if (ret)
return -EFAULT;
if (sec_rules_arg.num_group_info <= 0)
return -EINVAL;
if (sec_rules_arg.num_group_info > (SIZE_MAX / sizeof(gid_t))) {
pr_err("%s: Integer Overflow %zu * %d\n", __func__,
sizeof(gid_t), sec_rules_arg.num_group_info);
return -EINVAL;
}
group_info_sz = sec_rules_arg.num_group_info * sizeof(gid_t);
if (sec_rules_arg.num_group_info > (SIZE_MAX / sizeof(kgid_t))) {
pr_err("%s: Integer Overflow %zu * %d\n", __func__,
sizeof(kgid_t), sec_rules_arg.num_group_info);
return -EINVAL;
}
kgroup_info_sz = sec_rules_arg.num_group_info * sizeof(kgid_t);
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
rule->group_id = kzalloc(kgroup_info_sz, GFP_KERNEL);
if (!rule->group_id) {
kfree(rule);
return -ENOMEM;
}
group_id = kzalloc(group_info_sz, GFP_KERNEL);
if (!group_id) {
kfree(rule->group_id);
kfree(rule);
return -ENOMEM;
}
rule->service_id = sec_rules_arg.service_id;
rule->instance_id = sec_rules_arg.instance_id;
rule->reserved = sec_rules_arg.reserved;
rule->num_group_info = sec_rules_arg.num_group_info;
ret = copy_from_user(group_id, ((void *)(arg + sizeof(sec_rules_arg))),
group_info_sz);
if (ret) {
kfree(group_id);
kfree(rule->group_id);
kfree(rule);
return -EFAULT;
}
for (loop = 0; loop < rule->num_group_info; loop++)
rule->group_id[loop] = make_kgid(current_user_ns(),
group_id[loop]);
kfree(group_id);
key = rule->service_id & (SEC_RULES_HASH_SZ - 1);
down_write(&security_rules_lock_lha4);
if (rule->service_id == ALL_SERVICE) {
temp_rule = list_first_entry(&security_rules[key],
struct security_rule, list);
list_del(&temp_rule->list);
kfree(temp_rule->group_id);
kfree(temp_rule);
}
list_add_tail(&rule->list, &security_rules[key]);
up_write(&security_rules_lock_lha4);
if (rule->service_id == ALL_SERVICE)
msm_ipc_sync_default_sec_rule((void *)rule);
else
msm_ipc_sync_sec_rule(rule->service_id, rule->instance_id,
(void *)rule);
return 0;
}
EXPORT_SYMBOL(msm_ipc_config_sec_rules);
/**
* msm_ipc_add_default_rule() - Add default security rule
*
* @return: 0 on success, < 0 on error/
*
* This function is used to ensure the basic security, if there is no
* security rule defined for a service. It can be overwritten by the
* default security rule from user-space script.
*/
static int msm_ipc_add_default_rule(void)
{
struct security_rule *rule;
int key;
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
rule->group_id = kzalloc(sizeof(*rule->group_id), GFP_KERNEL);
if (!rule->group_id) {
kfree(rule);
return -ENOMEM;
}
rule->service_id = ALL_SERVICE;
rule->instance_id = ALL_INSTANCE;
rule->num_group_info = 1;
*rule->group_id = AID_NET_RAW;
down_write(&security_rules_lock_lha4);
key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
list_add_tail(&rule->list, &security_rules[key]);
up_write(&security_rules_lock_lha4);
return 0;
}
/**
* msm_ipc_get_security_rule() - Get the security rule corresponding to a
* service
* @service_id: Service ID for which the rule has to be got.
* @instance_id: Instance ID for which the rule has to be got.
*
* @return: Returns the rule info on success, NULL on error.
*
* This function is used when the service comes up and gets registered with
* the IPC Router.
*/
void *msm_ipc_get_security_rule(u32 service_id, u32 instance_id)
{
int key;
struct security_rule *rule;
key = (service_id & (SEC_RULES_HASH_SZ - 1));
down_read(&security_rules_lock_lha4);
/* Return the rule for a specific <service:instance>, if found. */
list_for_each_entry(rule, &security_rules[key], list) {
if ((rule->service_id == service_id) &&
(rule->instance_id == instance_id)) {
up_read(&security_rules_lock_lha4);
return (void *)rule;
}
}
/* Return the rule for a specific service, if found. */
list_for_each_entry(rule, &security_rules[key], list) {
if ((rule->service_id == service_id) &&
(rule->instance_id == ALL_INSTANCE)) {
up_read(&security_rules_lock_lha4);
return (void *)rule;
}
}
/* Return the default rule, if no rule defined for a service. */
key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
list_for_each_entry(rule, &security_rules[key], list) {
if ((rule->service_id == ALL_SERVICE) &&
(rule->instance_id == ALL_INSTANCE)) {
up_read(&security_rules_lock_lha4);
return (void *)rule;
}
}
up_read(&security_rules_lock_lha4);
return NULL;
}
EXPORT_SYMBOL(msm_ipc_get_security_rule);
/**
* msm_ipc_check_send_permissions() - Check if the sendng process has
* permissions specified as per the rule
* @data: Security rule to be checked.
*
* @return: true if the process has permissions, else false.
*
* This function is used to check if the current executing process has
* permissions to send message to the remote entity. The security rule
* corresponding to the remote entity is specified by "data" parameter
*/
int msm_ipc_check_send_permissions(void *data)
{
int i;
struct security_rule *rule = (struct security_rule *)data;
/* Source/Sender is Root user */
if (uid_eq(current_euid(), GLOBAL_ROOT_UID))
return 1;
/* Destination has no rules defined, possibly a client. */
if (!rule)
return 1;
for (i = 0; i < rule->num_group_info; i++) {
if (!gid_valid(rule->group_id[i]))
continue;
if (in_egroup_p(rule->group_id[i]))
return 1;
}
return 0;
}
EXPORT_SYMBOL(msm_ipc_check_send_permissions);
/**
* msm_ipc_router_security_init() - Initialize the security rule database
*
* @return: 0 if successful, < 0 for error.
*/
int msm_ipc_router_security_init(void)
{
int i;
for (i = 0; i < SEC_RULES_HASH_SZ; i++)
INIT_LIST_HEAD(&security_rules[i]);
msm_ipc_add_default_rule();
return 0;
}
EXPORT_SYMBOL(msm_ipc_router_security_init);

View file

@ -0,0 +1,120 @@
/* Copyright (c) 2012-2014,2016 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.
*/
#ifndef _IPC_ROUTER_SECURITY_H
#define _IPC_ROUTER_SECURITY_H
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/errno.h>
#ifdef CONFIG_IPC_ROUTER_SECURITY
#include <linux/android_aid.h>
/**
* check_permisions() - Check whether the process has permissions to
* create an interface handle with IPC Router
*
* @return: true if the process has permissions, else false.
*/
int check_permissions(void);
/**
* msm_ipc_config_sec_rules() - Add a security rule to the database
* @arg: Pointer to the buffer containing the rule.
*
* @return: 0 if successfully added, < 0 for error.
*
* A security rule is defined using <Service_ID: Group_ID> tuple. The rule
* implies that a user-space process in order to send a QMI message to
* service Service_ID should belong to the Linux group Group_ID.
*/
int msm_ipc_config_sec_rules(void *arg);
/**
* msm_ipc_get_security_rule() - Get the security rule corresponding to a
* service
* @service_id: Service ID for which the rule has to be got.
* @instance_id: Instance ID for which the rule has to be got.
*
* @return: Returns the rule info on success, NULL on error.
*
* This function is used when the service comes up and gets registered with
* the IPC Router.
*/
void *msm_ipc_get_security_rule(u32 service_id, u32 instance_id);
/**
* msm_ipc_check_send_permissions() - Check if the sendng process has
* permissions specified as per the rule
* @data: Security rule to be checked.
*
* @return: true if the process has permissions, else false.
*
* This function is used to check if the current executing process has
* permissions to send message to the remote entity. The security rule
* corresponding to the remote entity is specified by "data" parameter
*/
int msm_ipc_check_send_permissions(void *data);
/**
* msm_ipc_router_security_init() - Initialize the security rule database
*
* @return: 0 if successful, < 0 for error.
*/
int msm_ipc_router_security_init(void);
/**
* wait_for_irsc_completion() - Wait for IPC Router Security Configuration
* (IRSC) to complete
*/
void wait_for_irsc_completion(void);
/**
* signal_irsc_completion() - Signal the completion of IRSC
*/
void signal_irsc_completion(void);
#else
static inline int check_permissions(void)
{
return 1;
}
static inline int msm_ipc_config_sec_rules(void *arg)
{
return -ENODEV;
}
static inline void *msm_ipc_get_security_rule(u32 service_id,
u32 instance_id)
{
return NULL;
}
static inline int msm_ipc_check_send_permissions(void *data)
{
return 1;
}
static inline int msm_ipc_router_security_init(void)
{
return 0;
}
static inline void wait_for_irsc_completion(void) { }
static inline void signal_irsc_completion(void) { }
#endif
#endif

View file

@ -0,0 +1,681 @@
/* Copyright (c) 2011-2016, 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);
ctl_msg = (union rr_control_msg *)(temp->data);
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)) {
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 = msm_ipc_sk_port(sk);
int ret;
lock_sock(sk);
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);
}