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