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