pim_ssmpingd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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 const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
  28. enum {
  29. PIM_SSMPINGD_REQUEST = 'Q',
  30. PIM_SSMPINGD_REPLY = 'A'
  31. };
  32. static void ssmpingd_read_on(struct ssmpingd_sock *ss);
  33. void pim_ssmpingd_init()
  34. {
  35. int result;
  36. zassert(!qpim_ssmpingd_list);
  37. result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr);
  38. zassert(result > 0);
  39. }
  40. void pim_ssmpingd_destroy()
  41. {
  42. if (qpim_ssmpingd_list) {
  43. list_free(qpim_ssmpingd_list);
  44. qpim_ssmpingd_list = 0;
  45. }
  46. }
  47. static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
  48. {
  49. struct listnode *node;
  50. struct ssmpingd_sock *ss;
  51. if (!qpim_ssmpingd_list)
  52. return 0;
  53. for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
  54. if (source_addr.s_addr == ss->source_addr.s_addr)
  55. return ss;
  56. return 0;
  57. }
  58. static void ssmpingd_free(struct ssmpingd_sock *ss)
  59. {
  60. XFREE(MTYPE_PIM_SSMPINGD, ss);
  61. }
  62. static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
  63. {
  64. struct sockaddr_in sockaddr;
  65. int fd;
  66. fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  67. if (fd < 0) {
  68. zlog_err("%s: could not create socket: errno=%d: %s",
  69. __PRETTY_FUNCTION__, errno, safe_strerror(errno));
  70. return -1;
  71. }
  72. sockaddr.sin_family = AF_INET;
  73. sockaddr.sin_addr = addr;
  74. sockaddr.sin_port = htons(port);
  75. if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
  76. char addr_str[100];
  77. pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
  78. zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
  79. __PRETTY_FUNCTION__,
  80. fd, addr_str, port, sizeof(sockaddr),
  81. errno, safe_strerror(errno));
  82. close(fd);
  83. return -1;
  84. }
  85. /* Needed to obtain destination address from recvmsg() */
  86. {
  87. #if defined(HAVE_IP_PKTINFO)
  88. /* Linux and Solaris IP_PKTINFO */
  89. int opt = 1;
  90. if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
  91. zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
  92. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  93. }
  94. #elif defined(HAVE_IP_RECVDSTADDR)
  95. /* BSD IP_RECVDSTADDR */
  96. int opt = 1;
  97. if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
  98. zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
  99. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  100. }
  101. #else
  102. zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
  103. __FILE__, __PRETTY_FUNCTION__);
  104. close(fd);
  105. return -1;
  106. #endif
  107. }
  108. {
  109. int reuse = 1;
  110. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
  111. (void *) &reuse, sizeof(reuse))) {
  112. zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
  113. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  114. close(fd);
  115. return -1;
  116. }
  117. }
  118. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
  119. (void *) &mttl, sizeof(mttl))) {
  120. zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
  121. __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
  122. close(fd);
  123. return -1;
  124. }
  125. {
  126. int loop = 0;
  127. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
  128. (void *) &loop, sizeof(loop))) {
  129. zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
  130. __PRETTY_FUNCTION__,
  131. loop ? "enable" : "disable",
  132. fd, errno, safe_strerror(errno));
  133. close(fd);
  134. return PIM_SOCK_ERR_LOOP;
  135. }
  136. }
  137. if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
  138. (void *) &addr, sizeof(addr))) {
  139. zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
  140. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  141. close(fd);
  142. return -1;
  143. }
  144. {
  145. long flags;
  146. flags = fcntl(fd, F_GETFL, 0);
  147. if (flags < 0) {
  148. zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
  149. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  150. close(fd);
  151. return -1;
  152. }
  153. if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
  154. zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
  155. __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
  156. close(fd);
  157. return -1;
  158. }
  159. }
  160. return fd;
  161. }
  162. static void ssmpingd_delete(struct ssmpingd_sock *ss)
  163. {
  164. zassert(ss);
  165. zassert(qpim_ssmpingd_list);
  166. THREAD_OFF(ss->t_sock_read);
  167. if (close(ss->sock_fd)) {
  168. int e = errno;
  169. char source_str[100];
  170. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  171. zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
  172. __PRETTY_FUNCTION__,
  173. ss->sock_fd, source_str, e, safe_strerror(e));
  174. /* warning only */
  175. }
  176. listnode_delete(qpim_ssmpingd_list, ss);
  177. ssmpingd_free(ss);
  178. }
  179. static void ssmpingd_sendto(struct ssmpingd_sock *ss,
  180. const uint8_t *buf,
  181. int len,
  182. struct sockaddr_in to)
  183. {
  184. socklen_t tolen = sizeof(to);
  185. int sent;
  186. sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
  187. (struct sockaddr *)&to, tolen);
  188. if (sent != len) {
  189. int e = errno;
  190. char to_str[100];
  191. pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
  192. if (sent < 0) {
  193. zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
  194. __PRETTY_FUNCTION__,
  195. to_str, ntohs(to.sin_port), ss->sock_fd, len,
  196. e, safe_strerror(e));
  197. }
  198. else {
  199. zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
  200. __PRETTY_FUNCTION__,
  201. to_str, ntohs(to.sin_port), ss->sock_fd,
  202. len, sent);
  203. }
  204. }
  205. }
  206. static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
  207. {
  208. struct interface *ifp;
  209. struct sockaddr_in from;
  210. struct sockaddr_in to;
  211. socklen_t fromlen = sizeof(from);
  212. socklen_t tolen = sizeof(to);
  213. ifindex_t ifindex = -1;
  214. uint8_t buf[1000];
  215. int len;
  216. ++ss->requests;
  217. len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
  218. &from, &fromlen,
  219. &to, &tolen,
  220. &ifindex);
  221. if (len < 0) {
  222. char source_str[100];
  223. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  224. zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
  225. __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
  226. return -1;
  227. }
  228. ifp = if_lookup_by_index(ifindex);
  229. if (buf[0] != PIM_SSMPINGD_REQUEST) {
  230. char source_str[100];
  231. char from_str[100];
  232. char to_str[100];
  233. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  234. pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
  235. pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
  236. zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
  237. __PRETTY_FUNCTION__,
  238. buf[0],
  239. from_str, ntohs(from.sin_port),
  240. to_str, ntohs(to.sin_port),
  241. ifp ? ifp->name : "<iface?>",
  242. ifindex, ss->sock_fd,
  243. source_str);
  244. return 0;
  245. }
  246. if (PIM_DEBUG_SSMPINGD) {
  247. char source_str[100];
  248. char from_str[100];
  249. char to_str[100];
  250. pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
  251. pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
  252. pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
  253. zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
  254. __PRETTY_FUNCTION__,
  255. from_str, ntohs(from.sin_port),
  256. to_str, ntohs(to.sin_port),
  257. ifp ? ifp->name : "<iface?>",
  258. ifindex, ss->sock_fd,
  259. source_str);
  260. }
  261. buf[0] = PIM_SSMPINGD_REPLY;
  262. /* unicast reply */
  263. ssmpingd_sendto(ss, buf, len, from);
  264. /* multicast reply */
  265. from.sin_addr = qpim_ssmpingd_group_addr;
  266. ssmpingd_sendto(ss, buf, len, from);
  267. return 0;
  268. }
  269. static int ssmpingd_sock_read(struct thread *t)
  270. {
  271. struct ssmpingd_sock *ss;
  272. int sock_fd;
  273. int result;
  274. zassert(t);
  275. ss = THREAD_ARG(t);
  276. zassert(ss);
  277. sock_fd = THREAD_FD(t);
  278. zassert(sock_fd == ss->sock_fd);
  279. result = ssmpingd_read_msg(ss);
  280. /* Keep reading */
  281. ss->t_sock_read = 0;
  282. ssmpingd_read_on(ss);
  283. return result;
  284. }
  285. static void ssmpingd_read_on(struct ssmpingd_sock *ss)
  286. {
  287. zassert(!ss->t_sock_read);
  288. THREAD_READ_ON(master, ss->t_sock_read,
  289. ssmpingd_sock_read, ss, ss->sock_fd);
  290. }
  291. static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
  292. {
  293. struct ssmpingd_sock *ss;
  294. int sock_fd;
  295. if (!qpim_ssmpingd_list) {
  296. qpim_ssmpingd_list = list_new();
  297. if (!qpim_ssmpingd_list) {
  298. zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
  299. __FILE__, __PRETTY_FUNCTION__);
  300. return 0;
  301. }
  302. qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
  303. }
  304. sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
  305. if (sock_fd < 0) {
  306. char source_str[100];
  307. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  308. zlog_warn("%s: ssmpingd_socket() failure for source %s",
  309. __PRETTY_FUNCTION__, source_str);
  310. return 0;
  311. }
  312. ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
  313. if (!ss) {
  314. char source_str[100];
  315. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  316. zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
  317. __PRETTY_FUNCTION__,
  318. sizeof(*ss), source_str);
  319. close(sock_fd);
  320. return 0;
  321. }
  322. ss->sock_fd = sock_fd;
  323. ss->t_sock_read = 0;
  324. ss->source_addr = source_addr;
  325. ss->creation = pim_time_monotonic_sec();
  326. ss->requests = 0;
  327. listnode_add(qpim_ssmpingd_list, ss);
  328. ssmpingd_read_on(ss);
  329. return ss;
  330. }
  331. int pim_ssmpingd_start(struct in_addr source_addr)
  332. {
  333. struct ssmpingd_sock *ss;
  334. ss = ssmpingd_find(source_addr);
  335. if (ss) {
  336. /* silently ignore request to recreate entry */
  337. return 0;
  338. }
  339. {
  340. char source_str[100];
  341. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  342. zlog_info("%s: starting ssmpingd for source %s",
  343. __PRETTY_FUNCTION__, source_str);
  344. }
  345. ss = ssmpingd_new(source_addr);
  346. if (!ss) {
  347. char source_str[100];
  348. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  349. zlog_warn("%s: ssmpingd_new() failure for source %s",
  350. __PRETTY_FUNCTION__, source_str);
  351. return -1;
  352. }
  353. return 0;
  354. }
  355. int pim_ssmpingd_stop(struct in_addr source_addr)
  356. {
  357. struct ssmpingd_sock *ss;
  358. ss = ssmpingd_find(source_addr);
  359. if (!ss) {
  360. char source_str[100];
  361. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  362. zlog_warn("%s: could not find ssmpingd for source %s",
  363. __PRETTY_FUNCTION__, source_str);
  364. return -1;
  365. }
  366. {
  367. char source_str[100];
  368. pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  369. zlog_info("%s: stopping ssmpingd for source %s",
  370. __PRETTY_FUNCTION__, source_str);
  371. }
  372. ssmpingd_delete(ss);
  373. return 0;
  374. }