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