GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/bgpd/control.c Lines: 0 253 0.0 %
Date: 2017-11-07 Branches: 0 186 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: control.c,v 1.90 2017/08/11 16:02:53 claudio Exp $ */
2
3
/*
4
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/stat.h>
21
#include <sys/socket.h>
22
#include <sys/un.h>
23
#include <errno.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <unistd.h>
27
28
#include "bgpd.h"
29
#include "session.h"
30
#include "log.h"
31
32
#define	CONTROL_BACKLOG	5
33
34
struct ctl_conn	*control_connbyfd(int);
35
struct ctl_conn	*control_connbypid(pid_t);
36
int		 control_close(int);
37
void		 control_result(struct ctl_conn *, u_int);
38
ssize_t		 imsg_read_nofd(struct imsgbuf *);
39
40
int
41
control_init(int restricted, char *path)
42
{
43
	struct sockaddr_un	 sun;
44
	int			 fd;
45
	mode_t			 old_umask, mode;
46
47
	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
48
	     0)) == -1) {
49
		log_warn("control_init: socket");
50
		return (-1);
51
	}
52
53
	bzero(&sun, sizeof(sun));
54
	sun.sun_family = AF_UNIX;
55
	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
56
	    sizeof(sun.sun_path)) {
57
		log_warn("control_init: socket name too long");
58
		close(fd);
59
		return (-1);
60
	}
61
62
	if (unlink(path) == -1)
63
		if (errno != ENOENT) {
64
			log_warn("control_init: unlink %s", path);
65
			close(fd);
66
			return (-1);
67
		}
68
69
	if (restricted) {
70
		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
71
		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
72
	} else {
73
		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
74
		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
75
	}
76
77
	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
78
		log_warn("control_init: bind: %s", path);
79
		close(fd);
80
		umask(old_umask);
81
		return (-1);
82
	}
83
84
	umask(old_umask);
85
86
	if (chmod(path, mode) == -1) {
87
		log_warn("control_init: chmod: %s", path);
88
		close(fd);
89
		unlink(path);
90
		return (-1);
91
	}
92
93
	return (fd);
94
}
95
96
int
97
control_listen(int fd)
98
{
99
	if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
100
		log_warn("control_listen: listen");
101
		return (-1);
102
	}
103
104
	return (0);
105
}
106
107
void
108
control_shutdown(int fd)
109
{
110
	close(fd);
111
}
112
113
void
114
control_cleanup(const char *path)
115
{
116
	if (path)
117
		unlink(path);
118
}
119
120
unsigned int
121
control_accept(int listenfd, int restricted)
122
{
123
	int			 connfd;
124
	socklen_t		 len;
125
	struct sockaddr_un	 sun;
126
	struct ctl_conn		*ctl_conn;
127
128
	len = sizeof(sun);
129
	if ((connfd = accept4(listenfd,
130
	    (struct sockaddr *)&sun, &len,
131
	    SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) {
132
		if (errno == ENFILE || errno == EMFILE) {
133
			pauseaccept = getmonotime();
134
			return (0);
135
		} else if (errno != EWOULDBLOCK && errno != EINTR &&
136
		    errno != ECONNABORTED)
137
			log_warn("control_accept: accept");
138
		return (0);
139
	}
140
141
	if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
142
		log_warn("control_accept");
143
		close(connfd);
144
		return (0);
145
	}
146
147
	imsg_init(&ctl_conn->ibuf, connfd);
148
	ctl_conn->restricted = restricted;
149
150
	TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
151
152
	return (1);
153
}
154
155
struct ctl_conn *
156
control_connbyfd(int fd)
157
{
158
	struct ctl_conn	*c;
159
160
	TAILQ_FOREACH(c, &ctl_conns, entry) {
161
		if (c->ibuf.fd == fd)
162
			break;
163
	}
164
165
	return (c);
166
}
167
168
struct ctl_conn *
169
control_connbypid(pid_t pid)
170
{
171
	struct ctl_conn	*c;
172
173
	TAILQ_FOREACH(c, &ctl_conns, entry) {
174
		if (c->ibuf.pid == pid)
175
			break;
176
	}
177
178
	return (c);
179
}
180
181
int
182
control_close(int fd)
183
{
184
	struct ctl_conn	*c;
185
186
	if ((c = control_connbyfd(fd)) == NULL) {
187
		log_warn("control_close: fd %d: not found", fd);
188
		return (0);
189
	}
190
191
	msgbuf_clear(&c->ibuf.w);
192
	TAILQ_REMOVE(&ctl_conns, c, entry);
193
194
	close(c->ibuf.fd);
195
	free(c);
196
	pauseaccept = 0;
197
	return (1);
198
}
199
200
int
201
control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
202
{
203
	struct imsg		 imsg;
204
	struct ctl_conn		*c;
205
	ssize_t			 n;
206
	int			 verbose;
207
	struct peer		*p;
208
	struct ctl_neighbor	*neighbor;
209
	struct ctl_show_rib_request	*ribreq;
210
211
	if ((c = control_connbyfd(pfd->fd)) == NULL) {
212
		log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
213
		return (0);
214
	}
215
216
	if (pfd->revents & POLLOUT) {
217
		if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
218
			*ctl_cnt -= control_close(pfd->fd);
219
			return (1);
220
		}
221
		if (c->throttled && c->ibuf.w.queued < CTL_MSG_LOW_MARK) {
222
			if (imsg_ctl_rde(IMSG_XON, c->ibuf.pid, NULL, 0) != -1)
223
				c->throttled = 0;
224
		}
225
	}
226
227
	if (!(pfd->revents & POLLIN))
228
		return (0);
229
230
	if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) ||
231
	    n == 0) {
232
		*ctl_cnt -= control_close(pfd->fd);
233
		return (1);
234
	}
235
236
	for (;;) {
237
		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
238
			*ctl_cnt -= control_close(pfd->fd);
239
			return (1);
240
		}
241
242
		if (n == 0)
243
			break;
244
245
		if (c->restricted) {
246
			switch (imsg.hdr.type) {
247
			case IMSG_CTL_SHOW_NEIGHBOR:
248
			case IMSG_CTL_SHOW_NEXTHOP:
249
			case IMSG_CTL_SHOW_INTERFACE:
250
			case IMSG_CTL_SHOW_RIB:
251
			case IMSG_CTL_SHOW_RIB_AS:
252
			case IMSG_CTL_SHOW_RIB_PREFIX:
253
			case IMSG_CTL_SHOW_RIB_MEM:
254
			case IMSG_CTL_SHOW_RIB_COMMUNITY:
255
			case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY:
256
			case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
257
			case IMSG_CTL_SHOW_NETWORK:
258
			case IMSG_CTL_SHOW_TERSE:
259
			case IMSG_CTL_SHOW_TIMER:
260
				break;
261
			default:
262
				/* clear imsg type to prevent processing */
