pim_ssmpingd.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. PIM for Quagga
  3. Copyright (C) 2008 Everton da Silva Marques
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; see the file COPYING; if not, write to the
  14. Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
  15. MA 02110-1301 USA
  16. $QuaggaId: $Format:%an, %ai, %h$ $
  17. */
  18. #include <zebra.h>
  19. #include "if.h"
  20. #include "log.h"
  21. #include "memory.h"
  22. #include "pim_ssmpingd.h"
  23. #include "pim_time.h"
  24. #include "pim_sock.h"
  25. #include "pim_str.h"
  26. #include "pimd.h"
  27. static void ssmpingd_read_on(struct ssmpingd_sock *ss);
  28. void pim_ssmpingd_init()
  29. {
  30. zassert(!qpim_ssmpingd_list);
  31. }
  32. void pim_ssmpingd_destroy()
  33. {
  34. if (qpim_ssmpingd_list) {
  35. list_free(qpim_ssmpingd_list);
  36. qpim_ssmpingd_list = 0;
  37. }
  38. }
  39. static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
  40. {
  41. struct listnode *node;
  42. struct ssmpingd_sock *ss;
  43. if (!qpim_ssmpingd_list)
  44. return 0;
  45. for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
  46. if (source_addr.s_addr == ss->source_addr.s_addr)
  47. return ss;
  48. return 0;
  49. }
  50. static void ssmpingd_free(struct ssmpingd_sock *ss)
  51. {
  52. XFREE(MTYPE_PIM_SSMPINGD, ss);
  53. }
  54. static int ssmpingd_socket(struct in_addr addr, int mttl)
  55. {
  56. int fd;
  57. fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  58. if (fd < 0) {
  59. zlog_err("%s: could not create socket: errno=%d: %s",
  60. __PRETTY_FUNCTION__, errno, safe_strerror(errno));
  61. return -1;
  62. }
  63. /* Needed to obtain destination address from recvmsg() */
  64. {
  65. #if defined(HAVE_IP_PKTINFO)
  66. /* Linux IP_PKTINFO */
  67. int opt = 1;
  68. if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
  69. zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
  70. fd, errno, safe_strerror(errno));
  71. }
  72. #elif defined(HAVE_IP_RECVDSTADDR)
  73. /* BSD IP_RECVDSTADDR */
  74. int opt = 1;
  75. if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
  76. zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
  77. fd, errno, safe_strerror(errno));
  78. }
  79. #else
  80. zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
  81. __FILE__, __PRETTY_FUNCTION__);
  82. close(fd);
  83. return -1;
  84. #endif
  85. }
  86. {
  87. int reuse = 1;
  88. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
  89. (void *) &reuse, sizeof(reuse))) {
  90. zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
  91. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  92. close(fd);
  93. return -1;
  94. }
  95. }
  96. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
  97. (void *) &mttl, sizeof(mttl))) {
  98. zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
  99. __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
  100. close(fd);
  101. return -1;
  102. }
  103. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
  104. (void *) &addr, sizeof(addr))) {
  105. zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
  106. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  107. close(fd);
  108. return -1;
  109. }
  110. {
  111. long flags;
  112. flags = fcntl(fd, F_GETFL, 0);
  113. if (flags < 0) {
  114. zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
  115. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  116. close(fd);
  117. return -1;
  118. }
  119. if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
  120. zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
  121. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  122. close(fd);
  123. return -1;
  124. }
  125. }
  126. return fd;
  127. }
  128. static void ssmpingd_delete(struct ssmpingd_sock *ss)
  129. {
  130. zassert(ss);
  131. zassert(qpim_ssmpingd_list);
  132. THREAD_OFF(ss->t_sock_read);
  133. if (close(ss->sock_fd)) {
  134. int e = errno;
  135. char source_str[100];
  136. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  137. zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
  138. __PRETTY_FUNCTION__,
  139. ss->sock_fd, source_str, e, safe_strerror(e));
  140. /* warning only */
  141. }
  142. listnode_delete(qpim_ssmpingd_list, ss);
  143. ssmpingd_free(ss);
  144. }
  145. static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
  146. {
  147. struct interface *ifp;
  148. struct sockaddr_in from;
  149. struct sockaddr_in to;
  150. socklen_t fromlen = sizeof(from);
  151. socklen_t tolen = sizeof(to);
  152. int ifindex = -1;
  153. char buf[1000];
  154. int len;
  155. len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
  156. &from, &fromlen,
  157. &to, &tolen,
  158. &ifindex);
  159. if (len < 0) {
  160. char source_str[100];
  161. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  162. zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
  163. __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
  164. return -1;
  165. }
  166. ifp = if_lookup_by_index(ifindex);
  167. if (PIM_DEBUG_SSMPINGD) {
  168. char source_str[100];
  169. char from_str[100];
  170. char to_str[100];
  171. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  172. pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
  173. pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
  174. zlog_debug("%s: ssmpingd on source %s: interface %s ifindex=%d received ssmping from %s to %s on fd=%d",
  175. __PRETTY_FUNCTION__,
  176. source_str,
  177. ifp ? ifp->name : "<iface?>",
  178. ifindex, from_str, to_str, ss->sock_fd);
  179. }
  180. return 0;
  181. }
  182. static int ssmpingd_sock_read(struct thread *t)
  183. {
  184. struct ssmpingd_sock *ss;
  185. int sock_fd;
  186. int result;
  187. zassert(t);
  188. ss = THREAD_ARG(t);
  189. zassert(ss);
  190. sock_fd = THREAD_FD(t);
  191. zassert(sock_fd == ss->sock_fd);
  192. result = ssmpingd_read_msg(ss);
  193. /* Keep reading */
  194. ss->t_sock_read = 0;
  195. ssmpingd_read_on(ss);
  196. return result;
  197. }
  198. static void ssmpingd_read_on(struct ssmpingd_sock *ss)
  199. {
  200. zassert(!ss->t_sock_read);
  201. THREAD_READ_ON(master, ss->t_sock_read,
  202. ssmpingd_sock_read, ss, ss->sock_fd);
  203. }
  204. static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
  205. {
  206. struct ssmpingd_sock *ss;
  207. int sock_fd;
  208. if (!qpim_ssmpingd_list) {
  209. qpim_ssmpingd_list = list_new();
  210. if (!qpim_ssmpingd_list) {
  211. zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
  212. __FILE__, __PRETTY_FUNCTION__);
  213. return 0;
  214. }
  215. qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
  216. }
  217. sock_fd = ssmpingd_socket(source_addr, 64 /* ttl */);
  218. if (sock_fd < 0) {
  219. char source_str[100];
  220. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  221. zlog_warn("%s: ssmpingd_socket() failure for source %s",
  222. __PRETTY_FUNCTION__, source_str);
  223. return 0;
  224. }
  225. ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
  226. if (!ss) {
  227. char source_str[100];
  228. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  229. zlog_err("%s: XMALLOC(%d) failure for ssmpingd source %s",
  230. __PRETTY_FUNCTION__,
  231. sizeof(*ss), source_str);
  232. close(sock_fd);
  233. return 0;
  234. }
  235. ss->sock_fd = sock_fd;
  236. ss->t_sock_read = 0;
  237. ss->source_addr = source_addr;
  238. ss->creation = pim_time_monotonic_sec();
  239. ss->requests = 0;
  240. listnode_add(qpim_ssmpingd_list, ss);
  241. ssmpingd_read_on(ss);
  242. return ss;
  243. }
  244. int pim_ssmpingd_start(struct in_addr source_addr)
  245. {
  246. struct ssmpingd_sock *ss;
  247. ss = ssmpingd_find(source_addr);
  248. if (ss) {
  249. /* silently ignore request to recreate entry */
  250. return 0;
  251. }
  252. {
  253. char source_str[100];
  254. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  255. zlog_info("%s: starting ssmpingd for source %s",
  256. __PRETTY_FUNCTION__, source_str);
  257. }
  258. ss = ssmpingd_new(source_addr);
  259. if (!ss) {
  260. char source_str[100];
  261. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  262. zlog_warn("%s: ssmpingd_new() failure for source %s",
  263. __PRETTY_FUNCTION__, source_str);
  264. return -1;
  265. }
  266. return 0;
  267. }
  268. int pim_ssmpingd_stop(struct in_addr source_addr)
  269. {
  270. struct ssmpingd_sock *ss;
  271. ss = ssmpingd_find(source_addr);
  272. if (!ss) {
  273. char source_str[100];
  274. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  275. zlog_warn("%s: could not find ssmpingd for source %s",
  276. __PRETTY_FUNCTION__, source_str);
  277. return -1;
  278. }
  279. {
  280. char source_str[100];
  281. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  282. zlog_info("%s: stopping ssmpingd for source %s",
  283. __PRETTY_FUNCTION__, source_str);
  284. }
  285. ssmpingd_delete(ss);
  286. return 0;
  287. }