if_pfsync.c revision 128209
1/* $FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 128209 2004-04-14 00:57:49Z brooks $ */ 2/* $OpenBSD: if_pfsync.c,v 1.6 2003/06/21 09:07:01 djm Exp $ */ 3 4/* 5 * Copyright (c) 2002 Michael Shalayeff 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#ifdef __FreeBSD__ 31#include "opt_inet.h" 32#include "opt_inet6.h" 33#endif 34 35#ifndef __FreeBSD__ 36#include "bpfilter.h" 37#include "pfsync.h" 38#elif __FreeBSD__ >= 5 39#include "opt_bpf.h" 40#include "opt_pf.h" 41#define NBPFILTER DEV_BPF 42#define NPFSYNC DEV_PFSYNC 43#endif 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/time.h> 48#include <sys/mbuf.h> 49#include <sys/socket.h> 50#ifdef __FreeBSD__ 51#include <sys/kernel.h> 52#include <sys/malloc.h> 53#include <sys/sockio.h> 54#else 55#include <sys/ioctl.h> 56#include <sys/timeout.h> 57#endif 58 59#include <net/if.h> 60#include <net/if_types.h> 61#include <net/route.h> 62#include <net/bpf.h> 63 64#ifdef INET 65#include <netinet/in.h> 66#include <netinet/in_var.h> 67#endif 68 69#ifdef INET6 70#ifndef INET 71#include <netinet/in.h> 72#endif 73#include <netinet6/nd6.h> 74#endif /* INET6 */ 75 76#include <net/pfvar.h> 77#include <net/if_pfsync.h> 78 79#ifdef __FreeBSD__ 80#define PFSYNCNAME "pfsync" 81#endif 82 83#define PFSYNC_MINMTU \ 84 (sizeof(struct pfsync_header) + sizeof(struct pf_state)) 85 86#ifdef PFSYNCDEBUG 87#define DPRINTF(x) do { if (pfsyncdebug) printf x ; } while (0) 88int pfsyncdebug; 89#else 90#define DPRINTF(x) 91#endif 92 93#ifndef __FreeBSD__ 94struct pfsync_softc pfsyncif; 95#endif 96 97#ifdef __FreeBSD__ 98static void pfsync_clone_destroy(struct ifnet *); 99static int pfsync_clone_create(struct if_clone *, int); 100#else 101void pfsyncattach(int); 102#endif 103void pfsync_setmtu(struct pfsync_softc *sc, int); 104int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 105 struct rtentry *); 106int pfsyncioctl(struct ifnet *, u_long, caddr_t); 107void pfsyncstart(struct ifnet *); 108 109struct mbuf *pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action); 110int pfsync_sendout(struct pfsync_softc *sc); 111void pfsync_timeout(void *v); 112 113#ifndef __FreeBSD__ 114extern int ifqmaxlen; 115#endif 116 117#ifdef __FreeBSD__ 118static MALLOC_DEFINE(M_PFSYNC, PFSYNCNAME, "Packet Filter State Sync. Interface"); 119static LIST_HEAD(pfsync_list, pfsync_softc) pfsync_list; 120struct if_clone pfsync_cloner = IF_CLONE_INITIALIZER(PFSYNCNAME, 121 pfsync_clone_create, pfsync_clone_destroy, 1, IF_MAXUNIT); 122 123static void 124pfsync_clone_destroy(struct ifnet *ifp) 125{ 126 struct pfsync_softc *sc; 127 128 sc = ifp->if_softc; 129 callout_stop(&sc->sc_tmo); 130 131 /* 132 * Does we really need this? 133 */ 134 IF_DRAIN(&ifp->if_snd); 135 136#if NBPFILTER > 0 137 bpfdetach(ifp); 138#endif 139 if_detach(ifp); 140 LIST_REMOVE(sc, sc_next); 141 free(sc, M_PFSYNC); 142} 143 144static int 145pfsync_clone_create(struct if_clone *ifc, int unit) 146{ 147 struct pfsync_softc *sc; 148 149 MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC, 150 M_WAITOK|M_ZERO); 151 152 sc->sc_count = 8; 153#if (__FreeBSD_version < 501113) 154 sc->sc_if.if_name = PFSYNCNAME; 155 sc->sc_if.if_unit = unit; 156#else 157 if_initname(&sc->sc_if, ifc->ifc_name, unit); 158#endif 159 sc->sc_if.if_ioctl = pfsyncioctl; 160 sc->sc_if.if_output = pfsyncoutput; 161 sc->sc_if.if_start = pfsyncstart; 162 sc->sc_if.if_type = IFT_PFSYNC; 163 sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen; 164 sc->sc_if.if_hdrlen = PFSYNC_HDRLEN; 165 sc->sc_if.if_baudrate = IF_Mbps(100); 166 sc->sc_if.if_softc = sc; 167 pfsync_setmtu(sc, MCLBYTES); 168 /* 169 * XXX 170 * The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE 171 * if Gaint lock is removed from the network stack. 172 */ 173 callout_init(&sc->sc_tmo, 0); 174 if_attach(&sc->sc_if); 175 176 LIST_INSERT_HEAD(&pfsync_list, sc, sc_next); 177#if NBPFILTER > 0 178 bpfattach(&sc->sc_if, DLT_PFSYNC, PFSYNC_HDRLEN); 179#endif 180 181 return (0); 182} 183#else /* !__FreeBSD__ */ 184void 185pfsyncattach(int npfsync) 186{ 187 struct ifnet *ifp; 188 189 pfsyncif.sc_mbuf = NULL; 190 pfsyncif.sc_ptr = NULL; 191 pfsyncif.sc_count = 8; 192 ifp = &pfsyncif.sc_if; 193 strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname); 194 ifp->if_softc = &pfsyncif; 195 ifp->if_ioctl = pfsyncioctl; 196 ifp->if_output = pfsyncoutput; 197 ifp->if_start = pfsyncstart; 198 ifp->if_type = IFT_PFSYNC; 199 ifp->if_snd.ifq_maxlen = ifqmaxlen; 200 ifp->if_hdrlen = PFSYNC_HDRLEN; 201 ifp->if_baudrate = IF_Mbps(100); 202 pfsync_setmtu(&pfsyncif, MCLBYTES); 203 timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif); 204 if_attach(ifp); 205 if_alloc_sadl(ifp); 206 207#if NBPFILTER > 0 208 bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN); 209#endif 210} 211#endif 212 213/* 214 * Start output on the pfsync interface. 215 */ 216void 217pfsyncstart(struct ifnet *ifp) 218{ 219 struct mbuf *m; 220#if defined(__FreeBSD__) && defined(ALTQ) 221 struct ifaltq *ifq; 222#else 223 struct ifqueue *ifq; 224#endif 225 int s; 226 227#ifdef __FreeBSD__ 228 ifq = &ifp->if_snd; 229#endif 230 for (;;) { 231 s = splimp(); 232#ifdef __FreeBSD__ 233 IF_LOCK(ifq); 234 _IF_DROP(ifq); 235 _IF_DEQUEUE(ifq, m); 236 IF_UNLOCK(ifq); 237#else 238 IF_DROP(&ifp->if_snd); 239 IF_DEQUEUE(&ifp->if_snd, m); 240#endif 241 splx(s); 242 243 if (m == NULL) 244 return; 245 else 246 m_freem(m); 247 } 248} 249 250int 251pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 252 struct rtentry *rt) 253{ 254 m_freem(m); 255 return (0); 256} 257 258/* ARGSUSED */ 259int 260pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 261{ 262 struct pfsync_softc *sc = ifp->if_softc; 263 struct ifreq *ifr = (struct ifreq *)data; 264 int s; 265 266 switch (cmd) { 267 case SIOCSIFADDR: 268 case SIOCAIFADDR: 269 case SIOCSIFDSTADDR: 270 case SIOCSIFFLAGS: 271 if (ifp->if_flags & IFF_UP) 272 ifp->if_flags |= IFF_RUNNING; 273 else 274 ifp->if_flags &= ~IFF_RUNNING; 275 break; 276 case SIOCSIFMTU: 277 if (ifr->ifr_mtu < PFSYNC_MINMTU) 278 return (EINVAL); 279 if (ifr->ifr_mtu > MCLBYTES) 280 ifr->ifr_mtu = MCLBYTES; 281 s = splnet(); 282 if (ifr->ifr_mtu < ifp->if_mtu) 283 pfsync_sendout(sc); 284 pfsync_setmtu(sc, ifr->ifr_mtu); 285 splx(s); 286 break; 287 default: 288 return (ENOTTY); 289 } 290 291 return (0); 292} 293 294void 295pfsync_setmtu(sc, mtu) 296 struct pfsync_softc *sc; 297 int mtu; 298{ 299 sc->sc_count = (mtu - sizeof(struct pfsync_header)) / 300 sizeof(struct pf_state); 301 sc->sc_if.if_mtu = sizeof(struct pfsync_header) + 302 sc->sc_count * sizeof(struct pf_state); 303} 304 305struct mbuf * 306pfsync_get_mbuf(sc, action) 307 struct pfsync_softc *sc; 308 u_int8_t action; 309{ 310#ifndef __FreeBSD__ 311 extern int hz; 312#endif 313 struct pfsync_header *h; 314 struct mbuf *m; 315 int len; 316 317 MGETHDR(m, M_DONTWAIT, MT_DATA); 318 if (m == NULL) { 319 sc->sc_if.if_oerrors++; 320 return (NULL); 321 } 322 323 len = sc->sc_if.if_mtu; 324 if (len > MHLEN) { 325 MCLGET(m, M_DONTWAIT); 326 if ((m->m_flags & M_EXT) == 0) { 327 m_free(m); 328 sc->sc_if.if_oerrors++; 329 return (NULL); 330 } 331 } 332 m->m_pkthdr.rcvif = NULL; 333 m->m_pkthdr.len = m->m_len = len; 334 335 h = mtod(m, struct pfsync_header *); 336 h->version = PFSYNC_VERSION; 337 h->af = 0; 338 h->count = 0; 339 h->action = action; 340 341 sc->sc_mbuf = m; 342 sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN); 343#ifdef __FreeBSD__ 344 callout_reset(&sc->sc_tmo, hz, pfsync_timeout, 345 LIST_FIRST(&pfsync_list)); 346#else 347 timeout_add(&sc->sc_tmo, hz); 348#endif 349 350 return (m); 351} 352 353/* 354 * XXX: This function should be called with PF_LOCK held as it references 355 * pf_state. 356 */ 357int 358pfsync_pack_state(action, st) 359 u_int8_t action; 360 struct pf_state *st; 361{ 362#ifdef __FreeBSD__ 363 struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); 364#else 365 extern struct timeval time; 366 struct ifnet *ifp = &pfsyncif.sc_if; 367 struct pfsync_softc *sc = ifp->if_softc; 368#endif 369 struct pfsync_header *h; 370 struct pf_state *sp; 371 struct pf_rule *r = st->rule.ptr; 372 struct mbuf *m; 373 u_long secs; 374 int s, ret; 375 376 if (action >= PFSYNC_ACT_MAX) 377 return (EINVAL); 378 379#ifdef __FreeBSD__ 380 /* 381 * XXX 382 * If we need to check mutex owned, PF_LOCK should be 383 * declared in pflog.ko. 384 * 385 * PF_LOCK_ASSERT(); 386 */ 387 KASSERT((!LIST_EMPTY(&pfsync_list)), ("pfsync: no interface")); 388#endif 389 s = splnet(); 390 m = sc->sc_mbuf; 391 if (m == NULL) { 392 if ((m = pfsync_get_mbuf(sc, action)) == NULL) { 393 splx(s); 394 return (ENOMEM); 395 } 396 h = mtod(m, struct pfsync_header *); 397 } else { 398 h = mtod(m, struct pfsync_header *); 399 if (h->action != action) { 400 pfsync_sendout(sc); 401 if ((m = pfsync_get_mbuf(sc, action)) == NULL) { 402 splx(s); 403 return (ENOMEM); 404 } 405 h = mtod(m, struct pfsync_header *); 406 } 407 } 408 409 sp = sc->sc_ptr++; 410 h->count++; 411 bzero(sp, sizeof(*sp)); 412 413 bcopy(&st->lan, &sp->lan, sizeof(sp->lan)); 414 bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy)); 415 bcopy(&st->ext, &sp->ext, sizeof(sp->ext)); 416 417 pf_state_peer_hton(&st->src, &sp->src); 418 pf_state_peer_hton(&st->dst, &sp->dst); 419 420 bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); 421#ifdef __FreeBSD__ 422 secs = time_second; 423#else 424 secs = time.tv_sec; 425#endif 426 sp->creation = htonl(secs - st->creation); 427 if (st->expire <= secs) 428 sp->expire = htonl(0); 429 else 430 sp->expire = htonl(st->expire - secs); 431 sp->packets[0] = htonl(st->packets[0]); 432 sp->packets[1] = htonl(st->packets[1]); 433 sp->bytes[0] = htonl(st->bytes[0]); 434 sp->bytes[1] = htonl(st->bytes[1]); 435 if (r == NULL) 436 sp->rule.nr = htonl(-1); 437 else 438 sp->rule.nr = htonl(r->nr); 439 sp->af = st->af; 440 sp->proto = st->proto; 441 sp->direction = st->direction; 442 sp->log = st->log; 443 sp->allow_opts = st->allow_opts; 444 445 ret = 0; 446 if (h->count == sc->sc_count) 447 ret = pfsync_sendout(sc); 448 449 splx(s); 450 return (0); 451} 452 453int 454pfsync_clear_state(st) 455 struct pf_state *st; 456{ 457#ifdef __FreeBSD__ 458 struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); 459#else 460 struct ifnet *ifp = &pfsyncif.sc_if; 461 struct pfsync_softc *sc = ifp->if_softc; 462#endif 463 struct mbuf *m = sc->sc_mbuf; 464 int s, ret; 465 466 s = splnet(); 467 if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) { 468 splx(s); 469 return (ENOMEM); 470 } 471 472 ret = (pfsync_sendout(sc)); 473 splx(s); 474 return (ret); 475} 476 477void 478pfsync_timeout(void *v) 479{ 480 struct pfsync_softc *sc = v; 481 int s; 482 483 /* We don't need PF_LOCK/PF_UNLOCK here! */ 484 s = splnet(); 485 pfsync_sendout(sc); 486 splx(s); 487} 488 489int 490pfsync_sendout(sc) 491 struct pfsync_softc *sc; 492{ 493 struct ifnet *ifp = &sc->sc_if; 494 struct mbuf *m = sc->sc_mbuf; 495 496#ifdef __FreeBSD__ 497 callout_stop(&sc->sc_tmo); 498#else 499 timeout_del(&sc->sc_tmo); 500#endif 501 sc->sc_mbuf = NULL; 502 sc->sc_ptr = NULL; 503 504#ifdef __FreeBSD__ 505 KASSERT(m != NULL, ("pfsync_sendout: null mbuf")); 506#endif 507#if NBPFILTER > 0 508 if (ifp->if_bpf) 509 bpf_mtap(ifp->if_bpf, m); 510#endif 511 512 m_freem(m); 513 514 return (0); 515} 516 517 518#ifdef __FreeBSD__ 519static int 520pfsync_modevent(module_t mod, int type, void *data) 521{ 522 int error = 0; 523 524 switch (type) { 525 case MOD_LOAD: 526 LIST_INIT(&pfsync_list); 527 if_clone_attach(&pfsync_cloner); 528 break; 529 530 case MOD_UNLOAD: 531 if_clone_detach(&pfsync_cloner); 532 while (!LIST_EMPTY(&pfsync_list)) 533 pfsync_clone_destroy( 534 &LIST_FIRST(&pfsync_list)->sc_if); 535 break; 536 537 default: 538 error = EINVAL; 539 break; 540 } 541 542 return error; 543} 544 545static moduledata_t pfsync_mod = { 546 "pfsync", 547 pfsync_modevent, 548 0 549}; 550 551#define PFSYNC_MODVER 1 552 553DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 554MODULE_VERSION(pfsync, PFSYNC_MODVER); 555#endif /* __FreeBSD__ */ 556