NCDInterfaceMonitor.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /**
  2. * @file NCDInterfaceMonitor.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #include <string.h>
  30. #include <unistd.h>
  31. #include <stdint.h>
  32. #include <sys/socket.h>
  33. #include <net/if.h>
  34. #include <linux/netlink.h>
  35. #include <linux/rtnetlink.h>
  36. #include <asm/types.h>
  37. #include <asm/types.h>
  38. #include <misc/debug.h>
  39. #include <misc/nonblocking.h>
  40. #include <base/BLog.h>
  41. #include <ncd/NCDInterfaceMonitor.h>
  42. #include <generated/blog_channel_NCDInterfaceMonitor.h>
  43. #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
  44. static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type);
  45. static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len);
  46. static void report_error (NCDInterfaceMonitor *o);
  47. static int send_next_dump_request (NCDInterfaceMonitor *o);
  48. static void netlink_fd_handler (NCDInterfaceMonitor *o, int events);
  49. static void process_buffer (NCDInterfaceMonitor *o);
  50. static void more_job_handler (NCDInterfaceMonitor *o);
  51. static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type)
  52. {
  53. struct {
  54. struct nlmsghdr nlh;
  55. struct rtgenmsg g;
  56. } req;
  57. memset(&req, 0, sizeof(req));
  58. req.nlh.nlmsg_len = sizeof(req);
  59. req.nlh.nlmsg_type = type;
  60. req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
  61. req.nlh.nlmsg_pid = 0;
  62. req.nlh.nlmsg_seq = seq;
  63. req.g.rtgen_family = family;
  64. int res = write(fd, &req, sizeof(req));
  65. if (res < 0) {
  66. BLog(BLOG_ERROR, "write failed");
  67. return 0;
  68. }
  69. if (res != sizeof(req)) {
  70. BLog(BLOG_ERROR, "write short");
  71. return 0;
  72. }
  73. return 1;
  74. }
  75. static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len)
  76. {
  77. for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
  78. uint8_t *attr = RTA_DATA(rta);
  79. int attr_len = RTA_PAYLOAD(rta);
  80. if (rta->rta_type == type) {
  81. *out_attr = attr;
  82. *out_attr_len = attr_len;
  83. return 1;
  84. }
  85. }
  86. return 0;
  87. }
  88. static void report_error (NCDInterfaceMonitor *o)
  89. {
  90. DEBUGERROR(&o->d_err, o->handler_error(o->user))
  91. }
  92. static int send_next_dump_request (NCDInterfaceMonitor *o)
  93. {
  94. ASSERT(o->dump_queue)
  95. if (o->dump_queue & NCDIFMONITOR_WATCH_LINK) {
  96. o->dump_queue &= ~NCDIFMONITOR_WATCH_LINK;
  97. return send_wilddump_request(o, o->netlink_fd, o->dump_seq, 0, RTM_GETLINK);
  98. }
  99. else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV4_ADDR) {
  100. o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV4_ADDR;
  101. return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET, RTM_GETADDR);
  102. }
  103. else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV6_ADDR) {
  104. o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV6_ADDR;
  105. return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET6, RTM_GETADDR);
  106. }
  107. ASSERT(0)
  108. return 0;
  109. }
  110. void netlink_fd_handler (NCDInterfaceMonitor *o, int events)
  111. {
  112. DebugObject_Access(&o->d_obj);
  113. ASSERT(o->have_bfd)
  114. // handler fd error
  115. if (o->buf_left >= 0) {
  116. BLog(BLOG_ERROR, "file descriptor error");
  117. goto fail;
  118. }
  119. // read from netlink fd
  120. int len = read(o->netlink_fd, o->buf.buf, sizeof(o->buf));
  121. if (len < 0) {
  122. BLog(BLOG_ERROR, "read failed");
  123. goto fail;
  124. }
  125. // stop receiving fd events
  126. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
  127. // set buffer
  128. o->buf_nh = &o->buf.nlh;
  129. o->buf_left = len;
  130. // process buffer
  131. process_buffer(o);
  132. return;
  133. fail:
  134. report_error(o);
  135. }
  136. void process_buffer (NCDInterfaceMonitor *o)
  137. {
  138. ASSERT(o->buf_left >= 0)
  139. ASSERT(o->have_bfd)
  140. int done = 0;
  141. for (; NLMSG_OK(o->buf_nh, o->buf_left); o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left)) {
  142. if (o->buf_nh->nlmsg_type == NLMSG_DONE) {
  143. done = 1;
  144. break;
  145. }
  146. struct nlmsghdr *buf = o->buf_nh;
  147. void *pl = NLMSG_DATA(buf);
  148. int pl_len = NLMSG_PAYLOAD(buf, 0);
  149. struct NCDInterfaceMonitor_event ev;
  150. switch (buf->nlmsg_type) {
  151. case RTM_NEWLINK: { // not RTM_DELLINK! who knows what these mean...
  152. if (pl_len < sizeof(struct ifinfomsg)) {
  153. BLog(BLOG_ERROR, "ifinfomsg too short");
  154. goto fail;
  155. }
  156. struct ifinfomsg *msg = pl;
  157. if (msg->ifi_index == o->ifindex && (o->watch_events & NCDIFMONITOR_WATCH_LINK)) {
  158. ev.event = (buf->nlmsg_type == RTM_NEWLINK && (msg->ifi_flags & IFF_RUNNING)) ? NCDIFMONITOR_EVENT_LINK_UP : NCDIFMONITOR_EVENT_LINK_DOWN;
  159. goto dispatch;
  160. }
  161. } break;
  162. case RTM_NEWADDR:
  163. case RTM_DELADDR: {
  164. if (pl_len < sizeof(struct ifaddrmsg)) {
  165. BLog(BLOG_ERROR, "ifaddrmsg too short");
  166. goto fail;
  167. }
  168. struct ifaddrmsg *msg = pl;
  169. void *addr;
  170. int addr_len;
  171. if (!get_attr(IFA_ADDRESS, IFA_RTA(msg), buf->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)), &addr, &addr_len)) {
  172. break;
  173. }
  174. if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET && (o->watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR)) {
  175. if (addr_len != 4 || msg->ifa_prefixlen > 32) {
  176. BLog(BLOG_ERROR, "bad ipv4 ifaddrmsg");
  177. goto fail;
  178. }
  179. ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED;
  180. ev.u.ipv4_addr.addr.addr = ((struct in_addr *)addr)->s_addr;
  181. ev.u.ipv4_addr.addr.prefix = msg->ifa_prefixlen;
  182. goto dispatch;
  183. }
  184. if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET6 && (o->watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR)) {
  185. if (addr_len != 16 || msg->ifa_prefixlen > 128) {
  186. BLog(BLOG_ERROR, "bad ipv6 ifaddrmsg");
  187. goto fail;
  188. }
  189. ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED;
  190. memcpy(ev.u.ipv6_addr.addr.addr, ((struct in6_addr *)addr)->s6_addr, 16);
  191. ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen;
  192. ev.u.ipv6_addr.addr.scope = msg->ifa_scope;
  193. ev.u.ipv6_addr.addr_flags = 0;
  194. if (!(msg->ifa_flags & IFA_F_PERMANENT)) {
  195. ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC;
  196. }
  197. goto dispatch;
  198. }
  199. } break;
  200. }
  201. continue;
  202. dispatch:
  203. // move to next message
  204. o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left);
  205. // schedule more job
  206. BPending_Set(&o->more_job);
  207. // dispatch event
  208. o->handler(o->user, ev);
  209. return;
  210. }
  211. if (done) {
  212. if (o->dump_queue) {
  213. // increment dump request sequence number
  214. o->dump_seq++;
  215. // send next dump request
  216. if (!send_next_dump_request(o)) {
  217. goto fail;
  218. }
  219. }
  220. else if (o->event_netlink_fd >= 0) {
  221. // stop watching dump fd
  222. BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
  223. o->have_bfd = 0;
  224. // close dump fd, make event fd current
  225. close(o->netlink_fd);
  226. o->netlink_fd = o->event_netlink_fd;
  227. o->event_netlink_fd = -1;
  228. // start watching event fd
  229. BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
  230. if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
  231. BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
  232. goto fail;
  233. }
  234. o->have_bfd = 1;
  235. }
  236. }
  237. // set no buffer
  238. o->buf_left = -1;
  239. // continue receiving fd events
  240. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
  241. return;
  242. fail:
  243. report_error(o);
  244. }
  245. void more_job_handler (NCDInterfaceMonitor *o)
  246. {
  247. DebugObject_Access(&o->d_obj);
  248. ASSERT(o->buf_left >= 0)
  249. // process buffer
  250. process_buffer(o);
  251. return;
  252. }
  253. int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user,
  254. NCDInterfaceMonitor_handler handler,
  255. NCDInterfaceMonitor_handler_error handler_error)
  256. {
  257. ASSERT(watch_events)
  258. ASSERT((watch_events&~(NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR)) == 0)
  259. ASSERT(handler)
  260. ASSERT(handler_error)
  261. BNetwork_Assert();
  262. // init arguments
  263. o->ifindex = ifindex;
  264. o->watch_events = watch_events;
  265. o->reactor = reactor;
  266. o->user = user;
  267. o->handler = handler;
  268. o->handler_error = handler_error;
  269. // init dump netlink fd
  270. if ((o->netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
  271. BLog(BLOG_ERROR, "socket failed");
  272. goto fail0;
  273. }
  274. if (!badvpn_set_nonblocking(o->netlink_fd)) {
  275. BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
  276. goto fail1;
  277. }
  278. // init event netlink fd
  279. if ((o->event_netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
  280. BLog(BLOG_ERROR, "socket failed");
  281. goto fail1;
  282. }
  283. if (!badvpn_set_nonblocking(o->event_netlink_fd)) {
  284. BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
  285. goto fail2;
  286. }
  287. // build bind address
  288. struct sockaddr_nl sa;
  289. memset(&sa, 0, sizeof(sa));
  290. sa.nl_family = AF_NETLINK;
  291. sa.nl_groups = 0;
  292. if (watch_events & NCDIFMONITOR_WATCH_LINK) sa.nl_groups |= RTMGRP_LINK;
  293. if (watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR) sa.nl_groups |= RTMGRP_IPV4_IFADDR;
  294. if (watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR) sa.nl_groups |= RTMGRP_IPV6_IFADDR;
  295. // bind event netlink fd
  296. if (bind(o->event_netlink_fd, (void *)&sa, sizeof(sa)) < 0) {
  297. BLog(BLOG_ERROR, "bind failed");
  298. goto fail2;
  299. }
  300. // set dump state
  301. o->dump_queue = watch_events;
  302. o->dump_seq = 0;
  303. // init BFileDescriptor
  304. BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
  305. if (!BReactor_AddFileDescriptor(reactor, &o->bfd)) {
  306. BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
  307. goto fail2;
  308. }
  309. o->have_bfd = 1;
  310. // set nothing in buffer
  311. o->buf_left = -1;
  312. // init more job
  313. BPending_Init(&o->more_job, BReactor_PendingGroup(reactor), (BPending_handler)more_job_handler, o);
  314. // send first dump request
  315. if (!send_next_dump_request(o)) {
  316. goto fail3;
  317. }
  318. // wait for reading fd
  319. BReactor_SetFileDescriptorEvents(reactor, &o->bfd, BREACTOR_READ);
  320. DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
  321. DebugObject_Init(&o->d_obj);
  322. return 1;
  323. fail3:
  324. BPending_Free(&o->more_job);
  325. BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
  326. fail2:
  327. close(o->event_netlink_fd);
  328. fail1:
  329. close(o->netlink_fd);
  330. fail0:
  331. return 0;
  332. }
  333. void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o)
  334. {
  335. DebugObject_Free(&o->d_obj);
  336. DebugError_Free(&o->d_err);
  337. // free more job
  338. BPending_Free(&o->more_job);
  339. // free BFileDescriptor
  340. if (o->have_bfd) {
  341. BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
  342. }
  343. // close event fd, in case we're still dumping
  344. if (o->event_netlink_fd >= 0) {
  345. close(o->event_netlink_fd);
  346. }
  347. // close fd
  348. close(o->netlink_fd);
  349. }
  350. void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o)
  351. {
  352. DebugObject_Access(&o->d_obj);
  353. DebugError_AssertNoError(&o->d_err);
  354. ASSERT(o->have_bfd)
  355. if (o->buf_left >= 0) {
  356. BPending_Unset(&o->more_job);
  357. } else {
  358. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
  359. }
  360. }
  361. void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o)
  362. {
  363. DebugObject_Access(&o->d_obj);
  364. DebugError_AssertNoError(&o->d_err);
  365. ASSERT(o->have_bfd)
  366. if (o->buf_left >= 0) {
  367. BPending_Set(&o->more_job);
  368. } else {
  369. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
  370. }
  371. }