iface_mgr_linux.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #if defined(OS_LINUX)
  16. #include <net/if.h>
  17. #include <linux/rtnetlink.h>
  18. #include <boost/array.hpp>
  19. #include <boost/static_assert.hpp>
  20. #include <dhcp/iface_mgr.h>
  21. #include <exceptions/exceptions.h>
  22. #include <asiolink/io_address.h>
  23. using namespace std;
  24. using namespace isc;
  25. using namespace isc::asiolink;
  26. using namespace isc::dhcp;
  27. namespace {
  28. /// @brief Holds pointers to netlink messages.
  29. ///
  30. /// netlink (a Linux interface for getting information about network
  31. /// interfaces) uses memory aliasing. Linux kernel returns a memory
  32. /// blob that should be interpreted as series of nlmessages. There
  33. /// are different nlmsg structures defined with varying size. They
  34. /// have one thing common - inital fields are laid out in the same
  35. /// way as nlmsghdr. Therefore different messages can be represented
  36. /// as nlmsghdr with followed variable number of bytes that are
  37. /// message-specific. The only reasonable way to represent this in
  38. /// C++ is to use vector of pointers to nlmsghdr (the common structure).
  39. typedef vector<nlmsghdr*> NetlinkMessages;
  40. /// @brief Holds pointers to interface or address attributes.
  41. ///
  42. /// Note that to get address info, a shorter (IFA_MAX rather than IFLA_MAX)
  43. /// table could be used, but we will use the bigger one anyway to
  44. /// make the code reusable.
  45. ///
  46. /// rtattr is a generic structure, similar to sockaddr. It is defined
  47. /// in linux/rtnetlink.h and shown here for documentation purposes only:
  48. ///
  49. /// struct rtattr {
  50. /// unsigned short<>rta_len;
  51. /// unsigned short<>rta_type;
  52. /// };
  53. ///
  54. typedef boost::array<struct rtattr*, IFLA_MAX+1> RTattribPtrs;
  55. BOOST_STATIC_ASSERT(IFLA_MAX>=IFA_MAX);
  56. /// @brief This structure defines context for netlink connection.
  57. struct rtnl_handle
  58. {
  59. rtnl_handle() :fd(-1), seq(0), dump(0) {
  60. memset(&local, 0, sizeof(struct sockaddr_nl));
  61. memset(&peer, 0, sizeof(struct sockaddr_nl));
  62. }
  63. ~rtnl_handle() {
  64. if (fd != -1) {
  65. close(fd);
  66. }
  67. }
  68. int fd; // netlink file descriptor
  69. sockaddr_nl local; // local and remote addresses
  70. sockaddr_nl peer;
  71. __u32 seq; // counter used for generating unique sequence numbers
  72. __u32 dump; // number of expected message response
  73. };
  74. const size_t sndbuf = 32768;
  75. const size_t rcvbuf = 32768;
  76. /// @brief Opens netlink socket and initializes handle structure.
  77. ///
  78. /// @exception Unexpected Thrown if socket configuration fails.
  79. ///
  80. /// @param handle Context will be stored in this structure.
  81. void rtnl_open_socket(struct rtnl_handle& handle) {
  82. // equivalent of rtnl_open
  83. handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  84. if (handle.fd < 0) {
  85. isc_throw(Unexpected, "Failed to create NETLINK socket.");
  86. }
  87. if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
  88. isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
  89. }
  90. if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
  91. isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
  92. }
  93. memset(&handle.local, 0, sizeof(handle.local));
  94. handle.local.nl_family = AF_NETLINK;
  95. handle.local.nl_groups = 0;
  96. if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
  97. isc_throw(Unexpected, "Failed to bind netlink socket.");
  98. }
  99. socklen_t addr_len = sizeof(handle.local);
  100. if (getsockname(handle.fd, (struct sockaddr*)&handle.local, &addr_len) < 0) {
  101. isc_throw(Unexpected, "Getsockname for netlink socket failed.");
  102. }
  103. // just 2 sanity checks and we are done
  104. if ( (addr_len != sizeof(handle.local)) ||
  105. (handle.local.nl_family != AF_NETLINK) ) {
  106. isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
  107. }
  108. }
  109. /// @brief Sends request over NETLINK socket.
  110. ///
  111. /// @param handle structure that contains necessary information about netlink
  112. /// @param family requested information family
  113. /// @param type request type (RTM_GETLINK or RTM_GETADDR)
  114. void rtnl_send_request(rtnl_handle& handle, int family, int type) {
  115. struct {
  116. nlmsghdr netlink_header;
  117. rtgenmsg generic;
  118. } req;
  119. struct sockaddr_nl nladdr;
  120. // This doesn't work as gcc is confused with coma appearing in
  121. // the expression and thinks that there are 2 parameters passed to
  122. // BOOST_STATIC_ASSERT macro, while it only takes one.
  123. // BOOST_STATIC_ASSERT(sizeof(nlmsghdr) == offsetof(req,generic) );
  124. memset(&nladdr, 0, sizeof(nladdr));
  125. nladdr.nl_family = AF_NETLINK;
  126. // according to netlink(7) manpage, mlmsg_seq must be set to
  127. // sequence number and is used to track messages. That is just a
  128. // value that is opaque to kernel and user-space code is supposed
  129. // to use it to match incoming responses to sent requests. That is
  130. // not really useful, as we send a single request and get a single
  131. // response at a time, but still it better to obey man page suggestion
  132. // and just set this to monotonically increasing numbers.
  133. handle.seq++;
  134. // this will be used to finding correct response (responses
  135. // sent by kernel are supposed to have the same sequence number
  136. // as the request we sent)
  137. handle.dump = handle.seq;
  138. memset(&req, 0, sizeof(req));
  139. req.netlink_header.nlmsg_len = sizeof(req);
  140. req.netlink_header.nlmsg_type = type;
  141. req.netlink_header.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
  142. req.netlink_header.nlmsg_pid = 0;
  143. req.netlink_header.nlmsg_seq = handle.seq;
  144. req.generic.rtgen_family = family;
  145. int status = sendto(handle.fd, static_cast<void*>(&req), sizeof(req), 0,
  146. static_cast<struct sockaddr*>(static_cast<void*>(&nladdr)),
  147. sizeof(nladdr));
  148. if (status<0) {
  149. isc_throw(Unexpected, "Failed to send " << sizeof(nladdr)
  150. << " bytes over netlink socket.");
  151. }
  152. }
  153. /// @brief Appends nlmsg to a storage.
  154. ///
  155. /// This method copies pointed nlmsg to a newly allocated memory
  156. /// and adds it to storage.
  157. ///
  158. /// @param storage a vector that holds netlink messages
  159. /// @param msg a netlink message to be added
  160. void rtnl_store_reply(NetlinkMessages& storage, const struct nlmsghdr *msg)
  161. {
  162. // we need to make a copy of this message. We really can't allocate
  163. // nlmsghdr directly as it is only part of the structure. There are
  164. // many message types with varying lengths and a common header.
  165. struct nlmsghdr* copy = reinterpret_cast<struct nlmsghdr*>(new char[msg->nlmsg_len]);
  166. memcpy(copy, msg, msg->nlmsg_len);
  167. // push_back copies only pointer content, not the pointed object
  168. storage.push_back(copy);
  169. }
  170. /// @brief Parses rtattr message.
  171. ///
  172. /// Some netlink messages represent address information. Such messages
  173. /// are concatenated collection of rtaddr structures. This function
  174. /// iterates over that list and stores pointers to those messages in
  175. /// flat array (table).
  176. ///
  177. /// @param table rtattr messages will be stored here
  178. /// @param rta pointer to first rtattr object
  179. /// @param len length (in bytes) of concatenated rtattr list.
  180. void parse_rtattr(RTattribPtrs& table, struct rtattr * rta, int len)
  181. {
  182. std::fill(table.begin(), table.end(), static_cast<struct rtattr*>(NULL));
  183. // RTA_OK and RTA_NEXT() are macros defined in linux/rtnetlink.h
  184. // they are used to handle rtattributes. RTA_OK checks if the structure
  185. // pointed by rta is reasonable and passes all sanity checks.
  186. // RTA_NEXT() returns pointer to the next rtattr structure that
  187. // immediately follows pointed rta structure. See aforementioned
  188. // header for details.
  189. while (RTA_OK(rta, len)) {
  190. if (rta->rta_type <= table.size()-1) {
  191. table[rta->rta_type] = rta;
  192. }
  193. rta = RTA_NEXT(rta,len);
  194. }
  195. if (len) {
  196. isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
  197. }
  198. }
  199. /// @brief Parses addr_info and appends appropriate addresses to Iface object.
  200. ///
  201. /// Netlink is a fine, but convoluted interface. It returns concatenated
  202. /// collection of netlink messages. Some of those messages convey information
  203. /// about addresses. Those messages are in fact appropriate header followed
  204. /// by concatenated lists of rtattr structures that define various pieces
  205. /// of address information.
  206. ///
  207. /// @param iface interface representation (addresses will be added here)
  208. /// @param addr_info collection of parsed netlink messages
  209. void ipaddrs_get(IfaceMgr::Iface& iface, NetlinkMessages& addr_info) {
  210. uint8_t addr[V6ADDRESS_LEN];
  211. RTattribPtrs rta_tb;
  212. for (NetlinkMessages::const_iterator msg = addr_info.begin();
  213. msg != addr_info.end(); ++msg) {
  214. ifaddrmsg* ifa = static_cast<ifaddrmsg*>(NLMSG_DATA(*msg));
  215. // these are not the addresses you are looking for
  216. if (ifa->ifa_index != iface.getIndex()) {
  217. continue;
  218. }
  219. if ((ifa->ifa_family == AF_INET6) || (ifa->ifa_family == AF_INET)) {
  220. std::fill(rta_tb.begin(), rta_tb.end(), static_cast<rtattr*>(NULL));
  221. parse_rtattr(rta_tb, IFA_RTA(ifa), (*msg)->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
  222. if (!rta_tb[IFA_LOCAL]) {
  223. rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
  224. }
  225. if (!rta_tb[IFA_ADDRESS]) {
  226. rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
  227. }
  228. memcpy(addr, RTA_DATA(rta_tb[IFLA_ADDRESS]),
  229. ifa->ifa_family==AF_INET?V4ADDRESS_LEN:V6ADDRESS_LEN);
  230. IOAddress a = IOAddress::from_bytes(ifa->ifa_family, addr);
  231. iface.addAddress(a);
  232. /// TODO: Read lifetimes of configured IPv6 addresses
  233. }
  234. }
  235. }
  236. /// @brief Processes reply received over netlink socket.
  237. ///
  238. /// This method parses received buffer (a collection of concatenated
  239. /// netlink messages), copies each received message to newly allocated
  240. /// memory and stores pointers to it in info container.
  241. ///
  242. /// Make sure to release this memory, e.g. using release_info() function.
  243. ///
  244. /// @param context netlink parameters
  245. /// @param info received netlink messages will be stored here
  246. void rtnl_process_reply(const rtnl_handle& handle, NetlinkMessages& info) {
  247. sockaddr_nl nladdr;
  248. iovec iov;
  249. msghdr msg;
  250. memset(&msg, 0, sizeof(msghdr));
  251. msg.msg_name = &nladdr;
  252. msg.msg_namelen = sizeof(nladdr);
  253. msg.msg_iov = &iov;
  254. msg.msg_iovlen = 1;
  255. char buf[rcvbuf];
  256. iov.iov_base = buf;
  257. iov.iov_len = sizeof(buf);
  258. while (true) {
  259. int status = recvmsg(handle.fd, &msg, 0);
  260. if (status < 0) {
  261. if (errno == EINTR) {
  262. continue;
  263. }
  264. isc_throw(Unexpected, "Error " << errno
  265. << " while processing reply from netlink socket.");
  266. }
  267. if (status == 0) {
  268. isc_throw(Unexpected, "EOF while reading netlink socket.");
  269. }
  270. nlmsghdr* header = static_cast<nlmsghdr*>(static_cast<void*>(buf));
  271. while (NLMSG_OK(header, status)) {
  272. // Received a message not addressed to our process, or not
  273. // with a sequence number we are expecting. Ignore, and
  274. // look at the next one.
  275. if (nladdr.nl_pid != 0 ||
  276. header->nlmsg_pid != handle.local.nl_pid ||
  277. header->nlmsg_seq != handle.dump) {
  278. header = NLMSG_NEXT(header, status);
  279. continue;
  280. }
  281. if (header->nlmsg_type == NLMSG_DONE) {
  282. // End of message.
  283. return;
  284. }
  285. if (header->nlmsg_type == NLMSG_ERROR) {
  286. nlmsgerr* err = static_cast<nlmsgerr*>(NLMSG_DATA(header));
  287. if (header->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
  288. // We are really out of luck here. We can't even say what is
  289. // wrong as error message is truncated. D'oh.
  290. isc_throw(Unexpected, "Netlink reply read failed.");
  291. } else {
  292. isc_throw(Unexpected, "Netlink reply read error " << -err->error);
  293. }
  294. // never happens we throw before we reach here
  295. return;
  296. }
  297. // store the data
  298. rtnl_store_reply(info, header);
  299. header = NLMSG_NEXT(header, status);
  300. }
  301. if (msg.msg_flags & MSG_TRUNC) {
  302. isc_throw(Unexpected, "Message received over netlink truncated.");
  303. }
  304. if (status) {
  305. isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
  306. }
  307. }
  308. }
  309. /// @brief releases nlmsg structure
  310. ///
  311. /// @param messages first element of the list to be released
  312. void release_list(NetlinkMessages& messages) {
  313. // let's free local copies of stored messages
  314. for (NetlinkMessages::iterator msg = messages.begin(); msg != messages.end(); ++msg) {
  315. delete (*msg);
  316. }
  317. // ang get rid of the message pointers as well
  318. messages.clear();
  319. }
  320. } // end of anonymous namespace
  321. namespace isc {
  322. namespace dhcp {
  323. /// @brief Detect available interfaces on Linux systesm.
  324. ///
  325. /// For detailed information about netlink interface, please refer to
  326. /// http://en.wikipedia.org/wiki/Netlink and RFC3549. Following
  327. /// comments in the core is an overview on how netlink interface is
  328. /// used here. Please note that this interface is very robust and
  329. /// allows many operations: add/get/set/delete links, addresses,
  330. /// routes, queuing, manipulate traffic classes, manipulate
  331. /// neithborhood tables and even do something with address
  332. /// labels. Getting list of interfaces with addresses configured on it
  333. /// is just a small subset of all possible actions.
  334. void IfaceMgr::detectIfaces() {
  335. cout << "Linux: detecting interfaces." << endl;
  336. // Copies of netlink messages about links will be stored here.
  337. NetlinkMessages link_info;
  338. // Copies of netlink messages about addresses will be stored here.
  339. NetlinkMessages addr_info;
  340. // Socket descriptors and other rtnl-related parameters.
  341. struct rtnl_handle handle;
  342. RTattribPtrs attribs_table; // table with pointers to address attributes
  343. std::fill(attribs_table.begin(), attribs_table.end(),
  344. static_cast<struct rtattr*>(NULL));
  345. // open socket
  346. rtnl_open_socket(handle);
  347. // now we have open functional socket, let's use it!
  348. // ask for list of network interfaces...
  349. rtnl_send_request(handle, AF_PACKET, RTM_GETLINK);
  350. // Get reply and store it in link_info list:
  351. // response is received as with any other socket - just a series
  352. // of bytes. They are representing collection of netlink messages
  353. // concatenated together. rtnl_process_reply will parse this
  354. // buffer, copy each message to a newly allocated memory and
  355. // store pointers to it in link_info. This allocated memory will
  356. // be released later. See release_info(link_info) below.
  357. rtnl_process_reply(handle, link_info);
  358. // Now ask for list of addresses (AF_UNSPEC = of any family)
  359. // Let's repeat, but this time ask for any addresses.
  360. // That includes IPv4, IPv6 and any other address families that
  361. // are happen to be supported by this system.
  362. rtnl_send_request(handle, AF_UNSPEC, RTM_GETADDR);
  363. // Get reply and store it in addr_info list.
  364. // Again, we will allocate new memory and store messages in
  365. // addr_info. It will be released later using release_info(addr_info).
  366. rtnl_process_reply(handle, addr_info);
  367. // Now build list with interface names
  368. for (NetlinkMessages::iterator msg = link_info.begin(); msg != link_info.end(); ++msg) {
  369. // required to display information about interface
  370. struct ifinfomsg* interface_info = static_cast<ifinfomsg*>(NLMSG_DATA(*msg));
  371. int len = (*msg)->nlmsg_len;
  372. len -= NLMSG_LENGTH(sizeof(*interface_info));
  373. parse_rtattr(attribs_table, IFLA_RTA(interface_info), len);
  374. // valgrind reports *possible* memory leak in the line below,
  375. // but I do believe that this report is bogus. Nevertheless,
  376. // I've split the whole interface definition into three
  377. // separate steps for easier debugging.
  378. const char* tmp = static_cast<const char*>(RTA_DATA(attribs_table[IFLA_IFNAME]));
  379. string iface_name(tmp); // <--- (probably bogus) valgrind warning here
  380. Iface iface = Iface(iface_name, interface_info->ifi_index);
  381. iface.setHWType(interface_info->ifi_type);
  382. iface.setFlags(interface_info->ifi_flags);
  383. // Does inetface has LL_ADDR?
  384. if (attribs_table[IFLA_ADDRESS]) {
  385. iface.setMac(static_cast<const uint8_t*>(RTA_DATA(attribs_table[IFLA_ADDRESS])),
  386. RTA_PAYLOAD(attribs_table[IFLA_ADDRESS]));
  387. }
  388. else {
  389. // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and try to
  390. // dereference it in this manner
  391. }
  392. ipaddrs_get(iface, addr_info);
  393. ifaces_.push_back(iface);
  394. }
  395. release_list(link_info);
  396. release_list(addr_info);
  397. printIfaces();
  398. }
  399. /// @brief sets flag_*_ fields.
  400. ///
  401. /// This implementation is OS-specific as bits have different meaning
  402. /// on different OSes.
  403. ///
  404. /// @param flags flags bitfield read from OS
  405. void IfaceMgr::Iface::setFlags(uint32_t flags) {
  406. flags_ = flags;
  407. flag_loopback_ = flags & IFF_LOOPBACK;
  408. flag_up_ = flags & IFF_UP;
  409. flag_running_ = flags & IFF_RUNNING;
  410. flag_multicast_ = flags & IFF_MULTICAST;
  411. flag_broadcast_ = flags & IFF_BROADCAST;
  412. }
  413. } // end of isc::dhcp namespace
  414. } // end of isc namespace
  415. #endif // if defined(LINUX)