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: #include "log.h"
25: #include "privs.h"
26:
27: #include "pimd.h"
28: #include "pim_mroute.h"
29: #include "pim_str.h"
30: #include "pim_time.h"
31: #include "pim_iface.h"
32: #include "pim_macro.h"
33:
34: /* GLOBAL VARS */
35: extern struct zebra_privs_t pimd_privs;
36:
37: static void mroute_read_on(void);
38:
39: static int pim_mroute_set(int fd, int enable)
40: {
41: int err;
42: int opt = enable ? MRT_INIT : MRT_DONE;
43: socklen_t opt_len = sizeof(opt);
44:
45: err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
46: if (err) {
47: int e = errno;
48: zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
49: __FILE__, __PRETTY_FUNCTION__,
50: fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
51: errno = e;
52: return -1;
53: }
54:
55: #if 0
56: zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
57: __FILE__, __PRETTY_FUNCTION__,
58: fd, opt);
59: #endif
60:
61: return 0;
62: }
63:
64: int pim_mroute_msg(int fd, const char *buf, int buf_size)
65: {
66: struct interface *ifp;
67: const struct ip *ip_hdr;
68: const struct igmpmsg *msg;
69: const char *upcall;
70: char src_str[100];
71: char grp_str[100];
72:
73: ip_hdr = (const struct ip *) buf;
74:
75: /* kernel upcall must have protocol=0 */
76: if (ip_hdr->ip_p) {
77: /* this is not a kernel upcall */
78: #ifdef PIM_UNEXPECTED_KERNEL_UPCALL
79: zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d",
80: __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
81: #endif
82: return 0;
83: }
84:
85: msg = (const struct igmpmsg *) buf;
86:
87: switch (msg->im_msgtype) {
88: case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break;
89: case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break;
90: case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break;
91: default: upcall = "<unknown_upcall?>";
92: }
93: ifp = pim_if_find_by_vif_index(msg->im_vif);
94: pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
95: pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
96:
97: if (msg->im_msgtype == IGMPMSG_WRONGVIF) {
98: struct pim_ifchannel *ch;
99: struct pim_interface *pim_ifp;
100:
101: /*
102: Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
103:
104: RFC 4601 4.8.2. PIM-SSM-Only Routers
105:
106: iif is the incoming interface of the packet.
107: if (iif is in inherited_olist(S,G)) {
108: send Assert(S,G) on iif
109: }
110: */
111:
112: if (PIM_DEBUG_PIM_TRACE) {
113: zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
114: __PRETTY_FUNCTION__,
115: fd,
116: src_str,
117: grp_str,
118: ifp ? ifp->name : "<ifname?>",
119: msg->im_vif);
120: }
121:
122: if (!ifp) {
123: zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
124: __PRETTY_FUNCTION__,
125: src_str, grp_str, msg->im_vif);
126: return -1;
127: }
128:
129: pim_ifp = ifp->info;
130: if (!pim_ifp) {
131: zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
132: __PRETTY_FUNCTION__,
133: src_str, grp_str, ifp->name);
134: return -2;
135: }
136:
137: ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
138: if (!ch) {
139: zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
140: __PRETTY_FUNCTION__,
141: src_str, grp_str, ifp->name);
142: return -3;
143: }
144:
145: /*
146: RFC 4601: 4.6.1. (S,G) Assert Message State Machine
147:
148: Transitions from NoInfo State
149:
150: An (S,G) data packet arrives on interface I, AND
151: CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
152: downstream interface that is in our (S,G) outgoing interface
153: list. We optimistically assume that we will be the assert
154: winner for this (S,G), and so we transition to the "I am Assert
155: Winner" state and perform Actions A1 (below), which will
156: initiate the assert negotiation for (S,G).
157: */
158:
159: if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
160: zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
161: __PRETTY_FUNCTION__,
162: src_str, grp_str, ifp->name);
163: return -4;
164: }
165:
166: if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
167: zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
168: __PRETTY_FUNCTION__,
169: src_str, grp_str, ifp->name);
170: return -5;
171: }
172:
173: if (assert_action_a1(ch)) {
174: zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
175: __PRETTY_FUNCTION__,
176: src_str, grp_str, ifp->name);
177: return -6;
178: }
179:
180: return 0;
181: } /* IGMPMSG_WRONGVIF */
182:
183: zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
184: __PRETTY_FUNCTION__,
185: upcall,
186: msg->im_msgtype,
187: ip_hdr->ip_p,
188: fd,
189: src_str,
190: grp_str,
191: ifp ? ifp->name : "<ifname?>",
192: msg->im_vif);
193:
194: return 0;
195: }
196:
197: static int mroute_read_msg(int fd)
198: {
199: const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
200: char buf[1000];
201: int rd;
202:
203: if (((int) sizeof(buf)) < msg_min_size) {
204: zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
205: __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
206: return -1;
207: }
208:
209: rd = read(fd, buf, sizeof(buf));
210: if (rd < 0) {
211: zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
212: __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
213: return -2;
214: }
215:
216: if (rd < msg_min_size) {
217: zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
218: __PRETTY_FUNCTION__, fd, rd, msg_min_size);
219: return -3;
220: }
221:
222: return pim_mroute_msg(fd, buf, rd);
223: }
224:
225: static int mroute_read(struct thread *t)
226: {
227: int fd;
228: int result;
229:
230: zassert(t);
231: zassert(!THREAD_ARG(t));
232:
233: fd = THREAD_FD(t);
234: zassert(fd == qpim_mroute_socket_fd);
235:
236: result = mroute_read_msg(fd);
237:
238: /* Keep reading */
239: qpim_mroute_socket_reader = 0;
240: mroute_read_on();
241:
242: return result;
243: }
244:
245: static void mroute_read_on()
246: {
247: zassert(!qpim_mroute_socket_reader);
248: zassert(PIM_MROUTE_IS_ENABLED);
249:
250: THREAD_READ_ON(master, qpim_mroute_socket_reader,
251: mroute_read, 0, qpim_mroute_socket_fd);
252: }
253:
254: static void mroute_read_off()
255: {
256: THREAD_OFF(qpim_mroute_socket_reader);
257: }
258:
259: int pim_mroute_socket_enable()
260: {
261: int fd;
262:
263: if (PIM_MROUTE_IS_ENABLED)
264: return -1;
265:
266: if ( pimd_privs.change (ZPRIVS_RAISE) )
267: zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
268: safe_strerror (errno) );
269:
270: fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
271:
272: if ( pimd_privs.change (ZPRIVS_LOWER) )
273: zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
274: safe_strerror (errno) );
275:
276: if (fd < 0) {
277: zlog_warn("Could not create mroute socket: errno=%d: %s",
278: errno, safe_strerror(errno));
279: return -2;
280: }
281:
282: if (pim_mroute_set(fd, 1)) {
283: zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
284: fd, errno, safe_strerror(errno));
285: close(fd);
286: return -3;
287: }
288:
289: qpim_mroute_socket_fd = fd;
290: qpim_mroute_socket_creation = pim_time_monotonic_sec();
291: mroute_read_on();
292:
293: zassert(PIM_MROUTE_IS_ENABLED);
294:
295: return 0;
296: }
297:
298: int pim_mroute_socket_disable()
299: {
300: if (PIM_MROUTE_IS_DISABLED)
301: return -1;
302:
303: if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
304: zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
305: qpim_mroute_socket_fd, errno, safe_strerror(errno));
306: return -2;
307: }
308:
309: if (close(qpim_mroute_socket_fd)) {
310: zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
311: qpim_mroute_socket_fd, errno, safe_strerror(errno));
312: return -3;
313: }
314:
315: mroute_read_off();
316: qpim_mroute_socket_fd = -1;
317:
318: zassert(PIM_MROUTE_IS_DISABLED);
319:
320: return 0;
321: }
322:
323: /*
324: For each network interface (e.g., physical or a virtual tunnel) that
325: would be used for multicast forwarding, a corresponding multicast
326: interface must be added to the kernel.
327: */
328: int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
329: {
330: struct vifctl vc;
331: int err;
332:
333: if (PIM_MROUTE_IS_DISABLED) {
334: zlog_warn("%s: global multicast is disabled",
335: __PRETTY_FUNCTION__);
336: return -1;
337: }
338:
339: memset(&vc, 0, sizeof(vc));
340: vc.vifc_vifi = vif_index;
341: vc.vifc_flags = 0;
342: vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
343: vc.vifc_rate_limit = 0;
344: memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
345:
346: #ifdef PIM_DVMRP_TUNNEL
347: if (vc.vifc_flags & VIFF_TUNNEL) {
348: memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
349: }
350: #endif
351:
352: err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
353: if (err) {
354: char ifaddr_str[100];
355: int e = errno;
356:
357: pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
358:
359: zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
360: __FILE__, __PRETTY_FUNCTION__,
361: qpim_mroute_socket_fd, vif_index, ifaddr_str,
362: e, safe_strerror(e));
363: errno = e;
364: return -2;
365: }
366:
367: return 0;
368: }
369:
370: int pim_mroute_del_vif(int vif_index)
371: {
372: struct vifctl vc;
373: int err;
374:
375: if (PIM_MROUTE_IS_DISABLED) {
376: zlog_warn("%s: global multicast is disabled",
377: __PRETTY_FUNCTION__);
378: return -1;
379: }
380:
381: memset(&vc, 0, sizeof(vc));
382: vc.vifc_vifi = vif_index;
383:
384: err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
385: if (err) {
386: int e = errno;
387: zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
388: __FILE__, __PRETTY_FUNCTION__,
389: qpim_mroute_socket_fd, vif_index,
390: e, safe_strerror(e));
391: errno = e;
392: return -2;
393: }
394:
395: return 0;
396: }
397:
398: int pim_mroute_add(struct mfcctl *mc)
399: {
400: int err;
401:
402: qpim_mroute_add_last = pim_time_monotonic_sec();
403: ++qpim_mroute_add_events;
404:
405: if (PIM_MROUTE_IS_DISABLED) {
406: zlog_warn("%s: global multicast is disabled",
407: __PRETTY_FUNCTION__);
408: return -1;
409: }
410:
411: err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
412: mc, sizeof(*mc));
413: if (err) {
414: int e = errno;
415: zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
416: __FILE__, __PRETTY_FUNCTION__,
417: qpim_mroute_socket_fd,
418: e, safe_strerror(e));
419: errno = e;
420: return -2;
421: }
422:
423: return 0;
424: }
425:
426: int pim_mroute_del(struct mfcctl *mc)
427: {
428: int err;
429:
430: qpim_mroute_del_last = pim_time_monotonic_sec();
431: ++qpim_mroute_del_events;
432:
433: if (PIM_MROUTE_IS_DISABLED) {
434: zlog_warn("%s: global multicast is disabled",
435: __PRETTY_FUNCTION__);
436: return -1;
437: }
438:
439: err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
440: if (err) {
441: int e = errno;
442: zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
443: __FILE__, __PRETTY_FUNCTION__,
444: qpim_mroute_socket_fd,
445: e, safe_strerror(e));
446: errno = e;
447: return -2;
448: }
449:
450: return 0;
451: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>