]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
pimd: Refactor pim_mroute_add and _del
[mirror_frr.git] / pimd / pim_oil.c
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
25 #include "log.h"
26 #include "memory.h"
27 #include "linklist.h"
28 #include "if.h"
29
30 #include "pimd.h"
31 #include "pim_oil.h"
32 #include "pim_str.h"
33 #include "pim_iface.h"
34 #include "pim_time.h"
35
36 void pim_channel_oil_free(struct channel_oil *c_oil)
37 {
38 XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
39 }
40
41 static void pim_channel_oil_delete(struct channel_oil *c_oil)
42 {
43 /*
44 notice that listnode_delete() can't be moved
45 into pim_channel_oil_free() because the later is
46 called by list_delete_all_node()
47 */
48 listnode_delete(qpim_channel_oil_list, c_oil);
49
50 pim_channel_oil_free(c_oil);
51 }
52
53 static struct channel_oil *channel_oil_new(struct in_addr group_addr,
54 struct in_addr source_addr,
55 int input_vif_index)
56 {
57 struct channel_oil *c_oil;
58 struct interface *ifp_in;
59
60 ifp_in = pim_if_find_by_vif_index(input_vif_index);
61 if (!ifp_in) {
62 /* warning only */
63 char group_str[100];
64 char source_str[100];
65 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
66 pim_inet4_dump("<source?>", source_addr, source_str, sizeof(source_str));
67 zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
68 __PRETTY_FUNCTION__,
69 source_str, group_str, input_vif_index);
70 }
71
72 c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
73 if (!c_oil) {
74 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
75 return 0;
76 }
77
78 c_oil->oil.mfcc_mcastgrp = group_addr;
79 c_oil->oil.mfcc_origin = source_addr;
80 c_oil->oil.mfcc_parent = input_vif_index;
81 c_oil->oil_ref_count = 1;
82
83 zassert(c_oil->oil_size == 0);
84
85 return c_oil;
86 }
87
88 static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr,
89 struct in_addr source_addr,
90 int input_vif_index)
91 {
92 struct channel_oil *c_oil;
93
94 c_oil = channel_oil_new(group_addr, source_addr, input_vif_index);
95 if (!c_oil) {
96 zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil));
97 return 0;
98 }
99
100 listnode_add(qpim_channel_oil_list, c_oil);
101
102 return c_oil;
103 }
104
105 static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr,
106 struct in_addr source_addr)
107 {
108 struct listnode *node;
109 struct channel_oil *c_oil;
110
111 for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
112 if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) &&
113 (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr))
114 return c_oil;
115 }
116
117 return 0;
118 }
119
120 struct channel_oil *pim_channel_oil_add(struct in_addr group_addr,
121 struct in_addr source_addr,
122 int input_vif_index)
123 {
124 struct channel_oil *c_oil;
125
126 c_oil = pim_find_channel_oil(group_addr, source_addr);
127 if (c_oil) {
128 ++c_oil->oil_ref_count;
129 return c_oil;
130 }
131
132 return pim_add_channel_oil(group_addr, source_addr, input_vif_index);
133 }
134
135 void pim_channel_oil_del(struct channel_oil *c_oil)
136 {
137 --c_oil->oil_ref_count;
138
139 if (c_oil->oil_ref_count < 1) {
140 pim_channel_oil_delete(c_oil);
141 }
142 }
143
144 int pim_channel_add_oif(struct channel_oil *channel_oil,
145 struct interface *oif,
146 uint32_t proto_mask)
147 {
148 struct pim_interface *pim_ifp;
149 int old_ttl;
150
151 zassert(channel_oil);
152
153 pim_ifp = oif->info;
154
155 if (PIM_DEBUG_MROUTE) {
156 char group_str[100];
157 char source_str[100];
158 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
159 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
160 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
161 __FILE__, __PRETTY_FUNCTION__,
162 source_str, group_str,
163 proto_mask, oif->name, pim_ifp->mroute_vif_index);
164 }
165
166 #ifdef PIM_ENFORCE_LOOPFREE_MFC
167 /*
168 Prevent creating MFC entry with OIF=IIF.
169
170 This is a protection against implementation mistakes.
171
172 PIM protocol implicitely ensures loopfree multicast topology.
173
174 IGMP must be protected against adding looped MFC entries created
175 by both source and receiver attached to the same interface. See
176 TODO T22.
177 */
178 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
179 if (PIM_DEBUG_MROUTE)
180 {
181 char group_str[100];
182 char source_str[100];
183 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
184 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
185 zlog_debug("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
186 __FILE__, __PRETTY_FUNCTION__,
187 proto_mask, oif->name, pim_ifp->mroute_vif_index,
188 source_str, group_str);
189 }
190 return -2;
191 }
192 #endif
193
194 /* Prevent single protocol from subscribing same interface to
195 channel (S,G) multiple times */
196 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
197 if (PIM_DEBUG_MROUTE)
198 {
199 char group_str[100];
200 char source_str[100];
201 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
202 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
203 zlog_debug("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
204 __FILE__, __PRETTY_FUNCTION__,
205 proto_mask, oif->name, pim_ifp->mroute_vif_index,
206 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
207 source_str, group_str);
208 }
209 return -3;
210 }
211
212 /* Allow other protocol to request subscription of same interface to
213 channel (S,G) multiple times, by silently ignoring further
214 requests */
215 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
216
217 /* Check the OIF really exists before returning, and only log
218 warning otherwise */
219 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
220 if (PIM_DEBUG_MROUTE)
221 {
222 char group_str[100];
223 char source_str[100];
224 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
225 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
226 zlog_debug("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
227 __FILE__, __PRETTY_FUNCTION__,
228 proto_mask, oif->name, pim_ifp->mroute_vif_index,
229 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
230 source_str, group_str);
231 }
232 }
233
234 return 0;
235 }
236
237 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
238
239 if (old_ttl > 0) {
240 if (PIM_DEBUG_MROUTE)
241 {
242 char group_str[100];
243 char source_str[100];
244 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
245 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
246 zlog_debug("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
247 __FILE__, __PRETTY_FUNCTION__,
248 oif->name, pim_ifp->mroute_vif_index,
249 source_str, group_str);
250 }
251 return -4;
252 }
253
254 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
255
256 if (pim_mroute_add(channel_oil)) {
257 if (PIM_DEBUG_MROUTE)
258 {
259 char group_str[100];
260 char source_str[100];
261 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
262 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
263 zlog_debug("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
264 __FILE__, __PRETTY_FUNCTION__,
265 oif->name, pim_ifp->mroute_vif_index,
266 source_str, group_str);
267 }
268
269 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
270 return -5;
271 }
272
273 channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
274 ++channel_oil->oil_size;
275 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
276
277 if (PIM_DEBUG_MROUTE) {
278 char group_str[100];
279 char source_str[100];
280 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
281 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
282 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
283 __FILE__, __PRETTY_FUNCTION__,
284 source_str, group_str,
285 proto_mask, oif->name, pim_ifp->mroute_vif_index);
286 }
287
288 return 0;
289 }