GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/bgpd/control.c Lines: 0 213 0.0 %
Date: 2016-12-06 Branches: 0 145 0.0 %

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