NCDInterfaceMonitor.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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 <netinet/in.h>
  35. #include <linux/netlink.h>
  36. #include <linux/rtnetlink.h>
  37. #include <asm/types.h>
  38. #include <asm/types.h>
  39. #include <misc/debug.h>
  40. #include <misc/nonblocking.h>
  41. #include <base/BLog.h>
  42. #include <ncd/NCDInterfaceMonitor.h>
  43. #include <generated/blog_channel_NCDInterfaceMonitor.h>
  44. #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
  45. static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type);
  46. static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len);
  47. static void report_error (NCDInterfaceMonitor *o);
  48. static int send_next_dump_request (NCDInterfaceMonitor *o);
  49. static void netlink_fd_handler (NCDInterfaceMonitor *o, int events);
  50. static void process_buffer (NCDInterfaceMonitor *o);
  51. static void more_job_handler (NCDInterfaceMonitor *o);
  52. static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type)
  53. {
  54. struct {
  55. struct nlmsghdr nlh;
  56. struct rtgenmsg g;
  57. } req;
  58. memset(&req, 0, sizeof(req));
  59. req.nlh.nlmsg_len = sizeof(req);
  60. req.nlh.nlmsg_type = type;
  61. req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
  62. req.nlh.nlmsg_pid = 0;
  63. req.nlh.nlmsg_seq = seq;
  64. req.g.rtgen_family = family;
  65. int res = write(fd, &req, sizeof(req));
  66. if (res < 0) {
  67. BLog(BLOG_ERROR, "write failed");
  68. return 0;
  69. }
  70. if (res != sizeof(req)) {
  71. BLog(BLOG_ERROR, "write short");
  72. return 0;
  73. }
  74. return 1;
  75. }
  76. static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len)
  77. {
  78. for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
  79. uint8_t *attr = RTA_DATA(rta);
  80. int attr_len = RTA_PAYLOAD(rta);
  81. if (rta->rta_type == type) {
  82. *out_attr = attr;
  83. *out_attr_len = attr_len;
  84. return 1;
  85. }
  86. }
  87. return 0;
  88. }
  89. static void report_error (NCDInterfaceMonitor *o)
  90. {
  91. DEBUGERROR(&o->d_err, o->handler_error(o->user))
  92. }
  93. static int send_next_dump_request (NCDInterfaceMonitor *o)
  94. {
  95. ASSERT(o->dump_queue)
  96. if (o->dump_queue & NCDIFMONITOR_WATCH_LINK) {
  97. o->dump_queue &= ~NCDIFMONITOR_WATCH_LINK;
  98. return send_wilddump_request(o, o->netlink_fd, o->dump_seq, 0, RTM_GETLINK);
  99. }
  100. else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV4_ADDR) {
  101. o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV4_ADDR;
  102. return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET, RTM_GETADDR);
  103. }
  104. else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV6_ADDR) {
  105. o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV6_ADDR;
  106. return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET6, RTM_GETADDR);
  107. }
  108. ASSERT(0)
  109. return 0;
  110. }
  111. void netlink_fd_handler (NCDInterfaceMonitor *o, int events)
  112. {
  113. DebugObject_Access(&o->d_obj);
  114. ASSERT(o->have_bfd)
  115. // handler fd error
  116. if (o->buf_left >= 0) {
  117. BLog(BLOG_ERROR, "file descriptor error");
  118. goto fail;
  119. }
  120. // read from netlink fd
  121. int len = read(o->netlink_fd, o->buf.buf, sizeof(o->buf));
  122. if (len < 0) {
  123. BLog(BLOG_ERROR, "read failed");
  124. goto fail;
  125. }
  126. // stop receiving fd events
  127. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
  128. // set buffer
  129. o->buf_nh = &o->buf.nlh;
  130. o->buf_left = len;
  131. // process buffer
  132. process_buffer(o);
  133. return;
  134. fail:
  135. report_error(o);
  136. }
  137. void process_buffer (NCDInterfaceMonitor *o)
  138. {
  139. ASSERT(o->buf_left >= 0)
  140. ASSERT(o->have_bfd)
  141. int done = 0;
  142. for (; NLMSG_OK(o->buf_nh, o->buf_left); o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left)) {
  143. if (o->buf_nh->nlmsg_type == NLMSG_DONE) {
  144. done = 1;
  145. break;
  146. }
  147. struct nlmsghdr *buf = o->buf_nh;
  148. void *pl = NLMSG_DATA(buf);
  149. int pl_len = NLMSG_PAYLOAD(buf, 0);
  150. struct NCDInterfaceMonitor_event ev;
  151. switch (buf->nlmsg_type) {
  152. case RTM_NEWLINK: { // not RTM_DELLINK! who knows what these mean...
  153. if (pl_len < sizeof(struct ifinfomsg)) {
  154. BLog(BLOG_ERROR, "ifinfomsg too short");
  155. goto fail;
  156. }
  157. struct ifinfomsg *msg = pl;
  158. if (msg->ifi_index == o->ifindex && (o->watch_events & NCDIFMONITOR_WATCH_LINK)) {
  159. ev.event = (buf->nlmsg_type == RTM_NEWLINK && (msg->ifi_flags & IFF_RUNNING)) ? NCDIFMONITOR_EVENT_LINK_UP : NCDIFMONITOR_EVENT_LINK_DOWN;
  160. goto dispatch;
  161. }
  162. } break;
  163. case RTM_NEWADDR:
  164. case RTM_DELADDR: {
  165. if (pl_len < sizeof(struct ifaddrmsg)) {
  166. BLog(BLOG_ERROR, "ifaddrmsg too short");
  167. goto fail;
  168. }
  169. struct ifaddrmsg *msg = pl;
  170. void *addr;
  171. int addr_len;
  172. if (!get_attr(IFA_ADDRESS, IFA_RTA(msg), buf->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)), &addr, &addr_len)) {
  173. break;
  174. }
  175. if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET && (o->watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR)) {
  176. if (addr_len != 4 || msg->ifa_prefixlen > 32) {
  177. BLog(BLOG_ERROR, "bad ipv4 ifaddrmsg");
  178. goto fail;
  179. }
  180. ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED;
  181. ev.u.ipv4_addr.addr.addr = ((struct in_addr *)addr)->s_addr;
  182. ev.u.ipv4_addr.addr.prefix = msg->ifa_prefixlen;
  183. goto dispatch;
  184. }
  185. if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET6 && (o->watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR)) {
  186. if (addr_len != 16 || msg->ifa_prefixlen > 128) {
  187. BLog(BLOG_ERROR, "bad ipv6 ifaddrmsg");
  188. goto fail;
  189. }
  190. ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED;
  191. memcpy(ev.u.ipv6_addr.addr.addr.bytes, ((struct in6_addr *)addr)->s6_addr, 16);
  192. ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen;
  193. ev.u.ipv6_addr.addr_flags = 0;
  194. ev.u.ipv6_addr.scope = msg->ifa_scope;
  195. if (!(msg->ifa_flags & IFA_F_PERMANENT)) {
  196. ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC;
  197. }
  198. goto dispatch;
  199. }
  200. } break;
  201. }
  202. continue;
  203. dispatch:
  204. // move to next message
  205. o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left);
  206. // schedule more job
  207. BPending_Set(&o->more_job);
  208. // dispatch event
  209. o->handler(o->user, ev);
  210. return;
  211. }
  212. if (done) {
  213. if (o->dump_queue) {
  214. // increment dump request sequence number
  215. o->dump_seq++;
  216. // send next dump request
  217. if (!send_next_dump_request(o)) {
  218. goto fail;
  219. }
  220. }
  221. else if (o->event_netlink_fd >= 0) {
  222. // stop watching dump fd
  223. BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
  224. o->have_bfd = 0;
  225. // close dump fd, make event fd current
  226. close(o->netlink_fd);
  227. o->netlink_fd = o->event_netlink_fd;
  228. o->event_netlink_fd = -1;
  229. // start watching event fd
  230. BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
  231. if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
  232. BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
  233. goto fail;
  234. }
  235. o->have_bfd = 1;
  236. }
  237. }
  238. // set no buffer
  239. o->buf_left = -1;
  240. // continue receiving fd events
  241. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
  242. return;
  243. fail:
  244. report_error(o);
  245. }
  246. void more_job_handler (NCDInterfaceMonitor *o)
  247. {
  248. DebugObject_Access(&o->d_obj);
  249. ASSERT(o->buf_left >= 0)
  250. // process buffer
  251. process_buffer(o);
  252. return;
  253. }
  254. int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user,
  255. NCDInterfaceMonitor_handler handler,
  256. NCDInterfaceMonitor_handler_error handler_error)
  257. {
  258. ASSERT(watch_events)
  259. ASSERT((watch_events&~(NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR)) == 0)
  260. ASSERT(handler)
  261. ASSERT(handler_error)
  262. BNetwork_Assert();
  263. // init arguments
  264. o->ifindex = ifindex;
  265. o->watch_events = watch_events;
  266. o->reactor = reactor;
  267. o->user = user;
  268. o->handler = handler;
  269. o->handler_error = handler_error;
  270. // init dump netlink fd
  271. if ((o->netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
  272. BLog(BLOG_ERROR, "socket failed");
  273. goto fail0;
  274. }
  275. if (!badvpn_set_nonblocking(o->netlink_fd)) {
  276. BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
  277. goto fail1;
  278. }
  279. // init event netlink fd
  280. if ((o->event_netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
  281. BLog(BLOG_ERROR, "socket failed");
  282. goto fail1;
  283. }
  284. if (!badvpn_set_nonblocking(o->event_netlink_fd)) {
  285. BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
  286. goto fail2;
  287. }
  288. // build bind address
  289. struct sockaddr_nl sa;
  290. memset(&sa, 0, sizeof(sa));
  291. sa.nl_family = AF_NETLINK;
  292. sa.nl_groups = 0;
  293. if (watch_events & NCDIFMONITOR_WATCH_LINK) sa.nl_groups |= RTMGRP_LINK;
  294. if (watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR) sa.nl_groups |= RTMGRP_IPV4_IFADDR;
  295. if (watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR) sa.nl_groups |= RTMGRP_IPV6_IFADDR;
  296. // bind event netlink fd
  297. if (bind(o->event_netlink_fd, (void *)&sa, sizeof(sa)) < 0) {
  298. BLog(BLOG_ERROR, "bind failed");
  299. goto fail2;
  300. }
  301. // set dump state
  302. o->dump_queue = watch_events;
  303. o->dump_seq = 0;
  304. // init BFileDescriptor
  305. BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
  306. if (!BReactor_AddFileDescriptor(reactor, &o->bfd)) {
  307. BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
  308. goto fail2;
  309. }
  310. o->have_bfd = 1;
  311. // set nothing in buffer
  312. o->buf_left = -1;
  313. // init more job
  314. BPending_Init(&o->more_job, BReactor_PendingGroup(reactor), (BPending_handler)more_job_handler, o);
  315. // send first dump request
  316. if (!send_next_dump_request(o)) {
  317. goto fail3;
  318. }
  319. // wait for reading fd
  320. BReactor_SetFileDescriptorEvents(reactor, &o->bfd, BREACTOR_READ);
  321. DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
  322. DebugObject_Init(&o->d_obj);
  323. return 1;
  324. fail3:
  325. BPending_Free(&o->more_job);
  326. BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
  327. fail2:
  328. close(o->event_netlink_fd);
  329. fail1:
  330. close(o->netlink_fd);
  331. fail0:
  332. return 0;
  333. }
  334. void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o)
  335. {
  336. DebugObject_Free(&o->d_obj);
  337. DebugError_Free(&o->d_err);
  338. // free more job
  339. BPending_Free(&o->more_job);
  340. // free BFileDescriptor
  341. if (o->have_bfd) {
  342. BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
  343. }
  344. // close event fd, in case we're still dumping
  345. if (o->event_netlink_fd >= 0) {
  346. close(o->event_netlink_fd);
  347. }
  348. // close fd
  349. close(o->netlink_fd);
  350. }
  351. void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o)
  352. {
  353. DebugObject_Access(&o->d_obj);
  354. DebugError_AssertNoError(&o->d_err);
  355. ASSERT(o->have_bfd)
  356. if (o->buf_left >= 0) {
  357. BPending_Unset(&o->more_job);
  358. } else {
  359. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
  360. }
  361. }
  362. void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o)
  363. {
  364. DebugObject_Access(&o->d_obj);
  365. DebugError_AssertNoError(&o->d_err);
  366. ASSERT(o->have_bfd)
  367. if (o->buf_left >= 0) {
  368. BPending_Set(&o->more_job);
  369. } else {
  370. BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
  371. }
  372. }