1 /** @addtogroup MCD_MCDIMPL_DAEMON_SRV
2  * @{
3  * @file
4  *
5  * Connection data.
6  *
7  * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote
18  *    products derived from this software without specific prior
19  *    written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #include <cstring>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <sys/socket.h>
40 #include <linux/netlink.h>
41 
42 #include "NetlinkConnection.h"
43 
44 #include "log.h"
45 
46 
hashConnection(pid_t pid,uint32_t seq)47 uint64_t hashConnection(
48     pid_t pid,
49     uint32_t seq
50 )
51 {
52     return (((uint64_t)seq) << 32) | (uint64_t)pid;
53 }
54 
55 
56 //------------------------------------------------------------------------------
NetlinkConnection(void)57 NetlinkConnection::NetlinkConnection(
58     void
59 ) : Connection(),
60     dataLeft(0),
61     manager(NULL)
62 {
63     detached = false;
64     dataMsg = NULL;
65     dataStart = NULL;
66     dataLen = 0;
67 
68 
69     selfPid = getpid();
70     peerPid = 0;
71     sequenceMagic = 0;
72     hash = hashConnection(peerPid, sequenceMagic);
73 }
74 
75 
76 //------------------------------------------------------------------------------
NetlinkConnection(NetlinkConnectionManager * manager,int socketDescriptor,uint32_t pid,uint32_t seq)77 NetlinkConnection::NetlinkConnection(
78     NetlinkConnectionManager    *manager,
79     int                         socketDescriptor,
80     uint32_t                    pid,
81     uint32_t                    seq
82 ): Connection(),
83     dataLeft(0),
84     manager(manager)
85 {
86     detached = false;
87     dataMsg = NULL;
88     dataStart = NULL;
89     dataLen = 0;
90 
91     this->socketDescriptor = socketDescriptor;
92     selfPid = getpid();
93     peerPid = pid;
94     sequenceMagic = seq;
95     hash = hashConnection(pid, seq);
96 }
97 
98 
99 //------------------------------------------------------------------------------
~NetlinkConnection(void)100 NetlinkConnection::~NetlinkConnection(
101     void
102 )
103 {
104     LOG_I("%s: destroy connection for PID 0x%X", __FUNCTION__, peerPid);
105     socketDescriptor = -1;
106     free(dataMsg);
107 
108     if (manager) {
109         manager->removeConnection(hash);
110     }
111 }
112 
113 
114 //------------------------------------------------------------------------------
connect(const char * dest)115 bool NetlinkConnection::connect(
116     const char *dest
117 )
118 {
119     struct sockaddr_nl addr;
120     bool ret = false;
121 
122     assert(NULL != dest);
123 
124     LOG_I("%s: Connecting to SEQ 0x%X", __FUNCTION__, MC_DAEMON_PID);
125     do {
126         if ((socketDescriptor = socket(PF_NETLINK, SOCK_DGRAM, MC_DAEMON_NETLINK)) < 0) {
127             LOG_E("%s: Can't open netlink socket - errno: %d(%s)",
128                   __FUNCTION__, errno, strerror(errno));
129             break;
130         }
131         memset(&addr, 0, sizeof(addr));
132         addr.nl_family = AF_NETLINK;
133         addr.nl_pid = selfPid;  /* self pid */
134         addr.nl_groups = 0;  /* not in mcast groups */
135 
136         if (bind(socketDescriptor, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
137             LOG_E("%s: bind() failed - errno: %d(%s)", __FUNCTION__, errno, strerror(errno));
138             close(socketDescriptor);
139 
140             // Set invalid socketDescriptor
141             socketDescriptor = -1;
142             break;
143         }
144         ret = true;
145 
146 
147     } while (false);
148 
149     return ret;
150 }
151 
152 
153 //------------------------------------------------------------------------------
handleMessage(struct nlmsghdr * nlh)154 void NetlinkConnection::handleMessage(
155     struct nlmsghdr *nlh
156 )
157 {
158     dataMutex.lock();
159     /* Takeover the buffer */
160     dataMsg = nlh;
161     dataLen = NLMSG_PAYLOAD(dataMsg, 0);
162     dataStart = static_cast<uint8_t *>(NLMSG_DATA(dataMsg));
163     dataMutex.unlock();
164     dataLeft.signal();
165 }
166 
167 //------------------------------------------------------------------------------
readData(void * buffer,uint32_t len)168 size_t NetlinkConnection::readData(
169     void *buffer,
170     uint32_t len
171 )
172 {
173     return readData(buffer, len, -1);
174 }
175 
176 
177 //------------------------------------------------------------------------------
readData(void * buffer,uint32_t len,int32_t timeout)178 size_t NetlinkConnection::readData(
179     void      *buffer,
180     uint32_t  len,
181     int32_t   timeout
182 )
183 {
184     size_t ret = -1;
185     assert(NULL != buffer);
186 
187     if (!dataLeft.wait(timeout)) {
188         return -2;
189     }
190     dataMutex.lock();
191     // No data left?? Could we get this far?
192     if (dataLen <= 0) {
193         dataMutex.unlock();
194         return -2;
195     }
196 
197     //LOG_I("%s: reading connection data %u, connection data left %u",
198     //      __FUNCTION__, len, dataLen);
199 
200     assert(dataStart != NULL);
201 
202     // trying to read more than the left data
203     if (len > dataLen) {
204         ret = dataLen;
205         memcpy(buffer, dataStart, dataLen);
206         dataLen = 0;
207     } else {
208         ret = len;
209         memcpy(buffer, dataStart, len);
210         dataLen -= len;
211         dataStart += len;
212     }
213 
214     if (dataLen == 0) {
215         dataStart = NULL;
216         free(dataMsg);
217         dataMsg = NULL;
218     } else {
219         // Still some data left
220         dataLeft.signal();
221     }
222     dataMutex.unlock();
223 
224     //LOG_I("%s: read %u", __FUNCTION__, ret);
225     return ret;
226 }
227 
228 //------------------------------------------------------------------------------
writeData(void * buffer,uint32_t len)229 size_t NetlinkConnection::writeData(
230     void *buffer,
231     uint32_t len
232 )
233 {
234     size_t ret;
235     struct sockaddr_nl dest_addr;
236     struct nlmsghdr *nlh = NULL;
237     struct iovec iov;
238     struct msghdr msg;
239 
240     assert(NULL != buffer);
241     assert(-1 != socketDescriptor);
242 
243     //LOG_I("%s: send data %u to PID %u", __FUNCTION__, len, sequenceMagic);
244 
245     memset(&dest_addr, 0, sizeof(dest_addr));
246     dest_addr.nl_family = AF_NETLINK;
247     dest_addr.nl_pid = peerPid;
248     dest_addr.nl_groups = 0; /* unicast */
249 
250     nlh = (struct nlmsghdr *)malloc(
251               NLMSG_SPACE(len));
252     /* Fill the netlink message header */
253     nlh->nlmsg_len = NLMSG_SPACE(len);
254     nlh->nlmsg_pid = selfPid;
255     nlh->nlmsg_flags = NLM_F_REQUEST;
256     nlh->nlmsg_seq = sequenceMagic;
257 
258     /* Fill in the netlink message payload */
259     memcpy(NLMSG_DATA(nlh), buffer, len);
260 
261     iov.iov_base = (void *)nlh;
262     iov.iov_len = nlh->nlmsg_len;
263     msg.msg_name = (void *)&dest_addr;
264     msg.msg_namelen = sizeof(dest_addr);
265     msg.msg_iov = &iov;
266     msg.msg_iovlen = 1;
267     msg.msg_control = NULL;
268     msg.msg_controllen = 0;
269 
270     ret = sendmsg(socketDescriptor, &msg, 0);
271     if (ret != NLMSG_SPACE(len)) {
272         LOG_E( "%s: could no send all data, ret=%d, errno: %d(%s)",
273                __FUNCTION__, ret, errno, strerror(errno));
274         ret = -1;
275     } else {
276         /* The whole message sent also includes the header, so make sure to
277          * return only the number of payload data sent, not everything */
278         ret = len;
279     }
280 
281     free(nlh);
282 
283     return ret;
284 }
285 
286 /** @} */
287