1 /* $OpenBSD: pfutils.c,v 1.11 2015/01/16 06:40:16 deraadt 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 #include <sys/time.h> 22 23 #include <net/if.h> 24 #include <net/pfvar.h> 25 #include <arpa/inet.h> 26 27 #include <ctype.h> 28 #include <err.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <poll.h> 32 #include <pwd.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "dhcpd.h" 39 40 extern struct passwd *pw; 41 extern int pfpipe[2]; 42 extern int gotpipe; 43 extern char *abandoned_tab; 44 extern char *changedmac_tab; 45 extern char *leased_tab; 46 47 __dead void 48 pftable_handler() 49 { 50 struct pf_cmd cmd; 51 struct pollfd pfd[1]; 52 int l, r, fd, nfds; 53 54 if ((fd = open(_PATH_DEV_PF, O_RDWR|O_NOFOLLOW, 0660)) == -1) 55 error("can't open pf device: %m"); 56 if (chroot(_PATH_VAREMPTY) == -1) 57 error("chroot %s: %m", _PATH_VAREMPTY); 58 if (chdir("/") == -1) 59 error("chdir(\"/\"): %m"); 60 if (setgroups(1, &pw->pw_gid) || 61 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 62 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 63 error("can't drop privileges: %m"); 64 65 setproctitle("pf table handler"); 66 l = sizeof(struct pf_cmd); 67 68 for (;;) { 69 pfd[0].fd = pfpipe[0]; 70 pfd[0].events = POLLIN; 71 if ((nfds = poll(pfd, 1, -1)) == -1) 72 if (errno != EINTR) 73 error("poll: %m"); 74 75 if (nfds > 0 && (pfd[0].revents & POLLIN)) { 76 bzero(&cmd, l); 77 r = atomicio(read, pfpipe[0], &cmd, l); 78 79 if (r != l) 80 error("pf pipe error: %m"); 81 82 switch (cmd.type) { 83 case 'A': 84 /* 85 * When we abandon an address, we add it to 86 * the table of abandoned addresses, and remove 87 * it from the table of active leases. 88 */ 89 pf_change_table(fd, 1, cmd.ip, abandoned_tab); 90 pf_change_table(fd, 0, cmd.ip, leased_tab); 91 pf_kill_state(fd, cmd.ip); 92 break; 93 case 'C': 94 /* 95 * When the hardware address for an IP changes, 96 * remove it from the table of abandoned 97 * addresses, and from the table of overloaded 98 * addresses. 99 */ 100 pf_change_table(fd, 0, cmd.ip, abandoned_tab); 101 pf_change_table(fd, 0, cmd.ip, changedmac_tab); 102 break; 103 case 'L': 104 /* 105 * When a lease is granted or renewed, remove 106 * it from the table of abandoned addresses, 107 * and ensure it is in the table of active 108 * leases. 109 */ 110 pf_change_table(fd, 0, cmd.ip, abandoned_tab); 111 pf_change_table(fd, 1, cmd.ip, leased_tab); 112 break; 113 case 'R': 114 /* 115 * When we release or expire a lease, remove 116 * it from the table of active leases. As long 117 * as dhcpd doesn't abandon the address, no 118 * further action is required. 119 */ 120 pf_change_table(fd, 0, cmd.ip, leased_tab); 121 break; 122 default: 123 break; 124 } 125 } 126 } 127 /* not reached */ 128 exit(1); 129 } 130 131 /* inspired by ("stolen") from usr.sbin/authpf/authpf.c */ 132 void 133 pf_change_table(int fd, int op, struct in_addr ip, char *table) 134 { 135 struct pfioc_table io; 136 struct pfr_addr addr; 137 138 if (table == NULL) 139 return; 140 141 bzero(&io, sizeof(io)); 142 strlcpy(io.pfrio_table.pfrt_name, table, 143 sizeof(io.pfrio_table.pfrt_name)); 144 io.pfrio_buffer = &addr; 145 io.pfrio_esize = sizeof(addr); 146 io.pfrio_size = 1; 147 148 bzero(&addr, sizeof(addr)); 149 memcpy(&addr.pfra_ip4addr, &ip, 4); 150 addr.pfra_af = AF_INET; 151 addr.pfra_net = 32; 152 153 if (ioctl(fd, op ? DIOCRADDADDRS : DIOCRDELADDRS, &io) && 154 errno != ESRCH) { 155 warning( "DIOCR%sADDRS on table %s: %s", 156 op ? "ADD" : "DEL", table, strerror(errno)); 157 } 158 } 159 160 void 161 pf_kill_state(int fd, struct in_addr ip) 162 { 163 struct pfioc_state_kill psk; 164 struct pf_addr target; 165 166 bzero(&psk, sizeof(psk)); 167 bzero(&target, sizeof(target)); 168 169 memcpy(&target.v4, &ip.s_addr, 4); 170 psk.psk_af = AF_INET; 171 172 /* Kill all states from target */ 173 memcpy(&psk.psk_src.addr.v.a.addr, &target, 174 sizeof(psk.psk_src.addr.v.a.addr)); 175 memset(&psk.psk_src.addr.v.a.mask, 0xff, 176 sizeof(psk.psk_src.addr.v.a.mask)); 177 if (ioctl(fd, DIOCKILLSTATES, &psk)) { 178 warning("DIOCKILLSTATES failed (%s)", strerror(errno)); 179 } 180 181 /* Kill all states to target */ 182 bzero(&psk.psk_src, sizeof(psk.psk_src)); 183 memcpy(&psk.psk_dst.addr.v.a.addr, &target, 184 sizeof(psk.psk_dst.addr.v.a.addr)); 185 memset(&psk.psk_dst.addr.v.a.mask, 0xff, 186 sizeof(psk.psk_dst.addr.v.a.mask)); 187 if (ioctl(fd, DIOCKILLSTATES, &psk)) { 188 warning("DIOCKILLSTATES failed (%s)", strerror(errno)); 189 } 190 } 191 192 /* inspired by ("stolen") from usr.bin/ssh/atomicio.c */ 193 size_t 194 atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) 195 { 196 char *s = _s; 197 size_t pos = 0; 198 ssize_t res; 199 200 while (n > pos) { 201 res = (f) (fd, s + pos, n - pos); 202 switch (res) { 203 case -1: 204 if (errno == EINTR || errno == EAGAIN) 205 continue; 206 return 0; 207 case 0: 208 errno = EPIPE; 209 return pos; 210 default: 211 pos += (size_t)res; 212 } 213 } 214 return (pos); 215 } 216 217 /* 218 * This function sends commands to the pf table handler. It will safely and 219 * silently return if the handler is unconfigured, therefore it can be called 220 * on all interesting lease events, whether or not the user actually wants to 221 * use the pf table feature. 222 */ 223 void 224 pfmsg(char c, struct lease *lp) 225 { 226 struct pf_cmd cmd; 227 228 if (gotpipe == 0) 229 return; 230 231 cmd.type = c; 232 memcpy(&cmd.ip.s_addr, lp->ip_addr.iabuf, 4); 233 234 switch (c) { 235 case 'A': /* address is being abandoned */ 236 /* FALLTHROUGH */ 237 case 'C': /* IP moved to different ethernet address */ 238 /* FALLTHROUGH */ 239 case 'L': /* Address is being leased (unabandoned) */ 240 /* FALLTHROUGH */ 241 case 'R': /* Address is being released or lease has expired */ 242 (void)atomicio(vwrite, pfpipe[1], &cmd, 243 sizeof(struct pf_cmd)); 244 break; 245 default: /* silently ignore unknown commands */ 246 break; 247 } 248 } 249