]> git.proxmox.com Git - mirror_frr.git/blob - zebra/label_manager.c
Merge pull request #503 from pguibert6WIND/issue_473
[mirror_frr.git] / zebra / label_manager.c
1 /*
2 * Label Manager for FRR
3 *
4 * Copyright (C) 2017 by Bingen Eguzkitza,
5 * Volta Networks Inc.
6 *
7 * This file is part of FreeRangeRouting (FRR)
8 *
9 * FRR is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2, or (at your option) any
12 * later version.
13 *
14 * FRR is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with FRR; see the file COPYING. If not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 * 02111-1307, USA.
23 */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28
29 #include "zebra.h"
30 #include "zserv.h"
31 #include "lib/log.h"
32 #include "lib/memory.h"
33 #include "lib/mpls.h"
34 #include "lib/network.h"
35 #include "lib/stream.h"
36 #include "lib/zclient.h"
37
38 #include "label_manager.h"
39
40 #define CONNECTION_DELAY 5
41
42 struct label_manager lbl_mgr;
43
44 DEFINE_MGROUP(LBL_MGR, "Label Manager");
45 DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk");
46
47 /* In case this zebra daemon is not acting as label manager,
48 * it will be a proxy to relay messages to external label manager
49 * This zclient thus is to connect to it
50 */
51 static struct zclient *zclient;
52 bool lm_is_external;
53
54 static void delete_label_chunk(void *val)
55 {
56 XFREE(MTYPE_LM_CHUNK, val);
57 }
58
59 /**
60 * Receive a request to get or release a label chunk and forward it to external
61 * label manager.
62 *
63 * It's called from zserv in case it's not an actual label manager, but just a
64 * proxy.
65 *
66 * @param cmd Type of request (connect, get or release)
67 * @param src Input buffer from zserv
68 * @return 0 on success, -1 otherwise
69 */
70 int zread_relay_label_manager_request(int cmd, struct zserv *zserv)
71 {
72 struct stream *src, *dst;
73 int ret;
74
75 if (zclient->sock < 0) {
76 zlog_err("%s: Error relaying label chunk request: no zclient socket",
77 __func__);
78 return -1;
79 }
80 /* Send request to external label manager */
81 src = zserv->ibuf;
82 dst = zclient->obuf;
83
84 stream_copy(dst, src);
85
86 ret = writen(zclient->sock, dst->data, stream_get_endp(dst));
87 if (ret <= 0) {
88 zlog_err("%s: Error relaying label chunk request: %s", __func__,
89 strerror(errno));
90 return -1;
91 }
92 zlog_debug("%s: Label chunk request relayed. %d bytes sent", __func__,
93 ret);
94
95 /* Release label chunk has no response */
96 if (cmd == ZEBRA_RELEASE_LABEL_CHUNK)
97 return 0;
98
99 /* read response */
100 src = zclient->ibuf;
101 dst = zserv->obuf;
102
103 stream_reset(src);
104
105 u_int16_t size;
106 u_char marker;
107 u_char version;
108 vrf_id_t vrf_id;
109 u_int16_t resp_cmd;
110 ret = zclient_read_header(src, zclient->sock, &size, &marker, &version,
111 &vrf_id, &resp_cmd);
112 if (ret < 0) {
113 zlog_err("%s: Error reading label chunk response: %s", __func__,
114 strerror(errno));
115 return -1;
116 }
117 zlog_debug("%s: Label chunk response received, %d bytes", __func__,
118 size);
119
120 /* send response back */
121 stream_copy(dst, src);
122 stream_copy(zserv->obuf, zclient->ibuf);
123 ret = writen(zserv->sock, dst->data, stream_get_endp(dst));
124 if (ret <= 0) {
125 zlog_err("%s: Error sending label chunk response back: %s",
126 __func__, strerror(errno));
127 return -1;
128 }
129 zlog_debug("%s: Label chunk response (%d bytes) sent back", __func__,
130 ret);
131
132 return 0;
133 }
134
135 static int zclient_connect(struct thread *t)
136 {
137 zclient->t_connect = NULL;
138
139 if (zclient->sock >= 0)
140 return 0;
141
142 if (zclient_socket_connect(zclient) < 0) {
143 zlog_err("Error connecting synchronous zclient!");
144 thread_add_timer(zebrad.master, zclient_connect, zclient,
145 CONNECTION_DELAY, &zclient->t_connect);
146 return -1;
147 }
148
149 return 0;
150 }
151
152 /**
153 * Function to initialize zclient in case this is not an actual
154 * label manager, but just a proxy to an external one.
155 *
156 * @param lm_zserv_path Path to zserv socket of external label manager
157 */
158 static void lm_zclient_init(char *lm_zserv_path)
159 {
160 if (lm_zserv_path)
161 zclient_serv_path_set(lm_zserv_path);
162
163 /* Set default values. */
164 zclient = zclient_new(zebrad.master);
165 zclient->sock = -1;
166 zclient->t_connect = NULL;
167 zclient_connect (NULL);
168 }
169
170 /**
171 * Init label manager (or proxy to an external one)
172 */
173 void label_manager_init(char *lm_zserv_path)
174 {
175 /* this is an actual label manager */
176 if (!lm_zserv_path) {
177 zlog_debug("Initializing own label manager");
178 lm_is_external = false;
179 lbl_mgr.lc_list = list_new();
180 lbl_mgr.lc_list->del = delete_label_chunk;
181 } else { /* it's acting just as a proxy */
182 zlog_debug("Initializing external label manager at %s",
183 lm_zserv_path);
184 lm_is_external = true;
185 lm_zclient_init(lm_zserv_path);
186 }
187 }
188
189 /**
190 * Core function, assigns label cunks
191 *
192 * It first searches through the list to check if there's one available
193 * (previously released). Otherwise it creates and assigns a new one
194 *
195 * @param proto Daemon protocol of client, to identify the owner
196 * @param instance Instance, to identify the owner
197 * @param keep If set, avoid garbage collection
198 * @para size Size of the label chunk
199 * @return Pointer to the assigned label chunk
200 */
201 struct label_manager_chunk *assign_label_chunk(u_char proto, u_short instance,
202 u_char keep, uint32_t size)
203 {
204 struct label_manager_chunk *lmc;
205 struct listnode *node;
206
207 /* first check if there's one available */
208 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
209 if (lmc->proto == NO_PROTO && lmc->end - lmc->start + 1 == size) {
210 lmc->proto = proto;
211 lmc->instance = instance;
212 lmc->keep = keep;
213 return lmc;
214 }
215 }
216 /* otherwise create a new one */
217 lmc = XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk));
218 if (!lmc)
219 return NULL;
220
221 if (list_isempty(lbl_mgr.lc_list))
222 lmc->start = MPLS_MIN_UNRESERVED_LABEL;
223 else
224 lmc->start = ((struct label_manager_chunk *)
225 listgetdata(listtail(lbl_mgr.lc_list)))->end + 1;
226 if (lmc->start > MPLS_MAX_UNRESERVED_LABEL - size + 1) {
227 zlog_err("Reached max labels. Start: %u, size: %u", lmc->start,
228 size);
229 XFREE(MTYPE_LM_CHUNK, lmc);
230 return NULL;
231 }
232 lmc->end = lmc->start + size - 1;
233 lmc->proto = proto;
234 lmc->instance = instance;
235 lmc->keep = keep;
236 listnode_add(lbl_mgr.lc_list, lmc);
237
238 return lmc;
239 }
240
241 /**
242 * Core function, release no longer used label cunks
243 *
244 * @param proto Daemon protocol of client, to identify the owner
245 * @param instance Instance, to identify the owner
246 * @param start First label of the chunk
247 * @param end Last label of the chunk
248 * @return 0 on success, -1 otherwise
249 */
250 int
251 release_label_chunk(u_char proto, u_short instance, uint32_t start,
252 uint32_t end)
253 {
254 struct listnode *node;
255 struct label_manager_chunk *lmc;
256 int ret = -1;
257
258 /* check that size matches */
259 zlog_debug("Releasing label chunk: %u - %u", start, end);
260 /* find chunk and disown */
261 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
262 if (lmc->start != start)
263 continue;
264 if (lmc->end != end)
265 continue;
266 if (lmc->proto != proto || lmc->instance != instance) {
267 zlog_err("%s: Daemon mismatch!!", __func__);
268 continue;
269 }
270 lmc->proto = NO_PROTO;
271 lmc->instance = 0;
272 lmc->keep = 0;
273 ret = 0;
274 break;
275 }
276 if (ret != 0)
277 zlog_err("%s: Label chunk not released!!", __func__);
278
279 return ret;
280 }
281
282 /**
283 * Release label chunks from a client.
284 *
285 * Called on client disconnection or reconnection. It only releases chunks
286 * with empty keep value.
287 *
288 * @param proto Daemon protocol of client, to identify the owner
289 * @param instance Instance, to identify the owner
290 * @return Number of chunks released
291 */
292 int release_daemon_chunks(u_char proto, u_short instance)
293 {
294 struct listnode *node;
295 struct label_manager_chunk *lmc;
296 int count = 0;
297 int ret;
298
299 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
300 if (lmc->proto == proto && lmc->instance == instance
301 && lmc->keep == 0) {
302 ret =
303 release_label_chunk(lmc->proto, lmc->instance,
304 lmc->start, lmc->end);
305 if (ret == 0)
306 count++;
307 }
308 }
309
310 zlog_debug("%s: Released %d label chunks", __func__, count);
311
312 return count;
313 }
314
315 void label_manager_close()
316 {
317 list_delete(lbl_mgr.lc_list);
318 }