if_pfsync.c revision 126261
1/* $FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 126261 2004-02-26 02:34:12Z mlaier $ */ 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#if defined(__FreeBSD__) && __FreeBSD__ >= 5 31#include "opt_inet.h" 32#include "opt_inet6.h" 33#endif 34 35#if !defined(__FreeBSD__) 36#include "bpfilter.h" 37#include "pfsync.h" 38#elif __FreeBSD__ >= 5 39#include "opt_bpf.h" 40#define NBPFILTER DEV_BPF 41#include "opt_pf.h" 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#if defined(__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#if defined(__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#if !defined(__FreeBSD__) 94struct pfsync_softc pfsyncif; 95#endif 96 97#if defined(__FreeBSD__) 98void pfsync_clone_destroy(struct ifnet *); 99int 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#if !defined(__FreeBSD__) 114extern int ifqmaxlen; 115#endif 116 117#if defined(__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 123void 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#endif /* __FreeBSD__ */ 144 145#if defined(__FreeBSD__) 146int 147pfsync_clone_create(struct if_clone *ifc, int unit) 148{ 149 struct pfsync_softc *sc; 150 151 MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC, 152 M_WAITOK|M_ZERO); 153 154 sc->sc_count = 8; 155#if (__FreeBSD_version < 501113) 156 sc->sc_if.if_name = PFSYNCNAME; 157 sc->sc_if.if_unit = unit; 158#else 159 if_initname(&sc->sc_if, ifc->ifc_name, unit); 160#endif 161 sc->sc_if.if_ioctl = pfsyncioctl; 162 sc->sc_if.if_output = pfsyncoutput; 163 sc->sc_if.if_start = pfsyncstart; 164 sc->sc_if.if_type = IFT_PFSYNC; 165 sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen; 166 sc->sc_if.if_hdrlen = PFSYNC_HDRLEN; 167 sc->sc_if.if_baudrate = IF_Mbps(100); 168 sc->sc_if.if_softc = sc; 169 pfsync_setmtu(sc, MCLBYTES); 170 /* 171 * XXX 172 * The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE 173 * if Gaint lock is removed from the network stack. 174 */ 175 callout_init(&sc->sc_tmo, 0); 176 if_attach(&sc->sc_if); 177 178 LIST_INSERT_HEAD(&pfsync_list, sc, sc_next); 179#if NBPFILTER > 0 180 bpfattach(&sc->sc_if, DLT_PFSYNC, PFSYNC_HDRLEN); 181#endif 182 183 return (0); 184} 185#else /* !__FreeBSD__ */ 186void 187pfsyncattach(int npfsync) 188{ 189 struct ifnet *ifp; 190 191 pfsyncif.sc_mbuf = NULL; 192 pfsyncif.sc_ptr = NULL; 193 pfsyncif.sc_count = 8; 194 ifp = &pfsyncif.sc_if; 195 strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname); 196 ifp->if_softc = &pfsyncif; 197 ifp->if_ioctl = pfsyncioctl; 198 ifp->if_output = pfsyncoutput; 199 ifp->if_start = pfsyncstart; 200 ifp->if_type = IFT_PFSYNC; 201 ifp->if_snd.ifq_maxlen = ifqmaxlen; 202 ifp->if_hdrlen = PFSYNC_HDRLEN; 203 ifp->if_baudrate = IF_Mbps(100); 204 pfsync_setmtu(&pfsyncif, MCLBYTES); 205 timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif); 206 if_attach(ifp); 207 if_alloc_sadl(ifp); 208 209#if NBPFILTER > 0 210 bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN); 211#endif 212} 213#endif 214 215/* 216 * Start output on the pfsync interface. 217 */ 218void 219pfsyncstart(struct ifnet *ifp) 220{ 221 struct mbuf *m; 222#if defined(__FreeBSD__) && defined(ALTQ) 223 struct ifaltq *ifq; 224#else 225 struct ifqueue *ifq; 226#endif 227 int s; 228 229#if defined(__FreeBSD__) 230 ifq = &ifp->if_snd; 231#endif 232 for (;;) { 233 s = splimp(); 234#if defined(__FreeBSD__) 235 IF_LOCK(ifq); 236 _IF_DROP(ifq); 237 _IF_DEQUEUE(ifq, m); 238 IF_UNLOCK(ifq); 239#else 240 IF_DROP(&ifp->if_snd); 241 IF_DEQUEUE(&ifp->if_snd, m); 242#endif 243 splx(s); 244 245 if (m == NULL) 246 return; 247 else 248 m_freem(m); 249 } 250} 251 252int 253pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 254 struct rtentry *rt) 255{ 256 m_freem(m); 257 return (0); 258} 259 260/* ARGSUSED */ 261int 262pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 263{ 264 struct pfsync_softc *sc = ifp->if_softc; 265 struct ifreq *ifr = (struct ifreq *)data; 266 int s; 267 268 switch (cmd) { 269 case SIOCSIFADDR: 270 case SIOCAIFADDR: 271 case SIOCSIFDSTADDR: 272 case SIOCSIFFLAGS: 273 if (ifp->if_flags & IFF_UP) 274 ifp->if_flags |= IFF_RUNNING; 275 else 276 ifp->if_flags &= ~IFF_RUNNING; 277 break; 278 case SIOCSIFMTU: 279 if (ifr->ifr_mtu < PFSYNC_MINMTU) 280 return (EINVAL); 281 if (ifr->ifr_mtu > MCLBYTES) 282 ifr->ifr_mtu = MCLBYTES; 283 s = splnet(); 284 if (ifr->ifr_mtu < ifp->if_mtu) 285 pfsync_sendout(sc); 286 pfsync_setmtu(sc, ifr->ifr_mtu); 287 splx(s); 288 break; 289 default: 290 return (ENOTTY); 291 } 292 293 return (0); 294} 295 296void 297pfsync_setmtu(sc, mtu) 298 struct pfsync_softc *sc; 299 int mtu; 300{ 301 sc->sc_count = (mtu - sizeof(struct pfsync_header)) / 302 sizeof(struct pf_state); 303 sc->sc_if.if_mtu = sizeof(struct pfsync_header) + 304 sc->sc_count * sizeof(struct pf_state); 305} 306 307struct mbuf * 308pfsync_get_mbuf(sc, action) 309 struct pfsync_softc *sc; 310 u_int8_t action; 311{ 312#if !defined(__FreeBSD__) 313 extern int hz; 314#endif 315 struct pfsync_header *h; 316 struct mbuf *m; 317 int len; 318 319 MGETHDR(m, M_DONTWAIT, MT_DATA); 320 if (m == NULL) { 321 sc->sc_if.if_oerrors++; 322 return (NULL); 323 } 324 325 len = sc->sc_if.if_mtu; 326 if (len > MHLEN) { 327 MCLGET(m, M_DONTWAIT); 328 if ((m->m_flags & M_EXT) == 0) { 329 m_free(m); 330 sc->sc_if.if_oerrors++; 331 return (NULL); 332 } 333 } 334 m->m_pkthdr.rcvif = NULL; 335 m->m_pkthdr.len = m->m_len = len; 336 337 h = mtod(m, struct pfsync_header *); 338 h->version = PFSYNC_VERSION; 339 h->af = 0; 340 h->count = 0; 341 h->action = action; 342 343 sc->sc_mbuf = m; 344 sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN); 345#if defined(__FreeBSD__) 346 callout_reset(&sc->sc_tmo, hz, pfsync_timeout, 347 LIST_FIRST(&pfsync_list)); 348#else 349 timeout_add(&sc->sc_tmo, hz); 350#endif 351 352 return (m); 353} 354 355/* 356 * XXX: This function should be called with PF_LOCK held as it references 357 * pf_state. 358 */ 359int 360pfsync_pack_state(action, st) 361 u_int8_t action; 362 struct pf_state *st; 363{ 364#if defined(__FreeBSD__) 365 struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); 366#else 367 extern struct timeval time; 368 struct ifnet *ifp = &pfsyncif.sc_if; 369 struct pfsync_softc *sc = ifp->if_softc; 370#endif 371 struct pfsync_header *h; 372 struct pf_state *sp; 373 struct pf_rule *r = st->rule.ptr; 374 struct mbuf *m; 375 u_long secs; 376 int s, ret; 377 378 if (action >= PFSYNC_ACT_MAX) 379 return (EINVAL); 380 381#if defined(__FreeBSD__) 382 /* 383 * XXX 384 * If we need to check mutex owned, PF_LOCK should be 385 * declared in pflog.ko. :-( 386 * 387 * PF_LOCK_ASSERT(); 388 */ 389 KASSERT((!LIST_EMPTY(&pfsync_list)), ("pfsync: no interface")); 390#endif 391 s = splnet(); 392 m = sc->sc_mbuf; 393 if (m == NULL) { 394 if ((m = pfsync_get_mbuf(sc, action)) == NULL) { 395 splx(s); 396 return (ENOMEM); 397 } 398 h = mtod(m, struct pfsync_header *); 399 } else { 400 h = mtod(m, struct pfsync_header *); 401 if (h->action != action) { 402 pfsync_sendout(sc); 403 if ((m = pfsync_get_mbuf(sc, action)) == NULL) { 404 splx(s); 405 return (ENOMEM); 406 } 407 h = mtod(m, struct pfsync_header *); 408 } 409 } 410 411 sp = sc->sc_ptr++; 412 h->count++; 413 bzero(sp, sizeof(*sp)); 414 415 bcopy(&st->lan, &sp->lan, sizeof(sp->lan)); 416 bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy)); 417 bcopy(&st->ext, &sp->ext, sizeof(sp->ext)); 418 419 pf_state_peer_hton(&st->src, &sp->src); 420 pf_state_peer_hton(&st->dst, &sp->dst); 421 422 bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); 423#if defined(__FreeBSD__) 424 secs = time_second; 425#else 426 secs = time.tv_sec; 427#endif 428 sp->creation = htonl(secs - st->creation); 429 if (st->expire <= secs) 430 sp->expire = htonl(0); 431 else 432 sp->expire = htonl(st->expire - secs); 433 sp->packets[0] = htonl(st->packets[0]); 434 sp->packets[1] = htonl(st->packets[1]); 435 sp->bytes[0] = htonl(st->bytes[0]); 436 sp->bytes[1] = htonl(st->bytes[1]); 437 if (r == NULL) 438 sp->rule.nr = htonl(-1); 439 else 440 sp->rule.nr = htonl(r->nr); 441 sp->af = st->af; 442 sp->proto = st->proto; 443 sp->direction = st->direction; 444 sp->log = st->log; 445 sp->allow_opts = st->allow_opts; 446 447 ret = 0; 448 if (h->count == sc->sc_count) 449 ret = pfsync_sendout(sc); 450 451 splx(s); 452 return (0); 453} 454 455int 456pfsync_clear_state(st) 457 struct pf_state *st; 458{ 459#if defined(__FreeBSD__) 460 struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); 461#else 462 struct ifnet *ifp = &pfsyncif.sc_if; 463 struct pfsync_softc *sc = ifp->if_softc; 464#endif 465 struct mbuf *m = sc->sc_mbuf; 466 int s, ret; 467 468 s = splnet(); 469 if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) { 470 splx(s); 471 return (ENOMEM); 472 } 473 474 ret = (pfsync_sendout(sc)); 475 splx(s); 476 return (ret); 477} 478 479void 480pfsync_timeout(void *v) 481{ 482 struct pfsync_softc *sc = v; 483 int s; 484 485 /* We don't need PF_LOCK/PF_UNLOCK here! */ 486 s = splnet(); 487 pfsync_sendout(sc); 488 splx(s); 489} 490 491int 492pfsync_sendout(sc) 493 struct pfsync_softc *sc; 494{ 495 struct ifnet *ifp = &sc->sc_if; 496 struct mbuf *m = sc->sc_mbuf; 497 498#if defined(__FreeBSD__) 499 callout_stop(&sc->sc_tmo); 500#else 501 timeout_del(&sc->sc_tmo); 502#endif 503 sc->sc_mbuf = NULL; 504 sc->sc_ptr = NULL; 505 506#if defined(__FreeBSD__) 507 KASSERT(m != NULL, ("pfsync_sendout: null mbuf")); 508#endif 509#if NBPFILTER > 0 510 if (ifp->if_bpf) 511 bpf_mtap(ifp->if_bpf, m); 512#endif 513 514 m_freem(m); 515 516 return (0); 517} 518 519 520#if defined(__FreeBSD__) 521static int 522pfsync_modevent(module_t mod, int type, void *data) 523{ 524 int error = 0; 525 526 switch (type) { 527 case MOD_LOAD: 528 LIST_INIT(&pfsync_list); 529 if_clone_attach(&pfsync_cloner); 530 printf("pfsync: $Name: $\n"); 531 break; 532 533 case MOD_UNLOAD: 534 if_clone_detach(&pfsync_cloner); 535 while (!LIST_EMPTY(&pfsync_list)) 536 pfsync_clone_destroy( 537 &LIST_FIRST(&pfsync_list)->sc_if); 538 break; 539 540 default: 541 error = EINVAL; 542 break; 543 } 544 545 return error; 546} 547 548static moduledata_t pfsync_mod = { 549 "pfsync", 550 pfsync_modevent, 551 0 552}; 553 554#define PFSYNC_MODVER 1 555 556DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 557MODULE_VERSION(pfsync, PFSYNC_MODVER); 558#endif /* __FreeBSD__ */ 559