GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/pfutils.c Lines: 0 87 0.0 %
Date: 2017-11-13 Branches: 0 57 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: pfutils.c,v 1.18 2017/02/13 23:04:05 krw Exp $ */
2
/*
3
 * Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <sys/types.h>
19
#include <sys/ioctl.h>
20
#include <sys/socket.h>
21
22
#include <netinet/in.h>
23
#include <net/if.h>
24
#include <net/pfvar.h>
25
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <paths.h>
29
#include <poll.h>
30
#include <pwd.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
36
#include "dhcp.h"
37
#include "tree.h"
38
#include "dhcpd.h"
39
#include "log.h"
40
41
extern struct passwd *pw;
42
extern int pfpipe[2];
43
extern int gotpipe;
44
extern char *abandoned_tab;
45
extern char *changedmac_tab;
46
extern char *leased_tab;
47
48
__dead void
49
pftable_handler()
50
{
51
	struct pf_cmd cmd;
52
	struct pollfd pfd[1];
53
	int l, r, fd, nfds;
54
55
	if ((fd = open(_PATH_DEV_PF, O_RDWR|O_NOFOLLOW, 0660)) == -1)
56
		log_warn("can't open pf device");
57
	if (chroot(_PATH_VAREMPTY) == -1)
58
		log_warn("chroot %s", _PATH_VAREMPTY);
59
	if (chdir("/") == -1)
60
		log_warn("chdir(\"/\")");
61
	if (setgroups(1, &pw->pw_gid) ||
62
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
63
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
64
		log_warn("can't drop privileges");
65
66
	setproctitle("pf table handler");
67
	l = sizeof(struct pf_cmd);
68
69
	for (;;) {
70
		pfd[0].fd = pfpipe[0];
71
		pfd[0].events = POLLIN;
72
		if ((nfds = poll(pfd, 1, -1)) == -1)
73
			if (errno != EINTR)
74
				log_warn("poll");
75
76
		if (nfds > 0 && (pfd[0].revents & POLLHUP))
77
			log_warnx("pf pipe closed");
78
79
		if (nfds > 0 && (pfd[0].revents & POLLIN)) {
80
			memset(&cmd, 0, l);
81
			r = atomicio(read, pfpipe[0], &cmd, l);
82
83
			if (r != l)
84
				log_warn("pf pipe error");
85
86
			switch (cmd.type) {
87
			case 'A':
88
				/*
89
				 * When we abandon an address, we add it to
90
				 * the table of abandoned addresses, and remove
91
				 * it from the table of active leases.
92
				 */
93
				pf_change_table(fd, 1, cmd.ip, abandoned_tab);
94
				pf_change_table(fd, 0, cmd.ip, leased_tab);
95
				pf_kill_state(fd, cmd.ip);
96
				break;
97
			case 'C':
98
				/*
99
				 * When the hardware address for an IP changes,
100
				 * remove it from the table of abandoned
101
				 * addresses, and from the table of overloaded
102
				 * addresses.
103
				 */
104
				pf_change_table(fd, 0, cmd.ip, abandoned_tab);
105
				pf_change_table(fd, 0, cmd.ip, changedmac_tab);
106
				break;
107
			case 'L':
108
				/*
109
				 * When a lease is granted or renewed, remove
110
				 * it from the table of abandoned addresses,
111
				 * and ensure it is in the table of active
112
				 * leases.
113
				 */
114
				pf_change_table(fd, 0, cmd.ip, abandoned_tab);
115
				pf_change_table(fd, 1, cmd.ip, leased_tab);
116
				break;
117
			case 'R':
118
				/*
119
				 * When we release or expire a lease, remove
120
				 * it from the table of active leases. As long
121
				 * as dhcpd doesn't abandon the address, no
122
				 * further action is required.
123
				 */
124
				pf_change_table(fd, 0, cmd.ip, leased_tab);
125
				break;
126
			default:
127
				break;
128
			}
129
		}
130
	}
131
	/* not reached */
132
	exit(1);
