]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
pimd: Separate pim vif index spot from ifindex
[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 if (pim_ifp->mroute_vif_index < 1) {
167 zlog_warn("%s %s: interface %s vif_index=%d < 1",
168 __FILE__, __PRETTY_FUNCTION__,
169 oif->name, pim_ifp->mroute_vif_index);
170 return -1;
171 }
172
173 #ifdef PIM_ENFORCE_LOOPFREE_MFC
174 /*
175 Prevent creating MFC entry with OIF=IIF.
176
177 This is a protection against implementation mistakes.
178
179 PIM protocol implicitely ensures loopfree multicast topology.
180
181 IGMP must be protected against adding looped MFC entries created
182 by both source and receiver attached to the same interface. See
183 TODO T22.
184 */
185 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
186 char group_str[100];
187 char source_str[100];
188 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
189 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
190 zlog_warn("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
191 __FILE__, __PRETTY_FUNCTION__,
192 proto_mask, oif->name, pim_ifp->mroute_vif_index,
193 source_str, group_str);
194 return -2;
195 }
196 #endif
197
198 /* Prevent single protocol from subscribing same interface to
199 channel (S,G) multiple times */
200 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
201 char group_str[100];
202 char source_str[100];
203 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
204 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
205 zlog_warn("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
206 __FILE__, __PRETTY_FUNCTION__,
207 proto_mask, oif->name, pim_ifp->mroute_vif_index,
208 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
209 source_str, group_str);
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 char group_str[100];
222 char source_str[100];
223 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
224 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
225 zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
226 __FILE__, __PRETTY_FUNCTION__,
227 proto_mask, oif->name, pim_ifp->mroute_vif_index,
228 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
229 source_str, group_str);
230 }
231
232 return 0;
233 }
234
235 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
236
237 if (old_ttl > 0) {
238 char group_str[100];
239 char source_str[100];
240 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
241 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
242 zlog_warn("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
243 __FILE__, __PRETTY_FUNCTION__,
244 oif->name, pim_ifp->mroute_vif_index,
245 source_str, group_str);
246 return -4;
247 }
248
249 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
250
251 if (pim_mroute_add(&channel_oil->oil)) {
252 char group_str[100];
253 char source_str[100];
254 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
255 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
256 zlog_warn("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
257 __FILE__, __PRETTY_FUNCTION__,
258 oif->name, pim_ifp->mroute_vif_index,
259 source_str, group_str);
260
261 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
262 return -5;
263 }
264
265 channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
266 ++channel_oil->oil_size;
267 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
268
269 if (PIM_DEBUG_MROUTE) {
270 char group_str[100];
271 char source_str[100];
272 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
273 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
274 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
275 __FILE__, __PRETTY_FUNCTION__,
276 source_str, group_str,
277 proto_mask, oif->name, pim_ifp->mroute_vif_index);
278 }
279
280 return 0;
281 }