]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mroute.c
8048d4b3c70bd726953eb5b547cde5f7e4f319d3
[mirror_frr.git] / pimd / pim_mroute.c
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 #include "if.h"
27 #include "prefix.h"
28
29 #include "pimd.h"
30 #include "pim_mroute.h"
31 #include "pim_oil.h"
32 #include "pim_str.h"
33 #include "pim_time.h"
34 #include "pim_iface.h"
35 #include "pim_macro.h"
36 #include "pim_rp.h"
37 #include "pim_oil.h"
38 #include "pim_register.h"
39
40 /* GLOBAL VARS */
41 extern struct zebra_privs_t pimd_privs;
42
43 static void mroute_read_on(void);
44
45 static int pim_mroute_set(int fd, int enable)
46 {
47 int err;
48 int opt = enable ? MRT_INIT : MRT_DONE;
49 socklen_t opt_len = sizeof(opt);
50
51 err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
52 if (err) {
53 int e = errno;
54 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
55 __FILE__, __PRETTY_FUNCTION__,
56 fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
57 errno = e;
58 return -1;
59 }
60
61 #if 0
62 zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
63 __FILE__, __PRETTY_FUNCTION__,
64 fd, opt);
65 #endif
66
67 return 0;
68 }
69
70 static int
71 pim_mroute_connected_to_source (struct interface *ifp, struct in_addr src)
72 {
73 struct listnode *cnode;
74 struct connected *c;
75 struct prefix p;
76
77 p.family = AF_INET;
78 p.u.prefix4 = src;
79 p.prefixlen = IPV4_MAX_BITLEN;
80
81 for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c))
82 {
83 if ((c->address->family == AF_INET) &&
84 prefix_match (CONNECTED_PREFIX (c), &p))
85 {
86 return 1;
87 }
88 }
89
90 return 0;
91 }
92
93 static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = {
94 "<unknown_upcall?>",
95 "NOCACHE",
96 "WRONGVIF",
97 "WHOLEPKT", };
98
99 static int
100 pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg,
101 const char *src_str, const char *grp_str)
102 {
103 struct pim_interface *pim_ifp = ifp->info;
104 struct pim_upstream *up;
105 struct pim_rpf *rpg;
106
107 rpg = RP(msg->im_dst);
108 /*
109 * If the incoming interface is unknown OR
110 * the Interface type is SSM we don't need to
111 * do anything here
112 */
113 if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
114 (!pim_ifp) ||
115 (!(PIM_I_am_DR(pim_ifp))) ||
116 (pim_ifp->itype == PIM_INTERFACE_SSM))
117 return 0;
118
119 /*
120 * If we've received a multicast packet that isn't connected to
121 * us
122 */
123 if (!pim_mroute_connected_to_source (ifp, msg->im_src))
124 {
125 if (PIM_DEBUG_PIM_TRACE)
126 zlog_debug ("%s: Received incoming packet that does originate on our seg",
127 __PRETTY_FUNCTION__);
128 return 0;
129 }
130
131 if (PIM_DEBUG_PIM_TRACE) {
132 zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
133 __PRETTY_FUNCTION__, grp_str, src_str);
134 }
135
136 up = pim_upstream_add(msg->im_src, msg->im_dst, ifp);
137 if (!up) {
138 if (PIM_DEBUG_PIM_TRACE) {
139 zlog_debug("%s: Failure to add upstream information for (%s,%s)",
140 __PRETTY_FUNCTION__,
141 src_str, grp_str);
142 }
143 return 0;
144 }
145
146 up->channel_oil = pim_channel_oil_add(msg->im_dst,
147 msg->im_src,
148 pim_ifp->mroute_vif_index);
149 if (!up->channel_oil) {
150 if (PIM_DEBUG_PIM_TRACE) {
151 zlog_debug("%s: Failure to add channel oil for (%s,%s)",
152 __PRETTY_FUNCTION__,
153 src_str, grp_str);
154 }
155 return 0;
156 }
157
158 pim_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE);
159
160 return 0;
161 }
162
163 static int
164 pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf,
165 const char *src_str, const char *grp_str)
166 {
167 struct pim_interface *pim_ifp;
168 struct in_addr group;
169 struct in_addr src;
170 struct pim_rpf *rpg;
171 const struct ip *ip_hdr;
172 struct pim_upstream *up;
173
174 ip_hdr = (const struct ip *)buf;
175
176 src = ip_hdr->ip_src;
177 group = ip_hdr->ip_dst;
178
179 up = pim_upstream_find(src, group);
180 if (!up) {
181 if (PIM_DEBUG_PIM_TRACE) {
182 zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
183 __PRETTY_FUNCTION__, src_str, grp_str);
184 }
185 return 0;
186 }
187
188 pim_ifp = up->rpf.source_nexthop.interface->info;
189
190 rpg = RP(group);
191
192 if ((rpg->rpf_addr.s_addr == INADDR_NONE) ||
193 (!pim_ifp) ||
194 (!(PIM_I_am_DR(pim_ifp))) ||
195 (pim_ifp->itype == PIM_INTERFACE_SSM)) {
196 if (PIM_DEBUG_PIM_TRACE) {
197 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__);
198 }
199 return 0;
200 }
201
202 pim_register_send((const struct ip *)(buf + sizeof(struct ip)), rpg);
203 return 0;
204 }
205
206 static int
207 pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg,
208 const char *src_str, const char *grp_str)
209 {
210 struct pim_ifchannel *ch;
211 struct pim_interface *pim_ifp;
212
213 /*
214 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
215
216 RFC 4601 4.8.2. PIM-SSM-Only Routers
217
218 iif is the incoming interface of the packet.
219 if (iif is in inherited_olist(S,G)) {
220 send Assert(S,G) on iif
221 }
222 */
223
224 if (!ifp) {
225 if (PIM_DEBUG_PIM_TRACE) {
226 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
227 __PRETTY_FUNCTION__,
228 src_str, grp_str, msg->im_vif);
229 }
230 return -1;
231 }
232
233 pim_ifp = ifp->info;
234 if (!pim_ifp) {
235 if (PIM_DEBUG_PIM_TRACE) {
236 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
237 __PRETTY_FUNCTION__,
238 src_str, grp_str, ifp->name);
239 }
240 return -2;
241 }
242
243 ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
244 if (!ch) {
245 if (PIM_DEBUG_PIM_TRACE) {
246 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
247 __PRETTY_FUNCTION__,
248 src_str, grp_str, ifp->name);
249 }
250 return -3;
251 }
252
253 /*
254 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
255
256 Transitions from NoInfo State
257
258 An (S,G) data packet arrives on interface I, AND
259 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
260 downstream interface that is in our (S,G) outgoing interface
261 list. We optimistically assume that we will be the assert
262 winner for this (S,G), and so we transition to the "I am Assert
263 Winner" state and perform Actions A1 (below), which will
264 initiate the assert negotiation for (S,G).
265 */
266
267 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
268 if (PIM_DEBUG_PIM_TRACE) {
269 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
270 __PRETTY_FUNCTION__,
271 src_str, grp_str, ifp->name);
272 }
273 return -4;
274 }
275
276 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
277 if (PIM_DEBUG_PIM_TRACE) {
278 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
279 __PRETTY_FUNCTION__,
280 src_str, grp_str, ifp->name);
281 }
282 return -5;
283 }
284
285 if (assert_action_a1(ch)) {
286 if (PIM_DEBUG_PIM_TRACE) {
287 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
288 __PRETTY_FUNCTION__,
289 src_str, grp_str, ifp->name);
290 }
291 return -6;
292 }
293
294 return 0;
295 }
296
297 int pim_mroute_msg(int fd, const char *buf, int buf_size)
298 {
299 struct interface *ifp;
300 const struct ip *ip_hdr;
301 const struct igmpmsg *msg;
302 char src_str[100] = "<src?>";
303 char grp_str[100] = "<grp?>";
304
305 ip_hdr = (const struct ip *) buf;
306
307 /* kernel upcall must have protocol=0 */
308 if (ip_hdr->ip_p) {
309 /* this is not a kernel upcall */
310 if (PIM_DEBUG_PIM_TRACE) {
311 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
312 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
313 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
314 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
315 //zlog_hexdump(buf, buf_size);
316 }
317 return 0;
318 }
319
320 msg = (const struct igmpmsg *) buf;
321
322 ifp = pim_if_find_by_vif_index(msg->im_vif);
323
324 if (PIM_DEBUG_PIM_TRACE) {
325 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
326 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
327 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
328 __PRETTY_FUNCTION__,
329 igmpmsgtype2str[msg->im_msgtype],
330 msg->im_msgtype,
331 ip_hdr->ip_p,
332 fd,
333 src_str,
334 grp_str,
335 ifp ? ifp->name : "<ifname?>",
336 msg->im_vif);
337 }
338
339 switch (msg->im_msgtype) {
340 case IGMPMSG_WRONGVIF:
341 return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str);
342 break;
343 case IGMPMSG_NOCACHE:
344 return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str);
345 break;
346 case IGMPMSG_WHOLEPKT:
347 zlog_hexdump(buf, buf_size);
348 return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg, src_str, grp_str);
349 break;
350 default:
351 break;
352 }
353
354 return 0;
355 }
356
357 static int mroute_read_msg(int fd)
358 {
359 const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
360 char buf[1000];
361 int rd;
362
363 if (((int) sizeof(buf)) < msg_min_size) {
364 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
365 __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
366 return -1;
367 }
368
369 rd = read(fd, buf, sizeof(buf));
370 if (rd < 0) {
371 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
372 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
373 return -2;
374 }
375
376 if (rd < msg_min_size) {
377 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
378 __PRETTY_FUNCTION__, fd, rd, msg_min_size);
379 return -3;
380 }
381
382 return pim_mroute_msg(fd, buf, rd);
383 }
384
385 static int mroute_read(struct thread *t)
386 {
387 int fd;
388 int result;
389
390 zassert(t);
391 zassert(!THREAD_ARG(t));
392
393 fd = THREAD_FD(t);
394 zassert(fd == qpim_mroute_socket_fd);
395
396 result = mroute_read_msg(fd);
397
398 /* Keep reading */
399 qpim_mroute_socket_reader = 0;
400 mroute_read_on();
401
402 return result;
403 }
404
405 static void mroute_read_on()
406 {
407 zassert(!qpim_mroute_socket_reader);
408 zassert(PIM_MROUTE_IS_ENABLED);
409
410 THREAD_READ_ON(master, qpim_mroute_socket_reader,
411 mroute_read, 0, qpim_mroute_socket_fd);
412 }
413
414 static void mroute_read_off()
415 {
416 THREAD_OFF(qpim_mroute_socket_reader);
417 }
418
419 int pim_mroute_socket_enable()
420 {
421 int fd;
422
423 if (PIM_MROUTE_IS_ENABLED)
424 return -1;
425
426 if ( pimd_privs.change (ZPRIVS_RAISE) )
427 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
428 safe_strerror (errno) );
429
430 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
431
432 if ( pimd_privs.change (ZPRIVS_LOWER) )
433 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
434 safe_strerror (errno) );
435
436 if (fd < 0) {
437 zlog_warn("Could not create mroute socket: errno=%d: %s",
438 errno, safe_strerror(errno));
439 return -2;
440 }
441
442 if (pim_mroute_set(fd, 1)) {
443 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
444 fd, errno, safe_strerror(errno));
445 close(fd);
446 return -3;
447 }
448
449 qpim_mroute_socket_fd = fd;
450
451 qpim_mroute_socket_creation = pim_time_monotonic_sec();
452 mroute_read_on();
453
454 zassert(PIM_MROUTE_IS_ENABLED);
455
456 return 0;
457 }
458
459 int pim_mroute_socket_disable()
460 {
461 if (PIM_MROUTE_IS_DISABLED)
462 return -1;
463
464 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
465 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
466 qpim_mroute_socket_fd, errno, safe_strerror(errno));
467 return -2;
468 }
469
470 if (close(qpim_mroute_socket_fd)) {
471 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
472 qpim_mroute_socket_fd, errno, safe_strerror(errno));
473 return -3;
474 }
475
476 mroute_read_off();
477 qpim_mroute_socket_fd = -1;
478
479 zassert(PIM_MROUTE_IS_DISABLED);
480
481 return 0;
482 }
483
484 /*
485 For each network interface (e.g., physical or a virtual tunnel) that
486 would be used for multicast forwarding, a corresponding multicast
487 interface must be added to the kernel.
488 */
489 int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
490 {
491 struct pim_interface *pim_ifp = ifp->info;
492 struct vifctl vc;
493 int err;
494
495 if (PIM_MROUTE_IS_DISABLED) {
496 zlog_warn("%s: global multicast is disabled",
497 __PRETTY_FUNCTION__);
498 return -1;
499 }
500
501 memset(&vc, 0, sizeof(vc));
502 vc.vifc_vifi = pim_ifp->mroute_vif_index;
503 vc.vifc_lcl_ifindex = ifp->ifindex;
504 vc.vifc_flags = flags;
505 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
506 vc.vifc_rate_limit = 0;
507
508 #ifdef PIM_DVMRP_TUNNEL
509 if (vc.vifc_flags & VIFF_TUNNEL) {
510 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
511 }
512 #endif
513
514 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
515 if (err) {
516 char ifaddr_str[100];
517 int e = errno;
518
519 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
520
521 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
522 __FILE__, __PRETTY_FUNCTION__,
523 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
524 e, safe_strerror(e));
525 errno = e;
526 return -2;
527 }
528
529 return 0;
530 }
531
532 int pim_mroute_del_vif(int vif_index)
533 {
534 struct vifctl vc;
535 int err;
536
537 if (PIM_MROUTE_IS_DISABLED) {
538 zlog_warn("%s: global multicast is disabled",
539 __PRETTY_FUNCTION__);
540 return -1;
541 }
542
543 memset(&vc, 0, sizeof(vc));
544 vc.vifc_vifi = vif_index;
545
546 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
547 if (err) {
548 int e = errno;
549 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
550 __FILE__, __PRETTY_FUNCTION__,
551 qpim_mroute_socket_fd, vif_index,
552 e, safe_strerror(e));
553 errno = e;
554 return -2;
555 }
556
557 return 0;
558 }
559
560 int pim_mroute_add(struct mfcctl *mc)
561 {
562 int err;
563 int orig = 0;
564
565 qpim_mroute_add_last = pim_time_monotonic_sec();
566 ++qpim_mroute_add_events;
567
568 if (PIM_MROUTE_IS_DISABLED) {
569 zlog_warn("%s: global multicast is disabled",
570 __PRETTY_FUNCTION__);
571 return -1;
572 }
573
574 /* The linux kernel *expects* the incoming
575 * vif to be part of the outgoing list
576 * in the case of a (*,G).
577 */
578 if (mc->mfcc_origin.s_addr == INADDR_ANY)
579 {
580 orig = mc->mfcc_ttls[mc->mfcc_parent];
581 mc->mfcc_ttls[mc->mfcc_parent] = 1;
582 }
583
584 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
585 mc, sizeof(*mc));
586
587 if (mc->mfcc_origin.s_addr == INADDR_ANY)
588 mc->mfcc_ttls[mc->mfcc_parent] = orig;
589
590 if (err) {
591 int e = errno;
592 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
593 __FILE__, __PRETTY_FUNCTION__,
594 qpim_mroute_socket_fd,
595 e, safe_strerror(e));
596 errno = e;
597 return -2;
598 }
599
600 return 0;
601 }
602
603 int pim_mroute_del(struct mfcctl *mc)
604 {
605 int err;
606
607 qpim_mroute_del_last = pim_time_monotonic_sec();
608 ++qpim_mroute_del_events;
609
610 if (PIM_MROUTE_IS_DISABLED) {
611 zlog_warn("%s: global multicast is disabled",
612 __PRETTY_FUNCTION__);
613 return -1;
614 }
615
616 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
617 if (err) {
618 int e = errno;
619 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
620 __FILE__, __PRETTY_FUNCTION__,
621 qpim_mroute_socket_fd,
622 e, safe_strerror(e));
623 errno = e;
624 return -2;
625 }
626
627 return 0;
628 }