]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_static.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / pimd / pim_static.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PIM for Quagga: add the ability to configure multicast static routes
4 * Copyright (C) 2014 Nathan Bahr, ATCorp
5 */
6
7 #include <zebra.h>
8
9 #include "vty.h"
10 #include "if.h"
11 #include "log.h"
12 #include "memory.h"
13 #include "linklist.h"
14
15 #include "pimd.h"
16 #include "pim_instance.h"
17 #include "pim_oil.h"
18 #include "pim_static.h"
19 #include "pim_time.h"
20 #include "pim_str.h"
21 #include "pim_iface.h"
22
23 void pim_static_route_free(struct static_route *s_route)
24 {
25 XFREE(MTYPE_PIM_STATIC_ROUTE, s_route);
26 }
27
28 static struct static_route *static_route_alloc(void)
29 {
30 return XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(struct static_route));
31 }
32
33 static struct static_route *static_route_new(ifindex_t iif, ifindex_t oif,
34 pim_addr group,
35 pim_addr source)
36 {
37 struct static_route *s_route;
38 s_route = static_route_alloc();
39
40 s_route->group = group;
41 s_route->source = source;
42 s_route->iif = iif;
43 s_route->oif_ttls[oif] = 1;
44 s_route->c_oil.oil_ref_count = 1;
45 *oil_origin(&s_route->c_oil) = source;
46 *oil_mcastgrp(&s_route->c_oil) = group;
47 *oil_parent(&s_route->c_oil) = iif;
48 oil_if_set(&s_route->c_oil, oif, 1);
49 s_route->c_oil.oif_creation[oif] = pim_time_monotonic_sec();
50
51 return s_route;
52 }
53
54
55 int pim_static_add(struct pim_instance *pim, struct interface *iif,
56 struct interface *oif, pim_addr group, pim_addr source)
57 {
58 struct listnode *node = NULL;
59 struct static_route *s_route = NULL;
60 struct static_route *original_s_route = NULL;
61 struct pim_interface *pim_iif = iif ? iif->info : NULL;
62 struct pim_interface *pim_oif = oif ? oif->info : NULL;
63 ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
64 ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
65
66 if (!iif_index || !oif_index || iif_index == -1 || oif_index == -1) {
67 zlog_warn(
68 "%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)",
69 __FILE__, __func__, iif_index, oif_index);
70 return -2;
71 }
72
73 #ifdef PIM_ENFORCE_LOOPFREE_MFC
74 if (iif_index == oif_index) {
75 /* looped MFC entry */
76 zlog_warn(
77 "%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)",
78 __FILE__, __func__, iif_index, oif_index);
79 return -4;
80 }
81 #endif
82 if (iif->vrf->vrf_id != oif->vrf->vrf_id) {
83 return -3;
84 }
85
86 for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
87 if (!pim_addr_cmp(s_route->group, group) &&
88 !pim_addr_cmp(s_route->source, source) &&
89 (s_route->iif == iif_index)) {
90
91 if (s_route->oif_ttls[oif_index]) {
92 zlog_warn(
93 "%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%pPAs,source=%pPAs)",
94 __FILE__, __func__, iif_index,
95 oif_index, &group, &source);
96 return -3;
97 }
98
99 /* Ok, from here on out we will be making changes to the
100 * s_route structure, but if
101 * for some reason we fail to commit these changes to
102 * the kernel, we want to be able
103 * restore the state of the list. So copy the node data
104 * and if need be, we can copy
105 * back if it fails.
106 */
107 original_s_route = static_route_alloc();
108 memcpy(original_s_route, s_route,
109 sizeof(struct static_route));
110
111 /* Route exists and has the same input interface, but
112 * adding a new output interface */
113 s_route->oif_ttls[oif_index] = 1;
114 oil_if_set(&s_route->c_oil, oif_index, 1);
115 s_route->c_oil.oif_creation[oif_index] =
116 pim_time_monotonic_sec();
117 ++s_route->c_oil.oil_ref_count;
118 break;
119 }
120 }
121
122 /* If node is null then we reached the end of the list without finding a
123 * match */
124 if (!node) {
125 s_route = static_route_new(iif_index, oif_index, group, source);
126 listnode_add(pim->static_routes, s_route);
127 }
128
129 s_route->c_oil.pim = pim;
130
131 if (pim_static_mroute_add(&s_route->c_oil, __func__)) {
132 zlog_warn(
133 "%s %s: Unable to add static route(iif=%d,oif=%d,group=%pPAs,source=%pPAs)",
134 __FILE__, __func__, iif_index, oif_index, &group,
135 &source);
136
137 /* Need to put s_route back to the way it was */
138 if (original_s_route) {
139 memcpy(s_route, original_s_route,
140 sizeof(struct static_route));
141 } else {
142 /* we never stored off a copy, so it must have been a
143 * fresh new route */
144 listnode_delete(pim->static_routes, s_route);
145 pim_static_route_free(s_route);
146 }
147
148 if (original_s_route) {
149 pim_static_route_free(original_s_route);
150 }
151
152 return -1;
153 }
154
155 /* Make sure we free the memory for the route copy if used */
156 if (original_s_route) {
157 pim_static_route_free(original_s_route);
158 }
159
160 if (PIM_DEBUG_STATIC) {
161 zlog_debug(
162 "%s: Static route added(iif=%d,oif=%d,group=%pPAs,source=%pPAs)",
163 __func__, iif_index, oif_index, &group,
164 &source);
165 }
166
167 return 0;
168 }
169
170 int pim_static_del(struct pim_instance *pim, struct interface *iif,
171 struct interface *oif, pim_addr group, pim_addr source)
172 {
173 struct listnode *node = NULL;
174 struct listnode *nextnode = NULL;
175 struct static_route *s_route = NULL;
176 struct pim_interface *pim_iif = iif ? iif->info : 0;
177 struct pim_interface *pim_oif = oif ? oif->info : 0;
178 ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
179 ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
180
181 if (!iif_index || !oif_index) {
182 zlog_warn(
183 "%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)",
184 __FILE__, __func__, iif_index, oif_index);
185 return -2;
186 }
187
188 for (ALL_LIST_ELEMENTS(pim->static_routes, node, nextnode, s_route)) {
189 if (s_route->iif == iif_index
190 && !pim_addr_cmp(s_route->group, group)
191 && !pim_addr_cmp(s_route->source, source)
192 && s_route->oif_ttls[oif_index]) {
193 s_route->oif_ttls[oif_index] = 0;
194 oil_if_set(&s_route->c_oil, oif_index, 0);
195 --s_route->c_oil.oil_ref_count;
196
197 /* If there are no more outputs then delete the whole
198 * route, otherwise set the route with the new outputs
199 */
200 if (s_route->c_oil.oil_ref_count <= 0
201 ? pim_mroute_del(&s_route->c_oil, __func__)
202 : pim_static_mroute_add(&s_route->c_oil,
203 __func__)) {
204 zlog_warn(
205 "%s %s: Unable to remove static route(iif=%d,oif=%d,group=%pPAs,source=%pPAs)",
206 __FILE__, __func__, iif_index,
207 oif_index, &group, &source);
208
209 s_route->oif_ttls[oif_index] = 1;
210 oil_if_set(&s_route->c_oil, oif_index, 1);
211 ++s_route->c_oil.oil_ref_count;
212
213 return -1;
214 }
215
216 s_route->c_oil.oif_creation[oif_index] = 0;
217
218 if (s_route->c_oil.oil_ref_count <= 0) {
219 listnode_delete(pim->static_routes, s_route);
220 pim_static_route_free(s_route);
221 }
222
223 if (PIM_DEBUG_STATIC) {
224 zlog_debug(
225 "%s: Static route removed(iif=%d,oif=%d,group=%pPAs,source=%pPAs)",
226 __func__, iif_index, oif_index,
227 &group, &source);
228 }
229
230 break;
231 }
232 }
233
234 if (!node) {
235 zlog_warn(
236 "%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%pPAs,source=%pPAs)",
237 __FILE__, __func__, iif_index, oif_index, &group,
238 &source);
239 return -3;
240 }
241
242 return 0;
243 }
244
245 int pim_static_write_mroute(struct pim_instance *pim, struct vty *vty,
246 struct interface *ifp)
247 {
248 struct pim_interface *pim_ifp = ifp->info;
249 struct listnode *node;
250 struct static_route *sroute;
251 int count = 0;
252
253 if (!pim_ifp)
254 return 0;
255
256 for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sroute)) {
257 if (sroute->iif == pim_ifp->mroute_vif_index) {
258 int i;
259 for (i = 0; i < MAXVIFS; i++)
260 if (sroute->oif_ttls[i]) {
261 struct interface *oifp =
262 pim_if_find_by_vif_index(pim,
263 i);
264 if (pim_addr_is_any(sroute->source))
265 vty_out(vty,
266 " " PIM_AF_NAME " mroute %s %pPA\n",
267 oifp->name, &sroute->group);
268 else
269 vty_out(vty,
270 " " PIM_AF_NAME " mroute %s %pPA %pPA\n",
271 oifp->name, &sroute->group,
272 &sroute->source);
273 count++;
274 }
275 }
276 }
277
278 return count;
279 }