]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
12e41d03 | 2 | /* |
896014f4 DL |
3 | * PIM for Quagga |
4 | * Copyright (C) 2008 Everton da Silva Marques | |
896014f4 | 5 | */ |
12e41d03 DL |
6 | |
7 | #include <zebra.h> | |
8 | #include "log.h" | |
9 | #include "privs.h" | |
744d91b3 | 10 | #include "if.h" |
065bee4b | 11 | #include "prefix.h" |
dfe43e25 DW |
12 | #include "vty.h" |
13 | #include "plist.h" | |
fe232c19 | 14 | #include "sockopt.h" |
3613d898 | 15 | #include "lib_errors.h" |
1ce957d6 | 16 | #include "lib/network.h" |
12e41d03 DL |
17 | |
18 | #include "pimd.h" | |
8e38a2cf | 19 | #include "pim_rpf.h" |
12e41d03 | 20 | #include "pim_mroute.h" |
37653d4f | 21 | #include "pim_oil.h" |
12e41d03 DL |
22 | #include "pim_str.h" |
23 | #include "pim_time.h" | |
24 | #include "pim_iface.h" | |
25 | #include "pim_macro.h" | |
c8ae3ce8 | 26 | #include "pim_rp.h" |
59471fb8 | 27 | #include "pim_oil.h" |
998af219 | 28 | #include "pim_register.h" |
56638739 | 29 | #include "pim_ifchannel.h" |
e3be0432 | 30 | #include "pim_zlookup.h" |
15a5dafe | 31 | #include "pim_ssm.h" |
2002dcdb | 32 | #include "pim_sock.h" |
37c3fd98 | 33 | #include "pim_vxlan.h" |
a5fa9822 | 34 | #include "pim_msg.h" |
12e41d03 | 35 | |
8ea5d944 | 36 | static void mroute_read_on(struct pim_instance *pim); |
d650b3c7 DL |
37 | static int pim_upstream_mroute_update(struct channel_oil *c_oil, |
38 | const char *name); | |
12e41d03 | 39 | |
1ce957d6 | 40 | int pim_mroute_set(struct pim_instance *pim, int enable) |
41 | { | |
42 | int err; | |
43 | int opt, data; | |
44 | socklen_t data_len = sizeof(data); | |
45 | ||
46 | /* | |
47 | * We need to create the VRF table for the pim mroute_socket | |
48 | */ | |
ad855cdf | 49 | if (enable && pim->vrf->vrf_id != VRF_DEFAULT) { |
1ce957d6 | 50 | frr_with_privs (&pimd_privs) { |
51 | ||
52 | data = pim->vrf->data.l.table_id; | |
53 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, | |
54 | MRT_TABLE, &data, data_len); | |
55 | if (err) { | |
56 | zlog_warn( | |
57 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO, MRT_TABLE=%d): errno=%d: %s", | |
58 | __FILE__, __func__, pim->mroute_socket, | |
59 | data, errno, safe_strerror(errno)); | |
60 | return -1; | |
61 | } | |
62 | } | |
63 | } | |
64 | ||
65 | frr_with_privs (&pimd_privs) { | |
66 | opt = enable ? MRT_INIT : MRT_DONE; | |
67 | /* | |
68 | * *BSD *cares* about what value we pass down | |
69 | * here | |
70 | */ | |
71 | data = 1; | |
72 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &data, | |
73 | data_len); | |
74 | if (err) { | |
75 | zlog_warn( | |
76 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,%s=%d): errno=%d: %s", | |
77 | __FILE__, __func__, pim->mroute_socket, | |
78 | enable ? "MRT_INIT" : "MRT_DONE", data, errno, | |
79 | safe_strerror(errno)); | |
80 | return -1; | |
81 | } | |
82 | } | |
83 | ||
84 | #if defined(HAVE_IP_PKTINFO) | |
85 | if (enable) { | |
86 | /* Linux and Solaris IP_PKTINFO */ | |
87 | data = 1; | |
88 | if (setsockopt(pim->mroute_socket, PIM_IPPROTO, IP_PKTINFO, | |
89 | &data, data_len)) { | |
90 | zlog_warn( | |
91 | "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", | |
92 | pim->mroute_socket, errno, | |
93 | safe_strerror(errno)); | |
94 | } | |
95 | } | |
96 | #endif | |
97 | ||
98 | #if PIM_IPV == 6 | |
99 | if (enable) { | |
100 | /* Linux and Solaris IPV6_PKTINFO */ | |
101 | data = 1; | |
102 | if (setsockopt(pim->mroute_socket, PIM_IPPROTO, | |
103 | IPV6_RECVPKTINFO, &data, data_len)) { | |
104 | zlog_warn( | |
105 | "Could not set IPV6_RECVPKTINFO on socket fd=%d: errno=%d: %s", | |
106 | pim->mroute_socket, errno, | |
107 | safe_strerror(errno)); | |
108 | } | |
109 | } | |
110 | #endif | |
111 | setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8); | |
112 | ||
113 | if (set_nonblocking(pim->mroute_socket) < 0) { | |
114 | zlog_warn( | |
115 | "Could not set non blocking on socket fd=%d: errno=%d: %s", | |
116 | pim->mroute_socket, errno, safe_strerror(errno)); | |
117 | return -1; | |
118 | } | |
119 | ||
120 | if (enable) { | |
121 | #if defined linux | |
122 | int upcalls = GMMSG_WRVIFWHOLE; | |
123 | opt = MRT_PIM; | |
124 | ||
125 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &upcalls, | |
126 | sizeof(upcalls)); | |
127 | if (err) { | |
128 | zlog_warn( | |
129 | "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s", | |
130 | errno, safe_strerror(errno)); | |
131 | return -1; | |
132 | } | |
133 | #else | |
134 | zlog_warn( | |
135 | "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall"); | |
136 | #endif | |
137 | } | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
142 | static const char *const gmmsgtype2str[GMMSG_WRVIFWHOLE + 1] = { | |
143 | "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"}; | |
144 | ||
d62a17ae | 145 | |
a5fa9822 | 146 | int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg) |
12e41d03 | 147 | { |
d62a17ae | 148 | struct pim_interface *pim_ifp = ifp->info; |
149 | struct pim_upstream *up; | |
6fff2cc6 | 150 | pim_sgaddr sg; |
d650b3c7 | 151 | bool desync = false; |
d62a17ae | 152 | |
dbd596f6 DL |
153 | memset(&sg, 0, sizeof(sg)); |
154 | sg.src = msg->msg_im_src; | |
155 | sg.grp = msg->msg_im_dst; | |
156 | ||
b4ba03b3 SP |
157 | |
158 | if (!pim_ifp || !pim_ifp->pim_enable) { | |
dbd596f6 DL |
159 | if (PIM_DEBUG_MROUTE) |
160 | zlog_debug( | |
b4ba03b3 SP |
161 | "%s: %s on interface, dropping packet to %pSG", |
162 | ifp->name, | |
163 | !pim_ifp ? "Multicast not enabled" | |
164 | : "PIM not enabled", | |
165 | &sg); | |
dbd596f6 DL |
166 | return 0; |
167 | } | |
168 | ||
c86b4ff4 DL |
169 | if (!pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { |
170 | /* for ASM, check that we have enough information (i.e. path | |
171 | * to RP) to make a decision on what to do with this packet. | |
172 | * | |
173 | * for SSM, this is meaningless, everything is join-driven, | |
174 | * and for NOCACHE we need to install an empty OIL MFC entry | |
175 | * so the kernel doesn't keep nagging us. | |
176 | */ | |
177 | struct pim_rpf *rpg; | |
178 | ||
179 | rpg = RP(pim_ifp->pim, msg->msg_im_dst); | |
180 | if (!rpg) { | |
181 | if (PIM_DEBUG_MROUTE) | |
182 | zlog_debug("%s: no RPF for packet to %pSG", | |
183 | ifp->name, &sg); | |
184 | return 0; | |
185 | } | |
186 | if (pim_rpf_addr_is_inaddr_any(rpg)) { | |
187 | if (PIM_DEBUG_MROUTE) | |
188 | zlog_debug("%s: null RPF for packet to %pSG", | |
189 | ifp->name, &sg); | |
190 | return 0; | |
191 | } | |
d62a17ae | 192 | } |
04b40f02 | 193 | |
d62a17ae | 194 | /* |
195 | * If we've received a multicast packet that isn't connected to | |
196 | * us | |
197 | */ | |
a5fa9822 | 198 | if (!pim_if_connected_to_source(ifp, msg->msg_im_src)) { |
dbd596f6 | 199 | if (PIM_DEBUG_MROUTE) |
d62a17ae | 200 | zlog_debug( |
dbd596f6 DL |
201 | "%s: incoming packet to %pSG from non-connected source", |
202 | ifp->name, &sg); | |
d62a17ae | 203 | return 0; |
204 | } | |
065bee4b | 205 | |
b5469d02 | 206 | if (!(PIM_I_am_DR(pim_ifp))) { |
dbd596f6 DL |
207 | /* unlike the other debug messages, this one is further in the |
208 | * "normal operation" category and thus under _DETAIL | |
209 | */ | |
b5469d02 | 210 | if (PIM_DEBUG_MROUTE_DETAIL) |
a5fa9822 | 211 | zlog_debug( |
dbd596f6 DL |
212 | "%s: not DR on interface, not forwarding traffic for %pSG", |
213 | ifp->name, &sg); | |
b5469d02 DS |
214 | |
215 | /* | |
216 | * We are not the DR, but we are still receiving packets | |
217 | * Let's blackhole those packets for the moment | |
218 | * As that they will be coming up to the cpu | |
219 | * and causing us to consider them. | |
9e132a49 DS |
220 | * |
221 | * This *will* create a dangling channel_oil | |
222 | * that I see no way to get rid of. Just noting | |
223 | * this for future reference. | |
b5469d02 | 224 | */ |
02434c43 | 225 | up = pim_upstream_find_or_add( |
15569c58 DA |
226 | &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __func__); |
227 | pim_upstream_mroute_add(up->channel_oil, __func__); | |
b5469d02 DS |
228 | |
229 | return 0; | |
230 | } | |
231 | ||
d62a17ae | 232 | up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, |
15569c58 | 233 | __func__); |
d650b3c7 DL |
234 | if (up->channel_oil->installed) { |
235 | zlog_warn( | |
236 | "%s: NOCACHE for %pSG, MFC entry disappeared - reinstalling", | |
237 | ifp->name, &sg); | |
238 | desync = true; | |
239 | } | |
c29a5806 | 240 | |
d62a17ae | 241 | /* |
242 | * I moved this debug till after the actual add because | |
243 | * I want to take advantage of the up->sg_str being filled in. | |
244 | */ | |
245 | if (PIM_DEBUG_MROUTE) { | |
246 | zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption", | |
15569c58 | 247 | __func__, up->sg_str); |
d62a17ae | 248 | } |
8bfb8b67 | 249 | |
d62a17ae | 250 | PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); |
19b807ca | 251 | pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time); |
d62a17ae | 252 | |
253 | up->channel_oil->cc.pktcnt++; | |
d62a17ae | 254 | // resolve mfcc_parent prior to mroute_add in channel_add_oif |
957d93ea | 255 | if (up->rpf.source_nexthop.interface && |
a9338fa4 | 256 | *oil_parent(up->channel_oil) >= MAXVIFS) { |
7984af18 | 257 | pim_upstream_mroute_iif_update(up->channel_oil, __func__); |
d62a17ae | 258 | } |
259 | pim_register_join(up); | |
ac6c8d54 | 260 | /* if we have receiver, inherit from parent */ |
261 | pim_upstream_inherited_olist_decide(pim_ifp->pim, up); | |
59471fb8 | 262 | |
d650b3c7 DL |
263 | /* we just got NOCACHE from the kernel, so... MFC is not in the |
264 | * kernel for some reason or another. Try installing again. | |
265 | */ | |
266 | if (desync) | |
267 | pim_upstream_mroute_update(up->channel_oil, __func__); | |
d62a17ae | 268 | return 0; |
e355e30f | 269 | } |
12e41d03 | 270 | |
1b00ed5f DL |
271 | int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf, |
272 | size_t len) | |
e355e30f | 273 | { |
d62a17ae | 274 | struct pim_interface *pim_ifp; |
6fff2cc6 | 275 | pim_sgaddr sg; |
d62a17ae | 276 | struct pim_rpf *rpg; |
a5fa9822 | 277 | const ipv_hdr *ip_hdr; |
d62a17ae | 278 | struct pim_upstream *up; |
279 | ||
9b29ea95 DS |
280 | pim_ifp = ifp->info; |
281 | ||
a5fa9822 | 282 | ip_hdr = (const ipv_hdr *)buf; |
d62a17ae | 283 | |
6fff2cc6 | 284 | memset(&sg, 0, sizeof(sg)); |
a5fa9822 | 285 | sg.src = IPV_SRC(ip_hdr); |
286 | sg.grp = IPV_DST(ip_hdr); | |
d62a17ae | 287 | |
9b29ea95 | 288 | up = pim_upstream_find(pim_ifp->pim, &sg); |
d62a17ae | 289 | if (!up) { |
6fff2cc6 | 290 | pim_sgaddr star = sg; |
bca160c6 | 291 | star.src = PIMADDR_ANY; |
d62a17ae | 292 | |
9b29ea95 | 293 | up = pim_upstream_find(pim_ifp->pim, &star); |
d62a17ae | 294 | |
448139e7 | 295 | if (up && PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) { |
2002dcdb | 296 | up = pim_upstream_add(pim_ifp->pim, &sg, ifp, |
d62a17ae | 297 | PIM_UPSTREAM_FLAG_MASK_SRC_LHR, |
15569c58 | 298 | __func__, NULL); |
d62a17ae | 299 | if (!up) { |
300 | if (PIM_DEBUG_MROUTE) | |
a5fa9822 | 301 | zlog_debug( |
302 | "%s: Unable to create upstream information for %pSG", | |
303 | __func__, &sg); | |
d62a17ae | 304 | return 0; |
305 | } | |
306 | pim_upstream_keep_alive_timer_start( | |
19b807ca | 307 | up, pim_ifp->pim->keep_alive_time); |
9b29ea95 | 308 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
a53a9b3e | 309 | pim_upstream_update_join_desired(pim_ifp->pim, up); |
d62a17ae | 310 | |
311 | if (PIM_DEBUG_MROUTE) | |
312 | zlog_debug("%s: Creating %s upstream on LHR", | |
15569c58 | 313 | __func__, up->sg_str); |
d62a17ae | 314 | return 0; |
315 | } | |
316 | if (PIM_DEBUG_MROUTE_DETAIL) { | |
a5fa9822 | 317 | zlog_debug( |
318 | "%s: Unable to find upstream channel WHOLEPKT%pSG", | |
319 | __func__, &sg); | |
d62a17ae | 320 | } |
321 | return 0; | |
322 | } | |
59471fb8 | 323 | |
957d93ea | 324 | if (!up->rpf.source_nexthop.interface) { |
23fc858a | 325 | if (PIM_DEBUG_PIM_TRACE) |
15569c58 DA |
326 | zlog_debug("%s: up %s RPF is not present", __func__, |
327 | up->sg_str); | |
957d93ea SP |
328 | return 0; |
329 | } | |
330 | ||
d62a17ae | 331 | pim_ifp = up->rpf.source_nexthop.interface->info; |
998af219 | 332 | |
b575a12c | 333 | rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL; |
c8ae3ce8 | 334 | |
cc144e8b | 335 | if ((pim_rpf_addr_is_inaddr_any(rpg)) || (!pim_ifp) || |
336 | (!(PIM_I_am_DR(pim_ifp)))) { | |
d62a17ae | 337 | if (PIM_DEBUG_MROUTE) { |
15569c58 | 338 | zlog_debug("%s: Failed Check send packet", __func__); |
d62a17ae | 339 | } |
340 | return 0; | |
341 | } | |
84366c7e | 342 | |
d62a17ae | 343 | /* |
344 | * If we've received a register suppress | |
345 | */ | |
346 | if (!up->t_rs_timer) { | |
6f439a70 | 347 | if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { |
d62a17ae | 348 | if (PIM_DEBUG_PIM_REG) |
a5fa9822 | 349 | zlog_debug( |
350 | "%pSG register forward skipped as group is SSM", | |
351 | &sg); | |
d62a17ae | 352 | return 0; |
353 | } | |
2bc31c44 AK |
354 | |
355 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { | |
356 | if (PIM_DEBUG_PIM_REG) | |
357 | zlog_debug( | |
358 | "%s register forward skipped, not FHR", | |
359 | up->sg_str); | |
360 | return 0; | |
361 | } | |
362 | ||
a5fa9822 | 363 | pim_register_send((uint8_t *)buf + sizeof(ipv_hdr), |
1b00ed5f | 364 | len - sizeof(ipv_hdr), |
d62a17ae | 365 | pim_ifp->primary_address, rpg, 0, up); |
366 | } | |
367 | return 0; | |
e355e30f | 368 | } |
12e41d03 | 369 | |
a5fa9822 | 370 | int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg) |
e355e30f | 371 | { |
d62a17ae | 372 | struct pim_ifchannel *ch; |
373 | struct pim_interface *pim_ifp; | |
6fff2cc6 | 374 | pim_sgaddr sg; |
d62a17ae | 375 | |
6fff2cc6 | 376 | memset(&sg, 0, sizeof(sg)); |
a5fa9822 | 377 | sg.src = msg->msg_im_src; |
378 | sg.grp = msg->msg_im_dst; | |
d62a17ae | 379 | |
380 | /* | |
381 | Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. | |
382 | ||
383 | RFC 4601 4.8.2. PIM-SSM-Only Routers | |
384 | ||
385 | iif is the incoming interface of the packet. | |
386 | if (iif is in inherited_olist(S,G)) { | |
387 | send Assert(S,G) on iif | |
388 | } | |
389 | */ | |
390 | ||
391 | if (!ifp) { | |
392 | if (PIM_DEBUG_MROUTE) | |
a5fa9822 | 393 | zlog_debug( |
394 | "%s: WRONGVIF (S,G)=%pSG could not find input interface for input_vif_index=%d", | |
395 | __func__, &sg, msg->msg_im_vif); | |
d62a17ae | 396 | return -1; |
397 | } | |
12e41d03 | 398 | |
d62a17ae | 399 | pim_ifp = ifp->info; |
400 | if (!pim_ifp) { | |
401 | if (PIM_DEBUG_MROUTE) | |
a5fa9822 | 402 | zlog_debug( |
403 | "%s: WRONGVIF (S,G)=%pSG multicast not enabled on interface %s", | |
404 | __func__, &sg, ifp->name); | |
d62a17ae | 405 | return -2; |
406 | } | |
c29a5806 | 407 | |
d62a17ae | 408 | ch = pim_ifchannel_find(ifp, &sg); |
409 | if (!ch) { | |
6fff2cc6 | 410 | pim_sgaddr star_g = sg; |
d62a17ae | 411 | if (PIM_DEBUG_MROUTE) |
a5fa9822 | 412 | zlog_debug( |
413 | "%s: WRONGVIF (S,G)=%pSG could not find channel on interface %s", | |
414 | __func__, &sg, ifp->name); | |
d62a17ae | 415 | |
bca160c6 | 416 | star_g.src = PIMADDR_ANY; |
d62a17ae | 417 | ch = pim_ifchannel_find(ifp, &star_g); |
418 | if (!ch) { | |
419 | if (PIM_DEBUG_MROUTE) | |
a5fa9822 | 420 | zlog_debug( |
421 | "%s: WRONGVIF (*,G)=%pSG could not find channel on interface %s", | |
422 | __func__, &star_g, ifp->name); | |
d62a17ae | 423 | return -3; |
424 | } | |
425 | } | |
12e41d03 | 426 | |
d62a17ae | 427 | /* |
428 | RFC 4601: 4.6.1. (S,G) Assert Message State Machine | |
429 | ||
430 | Transitions from NoInfo State | |
431 | ||
432 | An (S,G) data packet arrives on interface I, AND | |
433 | CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an | |
434 | downstream interface that is in our (S,G) outgoing interface | |
435 | list. We optimistically assume that we will be the assert | |
436 | winner for this (S,G), and so we transition to the "I am Assert | |
437 | Winner" state and perform Actions A1 (below), which will | |
438 | initiate the assert negotiation for (S,G). | |
439 | */ | |
440 | ||
441 | if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { | |
442 | if (PIM_DEBUG_MROUTE) { | |
443 | zlog_debug( | |
444 | "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s", | |
15569c58 | 445 | __func__, ch->sg_str, ifp->name); |
d62a17ae | 446 | } |
447 | return -4; | |
448 | } | |
e355e30f | 449 | |
d62a17ae | 450 | if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { |
451 | if (PIM_DEBUG_MROUTE) { | |
452 | zlog_debug( | |
453 | "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel", | |
15569c58 | 454 | __func__, ch->sg_str, ifp->name); |
d62a17ae | 455 | } |
456 | return -5; | |
457 | } | |
458 | ||
459 | if (assert_action_a1(ch)) { | |
460 | if (PIM_DEBUG_MROUTE) { | |
461 | zlog_debug( | |
462 | "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s", | |
15569c58 | 463 | __func__, ch->sg_str, ifp->name); |
d62a17ae | 464 | } |
465 | return -6; | |
466 | } | |
e355e30f | 467 | |
d62a17ae | 468 | return 0; |
e355e30f DS |
469 | } |
470 | ||
1b00ed5f DL |
471 | int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf, |
472 | size_t len) | |
08e1fe76 | 473 | { |
a5fa9822 | 474 | const ipv_hdr *ip_hdr = (const ipv_hdr *)buf; |
d62a17ae | 475 | struct pim_interface *pim_ifp; |
a054f6d7 | 476 | struct pim_instance *pim; |
d62a17ae | 477 | struct pim_ifchannel *ch; |
478 | struct pim_upstream *up; | |
6fff2cc6 DL |
479 | pim_sgaddr star_g; |
480 | pim_sgaddr sg; | |
d62a17ae | 481 | |
fec883d9 DS |
482 | pim_ifp = ifp->info; |
483 | ||
6fff2cc6 | 484 | memset(&sg, 0, sizeof(sg)); |
a5fa9822 | 485 | sg.src = IPV_SRC(ip_hdr); |
486 | sg.grp = IPV_DST(ip_hdr); | |
d62a17ae | 487 | |
488 | ch = pim_ifchannel_find(ifp, &sg); | |
489 | if (ch) { | |
490 | if (PIM_DEBUG_MROUTE) | |
491 | zlog_debug( | |
492 | "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s", | |
493 | ch->sg_str, ifp->name); | |
494 | return -1; | |
495 | } | |
b7ddd2ec | 496 | |
d62a17ae | 497 | star_g = sg; |
bca160c6 | 498 | star_g.src = PIMADDR_ANY; |
a054f6d7 DS |
499 | |
500 | pim = pim_ifp->pim; | |
501 | /* | |
502 | * If the incoming interface is the pimreg, then | |
503 | * we know the callback is associated with a pim register | |
504 | * packet and there is nothing to do here as that | |
505 | * normal pim processing will see the packet and allow | |
506 | * us to do the right thing. | |
507 | */ | |
508 | if (ifp == pim->regiface) { | |
509 | return 0; | |
510 | } | |
08e1fe76 | 511 | |
9b29ea95 | 512 | up = pim_upstream_find(pim_ifp->pim, &sg); |
d62a17ae | 513 | if (up) { |
514 | struct pim_upstream *parent; | |
515 | struct pim_nexthop source; | |
fec883d9 | 516 | struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp); |
c783249b | 517 | |
518 | /* No RPF or No RPF interface or No mcast on RPF interface */ | |
a5fa9822 | 519 | if (!rpf || !rpf->source_nexthop.interface || |
520 | !rpf->source_nexthop.interface->info) | |
d62a17ae | 521 | return 0; |
522 | ||
523 | /* | |
524 | * If we have received a WRVIFWHOLE and are at this | |
525 | * point, we could be receiving the packet on the *,G | |
526 | * tree, let's check and if so we can safely drop | |
527 | * it. | |
528 | */ | |
9b29ea95 | 529 | parent = pim_upstream_find(pim_ifp->pim, &star_g); |
d62a17ae | 530 | if (parent && parent->rpf.source_nexthop.interface == ifp) |
531 | return 0; | |
532 | ||
533 | pim_ifp = rpf->source_nexthop.interface->info; | |
534 | ||
535 | memset(&source, 0, sizeof(source)); | |
536 | /* | |
537 | * If we are the fhr that means we are getting a callback during | |
538 | * the pimreg period, so I believe we can ignore this packet | |
539 | */ | |
540 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { | |
aeb67246 DS |
541 | /* |
542 | * No if channel, but upstream we are at the RP. | |
543 | * | |
544 | * This could be a anycast RP too and we may | |
545 | * not have received a register packet from | |
546 | * the source here at all. So gracefully | |
547 | * bow out of doing a nexthop lookup and | |
548 | * setting the SPTBIT to true | |
549 | */ | |
a5fa9822 | 550 | if (!(pim_addr_is_any(up->upstream_register)) && |
aeb67246 | 551 | pim_nexthop_lookup(pim_ifp->pim, &source, |
ade155e1 | 552 | up->upstream_register, 0)) { |
d62a17ae | 553 | pim_register_stop_send(source.interface, &sg, |
554 | pim_ifp->primary_address, | |
555 | up->upstream_register); | |
df766618 DS |
556 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
557 | } | |
46dd6edb | 558 | |
9b29ea95 | 559 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
d62a17ae | 560 | if (!up->channel_oil->installed) |
69e3538c | 561 | pim_upstream_mroute_add(up->channel_oil, |
15569c58 | 562 | __func__); |
d62a17ae | 563 | } else { |
d9c9a9ee DS |
564 | if (I_am_RP(pim_ifp->pim, up->sg.grp)) { |
565 | if (pim_nexthop_lookup(pim_ifp->pim, &source, | |
ade155e1 DS |
566 | up->upstream_register, |
567 | 0)) | |
d62a17ae | 568 | pim_register_stop_send( |
569 | source.interface, &sg, | |
570 | pim_ifp->primary_address, | |
571 | up->upstream_register); | |
572 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; | |
33ec4015 DS |
573 | } else { |
574 | /* | |
575 | * At this point pimd is connected to | |
576 | * the source, it has a parent, we are not | |
577 | * the RP and the SPTBIT should be set | |
578 | * since we know *the* S,G is on the SPT. | |
579 | * The first time this happens, let's cause | |
580 | * an immediate join to go out so that | |
581 | * the RP can trim this guy immediately | |
582 | * if necessary, instead of waiting | |
583 | * one join/prune send cycle | |
584 | */ | |
585 | if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE && | |
586 | up->parent && | |
587 | up->rpf.source_nexthop.interface != | |
588 | up->parent->rpf.source_nexthop | |
589 | .interface) { | |
590 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; | |
591 | pim_jp_agg_single_upstream_send( | |
592 | &up->parent->rpf, up->parent, | |
593 | true); | |
594 | } | |
d62a17ae | 595 | } |
596 | pim_upstream_keep_alive_timer_start( | |
19b807ca | 597 | up, pim_ifp->pim->keep_alive_time); |
9b29ea95 | 598 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
1b00ed5f | 599 | pim_mroute_msg_wholepkt(fd, ifp, buf, len); |
d62a17ae | 600 | } |
601 | return 0; | |
602 | } | |
8e38a2cf | 603 | |
d62a17ae | 604 | pim_ifp = ifp->info; |
d62a17ae | 605 | if (pim_if_connected_to_source(ifp, sg.src)) { |
2002dcdb | 606 | up = pim_upstream_add(pim_ifp->pim, &sg, ifp, |
15569c58 DA |
607 | PIM_UPSTREAM_FLAG_MASK_FHR, __func__, |
608 | NULL); | |
d62a17ae | 609 | if (!up) { |
610 | if (PIM_DEBUG_MROUTE) | |
a5fa9822 | 611 | zlog_debug( |
612 | "%pSG: WRONGVIF%s unable to create upstream on interface", | |
613 | &sg, ifp->name); | |
d62a17ae | 614 | return -2; |
615 | } | |
616 | PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); | |
996c9314 LB |
617 | pim_upstream_keep_alive_timer_start( |
618 | up, pim_ifp->pim->keep_alive_time); | |
d62a17ae | 619 | up->channel_oil->cc.pktcnt++; |
620 | pim_register_join(up); | |
9b29ea95 | 621 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
69e3538c AK |
622 | if (!up->channel_oil->installed) |
623 | pim_upstream_mroute_add(up->channel_oil, __func__); | |
d62a17ae | 624 | |
625 | // Send the packet to the RP | |
1b00ed5f | 626 | pim_mroute_msg_wholepkt(fd, ifp, buf, len); |
02434c43 DS |
627 | } else { |
628 | up = pim_upstream_add(pim_ifp->pim, &sg, ifp, | |
629 | PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, | |
15569c58 | 630 | __func__, NULL); |
02434c43 | 631 | if (!up->channel_oil->installed) |
15569c58 | 632 | pim_upstream_mroute_add(up->channel_oil, __func__); |
d62a17ae | 633 | } |
08e1fe76 | 634 | |
d62a17ae | 635 | return 0; |
08e1fe76 DS |
636 | } |
637 | ||
1ce957d6 | 638 | #if PIM_IPV == 4 |
639 | static int process_igmp_packet(struct pim_instance *pim, const char *buf, | |
640 | size_t buf_size, ifindex_t ifindex) | |
641 | { | |
642 | struct interface *ifp; | |
643 | struct pim_interface *pim_ifp; | |
644 | struct in_addr ifaddr; | |
645 | struct gm_sock *igmp; | |
646 | const struct prefix *connected_src; | |
647 | const struct ip *ip_hdr = (const struct ip *)buf; | |
648 | ||
649 | /* We have the IP packet but we do not know which interface this | |
650 | * packet was | |
651 | * received on. Find the interface that is on the same subnet as | |
652 | * the source | |
653 | * of the IP packet. | |
654 | */ | |
655 | ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id); | |
656 | ||
657 | if (!ifp || !ifp->info) | |
658 | return 0; | |
659 | ||
660 | connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src); | |
661 | ||
b2212b99 | 662 | if (!connected_src && !pim_addr_is_any(ip_hdr->ip_src)) { |
55eb347d | 663 | if (PIM_DEBUG_GM_PACKETS) { |
1ce957d6 | 664 | zlog_debug( |
665 | "Recv IGMP packet on interface: %s from a non-connected source: %pI4", | |
666 | ifp->name, &ip_hdr->ip_src); | |
667 | } | |
668 | return 0; | |
669 | } | |
670 | ||
671 | pim_ifp = ifp->info; | |
b2212b99 MR |
672 | ifaddr = connected_src ? connected_src->u.prefix4 |
673 | : pim_ifp->primary_address; | |
1ce957d6 | 674 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr); |
675 | ||
55eb347d | 676 | if (PIM_DEBUG_GM_PACKETS) { |
1ce957d6 | 677 | zlog_debug( |
678 | "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4", | |
679 | __func__, pim->vrf->name, ifp->name, igmp, | |
680 | &ip_hdr->ip_src, &ip_hdr->ip_dst); | |
681 | } | |
682 | if (igmp) | |
683 | pim_igmp_packet(igmp, (char *)buf, buf_size); | |
b2212b99 | 684 | else if (PIM_DEBUG_GM_PACKETS) |
1ce957d6 | 685 | zlog_debug( |
b2212b99 MR |
686 | "No IGMP socket on interface: %s with connected source: %pI4", |
687 | ifp->name, &ifaddr); | |
688 | ||
1ce957d6 | 689 | return 0; |
690 | } | |
691 | #endif | |
692 | ||
693 | int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size, | |
694 | ifindex_t ifindex) | |
695 | { | |
696 | struct interface *ifp; | |
697 | const ipv_hdr *ip_hdr; | |
698 | const kernmsg *msg; | |
699 | ||
700 | if (buf_size < (int)sizeof(ipv_hdr)) | |
701 | return 0; | |
702 | ||
703 | ip_hdr = (const ipv_hdr *)buf; | |
704 | ||
705 | #if PIM_IPV == 4 | |
706 | if (ip_hdr->ip_p == IPPROTO_IGMP) { | |
707 | process_igmp_packet(pim, buf, buf_size, ifindex); | |
708 | } else if (ip_hdr->ip_p) { | |
709 | if (PIM_DEBUG_MROUTE_DETAIL) { | |
710 | zlog_debug( | |
711 | "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%ld", | |
712 | __func__, ip_hdr->ip_p, &ip_hdr->ip_src, | |
713 | &ip_hdr->ip_dst, (long int)buf_size); | |
714 | } | |
715 | ||
716 | } else { | |
717 | #else | |
718 | ||
719 | if ((ip_hdr->ip6_vfc & 0xf) == 0) { | |
720 | #endif | |
721 | msg = (const kernmsg *)buf; | |
722 | ||
723 | ifp = pim_if_find_by_vif_index(pim, msg->msg_im_vif); | |
724 | ||
725 | if (!ifp) | |
726 | return 0; | |
727 | if (PIM_DEBUG_MROUTE) { | |
728 | #if PIM_IPV == 4 | |
729 | zlog_debug( | |
730 | "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%ld", | |
731 | __func__, gmmsgtype2str[msg->msg_im_msgtype], | |
732 | msg->msg_im_msgtype, ip_hdr->ip_p, | |
733 | pim->mroute_socket, &msg->msg_im_src, | |
734 | &msg->msg_im_dst, ifp->name, msg->msg_im_vif, | |
735 | (long int)buf_size); | |
736 | #else | |
737 | zlog_debug( | |
738 | "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI6,%pI6) on %s vifi=%d size=%ld", | |
739 | __func__, gmmsgtype2str[msg->msg_im_msgtype], | |
740 | msg->msg_im_msgtype, ip_hdr->ip6_nxt, | |
741 | pim->mroute_socket, &msg->msg_im_src, | |
742 | &msg->msg_im_dst, ifp->name, msg->msg_im_vif, | |
743 | (long int)buf_size); | |
744 | #endif | |
745 | } | |
746 | ||
747 | switch (msg->msg_im_msgtype) { | |
748 | case GMMSG_WRONGVIF: | |
749 | return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp, | |
750 | msg); | |
751 | case GMMSG_NOCACHE: | |
752 | return pim_mroute_msg_nocache(pim->mroute_socket, ifp, | |
753 | msg); | |
754 | case GMMSG_WHOLEPKT: | |
755 | return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp, | |
756 | (const char *)msg, | |
757 | buf_size); | |
758 | case GMMSG_WRVIFWHOLE: | |
759 | return pim_mroute_msg_wrvifwhole(pim->mroute_socket, | |
760 | ifp, (const char *)msg, | |
761 | buf_size); | |
762 | default: | |
763 | break; | |
764 | } | |
765 | } | |
766 | ||
767 | return 0; | |
768 | } | |
769 | ||
e6685141 | 770 | static void mroute_read(struct event *t) |
12e41d03 | 771 | { |
8ea5d944 | 772 | struct pim_instance *pim; |
d62a17ae | 773 | static long long count; |
774 | char buf[10000]; | |
d62a17ae | 775 | int cont = 1; |
d62a17ae | 776 | int rd; |
90450a3d | 777 | ifindex_t ifindex; |
e16d030c | 778 | pim = EVENT_ARG(t); |
d62a17ae | 779 | |
780 | while (cont) { | |
90450a3d DS |
781 | rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf, |
782 | sizeof(buf), NULL, NULL, NULL, NULL, | |
783 | &ifindex); | |
61e99c94 | 784 | if (rd <= 0) { |
d62a17ae | 785 | if (errno == EINTR) |
786 | continue; | |
787 | if (errno == EWOULDBLOCK || errno == EAGAIN) | |
788 | break; | |
789 | ||
15569c58 DA |
790 | zlog_warn( |
791 | "%s: failure reading rd=%d: fd=%d: errno=%d: %s", | |
792 | __func__, rd, pim->mroute_socket, errno, | |
793 | safe_strerror(errno)); | |
d62a17ae | 794 | goto done; |
795 | } | |
796 | ||
cc9f21da | 797 | pim_mroute_msg(pim, buf, rd, ifindex); |
d62a17ae | 798 | |
799 | count++; | |
75373cca | 800 | if (count % router->packet_process == 0) |
d62a17ae | 801 | cont = 0; |
802 | } | |
803 | /* Keep reading */ | |
804 | done: | |
8ea5d944 | 805 | mroute_read_on(pim); |
a5fa9822 | 806 | |
807 | return; | |
12e41d03 DL |
808 | } |
809 | ||
8ea5d944 | 810 | static void mroute_read_on(struct pim_instance *pim) |
12e41d03 | 811 | { |
907a2395 DS |
812 | event_add_read(router->master, mroute_read, pim, pim->mroute_socket, |
813 | &pim->thread); | |
12e41d03 DL |
814 | } |
815 | ||
8ea5d944 | 816 | static void mroute_read_off(struct pim_instance *pim) |
12e41d03 | 817 | { |
e16d030c | 818 | EVENT_OFF(pim->thread); |
12e41d03 DL |
819 | } |
820 | ||
6beed987 | 821 | int pim_mroute_socket_enable(struct pim_instance *pim) |
12e41d03 | 822 | { |
d62a17ae | 823 | int fd; |
12e41d03 | 824 | |
0cf6db21 | 825 | frr_with_privs(&pimd_privs) { |
12e41d03 | 826 | |
a5fa9822 | 827 | #if PIM_IPV == 4 |
01b9e3fd | 828 | fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); |
a5fa9822 | 829 | #else |
830 | fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); | |
831 | #endif | |
01b9e3fd DL |
832 | if (fd < 0) { |
833 | zlog_warn("Could not create mroute socket: errno=%d: %s", | |
834 | errno, | |
835 | safe_strerror(errno)); | |
836 | return -2; | |
837 | } | |
e691f179 | 838 | |
fef295d4 DL |
839 | #if PIM_IPV == 6 |
840 | struct icmp6_filter filter[1]; | |
841 | int ret; | |
842 | ||
843 | /* Unlike IPv4, this socket is not used for MLD, so just drop | |
844 | * everything with an empty ICMP6 filter. Otherwise we get | |
845 | * all kinds of garbage here, possibly even non-multicast | |
846 | * related ICMPv6 traffic (e.g. ping) | |
847 | * | |
848 | * (mroute kernel upcall "packets" are injected directly on the | |
849 | * socket, this sockopt -or any other- has no effect on them) | |
850 | */ | |
851 | ICMP6_FILTER_SETBLOCKALL(filter); | |
852 | ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter, | |
853 | sizeof(filter)); | |
854 | if (ret) | |
855 | zlog_err( | |
856 | "(VRF %s) failed to set mroute control filter: %m", | |
857 | pim->vrf->name); | |
858 | #endif | |
859 | ||
466e4e5b | 860 | #ifdef SO_BINDTODEVICE |
01b9e3fd | 861 | if (pim->vrf->vrf_id != VRF_DEFAULT |
633fc9b1 DL |
862 | && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, |
863 | pim->vrf->name, strlen(pim->vrf->name))) { | |
01b9e3fd DL |
864 | zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s", |
865 | safe_strerror(errno)); | |
866 | close(fd); | |
867 | return -3; | |
868 | } | |
466e4e5b | 869 | #endif |
90450a3d | 870 | |
01b9e3fd | 871 | } |
12e41d03 | 872 | |
cdbfaec5 DS |
873 | pim->mroute_socket = fd; |
874 | if (pim_mroute_set(pim, 1)) { | |
d62a17ae | 875 | zlog_warn( |
876 | "Could not enable mroute on socket fd=%d: errno=%d: %s", | |
877 | fd, errno, safe_strerror(errno)); | |
878 | close(fd); | |
cdbfaec5 | 879 | pim->mroute_socket = -1; |
d62a17ae | 880 | return -3; |
881 | } | |
12e41d03 | 882 | |
6beed987 | 883 | pim->mroute_socket_creation = pim_time_monotonic_sec(); |
b45cefcb | 884 | |
8ea5d944 | 885 | mroute_read_on(pim); |
12e41d03 | 886 | |
d62a17ae | 887 | return 0; |
12e41d03 DL |
888 | } |
889 | ||
6beed987 | 890 | int pim_mroute_socket_disable(struct pim_instance *pim) |
12e41d03 | 891 | { |
cdbfaec5 | 892 | if (pim_mroute_set(pim, 0)) { |
d62a17ae | 893 | zlog_warn( |
894 | "Could not disable mroute on socket fd=%d: errno=%d: %s", | |
405d6357 | 895 | pim->mroute_socket, errno, safe_strerror(errno)); |
d62a17ae | 896 | return -2; |
897 | } | |
898 | ||
6beed987 | 899 | if (close(pim->mroute_socket)) { |
d62a17ae | 900 | zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", |
405d6357 | 901 | pim->mroute_socket, errno, safe_strerror(errno)); |
d62a17ae | 902 | return -3; |
903 | } | |
904 | ||
8ea5d944 | 905 | mroute_read_off(pim); |
6beed987 | 906 | pim->mroute_socket = -1; |
d62a17ae | 907 | |
908 | return 0; | |
12e41d03 DL |
909 | } |
910 | ||
911 | /* | |
912 | For each network interface (e.g., physical or a virtual tunnel) that | |
913 | would be used for multicast forwarding, a corresponding multicast | |
914 | interface must be added to the kernel. | |
915 | */ | |
a9338fa4 | 916 | int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr, |
d62a17ae | 917 | unsigned char flags) |
12e41d03 | 918 | { |
d62a17ae | 919 | struct pim_interface *pim_ifp = ifp->info; |
a5fa9822 | 920 | pim_vifctl vc; |
d62a17ae | 921 | int err; |
12e41d03 | 922 | |
08f4f901 | 923 | if (PIM_DEBUG_MROUTE) |
15569c58 | 924 | zlog_debug("%s: Add Vif %d (%s[%s])", __func__, |
996c9314 LB |
925 | pim_ifp->mroute_vif_index, ifp->name, |
926 | pim_ifp->pim->vrf->name); | |
08f4f901 | 927 | |
d62a17ae | 928 | memset(&vc, 0, sizeof(vc)); |
a5fa9822 | 929 | vc.vc_vifi = pim_ifp->mroute_vif_index; |
930 | #if PIM_IPV == 4 | |
b3f2bf7c | 931 | #ifdef VIFF_USE_IFINDEX |
a5fa9822 | 932 | vc.vc_lcl_ifindex = ifp->ifindex; |
b3f2bf7c | 933 | #else |
d62a17ae | 934 | if (ifaddr.s_addr == INADDR_ANY) { |
935 | zlog_warn( | |
936 | "%s: unnumbered interfaces are not supported on this platform", | |
15569c58 | 937 | __func__); |
d62a17ae | 938 | return -1; |
939 | } | |
a5fa9822 | 940 | memcpy(&vc.vc_lcl_addr, &ifaddr, sizeof(vc.vc_lcl_addr)); |
941 | #endif | |
942 | #else | |
943 | vc.vc_pifi = ifp->ifindex; | |
b3f2bf7c | 944 | #endif |
a5fa9822 | 945 | vc.vc_flags = flags; |
946 | vc.vc_threshold = PIM_MROUTE_MIN_TTL; | |
947 | vc.vc_rate_limit = 0; | |
d62a17ae | 948 | |
a5fa9822 | 949 | #if PIM_IPV == 4 |
d62a17ae | 950 | #ifdef PIM_DVMRP_TUNNEL |
a5fa9822 | 951 | if (vc.vc_flags & VIFF_TUNNEL) { |
952 | memcpy(&vc.vc_rmt_addr, &vif_remote_addr, | |
953 | sizeof(vc.vc_rmt_addr)); | |
d62a17ae | 954 | } |
a5fa9822 | 955 | #endif |
12e41d03 DL |
956 | #endif |
957 | ||
a5fa9822 | 958 | err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF, |
d62a17ae | 959 | (void *)&vc, sizeof(vc)); |
960 | if (err) { | |
d62a17ae | 961 | zlog_warn( |
a5fa9822 | 962 | "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s", |
15569c58 | 963 | __func__, pim_ifp->pim->mroute_socket, ifp->ifindex, |
a9338fa4 | 964 | &ifaddr, flags, errno, safe_strerror(errno)); |
d62a17ae | 965 | return -2; |
966 | } | |
12e41d03 | 967 | |
d62a17ae | 968 | return 0; |
12e41d03 DL |
969 | } |
970 | ||
ea3d967b | 971 | int pim_mroute_del_vif(struct interface *ifp) |
12e41d03 | 972 | { |
ea3d967b | 973 | struct pim_interface *pim_ifp = ifp->info; |
a5fa9822 | 974 | pim_vifctl vc; |
d62a17ae | 975 | int err; |
976 | ||
ea3d967b | 977 | if (PIM_DEBUG_MROUTE) |
15569c58 | 978 | zlog_debug("%s: Del Vif %d (%s[%s])", __func__, |
996c9314 LB |
979 | pim_ifp->mroute_vif_index, ifp->name, |
980 | pim_ifp->pim->vrf->name); | |
12e41d03 | 981 | |
d62a17ae | 982 | memset(&vc, 0, sizeof(vc)); |
a5fa9822 | 983 | vc.vc_vifi = pim_ifp->mroute_vif_index; |
d62a17ae | 984 | |
a5fa9822 | 985 | err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF, |
d62a17ae | 986 | (void *)&vc, sizeof(vc)); |
987 | if (err) { | |
988 | zlog_warn( | |
a5fa9822 | 989 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", |
15569c58 DA |
990 | __FILE__, __func__, pim_ifp->pim->mroute_socket, |
991 | pim_ifp->mroute_vif_index, errno, safe_strerror(errno)); | |
d62a17ae | 992 | return -2; |
993 | } | |
12e41d03 | 994 | |
d62a17ae | 995 | return 0; |
12e41d03 DL |
996 | } |
997 | ||
60eb7e6b AK |
998 | /* |
999 | * Prevent creating MFC entry with OIF=IIF. | |
1000 | * | |
1001 | * This is a protection against implementation mistakes. | |
1002 | * | |
1003 | * PIM protocol implicitely ensures loopfree multicast topology. | |
1004 | * | |
1005 | * IGMP must be protected against adding looped MFC entries created | |
1006 | * by both source and receiver attached to the same interface. See | |
1007 | * TODO T22. | |
1008 | * We shall allow igmp to create upstream when it is DR for the intf. | |
1009 | * Assume RP reachable via non DR. | |
1010 | */ | |
1011 | bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, | |
1012 | int oif_index) | |
1013 | { | |
1014 | #ifdef PIM_ENFORCE_LOOPFREE_MFC | |
1015 | struct interface *ifp_out; | |
1016 | struct pim_interface *pim_ifp; | |
1017 | ||
1018 | if (c_oil->up && | |
1019 | PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags)) | |
1020 | return true; | |
1021 | ||
1022 | ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index); | |
1023 | if (!ifp_out) | |
1024 | return false; | |
1025 | pim_ifp = ifp_out->info; | |
1026 | if (!pim_ifp) | |
1027 | return false; | |
80a82b56 A |
1028 | if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_GM) && |
1029 | PIM_I_am_DR(pim_ifp)) | |
60eb7e6b AK |
1030 | return true; |
1031 | ||
1032 | return false; | |
1033 | #else | |
1034 | return true; | |
1035 | #endif | |
1036 | } | |
1037 | ||
a9338fa4 DL |
1038 | static inline void pim_mroute_copy(struct channel_oil *out, |
1039 | struct channel_oil *in) | |
5a5f404e AK |
1040 | { |
1041 | int i; | |
1042 | ||
a9338fa4 DL |
1043 | *oil_origin(out) = *oil_origin(in); |
1044 | *oil_mcastgrp(out) = *oil_mcastgrp(in); | |
1045 | *oil_parent(out) = *oil_parent(in); | |
5a5f404e AK |
1046 | |
1047 | for (i = 0; i < MAXVIFS; ++i) { | |
a9338fa4 DL |
1048 | if (*oil_parent(out) == i && |
1049 | !pim_mroute_allow_iif_in_oil(in, i)) { | |
1050 | oil_if_set(out, i, 0); | |
60eb7e6b AK |
1051 | continue; |
1052 | } | |
1053 | ||
a9338fa4 DL |
1054 | if (in->oif_flags[i] & PIM_OIF_FLAG_MUTE) |
1055 | oil_if_set(out, i, 0); | |
5a5f404e | 1056 | else |
a9338fa4 | 1057 | oil_if_set(out, i, oil_if_has(in, i)); |
5a5f404e AK |
1058 | } |
1059 | } | |
1060 | ||
69e3538c AK |
1061 | /* This function must not be called directly 0 |
1062 | * use pim_upstream_mroute_add or pim_static_mroute_add instead | |
1063 | */ | |
1064 | static int pim_mroute_add(struct channel_oil *c_oil, const char *name) | |
12e41d03 | 1065 | { |
9a0f71c9 | 1066 | struct pim_instance *pim = c_oil->pim; |
a9338fa4 | 1067 | struct channel_oil tmp_oil[1] = { }; |
d62a17ae | 1068 | int err; |
d62a17ae | 1069 | |
856e863f DS |
1070 | pim->mroute_add_last = pim_time_monotonic_sec(); |
1071 | ++pim->mroute_add_events; | |
d62a17ae | 1072 | |
5a5f404e AK |
1073 | /* Copy the oil to a temporary structure to fixup (without need to |
1074 | * later restore) before sending the mroute add to the dataplane | |
1075 | */ | |
a9338fa4 | 1076 | pim_mroute_copy(tmp_oil, c_oil); |
d3aded99 | 1077 | |
d62a17ae | 1078 | /* The linux kernel *expects* the incoming |
1079 | * vif to be part of the outgoing list | |
1080 | * in the case of a (*,G). | |
1081 | */ | |
a9338fa4 DL |
1082 | if (pim_addr_is_any(*oil_origin(c_oil))) { |
1083 | oil_if_set(tmp_oil, *oil_parent(c_oil), 1); | |
76e4825a AK |
1084 | } |
1085 | ||
d62a17ae | 1086 | /* |
1087 | * If we have an unresolved cache entry for the S,G | |
1088 | * it is owned by the pimreg for the incoming IIF | |
1089 | * So set pimreg as the IIF temporarily to cause | |
1090 | * the packets to be forwarded. Then set it | |
1091 | * to the correct IIF afterwords. | |
1092 | */ | |
a9338fa4 DL |
1093 | if (!c_oil->installed && !pim_addr_is_any(*oil_origin(c_oil)) |
1094 | && *oil_parent(c_oil) != 0) { | |
1095 | *oil_parent(tmp_oil) = 0; | |
d62a17ae | 1096 | } |
a5fa9822 | 1097 | /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */ |
1098 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC, | |
a9338fa4 | 1099 | &tmp_oil->oil, sizeof(tmp_oil->oil)); |
d62a17ae | 1100 | |
1101 | if (!err && !c_oil->installed | |
a9338fa4 DL |
1102 | && !pim_addr_is_any(*oil_origin(c_oil)) |
1103 | && *oil_parent(c_oil) != 0) { | |
1104 | *oil_parent(tmp_oil) = *oil_parent(c_oil); | |
a5fa9822 | 1105 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC, |
a9338fa4 | 1106 | &tmp_oil->oil, sizeof(tmp_oil->oil)); |
42e01756 | 1107 | } |
37c3fd98 | 1108 | |
d62a17ae | 1109 | if (err) { |
1110 | zlog_warn( | |
a5fa9822 | 1111 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_MFC): errno=%d: %s", |
15569c58 DA |
1112 | __FILE__, __func__, pim->mroute_socket, errno, |
1113 | safe_strerror(errno)); | |
d62a17ae | 1114 | return -2; |
1115 | } | |
12e41d03 | 1116 | |
d62a17ae | 1117 | if (PIM_DEBUG_MROUTE) { |
1118 | char buf[1000]; | |
15569c58 DA |
1119 | zlog_debug("%s(%s), vrf %s Added Route: %s", __func__, name, |
1120 | pim->vrf->name, | |
d62a17ae | 1121 | pim_channel_oil_dump(c_oil, buf, sizeof(buf))); |
1122 | } | |
6a78764e | 1123 | |
fe75a058 SP |
1124 | if (!c_oil->installed) { |
1125 | c_oil->installed = 1; | |
1126 | c_oil->mroute_creation = pim_time_monotonic_sec(); | |
1127 | } | |
e7cd85bd | 1128 | |
d62a17ae | 1129 | return 0; |
12e41d03 DL |
1130 | } |
1131 | ||
7984af18 AK |
1132 | static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil, |
1133 | const char *name) | |
69e3538c AK |
1134 | { |
1135 | vifi_t iif = MAXVIFS; | |
69e3538c AK |
1136 | struct interface *ifp = NULL; |
1137 | struct pim_interface *pim_ifp; | |
1138 | struct pim_upstream *up = c_oil->up; | |
1139 | ||
1140 | if (up) { | |
1141 | if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) { | |
1142 | if (up->parent) | |
1143 | ifp = up->parent->rpf.source_nexthop.interface; | |
1144 | } else { | |
1145 | ifp = up->rpf.source_nexthop.interface; | |
1146 | } | |
1147 | if (ifp) { | |
1148 | pim_ifp = (struct pim_interface *)ifp->info; | |
1149 | if (pim_ifp) | |
1150 | iif = pim_ifp->mroute_vif_index; | |
1151 | } | |
1152 | } | |
7984af18 AK |
1153 | return iif; |
1154 | } | |
69e3538c | 1155 | |
7984af18 AK |
1156 | static int pim_upstream_mroute_update(struct channel_oil *c_oil, |
1157 | const char *name) | |
1158 | { | |
1159 | char buf[1000]; | |
69e3538c | 1160 | |
a9338fa4 | 1161 | if (*oil_parent(c_oil) >= MAXVIFS) { |
69e3538c AK |
1162 | /* the c_oil cannot be installed as a mroute yet */ |
1163 | if (PIM_DEBUG_MROUTE) | |
1164 | zlog_debug( | |
1165 | "%s(%s) %s mroute not ready to be installed; %s", | |
7984af18 | 1166 | __func__, name, |
69e3538c AK |
1167 | pim_channel_oil_dump(c_oil, buf, |
1168 | sizeof(buf)), | |
1169 | c_oil->installed ? | |
1170 | "uninstall" : "skip"); | |
1171 | /* if already installed flush it out as we are going to stop | |
1172 | * updates to it leaving it in a stale state | |
1173 | */ | |
1174 | if (c_oil->installed) | |
1175 | pim_mroute_del(c_oil, name); | |
1176 | /* return success (skipped) */ | |
1177 | return 0; | |
1178 | } | |
1179 | ||
1180 | return pim_mroute_add(c_oil, name); | |
1181 | } | |
1182 | ||
70c86421 AK |
1183 | /* IIF associated with SGrpt entries are re-evaluated when the parent |
1184 | * (*,G) entries IIF changes | |
1185 | */ | |
1186 | static void pim_upstream_all_sources_iif_update(struct pim_upstream *up) | |
1187 | { | |
1188 | struct listnode *listnode; | |
1189 | struct pim_upstream *child; | |
1190 | ||
1191 | for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, | |
1192 | child)) { | |
1193 | if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) | |
1194 | pim_upstream_mroute_iif_update(child->channel_oil, | |
1195 | __func__); | |
1196 | } | |
1197 | } | |
1198 | ||
7984af18 AK |
1199 | /* In the case of "PIM state machine" added mroutes an upstream entry |
1200 | * must be present to decide on the SPT-forwarding vs. RPT-forwarding. | |
1201 | */ | |
1202 | int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name) | |
1203 | { | |
70c86421 AK |
1204 | vifi_t iif; |
1205 | ||
1206 | iif = pim_upstream_get_mroute_iif(c_oil, name); | |
1207 | ||
a9338fa4 DL |
1208 | if (*oil_parent(c_oil) != iif) { |
1209 | *oil_parent(c_oil) = iif; | |
1210 | if (pim_addr_is_any(*oil_origin(c_oil)) && | |
70c86421 AK |
1211 | c_oil->up) |
1212 | pim_upstream_all_sources_iif_update(c_oil->up); | |
1213 | } else { | |
a9338fa4 | 1214 | *oil_parent(c_oil) = iif; |
70c86421 | 1215 | } |
7984af18 AK |
1216 | |
1217 | return pim_upstream_mroute_update(c_oil, name); | |
1218 | } | |
1219 | ||
1220 | /* Look for IIF changes and update the dateplane entry only if the IIF | |
1221 | * has changed. | |
1222 | */ | |
1223 | int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name) | |
1224 | { | |
1225 | vifi_t iif; | |
1226 | char buf[1000]; | |
1227 | ||
1228 | iif = pim_upstream_get_mroute_iif(c_oil, name); | |
a9338fa4 | 1229 | if (*oil_parent(c_oil) == iif) { |
7984af18 AK |
1230 | /* no change */ |
1231 | return 0; | |
1232 | } | |
a9338fa4 | 1233 | *oil_parent(c_oil) = iif; |
7984af18 | 1234 | |
a9338fa4 | 1235 | if (pim_addr_is_any(*oil_origin(c_oil)) && |
70c86421 AK |
1236 | c_oil->up) |
1237 | pim_upstream_all_sources_iif_update(c_oil->up); | |
1238 | ||
7984af18 AK |
1239 | if (PIM_DEBUG_MROUTE_DETAIL) |
1240 | zlog_debug("%s(%s) %s mroute iif update %d", | |
1241 | __func__, name, | |
1242 | pim_channel_oil_dump(c_oil, buf, | |
1243 | sizeof(buf)), iif); | |
1244 | /* XXX: is this hack needed? */ | |
1245 | c_oil->oil_inherited_rescan = 1; | |
1246 | return pim_upstream_mroute_update(c_oil, name); | |
1247 | } | |
1248 | ||
69e3538c AK |
1249 | int pim_static_mroute_add(struct channel_oil *c_oil, const char *name) |
1250 | { | |
1251 | return pim_mroute_add(c_oil, name); | |
1252 | } | |
1253 | ||
7984af18 AK |
1254 | void pim_static_mroute_iif_update(struct channel_oil *c_oil, |
1255 | int input_vif_index, | |
1256 | const char *name) | |
1257 | { | |
a9338fa4 | 1258 | if (*oil_parent(c_oil) == input_vif_index) |
7984af18 AK |
1259 | return; |
1260 | ||
a9338fa4 | 1261 | *oil_parent(c_oil) = input_vif_index; |
7984af18 AK |
1262 | if (input_vif_index == MAXVIFS) |
1263 | pim_mroute_del(c_oil, name); | |
1264 | else | |
1265 | pim_static_mroute_add(c_oil, name); | |
1266 | } | |
1267 | ||
d62a17ae | 1268 | int pim_mroute_del(struct channel_oil *c_oil, const char *name) |
12e41d03 | 1269 | { |
9a0f71c9 | 1270 | struct pim_instance *pim = c_oil->pim; |
d62a17ae | 1271 | int err; |
1272 | ||
856e863f DS |
1273 | pim->mroute_del_last = pim_time_monotonic_sec(); |
1274 | ++pim->mroute_del_events; | |
d62a17ae | 1275 | |
1276 | if (!c_oil->installed) { | |
1277 | if (PIM_DEBUG_MROUTE) { | |
1278 | char buf[1000]; | |
1279 | zlog_debug( | |
1280 | "%s %s: vifi %d for route is %s not installed, do not need to send del req. ", | |
a9338fa4 | 1281 | __FILE__, __func__, *oil_parent(c_oil), |
d62a17ae | 1282 | pim_channel_oil_dump(c_oil, buf, sizeof(buf))); |
1283 | } | |
1284 | return -2; | |
1285 | } | |
12e41d03 | 1286 | |
a5fa9822 | 1287 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_DEL_MFC, |
d62a17ae | 1288 | &c_oil->oil, sizeof(c_oil->oil)); |
1289 | if (err) { | |
1290 | if (PIM_DEBUG_MROUTE) | |
1291 | zlog_warn( | |
a5fa9822 | 1292 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_MFC): errno=%d: %s", |
15569c58 | 1293 | __FILE__, __func__, pim->mroute_socket, errno, |
d62a17ae | 1294 | safe_strerror(errno)); |
1295 | return -2; | |
1296 | } | |
429a291b | 1297 | |
d62a17ae | 1298 | if (PIM_DEBUG_MROUTE) { |
1299 | char buf[1000]; | |
15569c58 DA |
1300 | zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__, name, |
1301 | pim->vrf->name, | |
d62fd596 | 1302 | pim_channel_oil_dump(c_oil, buf, sizeof(buf))); |
d62a17ae | 1303 | } |
781a1745 | 1304 | |
d62a17ae | 1305 | // Reset kernel installed flag |
1306 | c_oil->installed = 0; | |
58302dc7 | 1307 | |
d62a17ae | 1308 | return 0; |
12e41d03 | 1309 | } |
3667e8a0 | 1310 | |
d62a17ae | 1311 | void pim_mroute_update_counters(struct channel_oil *c_oil) |
3667e8a0 | 1312 | { |
9a0f71c9 | 1313 | struct pim_instance *pim = c_oil->pim; |
a5fa9822 | 1314 | pim_sioc_sg_req sgreq; |
d62a17ae | 1315 | |
1316 | c_oil->cc.oldpktcnt = c_oil->cc.pktcnt; | |
1317 | c_oil->cc.oldbytecnt = c_oil->cc.bytecnt; | |
1318 | c_oil->cc.oldwrong_if = c_oil->cc.wrong_if; | |
1319 | ||
1320 | if (!c_oil->installed) { | |
19b807ca | 1321 | c_oil->cc.lastused = 100 * pim->keep_alive_time; |
d62a17ae | 1322 | if (PIM_DEBUG_MROUTE) { |
6fff2cc6 | 1323 | pim_sgaddr sg; |
d62a17ae | 1324 | |
a9338fa4 DL |
1325 | sg.src = *oil_origin(c_oil); |
1326 | sg.grp = *oil_mcastgrp(c_oil); | |
98a81d2b DL |
1327 | zlog_debug("Channel%pSG is not installed no need to collect data from kernel", |
1328 | &sg); | |
d62a17ae | 1329 | } |
1330 | return; | |
c7b1183f | 1331 | } |
c7b1183f | 1332 | |
a9338fa4 | 1333 | |
d62a17ae | 1334 | memset(&sgreq, 0, sizeof(sgreq)); |
a5fa9822 | 1335 | |
0696c2ff MR |
1336 | pim_zlookup_sg_statistics(c_oil); |
1337 | ||
a5fa9822 | 1338 | #if PIM_IPV == 4 |
a9338fa4 DL |
1339 | sgreq.src = *oil_origin(c_oil); |
1340 | sgreq.grp = *oil_mcastgrp(c_oil); | |
a5fa9822 | 1341 | #else |
1342 | sgreq.src = c_oil->oil.mf6cc_origin; | |
1343 | sgreq.grp = c_oil->oil.mf6cc_mcastgrp; | |
a5fa9822 | 1344 | #endif |
1345 | if (ioctl(pim->mroute_socket, PIM_SIOCGETSGCNT, &sgreq)) { | |
6fff2cc6 | 1346 | pim_sgaddr sg; |
d62a17ae | 1347 | |
a9338fa4 DL |
1348 | sg.src = *oil_origin(c_oil); |
1349 | sg.grp = *oil_mcastgrp(c_oil); | |
d62a17ae | 1350 | |
a5fa9822 | 1351 | zlog_warn( |
1352 | "ioctl(PIM_SIOCGETSGCNT=%lu) failure for (S,G)=%pSG: errno=%d: %s", | |
1353 | (unsigned long)PIM_SIOCGETSGCNT, &sg, errno, | |
1354 | safe_strerror(errno)); | |
d62a17ae | 1355 | return; |
c7b1183f | 1356 | } |
3667e8a0 | 1357 | |
d62a17ae | 1358 | c_oil->cc.pktcnt = sgreq.pktcnt; |
1359 | c_oil->cc.bytecnt = sgreq.bytecnt; | |
1360 | c_oil->cc.wrong_if = sgreq.wrong_if; | |
d62a17ae | 1361 | return; |
3667e8a0 | 1362 | } |