Adjust to coding style rules
[platform/core/security/vasum.git] / common / netlink / netlink.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 class definition
23  */
24
25 #include "config.hpp"
26 #include "netlink.hpp"
27 #include "utils.hpp"
28 #include "utils/exception.hpp"
29 #include "utils/make-clean.hpp"
30 #include "utils/environment.hpp"
31 #include "base-exception.hpp"
32 #include "logger/logger.hpp"
33
34 #include <cassert>
35 #include <algorithm>
36 #include <sys/socket.h>
37 #include <unistd.h>
38 #include <linux/netlink.h>
39
40 #ifndef PAGE_SIZE
41 #define PAGE_SIZE 4096
42 #endif
43
44 using namespace utils;
45 using namespace vasum;
46
47 namespace {
48
49 const int NLMSG_RCV_GOOD_SIZE = 2*PAGE_SIZE;
50
51 int vsm_recvmsg(int fd, struct msghdr *msg, int flags)
52 {
53     int ret = recvmsg(fd, msg, flags);
54     if (ret < 0) {
55         LOGE("Can't receive message: " + getSystemErrorMessage());
56     } else if (ret == 0 && msg->msg_iov && msg->msg_iov->iov_len > 0) {
57         LOGE("Peer has performed an orderly shutdown");
58     } else if (msg->msg_flags & MSG_TRUNC) {
59         LOGE("Can't receive message: " + getSystemErrorMessage(EMSGSIZE));
60     } else if (msg->msg_flags & MSG_ERRQUEUE) {
61         LOGE("No data was received but an extended error");
62     } else if (msg->msg_flags & MSG_OOB) {
63         LOGE("Internal error (expedited or out-of-band data were received)");
64     } else if (msg->msg_flags & MSG_CTRUNC) {
65         LOGE("Some control data were discarded");
66     } else if (msg->msg_flags & MSG_EOR) {
67         LOGE("End-of-record");
68     } else {
69         // All ok
70         return ret;
71     }
72     throw VasumException("Can't receive netlink message");
73 }
74
75 void vsm_sendmsg(int fd, const struct msghdr *msg, int flags)
76 {
77     int ret = sendmsg(fd, msg, flags);
78     if (ret < 0) {
79         LOGE("Can't send message: " << getSystemErrorMessage());
80         throw VasumException("Can't send netlink message");
81     }
82 }
83
84 } // namespace
85
86 namespace vasum {
87 namespace netlink {
88
89 Netlink::Netlink() : mFd(-1)
90 {
91 }
92
93 Netlink::~Netlink()
94 {
95     close();
96 }
97
98 void Netlink::open(int netNsPid)
99 {
100     auto fdFactory = []{ return socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); };
101
102     assert(mFd == -1);
103     if (netNsPid == 0 || netNsPid == getpid()) {
104         mFd = fdFactory();
105         if (mFd == -1) {
106             LOGE("Can't open socket: " << getSystemErrorMessage());
107         }
108     } else {
109         mFd = utils::passNamespacedFd(netNsPid, CLONE_NEWNET, fdFactory);
110     }
111     if (mFd == -1) {
112         throw VasumException("Can't open netlink connection");
113     }
114
115     sockaddr_nl local = utils::make_clean<sockaddr_nl>();
116     local.nl_family = AF_NETLINK;
117
118     if (bind(mFd, (struct sockaddr *)&local, sizeof(local)) < 0) {
119         int err = errno;
120         close();
121         LOGE("Can't bind to socket: " << getSystemErrorMessage(err));
122         throw VasumException("Can't set up netlink connection");
123     }
124 }
125
126 void Netlink::close()
127 {
128     if (mFd != -1) {
129         ::close(mFd);
130         mFd = -1;
131     }
132 }
133
134 unsigned int Netlink::send(const void *nlmsg)
135 {
136     msghdr msg = utils::make_clean<msghdr>();
137     sockaddr_nl nladdr = utils::make_clean<sockaddr_nl>();
138     iovec iov = utils::make_clean<iovec>();
139
140     iov.iov_base = const_cast<void*>(nlmsg);
141     iov.iov_len = reinterpret_cast<const nlmsghdr*>(nlmsg)->nlmsg_len;
142     msg.msg_name = &nladdr;
143     msg.msg_namelen = sizeof(nladdr);
144     msg.msg_iov = &iov;
145     msg.msg_iovlen = 1;
146     nladdr.nl_family = AF_NETLINK;
147
148     vsm_sendmsg(mFd, &msg, 0);
149     return reinterpret_cast<const nlmsghdr*>(nlmsg)->nlmsg_seq;
150 }
151
152 std::unique_ptr<std::vector<char>> Netlink::rcv(unsigned int nlmsgSeq)
153 {
154     std::unique_ptr<std::vector<char>> buf(new std::vector<char>());
155
156     msghdr msg = utils::make_clean<msghdr>();
157     sockaddr_nl nladdr = utils::make_clean<sockaddr_nl>();
158     iovec iov = utils::make_clean<iovec>();
159
160     msg.msg_name = &nladdr;
161     msg.msg_namelen = sizeof(nladdr);
162     msg.msg_iov = &iov;
163     msg.msg_iovlen = 1;
164     nladdr.nl_family = AF_NETLINK;
165
166     nlmsghdr* answer;
167     nlmsghdr* lastOk = NULL;
168     size_t offset = 0;
169     do {
170         buf->resize(offset + NLMSG_RCV_GOOD_SIZE);
171         answer = reinterpret_cast<nlmsghdr*>(buf->data() + offset);
172         iov.iov_base = answer;
173         iov.iov_len = buf->size() - offset;
174         unsigned int ret = vsm_recvmsg(mFd, &msg, 0);
175         for (unsigned int len = ret; NLMSG_OK(answer, len); answer = NLMSG_NEXT(answer, len)) {
176             lastOk = answer;
177             if (answer->nlmsg_type == NLMSG_ERROR) {
178                 // It is NACK/ACK message
179                 nlmsgerr *err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(answer));
180                 if (answer->nlmsg_seq != nlmsgSeq) {
181                     throw VasumException("Sending failed: answer message was mismatched");
182                 }
183                 if (err->error) {
184                     throw VasumException("Sending failed: " + getSystemErrorMessage(-err->error));
185                 }
186             } else if (answer->nlmsg_type == NLMSG_OVERRUN) {
187                 throw VasumException("Sending failed: data lost");
188             }
189         }
190         if (lastOk == NULL) {
191             LOGE("Something went terribly wrong. Check vsm_recvmsg function");
192             throw VasumException("Can't receive data from system");
193         }
194         offset +=  NLMSG_ALIGN(ret);
195     } while (lastOk->nlmsg_type != NLMSG_DONE && lastOk->nlmsg_flags & NLM_F_MULTI);
196
197     buf->resize(offset);
198     return buf;
199 }
200
201 } //namespace netlink
202 } //namespace vasum