Line data Source code
1 : /*
2 : * Copyright (c) 2023 Matvey Kraposhin
3 : */
4 : extern "C" {
5 : #include <errno.h>
6 : #include <net/if.h>
7 : #include <sys/socket.h>
8 : #include <unistd.h>
9 : #include <linux/netlink.h>
10 : #include <linux/rtnetlink.h>
11 : #include <linux/neighbour.h>
12 : #include <linux/if_addr.h>
13 : #include <arpa/inet.h>
14 : }
15 :
16 : #ifndef NLM_F_DUMP_FILTERED
17 : #define NLM_F_DUMP_FILTERED 0x20
18 : #endif
19 :
20 : #include <cstring>
21 : #include <cmn/agent_cmn.h>
22 : #include <oper/interface_common.h>
23 : #include "services/metadata_proxy.h"
24 : #include "services/services_init.h"
25 :
26 : namespace aux {
27 :
28 : /// @brief A wrapper function to insert an attribute
29 : /// into a netlink message
30 48 : void insert_attr(nlmsghdr* nl_msg,
31 : unsigned short attr_type, int attr_len, const void* attr_data) // T-L-V
32 : {
33 48 : if (nl_msg == NULL) {
34 0 : LOG(ERROR, "NULL pointer in nl_msg in" // INFO | DEBUG
35 : " void insert_attr, metadata_ipv6_netlink.cc");
36 0 : return;
37 : }
38 48 : if (attr_data == NULL) {
39 0 : LOG(ERROR, "NULL pointer in attr_data in"
40 : " void insert_attr, metadata_ipv6_netlink.cc");
41 0 : return;
42 : }
43 :
44 48 : struct rtattr *payload =
45 48 : (struct rtattr*)(((char*)nl_msg) + NLMSG_ALIGN(nl_msg->nlmsg_len));
46 48 : payload->rta_type = attr_type;
47 48 : payload->rta_len = RTA_LENGTH(attr_len);
48 : std::memcpy
49 48 : (
50 : RTA_DATA(payload),
51 : attr_data, attr_len);
52 48 : nl_msg->nlmsg_len = NLMSG_ALIGN(nl_msg->nlmsg_len) + payload->rta_len;
53 : }
54 :
55 :
56 : /// @brief A class template to store Netlink request data
57 : template <class T>
58 : struct NetlinkRequest
59 : {
60 : /// @brief A structure carrying the main netlink header
61 : nlmsghdr nl_message_hdr;
62 :
63 : /// @brief A structure carrying the request-specific header
64 : T nl_req_hdr;
65 :
66 : /// @brief A block of memory of memory carrying attributes
67 : /// of this request
68 : char payload[16384];
69 :
70 : /// @brief A reference to the total length of this Netlink request
71 : unsigned int& msg_len;
72 :
73 : /// @brief Returns a pointer to the Netlink message, generated
74 : /// using this class
75 48 : msghdr* message (sockaddr_nl& s_nl) {
76 48 : if (this->msg_len < NLMSG_LENGTH(sizeof(T))) {
77 0 : LOG(ERROR, " too short msg_len, "
78 : "NetlinkRequest::message, metadata_ipv6_netlink.cc, "<<
79 : "this->msg_len = " << this->msg_len <<
80 : ", NLMSG_LENGTH(sizeof(T)) = " << NLMSG_LENGTH(sizeof(T)));
81 0 : return NULL;
82 : }
83 :
84 48 : iov_.iov_len = this->msg_len;
85 48 : msg_ptr_.reset(new msghdr);
86 48 : std::memset(msg_ptr_.get(), 0, sizeof(msghdr));
87 48 : msg_ptr_->msg_name = &s_nl;
88 48 : msg_ptr_->msg_namelen = sizeof(s_nl);
89 48 : msg_ptr_->msg_iov = &iov_;
90 48 : msg_ptr_->msg_iovlen = 1; // 1 iov entry
91 :
92 48 : return msg_ptr_.get();
93 : }
94 :
95 : /// @brief Inserts an attribute stored in attr_data into this
96 : /// Netlink reequest
97 48 : void insert_attr(unsigned short attr_type,
98 : int attr_len, const void* attr_data) {
99 48 : aux::insert_attr(&nl_message_hdr, attr_type, attr_len, attr_data);
100 48 : }
101 :
102 : /// @brief Creates new blank request
103 48 : NetlinkRequest() : msg_len(nl_message_hdr.nlmsg_len) {
104 48 : std::memset(&nl_message_hdr, 0, sizeof(nl_message_hdr));
105 48 : std::memset(&nl_req_hdr, 0, sizeof(nl_req_hdr));
106 48 : std::memset(&payload, 0, sizeof(payload));
107 :
108 48 : iov_.iov_base = &nl_message_hdr;
109 48 : }
110 :
111 : /// @brief NetlinkRequest dtor
112 48 : ~NetlinkRequest() {}
113 :
114 : private:
115 :
116 : /// @brief Disallow copy ctor
117 : NetlinkRequest(const NetlinkRequest<T>&);
118 :
119 : /// @brief Disallow assignment operator
120 : const NetlinkRequest& operator = (const NetlinkRequest<T>&);
121 :
122 : /// @brief An iovec struct to keep headers with data
123 : iovec iov_;
124 :
125 : /// @brief A pointer to the Netlink message header
126 : std::unique_ptr<msghdr> msg_ptr_;
127 : };
128 :
129 :
130 : /// @brief A storage for the Netlink socket descriptor
131 : /// @brief and for the Netlink socket sockaddr structure
132 : class NetlinkSocket {
133 :
134 : private:
135 :
136 : /// @brief Forbid the copy ctor
137 : NetlinkSocket(const NetlinkSocket& nl);
138 :
139 : /// @brief Forbid the copy assignment
140 : const NetlinkSocket& operator = (const NetlinkSocket& nl);
141 :
142 : private:
143 :
144 : /// @brief A Netlink socket ID (descriptor)
145 : int socket_fd;
146 :
147 : /// @brief A Netlink socket sockaddr structure
148 : sockaddr_nl local_sock_addr;
149 :
150 : public:
151 :
152 : /// @brief Creates a socket and binds it with AF_NETLINK family
153 : /// @brief and the kernel process
154 24 : NetlinkSocket() {
155 24 : socket_fd = -1;
156 : // open a socket
157 24 : socket_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
158 24 : if (socket_fd < 0) {
159 0 : LOG(ERROR, "An error has occured during opening the socket"
160 : "NetlinkSocket::NetlinkSocket, metadata_ipv6_netlink.cc, "
161 : "errno = " << errno << std::endl);
162 0 : return;
163 : }
164 :
165 : // bind the socket to an address
166 24 : local_sock_addr.nl_family = AF_NETLINK;
167 24 : local_sock_addr.nl_groups = 0; // no multicast
168 24 : local_sock_addr.nl_pad = 0; // not used here
169 24 : local_sock_addr.nl_pid = 0;
170 48 : if (bind(socket_fd,
171 24 : (sockaddr*)(&local_sock_addr), sizeof(local_sock_addr)) < 0) {
172 0 : LOG(ERROR, "An error has occured during binding the socket"
173 : "NetlinkSocket::NetlinkSocket, metadata_ipv6_netlink.cc, "
174 : "errno = " << errno << std::endl);
175 0 : close(socket_fd);
176 : }
177 : }
178 :
179 : /// @brief Closes the Netlink connection
180 24 : ~NetlinkSocket() {
181 24 : int close_res = close(socket_fd);
182 24 : if (close_res) {
183 0 : LOG(ERROR, "An error has occured during closing the socket"
184 : "NetlinkSocket::NetlinkSocket, metadata_ipv6_netlink.cc, "
185 : "errno = " << errno << std::endl);
186 : }
187 24 : }
188 :
189 : /// @brief Returns a reference to the Netlink socket descriptor
190 48 : const int& fd() const {
191 48 : return socket_fd;
192 : }
193 :
194 : /// @brief Returns a reference to the Netlink socket sockaddr struct
195 48 : sockaddr_nl& netlink_socket() {
196 48 : return local_sock_addr;
197 : }
198 : };
199 :
200 : struct NetlinkResponse {
201 :
202 : /// @brief A pointer to the begin of the response
203 : struct nlmsghdr *resp_nh_;
204 :
205 : /// @brief The number of received bytes in the response
206 : int n_recv_;
207 :
208 : /// @brief contains message with response
209 : struct msghdr msg_;
210 :
211 : /// @brief Contains response data
212 : struct iovec iov_;
213 :
214 : /// @brief A buffer for response data
215 : char buffer_[16384];
216 :
217 : private:
218 :
219 : /// @brief Forbid default ctor for NetlinkResponse
220 : NetlinkResponse();
221 :
222 : public:
223 :
224 : /// @brief Construct a NetlinkResponse object from the request
225 : /// message and the Netlink socket
226 0 : NetlinkResponse(msghdr* req_msg, NetlinkSocket& nl_sock) {
227 0 : resp_nh_ = NULL;
228 0 : memset(buffer_, 0, sizeof(buffer_));
229 0 : memset(&iov_, 0, sizeof(iov_));
230 :
231 0 : msg_.msg_name = req_msg->msg_name;
232 0 : msg_.msg_namelen = req_msg->msg_namelen;
233 0 : msg_.msg_iov = &iov_;
234 0 : msg_.msg_iovlen = 1;
235 0 : iov_.iov_base = buffer_;
236 0 : iov_.iov_len = sizeof(buffer_);
237 0 : n_recv_ = recvmsg(nl_sock.fd(), &msg_, 0);
238 0 : if (n_recv_ < 0) {
239 0 : LOG(ERROR, "Error receiving message from netlink: "
240 : << strerror(errno) << std::endl);
241 : } else {
242 0 : resp_nh_ = reinterpret_cast<struct nlmsghdr *>(&buffer_[0]);
243 : }
244 0 : }
245 :
246 : /// @brief NetlinkResponse dtor
247 0 : ~NetlinkResponse() {}
248 :
249 : /// @brief Iterates over all response parts
250 : template <class T>
251 0 : int response_iterate(const T& handler_func) {
252 0 : for (;
253 0 : NLMSG_OK(resp_nh_, n_recv_);
254 0 : resp_nh_ = NLMSG_NEXT(resp_nh_, n_recv_)) {
255 0 : int handle_res = handler_func(resp_nh_);
256 0 : if (handle_res >= 0) {
257 0 : return handle_res;
258 : }
259 : }
260 0 : return -1;
261 : }
262 : };
263 :
264 : struct IPv6Reader {
265 :
266 0 : explicit IPv6Reader(const unsigned int itf_idx)
267 0 : :
268 0 : vhost_idx_(itf_idx) {
269 0 : }
270 :
271 : unsigned int vhost_idx_;
272 :
273 : mutable Ip6Address vhost_addr_;
274 :
275 0 : int read_ipv6_address(const struct nlmsghdr *resp_nh) const {
276 0 : struct rtattr *resp_attr = NULL;
277 0 : struct ifaddrmsg *ifa_resp = NULL;
278 0 : int attr_len = 0;
279 0 : if (resp_nh->nlmsg_type != RTM_NEWADDR) {
280 0 : return -1;
281 : }
282 0 : ifa_resp = static_cast<struct ifaddrmsg *>(NLMSG_DATA(resp_nh));
283 0 : if (ifa_resp == NULL) {
284 0 : return -1;
285 : }
286 0 : if (ifa_resp->ifa_index != vhost_idx_) {
287 0 : return -1;
288 : }
289 0 : attr_len = resp_nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa_resp));
290 0 : for (resp_attr = IFA_RTA(ifa_resp);
291 0 : RTA_OK(resp_attr, attr_len);
292 0 : resp_attr = RTA_NEXT(resp_attr, attr_len)) {
293 0 : if (resp_attr->rta_type == IFA_ADDRESS) {
294 : char addr_str[256];
295 0 : memset(addr_str, 0, 256);
296 0 : inet_ntop(AF_INET6,
297 : RTA_DATA(resp_attr), addr_str, 256);
298 0 : if (addr_str[0] != 0) {
299 0 : vhost_addr_ = Ip6Address::from_string(addr_str);
300 0 : if (!vhost_addr_.is_unspecified() &&
301 0 : vhost_addr_.is_link_local()) {
302 0 : return 1;
303 : }
304 : }
305 : }
306 : }
307 0 : return -1;
308 : }
309 :
310 0 : int operator() (const struct nlmsghdr *resp_nh) const {
311 0 : return read_ipv6_address(resp_nh);
312 : }
313 :
314 : private:
315 :
316 : IPv6Reader();
317 :
318 : IPv6Reader(const IPv6Reader&);
319 :
320 : };
321 :
322 : /// @brief Reads a response (ack/nock) after a Netlink request
323 24 : static bool read_ack_response(
324 : const msghdr *msg,
325 : int send_n,
326 : NetlinkSocket& nl_sock,
327 : const char* func_name) {
328 :
329 24 : aux::NetlinkRequest<nlmsgerr> nl_ack;
330 24 : nl_ack.msg_len = NLMSG_LENGTH(sizeof(nl_ack.nl_req_hdr));
331 24 : msghdr *ack = nl_ack.message(nl_sock.netlink_socket());
332 24 : int recv_n = -1;
333 24 : if (msg && send_n > 0 && ack) {
334 24 : recv_n = recvmsg(nl_sock.fd(), ack, MSG_WAITALL);
335 24 : if (recv_n <= 0) {
336 0 : LOG(ERROR, "Recvmsg failed,"<<
337 : func_name << ", metadata_ipv6_netlink.cc, "
338 : "errno = " << errno << std::endl);
339 0 : return false;
340 : }
341 24 : if (nl_ack.nl_message_hdr.nlmsg_type == NLMSG_ERROR) {
342 24 : const int ack_err = -nl_ack.nl_req_hdr.error;
343 24 : if (ack_err == 0
344 24 : || ack_err == EEXIST || ack_err == EADDRNOTAVAIL) {
345 : // EEXIST -- address is already present, not an error
346 : // EADDRNOTAVAIL -- address is not present, not an error
347 0 : return true;
348 : }
349 24 : LOG(ERROR, "Error in the Netlink message, "<<
350 : func_name << ", metadata_ipv6_netlink.cc, "
351 : "error code = " << ack_err);
352 : }
353 : }
354 24 : return false;
355 24 : }
356 :
357 : } //namespace aux
358 :
359 1 : bool MetadataProxy::NetlinkGetVhostIp(Ip6Address& vhost_ll_ip) {
360 : // set ip address
361 : //
362 : // set device index
363 : const int dev_idx =
364 1 : VhostIndex(services_->agent()->vhost_interface_name().c_str());
365 1 : if (dev_idx == 0) {
366 1 : std::cout<< "Device " << services_->agent()->vhost_interface_name()
367 2 : << " has not been found "
368 1 : << std::endl;
369 1 : return false;
370 : } else {
371 :
372 0 : std::cout<< "dev_idx = " << dev_idx
373 0 : << ", dev_name = " << services_->agent()->vhost_interface_name()
374 0 : << std::endl;
375 : }
376 :
377 : // open a socket
378 0 : aux::NetlinkSocket nl_sock;
379 0 : int send_n = -1;
380 : {
381 0 : aux::NetlinkRequest<ifaddrmsg> addr_nl_req;
382 0 : ifaddrmsg &nl_req_param = addr_nl_req.nl_req_hdr;
383 0 : nl_req_param.ifa_family = AF_INET6;
384 0 : nl_req_param.ifa_prefixlen = 128;
385 0 : nl_req_param.ifa_index = dev_idx;
386 :
387 0 : nlmsghdr &msg_hdr = addr_nl_req.nl_message_hdr;
388 0 : msg_hdr.nlmsg_type = RTM_GETADDR;
389 0 : msg_hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; // | NLM_F_DUMP_FILTERED;
390 0 : addr_nl_req.msg_len = NLMSG_LENGTH(sizeof(nl_req_param));
391 :
392 0 : msghdr *msg = addr_nl_req.message(nl_sock.netlink_socket());
393 0 : send_n = -1;
394 0 : if (msg == NULL) {
395 0 : return false;
396 : }
397 :
398 : // send a request to the kernel
399 0 : send_n = sendmsg(nl_sock.fd(), msg, 0);
400 0 : if (send_n <= 0) {
401 0 : LOG(ERROR, "sendmsg failed (address request), "
402 : "MetadataProxy::NetlinkAddVhostIp, metadata_ipv6_netlink.cc, "
403 : "errno = " << errno << std::endl);
404 0 : return false;
405 : }
406 :
407 : // read the response
408 0 : aux::NetlinkResponse addr_resp(msg, nl_sock);
409 0 : aux::IPv6Reader ipv6_reader(dev_idx);
410 0 : int ret_value = addr_resp.response_iterate(ipv6_reader);
411 0 : if (ret_value > 0) {
412 0 : vhost_ll_ip = ipv6_reader.vhost_addr_;
413 0 : return true;
414 : }
415 0 : }
416 :
417 0 : return false;
418 0 : }
419 :
420 12 : void MetadataProxy::
421 : NetlinkAddVhostNb(const IpAddress& nb_ip, const MacAddress& via_mac) {
422 12 : if (!nb_ip.is_v6()) {
423 0 : return;
424 : }
425 :
426 : // set ip address
427 : in6_addr addr6;
428 12 : int addr_res = inet_pton(AF_INET6, nb_ip.to_string().c_str(), &addr6);
429 12 : if (addr_res < 0) {
430 0 : LOG(ERROR, "An error has occured during address initialization,"
431 : "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
432 : "errno = " << errno << std::endl);
433 0 : return;
434 : }
435 12 : if (addr_res == 0) {
436 0 : LOG(ERROR, "A wrong address has been specified,"
437 : "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
438 : "address = " << nb_ip.to_string().c_str() << std::endl);
439 0 : return;
440 : }
441 :
442 : // set mac address
443 12 : unsigned char nb_mac_addr[] = {0,0,0,0,0,0};
444 12 : via_mac.ToArray(nb_mac_addr, sizeof(nb_mac_addr));
445 :
446 : // set device index
447 : const int dev_idx =
448 12 : VhostIndex(services_->agent()->vhost_interface_name());
449 12 : if (dev_idx <= 0) {
450 12 : LOG(ERROR, "Error while retreiving device index,"
451 : "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
452 : "dev_idx = " << dev_idx << std::endl);
453 : }
454 :
455 : // open a socket
456 12 : aux::NetlinkSocket nl_sock;
457 :
458 12 : aux::NetlinkRequest<ndmsg> nd_req;
459 :
460 12 : ndmsg &nd_message_hdr = nd_req.nl_req_hdr;
461 12 : nd_message_hdr.ndm_family = AF_INET6; // AF_INET/AF_INET6/AF_UNSPEC
462 12 : nd_message_hdr.ndm_ifindex = dev_idx;
463 12 : nd_message_hdr.ndm_state = NUD_PERMANENT;
464 :
465 12 : nlmsghdr &nl_message_hdr = nd_req.nl_message_hdr;
466 12 : nl_message_hdr.nlmsg_type = RTM_NEWNEIGH;
467 12 : nl_message_hdr.nlmsg_flags = // NLM_F_REPLACE is neccessary, since a
468 : NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE // route might be already
469 : | NLM_F_ACK; // in the table
470 :
471 12 : nd_req.msg_len = NLMSG_LENGTH(sizeof(nd_message_hdr));
472 :
473 : // insert lladdr (MAC)
474 12 : nd_req.insert_attr(NDA_LLADDR, sizeof(nb_mac_addr), &nb_mac_addr[0]);
475 : // insert IP (IPv6)
476 12 : nd_req.insert_attr(NDA_DST, sizeof(addr6), &addr6);
477 :
478 12 : msghdr* msg = nd_req.message(nl_sock.netlink_socket());
479 12 : int send_n = -1;
480 12 : if (msg) {
481 : // sending of a request to the kernel
482 12 : send_n = sendmsg(nl_sock.fd(), msg, 0);
483 12 : if (send_n <= 0) {
484 0 : LOG(ERROR, "Sendmsg failed,"
485 : "MetadataProxy::NetlinkAddVhostNb, metadata_ipv6_netlink.cc, "
486 : "errno = " << errno << std::endl);
487 : }
488 : }
489 :
490 12 : aux::read_ack_response(msg, send_n, nl_sock, "NetlinkAddVhostNb");
491 12 : }
492 :
493 12 : void MetadataProxy::NetlinkAddInterfaceRoute(const IpAddress& intf_addr) {
494 12 : if (!intf_addr.is_v6()) {
495 0 : return;
496 : }
497 :
498 : // set ip address
499 : in6_addr addr6;
500 12 : int addr_res = inet_pton(AF_INET6, intf_addr.to_string().c_str(), &addr6);
501 12 : if (addr_res < 0) {
502 0 : LOG(ERROR, "An error has occured during address initialization,"
503 : "MetadataProxy::NetlinkAddInterfaceRoute, metadata_ipv6_netlink.cc, "
504 : "errno = " << errno << std::endl);
505 0 : return;
506 : }
507 12 : if (addr_res == 0) {
508 0 : LOG(ERROR, "A wrong address has been specified,"
509 : "MetadataProxy::NetlinkAddInterfaceRoute, metadata_ipv6_netlink.cc, "
510 : "address = " << intf_addr.to_string().c_str() << std::endl);
511 0 : return;
512 : }
513 :
514 : // set device index
515 : const int dev_idx =
516 12 : VhostIndex(services_->agent()->vhost_interface_name());
517 12 : if (dev_idx <= 0) {
518 12 : LOG(ERROR, "Error while retreiving device index,"
519 : "MetadataProxy::NetlinkAddInterfaceRoute, metadata_ipv6_netlink.cc, "
520 : "dev_idx = " << dev_idx << std::endl);
521 : }
522 :
523 : // open a socket
524 12 : aux::NetlinkSocket nl_sock;
525 :
526 12 : aux::NetlinkRequest<rtmsg> rt_req;
527 :
528 12 : rtmsg &rt_message_hdr = rt_req.nl_req_hdr;
529 12 : std::memset(&rt_message_hdr, 0, sizeof(rt_message_hdr));
530 12 : rt_message_hdr.rtm_family = AF_INET6; // AF_INET/AF_INET6/AF_UNSPEC
531 12 : rt_message_hdr.rtm_table = RT_TABLE_MAIN;
532 12 : rt_message_hdr.rtm_protocol = RTPROT_STATIC;
533 12 : rt_message_hdr.rtm_scope = RT_SCOPE_UNIVERSE;
534 12 : rt_message_hdr.rtm_type = RTN_UNICAST;
535 12 : rt_message_hdr.rtm_dst_len = 128;
536 :
537 :
538 12 : nlmsghdr &nl_message_hdr = rt_req.nl_message_hdr;
539 12 : nl_message_hdr.nlmsg_type = RTM_NEWROUTE;
540 12 : nl_message_hdr.nlmsg_flags = // NLM_F_REPLACE is neccessary, since a
541 : NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE // route might be already
542 : | NLM_F_ACK; // in the table
543 :
544 12 : rt_req.msg_len = NLMSG_LENGTH(sizeof(rt_message_hdr));
545 :
546 12 : rt_req.insert_attr(RTA_DST, sizeof(addr6), &addr6);
547 12 : rt_req.insert_attr(RTA_OIF, sizeof(dev_idx), &dev_idx);
548 :
549 12 : msghdr* msg = rt_req.message(nl_sock.netlink_socket());
550 12 : int send_n = -1;
551 12 : if (msg) {
552 : // sending of a request to the kernel
553 12 : send_n = sendmsg(nl_sock.fd(), msg, 0);
554 12 : if (send_n <= 0) {
555 0 : LOG(ERROR, "Sendmsg failed,"
556 : "MetadataProxy::NetlinkAddInterfaceRoute,"
557 : " metadata_ipv6_netlink.cc, "
558 : "errno = " << errno << std::endl);
559 : }
560 : }
561 :
562 12 : aux::read_ack_response(msg, send_n, nl_sock, "NetlinkAddVhostNb");
563 12 : }
564 :
565 : //
566 : // END-OF-FILE
567 : //
568 :
|