133
}
134
135
/* inspired by ("stolen") from usr.sbin/authpf/authpf.c */
136
void
137
pf_change_table(int fd, int op, struct in_addr ip, char *table)
138
{
139
	struct pfioc_table	io;
140
	struct pfr_addr		addr;
141
142
	if (table == NULL)
143
		return;
144
145
	memset(&io, 0, sizeof(io));
146
	strlcpy(io.pfrio_table.pfrt_name, table,
147
	    sizeof(io.pfrio_table.pfrt_name));
148
	io.pfrio_buffer = &addr;
149
	io.pfrio_esize = sizeof(addr);
150
	io.pfrio_size = 1;
151
152
	memset(&addr, 0, sizeof(addr));
153
	memcpy(&addr.pfra_ip4addr, &ip, 4);
154
	addr.pfra_af = AF_INET;
155
	addr.pfra_net = 32;
156
157
	if (ioctl(fd, op ? DIOCRADDADDRS : DIOCRDELADDRS, &io) &&
158
	    errno != ESRCH) {
159
		log_warn( "DIOCR%sADDRS on table %s", op ? "ADD" : "DEL",
160
		    table);
161
	}
162
}
163
164
void
165
pf_kill_state(int fd, struct in_addr ip)
166
{
167
	struct pfioc_state_kill	psk;
168
	struct pf_addr target;
169
170
	memset(&psk, 0, sizeof(psk));
171
	memset(&target, 0, sizeof(target));
172
173
	memcpy(&target.v4, &ip.s_addr, 4);
174
	psk.psk_af = AF_INET;
175
176
	/* Kill all states from target */
177
	memcpy(&psk.psk_src.addr.v.a.addr, &target,
178
	    sizeof(psk.psk_src.addr.v.a.addr));
179
	memset(&psk.psk_src.addr.v.a.mask, 0xff,
180
	    sizeof(psk.psk_src.addr.v.a.mask));
181
	if (ioctl(fd, DIOCKILLSTATES, &psk)) {
182
		log_warn("DIOCKILLSTATES failed");
183
	}
184
185
	/* Kill all states to target */
186
	memset(&psk.psk_src, 0, sizeof(psk.psk_src));
187
	memcpy(&psk.psk_dst.addr.v.a.addr, &target,
188
	    sizeof(psk.psk_dst.addr.v.a.addr));
189
	memset(&psk.psk_dst.addr.v.a.mask, 0xff,
190
	    sizeof(psk.psk_dst.addr.v.a.mask));
191
	if (ioctl(fd, DIOCKILLSTATES, &psk)) {
192
		log_warn("DIOCKILLSTATES failed");
193
	}
194
}
195
196
/* inspired by ("stolen") from usr.bin/ssh/atomicio.c */
197
size_t
198
atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
199
{
200
	char *s = _s;
201
	size_t pos = 0;
202
	ssize_t res;
203
204
	while (n > pos) {
205
		res = (f) (fd, s + pos, n - pos);
206
		switch (res) {
207
		case -1:
208
			if (errno == EINTR || errno == EAGAIN)
209
				continue;
210
			return 0;
211
		case 0:
212
			errno = EPIPE;
213
			return pos;
214
		default:
215
			pos += (size_t)res;
216
		}
217
	}
218
	return (pos);
219
}
220
221
/*
222
 * This function sends commands to the pf table handler. It will safely and
223
 * silently return if the handler is unconfigured, therefore it can be called
224
 * on all interesting lease events, whether or not the user actually wants to
225
 * use the pf table feature.
226
 */
227
void
228
pfmsg(char c, struct lease *lp)
229
{
230
	struct pf_cmd cmd;
231
232
	if (gotpipe == 0)
233
		return;
234
235
	cmd.type = c;
236
	memcpy(&cmd.ip.s_addr, lp->ip_addr.iabuf, 4);
237
238
	switch (c) {
239
	case 'A': /* address is being abandoned */
240
		/* FALLTHROUGH */
241
	case 'C': /* IP moved to different ethernet address */
242
		/* FALLTHROUGH */
243
	case 'L': /* Address is being leased (unabandoned) */
244
		/* FALLTHROUGH */
245
	case 'R': /* Address is being released or lease has expired */
246
		(void)atomicio(vwrite, pfpipe[1], &cmd,
247
		    sizeof(struct pf_cmd));
248
		break;
249
	default: /* silently ignore unknown commands */
250
		break;
251
	}
252
}