]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
pimd: Fixup some broken code for the register receive
[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"
29#include "pim_str.h"
30#include "pim_time.h"
31#include "pim_iface.h"
32#include "pim_macro.h"
33
34/* GLOBAL VARS */
35extern struct zebra_privs_t pimd_privs;
36
37static void mroute_read_on(void);
38
39static 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
e355e30f
DS
64static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = {
65 "<unknown_upcall?>",
66 "NOCACHE",
67 "WRONGVIF",
68 "WHOLEPKT", };
69
70static int
71pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg,
72 const char *src_str, const char *grp_str)
12e41d03 73{
8813406f
DS
74 struct mfcctl mc;
75
76 /*
77 * This is just a hack to get the (S,G) received packet up into
78 * our user space so that we can register it.
79 *
80 * Once we run into a few more issues than we'll fix this.
81 */
82 memset(&mc, 0, sizeof(struct mfcctl));
83 mc.mfcc_origin = msg->im_src;
84 mc.mfcc_mcastgrp = msg->im_dst;
85 mc.mfcc_parent = ifp->ifindex;
86 mc.mfcc_ttls[MAXVIFS-1] = 1;
87 pim_mroute_add(&mc);
e355e30f
DS
88 return 0;
89}
12e41d03 90
e355e30f
DS
91static int
92pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const struct igmpmsg *msg,
93 const char *src_str, const char *grp_str)
94{
95 return 0;
96}
12e41d03 97
e355e30f
DS
98static int
99pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg,
100 const char *src_str, const char *grp_str)
101{
102 struct pim_ifchannel *ch;
103 struct pim_interface *pim_ifp;
12e41d03 104
e355e30f
DS
105 /*
106 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
12e41d03 107
e355e30f
DS
108 RFC 4601 4.8.2. PIM-SSM-Only Routers
109
110 iif is the incoming interface of the packet.
111 if (iif is in inherited_olist(S,G)) {
112 send Assert(S,G) on iif
113 }
114 */
12e41d03 115
e355e30f 116 if (!ifp) {
12e41d03 117 if (PIM_DEBUG_PIM_TRACE) {
e355e30f 118 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
12e41d03 119 __PRETTY_FUNCTION__,
e355e30f 120 src_str, grp_str, msg->im_vif);
12e41d03 121 }
e355e30f
DS
122 return -1;
123 }
12e41d03 124
e355e30f
DS
125 pim_ifp = ifp->info;
126 if (!pim_ifp) {
127 if (PIM_DEBUG_PIM_TRACE) {
128 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
129 __PRETTY_FUNCTION__,
130 src_str, grp_str, ifp->name);
12e41d03 131 }
e355e30f
DS
132 return -2;
133 }
12e41d03 134
e355e30f
DS
135 ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
136 if (!ch) {
137 if (PIM_DEBUG_PIM_TRACE) {
138 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
139 __PRETTY_FUNCTION__,
140 src_str, grp_str, ifp->name);
12e41d03 141 }
e355e30f
DS
142 return -3;
143 }
12e41d03 144
e355e30f
DS
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 */
12e41d03 158
e355e30f
DS
159 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
160 if (PIM_DEBUG_PIM_TRACE) {
161 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
162 __PRETTY_FUNCTION__,
163 src_str, grp_str, ifp->name);
12e41d03 164 }
e355e30f
DS
165 return -4;
166 }
12e41d03 167
e355e30f
DS
168 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
169 if (PIM_DEBUG_PIM_TRACE) {
170 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
171 __PRETTY_FUNCTION__,
172 src_str, grp_str, ifp->name);
12e41d03 173 }
e355e30f
DS
174 return -5;
175 }
12e41d03 176
e355e30f
DS
177 if (assert_action_a1(ch)) {
178 if (PIM_DEBUG_PIM_TRACE) {
179 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
180 __PRETTY_FUNCTION__,
181 src_str, grp_str, ifp->name);
12e41d03 182 }
e355e30f
DS
183 return -6;
184 }
185
186 return 0;
187}
188
189int pim_mroute_msg(int fd, const char *buf, int buf_size)
190{
191 struct interface *ifp;
192 const struct ip *ip_hdr;
193 const struct igmpmsg *msg;
194 char src_str[100] = "<src?>";
195 char grp_str[100] = "<grp?>";
12e41d03 196
e355e30f
DS
197 ip_hdr = (const struct ip *) buf;
198
199 /* kernel upcall must have protocol=0 */
200 if (ip_hdr->ip_p) {
201 /* this is not a kernel upcall */
202 if (PIM_DEBUG_PIM_TRACE) {
203 zlog_debug("%s: not a kernel upcall proto=%d msg_size=%d",
204 __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
205 }
12e41d03 206 return 0;
e355e30f
DS
207 }
208
209 msg = (const struct igmpmsg *) buf;
210
211 ifp = pim_if_find_by_vif_index(msg->im_vif);
212
213 if (PIM_DEBUG_PIM_TRACE) {
214 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
215 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
216 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
217 __PRETTY_FUNCTION__,
218 igmpmsgtype2str[msg->im_msgtype],
219 msg->im_msgtype,
220 ip_hdr->ip_p,
221 fd,
222 src_str,
223 grp_str,
224 ifp ? ifp->name : "<ifname?>",
225 msg->im_vif);
226 }
227
228 switch (msg->im_msgtype) {
229 case IGMPMSG_WRONGVIF:
230 return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str);
231 break;
232 case IGMPMSG_NOCACHE:
233 return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str);
234 break;
235 case IGMPMSG_WHOLEPKT:
236 return pim_mroute_msg_wholepkt(fd, ifp, msg, src_str, grp_str);
237 break;
238 default:
239 break;
240 }
12e41d03
DL
241
242 return 0;
243}
244
245static int mroute_read_msg(int fd)
246{
247 const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
248 char buf[1000];
249 int rd;
250
251 if (((int) sizeof(buf)) < msg_min_size) {
252 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
253 __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
254 return -1;
255 }
256
257 rd = read(fd, buf, sizeof(buf));
258 if (rd < 0) {
259 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
260 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
261 return -2;
262 }
263
264 if (rd < msg_min_size) {
265 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
266 __PRETTY_FUNCTION__, fd, rd, msg_min_size);
267 return -3;
268 }
269
270 return pim_mroute_msg(fd, buf, rd);
271}
272
273static int mroute_read(struct thread *t)
274{
275 int fd;
276 int result;
277
278 zassert(t);
279 zassert(!THREAD_ARG(t));
280
281 fd = THREAD_FD(t);
282 zassert(fd == qpim_mroute_socket_fd);
283
284 result = mroute_read_msg(fd);
285
286 /* Keep reading */
287 qpim_mroute_socket_reader = 0;
288 mroute_read_on();
289
290 return result;
291}
292
293static void mroute_read_on()
294{
295 zassert(!qpim_mroute_socket_reader);
296 zassert(PIM_MROUTE_IS_ENABLED);
297
298 THREAD_READ_ON(master, qpim_mroute_socket_reader,
299 mroute_read, 0, qpim_mroute_socket_fd);
300}
301
302static void mroute_read_off()
303{
304 THREAD_OFF(qpim_mroute_socket_reader);
305}
306
307int pim_mroute_socket_enable()
308{
309 int fd;
b45cefcb 310 struct in_addr pimreg = { .s_addr = 0 };
12e41d03
DL
311
312 if (PIM_MROUTE_IS_ENABLED)
313 return -1;
314
315 if ( pimd_privs.change (ZPRIVS_RAISE) )
316 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
317 safe_strerror (errno) );
318
319 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
320
321 if ( pimd_privs.change (ZPRIVS_LOWER) )
322 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
323 safe_strerror (errno) );
324
325 if (fd < 0) {
326 zlog_warn("Could not create mroute socket: errno=%d: %s",
327 errno, safe_strerror(errno));
328 return -2;
329 }
330
331 if (pim_mroute_set(fd, 1)) {
332 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
333 fd, errno, safe_strerror(errno));
334 close(fd);
335 return -3;
336 }
337
338 qpim_mroute_socket_fd = fd;
b45cefcb
DS
339 pim_mroute_add_vif(MAXVIFS-1, pimreg, VIFF_REGISTER);
340
12e41d03
DL
341 qpim_mroute_socket_creation = pim_time_monotonic_sec();
342 mroute_read_on();
343
344 zassert(PIM_MROUTE_IS_ENABLED);
345
346 return 0;
347}
348
349int pim_mroute_socket_disable()
350{
351 if (PIM_MROUTE_IS_DISABLED)
352 return -1;
353
354 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
355 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
356 qpim_mroute_socket_fd, errno, safe_strerror(errno));
357 return -2;
358 }
359
360 if (close(qpim_mroute_socket_fd)) {
361 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
362 qpim_mroute_socket_fd, errno, safe_strerror(errno));
363 return -3;
364 }
365
366 mroute_read_off();
367 qpim_mroute_socket_fd = -1;
368
369 zassert(PIM_MROUTE_IS_DISABLED);
370
371 return 0;
372}
373
374/*
375 For each network interface (e.g., physical or a virtual tunnel) that
376 would be used for multicast forwarding, a corresponding multicast
377 interface must be added to the kernel.
378 */
b45cefcb 379int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr, unsigned char flags)
12e41d03
DL
380{
381 struct vifctl vc;
382 int err;
383
384 if (PIM_MROUTE_IS_DISABLED) {
385 zlog_warn("%s: global multicast is disabled",
386 __PRETTY_FUNCTION__);
387 return -1;
388 }
389
390 memset(&vc, 0, sizeof(vc));
391 vc.vifc_vifi = vif_index;
b45cefcb 392 vc.vifc_flags = flags;
12e41d03
DL
393 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
394 vc.vifc_rate_limit = 0;
395 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
396
397#ifdef PIM_DVMRP_TUNNEL
398 if (vc.vifc_flags & VIFF_TUNNEL) {
399 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
400 }
401#endif
402
b45cefcb 403 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03
DL
404 if (err) {
405 char ifaddr_str[100];
406 int e = errno;
407
408 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
409
410 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
411 __FILE__, __PRETTY_FUNCTION__,
412 qpim_mroute_socket_fd, vif_index, ifaddr_str,
413 e, safe_strerror(e));
414 errno = e;
415 return -2;
416 }
417
418 return 0;
419}
420
421int pim_mroute_del_vif(int vif_index)
422{
423 struct vifctl vc;
424 int err;
425
426 if (PIM_MROUTE_IS_DISABLED) {
427 zlog_warn("%s: global multicast is disabled",
428 __PRETTY_FUNCTION__);
429 return -1;
430 }
431
432 memset(&vc, 0, sizeof(vc));
433 vc.vifc_vifi = vif_index;
434
435 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
436 if (err) {
437 int e = errno;
438 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
439 __FILE__, __PRETTY_FUNCTION__,
440 qpim_mroute_socket_fd, vif_index,
441 e, safe_strerror(e));
442 errno = e;
443 return -2;
444 }
445
446 return 0;
447}
448
449int pim_mroute_add(struct mfcctl *mc)
450{
451 int err;
452
453 qpim_mroute_add_last = pim_time_monotonic_sec();
454 ++qpim_mroute_add_events;
455
456 if (PIM_MROUTE_IS_DISABLED) {
457 zlog_warn("%s: global multicast is disabled",
458 __PRETTY_FUNCTION__);
459 return -1;
460 }
461
462 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
463 mc, sizeof(*mc));
464 if (err) {
465 int e = errno;
466 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
467 __FILE__, __PRETTY_FUNCTION__,
468 qpim_mroute_socket_fd,
469 e, safe_strerror(e));
470 errno = e;
471 return -2;
472 }
473
474 return 0;
475}
476
477int pim_mroute_del(struct mfcctl *mc)
478{
479 int err;
480
481 qpim_mroute_del_last = pim_time_monotonic_sec();
482 ++qpim_mroute_del_events;
483
484 if (PIM_MROUTE_IS_DISABLED) {
485 zlog_warn("%s: global multicast is disabled",
486 __PRETTY_FUNCTION__);
487 return -1;
488 }
489
490 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
491 if (err) {
492 int e = errno;
493 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
494 __FILE__, __PRETTY_FUNCTION__,
495 qpim_mroute_socket_fd,
496 e, safe_strerror(e));
497 errno = e;
498 return -2;
499 }
500
501 return 0;
502}