blob: f021abaa31aad55e64a8a3359ab05b496e3f44fd [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"
25
26#include "pimd.h"
27#include "pim_mroute.h"
28#include "pim_str.h"
29#include "pim_time.h"
30#include "pim_iface.h"
31#include "pim_macro.h"
32
33static void mroute_read_on(void);
34
35static int pim_mroute_set(int fd, int enable)
36{
37 int err;
38 int opt = enable ? MRT_INIT : MRT_DONE;
39 socklen_t opt_len = sizeof(opt);
40
41 err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
42 if (err) {
43 int e = errno;
44 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
45 __FILE__, __PRETTY_FUNCTION__,
46 fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, strerror(e));
47 errno = e;
48 return -1;
49 }
50
51#if 0
52 zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
53 __FILE__, __PRETTY_FUNCTION__,
54 fd, opt);
55#endif
56
57 return 0;
58}
59
60int pim_mroute_msg(int fd, const char *buf, int buf_size)
61{
62 struct interface *ifp;
63 const struct ip *ip_hdr;
64 const struct igmpmsg *msg;
65 const char *upcall;
66 char src_str[100];
67 char grp_str[100];
68
69 ip_hdr = (const struct ip *) buf;
70
71 /* kernel upcall must have protocol=0 */
72 if (ip_hdr->ip_p) {
73 /* this is not a kernel upcall */
74#ifdef PIM_UNEXPECTED_KERNEL_UPCALL
75 zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d",
76 __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
77#endif
78 return 0;
79 }
80
81 msg = (const struct igmpmsg *) buf;
82
83 switch (msg->im_msgtype) {
84 case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break;
85 case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break;
86 case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break;
87 default: upcall = "<unknown_upcall?>";
88 }
89 ifp = pim_if_find_by_vif_index(msg->im_vif);
90 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
91 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
92
93 if (msg->im_msgtype == IGMPMSG_WRONGVIF) {
94 struct pim_ifchannel *ch;
95 struct pim_interface *pim_ifp;
96
97 /*
98 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
99
100 RFC 4601 4.8.2. PIM-SSM-Only Routers
101
102 iif is the incoming interface of the packet.
103 if (iif is in inherited_olist(S,G)) {
104 send Assert(S,G) on iif
105 }
106 */
107
108 if (PIM_DEBUG_PIM_TRACE) {
109 zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
110 __PRETTY_FUNCTION__,
111 fd,
112 src_str,
113 grp_str,
114 ifp ? ifp->name : "<ifname?>",
115 msg->im_vif);
116 }
117
118 if (!ifp) {
119 zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
120 __PRETTY_FUNCTION__,
121 src_str, grp_str, msg->im_vif);
122 return -1;
123 }
124
125 pim_ifp = ifp->info;
126 if (!pim_ifp) {
127 zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
128 __PRETTY_FUNCTION__,
129 src_str, grp_str, ifp->name);
130 return -2;
131 }
132
133 ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
134 if (!ch) {
135 zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
136 __PRETTY_FUNCTION__,
137 src_str, grp_str, ifp->name);
138 return -3;
139 }
140
141 /*
142 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
143
144 Transitions from NoInfo State
145
146 An (S,G) data packet arrives on interface I, AND
147 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
148 downstream interface that is in our (S,G) outgoing interface
149 list. We optimistically assume that we will be the assert
150 winner for this (S,G), and so we transition to the "I am Assert
151 Winner" state and perform Actions A1 (below), which will
152 initiate the assert negotiation for (S,G).
153 */
154
155 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
156 zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
157 __PRETTY_FUNCTION__,
158 src_str, grp_str, ifp->name);
159 return -4;
160 }
161
162 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
163 zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
164 __PRETTY_FUNCTION__,
165 src_str, grp_str, ifp->name);
166 return -5;
167 }
168
169 if (assert_action_a1(ch)) {
170 zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
171 __PRETTY_FUNCTION__,
172 src_str, grp_str, ifp->name);
173 return -6;
174 }
175
176 return 0;
177 } /* IGMPMSG_WRONGVIF */
178
179 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
180 __PRETTY_FUNCTION__,
181 upcall,
182 msg->im_msgtype,
183 ip_hdr->ip_p,
184 fd,
185 src_str,
186 grp_str,
187 ifp ? ifp->name : "<ifname?>",
188 msg->im_vif);
189
190 return 0;
191}
192
193static int mroute_read_msg(int fd)
194{
195 const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
196 char buf[1000];
197 int rd;
198
199 if (((int) sizeof(buf)) < msg_min_size) {
200 zlog_err("%s: fd=%d: buf size=%d lower than msg_min=%d",
201 __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
202 return -1;
203 }
204
205 rd = read(fd, buf, sizeof(buf));
206 if (rd < 0) {
207 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
208 __PRETTY_FUNCTION__, fd, errno, strerror(errno));
209 return -2;
210 }
211
212 if (rd < msg_min_size) {
213 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
214 __PRETTY_FUNCTION__, fd, rd, msg_min_size);
215 return -3;
216 }
217
218 return pim_mroute_msg(fd, buf, rd);
219}
220
221static int mroute_read(struct thread *t)
222{
223 int fd;
224 int result;
225
226 zassert(t);
227 zassert(!THREAD_ARG(t));
228
229 fd = THREAD_FD(t);
230 zassert(fd == qpim_mroute_socket_fd);
231
232 result = mroute_read_msg(fd);
233
234 /* Keep reading */
235 qpim_mroute_socket_reader = 0;
236 mroute_read_on();
237
238 return result;
239}
240
241static void mroute_read_on()
242{
243 zassert(!qpim_mroute_socket_reader);
244 zassert(PIM_MROUTE_IS_ENABLED);
245
246 THREAD_READ_ON(master, qpim_mroute_socket_reader,
247 mroute_read, 0, qpim_mroute_socket_fd);
248}
249
250static void mroute_read_off()
251{
252 THREAD_OFF(qpim_mroute_socket_reader);
253}
254
255int pim_mroute_socket_enable()
256{
257 int fd;
258
259 if (PIM_MROUTE_IS_ENABLED)
260 return -1;
261
262 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
263 if (fd < 0) {
264 zlog_warn("Could not create mroute socket: errno=%d: %s",
265 errno, strerror(errno));
266 return -2;
267 }
268
269 if (pim_mroute_set(fd, 1)) {
270 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
271 fd, errno, strerror(errno));
272 close(fd);
273 return -3;
274 }
275
276 qpim_mroute_socket_fd = fd;
277 qpim_mroute_socket_creation = pim_time_monotonic_sec();
278 mroute_read_on();
279
280 zassert(PIM_MROUTE_IS_ENABLED);
281
282 return 0;
283}
284
285int pim_mroute_socket_disable()
286{
287 if (PIM_MROUTE_IS_DISABLED)
288 return -1;
289
290 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
291 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
292 qpim_mroute_socket_fd, errno, strerror(errno));
293 return -2;
294 }
295
296 if (close(qpim_mroute_socket_fd)) {
297 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
298 qpim_mroute_socket_fd, errno, strerror(errno));
299 return -3;
300 }
301
302 mroute_read_off();
303 qpim_mroute_socket_fd = -1;
304
305 zassert(PIM_MROUTE_IS_DISABLED);
306
307 return 0;
308}
309
310/*
311 For each network interface (e.g., physical or a virtual tunnel) that
312 would be used for multicast forwarding, a corresponding multicast
313 interface must be added to the kernel.
314 */
315int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
316{
317 struct vifctl vc;
318 int err;
319
320 if (PIM_MROUTE_IS_DISABLED) {
321 zlog_warn("%s: global multicast is disabled",
322 __PRETTY_FUNCTION__);
323 return -1;
324 }
325
326 memset(&vc, 0, sizeof(vc));
327 vc.vifc_vifi = vif_index;
328 vc.vifc_flags = 0;
329 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
330 vc.vifc_rate_limit = 0;
331 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
332
333#ifdef PIM_DVMRP_TUNNEL
334 if (vc.vifc_flags & VIFF_TUNNEL) {
335 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
336 }
337#endif
338
339 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
340 if (err) {
341 char ifaddr_str[100];
342 int e = errno;
343
344 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
345
346 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
347 __FILE__, __PRETTY_FUNCTION__,
348 qpim_mroute_socket_fd, vif_index, ifaddr_str,
349 e, strerror(e));
350 errno = e;
351 return -2;
352 }
353
354 return 0;
355}
356
357int pim_mroute_del_vif(int vif_index)
358{
359 struct vifctl vc;
360 int err;
361
362 if (PIM_MROUTE_IS_DISABLED) {
363 zlog_warn("%s: global multicast is disabled",
364 __PRETTY_FUNCTION__);
365 return -1;
366 }
367
368 memset(&vc, 0, sizeof(vc));
369 vc.vifc_vifi = vif_index;
370
371 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
372 if (err) {
373 int e = errno;
374 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
375 __FILE__, __PRETTY_FUNCTION__,
376 qpim_mroute_socket_fd, vif_index,
377 e, strerror(e));
378 errno = e;
379 return -2;
380 }
381
382 return 0;
383}
384
385int pim_mroute_add(struct mfcctl *mc)
386{
387 int err;
388
389 if (PIM_MROUTE_IS_DISABLED) {
390 zlog_warn("%s: global multicast is disabled",
391 __PRETTY_FUNCTION__);
392 return -1;
393 }
394
395 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
396 mc, sizeof(*mc));
397 if (err) {
398 int e = errno;
399 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
400 __FILE__, __PRETTY_FUNCTION__,
401 qpim_mroute_socket_fd,
402 e, strerror(e));
403 errno = e;
404 return -2;
405 }
406
407 return 0;
408}
409
410int pim_mroute_del(struct mfcctl *mc)
411{
412 int err;
413
414 if (PIM_MROUTE_IS_DISABLED) {
415 zlog_warn("%s: global multicast is disabled",
416 __PRETTY_FUNCTION__);
417 return -1;
418 }
419
420 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
421 if (err) {
422 int e = errno;
423 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
424 __FILE__, __PRETTY_FUNCTION__,
425 qpim_mroute_socket_fd,
426 e, strerror(e));
427 errno = e;
428 return -2;
429 }
430
431 return 0;
432}