]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
pimd: Register with kernel to get unknown multicast packets
[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
64int 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
197static 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
225static 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
245static 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
254static void mroute_read_off()
255{
256 THREAD_OFF(qpim_mroute_socket_reader);
257}
258
259int pim_mroute_socket_enable()
260{
261 int fd;
b45cefcb 262 struct in_addr pimreg = { .s_addr = 0 };
12e41d03
DL
263
264 if (PIM_MROUTE_IS_ENABLED)
265 return -1;
266
267 if ( pimd_privs.change (ZPRIVS_RAISE) )
268 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
269 safe_strerror (errno) );
270
271 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
272
273 if ( pimd_privs.change (ZPRIVS_LOWER) )
274 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
275 safe_strerror (errno) );
276
277 if (fd < 0) {
278 zlog_warn("Could not create mroute socket: errno=%d: %s",
279 errno, safe_strerror(errno));
280 return -2;
281 }
282
283 if (pim_mroute_set(fd, 1)) {
284 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
285 fd, errno, safe_strerror(errno));
286 close(fd);
287 return -3;
288 }
289
290 qpim_mroute_socket_fd = fd;
b45cefcb
DS
291 pim_mroute_add_vif(MAXVIFS-1, pimreg, VIFF_REGISTER);
292
12e41d03
DL
293 qpim_mroute_socket_creation = pim_time_monotonic_sec();
294 mroute_read_on();
295
296 zassert(PIM_MROUTE_IS_ENABLED);
297
298 return 0;
299}
300
301int pim_mroute_socket_disable()
302{
303 if (PIM_MROUTE_IS_DISABLED)
304 return -1;
305
306 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
307 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
308 qpim_mroute_socket_fd, errno, safe_strerror(errno));
309 return -2;
310 }
311
312 if (close(qpim_mroute_socket_fd)) {
313 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
314 qpim_mroute_socket_fd, errno, safe_strerror(errno));
315 return -3;
316 }
317
318 mroute_read_off();
319 qpim_mroute_socket_fd = -1;
320
321 zassert(PIM_MROUTE_IS_DISABLED);
322
323 return 0;
324}
325
326/*
327 For each network interface (e.g., physical or a virtual tunnel) that
328 would be used for multicast forwarding, a corresponding multicast
329 interface must be added to the kernel.
330 */
b45cefcb 331int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr, unsigned char flags)
12e41d03
DL
332{
333 struct vifctl vc;
334 int err;
335
336 if (PIM_MROUTE_IS_DISABLED) {
337 zlog_warn("%s: global multicast is disabled",
338 __PRETTY_FUNCTION__);
339 return -1;
340 }
341
342 memset(&vc, 0, sizeof(vc));
343 vc.vifc_vifi = vif_index;
b45cefcb 344 vc.vifc_flags = flags;
12e41d03
DL
345 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
346 vc.vifc_rate_limit = 0;
347 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
348
349#ifdef PIM_DVMRP_TUNNEL
350 if (vc.vifc_flags & VIFF_TUNNEL) {
351 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
352 }
353#endif
354
b45cefcb 355 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03
DL
356 if (err) {
357 char ifaddr_str[100];
358 int e = errno;
359
360 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
361
362 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
363 __FILE__, __PRETTY_FUNCTION__,
364 qpim_mroute_socket_fd, vif_index, ifaddr_str,
365 e, safe_strerror(e));
366 errno = e;
367 return -2;
368 }
369
370 return 0;
371}
372
373int pim_mroute_del_vif(int vif_index)
374{
375 struct vifctl vc;
376 int err;
377
378 if (PIM_MROUTE_IS_DISABLED) {
379 zlog_warn("%s: global multicast is disabled",
380 __PRETTY_FUNCTION__);
381 return -1;
382 }
383
384 memset(&vc, 0, sizeof(vc));
385 vc.vifc_vifi = vif_index;
386
387 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
388 if (err) {
389 int e = errno;
390 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
391 __FILE__, __PRETTY_FUNCTION__,
392 qpim_mroute_socket_fd, vif_index,
393 e, safe_strerror(e));
394 errno = e;
395 return -2;
396 }
397
398 return 0;
399}
400
401int pim_mroute_add(struct mfcctl *mc)
402{
403 int err;
404
405 qpim_mroute_add_last = pim_time_monotonic_sec();
406 ++qpim_mroute_add_events;
407
408 if (PIM_MROUTE_IS_DISABLED) {
409 zlog_warn("%s: global multicast is disabled",
410 __PRETTY_FUNCTION__);
411 return -1;
412 }
413
414 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
415 mc, sizeof(*mc));
416 if (err) {
417 int e = errno;
418 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
419 __FILE__, __PRETTY_FUNCTION__,
420 qpim_mroute_socket_fd,
421 e, safe_strerror(e));
422 errno = e;
423 return -2;
424 }
425
426 return 0;
427}
428
429int pim_mroute_del(struct mfcctl *mc)
430{
431 int err;
432
433 qpim_mroute_del_last = pim_time_monotonic_sec();
434 ++qpim_mroute_del_events;
435
436 if (PIM_MROUTE_IS_DISABLED) {
437 zlog_warn("%s: global multicast is disabled",
438 __PRETTY_FUNCTION__);
439 return -1;
440 }
441
442 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
443 if (err) {
444 int e = errno;
445 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
446 __FILE__, __PRETTY_FUNCTION__,
447 qpim_mroute_socket_fd,
448 e, safe_strerror(e));
449 errno = e;
450 return -2;
451 }
452
453 return 0;
454}