Kea 1.5.0
iface_mgr_linux.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
21
22#include <config.h>
23
24#if defined(OS_LINUX)
25
26#include <asiolink/io_address.h>
27#include <dhcp/iface_mgr.h>
30#include <dhcp/pkt_filter_lpf.h>
33
34#include <boost/array.hpp>
35#include <boost/static_assert.hpp>
36
37#include <fcntl.h>
38#include <stdint.h>
39#include <net/if.h>
40#include <linux/rtnetlink.h>
41
42using namespace std;
43using namespace isc;
44using namespace isc::asiolink;
45using namespace isc::dhcp;
46using namespace isc::util::io::internal;
47
48BOOST_STATIC_ASSERT(IFLA_MAX>=IFA_MAX);
49
50namespace {
51
56class Netlink
57{
58public:
59
71 typedef vector<nlmsghdr*> NetlinkMessages;
72
86 typedef boost::array<struct rtattr*, IFLA_MAX + 1> RTattribPtrs;
87
88 Netlink() : fd_(-1), seq_(0), dump_(0) {
89 memset(&local_, 0, sizeof(struct sockaddr_nl));
90 memset(&peer_, 0, sizeof(struct sockaddr_nl));
91 }
92
93 ~Netlink() {
94 rtnl_close_socket();
95 }
96
97
98 void rtnl_open_socket();
99 void rtnl_send_request(int family, int type);
100 void rtnl_store_reply(NetlinkMessages& storage, const nlmsghdr* msg);
101 void parse_rtattr(RTattribPtrs& table, rtattr* rta, int len);
102 void ipaddrs_get(Iface& iface, NetlinkMessages& addr_info);
103 void rtnl_process_reply(NetlinkMessages& info);
104 void release_list(NetlinkMessages& messages);
105 void rtnl_close_socket();
106
107private:
108 int fd_; // Netlink file descriptor
109 sockaddr_nl local_; // Local addresses
110 sockaddr_nl peer_; // Remote address
111 uint32_t seq_; // Counter used for generating unique sequence numbers
112 uint32_t dump_; // Number of expected message response
113};
114
116const static size_t SNDBUF_SIZE = 32768;
117
119const static size_t RCVBUF_SIZE = 32768;
120
124void Netlink::rtnl_open_socket() {
125
126 fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
127 if (fd_ < 0) {
128 isc_throw(Unexpected, "Failed to create NETLINK socket.");
129 }
130
131 if (fcntl(fd_, F_SETFD, FD_CLOEXEC) < 0) {
132 isc_throw(Unexpected, "Failed to set close-on-exec in NETLINK socket.");
133 }
134
135 if (setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &SNDBUF_SIZE, sizeof(SNDBUF_SIZE)) < 0) {
136 isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
137 }
138
139 if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &RCVBUF_SIZE, sizeof(RCVBUF_SIZE)) < 0) {
140 isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
141 }
142
143 local_.nl_family = AF_NETLINK;
144 local_.nl_groups = 0;
145
146 if (::bind(fd_, convertSockAddr(&local_), sizeof(local_)) < 0) {
147 isc_throw(Unexpected, "Failed to bind netlink socket.");
148 }
149
150 socklen_t addr_len = sizeof(local_);
151 if (getsockname(fd_, convertSockAddr(&local_), &addr_len) < 0) {
152 isc_throw(Unexpected, "Getsockname for netlink socket failed.");
153 }
154
155 // just 2 sanity checks and we are done
156 if ( (addr_len != sizeof(local_)) ||
157 (local_.nl_family != AF_NETLINK) ) {
158 isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
159 }
160}
161
163void Netlink::rtnl_close_socket() {
164 if (fd_ != -1) {
165 close(fd_);
166 }
167 fd_ = -1;
168}
169
174void Netlink::rtnl_send_request(int family, int type) {
175 struct Req {
176 nlmsghdr netlink_header;
177 rtgenmsg generic;
178 };
179 Req req; // we need this type named for offsetof() used in assert
180 struct sockaddr_nl nladdr;
181
182 // do a sanity check. Verify that Req structure is aligned properly
183 BOOST_STATIC_ASSERT(sizeof(nlmsghdr) == offsetof(Req, generic));
184
185 memset(&nladdr, 0, sizeof(nladdr));
186 nladdr.nl_family = AF_NETLINK;
187
188 // According to netlink(7) manpage, mlmsg_seq must be set to a sequence
189 // number and is used to track messages. That is just a value that is
190 // opaque to kernel, and user-space code is supposed to use it to match
191 // incoming responses to sent requests. That is not really useful as we
192 // send a single request and get a single response at a time. However, we
193 // obey the man page suggestion and just set this to monotonically
194 // increasing numbers.
195 seq_++;
196
197 // This will be used to finding correct response (responses
198 // sent by kernel are supposed to have the same sequence number
199 // as the request we sent).
200 dump_ = seq_;
201
202 memset(&req, 0, sizeof(req));
203 req.netlink_header.nlmsg_len = sizeof(req);
204 req.netlink_header.nlmsg_type = type;
205 req.netlink_header.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
206 req.netlink_header.nlmsg_pid = 0;
207 req.netlink_header.nlmsg_seq = seq_;
208 req.generic.rtgen_family = family;
209
210 int status = sendto(fd_, static_cast<void*>(&req), sizeof(req), 0,
211 static_cast<struct sockaddr*>(static_cast<void*>(&nladdr)),
212 sizeof(nladdr));
213
214 if (status<0) {
215 isc_throw(Unexpected, "Failed to send " << sizeof(nladdr)
216 << " bytes over netlink socket.");
217 }
218}
219
228void Netlink::rtnl_store_reply(NetlinkMessages& storage, const struct nlmsghdr *msg)
229{
230 // we need to make a copy of this message. We really can't allocate
231 // nlmsghdr directly as it is only part of the structure. There are
232 // many message types with varying lengths and a common header.
233 struct nlmsghdr* copy = reinterpret_cast<struct nlmsghdr*>(new char[msg->nlmsg_len]);
234 memcpy(copy, msg, msg->nlmsg_len);
235
236 // push_back copies only pointer content, not the pointed-to object.
237 storage.push_back(copy);
238}
239
250void Netlink::parse_rtattr(RTattribPtrs& table, struct rtattr* rta, int len)
251{
252 std::fill(table.begin(), table.end(), static_cast<struct rtattr*>(NULL));
253 // RTA_OK and RTA_NEXT() are macros defined in linux/rtnetlink.h
254 // they are used to handle rtattributes. RTA_OK checks if the structure
255 // pointed by rta is reasonable and passes all sanity checks.
256 // RTA_NEXT() returns pointer to the next rtattr structure that
257 // immediately follows pointed rta structure. See aforementioned
258 // header for details.
259 while (RTA_OK(rta, len)) {
260 if (rta->rta_type < table.size()) {
261 table[rta->rta_type] = rta;
262 }
263 rta = RTA_NEXT(rta,len);
264 }
265 if (len) {
266 isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
267 }
268}
269
280void Netlink::ipaddrs_get(Iface& iface, NetlinkMessages& addr_info) {
281 uint8_t addr[V6ADDRESS_LEN];
282 RTattribPtrs rta_tb;
283
284 for (NetlinkMessages::const_iterator msg = addr_info.begin();
285 msg != addr_info.end(); ++msg) {
286 ifaddrmsg* ifa = static_cast<ifaddrmsg*>(NLMSG_DATA(*msg));
287
288 // These are not the addresses you are looking for
289 if (ifa->ifa_index != iface.getIndex()) {
290 continue;
291 }
292
293 if ((ifa->ifa_family == AF_INET6) || (ifa->ifa_family == AF_INET)) {
294 std::fill(rta_tb.begin(), rta_tb.end(), static_cast<rtattr*>(NULL));
295 parse_rtattr(rta_tb, IFA_RTA(ifa), (*msg)->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
296 if (!rta_tb[IFA_LOCAL]) {
297 rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
298 }
299 if (!rta_tb[IFA_ADDRESS]) {
300 rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
301 }
302
303 memcpy(addr, RTA_DATA(rta_tb[IFLA_ADDRESS]),
304 ifa->ifa_family==AF_INET?V4ADDRESS_LEN:V6ADDRESS_LEN);
305 IOAddress a = IOAddress::fromBytes(ifa->ifa_family, addr);
306 iface.addAddress(a);
307
309 }
310 }
311}
312
322void Netlink::rtnl_process_reply(NetlinkMessages& info) {
323 sockaddr_nl nladdr;
324 iovec iov;
325 msghdr msg;
326 memset(&msg, 0, sizeof(msghdr));
327 msg.msg_name = &nladdr;
328 msg.msg_namelen = sizeof(nladdr);
329 msg.msg_iov = &iov;
330 msg.msg_iovlen = 1;
331
332 char buf[RCVBUF_SIZE];
333
334 iov.iov_base = buf;
335 iov.iov_len = sizeof(buf);
336 while (true) {
337 int status = recvmsg(fd_, &msg, 0);
338
339 if (status < 0) {
340 if (errno == EINTR) {
341 continue;
342 }
343 isc_throw(Unexpected, "Error " << errno
344 << " while processing reply from netlink socket.");
345 }
346
347 if (status == 0) {
348 isc_throw(Unexpected, "EOF while reading netlink socket.");
349 }
350
351 nlmsghdr* header = static_cast<nlmsghdr*>(static_cast<void*>(buf));
352 while (NLMSG_OK(header, status)) {
353
354 // Received a message not addressed to our process, or not
355 // with a sequence number we are expecting. Ignore, and
356 // look at the next one.
357 if (nladdr.nl_pid != 0 ||
358 header->nlmsg_pid != local_.nl_pid ||
359 header->nlmsg_seq != dump_) {
360 header = NLMSG_NEXT(header, status);
361 continue;
362 }
363
364 if (header->nlmsg_type == NLMSG_DONE) {
365 // End of message.
366 return;
367 }
368
369 if (header->nlmsg_type == NLMSG_ERROR) {
370 nlmsgerr* err = static_cast<nlmsgerr*>(NLMSG_DATA(header));
371 if (header->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
372 // We are really out of luck here. We can't even say what is
373 // wrong as error message is truncated. D'oh.
374 isc_throw(Unexpected, "Netlink reply read failed.");
375 } else {
376 isc_throw(Unexpected, "Netlink reply read error " << -err->error);
377 }
378 // Never happens we throw before we reach here
379 return;
380 }
381
382 // store the data
383 rtnl_store_reply(info, header);
384
385 header = NLMSG_NEXT(header, status);
386 }
387 if (msg.msg_flags & MSG_TRUNC) {
388 isc_throw(Unexpected, "Message received over netlink truncated.");
389 }
390 if (status) {
391 isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
392 }
393 }
394}
395
399void Netlink::release_list(NetlinkMessages& messages) {
400 // let's free local copies of stored messages
401 for (NetlinkMessages::iterator msg = messages.begin(); msg != messages.end(); ++msg) {
402 delete[] (*msg);
403 }
404
405 // ang get rid of the message pointers as well
406 messages.clear();
407}
408
409} // end of anonymous namespace
410
411namespace isc {
412namespace dhcp {
413
418void IfaceMgr::detectIfaces() {
419 // Copies of netlink messages about links will be stored here.
420 Netlink::NetlinkMessages link_info;
421
422 // Copies of netlink messages about addresses will be stored here.
423 Netlink::NetlinkMessages addr_info;
424
425 // Socket descriptors and other rtnl-related parameters.
426 Netlink nl;
427
428 // Table with pointers to address attributes.
429 Netlink::RTattribPtrs attribs_table;
430 std::fill(attribs_table.begin(), attribs_table.end(),
431 static_cast<struct rtattr*>(NULL));
432
433 // Open socket
434 nl.rtnl_open_socket();
435
436 // Now we have open functional socket, let's use it!
437 // Ask for list of network interfaces...
438 nl.rtnl_send_request(AF_PACKET, RTM_GETLINK);
439
440 // Get reply and store it in link_info list:
441 // response is received as with any other socket - just a series
442 // of bytes. They are representing collection of netlink messages
443 // concatenated together. rtnl_process_reply will parse this
444 // buffer, copy each message to a newly allocated memory and
445 // store pointers to it in link_info. This allocated memory will
446 // be released later. See release_info(link_info) below.
447 nl.rtnl_process_reply(link_info);
448
449 // Now ask for list of addresses (AF_UNSPEC = of any family)
450 // Let's repeat, but this time ask for any addresses.
451 // That includes IPv4, IPv6 and any other address families that
452 // are happen to be supported by this system.
453 nl.rtnl_send_request(AF_UNSPEC, RTM_GETADDR);
454
455 // Get reply and store it in addr_info list.
456 // Again, we will allocate new memory and store messages in
457 // addr_info. It will be released later using release_info(addr_info).
458 nl.rtnl_process_reply(addr_info);
459
460 // Now build list with interface names
461 for (Netlink::NetlinkMessages::iterator msg = link_info.begin();
462 msg != link_info.end(); ++msg) {
463 // Required to display information about interface
464 struct ifinfomsg* interface_info = static_cast<ifinfomsg*>(NLMSG_DATA(*msg));
465 int len = (*msg)->nlmsg_len;
466 len -= NLMSG_LENGTH(sizeof(*interface_info));
467 nl.parse_rtattr(attribs_table, IFLA_RTA(interface_info), len);
468
469 // valgrind reports *possible* memory leak in the line below, but it is
470 // bogus. Nevertheless, the whole interface definition has been split
471 // into three separate steps for easier debugging.
472 const char* tmp = static_cast<const char*>(RTA_DATA(attribs_table[IFLA_IFNAME]));
473 string iface_name(tmp); // <--- bogus valgrind warning here
474 IfacePtr iface(new Iface(iface_name, interface_info->ifi_index));
475
476 iface->setHWType(interface_info->ifi_type);
477 iface->setFlags(interface_info->ifi_flags);
478
479 // Does interface have LL_ADDR?
480 if (attribs_table[IFLA_ADDRESS]) {
481 iface->setMac(static_cast<const uint8_t*>(RTA_DATA(attribs_table[IFLA_ADDRESS])),
482 RTA_PAYLOAD(attribs_table[IFLA_ADDRESS]));
483 }
484 else {
485 // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and
486 // try to dereference it in this manner
487 }
488
489 nl.ipaddrs_get(*iface, addr_info);
490 addInterface(iface);
491 }
492
493 nl.release_list(link_info);
494 nl.release_list(addr_info);
495}
496
503void Iface::setFlags(uint64_t flags) {
504 flags_ = flags;
505
506 flag_loopback_ = flags & IFF_LOOPBACK;
507 flag_up_ = flags & IFF_UP;
508 flag_running_ = flags & IFF_RUNNING;
509 flag_multicast_ = flags & IFF_MULTICAST;
510 flag_broadcast_ = flags & IFF_BROADCAST;
511}
512
513void
514IfaceMgr::setMatchingPacketFilter(const bool direct_response_desired) {
515 if (direct_response_desired) {
516 setPacketFilter(PktFilterPtr(new PktFilterLPF()));
517
518 } else {
519 setPacketFilter(PktFilterPtr(new PktFilterInet()));
520
521 }
522}
523
524bool
525IfaceMgr::openMulticastSocket(Iface& iface,
526 const isc::asiolink::IOAddress& addr,
527 const uint16_t port,
528 IfaceMgrErrorMsgCallback error_handler) {
529 // This variable will hold a descriptor of the socket bound to
530 // link-local address. It may be required for us to close this
531 // socket if an attempt to open and bind a socket to multicast
532 // address fails.
533 int sock;
534 try {
535 sock = openSocket(iface.getName(), addr, port,
536 iface.flag_multicast_);
537
538 } catch (const Exception& ex) {
539 IFACEMGR_ERROR(SocketConfigError, error_handler,
540 "Failed to open link-local socket on "
541 " interface " << iface.getName() << ": "
542 << ex.what());
543 return (false);
544
545 }
546
547 // In order to receive multicast traffic another socket is opened
548 // and bound to the multicast address.
549
556 if (iface.flag_multicast_) {
557 try {
558 openSocket(iface.getName(),
560 port);
561 } catch (const Exception& ex) {
562 // An attempt to open and bind a socket to multicast addres
563 // has failed. We have to close the socket we previously
564 // bound to link-local address - this is everything or
565 // nothing strategy.
566 iface.delSocket(sock);
567 IFACEMGR_ERROR(SocketConfigError, error_handler,
568 "Failed to open multicast socket on"
569 " interface " << iface.getName()
570 << ", reason: " << ex.what());
571 return (false);
572 }
573 }
574 // Both sockets have opened successfully.
575 return (true);
576}
577
578int
579IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
580 const bool join_multicast) {
581 // Assuming that packet filter is not NULL, because its modifier checks it.
582 SocketInfo info = packet_filter6_->openSocket(iface, addr, port,
583 join_multicast);
584 iface.addSocket(info);
585
586 return (info.sockfd_);
587}
588
589} // end of isc::dhcp namespace
590} // end of isc namespace
591
592#endif // if defined(LINUX)
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
Represents a single network interface.
Definition: iface_mgr.h:114
bool flag_multicast_
Flag specifies if selected interface is multicast capable.
Definition: iface_mgr.h:430
std::string getName() const
Returns interface name.
Definition: iface_mgr.h:216
void setFlags(uint64_t flags)
Sets flag_*_ fields based on bitmask value returned by OS.
void setMac(const uint8_t *mac, size_t macLen)
Sets MAC address of the interface.
Definition: iface_mgr.cc:141
bool delSocket(uint16_t sockfd)
Closes socket.
Definition: iface_mgr.cc:165
void addAddress(const isc::asiolink::IOAddress &addr)
Adds an address to an interface.
Definition: iface_mgr.cc:246
void addSocket(const SocketInfo &sock)
Adds socket descriptor to an interface.
Definition: iface_mgr.h:313
void setHWType(uint16_t type)
Sets up hardware type of the interface.
Definition: iface_mgr.h:221
uint16_t getIndex() const
Returns interface index.
Definition: iface_mgr.h:211
Packet handling class using AF_INET socket family.
Packet handling class using Linux Packet Filtering.
IfaceMgr exception thrown thrown when socket opening or configuration failed.
Definition: iface_mgr.h:59
#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS
Definition: dhcp6.h:302
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define IFACEMGR_ERROR(ex_type, handler, stream)
A macro which handles an error in IfaceMgr.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1114
boost::shared_ptr< PktFilter > PktFilterPtr
Pointer to a PktFilter object.
Definition: pkt_filter.h:134
boost::shared_ptr< Iface > IfacePtr
Definition: iface_mgr.h:457
boost::function< void(const std::string &errmsg)> IfaceMgrErrorMsgCallback
This type describes the callback function invoked when error occurs in the IfaceMgr.
Definition: iface_mgr.h:470
const struct sockaddr * convertSockAddr(const SAType *sa)
Definition: sockaddr_util.h:41
Defines the logger used by the top-level component of kea-dhcp-ddns.
int fd_
Holds information about socket.
Definition: socket_info.h:19
int sockfd_
IPv4 or IPv6.
Definition: socket_info.h:26