]> git.proxmox.com Git - mirror_frr.git/blame - lib/srcdest_table.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[mirror_frr.git] / lib / srcdest_table.c
CommitLineData
0964ad9c
DL
1/*
2 * SRC-DEST Routing Table
3 *
4 * Copyright (C) 2017 by David Lamparter & Christian Franke,
5 * Open Source Routing / NetDEF Inc.
6 *
7 * This file is part of FreeRangeRouting (FRR)
8 *
9 * FRR is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2, or (at your option) any
12 * later version.
13 *
14 * FRR is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
896014f4
DL
19 * You should have received a copy of the GNU General Public License along
20 * with this program; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
0964ad9c
DL
22 */
23
24#include <zebra.h>
25
26#include "srcdest_table.h"
27
28#include "memory.h"
29#include "prefix.h"
30#include "table.h"
31
32DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node")
33
34/* ----- functions to manage rnodes _with_ srcdest table ----- */
d62a17ae 35struct srcdest_rnode {
36 /* must be first in structure for casting to/from route_node */
37 ROUTE_NODE_FIELDS;
0964ad9c 38
d62a17ae 39 struct route_table *src_table;
0964ad9c
DL
40};
41
d62a17ae 42static struct srcdest_rnode *srcdest_rnode_from_rnode(struct route_node *rn)
0964ad9c 43{
d62a17ae 44 assert(rnode_is_dstnode(rn));
45 return (struct srcdest_rnode *)rn;
0964ad9c
DL
46}
47
d62a17ae 48static struct route_node *srcdest_rnode_to_rnode(struct srcdest_rnode *srn)
0964ad9c 49{
d62a17ae 50 return (struct route_node *)srn;
0964ad9c
DL
51}
52
d62a17ae 53static struct route_node *srcdest_rnode_create(route_table_delegate_t *delegate,
54 struct route_table *table)
0964ad9c 55{
d62a17ae 56 struct srcdest_rnode *srn;
57 srn = XCALLOC(MTYPE_ROUTE_NODE, sizeof(struct srcdest_rnode));
58 return srcdest_rnode_to_rnode(srn);
0964ad9c
DL
59}
60
d62a17ae 61static void srcdest_rnode_destroy(route_table_delegate_t *delegate,
62 struct route_table *table,
63 struct route_node *rn)
0964ad9c 64{
d62a17ae 65 struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn);
66 struct route_table *src_table;
67
68 /* Clear route node's src_table here already, otherwise the
69 * deletion of the last node in the src_table will trigger
70 * another call to route_table_finish for the src_table.
71 *
72 * (Compare with srcdest_srcnode_destroy)
73 */
74 src_table = srn->src_table;
75 srn->src_table = NULL;
76 route_table_finish(src_table);
77 XFREE(MTYPE_ROUTE_NODE, rn);
0964ad9c
DL
78}
79
80route_table_delegate_t _srcdest_dstnode_delegate = {
d62a17ae 81 .create_node = srcdest_rnode_create,
82 .destroy_node = srcdest_rnode_destroy};
0964ad9c
DL
83
84/* ----- functions to manage rnodes _in_ srcdest table ----- */
85
86/* node creation / deletion for srcdest source prefix nodes.
87 * the route_node isn't actually different from the normal route_node,
88 * but the cleanup is special to free the table (and possibly the
89 * destination prefix's route_node) */
90
91static struct route_node *
d62a17ae 92srcdest_srcnode_create(route_table_delegate_t *delegate,
93 struct route_table *table)
0964ad9c 94{
d62a17ae 95 return XCALLOC(MTYPE_ROUTE_SRC_NODE, sizeof(struct route_node));
0964ad9c
DL
96}
97
d62a17ae 98static void srcdest_srcnode_destroy(route_table_delegate_t *delegate,
99 struct route_table *table,
100 struct route_node *rn)
0964ad9c 101{
d62a17ae 102 struct srcdest_rnode *srn;
103
104 XFREE(MTYPE_ROUTE_SRC_NODE, rn);
105
6ca30e9e 106 srn = route_table_get_info(table);
d62a17ae 107 if (srn->src_table && route_table_count(srn->src_table) == 0) {
108 /* deleting the route_table from inside destroy_node is ONLY
109 * permitted IF table->count is 0! see lib/table.c
110 * route_node_delete()
111 * for details */
112 route_table_finish(srn->src_table);
113 srn->src_table = NULL;
114
115 /* drop the ref we're holding in srcdest_node_get(). there
116 * might be
117 * non-srcdest routes, so the route_node may still exist.
118 * hence, it's
119 * important to clear src_table above. */
120 route_unlock_node(srcdest_rnode_to_rnode(srn));
121 }
0964ad9c
DL
122}
123
124route_table_delegate_t _srcdest_srcnode_delegate = {
d62a17ae 125 .create_node = srcdest_srcnode_create,
126 .destroy_node = srcdest_srcnode_destroy};
0964ad9c
DL
127
128/* NB: read comments in code for refcounting before using! */
d62a17ae 129static struct route_node *srcdest_srcnode_get(struct route_node *rn,
86391e56 130 const struct prefix_ipv6 *src_p)
0964ad9c 131{
d62a17ae 132 struct srcdest_rnode *srn;
133
134 if (!src_p || src_p->prefixlen == 0)
135 return rn;
136
137 srn = srcdest_rnode_from_rnode(rn);
138 if (!srn->src_table) {
139 /* this won't use srcdest_rnode, we're already on the source
140 * here */
141 srn->src_table = route_table_init_with_delegate(
142 &_srcdest_srcnode_delegate);
6ca30e9e 143 route_table_set_info(srn->src_table, srn);
d62a17ae 144
145 /* there is no route_unlock_node on the original rn here.
146 * The reference is kept for the src_table. */
147 } else {
148 /* only keep 1 reference for the src_table, makes the
149 * refcounting
150 * more similar to the non-srcdest case. Either way after
151 * return from
152 * function, the only reference held is the one on the return
153 * value.
154 *
155 * We can safely drop our reference here because src_table is
156 * holding
157 * another reference, so this won't free rn */
158 route_unlock_node(rn);
159 }
160
86391e56 161 return route_node_get(srn->src_table, (const struct prefix *)src_p);
0964ad9c
DL
162}
163
86391e56
MS
164static struct route_node *srcdest_srcnode_lookup(
165 struct route_node *rn,
166 const struct prefix_ipv6 *src_p)
0964ad9c 167{
d62a17ae 168 struct srcdest_rnode *srn;
0964ad9c 169
d62a17ae 170 if (!rn || !src_p || src_p->prefixlen == 0)
171 return rn;
0964ad9c 172
d62a17ae 173 /* We got this rn from a lookup, so its refcnt was incremented. As we
174 * won't
175 * return return rn from any point beyond here, we should decrement its
176 * refcnt.
177 */
178 route_unlock_node(rn);
0964ad9c 179
d62a17ae 180 srn = srcdest_rnode_from_rnode(rn);
181 if (!srn->src_table)
182 return NULL;
0964ad9c 183
86391e56 184 return route_node_lookup(srn->src_table, (const struct prefix *)src_p);
0964ad9c
DL
185}
186
187/* ----- exported functions ----- */
188
d62a17ae 189struct route_table *srcdest_table_init(void)
0964ad9c 190{
d62a17ae 191 return route_table_init_with_delegate(&_srcdest_dstnode_delegate);
0964ad9c
DL
192}
193
d62a17ae 194struct route_node *srcdest_route_next(struct route_node *rn)
0964ad9c 195{
d62a17ae 196 struct route_node *next, *parent;
197
198 /* For a non src-dest node, just return route_next */
199 if (!(rnode_is_dstnode(rn) || rnode_is_srcnode(rn)))
200 return route_next(rn);
201
202 if (rnode_is_dstnode(rn)) {
203 /* This means the route_node is part of the top hierarchy
204 * and refers to a destination prefix. */
205 struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn);
206
207 if (srn->src_table)
208 next = route_top(srn->src_table);
209 else
210 next = NULL;
211
212 if (next) {
213 /* There is a source prefix. Return the node for it */
214 route_unlock_node(rn);
215 return next;
216 } else {
217 /* There is no source prefix, just continue as usual */
218 return route_next(rn);
219 }
220 }
221
222 /* This part handles the case of iterating source nodes. */
6ca30e9e 223 parent = route_lock_node(route_table_get_info(rn->table));
d62a17ae 224 next = route_next(rn);
225
226 if (next) {
227 /* There is another source node, continue in the source table */
228 route_unlock_node(parent);
229 return next;
230 } else {
231 /* The source table is complete, continue in the parent table */
232 return route_next(parent);
233 }
0964ad9c
DL
234}
235
d62a17ae 236struct route_node *srcdest_rnode_get(struct route_table *table,
86391e56
MS
237 union prefixconstptr dst_pu,
238 const struct prefix_ipv6 *src_p)
0964ad9c 239{
86391e56 240 const struct prefix_ipv6 *dst_p = dst_pu.p6;
d62a17ae 241 struct route_node *rn;
0964ad9c 242
86391e56 243 rn = route_node_get(table, (const struct prefix *)dst_p);
d62a17ae 244 return srcdest_srcnode_get(rn, src_p);
0964ad9c
DL
245}
246
d62a17ae 247struct route_node *srcdest_rnode_lookup(struct route_table *table,
86391e56
MS
248 union prefixconstptr dst_pu,
249 const struct prefix_ipv6 *src_p)
0964ad9c 250{
86391e56 251 const struct prefix_ipv6 *dst_p = dst_pu.p6;
d62a17ae 252 struct route_node *rn;
253 struct route_node *srn;
254
86391e56 255 rn = route_node_lookup_maynull(table, (const struct prefix *)dst_p);
d62a17ae 256 srn = srcdest_srcnode_lookup(rn, src_p);
257
258 if (rn != NULL && rn == srn && !rn->info) {
259 /* Match the behavior of route_node_lookup and don't return an
260 * empty route-node for a dest-route */
261 route_unlock_node(rn);
262 return NULL;
263 }
264 return srn;
0964ad9c
DL
265}
266
86391e56
MS
267void srcdest_rnode_prefixes(struct route_node *rn, const struct prefix **p,
268 const struct prefix **src_p)
0964ad9c 269{
d62a17ae 270 if (rnode_is_srcnode(rn)) {
6ca30e9e 271 struct route_node *dst_rn = route_table_get_info(rn->table);
d62a17ae 272 if (p)
273 *p = &dst_rn->p;
274 if (src_p)
275 *src_p = &rn->p;
276 } else {
277 if (p)
278 *p = &rn->p;
279 if (src_p)
280 *src_p = NULL;
281 }
0964ad9c
DL
282}
283
321c1bbb
CF
284const char *srcdest2str(const struct prefix *dst_p,
285 const struct prefix_ipv6 *src_p,
286 char *str, int size)
0964ad9c 287{
d62a17ae 288 char dst_buf[PREFIX_STRLEN], src_buf[PREFIX_STRLEN];
289
d62a17ae 290 snprintf(str, size, "%s%s%s",
291 prefix2str(dst_p, dst_buf, sizeof(dst_buf)),
292 (src_p && src_p->prefixlen) ? " from " : "",
293 (src_p && src_p->prefixlen)
294 ? prefix2str(src_p, src_buf, sizeof(src_buf))
295 : "");
296 return str;
0964ad9c 297}
321c1bbb
CF
298
299const char *srcdest_rnode2str(struct route_node *rn, char *str, int size)
300{
301 const struct prefix *dst_p, *src_p;
302
303 srcdest_rnode_prefixes(rn, &dst_p, &src_p);
36de6e0e 304 return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size);
321c1bbb 305}