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