iface_mgr_linux.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Copyright (C) 2011 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 <dhcp/iface_mgr.h>
  17. #include <exceptions/exceptions.h>
  18. #include <net/if.h>
  19. #include <linux/rtnetlink.h>
  20. using namespace std;
  21. using namespace isc;
  22. using namespace isc::asiolink;
  23. using namespace isc::dhcp;
  24. /// This is a structure that defines context for netlink connection.
  25. struct rtnl_handle
  26. {
  27. int fd;
  28. struct sockaddr_nl local;
  29. struct sockaddr_nl peer;
  30. __u32 seq;
  31. __u32 dump;
  32. };
  33. struct nlmsg_list
  34. {
  35. struct nlmsg_list *next;
  36. struct nlmsghdr h;
  37. };
  38. const int sndbuf = 32768;
  39. const int rcvbuf = 32768;
  40. namespace isc {
  41. /// @brief Opens netlink socket and initializes handle structure.
  42. ///
  43. /// @exception Unexpected Thrown if socket configuration fails.
  44. ///
  45. /// @param handle Context will be stored in this structure.
  46. void rtnl_open_socket(struct rtnl_handle& handle) {
  47. // equivalent of rtnl_open
  48. handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  49. if (handle.fd < 0) {
  50. isc_throw(Unexpected, "Failed to create NETLINK socket.");
  51. }
  52. if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
  53. isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
  54. }
  55. if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
  56. isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
  57. }
  58. memset(&handle.local, 0, sizeof(handle.local));
  59. handle.local.nl_family = AF_NETLINK;
  60. handle.local.nl_groups = 0;
  61. if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
  62. isc_throw(Unexpected, "Failed to bind netlink socket.");
  63. }
  64. socklen_t addr_len = sizeof(handle.local);
  65. if (getsockname(handle.fd, (struct sockaddr*)&handle.local, &addr_len) < 0) {
  66. isc_throw(Unexpected, "Getsockname for netlink socket failed.");
  67. }
  68. // just 2 sanity checks and we are done
  69. if ( (addr_len != sizeof(handle.local)) ||
  70. (handle.local.nl_family != AF_NETLINK) ) {
  71. isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
  72. }
  73. }
  74. /// @brief Sends request over NETLINK socket.
  75. ///
  76. /// @param handle context structure
  77. /// @param family requested information family
  78. /// @param type request type (RTM_GETLINK or RTM_GETADDR)
  79. void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
  80. struct {
  81. struct nlmsghdr nlh;
  82. struct rtgenmsg g;
  83. } req;
  84. struct sockaddr_nl nladdr;
  85. memset(&nladdr, 0, sizeof(nladdr));
  86. nladdr.nl_family = AF_NETLINK;
  87. memset(&req, 0, sizeof(req));
  88. req.nlh.nlmsg_len = sizeof(req);
  89. req.nlh.nlmsg_type = type;
  90. req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
  91. req.nlh.nlmsg_pid = 0;
  92. req.nlh.nlmsg_seq = handle.dump = ++handle.seq;
  93. req.g.rtgen_family = family;
  94. int status = sendto(handle.fd, (void*)&req, sizeof(req), 0,
  95. (struct sockaddr*)&nladdr, sizeof(nladdr));
  96. if (status<0) {
  97. isc_throw(Unexpected, "Failed to send " << sizeof(nladdr) << " bytes over netlink socket.");
  98. }
  99. }
  100. /// @brief Appends nlmsg to a list
  101. ///
  102. /// @param n a message to be added
  103. /// @param link_info a list
  104. void rtnl_store_reply(struct nlmsghdr *n, struct nlmsg_list** link_info)
  105. {
  106. struct nlmsg_list *h;
  107. struct nlmsg_list **lp;
  108. h = (nlmsg_list*)malloc(n->nlmsg_len+sizeof(void*));
  109. if (h == NULL) {
  110. isc_throw(Unexpected, "Failed to allocate " << n->nlmsg_len+sizeof(void*)
  111. << " bytes.");
  112. }
  113. memcpy(&h->h, n, n->nlmsg_len);
  114. h->next = NULL;
  115. for (lp = link_info; *lp; lp = &(*lp)->next) /* NOTHING */;
  116. *lp = h;
  117. }
  118. void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
  119. {
  120. memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
  121. while (RTA_OK(rta, len)) {
  122. if (rta->rta_type <= max)
  123. tb[rta->rta_type] = rta;
  124. rta = RTA_NEXT(rta,len);
  125. }
  126. if (len) {
  127. isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
  128. }
  129. }
  130. void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
  131. uint8_t addr[16];
  132. struct rtattr * rta_tb[IFA_MAX+1];
  133. for ( ;addr_info ; addr_info = addr_info->next) {
  134. struct nlmsghdr *n = &addr_info->h;
  135. struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
  136. // these are not the addresses you are looking for
  137. if ( ifa->ifa_index != iface.getIndex()) {
  138. continue;
  139. }
  140. if ( ifa->ifa_family == AF_INET6 ) {
  141. memset(rta_tb, 0, sizeof(rta_tb));
  142. parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
  143. if (!rta_tb[IFA_LOCAL])
  144. rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
  145. if (!rta_tb[IFA_ADDRESS])
  146. rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
  147. memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),16);
  148. IOAddress a = IOAddress::from_bytes(AF_INET6, addr);
  149. iface.addAddress(a);
  150. /// TODO: Read lifetimes of configured addresses
  151. }
  152. if ( ifa->ifa_family == AF_INET ) {
  153. memset(rta_tb, 0, sizeof(rta_tb));
  154. parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
  155. if (!rta_tb[IFA_LOCAL])
  156. rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
  157. if (!rta_tb[IFA_ADDRESS])
  158. rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
  159. memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),4);
  160. IOAddress a = IOAddress::from_bytes(AF_INET, addr);
  161. iface.addAddress(a);
  162. }
  163. }
  164. }
  165. void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
  166. struct sockaddr_nl nladdr;
  167. struct iovec iov;
  168. struct msghdr msg;
  169. memset(&msg, 0, sizeof(struct msghdr));
  170. msg.msg_name = &nladdr;
  171. msg.msg_namelen = sizeof(nladdr);
  172. msg.msg_iov = &iov;
  173. msg.msg_iovlen = 1;
  174. char buf[rcvbuf];
  175. iov.iov_base = buf;
  176. while (1) {
  177. int status;
  178. struct nlmsghdr *h;
  179. iov.iov_len = sizeof(buf);
  180. status = recvmsg(rth.fd, &msg, 0);
  181. if (status < 0) {
  182. if (errno == EINTR)
  183. continue;
  184. isc_throw(Unexpected, "Overrun while processing reply from netlink socket.");
  185. }
  186. if (status == 0) {
  187. isc_throw(Unexpected, "EOF while reading netlink socket.");
  188. }
  189. h = (struct nlmsghdr*)buf;
  190. while (NLMSG_OK(h, status)) {
  191. // why we received this anyway?
  192. if (nladdr.nl_pid != 0 ||
  193. h->nlmsg_pid != rth.local.nl_pid ||
  194. h->nlmsg_seq != rth.dump) {
  195. h = NLMSG_NEXT(h, status);
  196. continue;
  197. }
  198. if (h->nlmsg_type == NLMSG_DONE) {
  199. // end of message
  200. return;
  201. }
  202. if (h->nlmsg_type == NLMSG_ERROR) {
  203. struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
  204. if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
  205. // we are really out of luck here. We can't even say what is
  206. // wrong as error message is truncated. D'oh.
  207. isc_throw(Unexpected, "Netlink reply read failed.");
  208. } else {
  209. isc_throw(Unexpected, "Netlink reply read error " << -err->error);
  210. }
  211. // never happens we throw before we reach here
  212. return;
  213. }
  214. // store the data
  215. rtnl_store_reply(h, &info);
  216. h = NLMSG_NEXT(h, status);
  217. }
  218. if (msg.msg_flags & MSG_TRUNC) {
  219. isc_throw(Unexpected, "Message received over netlink truncated.");
  220. }
  221. if (status) {
  222. isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
  223. }
  224. }
  225. }
  226. /// @brief releases nlmsg list
  227. ///
  228. /// @param head first element of the list to be released
  229. void release_list(struct nlmsg_list *head) {
  230. struct nlmsg_list *tmp;
  231. while (head) {
  232. tmp = head->next;
  233. free(head);
  234. head = tmp;
  235. }
  236. }
  237. void IfaceMgr::detectIfaces() {
  238. cout << "Linux: detecting interfaces." << endl;
  239. struct nlmsg_list* link_info = NULL; // link info list
  240. struct nlmsg_list* addr_info = NULL; // address info list
  241. struct nlmsg_list* l = NULL;
  242. struct rtnl_handle rth;
  243. // required to display information about interface
  244. struct ifinfomsg* ifi = NULL;
  245. struct rtattr* tb[IFLA_MAX+1];
  246. int len = 0;
  247. memset(tb, 0, sizeof(tb));
  248. memset(&rth,0, sizeof(rth));
  249. // open socket
  250. rtnl_open_socket(rth);
  251. // now we have open functional socket, let's use it!
  252. // ask for list of interface...
  253. rtnl_send_request(rth, AF_PACKET, RTM_GETLINK);
  254. // Get reply and store it in link_info list.
  255. rtnl_process_reply(rth, link_info);
  256. // Now ask for list of addresses (AF_UNSPEC = of any family)
  257. rtnl_send_request(rth, AF_UNSPEC, RTM_GETADDR);
  258. // Get reply and store it in addr_info list.
  259. rtnl_process_reply(rth, addr_info);
  260. // Now build list with interface names
  261. for (l=link_info; l; l = l->next) {
  262. ifi = (ifinfomsg*)NLMSG_DATA(&l->h);
  263. len = (&l->h)->nlmsg_len;
  264. len -= NLMSG_LENGTH(sizeof(*ifi));
  265. parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
  266. Iface* iface = new Iface(string( (char*)RTA_DATA(tb[IFLA_IFNAME])), ifi->ifi_index);
  267. iface->hardware_type_ = ifi->ifi_type;
  268. iface->setFlags(ifi->ifi_flags);
  269. iface->mac_len_ = 0;
  270. memset(iface->mac_, 0, IfaceMgr::MAX_MAC_LEN);
  271. // Does inetface has LL_ADDR?
  272. if (tb[IFLA_ADDRESS]) {
  273. iface->mac_len_ = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
  274. if (iface->mac_len_ > IfaceMgr::MAX_MAC_LEN) {
  275. iface->mac_len_ = 0;
  276. isc_throw(Unexpected, "Interface " << iface->getFullName()
  277. << " was detected to have link address of length " << RTA_PAYLOAD(tb[IFLA_ADDRESS])
  278. << ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
  279. }
  280. memcpy(iface->mac_, RTA_DATA(tb[IFLA_ADDRESS]), iface->mac_len_);
  281. }
  282. else {
  283. // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and try to
  284. // dereference it in this manner
  285. // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
  286. iface->mac_len_ = 0;
  287. }
  288. ipaddrs_get(*iface, addr_info);
  289. ifaces_.push_back(*iface);
  290. }
  291. release_list(link_info);
  292. release_list(addr_info);
  293. printIfaces();
  294. }
  295. /// @brief sets flag_*_ fields.
  296. ///
  297. /// This implementation is OS-specific as bits have different meaning
  298. /// on different OSes.
  299. ///
  300. /// @param flags flags bitfield read from OS
  301. void IfaceMgr::Iface::setFlags(uint32_t flags) {
  302. flags_ = flags;
  303. flag_loopback_ = flags & IFF_LOOPBACK;
  304. flag_up_ = flags & IFF_UP;
  305. flag_running_ = flags & IFF_RUNNING;
  306. flag_multicast_ = flags & IFF_MULTICAST;
  307. flag_broadcast_ = flags & IFF_BROADCAST;
  308. }
  309. }
  310. #endif // if defined(LINUX)