]>
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 | |
12e41d03 DL |
19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | #include "log.h" | |
23 | #include "privs.h" | |
744d91b3 | 24 | #include "if.h" |
065bee4b | 25 | #include "prefix.h" |
12e41d03 DL |
26 | |
27 | #include "pimd.h" | |
28 | #include "pim_mroute.h" | |
37653d4f | 29 | #include "pim_oil.h" |
12e41d03 DL |
30 | #include "pim_str.h" |
31 | #include "pim_time.h" | |
32 | #include "pim_iface.h" | |
33 | #include "pim_macro.h" | |
c8ae3ce8 | 34 | #include "pim_rp.h" |
59471fb8 | 35 | #include "pim_oil.h" |
998af219 | 36 | #include "pim_register.h" |
12e41d03 DL |
37 | |
38 | /* GLOBAL VARS */ | |
39 | extern struct zebra_privs_t pimd_privs; | |
40 | ||
41 | static void mroute_read_on(void); | |
42 | ||
43 | static int pim_mroute_set(int fd, int enable) | |
44 | { | |
45 | int err; | |
46 | int opt = enable ? MRT_INIT : MRT_DONE; | |
47 | socklen_t opt_len = sizeof(opt); | |
48 | ||
49 | err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len); | |
50 | if (err) { | |
51 | int e = errno; | |
52 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", | |
53 | __FILE__, __PRETTY_FUNCTION__, | |
54 | fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e)); | |
55 | errno = e; | |
56 | return -1; | |
57 | } | |
58 | ||
59 | #if 0 | |
60 | zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok", | |
61 | __FILE__, __PRETTY_FUNCTION__, | |
62 | fd, opt); | |
63 | #endif | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
065bee4b DS |
68 | static int |
69 | pim_mroute_connected_to_source (struct interface *ifp, struct in_addr src) | |
70 | { | |
71 | struct listnode *cnode; | |
72 | struct connected *c; | |
73 | struct prefix p; | |
74 | ||
75 | p.family = AF_INET; | |
76 | p.u.prefix4 = src; | |
77 | p.prefixlen = IPV4_MAX_BITLEN; | |
78 | ||
79 | for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) | |
80 | { | |
81 | if ((c->address->family == AF_INET) && | |
82 | prefix_match (CONNECTED_PREFIX (c), &p)) | |
83 | { | |
84 | return 1; | |
85 | } | |
86 | } | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
e355e30f DS |
91 | static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = { |
92 | "<unknown_upcall?>", | |
93 | "NOCACHE", | |
94 | "WRONGVIF", | |
95 | "WHOLEPKT", }; | |
96 | ||
97 | static int | |
98 | pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg, | |
99 | const char *src_str, const char *grp_str) | |
12e41d03 | 100 | { |
04b40f02 | 101 | struct pim_interface *pim_ifp = ifp->info; |
59471fb8 | 102 | struct pim_upstream *up; |
065bee4b | 103 | struct pim_rpf *rpg; |
04b40f02 | 104 | |
c8ae3ce8 | 105 | rpg = RP(msg->im_dst); |
04b40f02 DS |
106 | /* |
107 | * If the incoming interface is unknown OR | |
108 | * the Interface type is SSM we don't need to | |
109 | * do anything here | |
110 | */ | |
ed66602c | 111 | if ((rpg->rpf_addr.s_addr == INADDR_NONE) || |
04b40f02 | 112 | (!pim_ifp) || |
b45fd505 | 113 | (!(PIM_I_am_DR(pim_ifp))) || |
04b40f02 DS |
114 | (pim_ifp->itype == PIM_INTERFACE_SSM)) |
115 | return 0; | |
116 | ||
065bee4b DS |
117 | /* |
118 | * If we've received a multicast packet that isn't connected to | |
119 | * us | |
120 | */ | |
121 | if (!pim_mroute_connected_to_source (ifp, msg->im_src)) | |
122 | { | |
123 | if (PIM_DEBUG_PIM_TRACE) | |
124 | zlog_debug ("%s: Received incoming packet that does originate on our seg", | |
125 | __PRETTY_FUNCTION__); | |
126 | return 0; | |
127 | } | |
128 | ||
04b40f02 DS |
129 | if (PIM_DEBUG_PIM_TRACE) { |
130 | zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption", | |
131 | __PRETTY_FUNCTION__, grp_str, src_str); | |
132 | } | |
8813406f | 133 | |
651d0f71 | 134 | up = pim_upstream_add(msg->im_src, msg->im_dst, ifp); |
59471fb8 DS |
135 | if (!up) { |
136 | if (PIM_DEBUG_PIM_TRACE) { | |
137 | zlog_debug("%s: Failure to add upstream information for (%s,%s)", | |
138 | __PRETTY_FUNCTION__, | |
139 | src_str, grp_str); | |
140 | } | |
141 | return 0; | |
142 | } | |
143 | ||
3667e8a0 DS |
144 | pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD); |
145 | ||
59471fb8 DS |
146 | up->channel_oil = pim_channel_oil_add(msg->im_dst, |
147 | msg->im_src, | |
148 | pim_ifp->mroute_vif_index); | |
149 | if (!up->channel_oil) { | |
150 | if (PIM_DEBUG_PIM_TRACE) { | |
151 | zlog_debug("%s: Failure to add channel oil for (%s,%s)", | |
152 | __PRETTY_FUNCTION__, | |
153 | src_str, grp_str); | |
154 | } | |
155 | return 0; | |
156 | } | |
25a335e0 | 157 | up->channel_oil->cc.pktcnt++; |
59471fb8 DS |
158 | |
159 | pim_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE); | |
160 | ||
e355e30f DS |
161 | return 0; |
162 | } | |
12e41d03 | 163 | |
e355e30f | 164 | static int |
c8ae3ce8 | 165 | pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf, |
e355e30f DS |
166 | const char *src_str, const char *grp_str) |
167 | { | |
59471fb8 | 168 | struct pim_interface *pim_ifp; |
c8ae3ce8 | 169 | struct in_addr group; |
59471fb8 | 170 | struct in_addr src; |
ed66602c | 171 | struct pim_rpf *rpg; |
c8ae3ce8 | 172 | const struct ip *ip_hdr; |
59471fb8 | 173 | struct pim_upstream *up; |
04b40f02 | 174 | |
c8ae3ce8 DS |
175 | ip_hdr = (const struct ip *)buf; |
176 | ||
59471fb8 | 177 | src = ip_hdr->ip_src; |
c8ae3ce8 DS |
178 | group = ip_hdr->ip_dst; |
179 | ||
59471fb8 DS |
180 | up = pim_upstream_find(src, group); |
181 | if (!up) { | |
182 | if (PIM_DEBUG_PIM_TRACE) { | |
183 | zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)", | |
184 | __PRETTY_FUNCTION__, src_str, grp_str); | |
185 | } | |
186 | return 0; | |
187 | } | |
188 | ||
998af219 DS |
189 | pim_ifp = up->rpf.source_nexthop.interface->info; |
190 | ||
c8ae3ce8 DS |
191 | rpg = RP(group); |
192 | ||
ed66602c | 193 | if ((rpg->rpf_addr.s_addr == INADDR_NONE) || |
c8ae3ce8 | 194 | (!pim_ifp) || |
b45fd505 | 195 | (!(PIM_I_am_DR(pim_ifp))) || |
c8ae3ce8 | 196 | (pim_ifp->itype == PIM_INTERFACE_SSM)) { |
998af219 DS |
197 | if (PIM_DEBUG_PIM_TRACE) { |
198 | zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__); | |
199 | } | |
c8ae3ce8 | 200 | return 0; |
04b40f02 | 201 | } |
84366c7e | 202 | |
998af219 | 203 | pim_register_send((const struct ip *)(buf + sizeof(struct ip)), rpg); |
e355e30f DS |
204 | return 0; |
205 | } | |
12e41d03 | 206 | |
e355e30f DS |
207 | static int |
208 | pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg, | |
209 | const char *src_str, const char *grp_str) | |
210 | { | |
211 | struct pim_ifchannel *ch; | |
212 | struct pim_interface *pim_ifp; | |
12e41d03 | 213 | |
e355e30f DS |
214 | /* |
215 | Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. | |
12e41d03 | 216 | |
e355e30f DS |
217 | RFC 4601 4.8.2. PIM-SSM-Only Routers |
218 | ||
219 | iif is the incoming interface of the packet. | |
220 | if (iif is in inherited_olist(S,G)) { | |
221 | send Assert(S,G) on iif | |
222 | } | |
223 | */ | |
12e41d03 | 224 | |
e355e30f | 225 | if (!ifp) { |
12e41d03 | 226 | if (PIM_DEBUG_PIM_TRACE) { |
e355e30f | 227 | zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", |
12e41d03 | 228 | __PRETTY_FUNCTION__, |
e355e30f | 229 | src_str, grp_str, msg->im_vif); |
12e41d03 | 230 | } |
e355e30f DS |
231 | return -1; |
232 | } | |
12e41d03 | 233 | |
e355e30f DS |
234 | pim_ifp = ifp->info; |
235 | if (!pim_ifp) { | |
236 | if (PIM_DEBUG_PIM_TRACE) { | |
237 | zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s", | |
238 | __PRETTY_FUNCTION__, | |
239 | src_str, grp_str, ifp->name); | |
12e41d03 | 240 | } |
e355e30f DS |
241 | return -2; |
242 | } | |
12e41d03 | 243 | |
e355e30f DS |
244 | ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst); |
245 | if (!ch) { | |
246 | if (PIM_DEBUG_PIM_TRACE) { | |
247 | zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s", | |
248 | __PRETTY_FUNCTION__, | |
249 | src_str, grp_str, ifp->name); | |
12e41d03 | 250 | } |
e355e30f DS |
251 | return -3; |
252 | } | |
12e41d03 | 253 | |
e355e30f DS |
254 | /* |
255 | RFC 4601: 4.6.1. (S,G) Assert Message State Machine | |
256 | ||
257 | Transitions from NoInfo State | |
258 | ||
259 | An (S,G) data packet arrives on interface I, AND | |
260 | CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an | |
261 | downstream interface that is in our (S,G) outgoing interface | |
262 | list. We optimistically assume that we will be the assert | |
263 | winner for this (S,G), and so we transition to the "I am Assert | |
264 | Winner" state and perform Actions A1 (below), which will | |
265 | initiate the assert negotiation for (S,G). | |
266 | */ | |
12e41d03 | 267 | |
e355e30f DS |
268 | if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { |
269 | if (PIM_DEBUG_PIM_TRACE) { | |
270 | zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s", | |
271 | __PRETTY_FUNCTION__, | |
272 | src_str, grp_str, ifp->name); | |
12e41d03 | 273 | } |
e355e30f DS |
274 | return -4; |
275 | } | |
12e41d03 | 276 | |
e355e30f DS |
277 | if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { |
278 | if (PIM_DEBUG_PIM_TRACE) { | |
279 | zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel", | |
280 | __PRETTY_FUNCTION__, | |
281 | src_str, grp_str, ifp->name); | |
12e41d03 | 282 | } |
e355e30f DS |
283 | return -5; |
284 | } | |
12e41d03 | 285 | |
e355e30f DS |
286 | if (assert_action_a1(ch)) { |
287 | if (PIM_DEBUG_PIM_TRACE) { | |
288 | zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s", | |
289 | __PRETTY_FUNCTION__, | |
290 | src_str, grp_str, ifp->name); | |
12e41d03 | 291 | } |
e355e30f DS |
292 | return -6; |
293 | } | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | int pim_mroute_msg(int fd, const char *buf, int buf_size) | |
299 | { | |
300 | struct interface *ifp; | |
301 | const struct ip *ip_hdr; | |
302 | const struct igmpmsg *msg; | |
303 | char src_str[100] = "<src?>"; | |
304 | char grp_str[100] = "<grp?>"; | |
12e41d03 | 305 | |
e355e30f DS |
306 | ip_hdr = (const struct ip *) buf; |
307 | ||
308 | /* kernel upcall must have protocol=0 */ | |
309 | if (ip_hdr->ip_p) { | |
310 | /* this is not a kernel upcall */ | |
311 | if (PIM_DEBUG_PIM_TRACE) { | |
e5d33c83 DS |
312 | pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str)); |
313 | pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str)); | |
314 | zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d", | |
315 | __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size); | |
e355e30f | 316 | } |
12e41d03 | 317 | return 0; |
e355e30f DS |
318 | } |
319 | ||
320 | msg = (const struct igmpmsg *) buf; | |
321 | ||
322 | ifp = pim_if_find_by_vif_index(msg->im_vif); | |
323 | ||
324 | if (PIM_DEBUG_PIM_TRACE) { | |
325 | pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str)); | |
326 | pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str)); | |
327 | zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", | |
328 | __PRETTY_FUNCTION__, | |
329 | igmpmsgtype2str[msg->im_msgtype], | |
330 | msg->im_msgtype, | |
331 | ip_hdr->ip_p, | |
332 | fd, | |
333 | src_str, | |
334 | grp_str, | |
335 | ifp ? ifp->name : "<ifname?>", | |
336 | msg->im_vif); | |
337 | } | |
338 | ||
339 | switch (msg->im_msgtype) { | |
340 | case IGMPMSG_WRONGVIF: | |
341 | return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str); | |
342 | break; | |
343 | case IGMPMSG_NOCACHE: | |
344 | return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str); | |
345 | break; | |
346 | case IGMPMSG_WHOLEPKT: | |
9f6d6b12 | 347 | return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg, src_str, grp_str); |
e355e30f DS |
348 | break; |
349 | default: | |
350 | break; | |
351 | } | |
12e41d03 DL |
352 | |
353 | return 0; | |
354 | } | |
355 | ||
356 | static int mroute_read_msg(int fd) | |
357 | { | |
358 | const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg)); | |
359 | char buf[1000]; | |
360 | int rd; | |
361 | ||
362 | if (((int) sizeof(buf)) < msg_min_size) { | |
363 | zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d", | |
364 | __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size); | |
365 | return -1; | |
366 | } | |
367 | ||
368 | rd = read(fd, buf, sizeof(buf)); | |
369 | if (rd < 0) { | |
370 | zlog_warn("%s: failure reading fd=%d: errno=%d: %s", | |
371 | __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); | |
372 | return -2; | |
373 | } | |
374 | ||
375 | if (rd < msg_min_size) { | |
376 | zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d", | |
377 | __PRETTY_FUNCTION__, fd, rd, msg_min_size); | |
378 | return -3; | |
379 | } | |
380 | ||
381 | return pim_mroute_msg(fd, buf, rd); | |
382 | } | |
383 | ||
384 | static int mroute_read(struct thread *t) | |
385 | { | |
386 | int fd; | |
387 | int result; | |
388 | ||
389 | zassert(t); | |
390 | zassert(!THREAD_ARG(t)); | |
391 | ||
392 | fd = THREAD_FD(t); | |
393 | zassert(fd == qpim_mroute_socket_fd); | |
394 | ||
395 | result = mroute_read_msg(fd); | |
396 | ||
397 | /* Keep reading */ | |
398 | qpim_mroute_socket_reader = 0; | |
399 | mroute_read_on(); | |
400 | ||
401 | return result; | |
402 | } | |
403 | ||
404 | static void mroute_read_on() | |
405 | { | |
406 | zassert(!qpim_mroute_socket_reader); | |
407 | zassert(PIM_MROUTE_IS_ENABLED); | |
408 | ||
409 | THREAD_READ_ON(master, qpim_mroute_socket_reader, | |
410 | mroute_read, 0, qpim_mroute_socket_fd); | |
411 | } | |
412 | ||
413 | static void mroute_read_off() | |
414 | { | |
415 | THREAD_OFF(qpim_mroute_socket_reader); | |
416 | } | |
417 | ||
418 | int pim_mroute_socket_enable() | |
419 | { | |
420 | int fd; | |
421 | ||
422 | if (PIM_MROUTE_IS_ENABLED) | |
423 | return -1; | |
424 | ||
425 | if ( pimd_privs.change (ZPRIVS_RAISE) ) | |
426 | zlog_err ("pim_mroute_socket_enable: could not raise privs, %s", | |
427 | safe_strerror (errno) ); | |
428 | ||
429 | fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); | |
430 | ||
431 | if ( pimd_privs.change (ZPRIVS_LOWER) ) | |
432 | zlog_err ("pim_mroute_socket_enable: could not lower privs, %s", | |
433 | safe_strerror (errno) ); | |
434 | ||
435 | if (fd < 0) { | |
436 | zlog_warn("Could not create mroute socket: errno=%d: %s", | |
437 | errno, safe_strerror(errno)); | |
438 | return -2; | |
439 | } | |
440 | ||
441 | if (pim_mroute_set(fd, 1)) { | |
442 | zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s", | |
443 | fd, errno, safe_strerror(errno)); | |
444 | close(fd); | |
445 | return -3; | |
446 | } | |
447 | ||
448 | qpim_mroute_socket_fd = fd; | |
b45cefcb | 449 | |
12e41d03 DL |
450 | qpim_mroute_socket_creation = pim_time_monotonic_sec(); |
451 | mroute_read_on(); | |
452 | ||
453 | zassert(PIM_MROUTE_IS_ENABLED); | |
454 | ||
455 | return 0; | |
456 | } | |
457 | ||
458 | int pim_mroute_socket_disable() | |
459 | { | |
460 | if (PIM_MROUTE_IS_DISABLED) | |
461 | return -1; | |
462 | ||
463 | if (pim_mroute_set(qpim_mroute_socket_fd, 0)) { | |
464 | zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s", | |
465 | qpim_mroute_socket_fd, errno, safe_strerror(errno)); | |
466 | return -2; | |
467 | } | |
468 | ||
469 | if (close(qpim_mroute_socket_fd)) { | |
470 | zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", | |
471 | qpim_mroute_socket_fd, errno, safe_strerror(errno)); | |
472 | return -3; | |
473 | } | |
474 | ||
475 | mroute_read_off(); | |
476 | qpim_mroute_socket_fd = -1; | |
477 | ||
478 | zassert(PIM_MROUTE_IS_DISABLED); | |
479 | ||
480 | return 0; | |
481 | } | |
482 | ||
483 | /* | |
484 | For each network interface (e.g., physical or a virtual tunnel) that | |
485 | would be used for multicast forwarding, a corresponding multicast | |
486 | interface must be added to the kernel. | |
487 | */ | |
744d91b3 | 488 | int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags) |
12e41d03 | 489 | { |
744d91b3 | 490 | struct pim_interface *pim_ifp = ifp->info; |
12e41d03 DL |
491 | struct vifctl vc; |
492 | int err; | |
493 | ||
494 | if (PIM_MROUTE_IS_DISABLED) { | |
495 | zlog_warn("%s: global multicast is disabled", | |
496 | __PRETTY_FUNCTION__); | |
497 | return -1; | |
498 | } | |
499 | ||
500 | memset(&vc, 0, sizeof(vc)); | |
744d91b3 | 501 | vc.vifc_vifi = pim_ifp->mroute_vif_index; |
b3f2bf7c | 502 | #ifdef VIFF_USE_IFINDEX |
744d91b3 | 503 | vc.vifc_lcl_ifindex = ifp->ifindex; |
b3f2bf7c RW |
504 | #else |
505 | if (ifaddr.s_addr == INADDR_ANY) { | |
506 | zlog_warn("%s: unnumbered interfaces are not supported on this platform", | |
507 | __PRETTY_FUNCTION__); | |
508 | return -1; | |
509 | } | |
510 | memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr)); | |
511 | #endif | |
b45cefcb | 512 | vc.vifc_flags = flags; |
12e41d03 DL |
513 | vc.vifc_threshold = PIM_MROUTE_MIN_TTL; |
514 | vc.vifc_rate_limit = 0; | |
12e41d03 DL |
515 | |
516 | #ifdef PIM_DVMRP_TUNNEL | |
517 | if (vc.vifc_flags & VIFF_TUNNEL) { | |
518 | memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr)); | |
519 | } | |
520 | #endif | |
521 | ||
b45cefcb | 522 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc)); |
12e41d03 DL |
523 | if (err) { |
524 | char ifaddr_str[100]; | |
525 | int e = errno; | |
526 | ||
527 | pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str)); | |
528 | ||
59471fb8 | 529 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s", |
12e41d03 | 530 | __FILE__, __PRETTY_FUNCTION__, |
744d91b3 | 531 | qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags, |
12e41d03 DL |
532 | e, safe_strerror(e)); |
533 | errno = e; | |
534 | return -2; | |
535 | } | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | int pim_mroute_del_vif(int vif_index) | |
541 | { | |
542 | struct vifctl vc; | |
543 | int err; | |
544 | ||
545 | if (PIM_MROUTE_IS_DISABLED) { | |
546 | zlog_warn("%s: global multicast is disabled", | |
547 | __PRETTY_FUNCTION__); | |
548 | return -1; | |
549 | } | |
550 | ||
551 | memset(&vc, 0, sizeof(vc)); | |
552 | vc.vifc_vifi = vif_index; | |
553 | ||
554 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); | |
555 | if (err) { | |
556 | int e = errno; | |
557 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", | |
558 | __FILE__, __PRETTY_FUNCTION__, | |
559 | qpim_mroute_socket_fd, vif_index, | |
560 | e, safe_strerror(e)); | |
561 | errno = e; | |
562 | return -2; | |
563 | } | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
c171d6d8 | 568 | int pim_mroute_add(struct channel_oil *c_oil) |
12e41d03 DL |
569 | { |
570 | int err; | |
2ca35b3d | 571 | int orig = 0; |
12e41d03 DL |
572 | |
573 | qpim_mroute_add_last = pim_time_monotonic_sec(); | |
574 | ++qpim_mroute_add_events; | |
575 | ||
576 | if (PIM_MROUTE_IS_DISABLED) { | |
577 | zlog_warn("%s: global multicast is disabled", | |
578 | __PRETTY_FUNCTION__); | |
579 | return -1; | |
580 | } | |
581 | ||
d3aded99 DS |
582 | /* The linux kernel *expects* the incoming |
583 | * vif to be part of the outgoing list | |
584 | * in the case of a (*,G). | |
585 | */ | |
c171d6d8 | 586 | if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) |
d3aded99 | 587 | { |
c171d6d8 DS |
588 | orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent]; |
589 | c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; | |
d3aded99 DS |
590 | } |
591 | ||
12e41d03 | 592 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC, |
c171d6d8 | 593 | &c_oil->oil, sizeof(c_oil->oil)); |
d3aded99 | 594 | |
c171d6d8 DS |
595 | if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) |
596 | c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; | |
d3aded99 | 597 | |
12e41d03 DL |
598 | if (err) { |
599 | int e = errno; | |
600 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", | |
601 | __FILE__, __PRETTY_FUNCTION__, | |
602 | qpim_mroute_socket_fd, | |
603 | e, safe_strerror(e)); | |
604 | errno = e; | |
605 | return -2; | |
606 | } | |
607 | ||
58302dc7 | 608 | c_oil->installed = 1; |
12e41d03 DL |
609 | return 0; |
610 | } | |
611 | ||
c171d6d8 | 612 | int pim_mroute_del (struct channel_oil *c_oil) |
12e41d03 DL |
613 | { |
614 | int err; | |
615 | ||
616 | qpim_mroute_del_last = pim_time_monotonic_sec(); | |
617 | ++qpim_mroute_del_events; | |
618 | ||
619 | if (PIM_MROUTE_IS_DISABLED) { | |
620 | zlog_warn("%s: global multicast is disabled", | |
621 | __PRETTY_FUNCTION__); | |
622 | return -1; | |
623 | } | |
624 | ||
c171d6d8 | 625 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil)); |
12e41d03 DL |
626 | if (err) { |
627 | int e = errno; | |
628 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", | |
629 | __FILE__, __PRETTY_FUNCTION__, | |
630 | qpim_mroute_socket_fd, | |
631 | e, safe_strerror(e)); | |
632 | errno = e; | |
633 | return -2; | |
634 | } | |
635 | ||
58302dc7 DS |
636 | c_oil->installed = 0; |
637 | ||
12e41d03 DL |
638 | return 0; |
639 | } | |
3667e8a0 DS |
640 | |
641 | void | |
642 | pim_mroute_update_counters (struct channel_oil *c_oil) | |
643 | { | |
644 | struct sioc_sg_req sgreq; | |
645 | ||
646 | memset (&sgreq, 0, sizeof(sgreq)); | |
647 | sgreq.src = c_oil->oil.mfcc_origin; | |
648 | sgreq.grp = c_oil->oil.mfcc_mcastgrp; | |
649 | ||
650 | c_oil->cc.oldpktcnt = c_oil->cc.pktcnt; | |
651 | c_oil->cc.oldbytecnt = c_oil->cc.bytecnt; | |
652 | c_oil->cc.oldwrong_if = c_oil->cc.wrong_if; | |
653 | ||
654 | if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) | |
655 | { | |
656 | char group_str[100]; | |
657 | char source_str[100]; | |
658 | ||
659 | pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); | |
660 | pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); | |
661 | ||
662 | zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s", | |
663 | (unsigned long)SIOCGETSGCNT, | |
664 | source_str, | |
665 | group_str, | |
666 | errno, | |
667 | safe_strerror(errno)); | |
668 | return; | |
669 | } | |
670 | ||
671 | c_oil->cc.pktcnt = sgreq.pktcnt; | |
672 | c_oil->cc.bytecnt = sgreq.bytecnt; | |
673 | c_oil->cc.wrong_if = sgreq.wrong_if; | |
674 | ||
675 | return; | |
676 | } |