Line data Source code
1 : /* zebra table Manager for routing table identifier management
2 : * Copyright (C) 2018 6WIND
3 : *
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)
7 : * any later version.
8 : *
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
12 : * more details.
13 : *
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
17 : */
18 :
19 : #include "zebra.h"
20 :
21 : #include <stdio.h>
22 : #include <string.h>
23 : #include <sys/types.h>
24 :
25 : #include "lib/log.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"
32 : #include "lib/vrf.h"
33 :
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"
39 :
40 : /* routing table identifiers
41 : *
42 : */
43 : #if !defined(GNU_LINUX)
44 : /* BSD systems
45 : */
46 : #else
47 : /* Linux Systems
48 : */
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
57 :
58 12 : DEFINE_MGROUP(TABLE_MGR, "Table Manager");
59 12 : DEFINE_MTYPE_STATIC(TABLE_MGR, TM_CHUNK, "Table Manager Chunk");
60 12 : DEFINE_MTYPE_STATIC(TABLE_MGR, TM_TABLE, "Table Manager Context");
61 :
62 0 : static void delete_table_chunk(void *val)
63 : {
64 0 : XFREE(MTYPE_TM_CHUNK, val);
65 0 : }
66 :
67 : /**
68 : * Init table manager
69 : */
70 4 : void table_manager_enable(struct zebra_vrf *zvrf)
71 : {
72 :
73 4 : if (zvrf->tbl_mgr)
74 : return;
75 4 : if (!vrf_is_backend_netns()
76 8 : && strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) {
77 0 : struct zebra_vrf *def = zebra_vrf_lookup_by_id(VRF_DEFAULT);
78 :
79 0 : if (def)
80 0 : zvrf->tbl_mgr = def->tbl_mgr;
81 0 : return;
82 : }
83 4 : zvrf->tbl_mgr = XCALLOC(MTYPE_TM_TABLE, sizeof(struct table_manager));
84 4 : zvrf->tbl_mgr->lc_list = list_new();
85 4 : zvrf->tbl_mgr->lc_list->del = delete_table_chunk;
86 : }
87 :
88 : /**
89 : * Core function, assigns table chunks
90 : *
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
93 : *
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
98 : */
99 0 : struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
100 : uint32_t size,
101 : struct zebra_vrf *zvrf)
102 : {
103 0 : struct table_manager_chunk *tmc;
104 0 : struct listnode *node;
105 0 : uint32_t start;
106 0 : bool manual_conf = false;
107 :
108 0 : if (!zvrf)
109 : return NULL;
110 :
111 : /* first check if there's one available */
112 0 : for (ALL_LIST_ELEMENTS_RO(zvrf->tbl_mgr->lc_list, node, tmc)) {
113 0 : if (tmc->proto == NO_PROTO
114 0 : && tmc->end - tmc->start + 1 == size) {
115 0 : tmc->proto = proto;
116 0 : tmc->instance = instance;
117 0 : return tmc;
118 : }
119 : }
120 : /* otherwise create a new one */
121 0 : tmc = XCALLOC(MTYPE_TM_CHUNK, sizeof(struct table_manager_chunk));
122 :
123 0 : if (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end)
124 0 : manual_conf = true;
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.
130 : */
131 0 : if (list_isempty(zvrf->tbl_mgr->lc_list)) {
132 0 : if (!manual_conf)
133 : start = RT_TABLE_ID_UNRESERVED_MIN;
134 : else
135 0 : start = zvrf->tbl_mgr->start;
136 : } else
137 0 : start = ((struct table_manager_chunk *)listgetdata(
138 : listtail(zvrf->tbl_mgr->lc_list)))
139 0 : ->end
140 : + 1;
141 :
142 0 : if (!manual_conf) {
143 :
144 : #if !defined(GNU_LINUX)
145 : /* BSD systems
146 : */
147 : #else
148 : /* Linux Systems
149 : */
150 : /* if not enough room space between MIN and COMPAT,
151 : * then begin after LOCAL
152 : */
153 0 : if (start < RT_TABLE_ID_COMPAT
154 0 : && (size > RT_TABLE_ID_COMPAT - RT_TABLE_ID_UNRESERVED_MIN))
155 0 : start = RT_TABLE_ID_LOCAL + 1;
156 : #endif /* !def(GNU_LINUX) */
157 0 : tmc->start = start;
158 0 : if (RT_TABLE_ID_UNRESERVED_MAX - size + 1 < start) {
159 0 : flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
160 : "Reached max table id. Start/Size %u/%u",
161 : start, size);
162 0 : XFREE(MTYPE_TM_CHUNK, tmc);
163 0 : return NULL;
164 : }
165 : } else {
166 0 : tmc->start = start;
167 0 : if (zvrf->tbl_mgr->end - size + 1 < start) {
168 0 : flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
169 : "Reached max table id. Start/Size %u/%u",
170 : start, size);
171 0 : XFREE(MTYPE_TM_CHUNK, tmc);
172 0 : return NULL;
173 : }
174 : }
175 0 : tmc->end = tmc->start + size - 1;
176 0 : tmc->proto = proto;
177 0 : tmc->instance = instance;
178 0 : listnode_add(zvrf->tbl_mgr->lc_list, tmc);
179 :
180 0 : return tmc;
181 : }
182 :
183 : /**
184 : * Core function, release no longer used table chunks
185 : *
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
191 : */
192 0 : int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start,
193 : uint32_t end, struct zebra_vrf *zvrf)
194 : {
195 0 : struct listnode *node;
196 0 : struct table_manager_chunk *tmc;
197 0 : int ret = -1;
198 0 : struct table_manager *tbl_mgr;
199 :
200 0 : if (!zvrf)
201 : return -1;
202 :
203 0 : tbl_mgr = zvrf->tbl_mgr;
204 0 : if (!tbl_mgr)
205 : return ret;
206 : /* check that size matches */
207 0 : zlog_debug("Releasing table chunk: %u - %u", start, end);
208 : /* find chunk and disown */
209 0 : for (ALL_LIST_ELEMENTS_RO(tbl_mgr->lc_list, node, tmc)) {
210 0 : if (tmc->start != start)
211 0 : continue;
212 0 : if (tmc->end != end)
213 0 : continue;
214 0 : if (tmc->proto != proto || tmc->instance != instance) {
215 0 : flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH,
216 : "%s: Daemon mismatch!!", __func__);
217 0 : continue;
218 : }
219 0 : tmc->proto = NO_PROTO;
220 0 : tmc->instance = 0;
221 0 : ret = 0;
222 0 : break;
223 : }
224 0 : if (ret != 0)
225 0 : flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK,
226 : "%s: Table chunk not released!!", __func__);
227 :
228 : return ret;
229 : }
230 :
231 : /**
232 : * Release table chunks from a client.
233 : *
234 : * Called on client disconnection or reconnection. It only releases chunks
235 : * with empty keep value.
236 : *
237 : * @param client the client to release chunks from
238 : * @return Number of chunks released
239 : */
240 12 : int release_daemon_table_chunks(struct zserv *client)
241 : {
242 12 : uint8_t proto = client->proto;
243 12 : uint16_t instance = client->instance;
244 12 : struct listnode *node;
245 12 : struct table_manager_chunk *tmc;
246 12 : int count = 0;
247 12 : int ret;
248 12 : struct vrf *vrf;
249 12 : struct zebra_vrf *zvrf;
250 :
251 36 : RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
252 12 : zvrf = vrf->info;
253 :
254 12 : if (!zvrf)
255 0 : continue;
256 12 : if (!vrf_is_backend_netns() && vrf->vrf_id != VRF_DEFAULT)
257 0 : continue;
258 24 : for (ALL_LIST_ELEMENTS_RO(zvrf->tbl_mgr->lc_list, node, tmc)) {
259 0 : if (tmc->proto == proto && tmc->instance == instance) {
260 0 : ret = release_table_chunk(
261 : tmc->proto, tmc->instance, tmc->start,
262 : tmc->end, zvrf);
263 0 : if (ret == 0)
264 0 : count++;
265 : }
266 : }
267 : }
268 12 : zlog_debug("%s: Released %d table chunks", __func__, count);
269 :
270 12 : return count;
271 : }
272 :
273 0 : static void table_range_add(struct zebra_vrf *zvrf, uint32_t start,
274 : uint32_t end)
275 : {
276 0 : if (!zvrf->tbl_mgr)
277 : return;
278 0 : zvrf->tbl_mgr->start = start;
279 0 : zvrf->tbl_mgr->end = end;
280 : }
281 :
282 4 : void table_manager_disable(struct zebra_vrf *zvrf)
283 : {
284 4 : if (!zvrf->tbl_mgr)
285 : return;
286 4 : if (!vrf_is_backend_netns()
287 8 : && strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) {
288 0 : zvrf->tbl_mgr = NULL;
289 0 : return;
290 : }
291 4 : list_delete(&zvrf->tbl_mgr->lc_list);
292 4 : XFREE(MTYPE_TM_TABLE, zvrf->tbl_mgr);
293 4 : zvrf->tbl_mgr = NULL;
294 : }
295 :
296 0 : int table_manager_range(struct vty *vty, bool add, struct zebra_vrf *zvrf,
297 : const char *start_table_str, const char *end_table_str)
298 : {
299 0 : uint32_t start;
300 0 : uint32_t end;
301 :
302 0 : if (add) {
303 0 : if (!start_table_str || !end_table_str) {
304 0 : vty_out(vty, "%% Labels not specified\n");
305 0 : return CMD_WARNING_CONFIG_FAILED;
306 : }
307 0 : start = atoi(start_table_str);
308 0 : end = atoi(end_table_str);
309 0 : if (end < start) {
310 0 : vty_out(vty, "%% End table is less than Start table\n");
311 0 : return CMD_WARNING_CONFIG_FAILED;
312 : }
313 :
314 : #if !defined(GNU_LINUX)
315 : /* BSD systems
316 : */
317 : #else
318 : /* Linux Systems
319 : */
320 0 : if ((start >= RT_TABLE_ID_COMPAT && start <= RT_TABLE_ID_LOCAL)
321 0 : || (end >= RT_TABLE_ID_COMPAT
322 0 : && end <= RT_TABLE_ID_LOCAL)) {
323 0 : vty_out(vty, "%% Values forbidden in range [%u;%u]\n",
324 : RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL);
325 0 : return CMD_WARNING_CONFIG_FAILED;
326 : }
327 0 : if (start < RT_TABLE_ID_COMPAT && end > RT_TABLE_ID_LOCAL) {
328 0 : vty_out(vty,
329 : "%% Range overlaps range [%u;%u] forbidden\n",
330 : RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL);
331 0 : return CMD_WARNING_CONFIG_FAILED;
332 : }
333 : #endif
334 0 : if (zvrf->tbl_mgr
335 0 : && ((zvrf->tbl_mgr->start && zvrf->tbl_mgr->start != start)
336 0 : || (zvrf->tbl_mgr->end && zvrf->tbl_mgr->end != end))) {
337 0 : vty_out(vty,
338 : "%% New range will be taken into account at restart\n");
339 : }
340 0 : table_range_add(zvrf, start, end);
341 : } else
342 0 : table_range_add(zvrf, 0, 0);
343 : return CMD_SUCCESS;
344 : }
|