1 /* zebra table Manager for routing table identifier management
2 * Copyright (C) 2018 6WIND
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along
15 * with this program; see the file COPYING; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <sys/types.h>
26 #include "lib/memory.h"
27 #include "lib/table.h"
28 #include "lib/network.h"
29 #include "lib/stream.h"
30 #include "lib/zclient.h"
31 #include "lib/libfrr.h"
34 #include "zebra/zserv.h"
35 #include "zebra/zebra_vrf.h"
36 #include "zebra/label_manager.h" /* for NO_PROTO */
37 #include "zebra/table_manager.h"
38 #include "zebra/zebra_errors.h"
40 /* routing table identifiers
43 #if !defined(GNU_LINUX)
49 #define RT_TABLE_ID_LOCAL 255
50 #define RT_TABLE_ID_MAIN 254
51 #define RT_TABLE_ID_DEFAULT 253
52 #define RT_TABLE_ID_COMPAT 252
53 #define RT_TABLE_ID_UNSPEC 0
54 #endif /* !def(GNU_LINUX) */
55 #define RT_TABLE_ID_UNRESERVED_MIN 1
56 #define RT_TABLE_ID_UNRESERVED_MAX 0xffffffff
58 DEFINE_MGROUP(TABLE_MGR
, "Table Manager");
59 DEFINE_MTYPE_STATIC(TABLE_MGR
, TM_CHUNK
, "Table Manager Chunk");
60 DEFINE_MTYPE_STATIC(TABLE_MGR
, TM_TABLE
, "Table Manager Context");
62 static void delete_table_chunk(void *val
)
64 XFREE(MTYPE_TM_CHUNK
, val
);
70 void table_manager_enable(struct zebra_vrf
*zvrf
)
75 if (!vrf_is_backend_netns()
76 && strcmp(zvrf_name(zvrf
), VRF_DEFAULT_NAME
)) {
77 struct zebra_vrf
*def
= zebra_vrf_lookup_by_id(VRF_DEFAULT
);
80 zvrf
->tbl_mgr
= def
->tbl_mgr
;
83 zvrf
->tbl_mgr
= XCALLOC(MTYPE_TM_TABLE
, sizeof(struct table_manager
));
84 zvrf
->tbl_mgr
->lc_list
= list_new();
85 zvrf
->tbl_mgr
->lc_list
->del
= delete_table_chunk
;
89 * Core function, assigns table chunks
91 * It first searches through the list to check if there's one available
92 * (previously released). Otherwise it creates and assigns a new one
94 * @param proto Daemon protocol of client, to identify the owner
95 * @param instance Instance, to identify the owner
96 * @para size Size of the table chunk
97 * @return Pointer to the assigned table chunk
99 struct table_manager_chunk
*assign_table_chunk(uint8_t proto
, uint16_t instance
,
101 struct zebra_vrf
*zvrf
)
103 struct table_manager_chunk
*tmc
;
104 struct listnode
*node
;
106 bool manual_conf
= false;
111 /* first check if there's one available */
112 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
113 if (tmc
->proto
== NO_PROTO
114 && tmc
->end
- tmc
->start
+ 1 == size
) {
116 tmc
->instance
= instance
;
120 /* otherwise create a new one */
121 tmc
= XCALLOC(MTYPE_TM_CHUNK
, sizeof(struct table_manager_chunk
));
125 if (zvrf
->tbl_mgr
->start
|| zvrf
->tbl_mgr
->end
)
127 /* table RT IDs range are [1;252] and [256;0xffffffff]
128 * - check if the requested range can be within the first range,
129 * otherwise elect second one
130 * - TODO : vrf-lites have their own table identifier.
131 * In that case, table_id should be removed from the table range.
133 if (list_isempty(zvrf
->tbl_mgr
->lc_list
)) {
135 start
= RT_TABLE_ID_UNRESERVED_MIN
;
137 start
= zvrf
->tbl_mgr
->start
;
139 start
= ((struct table_manager_chunk
*)listgetdata(
140 listtail(zvrf
->tbl_mgr
->lc_list
)))
146 #if !defined(GNU_LINUX)
152 /* if not enough room space between MIN and COMPAT,
153 * then begin after LOCAL
155 if (start
< RT_TABLE_ID_COMPAT
156 && (size
> RT_TABLE_ID_COMPAT
- RT_TABLE_ID_UNRESERVED_MIN
))
157 start
= RT_TABLE_ID_LOCAL
+ 1;
158 #endif /* !def(GNU_LINUX) */
160 if (RT_TABLE_ID_UNRESERVED_MAX
- size
+ 1 < start
) {
161 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
162 "Reached max table id. Start/Size %u/%u",
164 XFREE(MTYPE_TM_CHUNK
, tmc
);
169 if (zvrf
->tbl_mgr
->end
- size
+ 1 < start
) {
170 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
171 "Reached max table id. Start/Size %u/%u",
173 XFREE(MTYPE_TM_CHUNK
, tmc
);
177 tmc
->end
= tmc
->start
+ size
- 1;
179 tmc
->instance
= instance
;
180 listnode_add(zvrf
->tbl_mgr
->lc_list
, tmc
);
186 * Core function, release no longer used table chunks
188 * @param proto Daemon protocol of client, to identify the owner
189 * @param instance Instance, to identify the owner
190 * @param start First table RT ID of the chunk
191 * @param end Last table RT ID of the chunk
192 * @return 0 on success, -1 otherwise
194 int release_table_chunk(uint8_t proto
, uint16_t instance
, uint32_t start
,
195 uint32_t end
, struct zebra_vrf
*zvrf
)
197 struct listnode
*node
;
198 struct table_manager_chunk
*tmc
;
200 struct table_manager
*tbl_mgr
;
205 tbl_mgr
= zvrf
->tbl_mgr
;
208 /* check that size matches */
209 zlog_debug("Releasing table chunk: %u - %u", start
, end
);
210 /* find chunk and disown */
211 for (ALL_LIST_ELEMENTS_RO(tbl_mgr
->lc_list
, node
, tmc
)) {
212 if (tmc
->start
!= start
)
216 if (tmc
->proto
!= proto
|| tmc
->instance
!= instance
) {
217 flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH
,
218 "%s: Daemon mismatch!!", __func__
);
221 tmc
->proto
= NO_PROTO
;
227 flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK
,
228 "%s: Table chunk not released!!", __func__
);
234 * Release table chunks from a client.
236 * Called on client disconnection or reconnection. It only releases chunks
237 * with empty keep value.
239 * @param client the client to release chunks from
240 * @return Number of chunks released
242 int release_daemon_table_chunks(struct zserv
*client
)
244 uint8_t proto
= client
->proto
;
245 uint16_t instance
= client
->instance
;
246 struct listnode
*node
;
247 struct table_manager_chunk
*tmc
;
251 struct zebra_vrf
*zvrf
;
253 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
258 if (!vrf_is_backend_netns() && vrf
->vrf_id
!= VRF_DEFAULT
)
260 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
261 if (tmc
->proto
== proto
&& tmc
->instance
== instance
) {
262 ret
= release_table_chunk(
263 tmc
->proto
, tmc
->instance
, tmc
->start
,
270 zlog_debug("%s: Released %d table chunks", __func__
, count
);
275 static void table_range_add(struct zebra_vrf
*zvrf
, uint32_t start
,
280 zvrf
->tbl_mgr
->start
= start
;
281 zvrf
->tbl_mgr
->end
= end
;
284 void table_manager_disable(struct zebra_vrf
*zvrf
)
288 if (!vrf_is_backend_netns()
289 && strcmp(zvrf_name(zvrf
), VRF_DEFAULT_NAME
)) {
290 zvrf
->tbl_mgr
= NULL
;
293 list_delete(&zvrf
->tbl_mgr
->lc_list
);
294 XFREE(MTYPE_TM_TABLE
, zvrf
->tbl_mgr
);
295 zvrf
->tbl_mgr
= NULL
;
298 int table_manager_range(struct vty
*vty
, bool add
, struct zebra_vrf
*zvrf
,
299 const char *start_table_str
, const char *end_table_str
)
305 if (!start_table_str
|| !end_table_str
) {
306 vty_out(vty
, "%% Labels not specified\n");
307 return CMD_WARNING_CONFIG_FAILED
;
309 start
= atoi(start_table_str
);
310 end
= atoi(end_table_str
);
312 vty_out(vty
, "%% End table is less than Start table\n");
313 return CMD_WARNING_CONFIG_FAILED
;
316 #if !defined(GNU_LINUX)
322 if ((start
>= RT_TABLE_ID_COMPAT
&& start
<= RT_TABLE_ID_LOCAL
)
323 || (end
>= RT_TABLE_ID_COMPAT
324 && end
<= RT_TABLE_ID_LOCAL
)) {
325 vty_out(vty
, "%% Values forbidden in range [%u;%u]\n",
326 RT_TABLE_ID_COMPAT
, RT_TABLE_ID_LOCAL
);
327 return CMD_WARNING_CONFIG_FAILED
;
329 if (start
< RT_TABLE_ID_COMPAT
&& end
> RT_TABLE_ID_LOCAL
) {
331 "%% Range overlaps range [%u;%u] forbidden\n",
332 RT_TABLE_ID_COMPAT
, RT_TABLE_ID_LOCAL
);
333 return CMD_WARNING_CONFIG_FAILED
;
337 && ((zvrf
->tbl_mgr
->start
&& zvrf
->tbl_mgr
->start
!= start
)
338 || (zvrf
->tbl_mgr
->end
&& zvrf
->tbl_mgr
->end
!= end
))) {
340 "%% New range will be taken into account at restart\n");
342 table_range_add(zvrf
, start
, end
);
344 table_range_add(zvrf
, 0, 0);