2 * SRC-DEST Routing Table
4 * Copyright (C) 2017 by David Lamparter & Christian Franke,
5 * Open Source Routing / NetDEF Inc.
7 * This file is part of FreeRangeRouting (FRR)
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
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.
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
26 #include "srcdest_table.h"
32 DEFINE_MTYPE_STATIC(LIB
, ROUTE_SRC_NODE
, "Route source node")
34 /* ----- functions to manage rnodes _with_ srcdest table ----- */
37 /* must be first in structure for casting to/from route_node */
40 struct route_table
*src_table
;
43 static struct srcdest_rnode
*
44 srcdest_rnode_from_rnode (struct route_node
*rn
)
46 assert (rnode_is_dstnode (rn
));
47 return (struct srcdest_rnode
*) rn
;
50 static struct route_node
*
51 srcdest_rnode_to_rnode (struct srcdest_rnode
*srn
)
53 return (struct route_node
*) srn
;
56 static struct route_node
*
57 srcdest_rnode_create (route_table_delegate_t
*delegate
,
58 struct route_table
*table
)
60 struct srcdest_rnode
*srn
;
61 srn
= XCALLOC (MTYPE_ROUTE_NODE
, sizeof (struct srcdest_rnode
));
62 return srcdest_rnode_to_rnode(srn
);
66 srcdest_rnode_destroy (route_table_delegate_t
*delegate
,
67 struct route_table
*table
, struct route_node
*rn
)
69 struct srcdest_rnode
*srn
= srcdest_rnode_from_rnode(rn
);
70 struct route_table
*src_table
;
72 /* Clear route node's src_table here already, otherwise the
73 * deletion of the last node in the src_table will trigger
74 * another call to route_table_finish for the src_table.
76 * (Compare with srcdest_srcnode_destroy)
78 src_table
= srn
->src_table
;
79 srn
->src_table
= NULL
;
80 route_table_finish(src_table
);
81 XFREE (MTYPE_ROUTE_NODE
, rn
);
84 route_table_delegate_t _srcdest_dstnode_delegate
= {
85 .create_node
= srcdest_rnode_create
,
86 .destroy_node
= srcdest_rnode_destroy
89 /* ----- functions to manage rnodes _in_ srcdest table ----- */
91 /* node creation / deletion for srcdest source prefix nodes.
92 * the route_node isn't actually different from the normal route_node,
93 * but the cleanup is special to free the table (and possibly the
94 * destination prefix's route_node) */
96 static struct route_node
*
97 srcdest_srcnode_create (route_table_delegate_t
*delegate
,
98 struct route_table
*table
)
100 return XCALLOC (MTYPE_ROUTE_SRC_NODE
, sizeof (struct route_node
));
104 srcdest_srcnode_destroy (route_table_delegate_t
*delegate
,
105 struct route_table
*table
, struct route_node
*rn
)
107 struct srcdest_rnode
*srn
;
109 XFREE (MTYPE_ROUTE_SRC_NODE
, rn
);
112 if (srn
->src_table
&& route_table_count (srn
->src_table
) == 0)
114 /* deleting the route_table from inside destroy_node is ONLY
115 * permitted IF table->count is 0! see lib/table.c route_node_delete()
117 route_table_finish (srn
->src_table
);
118 srn
->src_table
= NULL
;
120 /* drop the ref we're holding in srcdest_node_get(). there might be
121 * non-srcdest routes, so the route_node may still exist. hence, it's
122 * important to clear src_table above. */
123 route_unlock_node (srcdest_rnode_to_rnode (srn
));
127 route_table_delegate_t _srcdest_srcnode_delegate
= {
128 .create_node
= srcdest_srcnode_create
,
129 .destroy_node
= srcdest_srcnode_destroy
132 /* NB: read comments in code for refcounting before using! */
133 static struct route_node
*
134 srcdest_srcnode_get (struct route_node
*rn
, struct prefix_ipv6
*src_p
)
136 struct srcdest_rnode
*srn
;
138 if (!src_p
|| src_p
->prefixlen
== 0)
141 srn
= srcdest_rnode_from_rnode (rn
);
144 /* this won't use srcdest_rnode, we're already on the source here */
145 srn
->src_table
= route_table_init_with_delegate (&_srcdest_srcnode_delegate
);
146 srn
->src_table
->info
= srn
;
148 /* there is no route_unlock_node on the original rn here.
149 * The reference is kept for the src_table. */
153 /* only keep 1 reference for the src_table, makes the refcounting
154 * more similar to the non-srcdest case. Either way after return from
155 * function, the only reference held is the one on the return value.
157 * We can safely drop our reference here because src_table is holding
158 * another reference, so this won't free rn */
159 route_unlock_node (rn
);
162 return route_node_get (srn
->src_table
, (struct prefix
*)src_p
);
165 static struct route_node
*
166 srcdest_srcnode_lookup (struct route_node
*rn
, struct prefix_ipv6
*src_p
)
168 struct srcdest_rnode
*srn
;
170 if (!rn
|| !src_p
|| src_p
->prefixlen
== 0)
173 /* We got this rn from a lookup, so its refcnt was incremented. As we won't
174 * return return rn from any point beyond here, we should decrement its refcnt.
176 route_unlock_node (rn
);
178 srn
= srcdest_rnode_from_rnode (rn
);
182 return route_node_lookup (srn
->src_table
, (struct prefix
*)src_p
);
185 /* ----- exported functions ----- */
188 srcdest_table_init(void)
190 return route_table_init_with_delegate(&_srcdest_dstnode_delegate
);
194 srcdest_route_next(struct route_node
*rn
)
196 struct route_node
*next
, *parent
;
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
);
202 if (rnode_is_dstnode(rn
))
204 /* This means the route_node is part of the top hierarchy
205 * and refers to a destination prefix. */
206 struct srcdest_rnode
*srn
= srcdest_rnode_from_rnode(rn
);
209 next
= route_top(srn
->src_table
);
215 /* There is a source prefix. Return the node for it */
216 route_unlock_node(rn
);
221 /* There is no source prefix, just continue as usual */
222 return route_next(rn
);
226 /* This part handles the case of iterating source nodes. */
227 parent
= route_lock_node(rn
->table
->info
);
228 next
= route_next(rn
);
232 /* There is another source node, continue in the source table */
233 route_unlock_node(parent
);
238 /* The source table is complete, continue in the parent table */
239 return route_next(parent
);
244 srcdest_rnode_get (struct route_table
*table
, union prefixptr dst_pu
,
245 struct prefix_ipv6
*src_p
)
247 struct prefix_ipv6
*dst_p
= dst_pu
.p6
;
248 struct route_node
*rn
;
250 rn
= route_node_get (table
, (struct prefix
*) dst_p
);
251 return srcdest_srcnode_get (rn
, src_p
);
255 srcdest_rnode_lookup (struct route_table
*table
, union prefixptr dst_pu
,
256 struct prefix_ipv6
*src_p
)
258 struct prefix_ipv6
*dst_p
= dst_pu
.p6
;
259 struct route_node
*rn
;
260 struct route_node
*srn
;
262 rn
= route_node_lookup_maynull (table
, (struct prefix
*) dst_p
);
263 srn
= srcdest_srcnode_lookup (rn
, src_p
);
265 if (rn
!= NULL
&& rn
== srn
&& !rn
->info
)
267 /* Match the behavior of route_node_lookup and don't return an
268 * empty route-node for a dest-route */
269 route_unlock_node(rn
);
276 srcdest_rnode_prefixes (struct route_node
*rn
, struct prefix
**p
,
277 struct prefix
**src_p
)
279 if (rnode_is_srcnode (rn
))
281 struct route_node
*dst_rn
= rn
->table
->info
;
297 srcdest_rnode2str (struct route_node
*rn
, char *str
, int size
)
299 struct prefix
*dst_p
, *src_p
;
300 char dst_buf
[PREFIX_STRLEN
], src_buf
[PREFIX_STRLEN
];
302 srcdest_rnode_prefixes(rn
, &dst_p
, &src_p
);
304 snprintf(str
, size
, "%s%s%s",
305 prefix2str(dst_p
, dst_buf
, sizeof(dst_buf
)),
306 (src_p
&& src_p
->prefixlen
) ? " from " : "",
307 (src_p
&& src_p
->prefixlen
) ? prefix2str(src_p
, src_buf
,