1 /* $OpenBSD: pftable.c,v 1.8 2015/01/21 21:50:32 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Damien Miller <djm@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/ioctl.h> 21 #include <sys/socket.h> 22 23 #include <netinet/in.h> 24 #include <net/if.h> 25 #include <net/pfvar.h> 26 27 #include <stdlib.h> 28 #include <string.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 32 /* Namespace collision: these are defined in both bgpd.h and pfvar.h */ 33 #undef v4 34 #undef v6 35 #undef addr8 36 #undef addr16 37 #undef addr32 38 39 #include "bgpd.h" 40 41 static int devpf = -1; 42 43 struct pf_table { 44 LIST_ENTRY(pf_table) entry; 45 char name[PFTABLE_LEN]; 46 unsigned long what; 47 struct pfr_addr *worklist; 48 int naddrs; 49 int nalloc; 50 }; 51 52 /* List of tables under management */ 53 LIST_HEAD(, pf_table) tables = LIST_HEAD_INITIALIZER(tables); 54 55 static int 56 pftable_change(struct pf_table *pft) 57 { 58 struct pfioc_table tio; 59 int ret; 60 61 if (pft->naddrs == 0 || pft->what == 0) 62 return (0); 63 64 if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR)) == -1)) 65 fatal("open(/dev/pf)"); 66 67 bzero(&tio, sizeof(tio)); 68 strlcpy(tio.pfrio_table.pfrt_name, pft->name, 69 sizeof(tio.pfrio_table.pfrt_name)); 70 tio.pfrio_buffer = pft->worklist; 71 tio.pfrio_esize = sizeof(*pft->worklist); 72 tio.pfrio_size = pft->naddrs; 73 74 ret = ioctl(devpf, pft->what, &tio); 75 76 /* bad prefixes shouldn't cause us to die */ 77 if (ret == -1) { 78 if (errno == EINVAL) 79 return (0); 80 log_warn("pftable_change ioctl"); 81 } 82 83 return (ret); 84 } 85 86 static int 87 pftable_clear(const char *name) 88 { 89 struct pfioc_table tio; 90 91 if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR)) == -1)) 92 fatal("open(/dev/pf)"); 93 94 bzero(&tio, sizeof(tio)); 95 strlcpy(tio.pfrio_table.pfrt_name, name, 96 sizeof(tio.pfrio_table.pfrt_name)); 97 98 if (ioctl(devpf, DIOCRCLRADDRS, &tio) != 0) { 99 log_warn("pftable_clear ioctl"); 100 return (-1); 101 } 102 103 return (0); 104 } 105 106 int 107 pftable_exists(const char *name) 108 { 109 struct pfioc_table tio; 110 struct pfr_astats dummy; 111 112 if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR)) == -1)) 113 fatal("open(/dev/pf)"); 114 115 bzero(&tio, sizeof(tio)); 116 strlcpy(tio.pfrio_table.pfrt_name, name, 117 sizeof(tio.pfrio_table.pfrt_name)); 118 tio.pfrio_buffer = &dummy; 119 tio.pfrio_esize = sizeof(dummy); 120 tio.pfrio_size = 1; 121 122 if (ioctl(devpf, DIOCRGETASTATS, &tio) != 0) 123 return (-1); 124 125 return (0); 126 } 127 128 int 129 pftable_add(const char *name) 130 { 131 struct pf_table *pft; 132 133 /* Ignore duplicates */ 134 LIST_FOREACH(pft, &tables, entry) 135 if (strcmp(pft->name, name) == 0) 136 return (0); 137 138 if ((pft = malloc(sizeof(*pft))) == NULL) { 139 log_warn("pftable malloc"); 140 return (-1); 141 } 142 143 bzero(pft, sizeof(*pft)); 144 if (strlcpy(pft->name, name, sizeof(pft->name)) >= sizeof(pft->name)) { 145 log_warn("pf_table name too long"); 146 free(pft); 147 return (-1); 148 } 149 150 LIST_INSERT_HEAD(&tables, pft, entry); 151 152 return (0); 153 } 154 155 int 156 pftable_clear_all(void) 157 { 158 struct pf_table *pft; 159 160 LIST_FOREACH(pft, &tables, entry) { 161 if (pftable_clear(pft->name) != 0) 162 return (-1); 163 if (pft->worklist != NULL) { 164 free(pft->worklist); 165 pft->worklist = NULL; 166 } 167 pft->nalloc = pft->naddrs = 0; 168 pft->what = 0; 169 } 170 171 return (0); 172 } 173 174 static int 175 pftable_add_work(const char *table, struct bgpd_addr *addr, 176 u_int8_t len, int del) 177 { 178 struct pf_table *pft; 179 struct pfr_addr *pfa, *tmp; 180 unsigned long what; 181 182 if (*table == '\0' || len > 128) 183 fatal("pftable_add_work: insane"); 184 185 /* Find table */ 186 LIST_FOREACH(pft, &tables, entry) 187 if (strcmp(pft->name, table) == 0) 188 break; 189 190 if (pft == NULL) { 191 log_warn("pf table %s not found", table); 192 return (-1); 193 } 194 195 /* Only one type of work on the list at a time */ 196 what = del ? DIOCRDELADDRS : DIOCRADDADDRS; 197 if (pft->naddrs != 0 && pft->what != what) 198 fatal("attempt to mix pf table additions/deletions"); 199 200 if (pft->nalloc <= pft->naddrs) 201 pft->nalloc = pft->nalloc == 0 ? 1 : pft->nalloc * 2; 202 tmp = reallocarray(pft->worklist, pft->nalloc, sizeof(*tmp)); 203 if (tmp == NULL) { 204 if (pft->worklist != NULL) { 205 log_warn("pftable_add_work: malloc"); 206 free(pft->worklist); 207 pft->worklist = NULL; 208 } 209 pft->nalloc = pft->naddrs = 0; 210 pft->what = 0; 211 return (-1); 212 } 213 pft->worklist = tmp; 214 pfa = &pft->worklist[pft->naddrs]; 215 216 bzero(pfa, sizeof(*pfa)); 217 memcpy(&pfa->pfra_u, &addr->ba, (len + 7U) / 8); 218 pfa->pfra_af = aid2af(addr->aid); 219 pfa->pfra_net = len; 220 221 pft->naddrs++; 222 pft->what = what; 223 224 /* Don't let the list grow too large */ 225 if (pft->naddrs >= 1024) 226 pftable_commit(); 227 228 return (0); 229 } 230 231 /* imsg handlers */ 232 int 233 pftable_addr_add(struct pftable_msg *m) 234 { 235 return (pftable_add_work(m->pftable, &m->addr, m->len, 0)); 236 } 237 238 int 239 pftable_addr_remove(struct pftable_msg *m) 240 { 241 return (pftable_add_work(m->pftable, &m->addr, m->len, 1)); 242 } 243 244 int 245 pftable_commit(void) 246 { 247 struct pf_table *pft; 248 int ret = 0; 249 250 LIST_FOREACH(pft, &tables, entry) { 251 if (pft->what != 0 && pftable_change(pft) != 0) 252 ret = -1; 253 if (pft->worklist != NULL) 254 free(pft->worklist); 255 pft->worklist = NULL; 256 pft->nalloc = pft->naddrs = 0; 257 pft->what = 0; 258 } 259 260 return (ret); 261 } 262 263