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
));
123 if (zvrf
->tbl_mgr
->start
|| zvrf
->tbl_mgr
->end
)
125 /* table RT IDs range are [1;252] and [256;0xffffffff]
126 * - check if the requested range can be within the first range,
127 * otherwise elect second one
128 * - TODO : vrf-lites have their own table identifier.
129 * In that case, table_id should be removed from the table range.
131 if (list_isempty(zvrf
->tbl_mgr
->lc_list
)) {
133 start
= RT_TABLE_ID_UNRESERVED_MIN
;
135 start
= zvrf
->tbl_mgr
->start
;
137 start
= ((struct table_manager_chunk
*)listgetdata(
138 listtail(zvrf
->tbl_mgr
->lc_list
)))
144 #if !defined(GNU_LINUX)
150 /* if not enough room space between MIN and COMPAT,
151 * then begin after LOCAL
153 if (start
< RT_TABLE_ID_COMPAT
154 && (size
> RT_TABLE_ID_COMPAT
- RT_TABLE_ID_UNRESERVED_MIN
))
155 start
= RT_TABLE_ID_LOCAL
+ 1;
156 #endif /* !def(GNU_LINUX) */
158 if (RT_TABLE_ID_UNRESERVED_MAX
- size
+ 1 < start
) {
159 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
160 "Reached max table id. Start/Size %u/%u",
162 XFREE(MTYPE_TM_CHUNK
, tmc
);
167 if (zvrf
->tbl_mgr
->end
- size
+ 1 < start
) {
168 flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS
,
169 "Reached max table id. Start/Size %u/%u",
171 XFREE(MTYPE_TM_CHUNK
, tmc
);
175 tmc
->end
= tmc
->start
+ size
- 1;
177 tmc
->instance
= instance
;
178 listnode_add(zvrf
->tbl_mgr
->lc_list
, tmc
);
184 * Core function, release no longer used table chunks
186 * @param proto Daemon protocol of client, to identify the owner
187 * @param instance Instance, to identify the owner
188 * @param start First table RT ID of the chunk
189 * @param end Last table RT ID of the chunk
190 * @return 0 on success, -1 otherwise
192 int release_table_chunk(uint8_t proto
, uint16_t instance
, uint32_t start
,
193 uint32_t end
, struct zebra_vrf
*zvrf
)
195 struct listnode
*node
;
196 struct table_manager_chunk
*tmc
;
198 struct table_manager
*tbl_mgr
;
203 tbl_mgr
= zvrf
->tbl_mgr
;
206 /* check that size matches */
207 zlog_debug("Releasing table chunk: %u - %u", start
, end
);
208 /* find chunk and disown */
209 for (ALL_LIST_ELEMENTS_RO(tbl_mgr
->lc_list
, node
, tmc
)) {
210 if (tmc
->start
!= start
)
214 if (tmc
->proto
!= proto
|| tmc
->instance
!= instance
) {
215 flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH
,
216 "%s: Daemon mismatch!!", __func__
);
219 tmc
->proto
= NO_PROTO
;
225 flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK
,
226 "%s: Table chunk not released!!", __func__
);
232 * Release table chunks from a client.
234 * Called on client disconnection or reconnection. It only releases chunks
235 * with empty keep value.
237 * @param client the client to release chunks from
238 * @return Number of chunks released
240 int release_daemon_table_chunks(struct zserv
*client
)
242 uint8_t proto
= client
->proto
;
243 uint16_t instance
= client
->instance
;
244 struct listnode
*node
;
245 struct table_manager_chunk
*tmc
;
249 struct zebra_vrf
*zvrf
;
251 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
256 if (!vrf_is_backend_netns() && vrf
->vrf_id
!= VRF_DEFAULT
)
258 for (ALL_LIST_ELEMENTS_RO(zvrf
->tbl_mgr
->lc_list
, node
, tmc
)) {
259 if (tmc
->proto
== proto
&& tmc
->instance
== instance
) {
260 ret
= release_table_chunk(
261 tmc
->proto
, tmc
->instance
, tmc
->start
,
268 zlog_debug("%s: Released %d table chunks", __func__
, count
);
273 static void table_range_add(struct zebra_vrf
*zvrf
, uint32_t start
,
278 zvrf
->tbl_mgr
->start
= start
;
279 zvrf
->tbl_mgr
->end
= end
;
282 void table_manager_disable(struct zebra_vrf
*zvrf
)
286 if (!vrf_is_backend_netns()
287 && strcmp(zvrf_name(zvrf
), VRF_DEFAULT_NAME
)) {
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);