1 /** @addtogroup MCD_MCDIMPL_DAEMON_SRV
2  * @{
3  * @file
4  *
5  * Connection server.
6  *
7  * Handles incoming socket connections from clients using the MobiCore driver.
8  *
9  * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote
20  *    products derived from this software without specific prior
21  *    written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
24  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
27  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 #include "public/NetlinkServer.h"
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <linux/netlink.h>
40 
41 #include <stdlib.h>
42 #include "NetlinkConnection.h"
43 #include <signal.h>
44 
45 #define LOG_TAG "McDaemon"
46 #include "log.h"
47 
48 //------------------------------------------------------------------------------
NetlinkServer(ConnectionHandler * connectionHandler)49 NetlinkServer::NetlinkServer(
50     ConnectionHandler *connectionHandler
51 ): Server(connectionHandler, "dummy")
52 {
53 }
54 
55 
56 //------------------------------------------------------------------------------
run()57 void NetlinkServer::run(
58 )
59 {
60     do {
61         LOG_I("NetlinkServer: Starting to listen on netlink bus");
62 
63         // Open a socket
64         serverSock = socket(PF_NETLINK, SOCK_DGRAM,  MC_DAEMON_NETLINK);
65         if (serverSock < 0) {
66             LOG_ERRNO("Opening socket");
67             break;
68         }
69 
70         // Fill in address structure and bind to socket
71         struct sockaddr_nl src_addr;
72         struct nlmsghdr *nlh = NULL;
73         struct iovec iov;
74         struct msghdr msg;
75         uint32_t len;
76 
77         memset(&src_addr, 0, sizeof(src_addr));
78         src_addr.nl_family = AF_NETLINK;
79         src_addr.nl_pid = MC_DAEMON_PID;  /* daemon pid */
80         src_addr.nl_groups = 0;  /* not in mcast groups */
81         if (bind(serverSock, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
82             LOG_ERRNO("Binding to server socket failed, because bind");
83             close(serverSock);
84             break;
85         }
86 
87         // Start reading the socket
88         LOG_I("\n********* successfully initialized *********\n");
89 
90         for (;;) {
91             // This buffer will be taken over by the connection it was routed to
92             nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
93             memset(&msg, 0, sizeof(msg));
94             iov.iov_base = (void *)nlh;
95             iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
96             msg.msg_iov = &iov;
97             msg.msg_iovlen = 1;
98             msg.msg_name = &src_addr;
99             msg.msg_namelen = sizeof(src_addr);
100 
101             memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
102 
103             // Read the incomming message and route it to the connection based
104             // on the incomming PID
105             if ((len = recvmsg(serverSock, &msg, 0)) < 0) {
106                 LOG_ERRNO("recvmsg");
107                 break;
108             }
109 
110             if (NLMSG_OK(nlh, len)) {
111                 handleMessage(nlh);
112             } else {
113                 break;
114             }
115         }
116     } while (false);
117 
118     LOG_ERRNO("Exiting NetlinkServer! Because it");
119 }
120 
121 //------------------------------------------------------------------------------
handleMessage(struct nlmsghdr * nlh)122 void NetlinkServer::handleMessage(
123     struct nlmsghdr *nlh
124 )
125 {
126     uint32_t seq = nlh->nlmsg_seq;
127     uint32_t pid = nlh->nlmsg_pid;
128     //LOG_I("%s: Handling NQ message for pid %u seq %u...", __FUNCTION__, pid, seq);
129     uint64_t hash = hashConnection(pid, seq);
130     /* First cleanup the connection list */
131     cleanupConnections();
132 
133     NetlinkConnection *connection = findConnection(hash);
134     // This is a message from a new client
135     if (connection == NULL) {
136         //LOG_I("%s: Cound't find the connection, creating a new one", __FUNCTION__);
137         connection = new NetlinkConnection(this, serverSock, pid, seq);
138         // Add the new connection
139         insertConnection(hash, connection);
140     }
141 
142     connection->handleMessage(nlh);
143 
144     // Only handle connections which have not been detached
145     if (connection->detached == false) {
146         if (!connectionHandler->handleConnection(connection)) {
147             LOG_I("%s: No command processed.", __FUNCTION__);
148             connection->socketDescriptor = -1;
149             //Inform the driver
150             connectionHandler->dropConnection(connection);
151 
152             // Remove connection from list
153             removeConnection(hash);
154             connection->socketDescriptor = -1;
155             delete connection;
156         }
157         // If connection data is set to NULL then device close has been called
158         // so we must remove all connections associated with this hash
159         else if (connection->connectionData == NULL &&
160                  connection->detached == false) {
161             delete connection;
162         }
163     }
164 }
165 
166 
167 //------------------------------------------------------------------------------
detachConnection(Connection * connection)168 void NetlinkServer::detachConnection(
169     Connection *connection
170 )
171 {
172     connection->detached = true;
173 }
174 
175 
176 //------------------------------------------------------------------------------
~NetlinkServer(void)177 NetlinkServer::~NetlinkServer(
178     void
179 )
180 {
181     connectionMap_t::iterator i;
182     // Shut down the server socket
183     close(serverSock);
184 
185     // Destroy all client connections
186     for (i = peerConnections.begin(); i != peerConnections.end(); i++) {
187         if (i->second->detached == false) {
188             delete i->second;
189         }
190     }
191     peerConnections.clear();
192 }
193 
194 
195 //------------------------------------------------------------------------------
findConnection(uint64_t hash)196 NetlinkConnection *NetlinkServer::findConnection(
197     uint64_t hash
198 )
199 {
200     connectionMap_t::iterator i = peerConnections.find(hash);
201     if (i != peerConnections.end()) {
202         return i->second;
203     }
204 
205     return NULL;
206 }
207 
208 
209 //------------------------------------------------------------------------------
insertConnection(uint64_t hash,NetlinkConnection * connection)210 void NetlinkServer::insertConnection(
211     uint64_t hash,
212     NetlinkConnection *connection
213 )
214 {
215     peerConnections[hash] = connection;
216 }
217 
218 /* This is called from multiple threads! */
219 //------------------------------------------------------------------------------
removeConnection(uint64_t hash)220 void NetlinkServer::removeConnection(
221     uint64_t hash
222 )
223 {
224     connectionMap_t::iterator i = peerConnections.find(hash);
225     if (i != peerConnections.end()) {
226         peerConnections.erase(i);
227     }
228 }
229 
230 //------------------------------------------------------------------------------
cleanupConnections(void)231 void NetlinkServer::cleanupConnections(
232     void
233 )
234 {
235     connectionMap_t::reverse_iterator i;
236     pid_t pid;
237     NetlinkConnection *connection = NULL;
238     // Destroy all client connections
239     for (i = peerConnections.rbegin(); i != peerConnections.rend(); ++i) {
240         connection = i->second;
241         // Only 16 bits are for the actual PID, the rest is session magic
242         pid = connection->peerPid & 0xFFFF;
243         //LOG_I("%s: checking PID %u", __FUNCTION__, pid);
244         // Check if the peer pid is still alive
245         if (pid == 0) {
246             continue;
247         }
248         if (kill(pid, 0)) {
249             bool detached = connection->detached;
250             LOG_I("%s: PID %u has died, cleaning up session 0x%X",
251                   __FUNCTION__, pid, connection->peerPid);
252 
253             connection->socketDescriptor = -1;
254             //Inform the driver
255             connectionHandler->dropConnection(connection);
256 
257             // We aren't handling this connection anymore no matter what
258             removeConnection(connection->hash);
259 
260             // Remove connection from list only if detached, the detached
261             // connections are managed by the device
262             if (detached == false) {
263                 delete connection;
264             }
265             if (peerConnections.size() == 0) {
266                 break;
267             }
268             i = peerConnections.rbegin();
269         }
270     }
271 }
272 
273 /** @} */
274