blob: d88373625607b8581b76aa7ac4bbe1ba5ee8b5a7 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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"
Leonard Herve596470f2009-08-11 15:45:26 -030025#include "privs.h"
Everton Marques871dbcf2009-08-11 15:43:05 -030026
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
Leonard Herve596470f2009-08-11 15:45:26 -030034/* GLOBAL VARS */
35extern struct zebra_privs_t pimd_privs;
36
Everton Marques871dbcf2009-08-11 15:43:05 -030037static 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__,
Everton Marquese96f0af2009-08-11 15:48:02 -030050 fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -030051 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) {
David Lamparter5c697982012-02-16 04:47:56 +0100204 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
Everton Marques871dbcf2009-08-11 15:43:05 -0300205 __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",
Everton Marquese96f0af2009-08-11 15:48:02 -0300212 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
Everton Marques871dbcf2009-08-11 15:43:05 -0300213 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;
262
263 if (PIM_MROUTE_IS_ENABLED)
264 return -1;
265
Leonard Herve596470f2009-08-11 15:45:26 -0300266 if ( pimd_privs.change (ZPRIVS_RAISE) )
267 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
268 safe_strerror (errno) );
269
Everton Marques871dbcf2009-08-11 15:43:05 -0300270 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
Leonard Herve596470f2009-08-11 15:45:26 -0300271
272 if ( pimd_privs.change (ZPRIVS_LOWER) )
273 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
274 safe_strerror (errno) );
275
Everton Marques871dbcf2009-08-11 15:43:05 -0300276 if (fd < 0) {
277 zlog_warn("Could not create mroute socket: errno=%d: %s",
Everton Marquese96f0af2009-08-11 15:48:02 -0300278 errno, safe_strerror(errno));
Everton Marques871dbcf2009-08-11 15:43:05 -0300279 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",
Everton Marquese96f0af2009-08-11 15:48:02 -0300284 fd, errno, safe_strerror(errno));
Everton Marques871dbcf2009-08-11 15:43:05 -0300285 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
298int 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",
Everton Marquese96f0af2009-08-11 15:48:02 -0300305 qpim_mroute_socket_fd, errno, safe_strerror(errno));
Everton Marques871dbcf2009-08-11 15:43:05 -0300306 return -2;
307 }
308
309 if (close(qpim_mroute_socket_fd)) {
310 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
Everton Marquese96f0af2009-08-11 15:48:02 -0300311 qpim_mroute_socket_fd, errno, safe_strerror(errno));
Everton Marques871dbcf2009-08-11 15:43:05 -0300312 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 */
328int 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,
Everton Marquese96f0af2009-08-11 15:48:02 -0300362 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -0300363 errno = e;
364 return -2;
365 }
366
367 return 0;
368}
369
370int 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,
Everton Marquese96f0af2009-08-11 15:48:02 -0300390 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -0300391 errno = e;
392 return -2;
393 }
394
395 return 0;
396}
397
398int pim_mroute_add(struct mfcctl *mc)
399{
400 int err;
401
402 if (PIM_MROUTE_IS_DISABLED) {
403 zlog_warn("%s: global multicast is disabled",
404 __PRETTY_FUNCTION__);
405 return -1;
406 }
407
408 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
409 mc, sizeof(*mc));
410 if (err) {
411 int e = errno;
412 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
413 __FILE__, __PRETTY_FUNCTION__,
414 qpim_mroute_socket_fd,
Everton Marquese96f0af2009-08-11 15:48:02 -0300415 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -0300416 errno = e;
417 return -2;
418 }
419
420 return 0;
421}
422
423int pim_mroute_del(struct mfcctl *mc)
424{
425 int err;
426
427 if (PIM_MROUTE_IS_DISABLED) {
428 zlog_warn("%s: global multicast is disabled",
429 __PRETTY_FUNCTION__);
430 return -1;
431 }
432
433 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
434 if (err) {
435 int e = errno;
436 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
437 __FILE__, __PRETTY_FUNCTION__,
438 qpim_mroute_socket_fd,
Everton Marquese96f0af2009-08-11 15:48:02 -0300439 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -0300440 errno = e;
441 return -2;
442 }
443
444 return 0;
445}