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() && zvrf_id(zvrf
) != VRF_DEFAULT
) {
76 struct zebra_vrf
*def
= zebra_vrf_lookup_by_id(VRF_DEFAULT
);
79 zvrf
->tbl_mgr
= def
->tbl_mgr
;
82 zvrf
->tbl_mgr
= XCALLOC(MTYPE_TM_TABLE
, sizeof(struct table_manager
));
83 zvrf
->tbl_mgr
->lc_list
= list_new();
84 zvrf
->tbl_mgr
->lc_list
->del
= delete_table_chunk
;
88 * Core function, assigns table chunks
90 * It first searches through the list to check if there's one available
91 * (previously released). Otherwise it creates and assigns a new one
93 * @param proto Daemon protocol of client, to identify the owner
94 * @param instance Instance, to identify the owner
95 * @para size Size of the table chunk
96 * @return Pointer to the assigned table chunk
98 struct table_manager_chunk
*assign_table_chunk(uint8_t proto
, uint16_t instance
,
100 struct zebra_vrf
*zvrf
)
102 struct table_manager_chunk
*tmc
;
103 struct listnode
*node
;
105 bool manual_conf
= false;
110 /* first check if there's one available */
111 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
112 if (tmc
->proto
== NO_PROTO
113 && tmc
->end
- tmc
->start
+ 1 == size
) {
115 tmc
->instance
= instance
;
119 /* otherwise create a new one */
120 tmc
= XCALLOC(MTYPE_TM_CHUNK
, sizeof(struct table_manager_chunk
));
124 if (zvrf
->tbl_mgr
->start
|| zvrf
->tbl_mgr
->end
)
126 /* table RT IDs range are [1;252] and [256;0xffffffff]
127 * - check if the requested range can be within the first range,
128 * otherwise elect second one
129 * - TODO : vrf-lites have their own table identifier.
130 * In that case, table_id should be removed from the table range.
132 if (list_isempty(zvrf
->tbl_mgr
->lc_list
)) {
134 start
= RT_TABLE_ID_UNRESERVED_MIN
;
136 start
= zvrf
->tbl_mgr
->start
;
138 start
= ((struct table_manager_chunk
*)listgetdata(
139 listtail(zvrf
->tbl_mgr
->lc_list
)))
145 #if !defined(GNU_LINUX)
151 /* if not enough room space between MIN and COMPAT,
152 * then begin after LOCAL
154 if (start
< RT_TABLE_ID_COMPAT
155 && (size
> RT_TABLE_ID_COMPAT
- RT_TABLE_ID_UNRESERVED_MIN
))
156 start
= RT_TABLE_ID_LOCAL
+ 1;
157 #endif /* !def(GNU_LINUX) */
159 if (RT_TABLE_ID_UNRESERVED_MAX
- size
+ 1 < start
) {
160 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
161 "Reached max table id. Start/Size %u/%u",
163 XFREE(MTYPE_TM_CHUNK
, tmc
);
168 if (zvrf
->tbl_mgr
->end
- size
+ 1 < start
) {
169 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
170 "Reached max table id. Start/Size %u/%u",
172 XFREE(MTYPE_TM_CHUNK
, tmc
);
176 tmc
->end
= tmc
->start
+ size
- 1;
178 tmc
->instance
= instance
;
179 listnode_add(zvrf
->tbl_mgr
->lc_list
, tmc
);
185 * Core function, release no longer used table chunks
187 * @param proto Daemon protocol of client, to identify the owner
188 * @param instance Instance, to identify the owner
189 * @param start First table RT ID of the chunk
190 * @param end Last table RT ID of the chunk
191 * @return 0 on success, -1 otherwise
193 int release_table_chunk(uint8_t proto
, uint16_t instance
, uint32_t start
,
194 uint32_t end
, struct zebra_vrf
*zvrf
)
196 struct listnode
*node
;
197 struct table_manager_chunk
*tmc
;
199 struct table_manager
*tbl_mgr
;
204 tbl_mgr
= zvrf
->tbl_mgr
;
207 /* check that size matches */
208 zlog_debug("Releasing table chunk: %u - %u", start
, end
);
209 /* find chunk and disown */
210 for (ALL_LIST_ELEMENTS_RO(tbl_mgr
->lc_list
, node
, tmc
)) {
211 if (tmc
->start
!= start
)
215 if (tmc
->proto
!= proto
|| tmc
->instance
!= instance
) {
216 flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH
,
217 "%s: Daemon mismatch!!", __func__
);
220 tmc
->proto
= NO_PROTO
;
226 flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK
,
227 "%s: Table chunk not released!!", __func__
);
233 * Release table chunks from a client.
235 * Called on client disconnection or reconnection. It only releases chunks
236 * with empty keep value.
238 * @param client the client to release chunks from
239 * @return Number of chunks released
241 int release_daemon_table_chunks(struct zserv
*client
)
243 uint8_t proto
= client
->proto
;
244 uint16_t instance
= client
->instance
;
245 struct listnode
*node
;
246 struct table_manager_chunk
*tmc
;
250 struct zebra_vrf
*zvrf
;
252 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
257 if (!vrf_is_backend_netns() && vrf
->vrf_id
!= VRF_DEFAULT
)
259 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
260 if (tmc
->proto
== proto
&& tmc
->instance
== instance
) {
261 ret
= release_table_chunk(
262 tmc
->proto
, tmc
->instance
, tmc
->start
,
269 zlog_debug("%s: Released %d table chunks", __func__
, count
);
274 static void table_range_add(struct zebra_vrf
*zvrf
, uint32_t start
,
279 zvrf
->tbl_mgr
->start
= start
;
280 zvrf
->tbl_mgr
->end
= end
;
283 void table_manager_disable(struct zebra_vrf
*zvrf
)
287 if (!vrf_is_backend_netns() && zvrf_id(zvrf
) != VRF_DEFAULT
) {
288 zvrf
->tbl_mgr
= NULL
;
291 list_delete(&zvrf
->tbl_mgr
->lc_list
);
292 XFREE(MTYPE_TM_TABLE
, zvrf
->tbl_mgr
);
293 zvrf
->tbl_mgr
= NULL
;
296 int table_manager_range(struct vty
*vty
, bool add
, struct zebra_vrf
*zvrf
,
297 const char *start_table_str
, const char *end_table_str
)
303 if (!start_table_str
|| !end_table_str
) {
304 vty_out(vty
, "%% Labels not specified\n");
305 return CMD_WARNING_CONFIG_FAILED
;
307 start
= atoi(start_table_str
);
308 end
= atoi(end_table_str
);
310 vty_out(vty
, "%% End table is less than Start table\n");
311 return CMD_WARNING_CONFIG_FAILED
;
314 #if !defined(GNU_LINUX)
320 if ((start
>= RT_TABLE_ID_COMPAT
&& start
<= RT_TABLE_ID_LOCAL
)
321 || (end
>= RT_TABLE_ID_COMPAT
322 && end
<= RT_TABLE_ID_LOCAL
)) {
323 vty_out(vty
, "%% Values forbidden in range [%u;%u]\n",
324 RT_TABLE_ID_COMPAT
, RT_TABLE_ID_LOCAL
);
325 return CMD_WARNING_CONFIG_FAILED
;
327 if (start
< RT_TABLE_ID_COMPAT
&& end
> RT_TABLE_ID_LOCAL
) {
329 "%% Range overlaps range [%u;%u] forbidden\n",
330 RT_TABLE_ID_COMPAT
, RT_TABLE_ID_LOCAL
);
331 return CMD_WARNING_CONFIG_FAILED
;
335 && ((zvrf
->tbl_mgr
->start
&& zvrf
->tbl_mgr
->start
!= start
)
336 || (zvrf
->tbl_mgr
->end
&& zvrf
->tbl_mgr
->end
!= end
))) {
338 "%% New range will be taken into account at restart\n");
340 table_range_add(zvrf
, start
, end
);
342 table_range_add(zvrf
, 0, 0);