1 /* $OpenBSD: pftable.c,v 1.9 2015/12/23 20:42:20 mmcc 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 free(pft->worklist); 164 pft->worklist = NULL; 165 pft->nalloc = pft->naddrs = 0; 166 pft->what = 0; 167 } 168 169 return (0); 170 } 171 172 static int 173 pftable_add_work(const char *table, struct bgpd_addr *addr, 174 u_int8_t len, int del) 175 { 176 struct pf_table *pft; 177 struct pfr_addr *pfa, *tmp; 178 unsigned long what; 179 180 if (*table == '\0' || len > 128) 181 fatal("pftable_add_work: insane"); 182 183 /* Find table */ 184 LIST_FOREACH(pft, &tables, entry) 185 if (strcmp(pft->name, table) == 0) 186 break; 187 188 if (pft == NULL) { 189 log_warn("pf table %s not found", table); 190 return (-1); 191 } 192 193 /* Only one type of work on the list at a time */ 194 what = del ? DIOCRDELADDRS : DIOCRADDADDRS; 195 if (pft->naddrs != 0 && pft->what != what) 196 fatal("attempt to mix pf table additions/deletions"); 197 198 if (pft->nalloc <= pft->naddrs) 199 pft->nalloc = pft->nalloc == 0 ? 1 : pft->nalloc * 2; 200 tmp = reallocarray(pft->worklist, pft->nalloc, sizeof(*tmp)); 201 if (tmp == NULL) { 202 if (pft->worklist != NULL) { 203 log_warn("pftable_add_work: malloc"); 204 free(pft->worklist); 205 pft->worklist = NULL; 206 } 207 pft->nalloc = pft->naddrs = 0; 208 pft->what = 0; 209 return (-1); 210 } 211 pft->worklist = tmp; 212 pfa = &pft->worklist[pft->naddrs]; 213 214 bzero(pfa, sizeof(*pfa)); 215 memcpy(&pfa->pfra_u, &addr->ba, (len + 7U) / 8); 216 pfa->pfra_af = aid2af(addr->aid); 217 pfa->pfra_net = len; 218 219 pft->naddrs++; 220 pft->what = what; 221 222 /* Don't let the list grow too large */ 223 if (pft->naddrs >= 1024) 224 pftable_commit(); 225 226 return (0); 227 } 228 229 /* imsg handlers */ 230 int 231 pftable_addr_add(struct pftable_msg *m) 232 { 233 return (pftable_add_work(m->pftable, &m->addr, m->len, 0)); 234 } 235 236 int 237 pftable_addr_remove(struct pftable_msg *m) 238 { 239 return (pftable_add_work(m->pftable, &m->addr, m->len, 1)); 240 } 241 242 int 243 pftable_commit(void) 244 { 245 struct pf_table *pft; 246 int ret = 0; 247 248 LIST_FOREACH(pft, &tables, entry) { 249 if (pft->what != 0 && pftable_change(pft) != 0) 250 ret = -1; 251 free(pft->worklist); 252 pft->worklist = NULL; 253 pft->nalloc = pft->naddrs = 0; 254 pft->what = 0; 255 } 256 257 return (ret); 258 } 259 260