Annotation of embedaddon/quagga/pimd/pim_ssmpingd.c, revision 1.1.1.1
1.1 misho 1: /*
2: PIM for Quagga
3: Copyright (C) 2008 Everton da Silva Marques
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License as published by
7: the Free Software Foundation; either version 2 of the License, or
8: (at your option) any later version.
9:
10: This program is distributed in the hope that it will be useful, but
11: WITHOUT ANY WARRANTY; without even the implied warranty of
12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: General Public License for more details.
14:
15: You should have received a copy of the GNU General Public License
16: along with this program; see the file COPYING; if not, write to the
17: Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18: MA 02110-1301 USA
19:
20: $QuaggaId: $Format:%an, %ai, %h$ $
21: */
22:
23: #include <zebra.h>
24:
25: #include "if.h"
26: #include "log.h"
27: #include "memory.h"
28:
29: #include "pim_ssmpingd.h"
30: #include "pim_time.h"
31: #include "pim_sock.h"
32: #include "pim_str.h"
33: #include "pimd.h"
34:
35: static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
36:
37: enum {
38: PIM_SSMPINGD_REQUEST = 'Q',
39: PIM_SSMPINGD_REPLY = 'A'
40: };
41:
42: static void ssmpingd_read_on(struct ssmpingd_sock *ss);
43:
44: void pim_ssmpingd_init()
45: {
46: int result;
47:
48: zassert(!qpim_ssmpingd_list);
49:
50: result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr);
51:
52: zassert(result > 0);
53: }
54:
55: void pim_ssmpingd_destroy()
56: {
57: if (qpim_ssmpingd_list) {
58: list_free(qpim_ssmpingd_list);
59: qpim_ssmpingd_list = 0;
60: }
61: }
62:
63: static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
64: {
65: struct listnode *node;
66: struct ssmpingd_sock *ss;
67:
68: if (!qpim_ssmpingd_list)
69: return 0;
70:
71: for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
72: if (source_addr.s_addr == ss->source_addr.s_addr)
73: return ss;
74:
75: return 0;
76: }
77:
78: static void ssmpingd_free(struct ssmpingd_sock *ss)
79: {
80: XFREE(MTYPE_PIM_SSMPINGD, ss);
81: }
82:
83: static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
84: {
85: struct sockaddr_in sockaddr;
86: int fd;
87:
88: fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
89: if (fd < 0) {
90: zlog_err("%s: could not create socket: errno=%d: %s",
91: __PRETTY_FUNCTION__, errno, safe_strerror(errno));
92: return -1;
93: }
94:
95: sockaddr.sin_family = AF_INET;
96: sockaddr.sin_addr = addr;
97: sockaddr.sin_port = htons(port);
98:
99: if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
100: char addr_str[100];
101: pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
102: zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
103: __PRETTY_FUNCTION__,
104: fd, addr_str, port, sizeof(sockaddr),
105: errno, safe_strerror(errno));
106: close(fd);
107: return -1;
108: }
109:
110: /* Needed to obtain destination address from recvmsg() */
111: {
112: #if defined(HAVE_IP_PKTINFO)
113: /* Linux and Solaris IP_PKTINFO */
114: int opt = 1;
115: if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
116: zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
117: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
118: }
119: #elif defined(HAVE_IP_RECVDSTADDR)
120: /* BSD IP_RECVDSTADDR */
121: int opt = 1;
122: if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
123: zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
124: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
125: }
126: #else
127: zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
128: __FILE__, __PRETTY_FUNCTION__);
129: close(fd);
130: return -1;
131: #endif
132: }
133:
134: {
135: int reuse = 1;
136: if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
137: (void *) &reuse, sizeof(reuse))) {
138: zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
139: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
140: close(fd);
141: return -1;
142: }
143: }
144:
145: if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
146: (void *) &mttl, sizeof(mttl))) {
147: zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
148: __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
149: close(fd);
150: return -1;
151: }
152:
153: {
154: int loop = 0;
155: if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
156: (void *) &loop, sizeof(loop))) {
157: zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
158: __PRETTY_FUNCTION__,
159: loop ? "enable" : "disable",
160: fd, errno, safe_strerror(errno));
161: close(fd);
162: return PIM_SOCK_ERR_LOOP;
163: }
164: }
165:
166: if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
167: (void *) &addr, sizeof(addr))) {
168: zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
169: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
170: close(fd);
171: return -1;
172: }
173:
174: {
175: long flags;
176:
177: flags = fcntl(fd, F_GETFL, 0);
178: if (flags < 0) {
179: zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
180: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
181: close(fd);
182: return -1;
183: }
184:
185: if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
186: zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
187: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
188: close(fd);
189: return -1;
190: }
191: }
192:
193: return fd;
194: }
195:
196: static void ssmpingd_delete(struct ssmpingd_sock *ss)
197: {
198: zassert(ss);
199: zassert(qpim_ssmpingd_list);
200:
201: THREAD_OFF(ss->t_sock_read);
202:
203: if (close(ss->sock_fd)) {
204: int e = errno;
205: char source_str[100];
206: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
207: zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
208: __PRETTY_FUNCTION__,
209: ss->sock_fd, source_str, e, safe_strerror(e));
210: /* warning only */
211: }
212:
213: listnode_delete(qpim_ssmpingd_list, ss);
214: ssmpingd_free(ss);
215: }
216:
217: static void ssmpingd_sendto(struct ssmpingd_sock *ss,
218: const uint8_t *buf,
219: int len,
220: struct sockaddr_in to)
221: {
222: socklen_t tolen = sizeof(to);
223: int sent;
224:
225: sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
226: (struct sockaddr *)&to, tolen);
227: if (sent != len) {
228: int e = errno;
229: char to_str[100];
230: pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
231: if (sent < 0) {
232: zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
233: __PRETTY_FUNCTION__,
234: to_str, ntohs(to.sin_port), ss->sock_fd, len,
235: e, safe_strerror(e));
236: }
237: else {
238: zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
239: __PRETTY_FUNCTION__,
240: to_str, ntohs(to.sin_port), ss->sock_fd,
241: len, sent);
242: }
243: }
244: }
245:
246: static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
247: {
248: struct interface *ifp;
249: struct sockaddr_in from;
250: struct sockaddr_in to;
251: socklen_t fromlen = sizeof(from);
252: socklen_t tolen = sizeof(to);
253: ifindex_t ifindex = -1;
254: uint8_t buf[1000];
255: int len;
256:
257: ++ss->requests;
258:
259: len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
260: &from, &fromlen,
261: &to, &tolen,
262: &ifindex);
263: if (len < 0) {
264: char source_str[100];
265: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
266: zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
267: __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
268: return -1;
269: }
270:
271: ifp = if_lookup_by_index(ifindex);
272:
273: if (buf[0] != PIM_SSMPINGD_REQUEST) {
274: char source_str[100];
275: char from_str[100];
276: char to_str[100];
277: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
278: pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
279: pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
280: zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
281: __PRETTY_FUNCTION__,
282: buf[0],
283: from_str, ntohs(from.sin_port),
284: to_str, ntohs(to.sin_port),
285: ifp ? ifp->name : "<iface?>",
286: ifindex, ss->sock_fd,
287: source_str);
288: return 0;
289: }
290:
291: if (PIM_DEBUG_SSMPINGD) {
292: char source_str[100];
293: char from_str[100];
294: char to_str[100];
295: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
296: pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
297: pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
298: zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
299: __PRETTY_FUNCTION__,
300: from_str, ntohs(from.sin_port),
301: to_str, ntohs(to.sin_port),
302: ifp ? ifp->name : "<iface?>",
303: ifindex, ss->sock_fd,
304: source_str);
305: }
306:
307: buf[0] = PIM_SSMPINGD_REPLY;
308:
309: /* unicast reply */
310: ssmpingd_sendto(ss, buf, len, from);
311:
312: /* multicast reply */
313: from.sin_addr = qpim_ssmpingd_group_addr;
314: ssmpingd_sendto(ss, buf, len, from);
315:
316: return 0;
317: }
318:
319: static int ssmpingd_sock_read(struct thread *t)
320: {
321: struct ssmpingd_sock *ss;
322: int sock_fd;
323: int result;
324:
325: zassert(t);
326:
327: ss = THREAD_ARG(t);
328: zassert(ss);
329:
330: sock_fd = THREAD_FD(t);
331: zassert(sock_fd == ss->sock_fd);
332:
333: result = ssmpingd_read_msg(ss);
334:
335: /* Keep reading */
336: ss->t_sock_read = 0;
337: ssmpingd_read_on(ss);
338:
339: return result;
340: }
341:
342: static void ssmpingd_read_on(struct ssmpingd_sock *ss)
343: {
344: zassert(!ss->t_sock_read);
345: THREAD_READ_ON(master, ss->t_sock_read,
346: ssmpingd_sock_read, ss, ss->sock_fd);
347: }
348:
349: static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
350: {
351: struct ssmpingd_sock *ss;
352: int sock_fd;
353:
354: if (!qpim_ssmpingd_list) {
355: qpim_ssmpingd_list = list_new();
356: if (!qpim_ssmpingd_list) {
357: zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
358: __FILE__, __PRETTY_FUNCTION__);
359: return 0;
360: }
361: qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
362: }
363:
364: sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
365: if (sock_fd < 0) {
366: char source_str[100];
367: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
368: zlog_warn("%s: ssmpingd_socket() failure for source %s",
369: __PRETTY_FUNCTION__, source_str);
370: return 0;
371: }
372:
373: ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
374: if (!ss) {
375: char source_str[100];
376: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
377: zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
378: __PRETTY_FUNCTION__,
379: sizeof(*ss), source_str);
380: close(sock_fd);
381: return 0;
382: }
383:
384: ss->sock_fd = sock_fd;
385: ss->t_sock_read = 0;
386: ss->source_addr = source_addr;
387: ss->creation = pim_time_monotonic_sec();
388: ss->requests = 0;
389:
390: listnode_add(qpim_ssmpingd_list, ss);
391:
392: ssmpingd_read_on(ss);
393:
394: return ss;
395: }
396:
397: int pim_ssmpingd_start(struct in_addr source_addr)
398: {
399: struct ssmpingd_sock *ss;
400:
401: ss = ssmpingd_find(source_addr);
402: if (ss) {
403: /* silently ignore request to recreate entry */
404: return 0;
405: }
406:
407: {
408: char source_str[100];
409: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
410: zlog_info("%s: starting ssmpingd for source %s",
411: __PRETTY_FUNCTION__, source_str);
412: }
413:
414: ss = ssmpingd_new(source_addr);
415: if (!ss) {
416: char source_str[100];
417: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
418: zlog_warn("%s: ssmpingd_new() failure for source %s",
419: __PRETTY_FUNCTION__, source_str);
420: return -1;
421: }
422:
423: return 0;
424: }
425:
426: int pim_ssmpingd_stop(struct in_addr source_addr)
427: {
428: struct ssmpingd_sock *ss;
429:
430: ss = ssmpingd_find(source_addr);
431: if (!ss) {
432: char source_str[100];
433: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
434: zlog_warn("%s: could not find ssmpingd for source %s",
435: __PRETTY_FUNCTION__, source_str);
436: return -1;
437: }
438:
439: {
440: char source_str[100];
441: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
442: zlog_info("%s: stopping ssmpingd for source %s",
443: __PRETTY_FUNCTION__, source_str);
444: }
445:
446: ssmpingd_delete(ss);
447:
448: return 0;
449: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>