1 /*- 2 * Copyright (c) 2014 Yandex LLC. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 /* 28 * Kernel interface tracking API. 29 * 30 */ 31 32 #include "opt_ipfw.h" 33 #include "opt_inet.h" 34 #ifndef INET 35 #error IPFIREWALL requires INET. 36 #endif /* INET */ 37 #include "opt_inet6.h" 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/malloc.h> 42 #include <sys/kernel.h> 43 #include <sys/lock.h> 44 #include <sys/rwlock.h> 45 #include <sys/rmlock.h> 46 #include <sys/socket.h> 47 #include <sys/queue.h> 48 #include <sys/eventhandler.h> 49 #include <net/if.h> 50 #include <net/if_var.h> 51 #include <net/if_private.h> 52 #include <net/vnet.h> 53 54 #include <netinet/in.h> 55 #include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 56 #include <netinet/ip_fw.h> 57 58 #include <netpfil/ipfw/ip_fw_private.h> 59 60 #define CHAIN_TO_II(ch) ((struct namedobj_instance *)ch->ifcfg) 61 62 #define DEFAULT_IFACES 128 63 64 static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif, 65 uint16_t ifindex); 66 static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif, 67 uint16_t ifindex); 68 static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 69 struct sockopt_data *sd); 70 71 static struct ipfw_sopt_handler scodes[] = { 72 { IP_FW_XIFLIST, 0, HDIR_GET, list_ifaces }, 73 }; 74 75 /* 76 * FreeBSD Kernel interface. 77 */ 78 static void ipfw_kifhandler(void *arg, struct ifnet *ifp); 79 static int ipfw_kiflookup(char *name); 80 static void iface_khandler_register(void); 81 static void iface_khandler_deregister(void); 82 83 static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event; 84 static int num_vnets = 0; 85 static struct mtx vnet_mtx; 86 87 /* 88 * Checks if kernel interface is contained in our tracked 89 * interface list and calls attach/detach handler. 90 */ 91 static void 92 ipfw_kifhandler(void *arg, struct ifnet *ifp) 93 { 94 struct ip_fw_chain *ch; 95 struct ipfw_iface *iif; 96 struct namedobj_instance *ii; 97 uintptr_t htype; 98 99 if (V_ipfw_vnet_ready == 0) 100 return; 101 102 ch = &V_layer3_chain; 103 htype = (uintptr_t)arg; 104 105 IPFW_UH_WLOCK(ch); 106 ii = CHAIN_TO_II(ch); 107 if (ii == NULL) { 108 IPFW_UH_WUNLOCK(ch); 109 return; 110 } 111 iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0, 112 if_name(ifp)); 113 if (iif != NULL) { 114 if (htype == 1) 115 handle_ifattach(ch, iif, ifp->if_index); 116 else 117 handle_ifdetach(ch, iif, ifp->if_index); 118 } 119 IPFW_UH_WUNLOCK(ch); 120 } 121 122 /* 123 * Reference current VNET as iface tracking API user. 124 * Registers interface tracking handlers for first VNET. 125 */ 126 static void 127 iface_khandler_register(void) 128 { 129 int create; 130 131 create = 0; 132 133 mtx_lock(&vnet_mtx); 134 if (num_vnets == 0) 135 create = 1; 136 num_vnets++; 137 mtx_unlock(&vnet_mtx); 138 139 if (create == 0) 140 return; 141 142 printf("IPFW: starting up interface tracker\n"); 143 144 ipfw_ifdetach_event = EVENTHANDLER_REGISTER( 145 ifnet_departure_event, ipfw_kifhandler, NULL, 146 EVENTHANDLER_PRI_ANY); 147 ipfw_ifattach_event = EVENTHANDLER_REGISTER( 148 ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1), 149 EVENTHANDLER_PRI_ANY); 150 } 151 152 /* 153 * 154 * Detach interface event handlers on last VNET instance 155 * detach. 156 */ 157 static void 158 iface_khandler_deregister(void) 159 { 160 int destroy; 161 162 destroy = 0; 163 mtx_lock(&vnet_mtx); 164 if (num_vnets == 1) 165 destroy = 1; 166 num_vnets--; 167 mtx_unlock(&vnet_mtx); 168 169 if (destroy == 0) 170 return; 171 172 EVENTHANDLER_DEREGISTER(ifnet_arrival_event, 173 ipfw_ifattach_event); 174 EVENTHANDLER_DEREGISTER(ifnet_departure_event, 175 ipfw_ifdetach_event); 176 } 177 178 /* 179 * Retrieves ifindex for given @name. 180 * 181 * Returns ifindex or 0. 182 */ 183 static int 184 ipfw_kiflookup(char *name) 185 { 186 struct ifnet *ifp; 187 int ifindex; 188 189 ifindex = 0; 190 191 if ((ifp = ifunit_ref(name)) != NULL) { 192 ifindex = ifp->if_index; 193 if_rele(ifp); 194 } 195 196 return (ifindex); 197 } 198 199 /* 200 * Global ipfw startup hook. 201 * Since we perform lazy initialization, do nothing except 202 * mutex init. 203 */ 204 int 205 ipfw_iface_init(void) 206 { 207 208 mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF); 209 IPFW_ADD_SOPT_HANDLER(1, scodes); 210 return (0); 211 } 212 213 /* 214 * Global ipfw destroy hook. 215 * Unregister khandlers iff init has been done. 216 */ 217 void 218 ipfw_iface_destroy(void) 219 { 220 221 IPFW_DEL_SOPT_HANDLER(1, scodes); 222 mtx_destroy(&vnet_mtx); 223 } 224 225 /* 226 * Perform actual init on internal request. 227 * Inits both namehash and global khandler. 228 */ 229 static void 230 vnet_ipfw_iface_init(struct ip_fw_chain *ch) 231 { 232 struct namedobj_instance *ii; 233 234 ii = ipfw_objhash_create(DEFAULT_IFACES); 235 IPFW_UH_WLOCK(ch); 236 if (ch->ifcfg == NULL) { 237 ch->ifcfg = ii; 238 ii = NULL; 239 } 240 IPFW_UH_WUNLOCK(ch); 241 242 if (ii != NULL) { 243 /* Already initialized. Free namehash. */ 244 ipfw_objhash_destroy(ii); 245 } else { 246 /* We're the first ones. Init kernel hooks. */ 247 iface_khandler_register(); 248 } 249 } 250 251 static int 252 destroy_iface(struct namedobj_instance *ii, struct named_object *no, 253 void *arg) 254 { 255 256 /* Assume all consumers have been already detached */ 257 free(no, M_IPFW); 258 return (0); 259 } 260 261 /* 262 * Per-VNET ipfw detach hook. 263 * 264 */ 265 void 266 vnet_ipfw_iface_destroy(struct ip_fw_chain *ch) 267 { 268 struct namedobj_instance *ii; 269 270 IPFW_UH_WLOCK(ch); 271 ii = CHAIN_TO_II(ch); 272 ch->ifcfg = NULL; 273 IPFW_UH_WUNLOCK(ch); 274 275 if (ii != NULL) { 276 ipfw_objhash_foreach(ii, destroy_iface, ch); 277 ipfw_objhash_destroy(ii); 278 iface_khandler_deregister(); 279 } 280 } 281 282 /* 283 * Notify the subsystem that we are interested in tracking 284 * interface @name. This function has to be called without 285 * holding any locks to permit allocating the necessary states 286 * for proper interface tracking. 287 * 288 * Returns 0 on success. 289 */ 290 int 291 ipfw_iface_ref(struct ip_fw_chain *ch, char *name, 292 struct ipfw_ifc *ic) 293 { 294 struct namedobj_instance *ii; 295 struct ipfw_iface *iif, *tmp; 296 297 if (strlen(name) >= sizeof(iif->ifname)) 298 return (EINVAL); 299 300 IPFW_UH_WLOCK(ch); 301 302 ii = CHAIN_TO_II(ch); 303 if (ii == NULL) { 304 /* 305 * First request to subsystem. 306 * Let's perform init. 307 */ 308 IPFW_UH_WUNLOCK(ch); 309 vnet_ipfw_iface_init(ch); 310 IPFW_UH_WLOCK(ch); 311 ii = CHAIN_TO_II(ch); 312 } 313 314 iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name); 315 316 if (iif != NULL) { 317 iif->no.refcnt++; 318 ic->iface = iif; 319 IPFW_UH_WUNLOCK(ch); 320 return (0); 321 } 322 323 IPFW_UH_WUNLOCK(ch); 324 325 /* Not found. Let's create one */ 326 iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO); 327 TAILQ_INIT(&iif->consumers); 328 iif->no.name = iif->ifname; 329 strlcpy(iif->ifname, name, sizeof(iif->ifname)); 330 331 /* 332 * Ref & link to the list. 333 * 334 * We assume ifnet_arrival_event / ifnet_departure_event 335 * are not holding any locks. 336 */ 337 iif->no.refcnt = 1; 338 IPFW_UH_WLOCK(ch); 339 340 tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name); 341 if (tmp != NULL) { 342 /* Interface has been created since unlock. Ref and return */ 343 tmp->no.refcnt++; 344 ic->iface = tmp; 345 IPFW_UH_WUNLOCK(ch); 346 free(iif, M_IPFW); 347 return (0); 348 } 349 350 iif->ifindex = ipfw_kiflookup(name); 351 if (iif->ifindex != 0) 352 iif->resolved = 1; 353 354 ipfw_objhash_add(ii, &iif->no); 355 ic->iface = iif; 356 357 IPFW_UH_WUNLOCK(ch); 358 359 return (0); 360 } 361 362 /* 363 * Adds @ic to the list of iif interface consumers. 364 * Must be called with holding both UH+WLOCK. 365 * Callback may be immediately called (if interface exists). 366 */ 367 void 368 ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic) 369 { 370 struct ipfw_iface *iif; 371 372 IPFW_UH_WLOCK_ASSERT(ch); 373 IPFW_WLOCK_ASSERT(ch); 374 375 iif = ic->iface; 376 377 TAILQ_INSERT_TAIL(&iif->consumers, ic, next); 378 if (iif->resolved != 0) 379 ic->cb(ch, ic->cbdata, iif->ifindex); 380 } 381 382 /* 383 * Unlinks interface tracker object @ic from interface. 384 * Must be called while holding UH lock. 385 */ 386 void 387 ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic) 388 { 389 struct ipfw_iface *iif; 390 391 IPFW_UH_WLOCK_ASSERT(ch); 392 393 iif = ic->iface; 394 TAILQ_REMOVE(&iif->consumers, ic, next); 395 } 396 397 /* 398 * Unreference interface specified by @ic. 399 * Must be called while holding UH lock. 400 */ 401 void 402 ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic) 403 { 404 struct ipfw_iface *iif; 405 406 IPFW_UH_WLOCK_ASSERT(ch); 407 408 iif = ic->iface; 409 ic->iface = NULL; 410 411 iif->no.refcnt--; 412 /* TODO: check for references & delete */ 413 } 414 415 /* 416 * Interface arrival handler. 417 */ 418 static void 419 handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif, 420 uint16_t ifindex) 421 { 422 struct ipfw_ifc *ic; 423 424 IPFW_UH_WLOCK_ASSERT(ch); 425 426 iif->gencnt++; 427 iif->resolved = 1; 428 iif->ifindex = ifindex; 429 430 IPFW_WLOCK(ch); 431 TAILQ_FOREACH(ic, &iif->consumers, next) 432 ic->cb(ch, ic->cbdata, iif->ifindex); 433 IPFW_WUNLOCK(ch); 434 } 435 436 /* 437 * Interface departure handler. 438 */ 439 static void 440 handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif, 441 uint16_t ifindex) 442 { 443 struct ipfw_ifc *ic; 444 445 IPFW_UH_WLOCK_ASSERT(ch); 446 447 IPFW_WLOCK(ch); 448 TAILQ_FOREACH(ic, &iif->consumers, next) 449 ic->cb(ch, ic->cbdata, 0); 450 IPFW_WUNLOCK(ch); 451 452 iif->gencnt++; 453 iif->resolved = 0; 454 iif->ifindex = 0; 455 } 456 457 struct dump_iface_args { 458 struct ip_fw_chain *ch; 459 struct sockopt_data *sd; 460 }; 461 462 static int 463 export_iface_internal(struct namedobj_instance *ii, struct named_object *no, 464 void *arg) 465 { 466 ipfw_iface_info *i; 467 struct dump_iface_args *da; 468 struct ipfw_iface *iif; 469 470 da = (struct dump_iface_args *)arg; 471 472 i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i)); 473 KASSERT(i != NULL, ("previously checked buffer is not enough")); 474 475 iif = (struct ipfw_iface *)no; 476 477 strlcpy(i->ifname, iif->ifname, sizeof(i->ifname)); 478 if (iif->resolved) 479 i->flags |= IPFW_IFFLAG_RESOLVED; 480 i->ifindex = iif->ifindex; 481 i->refcnt = iif->no.refcnt; 482 i->gencnt = iif->gencnt; 483 return (0); 484 } 485 486 /* 487 * Lists all interface currently tracked by ipfw. 488 * Data layout (v0)(current): 489 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 490 * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ] 491 * 492 * Returns 0 on success 493 */ 494 static int 495 list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 496 struct sockopt_data *sd) 497 { 498 struct namedobj_instance *ii; 499 struct _ipfw_obj_lheader *olh; 500 struct dump_iface_args da; 501 uint32_t count, size; 502 503 olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 504 if (olh == NULL) 505 return (EINVAL); 506 if (sd->valsize < olh->size) 507 return (EINVAL); 508 509 IPFW_UH_RLOCK(ch); 510 ii = CHAIN_TO_II(ch); 511 if (ii != NULL) 512 count = ipfw_objhash_count(ii); 513 else 514 count = 0; 515 size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader); 516 517 /* Fill in header regadless of buffer size */ 518 olh->count = count; 519 olh->objsize = sizeof(ipfw_iface_info); 520 521 if (size > olh->size) { 522 olh->size = size; 523 IPFW_UH_RUNLOCK(ch); 524 return (ENOMEM); 525 } 526 olh->size = size; 527 528 da.ch = ch; 529 da.sd = sd; 530 531 if (ii != NULL) 532 ipfw_objhash_foreach(ii, export_iface_internal, &da); 533 IPFW_UH_RUNLOCK(ch); 534 535 return (0); 536 } 537