Adjust to coding style rules
[platform/core/security/vasum.git] / common / netlink / netlink-message.cpp
1 /*
2  *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Mateusz Malicki <m.malicki2@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19 /**
20  * @file
21  * @author  Mateusz Malicki (m.malicki2@samsung.com)
22  * @brief   Netlink message class definition
23  */
24
25 #include "config.hpp"
26 #include "netlink-message.hpp"
27 #include "netlink.hpp"
28 #include "base-exception.hpp"
29 #include "logger/logger.hpp"
30
31 #include <algorithm>
32 #include <memory>
33 #include <cstring>
34 #include <atomic>
35 #include <cassert>
36
37 #include <linux/netlink.h>
38 #include <linux/rtnetlink.h>
39 #include <sys/socket.h>
40 #include <unistd.h>
41
42 namespace {
43
44 inline const rtattr* asAttr(const void* data) { return reinterpret_cast<const rtattr*>(data); }
45 inline const nlmsghdr* asHdr(const void* data) { return reinterpret_cast<const nlmsghdr*>(data); }
46 inline rtattr* asAttr(void* data) { return reinterpret_cast<rtattr*>(data); }
47 inline nlmsghdr* asHdr(void* data) { return reinterpret_cast<nlmsghdr*>(data); }
48 inline char* NLMSG_TAIL(nlmsghdr* nmsg)
49 {
50     return reinterpret_cast<char*>(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len);
51 }
52
53 } // namespace
54
55 namespace vasum {
56 namespace netlink {
57
58 NetlinkResponse send(const NetlinkMessage& msg)
59 {
60     return send(msg, 0);
61 }
62
63 NetlinkMessage::NetlinkMessage(uint16_t type, uint16_t flags)
64 {
65     static std::atomic<uint32_t> seq(0);
66     mNlmsg.resize(NLMSG_HDRLEN, 0);
67     hdr().nlmsg_len = NLMSG_HDRLEN;
68     hdr().nlmsg_flags = flags | NLM_F_ACK;
69     hdr().nlmsg_type = type;
70     hdr().nlmsg_seq = ++seq;
71     hdr().nlmsg_pid = getpid();
72 }
73
74 NetlinkMessage& NetlinkMessage::beginNested(int ifla)
75 {
76     auto offset = std::distance(reinterpret_cast<char*>(&hdr()), NLMSG_TAIL(&hdr()));
77     put(ifla, NULL, 0);
78     mNested.push(offset);
79     return *this;
80 }
81
82 NetlinkMessage& NetlinkMessage::endNested()
83 {
84     assert(!mNested.empty());
85     rtattr* nest = asAttr(reinterpret_cast<char*>(&hdr()) +  mNested.top());
86     nest->rta_len = std::distance(reinterpret_cast<char*>(nest), NLMSG_TAIL(&hdr()));
87     mNested.pop();
88     return *this;
89 }
90
91 NetlinkMessage& NetlinkMessage::put(int ifla, const std::string& value)
92 {
93     return put(ifla, value.c_str(), value.size() + 1);
94 }
95
96 NetlinkMessage& NetlinkMessage::put(int ifla, const void* data, int len)
97 {
98     struct rtattr *rta;
99     size_t rtalen = RTA_LENGTH(len);
100     int newLen = NLMSG_ALIGN(hdr().nlmsg_len) + RTA_ALIGN(rtalen);
101
102     setMinCapacity(newLen);
103     rta = asAttr(NLMSG_TAIL(&hdr()));
104     rta->rta_type = ifla;
105     rta->rta_len = rtalen;
106     memcpy(RTA_DATA(rta), data, len);
107     hdr().nlmsg_len = newLen;
108     return *this;
109 }
110
111 NetlinkMessage& NetlinkMessage::put(const void* data, int len)
112 {
113     setMinCapacity(hdr().nlmsg_len + len);
114     memcpy((reinterpret_cast<char*>(&hdr()) + hdr().nlmsg_len), data, len);
115     hdr().nlmsg_len += len;
116     return *this;
117 }
118
119 nlmsghdr& NetlinkMessage::hdr() {
120     return *asHdr(mNlmsg.data());
121 }
122
123 const nlmsghdr& NetlinkMessage::hdr() const {
124     return *asHdr(mNlmsg.data());
125 }
126
127 void NetlinkMessage::setMinCapacity(unsigned int size)
128 {
129     if (mNlmsg.size() < size) {
130         mNlmsg.resize(size, 0);
131     }
132 }
133
134 NetlinkResponse::NetlinkResponse(std::unique_ptr<std::vector<char>>&& message)
135     : mNlmsg(std::move(message))
136     , mNlmsgHdr(asHdr(mNlmsg.get()->data()))
137     , mPosition(NLMSG_HDRLEN)
138 {
139 }
140
141 bool NetlinkResponse::hasMessage() const
142 {
143     unsigned int tail = size() - getHdrPosition();
144     bool hasHeader = NLMSG_OK(mNlmsgHdr, tail);
145     if (!hasHeader) {
146         return false;
147     }
148     //Check if isn't ACK message
149     return NLMSG_PAYLOAD(mNlmsgHdr,0) > sizeof(uint32_t);
150 }
151
152 int NetlinkResponse::getMessageType() const
153 {
154     return mNlmsgHdr->nlmsg_type;
155 }
156
157 void NetlinkResponse::fetchNextMessage()
158 {
159     if (mNlmsgHdr->nlmsg_type == NLMSG_DONE) {
160         throw VasumException("There is no next message");
161     }
162     int tail = size() - mPosition;
163     mNlmsgHdr = NLMSG_NEXT(mNlmsgHdr, tail);
164     mPosition = getHdrPosition() + NLMSG_HDRLEN;
165 }
166
167 bool NetlinkResponse::hasAttribute() const
168 {
169     assert(mPosition >= getHdrPosition());
170     int tail = mNlmsgHdr->nlmsg_len - (mPosition - getHdrPosition());
171     return RTA_OK(asAttr(get(0)), tail);
172 }
173
174 bool NetlinkResponse::isNestedAttribute() const
175 {
176     return asAttr(get(RTA_LENGTH(0)))->rta_len == RTA_LENGTH(0);
177 }
178
179 void NetlinkResponse::skipAttribute()
180 {
181     const rtattr *rta = asAttr(get(RTA_LENGTH(0)));
182     if (size() < mPosition + RTA_ALIGN(rta->rta_len)) {
183         LOGE("Skipping out of buffer:"
184                 << " to: " << mPosition + RTA_ALIGN(rta->rta_len)
185                 << ", buf size: " << size());
186         throw VasumException("Skipping out of buffer");
187     }
188     seek(RTA_ALIGN(rta->rta_len));
189 }
190
191 NetlinkResponse& NetlinkResponse::openNested(int ifla)
192 {
193     const rtattr *rta = asAttr(get(RTA_LENGTH(0)));
194     if (rta->rta_type == ifla) {
195         LOGE("Wrong attribute type, expected: " << ifla << ", got: " << rta->rta_type);
196         throw VasumException("Wrong attribute type");
197     }
198     int pos = mPosition;
199     seek(RTA_LENGTH(0));
200     mNested.push(pos);
201     return *this;
202 }
203
204 NetlinkResponse& NetlinkResponse::closeNested()
205 {
206     assert(!mNested.empty());
207     int pos = mNested.top();
208     const rtattr *rta = asAttr(mNlmsg->data() + pos);
209     if (rta->rta_len != mPosition - pos) {
210         LOGE("There is no nested attribute end. Did you read all attributes (read: "
211                 << mPosition -  pos << ", length: " << rta->rta_len);
212         throw VasumException("There is no nested attribute end");
213     }
214     mNested.pop();
215     mPosition = pos;
216     return *this;
217 }
218
219 NetlinkResponse& NetlinkResponse::fetch(int ifla, std::string& value, int maxSize)
220 {
221     value = std::string(get(ifla, maxSize));
222     skipAttribute();
223     return *this;
224 }
225
226 const char* NetlinkResponse::get(int ifla, int len) const
227 {
228     const rtattr *rta = asAttr(get(RTA_LENGTH(len < 0 ? 0 : len)));
229     if (rta->rta_type != ifla) {
230         LOGE("Wrong attribute type, expected:" << ifla << ", got: " << rta->rta_type);
231         throw VasumException("Wrong attribute type");
232     }
233     if (len >= 0 && rta->rta_len != RTA_LENGTH(len)) {
234         LOGE("Wrong attribute length, expected: " << rta->rta_len << ", got " << len);
235         throw VasumException("Wrong attribute length");
236     }
237     return reinterpret_cast<const char*>(RTA_DATA(get(rta->rta_len)));
238 }
239
240 const char* NetlinkResponse::get(int len) const
241 {
242     if (size() < mPosition + len) {
243         LOGE("Read out of buffer:"
244                 << " from: " << mPosition + len
245                 << ", buf size: " << size());
246         throw VasumException("Read out of buffer");
247     }
248     return mNlmsg->data() + mPosition;
249 }
250
251 NetlinkResponse& NetlinkResponse::fetch(int ifla, char* data, int len)
252 {
253     std::copy_n(get(ifla, len), len, data);
254     skipAttribute();
255     return *this;
256 }
257
258 NetlinkResponse& NetlinkResponse::fetch(char* data, int len)
259 {
260     std::copy_n(get(len), len, data);
261     seek(len);
262     return *this;
263 }
264
265 int NetlinkResponse::getAttributeType() const
266 {
267     return asAttr(get(RTA_LENGTH(0)))->rta_type;
268 }
269
270 NetlinkResponse& NetlinkResponse::seek(int len)
271 {
272     if (size() < mPosition + len) {
273         throw VasumException("Skipping out of buffer");
274     }
275     mPosition += len;
276     return *this;
277 }
278
279 int NetlinkResponse::size() const
280 {
281     return mNlmsg->size();
282 }
283
284 inline int NetlinkResponse::getHdrPosition() const
285 {
286     return std::distance(reinterpret_cast<const char*>(mNlmsg->data()),
287                          reinterpret_cast<const char*>(mNlmsgHdr));
288 }
289
290 NetlinkResponse send(const NetlinkMessage& msg, int pid)
291 {
292     assert(msg.hdr().nlmsg_flags & NLM_F_ACK);
293
294     std::unique_ptr<std::vector<char>> data;
295     Netlink nl;
296     nl.open(pid);
297     try {
298         nl.send(&msg.hdr());
299         data = nl.rcv(msg.hdr().nlmsg_seq);
300     } catch (const std::exception& ex) {
301         LOGE("Sending failed (" << ex.what() << ")");
302         nl.close();
303         throw;
304     }
305     nl.close();
306     return NetlinkResponse(std::move(data));
307 }
308
309 } // namespace netlink
310 } // namespace vasum