interface.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
  3. * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License version 2.1
  7. * as published by the Free Software Foundation
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include <sys/socket.h>
  15. #include <sys/ioctl.h>
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <sys/utsname.h>
  19. #include <net/if.h>
  20. #include <linux/sockios.h>
  21. #include <arpa/inet.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <unistd.h>
  25. #include <stdio.h>
  26. #include <errno.h>
  27. #include <libubox/usock.h>
  28. #include <libubox/uloop.h>
  29. #include <libubox/avl-cmp.h>
  30. #include <libubox/utils.h>
  31. #include "interface.h"
  32. #include "util.h"
  33. #include "dns.h"
  34. #include "announce.h"
  35. int
  36. interface_send_packet(struct interface *iface, struct iovec *iov, int iov_len)
  37. {
  38. static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
  39. static struct sockaddr_in a = {
  40. .sin_family = AF_INET,
  41. .sin_port = htons(MCAST_PORT),
  42. };
  43. static struct msghdr m = {
  44. .msg_name = (struct sockaddr *) &a,
  45. .msg_namelen = sizeof(a),
  46. .msg_control = cmsg_data,
  47. .msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)),
  48. };
  49. struct in_pktinfo *pkti;
  50. struct cmsghdr *cmsg;
  51. int fd = iface->fd.fd;
  52. m.msg_iov = iov;
  53. m.msg_iovlen = iov_len;
  54. memset(cmsg_data, 0, sizeof(cmsg_data));
  55. cmsg = CMSG_FIRSTHDR(&m);
  56. cmsg->cmsg_len = m.msg_controllen;
  57. cmsg->cmsg_level = IPPROTO_IP;
  58. cmsg->cmsg_type = IP_PKTINFO;
  59. pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
  60. pkti->ipi_ifindex = iface->ifindex;
  61. a.sin_addr.s_addr = inet_addr(MCAST_ADDR);
  62. return sendmsg(fd, &m, 0);
  63. }
  64. static void interface_close(struct interface *iface)
  65. {
  66. if (iface->fd.fd < 0)
  67. return;
  68. announce_free(iface);
  69. uloop_fd_delete(&iface->fd);
  70. close(iface->fd.fd);
  71. iface->fd.fd = -1;
  72. }
  73. static void interface_free(struct interface *iface)
  74. {
  75. interface_close(iface);
  76. free(iface);
  77. }
  78. static void
  79. read_socket(struct uloop_fd *u, unsigned int events)
  80. {
  81. struct interface *iface = container_of(u, struct interface, fd);
  82. static uint8_t buffer[8 * 1024];
  83. int len;
  84. if (u->eof) {
  85. interface_close(iface);
  86. uloop_timeout_set(&iface->reconnect, 1000);
  87. return;
  88. }
  89. len = read(u->fd, buffer, sizeof(buffer));
  90. if (len < 1) {
  91. fprintf(stderr, "read failed: %s\n", strerror(errno));
  92. return;
  93. }
  94. dns_handle_packet(iface, buffer, len);
  95. }
  96. static int
  97. interface_socket_setup(struct interface *iface)
  98. {
  99. struct ip_mreqn mreq;
  100. uint8_t ttl = 255;
  101. int yes = 1;
  102. int no = 0;
  103. struct sockaddr_in sa = { 0 };
  104. int fd = iface->fd.fd;
  105. sa.sin_family = AF_INET;
  106. sa.sin_port = htons(MCAST_PORT);
  107. inet_pton(AF_INET, MCAST_ADDR, &sa.sin_addr);
  108. memset(&mreq, 0, sizeof(mreq));
  109. mreq.imr_address.s_addr = iface->v4_addr.s_addr;
  110. mreq.imr_multiaddr = sa.sin_addr;
  111. mreq.imr_ifindex = iface->ifindex;
  112. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
  113. fprintf(stderr, "ioctl failed: IP_MULTICAST_TTL\n");
  114. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
  115. fprintf(stderr, "ioctl failed: SO_REUSEADDR\n");
  116. /* Some network drivers have issues with dropping membership of
  117. * mcast groups when the iface is down, but don't allow rejoining
  118. * when it comes back up. This is an ugly workaround
  119. * -- this was copied from avahi --
  120. */
  121. setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
  122. if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
  123. fprintf(stderr, "failed to join multicast group: %s\n", strerror(errno));
  124. close(fd);
  125. fd = -1;
  126. return -1;
  127. }
  128. if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0)
  129. fprintf(stderr, "ioctl failed: IP_RECVTTL\n");
  130. if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0)
  131. fprintf(stderr, "ioctl failed: IP_PKTINFO\n");
  132. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no)) < 0)
  133. fprintf(stderr, "ioctl failed: IP_MULTICAST_LOOP\n");
  134. return 0;
  135. }
  136. static void
  137. reconnect_socket(struct uloop_timeout *timeout)
  138. {
  139. struct interface *iface = container_of(timeout, struct interface, reconnect);
  140. iface->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, MCAST_ADDR, "5353");
  141. if (iface->fd.fd < 0) {
  142. fprintf(stderr, "failed to add listener: %s\n", strerror(errno));
  143. goto retry;
  144. }
  145. if (interface_socket_setup(iface)) {
  146. iface->fd.fd = -1;
  147. goto retry;
  148. }
  149. uloop_fd_add(&iface->fd, ULOOP_READ);
  150. dns_send_question(iface, "_services._dns-sd._udp.local", TYPE_PTR);
  151. announce_init(iface);
  152. return;
  153. retry:
  154. uloop_timeout_set(timeout, 1000);
  155. }
  156. static void interface_start(struct interface *iface)
  157. {
  158. iface->fd.cb = read_socket;
  159. iface->reconnect.cb = reconnect_socket;
  160. uloop_timeout_set(&iface->reconnect, 100);
  161. }
  162. static void
  163. iface_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
  164. struct vlist_node *node_old)
  165. {
  166. struct interface *iface;
  167. if (node_old) {
  168. iface = container_of(node_old, struct interface, node);
  169. interface_free(iface);
  170. }
  171. if (node_new) {
  172. iface = container_of(node_new, struct interface, node);
  173. interface_start(iface);
  174. }
  175. }
  176. static int
  177. get_iface_ipv4(struct interface *iface)
  178. {
  179. struct sockaddr_in *sin;
  180. struct ifreq ir;
  181. int sock, ret = -1;
  182. sock = socket(AF_INET, SOCK_DGRAM, 0);
  183. if (sock < 0)
  184. return -1;
  185. memset(&ir, 0, sizeof(struct ifreq));
  186. strncpy(ir.ifr_name, iface->name, sizeof(ir.ifr_name));
  187. ret = ioctl(sock, SIOCGIFADDR, &ir);
  188. if (ret < 0)
  189. goto out;
  190. sin = (struct sockaddr_in *) &ir.ifr_addr;
  191. memcpy(&iface->v4_addr, &sin->sin_addr, sizeof(iface->v4_addr));
  192. out:
  193. close(sock);
  194. return ret;
  195. }
  196. int interface_add(const char *name)
  197. {
  198. struct interface *iface;
  199. char *name_buf;
  200. iface = calloc_a(sizeof(*iface),
  201. &name_buf, strlen(name) + 1);
  202. iface->name = strcpy(name_buf, name);
  203. iface->ifindex = if_nametoindex(name);
  204. iface->fd.fd = -1;
  205. if (iface->ifindex <= 0)
  206. goto error;
  207. if (get_iface_ipv4(iface))
  208. goto error;
  209. vlist_add(&interfaces, &iface->node, name);
  210. return 0;
  211. error:
  212. free(iface);
  213. return -1;
  214. }
  215. VLIST_TREE(interfaces, avl_strcmp, iface_update_cb, false, false);