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