Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * TIB (Tree Information Base) - just PIM <> IGMP/MLD glue for now |
4 | | * Copyright (C) 2022 David Lamparter for NetDEF, Inc. |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "pim_tib.h" |
10 | | |
11 | | #include "pimd.h" |
12 | | #include "pim_instance.h" |
13 | | #include "pim_iface.h" |
14 | | #include "pim_upstream.h" |
15 | | #include "pim_oil.h" |
16 | | #include "pim_nht.h" |
17 | | |
18 | | static struct channel_oil * |
19 | | tib_sg_oil_setup(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif) |
20 | 0 | { |
21 | 0 | struct pim_interface *pim_oif = oif->info; |
22 | 0 | int input_iface_vif_index = 0; |
23 | 0 | pim_addr vif_source; |
24 | 0 | struct prefix grp; |
25 | 0 | struct pim_nexthop nexthop; |
26 | 0 | struct pim_upstream *up = NULL; |
27 | |
|
28 | 0 | if (!pim_rp_set_upstream_addr(pim, &vif_source, sg.src, sg.grp)) { |
29 | | /* no PIM RP - create a dummy channel oil */ |
30 | 0 | return pim_channel_oil_add(pim, &sg, __func__); |
31 | 0 | } |
32 | | |
33 | 0 | pim_addr_to_prefix(&grp, sg.grp); |
34 | |
|
35 | 0 | up = pim_upstream_find(pim, &sg); |
36 | 0 | if (up) { |
37 | 0 | memcpy(&nexthop, &up->rpf.source_nexthop, |
38 | 0 | sizeof(struct pim_nexthop)); |
39 | 0 | (void)pim_ecmp_nexthop_lookup(pim, &nexthop, vif_source, &grp, |
40 | 0 | 0); |
41 | 0 | if (nexthop.interface) |
42 | 0 | input_iface_vif_index = pim_if_find_vifindex_by_ifindex( |
43 | 0 | pim, nexthop.interface->ifindex); |
44 | 0 | } else |
45 | 0 | input_iface_vif_index = |
46 | 0 | pim_ecmp_fib_lookup_if_vif_index(pim, vif_source, &grp); |
47 | |
|
48 | 0 | if (PIM_DEBUG_ZEBRA) |
49 | 0 | zlog_debug("%s: NHT %pSG vif_source %pPAs vif_index:%d", |
50 | 0 | __func__, &sg, &vif_source, input_iface_vif_index); |
51 | |
|
52 | 0 | if (input_iface_vif_index < 1) { |
53 | 0 | if (PIM_DEBUG_GM_TRACE) |
54 | 0 | zlog_debug( |
55 | 0 | "%s %s: could not find input interface for %pSG", |
56 | 0 | __FILE__, __func__, &sg); |
57 | |
|
58 | 0 | return pim_channel_oil_add(pim, &sg, __func__); |
59 | 0 | } |
60 | | |
61 | | /* |
62 | | * Protect IGMP against adding looped MFC entries created by both |
63 | | * source and receiver attached to the same interface. See TODO T22. |
64 | | * Block only when the intf is non DR DR must create upstream. |
65 | | */ |
66 | 0 | if ((input_iface_vif_index == pim_oif->mroute_vif_index) && |
67 | 0 | !(PIM_I_am_DR(pim_oif))) { |
68 | | /* ignore request for looped MFC entry */ |
69 | 0 | if (PIM_DEBUG_GM_TRACE) |
70 | 0 | zlog_debug( |
71 | 0 | "%s: ignoring request for looped MFC entry (S,G)=%pSG: oif=%s vif_index=%d", |
72 | 0 | __func__, &sg, oif->name, |
73 | 0 | input_iface_vif_index); |
74 | |
|
75 | 0 | return NULL; |
76 | 0 | } |
77 | | |
78 | 0 | return pim_channel_oil_add(pim, &sg, __func__); |
79 | 0 | } |
80 | | |
81 | | bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg, |
82 | | struct interface *oif, struct channel_oil **oilp) |
83 | 0 | { |
84 | 0 | struct pim_interface *pim_oif = oif->info; |
85 | |
|
86 | 0 | if (!pim_oif) { |
87 | 0 | if (PIM_DEBUG_GM_TRACE) |
88 | 0 | zlog_debug("%s: multicast not enabled on oif=%s?", |
89 | 0 | __func__, oif->name); |
90 | 0 | return false; |
91 | 0 | } |
92 | | |
93 | 0 | if (!*oilp) |
94 | 0 | *oilp = tib_sg_oil_setup(pim, sg, oif); |
95 | 0 | if (!*oilp) |
96 | 0 | return false; |
97 | | |
98 | 0 | if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { |
99 | 0 | int result; |
100 | |
|
101 | 0 | result = pim_channel_add_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_GM, |
102 | 0 | __func__); |
103 | 0 | if (result) { |
104 | 0 | if (PIM_DEBUG_MROUTE) |
105 | 0 | zlog_warn("%s: add_oif() failed with return=%d", |
106 | 0 | __func__, result); |
107 | 0 | return false; |
108 | 0 | } |
109 | 0 | } else { |
110 | 0 | if (PIM_DEBUG_GM_TRACE) |
111 | 0 | zlog_debug( |
112 | 0 | "%s: %pSG was received on %s interface but we are not DR for that interface", |
113 | 0 | __func__, &sg, oif->name); |
114 | |
|
115 | 0 | return false; |
116 | 0 | } |
117 | | /* |
118 | | Feed IGMPv3-gathered local membership information into PIM |
119 | | per-interface (S,G) state. |
120 | | */ |
121 | 0 | if (!pim_ifchannel_local_membership_add(oif, &sg, false /*is_vxlan*/)) { |
122 | 0 | if (PIM_DEBUG_MROUTE) |
123 | 0 | zlog_warn( |
124 | 0 | "%s: Failure to add local membership for %pSG", |
125 | 0 | __func__, &sg); |
126 | |
|
127 | 0 | pim_channel_del_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_GM, |
128 | 0 | __func__); |
129 | 0 | return false; |
130 | 0 | } |
131 | | |
132 | 0 | return true; |
133 | 0 | } |
134 | | |
135 | | void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg, |
136 | | struct interface *oif, struct channel_oil **oilp) |
137 | 0 | { |
138 | 0 | int result; |
139 | | |
140 | | /* |
141 | | It appears that in certain circumstances that |
142 | | igmp_source_forward_stop is called when IGMP forwarding |
143 | | was not enabled in oif_flags for this outgoing interface. |
144 | | Possibly because of multiple calls. When that happens, we |
145 | | enter the below if statement and this function returns early |
146 | | which in turn triggers the calling function to assert. |
147 | | Making the call to pim_channel_del_oif and ignoring the return code |
148 | | fixes the issue without ill effect, similar to |
149 | | pim_forward_stop below. |
150 | | */ |
151 | 0 | result = pim_channel_del_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_GM, |
152 | 0 | __func__); |
153 | 0 | if (result) { |
154 | 0 | if (PIM_DEBUG_GM_TRACE) |
155 | 0 | zlog_debug( |
156 | 0 | "%s: pim_channel_del_oif() failed with return=%d", |
157 | 0 | __func__, result); |
158 | 0 | return; |
159 | 0 | } |
160 | | |
161 | | /* |
162 | | Feed IGMPv3-gathered local membership information into PIM |
163 | | per-interface (S,G) state. |
164 | | */ |
165 | 0 | pim_ifchannel_local_membership_del(oif, &sg); |
166 | 0 | } |