263
				imsg.hdr.type = IMSG_NONE;
264
				control_result(c, CTL_RES_DENIED);
265
				break;
266
			}
267
		}
268
269
		switch (imsg.hdr.type) {
270
		case IMSG_NONE:
271
			/* message was filtered out, nothing to do */
272
			break;
273
		case IMSG_CTL_SHOW_NEIGHBOR:
274
			c->ibuf.pid = imsg.hdr.pid;
275
			if (imsg.hdr.len == IMSG_HEADER_SIZE +
276
			    sizeof(struct ctl_neighbor)) {
277
				neighbor = imsg.data;
278
				p = getpeerbyaddr(&neighbor->addr);
279
				if (p == NULL)
280
					p = getpeerbydesc(neighbor->descr);
281
				if (p == NULL) {
282
					control_result(c, CTL_RES_NOSUCHPEER);
283
					break;
284
				}
285
				if (!neighbor->show_timers) {
286
					imsg_ctl_rde(imsg.hdr.type,
287
					    imsg.hdr.pid,
288
					    p, sizeof(struct peer));
289
					imsg_ctl_rde(IMSG_CTL_END,
290
					    imsg.hdr.pid, NULL, 0);
291
				} else {
292
					u_int			 i;
293
					time_t			 d;
294
					struct ctl_timer	 ct;
295
296
					imsg_compose(&c->ibuf,
297
					    IMSG_CTL_SHOW_NEIGHBOR,
298
					    0, 0, -1, p, sizeof(*p));
299
					for (i = 1; i < Timer_Max; i++) {
300
						if (!timer_running(p, i, &d))
301
							continue;
302
						ct.type = i;
303
						ct.val = d;
304
						imsg_compose(&c->ibuf,
305
						    IMSG_CTL_SHOW_TIMER,
306
						    0, 0, -1, &ct, sizeof(ct));
307
					}
308
					imsg_compose(&c->ibuf, IMSG_CTL_END,
309
					    0, 0, -1, NULL, 0);
310
				}
311
			} else {
312
				for (p = peers; p != NULL; p = p->next)
313
					imsg_ctl_rde(imsg.hdr.type,
314
					    imsg.hdr.pid,
315
					    p, sizeof(struct peer));
316
				imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
317
					NULL, 0);
318
			}
