]>
Commit | Line | Data |
---|---|---|
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 */ | |
35 | extern struct zebra_privs_t pimd_privs; | |
36 | ||
37 | static void mroute_read_on(void); | |
38 | ||
39 | static 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 |
64 | static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = { |
65 | "<unknown_upcall?>", | |
66 | "NOCACHE", | |
67 | "WRONGVIF", | |
68 | "WHOLEPKT", }; | |
69 | ||
70 | static int | |
71 | pim_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 |
91 | static int |
92 | pim_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 |
98 | static int |
99 | pim_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 | ||
189 | int 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 | ||
245 | static 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 | ||
273 | static 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 | ||
293 | static 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 | ||
302 | static void mroute_read_off() | |
303 | { | |
304 | THREAD_OFF(qpim_mroute_socket_reader); | |
305 | } | |
306 | ||
307 | int 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 | ||
349 | int 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 | 379 | int 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 | ||
421 | int 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 | ||
449 | int 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 | ||
477 | int 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 | } |