1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* zebra table Manager for routing table identifier management
3 * Copyright (C) 2018 6WIND
10 #include <sys/types.h>
13 #include "lib/memory.h"
14 #include "lib/table.h"
15 #include "lib/network.h"
16 #include "lib/stream.h"
17 #include "lib/zclient.h"
18 #include "lib/libfrr.h"
21 #include "zebra/zserv.h"
22 #include "zebra/zebra_vrf.h"
23 #include "zebra/label_manager.h" /* for NO_PROTO */
24 #include "zebra/table_manager.h"
25 #include "zebra/zebra_errors.h"
27 /* routing table identifiers
30 #if !defined(GNU_LINUX)
36 #define RT_TABLE_ID_LOCAL 255
37 #define RT_TABLE_ID_MAIN 254
38 #define RT_TABLE_ID_DEFAULT 253
39 #define RT_TABLE_ID_COMPAT 252
40 #define RT_TABLE_ID_UNSPEC 0
41 #endif /* !def(GNU_LINUX) */
42 #define RT_TABLE_ID_UNRESERVED_MIN 1
43 #define RT_TABLE_ID_UNRESERVED_MAX 0xffffffff
45 DEFINE_MGROUP(TABLE_MGR
, "Table Manager");
46 DEFINE_MTYPE_STATIC(TABLE_MGR
, TM_CHUNK
, "Table Manager Chunk");
47 DEFINE_MTYPE_STATIC(TABLE_MGR
, TM_TABLE
, "Table Manager Context");
49 static void delete_table_chunk(void *val
)
51 XFREE(MTYPE_TM_CHUNK
, val
);
57 void table_manager_enable(struct zebra_vrf
*zvrf
)
62 if (!vrf_is_backend_netns()
63 && strcmp(zvrf_name(zvrf
), VRF_DEFAULT_NAME
)) {
64 struct zebra_vrf
*def
= zebra_vrf_lookup_by_id(VRF_DEFAULT
);
67 zvrf
->tbl_mgr
= def
->tbl_mgr
;
70 zvrf
->tbl_mgr
= XCALLOC(MTYPE_TM_TABLE
, sizeof(struct table_manager
));
71 zvrf
->tbl_mgr
->lc_list
= list_new();
72 zvrf
->tbl_mgr
->lc_list
->del
= delete_table_chunk
;
76 * Core function, assigns table chunks
78 * It first searches through the list to check if there's one available
79 * (previously released). Otherwise it creates and assigns a new one
81 * @param proto Daemon protocol of client, to identify the owner
82 * @param instance Instance, to identify the owner
83 * @para size Size of the table chunk
84 * @return Pointer to the assigned table chunk
86 struct table_manager_chunk
*assign_table_chunk(uint8_t proto
, uint16_t instance
,
88 struct zebra_vrf
*zvrf
)
90 struct table_manager_chunk
*tmc
;
91 struct listnode
*node
;
93 bool manual_conf
= false;
98 /* first check if there's one available */
99 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
100 if (tmc
->proto
== NO_PROTO
101 && tmc
->end
- tmc
->start
+ 1 == size
) {
103 tmc
->instance
= instance
;
107 /* otherwise create a new one */
108 tmc
= XCALLOC(MTYPE_TM_CHUNK
, sizeof(struct table_manager_chunk
));
110 if (zvrf
->tbl_mgr
->start
|| zvrf
->tbl_mgr
->end
)
112 /* table RT IDs range are [1;252] and [256;0xffffffff]
113 * - check if the requested range can be within the first range,
114 * otherwise elect second one
115 * - TODO : vrf-lites have their own table identifier.
116 * In that case, table_id should be removed from the table range.
118 if (list_isempty(zvrf
->tbl_mgr
->lc_list
)) {
120 start
= RT_TABLE_ID_UNRESERVED_MIN
;
122 start
= zvrf
->tbl_mgr
->start
;
124 start
= ((struct table_manager_chunk
*)listgetdata(
125 listtail(zvrf
->tbl_mgr
->lc_list
)))
131 #if !defined(GNU_LINUX)
137 /* if not enough room space between MIN and COMPAT,
138 * then begin after LOCAL
140 if (start
< RT_TABLE_ID_COMPAT
141 && (size
> RT_TABLE_ID_COMPAT
- RT_TABLE_ID_UNRESERVED_MIN
))
142 start
= RT_TABLE_ID_LOCAL
+ 1;
143 #endif /* !def(GNU_LINUX) */
145 if (RT_TABLE_ID_UNRESERVED_MAX
- size
+ 1 < start
) {
146 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
147 "Reached max table id. Start/Size %u/%u",
149 XFREE(MTYPE_TM_CHUNK
, tmc
);
154 if (zvrf
->tbl_mgr
->end
- size
+ 1 < start
) {
155 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
156 "Reached max table id. Start/Size %u/%u",
158 XFREE(MTYPE_TM_CHUNK
, tmc
);
162 tmc
->end
= tmc
->start
+ size
- 1;
164 tmc
->instance
= instance
;
165 listnode_add(zvrf
->tbl_mgr
->lc_list
, tmc
);
171 * Core function, release no longer used table chunks
173 * @param proto Daemon protocol of client, to identify the owner
174 * @param instance Instance, to identify the owner
175 * @param start First table RT ID of the chunk
176 * @param end Last table RT ID of the chunk
177 * @return 0 on success, -1 otherwise
179 int release_table_chunk(uint8_t proto
, uint16_t instance
, uint32_t start
,
180 uint32_t end
, struct zebra_vrf
*zvrf
)
182 struct listnode
*node
;
183 struct table_manager_chunk
*tmc
;
185 struct table_manager
*tbl_mgr
;
190 tbl_mgr
= zvrf
->tbl_mgr
;
193 /* check that size matches */
194 zlog_debug("Releasing table chunk: %u - %u", start
, end
);
195 /* find chunk and disown */
196 for (ALL_LIST_ELEMENTS_RO(tbl_mgr
->lc_list
, node
, tmc
)) {
197 if (tmc
->start
!= start
)
201 if (tmc
->proto
!= proto
|| tmc
->instance
!= instance
) {
202 flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH
,
203 "%s: Daemon mismatch!!", __func__
);
206 tmc
->proto
= NO_PROTO
;
212 flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK
,
213 "%s: Table chunk not released!!", __func__
);
219 * Release table chunks from a client.
221 * Called on client disconnection or reconnection. It only releases chunks
222 * with empty keep value.
224 * @param client the client to release chunks from
225 * @return Number of chunks released
227 int release_daemon_table_chunks(struct zserv
*client
)
229 uint8_t proto
= client
->proto
;
230 uint16_t instance
= client
->instance
;
231 struct listnode
*node
;
232 struct table_manager_chunk
*tmc
;
236 struct zebra_vrf
*zvrf
;
238 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
243 if (!vrf_is_backend_netns() && vrf
->vrf_id
!= VRF_DEFAULT
)
245 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
246 if (tmc
->proto
== proto
&& tmc
->instance
== instance
) {
247 ret
= release_table_chunk(
248 tmc
->proto
, tmc
->instance
, tmc
->start
,
255 zlog_debug("%s: Released %d table chunks", __func__
, count
);
260 static void table_range_add(struct zebra_vrf
*zvrf
, uint32_t start
,
265 zvrf
->tbl_mgr
->start
= start
;
266 zvrf
->tbl_mgr
->end
= end
;
269 void table_manager_disable(struct zebra_vrf
*zvrf
)
273 if (!vrf_is_backend_netns()
274 && strcmp(zvrf_name(zvrf
), VRF_DEFAULT_NAME
)) {
275 zvrf
->tbl_mgr
= NULL
;
278 list_delete(&zvrf
->tbl_mgr
->lc_list
);
279 XFREE(MTYPE_TM_TABLE
, zvrf
->tbl_mgr
);
280 zvrf
->tbl_mgr
= NULL
;
283 int table_manager_range(struct vty
*vty
, bool add
, struct zebra_vrf
*zvrf
,
284 const char *start_table_str
, const char *end_table_str
)
290 if (!start_table_str
|| !end_table_str
) {
291 vty_out(vty
, "%% Labels not specified\n");
292 return CMD_WARNING_CONFIG_FAILED
;
294 start
= atoi(start_table_str
);
295 end
= atoi(end_table_str
);
297 vty_out(vty
, "%% End table is less than Start table\n");
298 return CMD_WARNING_CONFIG_FAILED
;
301 #if !defined(GNU_LINUX)
307 if ((start
>= RT_TABLE_ID_COMPAT
&& start
<= RT_TABLE_ID_LOCAL
)
308 || (end
>= RT_TABLE_ID_COMPAT
309 && end
<= RT_TABLE_ID_LOCAL
)) {
310 vty_out(vty
, "%% Values forbidden in range [%u;%u]\n",
311 RT_TABLE_ID_COMPAT
, RT_TABLE_ID_LOCAL
);
312 return CMD_WARNING_CONFIG_FAILED
;
314 if (start
< RT_TABLE_ID_COMPAT
&& end
> RT_TABLE_ID_LOCAL
) {
316 "%% Range overlaps range [%u;%u] forbidden\n",
317 RT_TABLE_ID_COMPAT
, RT_TABLE_ID_LOCAL
);
318 return CMD_WARNING_CONFIG_FAILED
;
322 && ((zvrf
->tbl_mgr
->start
&& zvrf
->tbl_mgr
->start
!= start
)
323 || (zvrf
->tbl_mgr
->end
&& zvrf
->tbl_mgr
->end
!= end
))) {
325 "%% New range will be taken into account at restart\n");
327 table_range_add(zvrf
, start
, end
);
329 table_range_add(zvrf
, 0, 0);