1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * SRC-DEST Routing Table
5 * Copyright (C) 2017 by David Lamparter & Christian Franke,
6 * Open Source Routing / NetDEF Inc.
8 * This file is part of FRRouting (FRR)
13 #include "srcdest_table.h"
20 DEFINE_MTYPE_STATIC(LIB
, ROUTE_SRC_NODE
, "Route source node");
22 /* ----- functions to manage rnodes _with_ srcdest table ----- */
23 struct srcdest_rnode
{
24 /* must be first in structure for casting to/from route_node */
27 struct route_table
*src_table
;
30 static struct srcdest_rnode
*srcdest_rnode_from_rnode(struct route_node
*rn
)
32 assert(rnode_is_dstnode(rn
));
33 return (struct srcdest_rnode
*)rn
;
36 static struct route_node
*srcdest_rnode_to_rnode(struct srcdest_rnode
*srn
)
38 return (struct route_node
*)srn
;
41 static struct route_node
*srcdest_rnode_create(route_table_delegate_t
*delegate
,
42 struct route_table
*table
)
44 struct srcdest_rnode
*srn
;
45 srn
= XCALLOC(MTYPE_ROUTE_NODE
, sizeof(struct srcdest_rnode
));
46 return srcdest_rnode_to_rnode(srn
);
49 static void srcdest_rnode_destroy(route_table_delegate_t
*delegate
,
50 struct route_table
*table
,
51 struct route_node
*rn
)
53 struct srcdest_rnode
*srn
= srcdest_rnode_from_rnode(rn
);
54 struct route_table
*src_table
;
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.
60 * (Compare with srcdest_srcnode_destroy)
62 src_table
= srn
->src_table
;
63 srn
->src_table
= NULL
;
64 route_table_finish(src_table
);
65 XFREE(MTYPE_ROUTE_NODE
, rn
);
68 route_table_delegate_t _srcdest_dstnode_delegate
= {
69 .create_node
= srcdest_rnode_create
,
70 .destroy_node
= srcdest_rnode_destroy
};
72 /* ----- functions to manage rnodes _in_ srcdest table ----- */
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) */
79 static struct route_node
*
80 srcdest_srcnode_create(route_table_delegate_t
*delegate
,
81 struct route_table
*table
)
83 return XCALLOC(MTYPE_ROUTE_SRC_NODE
, sizeof(struct route_node
));
86 static void srcdest_srcnode_destroy(route_table_delegate_t
*delegate
,
87 struct route_table
*table
,
88 struct route_node
*rn
)
90 struct srcdest_rnode
*srn
;
92 XFREE(MTYPE_ROUTE_SRC_NODE
, rn
);
94 srn
= route_table_get_info(table
);
95 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
100 route_table_finish(srn
->src_table
);
101 srn
->src_table
= NULL
;
103 /* drop the ref we're holding in srcdest_node_get(). there
105 * non-srcdest routes, so the route_node may still exist.
107 * important to clear src_table above. */
108 route_unlock_node(srcdest_rnode_to_rnode(srn
));
112 route_table_delegate_t _srcdest_srcnode_delegate
= {
113 .create_node
= srcdest_srcnode_create
,
114 .destroy_node
= srcdest_srcnode_destroy
};
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
)
120 struct srcdest_rnode
*srn
;
122 if (!src_p
|| src_p
->prefixlen
== 0)
125 srn
= srcdest_rnode_from_rnode(rn
);
126 if (!srn
->src_table
) {
127 /* this won't use srcdest_rnode, we're already on the source
129 srn
->src_table
= route_table_init_with_delegate(
130 &_srcdest_srcnode_delegate
);
131 route_table_set_info(srn
->src_table
, srn
);
133 /* there is no route_unlock_node on the original rn here.
134 * The reference is kept for the src_table. */
136 /* only keep 1 reference for the src_table, makes the
138 * more similar to the non-srcdest case. Either way after
140 * function, the only reference held is the one on the return
143 * We can safely drop our reference here because src_table is
145 * another reference, so this won't free rn */
146 route_unlock_node(rn
);
149 return route_node_get(srn
->src_table
, (const struct prefix
*)src_p
);
152 static struct route_node
*srcdest_srcnode_lookup(
153 struct route_node
*rn
,
154 const struct prefix_ipv6
*src_p
)
156 struct srcdest_rnode
*srn
;
158 if (!rn
|| !src_p
|| src_p
->prefixlen
== 0)
161 /* We got this rn from a lookup, so its refcnt was incremented. As we
163 * return return rn from any point beyond here, we should decrement its
166 route_unlock_node(rn
);
168 srn
= srcdest_rnode_from_rnode(rn
);
172 return route_node_lookup(srn
->src_table
, (const struct prefix
*)src_p
);
175 /* ----- exported functions ----- */
177 struct route_table
*srcdest_table_init(void)
179 return route_table_init_with_delegate(&_srcdest_dstnode_delegate
);
182 struct route_node
*srcdest_route_next(struct route_node
*rn
)
184 struct route_node
*next
, *parent
;
186 /* For a non src-dest node, just return route_next */
187 if (!(rnode_is_dstnode(rn
) || rnode_is_srcnode(rn
)))
188 return route_next(rn
);
190 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 struct srcdest_rnode
*srn
= srcdest_rnode_from_rnode(rn
);
196 next
= route_top(srn
->src_table
);
201 /* There is a source prefix. Return the node for it */
202 route_unlock_node(rn
);
205 /* There is no source prefix, just continue as usual */
206 return route_next(rn
);
210 /* This part handles the case of iterating source nodes. */
211 parent
= route_lock_node(route_table_get_info(rn
->table
));
212 next
= route_next(rn
);
215 /* There is another source node, continue in the source table */
216 route_unlock_node(parent
);
219 /* The source table is complete, continue in the parent table */
220 return route_next(parent
);
224 struct route_node
*srcdest_rnode_get(struct route_table
*table
,
225 union prefixconstptr dst_pu
,
226 const struct prefix_ipv6
*src_p
)
228 const struct prefix_ipv6
*dst_p
= dst_pu
.p6
;
229 struct route_node
*rn
;
231 rn
= route_node_get(table
, (const struct prefix
*)dst_p
);
232 return srcdest_srcnode_get(rn
, src_p
);
235 struct route_node
*srcdest_rnode_lookup(struct route_table
*table
,
236 union prefixconstptr dst_pu
,
237 const struct prefix_ipv6
*src_p
)
239 const struct prefix_ipv6
*dst_p
= dst_pu
.p6
;
240 struct route_node
*rn
;
241 struct route_node
*srn
;
243 rn
= route_node_lookup_maynull(table
, (const struct prefix
*)dst_p
);
244 srn
= srcdest_srcnode_lookup(rn
, src_p
);
246 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 route_unlock_node(rn
);
255 void srcdest_rnode_prefixes(const struct route_node
*rn
,
256 const struct prefix
**p
,
257 const struct prefix
**src_p
)
259 if (rnode_is_srcnode(rn
)) {
260 struct route_node
*dst_rn
= route_table_get_info(rn
->table
);
273 const char *srcdest2str(const struct prefix
*dst_p
,
274 const struct prefix_ipv6
*src_p
,
277 char dst_buf
[PREFIX_STRLEN
], src_buf
[PREFIX_STRLEN
];
279 snprintf(str
, size
, "%s%s%s",
280 prefix2str(dst_p
, dst_buf
, sizeof(dst_buf
)),
281 (src_p
&& src_p
->prefixlen
) ? " from " : "",
282 (src_p
&& src_p
->prefixlen
)
283 ? prefix2str(src_p
, src_buf
, sizeof(src_buf
))
288 const char *srcdest_rnode2str(const struct route_node
*rn
, char *str
, int size
)
290 const struct prefix
*dst_p
, *src_p
;
292 srcdest_rnode_prefixes(rn
, &dst_p
, &src_p
);
293 return srcdest2str(dst_p
, (const struct prefix_ipv6
*)src_p
, str
, size
);
296 printfrr_ext_autoreg_p("RN", printfrr_rn
);
297 static ssize_t
printfrr_rn(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
300 const struct route_node
*rn
= ptr
;
301 const struct prefix
*dst_p
, *src_p
;
302 char cbuf
[PREFIX_STRLEN
* 2 + 6];
305 return bputs(buf
, "(null)");
307 srcdest_rnode_prefixes(rn
, &dst_p
, &src_p
);
308 srcdest2str(dst_p
, (const struct prefix_ipv6
*)src_p
,
310 return bputs(buf
, cbuf
);
313 struct route_table
*srcdest_srcnode_table(struct route_node
*rn
)
315 if (rnode_is_dstnode(rn
)) {
316 struct srcdest_rnode
*srn
= srcdest_rnode_from_rnode(rn
);
318 return srn
->src_table
;