]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/vici.c
*: manual SPDX License ID conversions
[mirror_frr.git] / nhrpd / vici.c
CommitLineData
2fb975da
TT
1/* strongSwan VICI protocol implementation for NHRP
2 * Copyright (c) 2014-2015 Timo Teräs
3 *
4 * This file is free software: you may copy, redistribute and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
b45ac5f5
DL
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
2fb975da
TT
14#include <string.h>
15#include <sys/socket.h>
16#include <sys/un.h>
17
18#include "thread.h"
19#include "zbuf.h"
20#include "log.h"
aed07011 21#include "lib_errors.h"
2fb975da 22
aed07011 23#include "nhrpd.h"
2fb975da 24#include "vici.h"
aed07011 25#include "nhrp_errors.h"
2fb975da
TT
26
27#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
28
29struct blob {
30 char *ptr;
31 int len;
32};
33
34static int blob_equal(const struct blob *b, const char *str)
35{
996c9314
LB
36 if (!b || b->len != (int)strlen(str))
37 return 0;
2fb975da
TT
38 return memcmp(b->ptr, str, b->len) == 0;
39}
40
41static int blob2buf(const struct blob *b, char *buf, size_t n)
42{
996c9314
LB
43 if (!b || b->len >= (int)n)
44 return 0;
2fb975da
TT
45 memcpy(buf, b->ptr, b->len);
46 buf[b->len] = 0;
47 return 1;
48}
49
50struct vici_conn {
51 struct thread *t_reconnect, *t_read, *t_write;
52 struct zbuf ibuf;
53 struct zbuf_queue obuf;
54 int fd;
55 uint8_t ibuf_data[VICI_MAX_MSGLEN];
56};
57
58struct vici_message_ctx {
59 const char *sections[8];
60 int nsections;
61};
62
cc9f21da 63static void vici_reconnect(struct thread *t);
2fb975da
TT
64static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
65
66static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
67{
68 size_t len = strlen(str);
69 zbuf_put8(obuf, len);
70 zbuf_put(obuf, str, len);
71}
72
73static void vici_connection_error(struct vici_conn *vici)
74{
75 nhrp_vc_reset();
76
77 THREAD_OFF(vici->t_read);
78 THREAD_OFF(vici->t_write);
79 zbuf_reset(&vici->ibuf);
80 zbufq_reset(&vici->obuf);
81
82 close(vici->fd);
83 vici->fd = -1;
ffa2c898 84 thread_add_timer(master, vici_reconnect, vici, 2, &vici->t_reconnect);
2fb975da
TT
85}
86
996c9314
LB
87static void vici_parse_message(struct vici_conn *vici, struct zbuf *msg,
88 void (*parser)(struct vici_message_ctx *ctx,
89 enum vici_type_t msgtype,
90 const struct blob *key,
91 const struct blob *val),
92 struct vici_message_ctx *ctx)
2fb975da
TT
93{
94 uint8_t *type;
996c9314
LB
95 struct blob key = {0};
96 struct blob val = {0};
2fb975da
TT
97
98 while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
99 switch (*type) {
100 case VICI_SECTION_START:
101 key.len = zbuf_get8(msg);
102 key.ptr = zbuf_pulln(msg, key.len);
996c9314
LB
103 debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'",
104 key.len, key.ptr);
2fb975da
TT
105 parser(ctx, *type, &key, NULL);
106 ctx->nsections++;
107 break;
108 case VICI_SECTION_END:
109 debugf(NHRP_DEBUG_VICI, "VICI: Section end");
110 parser(ctx, *type, NULL, NULL);
111 ctx->nsections--;
112 break;
113 case VICI_KEY_VALUE:
114 key.len = zbuf_get8(msg);
115 key.ptr = zbuf_pulln(msg, key.len);
116 val.len = zbuf_get_be16(msg);
117 val.ptr = zbuf_pulln(msg, val.len);
996c9314
LB
118 debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'",
119 key.len, key.ptr, val.len, val.ptr);
2fb975da
TT
120 parser(ctx, *type, &key, &val);
121 break;
122 case VICI_LIST_START:
123 key.len = zbuf_get8(msg);
124 key.ptr = zbuf_pulln(msg, key.len);
996c9314
LB
125 debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'",
126 key.len, key.ptr);
2fb975da
TT
127 break;
128 case VICI_LIST_ITEM:
129 val.len = zbuf_get_be16(msg);
130 val.ptr = zbuf_pulln(msg, val.len);
996c9314
LB
131 debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'",
132 val.len, val.ptr);
2fb975da
TT
133 parser(ctx, *type, &key, &val);
134 break;
135 case VICI_LIST_END:
136 debugf(NHRP_DEBUG_VICI, "VICI: List end");
137 break;
2fb975da
TT
138 }
139 }
140}
141
142struct handle_sa_ctx {
143 struct vici_message_ctx msgctx;
144 int event;
145 int child_ok;
146 int kill_ikesa;
147 uint32_t child_uniqueid, ike_uniqueid;
148 struct {
149 union sockunion host;
150 struct blob id, cert;
151 } local, remote;
152};
153
996c9314
LB
154static void parse_sa_message(struct vici_message_ctx *ctx,
155 enum vici_type_t msgtype, const struct blob *key,
156 const struct blob *val)
2fb975da 157{
996c9314
LB
158 struct handle_sa_ctx *sactx =
159 container_of(ctx, struct handle_sa_ctx, msgctx);
2fb975da
TT
160 struct nhrp_vc *vc;
161 char buf[512];
162
163 switch (msgtype) {
164 case VICI_SECTION_START:
165 if (ctx->nsections == 3) {
166 /* Begin of child-sa section, reset child vars */
167 sactx->child_uniqueid = 0;
168 sactx->child_ok = 0;
169 }
170 break;
171 case VICI_SECTION_END:
172 if (ctx->nsections == 3) {
173 /* End of child-sa section, update nhrp_vc */
174 int up = sactx->child_ok || sactx->event == 1;
175 if (up) {
996c9314
LB
176 vc = nhrp_vc_get(&sactx->local.host,
177 &sactx->remote.host, up);
2fb975da 178 if (vc) {
996c9314
LB
179 blob2buf(&sactx->local.id, vc->local.id,
180 sizeof(vc->local.id));
181 if (blob2buf(&sactx->local.cert,
182 (char *)vc->local.cert,
183 sizeof(vc->local.cert)))
184 vc->local.certlen =
185 sactx->local.cert.len;
186 blob2buf(&sactx->remote.id,
187 vc->remote.id,
188 sizeof(vc->remote.id));
189 if (blob2buf(&sactx->remote.cert,
190 (char *)vc->remote.cert,
191 sizeof(vc->remote.cert)))
192 vc->remote.certlen =
193 sactx->remote.cert.len;
194 sactx->kill_ikesa |=
195 nhrp_vc_ipsec_updown(
196 sactx->child_uniqueid,
197 vc);
4cbaf956 198 vc->ike_uniqueid = sactx->ike_uniqueid;
2fb975da
TT
199 }
200 } else {
201 nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
202 }
203 }
204 break;
d0038397
DS
205 case VICI_START:
206 case VICI_KEY_VALUE:
207 case VICI_LIST_START:
208 case VICI_LIST_ITEM:
209 case VICI_LIST_END:
210 case VICI_END:
fa3bf3a2 211 if (!key || !key->ptr)
6b07f6e1
JB
212 break;
213
2fb975da
TT
214 switch (key->ptr[0]) {
215 case 'l':
996c9314
LB
216 if (blob_equal(key, "local-host")
217 && ctx->nsections == 1) {
2fb975da 218 if (blob2buf(val, buf, sizeof(buf)))
996c9314
LB
219 if (str2sockunion(buf,
220 &sactx->local.host)
221 < 0)
1c50c1c0
QY
222 flog_err(
223 EC_NHRP_SWAN,
224 "VICI: bad strongSwan local-host: %s",
225 buf);
996c9314
LB
226 } else if (blob_equal(key, "local-id")
227 && ctx->nsections == 1) {
2fb975da 228 sactx->local.id = *val;
996c9314
LB
229 } else if (blob_equal(key, "local-cert-data")
230 && ctx->nsections == 1) {
2fb975da
TT
231 sactx->local.cert = *val;
232 }
233 break;
234 case 'r':
996c9314
LB
235 if (blob_equal(key, "remote-host")
236 && ctx->nsections == 1) {
2fb975da 237 if (blob2buf(val, buf, sizeof(buf)))
996c9314
LB
238 if (str2sockunion(buf,
239 &sactx->remote.host)
240 < 0)
1c50c1c0
QY
241 flog_err(
242 EC_NHRP_SWAN,
243 "VICI: bad strongSwan remote-host: %s",
244 buf);
996c9314
LB
245 } else if (blob_equal(key, "remote-id")
246 && ctx->nsections == 1) {
2fb975da 247 sactx->remote.id = *val;
996c9314
LB
248 } else if (blob_equal(key, "remote-cert-data")
249 && ctx->nsections == 1) {
2fb975da
TT
250 sactx->remote.cert = *val;
251 }
252 break;
253 case 'u':
996c9314
LB
254 if (blob_equal(key, "uniqueid")
255 && blob2buf(val, buf, sizeof(buf))) {
2fb975da 256 if (ctx->nsections == 3)
996c9314
LB
257 sactx->child_uniqueid =
258 strtoul(buf, NULL, 0);
2fb975da 259 else if (ctx->nsections == 1)
996c9314
LB
260 sactx->ike_uniqueid =
261 strtoul(buf, NULL, 0);
2fb975da
TT
262 }
263 break;
264 case 's':
265 if (blob_equal(key, "state") && ctx->nsections == 3) {
266 sactx->child_ok =
996c9314
LB
267 (sactx->event == 0
268 && (blob_equal(val, "INSTALLED")
269 || blob_equal(val, "REKEYED")));
2fb975da
TT
270 }
271 break;
272 }
273 break;
274 }
275}
276
996c9314
LB
277static void parse_cmd_response(struct vici_message_ctx *ctx,
278 enum vici_type_t msgtype, const struct blob *key,
279 const struct blob *val)
d139786a
TT
280{
281 char buf[512];
282
283 switch (msgtype) {
284 case VICI_KEY_VALUE:
996c9314
LB
285 if (blob_equal(key, "errmsg")
286 && blob2buf(val, buf, sizeof(buf)))
2b84a521 287 flog_err(EC_NHRP_SWAN, "VICI: strongSwan: %s", buf);
d139786a 288 break;
d0038397
DS
289 case VICI_START:
290 case VICI_SECTION_START:
291 case VICI_SECTION_END:
292 case VICI_LIST_START:
293 case VICI_LIST_ITEM:
294 case VICI_LIST_END:
295 case VICI_END:
d139786a
TT
296 break;
297 }
298}
299
2fb975da
TT
300static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
301{
302 char buf[32];
303 struct handle_sa_ctx ctx = {
304 .event = event,
0a939f4f 305 .msgctx.nsections = 0
2fb975da
TT
306 };
307
308 vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
309
310 if (ctx.kill_ikesa && ctx.ike_uniqueid) {
996c9314
LB
311 debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u",
312 ctx.ike_uniqueid);
0d6f7fd6 313 snprintf(buf, sizeof(buf), "%u", ctx.ike_uniqueid);
996c9314
LB
314 vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id",
315 strlen(buf), buf, VICI_END);
2fb975da
TT
316 }
317}
318
319static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
320{
321 uint32_t msglen;
322 uint8_t msgtype;
323 struct blob name;
7ea5df54 324 struct vici_message_ctx ctx = { .nsections = 0 };
2fb975da
TT
325
326 msglen = zbuf_get_be32(msg);
327 msgtype = zbuf_get8(msg);
328 debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
329
330 switch (msgtype) {
331 case VICI_EVENT:
332 name.len = zbuf_get8(msg);
333 name.ptr = zbuf_pulln(msg, name.len);
334
996c9314
LB
335 debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len,
336 name.ptr);
337 if (blob_equal(&name, "list-sa")
338 || blob_equal(&name, "child-updown")
339 || blob_equal(&name, "child-rekey"))
2fb975da 340 vici_recv_sa(vici, msg, 0);
996c9314
LB
341 else if (blob_equal(&name, "child-state-installed")
342 || blob_equal(&name, "child-state-rekeyed"))
2fb975da
TT
343 vici_recv_sa(vici, msg, 1);
344 else if (blob_equal(&name, "child-state-destroying"))
345 vici_recv_sa(vici, msg, 2);
346 break;
d139786a 347 case VICI_CMD_RESPONSE:
6c8ca260 348 vici_parse_message(vici, msg, parse_cmd_response, &ctx);
d139786a 349 break;
2fb975da 350 case VICI_EVENT_UNKNOWN:
d139786a 351 case VICI_CMD_UNKNOWN:
1c50c1c0
QY
352 flog_err(
353 EC_NHRP_SWAN,
996c9314 354 "VICI: StrongSwan does not support mandatory events (unpatched?)");
2fb975da
TT
355 break;
356 case VICI_EVENT_CONFIRM:
2fb975da
TT
357 break;
358 default:
359 zlog_notice("VICI: Unrecognized message type %d", msgtype);
360 break;
361 }
362}
363
cc9f21da 364static void vici_read(struct thread *t)
2fb975da
TT
365{
366 struct vici_conn *vici = THREAD_ARG(t);
367 struct zbuf *ibuf = &vici->ibuf;
368 struct zbuf pktbuf;
369
996c9314 370 if (zbuf_read(ibuf, vici->fd, (size_t)-1) < 0) {
2fb975da 371 vici_connection_error(vici);
cc9f21da 372 return;
2fb975da
TT
373 }
374
375 /* Process all messages in buffer */
376 do {
377 uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
378 if (!hdrlen)
379 break;
380 if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
381 zbuf_reset_head(ibuf, hdrlen);
382 break;
383 }
384
385 /* Handle packet */
996c9314
LB
386 zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen) + 4,
387 htonl(*hdrlen) + 4);
2fb975da
TT
388 vici_recv_message(vici, &pktbuf);
389 } while (1);
390
ffa2c898 391 thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read);
2fb975da
TT
392}
393
cc9f21da 394static void vici_write(struct thread *t)
2fb975da
TT
395{
396 struct vici_conn *vici = THREAD_ARG(t);
397 int r;
398
2fb975da
TT
399 r = zbufq_write(&vici->obuf, vici->fd);
400 if (r > 0) {
ffa2c898
QY
401 thread_add_write(master, vici_write, vici, vici->fd,
402 &vici->t_write);
2fb975da
TT
403 } else if (r < 0) {
404 vici_connection_error(vici);
405 }
2fb975da
TT
406}
407
408static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
409{
410 if (vici->fd < 0) {
411 zbuf_free(obuf);
412 return;
413 }
414
415 zbufq_queue(&vici->obuf, obuf);
ffa2c898 416 thread_add_write(master, vici_write, vici, vici->fd, &vici->t_write);
2fb975da
TT
417}
418
419static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
420{
421 struct zbuf *obuf;
422 uint32_t *hdrlen;
423 va_list va;
424 size_t len;
425 int type;
426
427 obuf = zbuf_alloc(256);
996c9314
LB
428 if (!obuf)
429 return;
2fb975da
TT
430
431 hdrlen = zbuf_push(obuf, uint32_t);
432 zbuf_put8(obuf, VICI_CMD_REQUEST);
433 vici_zbuf_puts(obuf, name);
434
435 va_start(va, name);
436 for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
437 zbuf_put8(obuf, type);
438 switch (type) {
439 case VICI_KEY_VALUE:
440 vici_zbuf_puts(obuf, va_arg(va, const char *));
441 len = va_arg(va, size_t);
442 zbuf_put_be16(obuf, len);
443 zbuf_put(obuf, va_arg(va, void *), len);
444 break;
2fb975da
TT
445 default:
446 break;
447 }
448 }
449 va_end(va);
450 *hdrlen = htonl(zbuf_used(obuf) - 4);
451 vici_submit(vici, obuf);
452}
453
454static void vici_register_event(struct vici_conn *vici, const char *name)
455{
456 struct zbuf *obuf;
457 uint32_t *hdrlen;
458 uint8_t namelen;
459
460 namelen = strlen(name);
461 obuf = zbuf_alloc(4 + 1 + 1 + namelen);
996c9314
LB
462 if (!obuf)
463 return;
2fb975da
TT
464
465 hdrlen = zbuf_push(obuf, uint32_t);
466 zbuf_put8(obuf, VICI_EVENT_REGISTER);
467 zbuf_put8(obuf, namelen);
468 zbuf_put(obuf, name, namelen);
469 *hdrlen = htonl(zbuf_used(obuf) - 4);
470
471 vici_submit(vici, obuf);
472}
473
40307370
PG
474static bool vici_charon_filepath_done;
475static bool vici_charon_not_found;
476
477static char *vici_get_charon_filepath(void)
478{
479 static char buff[1200];
480 FILE *fp;
481 char *ptr;
482 char line[1024];
483
484 if (vici_charon_filepath_done)
485 return (char *)buff;
486 fp = popen("ipsec --piddir", "r");
487 if (!fp) {
488 if (!vici_charon_not_found) {
489 flog_err(EC_NHRP_SWAN,
490 "VICI: Failed to retrieve charon file path");
491 vici_charon_not_found = true;
492 }
493 return NULL;
494 }
495 /* last line of output is used to get vici path */
496 while (fgets(line, sizeof(line), fp) != NULL) {
497 ptr = strchr(line, '\n');
498 if (ptr)
499 *ptr = '\0';
500 snprintf(buff, sizeof(buff), "%s/charon.vici", line);
501 }
502 pclose(fp);
503 vici_charon_filepath_done = true;
504 return buff;
505}
506
cc9f21da 507static void vici_reconnect(struct thread *t)
2fb975da
TT
508{
509 struct vici_conn *vici = THREAD_ARG(t);
510 int fd;
40307370 511 char *file_path;
2fb975da 512
996c9314 513 if (vici->fd >= 0)
cc9f21da 514 return;
2fb975da 515
354196c0 516 fd = sock_open_unix(VICI_SOCKET);
40307370
PG
517 if (fd < 0) {
518 file_path = vici_get_charon_filepath();
519 if (file_path)
520 fd = sock_open_unix(file_path);
521 }
2fb975da 522 if (fd < 0) {
996c9314 523 debugf(NHRP_DEBUG_VICI,
15569c58
DA
524 "%s: failure connecting VICI socket: %s", __func__,
525 strerror(errno));
ffa2c898
QY
526 thread_add_timer(master, vici_reconnect, vici, 2,
527 &vici->t_reconnect);
cc9f21da 528 return;
2fb975da
TT
529 }
530
531 debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
532 vici->fd = fd;
ffa2c898 533 thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read);
2fb975da
TT
534
535 /* Send event subscribtions */
996c9314
LB
536 // vici_register_event(vici, "child-updown");
537 // vici_register_event(vici, "child-rekey");
2fb975da
TT
538 vici_register_event(vici, "child-state-installed");
539 vici_register_event(vici, "child-state-rekeyed");
540 vici_register_event(vici, "child-state-destroying");
541 vici_register_event(vici, "list-sa");
542 vici_submit_request(vici, "list-sas", VICI_END);
2fb975da
TT
543}
544
545static struct vici_conn vici_connection;
546
547void vici_init(void)
548{
549 struct vici_conn *vici = &vici_connection;
550
551 vici->fd = -1;
552 zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
553 zbufq_init(&vici->obuf);
ffa2c898
QY
554 thread_add_timer_msec(master, vici_reconnect, vici, 10,
555 &vici->t_reconnect);
2fb975da
TT
556}
557
558void vici_terminate(void)
559{
560}
561
083bbfae
GG
562void vici_terminate_vc_by_profile_name(char *profile_name)
563{
564 struct vici_conn *vici = &vici_connection;
74e5ba3a 565
58ef1668 566 debugf(NHRP_DEBUG_VICI, "Terminate profile = %s", profile_name);
083bbfae
GG
567 vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike",
568 strlen(profile_name), profile_name, VICI_END);
569}
570
571void vici_terminate_vc_by_ike_id(unsigned int ike_id)
4cbaf956
GG
572{
573 struct vici_conn *vici = &vici_connection;
74e5ba3a
RD
574 char ike_id_str[10];
575
4cbaf956 576 snprintf(ike_id_str, sizeof(ike_id_str), "%d", ike_id);
58ef1668 577 debugf(NHRP_DEBUG_VICI, "Terminate ike_id_str = %s", ike_id_str);
4cbaf956
GG
578 vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id",
579 strlen(ike_id_str), ike_id_str, VICI_END);
580}
581
996c9314
LB
582void vici_request_vc(const char *profile, union sockunion *src,
583 union sockunion *dst, int prio)
2fb975da
TT
584{
585 struct vici_conn *vici = &vici_connection;
586 char buf[2][SU_ADDRSTRLEN];
587
0d6f7fd6
DA
588 sockunion2str(src, buf[0], sizeof(buf[0]));
589 sockunion2str(dst, buf[1], sizeof(buf[1]));
2fb975da 590
996c9314
LB
591 vici_submit_request(vici, "initiate", VICI_KEY_VALUE, "child",
592 strlen(profile), profile, VICI_KEY_VALUE, "timeout",
593 (size_t)2, "-1", VICI_KEY_VALUE, "async", (size_t)1,
594 "1", VICI_KEY_VALUE, "init-limits", (size_t)1,
595 prio ? "0" : "1", VICI_KEY_VALUE, "my-host",
596 strlen(buf[0]), buf[0], VICI_KEY_VALUE,
597 "other-host", strlen(buf[1]), buf[1], VICI_END);
2fb975da
TT
598}
599
600int sock_open_unix(const char *path)
601{
602 int ret, fd;
603 struct sockaddr_un addr;
604
605 fd = socket(AF_UNIX, SOCK_STREAM, 0);
606 if (fd < 0)
607 return -1;
608
6006b807 609 memset(&addr, 0, sizeof(addr));
2fb975da 610 addr.sun_family = AF_UNIX;
fa3bf3a2 611 strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
2fb975da 612
996c9314
LB
613 ret = connect(fd, (struct sockaddr *)&addr,
614 sizeof(addr.sun_family) + strlen(addr.sun_path));
2fb975da
TT
615 if (ret < 0) {
616 close(fd);
617 return -1;
618 }
619
6c8ca260
JB
620 ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
621 if (ret < 0) {
622 close(fd);
623 return -1;
624 }
2fb975da
TT
625
626 return fd;
627}