319
			break;
320
		case IMSG_CTL_SHOW_TERSE:
321
			for (p = peers; p != NULL; p = p->next)
322
				imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
323
				    0, 0, -1, p, sizeof(struct peer));
324
			imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
325
			break;
326
		case IMSG_CTL_FIB_COUPLE:
327
		case IMSG_CTL_FIB_DECOUPLE:
328
			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
329
			    0, NULL, 0);
330
			break;
331
		case IMSG_CTL_NEIGHBOR_UP:
332
		case IMSG_CTL_NEIGHBOR_DOWN:
333
		case IMSG_CTL_NEIGHBOR_CLEAR:
334
		case IMSG_CTL_NEIGHBOR_RREFRESH:
335
		case IMSG_CTL_NEIGHBOR_DESTROY:
336
			if (imsg.hdr.len == IMSG_HEADER_SIZE +
337
			    sizeof(struct ctl_neighbor)) {
338
				neighbor = imsg.data;
339
				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
340
				p = getpeerbyaddr(&neighbor->addr);
341
				if (p == NULL)
342
					p = getpeerbydesc(neighbor->descr);
343
				if (p == NULL) {
344
					control_result(c, CTL_RES_NOSUCHPEER);
345
					break;
346
				}
347
				switch (imsg.hdr.type) {
348
				case IMSG_CTL_NEIGHBOR_UP:
349
					bgp_fsm(p, EVNT_START);
350
					p->conf.down = 0;
351
					p->conf.shutcomm[0] = '\0';
352
					control_result(c, CTL_RES_OK);
353
					break;
354
				case IMSG_CTL_NEIGHBOR_DOWN:
355
					p->conf.down = 1;
356
					strlcpy(p->conf.shutcomm,
357
					    neighbor->shutcomm,
358
					    sizeof(neighbor->shutcomm));
359
					session_stop(p, ERR_CEASE_ADMIN_DOWN);
360
					control_result(c, CTL_RES_OK);
361
					break;
362
				case IMSG_CTL_NEIGHBOR_CLEAR:
363
					strlcpy(p->conf.shutcomm,
364
					    neighbor->shutcomm,
365
					    sizeof(neighbor->shutcomm));
366
					if (!p->conf.down) {
367
						session_stop(p,
368
						    ERR_CEASE_ADMIN_RESET);
369
						timer_set(p, Timer_IdleHold,
370
						    SESSION_CLEAR_DELAY);
371
					} else {
372
						session_stop(p,
373
						    ERR_CEASE_ADMIN_DOWN);
374
					}
375
					control_result(c, CTL_RES_OK);
376
					break;
377
				case IMSG_CTL_NEIGHBOR_RREFRESH:
378
					if (session_neighbor_rrefresh(p))
379
						control_result(c,
380
						    CTL_RES_NOCAP);
381
					else
382
						control_result(c, CTL_RES_OK);
383
					break;
384
				case IMSG_CTL_NEIGHBOR_DESTROY:
385
					if (!p->template)
386
						control_result(c,
387
						    CTL_RES_BADPEER);
388
					else if (p->state != STATE_IDLE)
389
						control_result(c,
390
						    CTL_RES_BADSTATE);
391
					else {
392
						/*
393
						 * Mark as deleted, will be
394
						 * collected on next poll loop.
395
						 */
396
						p->conf.reconf_action =
397
						    RECONF_DELETE;
398
						control_result(c, CTL_RES_OK);
399
					}
400
					break;
401
				default:
402
					fatal("king bula wants more humppa");
403
				}
404
			} else
405
				log_warnx("got IMSG_CTL_NEIGHBOR_ with "
406
				    "wrong length");
407
			break;
408
		case IMSG_CTL_RELOAD:
409
		case IMSG_CTL_SHOW_INTERFACE:
410
		case IMSG_CTL_SHOW_FIB_TABLES:
411
			c->ibuf.pid = imsg.hdr.pid;
412
			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
413
			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
414
			break;
415
		case IMSG_CTL_KROUTE:
416
		case IMSG_CTL_KROUTE_ADDR:
417
		case IMSG_CTL_SHOW_NEXTHOP:
418
			c->ibuf.pid = imsg.hdr.pid;
419
			imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
420
			    imsg.hdr.pid, imsg.data, imsg.hdr.len -
421
			    IMSG_HEADER_SIZE);
