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