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