422
			break;
423
		case IMSG_CTL_SHOW_RIB:
424
		case IMSG_CTL_SHOW_RIB_AS:
425
		case IMSG_CTL_SHOW_RIB_PREFIX:
426
			if (imsg.hdr.len == IMSG_HEADER_SIZE +
427
			    sizeof(struct ctl_show_rib_request)) {
428
				ribreq = imsg.data;
429
				neighbor = &ribreq->neighbor;
430
				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
431
				ribreq->peerid = 0;
432
				p = NULL;
433
				if (neighbor->addr.aid) {
434
					p = getpeerbyaddr(&neighbor->addr);
435
					if (p == NULL) {
436
						control_result(c,
437
						    CTL_RES_NOSUCHPEER);
438
						break;
439
					}
440
					ribreq->peerid = p->conf.id;
441
				} else if (neighbor->descr[0]) {
442
					p = getpeerbydesc(neighbor->descr);
443
					if (p == NULL) {
444
						control_result(c,
445
						    CTL_RES_NOSUCHPEER);
446
						break;
447
					}
448
					ribreq->peerid = p->conf.id;
449
				}
450
				if ((ribreq->flags &
451
				     (F_CTL_ADJ_OUT | F_CTL_ADJ_IN)) && !p) {
452
					/*
453
					 * both in and out tables are only
454
					 * meaningful if used on a single
455
					 * peer.
456
					 */
457
					control_result(c, CTL_RES_NOSUCHPEER);
458
					break;
459
				}
460
				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
461
				    && (ribreq->prefix.aid == AID_UNSPEC)) {
462
					/* malformed request, must specify af */
463
					control_result(c, CTL_RES_PARSE_ERROR);
464
					break;
465
				}
466
				c->ibuf.pid = imsg.hdr.pid;
467
				imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
468
				    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
469
			} else
470
				log_warnx("got IMSG_CTL_SHOW_RIB with "
471
				    "wrong length");
472
			break;
473
		case IMSG_CTL_SHOW_RIB_MEM:
474
		case IMSG_CTL_SHOW_RIB_COMMUNITY:
475
		case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY:
476
		case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
477
		case IMSG_CTL_SHOW_NETWORK:
478
			c->ibuf.pid = imsg.hdr.pid;
479
			imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
480
			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
481
			break;
482
		case IMSG_NETWORK_ADD:
483
		case IMSG_NETWORK_ASPATH:
484
		case IMSG_NETWORK_ATTR:
485
		case IMSG_NETWORK_REMOVE:
486
		case IMSG_NETWORK_FLUSH:
487
		case IMSG_NETWORK_DONE:
488
		case IMSG_FILTER_SET:
489
			imsg_ctl_rde(imsg.hdr.type, 0,
490
			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
491
			break;
492
		case IMSG_CTL_LOG_VERBOSE:
493
			if (imsg.hdr.len != IMSG_HEADER_SIZE +
494
			    sizeof(verbose))
495
				break;
496
497
			/* forward to other processes */
498
			imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
499
			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
500
			imsg_ctl_rde(imsg.hdr.type, 0,
501
			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
502
503
			memcpy(&verbose, imsg.data, sizeof(verbose));
504
			log_setverbose(verbose);
505
			break;
506
		default:
507
			break;
508
		}
509
		imsg_free(&imsg);
510
	}
511
512
	return (0);
513
}
514
515
int
516
control_imsg_relay(struct imsg *imsg)
517
{
518
	struct ctl_conn	*c;
519
520
	if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
521
		return (0);
522
523
	if (!c->throttled && c->ibuf.w.queued > CTL_MSG_HIGH_MARK) {
524
		if (imsg_ctl_rde(IMSG_XOFF, imsg->hdr.pid, NULL, 0) != -1)
525
			c->throttled = 1;
526
	}
527
528
	return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
529
	    imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
530
}
531
532
void
533
control_result(struct ctl_conn *c, u_int code)
534
{
535
	imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
536
	    &code, sizeof(code));
537
}
538
539
/* This should go into libutil, from smtpd/mproc.c */
540
ssize_t
541
imsg_read_nofd(struct imsgbuf *ibuf)
542
{
543
	ssize_t	 n;
544
	char	*buf;
545
	size_t	 len;
546
547
	buf = ibuf->r.buf + ibuf->r.wpos;
548
	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
549
550
	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
551
		if (errno != EINTR)
552
			return (n);
553
	}
554
555
	ibuf->r.wpos += n;
556
	return (n);
557
}