Index: stable/5/sys/net/if_bridge.c =================================================================== --- stable/5/sys/net/if_bridge.c (nonexistent) +++ stable/5/sys/net/if_bridge.c (revision 150838) @@ -0,0 +1,2656 @@ +/* $NetBSD: if_bridge.c,v 1.31 2005/06/01 19:45:34 jdc Exp $ */ + +/* + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp + */ + +/* + * Network interface bridge support. + * + * TODO: + * + * - Currently only supports Ethernet-like interfaces (Ethernet, + * 802.11, VLANs on Ethernet, etc.) Figure out a nice way + * to bridge other types of interfaces (FDDI-FDDI, and maybe + * consider heterogenous bridges). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include /* for net/if.h */ +#include +#include /* string functions */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include /* for struct arpcom */ +#include +#include +#include +#include +#ifdef INET6 +#include +#include +#endif +#include +#include /* for struct arpcom */ +#include +#include + +#include +#include +#include + +/* + * Size of the route hash table. Must be a power of two. + */ +#ifndef BRIDGE_RTHASH_SIZE +#define BRIDGE_RTHASH_SIZE 1024 +#endif + +#define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) + +/* + * Maximum number of addresses to cache. + */ +#ifndef BRIDGE_RTABLE_MAX +#define BRIDGE_RTABLE_MAX 100 +#endif + +/* + * Spanning tree defaults. + */ +#define BSTP_DEFAULT_MAX_AGE (20 * 256) +#define BSTP_DEFAULT_HELLO_TIME (2 * 256) +#define BSTP_DEFAULT_FORWARD_DELAY (15 * 256) +#define BSTP_DEFAULT_HOLD_TIME (1 * 256) +#define BSTP_DEFAULT_BRIDGE_PRIORITY 0x8000 +#define BSTP_DEFAULT_PORT_PRIORITY 0x80 +#define BSTP_DEFAULT_PATH_COST 55 + +/* + * Timeout (in seconds) for entries learned dynamically. + */ +#ifndef BRIDGE_RTABLE_TIMEOUT +#define BRIDGE_RTABLE_TIMEOUT (20 * 60) /* same as ARP */ +#endif + +/* + * Number of seconds between walks of the route list. + */ +#ifndef BRIDGE_RTABLE_PRUNE_PERIOD +#define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) +#endif + +static struct mtx bridge_list_mtx; + +extern struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); +extern int (*bridge_output_p)(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); + +int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; + +uma_zone_t bridge_rtnode_zone; + +int bridge_clone_create(struct if_clone *, int); +void bridge_clone_destroy(struct ifnet *); + +int bridge_ioctl(struct ifnet *, u_long, caddr_t); +static void bridge_init(void *); +void bridge_stop(struct ifnet *, int); +void bridge_start(struct ifnet *); + +void bridge_forward(struct bridge_softc *, struct mbuf *m); + +void bridge_timer(void *); + +void bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *, + int); + +int bridge_rtupdate(struct bridge_softc *, const uint8_t *, + struct ifnet *, int, uint8_t); +struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *); +void bridge_rttrim(struct bridge_softc *); +void bridge_rtage(struct bridge_softc *); +void bridge_rtflush(struct bridge_softc *, int); +int bridge_rtdaddr(struct bridge_softc *, const uint8_t *); + +int bridge_rtable_init(struct bridge_softc *); +void bridge_rtable_fini(struct bridge_softc *); + +struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *, + const uint8_t *); +int bridge_rtnode_insert(struct bridge_softc *, struct bridge_rtnode *); +void bridge_rtnode_destroy(struct bridge_softc *, struct bridge_rtnode *); + +struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, + const char *name); +struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, + struct ifnet *ifp); +void bridge_delete_member(struct bridge_softc *, struct bridge_iflist *); + +int bridge_ioctl_add(struct bridge_softc *, void *); +int bridge_ioctl_del(struct bridge_softc *, void *); +int bridge_ioctl_gifflags(struct bridge_softc *, void *); +int bridge_ioctl_sifflags(struct bridge_softc *, void *); +int bridge_ioctl_scache(struct bridge_softc *, void *); +int bridge_ioctl_gcache(struct bridge_softc *, void *); +int bridge_ioctl_gifs(struct bridge_softc *, void *); +int bridge_ioctl_rts(struct bridge_softc *, void *); +int bridge_ioctl_saddr(struct bridge_softc *, void *); +int bridge_ioctl_sto(struct bridge_softc *, void *); +int bridge_ioctl_gto(struct bridge_softc *, void *); +int bridge_ioctl_daddr(struct bridge_softc *, void *); +int bridge_ioctl_flush(struct bridge_softc *, void *); +int bridge_ioctl_gpri(struct bridge_softc *, void *); +int bridge_ioctl_spri(struct bridge_softc *, void *); +int bridge_ioctl_ght(struct bridge_softc *, void *); +int bridge_ioctl_sht(struct bridge_softc *, void *); +int bridge_ioctl_gfd(struct bridge_softc *, void *); +int bridge_ioctl_sfd(struct bridge_softc *, void *); +int bridge_ioctl_gma(struct bridge_softc *, void *); +int bridge_ioctl_sma(struct bridge_softc *, void *); +int bridge_ioctl_sifprio(struct bridge_softc *, void *); +int bridge_ioctl_sifcost(struct bridge_softc *, void *); +static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, int); +static int bridge_ip_checkbasic(struct mbuf **mp); +# ifdef INET6 +static int bridge_ip6_checkbasic(struct mbuf **mp); +# endif /* INET6 */ + +SYSCTL_DECL(_net_link); +SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge"); + +static int pfil_bridge = 1; /* run pfil hooks on the bridge interface */ +static int pfil_member = 1; /* run pfil hooks on the member interface */ +static int pfil_ipfw = 0; /* layer2 filter with ipfw */ +SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RW, + &pfil_bridge, 0, "Packet filter on the bridge interface"); +SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RW, + &pfil_member, 0, "Packet filter on the member interface"); + +struct bridge_control { + int (*bc_func)(struct bridge_softc *, void *); + int bc_argsize; + int bc_flags; +}; + +#define BC_F_COPYIN 0x01 /* copy arguments in */ +#define BC_F_COPYOUT 0x02 /* copy arguments out */ +#define BC_F_SUSER 0x04 /* do super-user check */ + +const struct bridge_control bridge_control_table[] = { + { bridge_ioctl_add, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_del, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gifflags, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_COPYOUT }, + { bridge_ioctl_sifflags, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_scache, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_gcache, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + + { bridge_ioctl_gifs, sizeof(struct ifbifconf), + BC_F_COPYIN|BC_F_COPYOUT }, + { bridge_ioctl_rts, sizeof(struct ifbaconf), + BC_F_COPYIN|BC_F_COPYOUT }, + + { bridge_ioctl_saddr, sizeof(struct ifbareq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_sto, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_gto, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + + { bridge_ioctl_daddr, sizeof(struct ifbareq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_flush, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gpri, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_spri, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_ght, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_sht, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gfd, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_sfd, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gma, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_sma, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_sifprio, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_sifcost, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, +}; +const int bridge_control_table_size = + sizeof(bridge_control_table) / sizeof(bridge_control_table[0]); + +LIST_HEAD(, bridge_softc) bridge_list; + +IFC_SIMPLE_DECLARE(bridge, 0); + +static int +bridge_modevent(module_t mod, int type, void *data) +{ + + switch (type) { + case MOD_LOAD: + mtx_init(&bridge_list_mtx, "if_bridge list", NULL, MTX_DEF); + if_clone_attach(&bridge_cloner); + bridge_rtnode_zone = uma_zcreate("bridge_rtnode", + sizeof(struct bridge_rtnode), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + LIST_INIT(&bridge_list); + bridge_input_p = bridge_input; + bridge_output_p = bridge_output; + bridge_dn_p = bridge_dummynet; + bstp_linkstate_p = bstp_linkstate; + break; + case MOD_UNLOAD: + if_clone_detach(&bridge_cloner); + while (!LIST_EMPTY(&bridge_list)) + bridge_clone_destroy(LIST_FIRST(&bridge_list)->sc_ifp); + uma_zdestroy(bridge_rtnode_zone); + bridge_input_p = NULL; + bridge_output_p = NULL; + bridge_dn_p = NULL; + bstp_linkstate_p = NULL; + mtx_destroy(&bridge_list_mtx); + break; + default: + return EOPNOTSUPP; + } + return 0; +} + +static moduledata_t bridge_mod = { + "if_bridge", + bridge_modevent, + 0 +}; + +DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); + +/* + * handler for net.link.bridge.pfil_ipfw + */ +static int +sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS) +{ + int enable = pfil_ipfw; + int error; + + error = sysctl_handle_int(oidp, &enable, 0, req); + enable = (enable) ? 1 : 0; + + if (enable != pfil_ipfw) { + pfil_ipfw = enable; + + /* + * Disable pfil so that ipfw doesnt run twice, if the user really wants + * both then they can re-enable pfil_bridge and/or pfil_member. + */ + if (pfil_ipfw) { + pfil_bridge = 0; + pfil_member = 0; + } + } + + return error; +} +SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, CTLTYPE_INT|CTLFLAG_RW, + &pfil_ipfw, 0, &sysctl_pfil_ipfw, "I", "Layer2 filter with IPFW"); + +/* + * bridge_clone_create: + * + * Create a new bridge instance. + */ +int +bridge_clone_create(struct if_clone *ifc, int unit) +{ + struct bridge_softc *sc; + struct ifnet *ifp; + u_char eaddr[6]; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); + BRIDGE_LOCK_INIT(sc); + ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + free(sc, M_DEVBUF); + return (ENOSPC); + } + + sc->sc_brtmax = BRIDGE_RTABLE_MAX; + sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; + sc->sc_bridge_max_age = BSTP_DEFAULT_MAX_AGE; + sc->sc_bridge_hello_time = BSTP_DEFAULT_HELLO_TIME; + sc->sc_bridge_forward_delay = BSTP_DEFAULT_FORWARD_DELAY; + sc->sc_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY; + sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME; + + /* Initialize our routing table. */ + bridge_rtable_init(sc); + + callout_init_mtx(&sc->sc_brcallout, &sc->sc_mtx, 0); + callout_init_mtx(&sc->sc_bstpcallout, &sc->sc_mtx, 0); + + LIST_INIT(&sc->sc_iflist); + + ifp->if_softc = sc; + if_initname(ifp, ifc->ifc_name, unit); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_MULTICAST; + ifp->if_ioctl = bridge_ioctl; + ifp->if_output = bridge_output; + ifp->if_start = bridge_start; + ifp->if_init = bridge_init; + ifp->if_type = IFT_BRIDGE; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + ifp->if_hdrlen = ETHER_HDR_LEN; + + /* + * Generate a random ethernet address and use the private AC:DE:48 + * OUI code. + */ + arc4rand(eaddr, ETHER_ADDR_LEN, 1); + eaddr[0] = 0xAC; + eaddr[1] = 0xDE; + eaddr[2] = 0x48; + + ether_ifattach(ifp, eaddr); + /* Now undo some of the damage... */ + ifp->if_baudrate = 0; + ifp->if_type = IFT_BRIDGE; + + mtx_lock(&bridge_list_mtx); + LIST_INSERT_HEAD(&bridge_list, sc, sc_list); + mtx_unlock(&bridge_list_mtx); + + return (0); +} + +/* + * bridge_clone_destroy: + * + * Destroy a bridge instance. + */ +void +bridge_clone_destroy(struct ifnet *ifp) +{ + struct bridge_softc *sc = ifp->if_softc; + struct bridge_iflist *bif; + + BRIDGE_LOCK(sc); + + bridge_stop(ifp, 1); + ifp->if_flags &= ~IFF_UP; + + while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL) + bridge_delete_member(sc, bif); + + BRIDGE_UNLOCK(sc); + + callout_drain(&sc->sc_brcallout); + callout_drain(&sc->sc_bstpcallout); + + mtx_lock(&bridge_list_mtx); + LIST_REMOVE(sc, sc_list); + mtx_unlock(&bridge_list_mtx); + + ether_ifdetach(ifp); + if_free_type(ifp, IFT_ETHER); + + /* Tear down the routing table. */ + bridge_rtable_fini(sc); + + BRIDGE_LOCK_DESTROY(sc); + free(sc, M_DEVBUF); +} + +/* + * bridge_ioctl: + * + * Handle a control request from the operator. + */ +int +bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct bridge_softc *sc = ifp->if_softc; + struct thread *td = curthread; + union { + struct ifbreq ifbreq; + struct ifbifconf ifbifconf; + struct ifbareq ifbareq; + struct ifbaconf ifbaconf; + struct ifbrparam ifbrparam; + } args; + struct ifdrv *ifd = (struct ifdrv *) data; + const struct bridge_control *bc; + int error = 0; + + BRIDGE_LOCK(sc); + + switch (cmd) { + + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCGDRVSPEC: + case SIOCSDRVSPEC: + if (ifd->ifd_cmd >= bridge_control_table_size) { + error = EINVAL; + break; + } + bc = &bridge_control_table[ifd->ifd_cmd]; + + if (cmd == SIOCGDRVSPEC && + (bc->bc_flags & BC_F_COPYOUT) == 0) { + error = EINVAL; + break; + } + else if (cmd == SIOCSDRVSPEC && + (bc->bc_flags & BC_F_COPYOUT) != 0) { + error = EINVAL; + break; + } + + if (bc->bc_flags & BC_F_SUSER) { + error = suser(td); + if (error) + break; + } + + if (ifd->ifd_len != bc->bc_argsize || + ifd->ifd_len > sizeof(args)) { + error = EINVAL; + break; + } + + if (bc->bc_flags & BC_F_COPYIN) { + error = copyin(ifd->ifd_data, &args, ifd->ifd_len); + if (error) + break; + } + + error = (*bc->bc_func)(sc, &args); + if (error) + break; + + if (bc->bc_flags & BC_F_COPYOUT) + error = copyout(&args, ifd->ifd_data, ifd->ifd_len); + + break; + + case SIOCSIFFLAGS: + if (!(ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING)) { + /* + * If interface is marked down and it is running, + * then stop and disable it. + */ + bridge_stop(ifp, 1); + } else if ((ifp->if_flags & IFF_UP) && + !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + BRIDGE_UNLOCK(sc); + (*ifp->if_init)(sc); + } + break; + + case SIOCSIFMTU: + /* Do not allow the MTU to be changed on the bridge */ + error = EINVAL; + break; + + default: + /* + * drop the lock as ether_ioctl() will call bridge_start() and + * cause the lock to be recursed. + */ + BRIDGE_UNLOCK(sc); + error = ether_ioctl(ifp, cmd, data); + break; + } + + if (BRIDGE_LOCKED(sc)) + BRIDGE_UNLOCK(sc); + + return (error); +} + +/* + * bridge_lookup_member: + * + * Lookup a bridge member interface. + */ +struct bridge_iflist * +bridge_lookup_member(struct bridge_softc *sc, const char *name) +{ + struct bridge_iflist *bif; + struct ifnet *ifp; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + ifp = bif->bif_ifp; + if (strcmp(ifp->if_xname, name) == 0) + return (bif); + } + + return (NULL); +} + +/* + * bridge_lookup_member_if: + * + * Lookup a bridge member interface by ifnet*. + */ +struct bridge_iflist * +bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) +{ + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (bif->bif_ifp == member_ifp) + return (bif); + } + + return (NULL); +} + +/* + * bridge_delete_member: + * + * Delete the specified member interface. + */ +void +bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + struct ifnet *ifs = bif->bif_ifp; + + BRIDGE_LOCK_ASSERT(sc); + + switch (ifs->if_type) { + case IFT_ETHER: + case IFT_L2VLAN: + /* + * Take the interface out of promiscuous mode. + */ + (void) ifpromisc(ifs, 0); + break; + + default: +#ifdef DIAGNOSTIC + panic("bridge_delete_member: impossible"); +#endif + break; + } + + ifs->if_bridge = NULL; + BRIDGE_XLOCK(sc); + LIST_REMOVE(bif, bif_next); + BRIDGE_XDROP(sc); + + bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); + + free(bif, M_DEVBUF); + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); +} + +int +bridge_ioctl_add(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif = NULL; + struct ifnet *ifs; + int error = 0; + + BRIDGE_LOCK_ASSERT(sc); + + ifs = ifunit(req->ifbr_ifsname); + if (ifs == NULL) + return (ENOENT); + + /* Allow the first member to define the MTU */ + if (LIST_EMPTY(&sc->sc_iflist)) + sc->sc_ifp->if_mtu = ifs->if_mtu; + else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { + if_printf(sc->sc_ifp, "invalid MTU for %s\n", ifs->if_xname); + return (EINVAL); + } + + if (ifs->if_bridge == sc) + return (EEXIST); + + if (ifs->if_bridge != NULL) + return (EBUSY); + + bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT); + if (bif == NULL) + return (ENOMEM); + + switch (ifs->if_type) { + case IFT_ETHER: + case IFT_L2VLAN: + /* + * Place the interface into promiscuous mode. + */ + error = ifpromisc(ifs, 1); + if (error) + goto out; + break; + + default: + error = EINVAL; + goto out; + } + + bif->bif_ifp = ifs; + bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; + bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY; + bif->bif_path_cost = BSTP_DEFAULT_PATH_COST; + + ifs->if_bridge = sc; + /* + * XXX: XLOCK HERE!?! + * + * NOTE: insert_***HEAD*** should be safe for the traversals. + */ + LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + else + bstp_stop(sc); + + out: + if (error) { + if (bif != NULL) + free(bif, M_DEVBUF); + } + return (error); +} + +int +bridge_ioctl_del(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + bridge_delete_member(sc, bif); + + return (0); +} + +int +bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + req->ifbr_ifsflags = bif->bif_flags; + req->ifbr_state = bif->bif_state; + req->ifbr_priority = bif->bif_priority; + req->ifbr_path_cost = bif->bif_path_cost; + req->ifbr_portno = bif->bif_ifp->if_index & 0xff; + + return (0); +} + +int +bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + if (req->ifbr_ifsflags & IFBIF_STP) { + switch (bif->bif_ifp->if_type) { + case IFT_ETHER: + /* These can do spanning tree. */ + break; + + default: + /* Nothing else can. */ + return (EINVAL); + } + } + + bif->bif_flags = req->ifbr_ifsflags; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_scache(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + sc->sc_brtmax = param->ifbrp_csize; + bridge_rttrim(sc); + + return (0); +} + +int +bridge_ioctl_gcache(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_csize = sc->sc_brtmax; + + return (0); +} + +int +bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) +{ + struct ifbifconf *bifc = arg; + struct bridge_iflist *bif; + struct ifbreq breq; + int count, len, error = 0; + + BRIDGE_LOCK_ASSERT(sc); + + count = 0; + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) + count++; + + if (bifc->ifbic_len == 0) { + bifc->ifbic_len = sizeof(breq) * count; + return (0); + } + + count = 0; + len = bifc->ifbic_len; + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (len < sizeof(breq)) + break; + + strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, + sizeof(breq.ifbr_ifsname)); + breq.ifbr_ifsflags = bif->bif_flags; + breq.ifbr_state = bif->bif_state; + breq.ifbr_priority = bif->bif_priority; + breq.ifbr_path_cost = bif->bif_path_cost; + breq.ifbr_portno = bif->bif_ifp->if_index & 0xff; + error = copyout(&breq, bifc->ifbic_req + count, sizeof(breq)); + if (error) + break; + count++; + len -= sizeof(breq); + } + + bifc->ifbic_len = sizeof(breq) * count; + return (error); +} + +int +bridge_ioctl_rts(struct bridge_softc *sc, void *arg) +{ + struct ifbaconf *bac = arg; + struct bridge_rtnode *brt; + struct ifbareq bareq; + struct timeval tv; + int count = 0, error = 0, len; + + BRIDGE_LOCK_ASSERT(sc); + + if (bac->ifbac_len == 0) + return (0); + + getmicrotime(&tv); + + len = bac->ifbac_len; + LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { + if (len < sizeof(bareq)) + goto out; + strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, + sizeof(bareq.ifba_ifsname)); + memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); + if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && + tv.tv_sec < brt->brt_expire) + bareq.ifba_expire = brt->brt_expire - tv.tv_sec; + else + bareq.ifba_expire = 0; + bareq.ifba_flags = brt->brt_flags; + + error = copyout(&bareq, bac->ifbac_req + count, sizeof(bareq)); + if (error) + goto out; + count++; + len -= sizeof(bareq); + } + out: + bac->ifbac_len = sizeof(bareq) * count; + return (error); +} + +int +bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) +{ + struct ifbareq *req = arg; + struct bridge_iflist *bif; + int error; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifba_ifsname); + if (bif == NULL) + return (ENOENT); + + error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1, + req->ifba_flags); + + return (error); +} + +int +bridge_ioctl_sto(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + sc->sc_brttimeout = param->ifbrp_ctime; + + return (0); +} + +int +bridge_ioctl_gto(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_ctime = sc->sc_brttimeout; + + return (0); +} + +int +bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) +{ + struct ifbareq *req = arg; + + BRIDGE_LOCK_ASSERT(sc); + + return (bridge_rtdaddr(sc, req->ifba_dst)); +} + +int +bridge_ioctl_flush(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + + BRIDGE_LOCK_ASSERT(sc); + + bridge_rtflush(sc, req->ifbr_ifsflags); + + return (0); +} + +int +bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_prio = sc->sc_bridge_priority; + + return (0); +} + +int +bridge_ioctl_spri(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + sc->sc_bridge_priority = param->ifbrp_prio; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_ght(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_hellotime = sc->sc_bridge_hello_time >> 8; + + return (0); +} + +int +bridge_ioctl_sht(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + if (param->ifbrp_hellotime == 0) + return (EINVAL); + sc->sc_bridge_hello_time = param->ifbrp_hellotime << 8; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_fwddelay = sc->sc_bridge_forward_delay >> 8; + + return (0); +} + +int +bridge_ioctl_sfd(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + if (param->ifbrp_fwddelay == 0) + return (EINVAL); + sc->sc_bridge_forward_delay = param->ifbrp_fwddelay << 8; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_gma(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_maxage = sc->sc_bridge_max_age >> 8; + + return (0); +} + +int +bridge_ioctl_sma(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + if (param->ifbrp_maxage == 0) + return (EINVAL); + sc->sc_bridge_max_age = param->ifbrp_maxage << 8; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + bif->bif_priority = req->ifbr_priority; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + bif->bif_path_cost = req->ifbr_path_cost; + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + bstp_initialization(sc); + + return (0); +} + +/* + * bridge_ifdetach: + * + * Detach an interface from a bridge. Called when a member + * interface is detaching. + */ +void +bridge_ifdetach(struct ifnet *ifp) +{ + struct bridge_softc *sc = ifp->if_bridge; + struct ifbreq breq; + + BRIDGE_LOCK_ASSERT(sc); + + memset(&breq, 0, sizeof(breq)); + snprintf(breq.ifbr_ifsname, sizeof(breq.ifbr_ifsname), ifp->if_xname); + + (void) bridge_ioctl_del(sc, &breq); +} + +/* + * bridge_init: + * + * Initialize a bridge interface. + */ +static void +bridge_init(void *xsc) +{ + struct bridge_softc *sc = (struct bridge_softc *)xsc; + struct ifnet *ifp = sc->sc_ifp; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + return; + + BRIDGE_LOCK(sc); + callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, + bridge_timer, sc); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + bstp_initialization(sc); + BRIDGE_UNLOCK(sc); + return; +} + +/* + * bridge_stop: + * + * Stop the bridge interface. + */ +void +bridge_stop(struct ifnet *ifp, int disable) +{ + struct bridge_softc *sc = ifp->if_softc; + + BRIDGE_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + callout_stop(&sc->sc_brcallout); + bstp_stop(sc); + + bridge_rtflush(sc, IFBF_FLUSHDYN); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; +} + +/* + * bridge_enqueue: + * + * Enqueue a packet on a bridge member interface. + * + */ +__inline void +bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m) +{ + int len, err; + short mflags; + + /* + * Clear any in-bound checksum flags for this packet. + */ + m->m_pkthdr.csum_flags = 0; + + len = m->m_pkthdr.len; + mflags = m->m_flags; + + IFQ_ENQUEUE(&dst_ifp->if_snd, m, err); + if (err == 0) { + + sc->sc_ifp->if_opackets++; + sc->sc_ifp->if_obytes += len; + + dst_ifp->if_obytes += len; + + if (mflags & M_MCAST) { + sc->sc_ifp->if_omcasts++; + dst_ifp->if_omcasts++; + } + } + + if ((dst_ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) + (*dst_ifp->if_start)(dst_ifp); +} + +/* + * bridge_dummynet: + * + * Receive a queued packet from dummynet and pass it on to the output + * interface. + * + * The mbuf has the Ethernet header already attached. + */ +void +bridge_dummynet(struct mbuf *m, struct ifnet *ifp) +{ + struct bridge_softc *sc; + + sc = ifp->if_bridge; + + /* + * The packet didnt originate from a member interface. This should only + * ever happen if a member interface is removed while packets are + * queued for it. + */ + if (sc == NULL) { + m_freem(m); + return; + } + + if (inet_pfil_hook.ph_busy_count >= 0 +#ifdef INET6 + || inet6_pfil_hook.ph_busy_count >= 0 +#endif + ) { + if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0) + return; + if (m == NULL) + return; + } + + bridge_enqueue(sc, ifp, m); +} + +/* + * bridge_output: + * + * Send output from a bridge member interface. This + * performs the bridging function for locally originated + * packets. + * + * The mbuf has the Ethernet header already attached. We must + * enqueue or free the mbuf before returning. + */ +int +bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, + struct rtentry *rt) +{ + struct ether_header *eh; + struct ifnet *dst_if; + struct bridge_softc *sc; + + if (m->m_len < ETHER_HDR_LEN) { + m = m_pullup(m, ETHER_HDR_LEN); + if (m == NULL) + return (0); + } + + eh = mtod(m, struct ether_header *); + sc = ifp->if_bridge; + + BRIDGE_LOCK(sc); + + /* + * If bridge is down, but the original output interface is up, + * go ahead and send out that interface. Otherwise, the packet + * is dropped below. + */ + if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + dst_if = ifp; + goto sendunicast; + } + + /* + * If the packet is a multicast, or we don't know a better way to + * get there, send to all interfaces. + */ + if (ETHER_IS_MULTICAST(eh->ether_dhost)) + dst_if = NULL; + else + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + if (dst_if == NULL) { + struct bridge_iflist *bif; + struct mbuf *mc; + int error = 0, used = 0; + + BRIDGE_LOCK2REF(sc, error); + if (error) { + m_freem(m); + return (0); + } + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + dst_if = bif->bif_ifp; + if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) + continue; + + /* + * If this is not the original output interface, + * and the interface is participating in spanning + * tree, make sure the port is in a state that + * allows forwarding. + */ + if (dst_if != ifp && + (bif->bif_flags & IFBIF_STP) != 0) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + continue; + } + } + + if (LIST_NEXT(bif, bif_next) == NULL) { + used = 1; + mc = m; + } else { + mc = m_copypacket(m, M_DONTWAIT); + if (mc == NULL) { + sc->sc_ifp->if_oerrors++; + continue; + } + } + + bridge_enqueue(sc, dst_if, mc); + } + if (used == 0) + m_freem(m); + BRIDGE_UNREF(sc); + return (0); + } + + sendunicast: + /* + * XXX Spanning tree consideration here? + */ + + if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { + m_freem(m); + BRIDGE_UNLOCK(sc); + return (0); + } + + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, dst_if, m); + return (0); +} + +/* + * bridge_start: + * + * Start output on a bridge. + * + */ +void +bridge_start(struct ifnet *ifp) +{ + struct bridge_softc *sc; + struct mbuf *m; + struct ether_header *eh; + struct ifnet *dst_if; + + sc = ifp->if_softc; + + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + for (;;) { + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; + BPF_MTAP(ifp, m); + + eh = mtod(m, struct ether_header *); + dst_if = NULL; + + BRIDGE_LOCK(sc); + if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + } + + if (dst_if == NULL) + bridge_broadcast(sc, ifp, m, 0); + else { + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, dst_if, m); + } + } + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + return; +} + +/* + * bridge_forward: + * + * The forwarding function of the bridge. + * + * NOTE: Releases the lock on return. + */ +void +bridge_forward(struct bridge_softc *sc, struct mbuf *m) +{ + struct bridge_iflist *bif; + struct ifnet *src_if, *dst_if, *ifp; + struct ether_header *eh; + + src_if = m->m_pkthdr.rcvif; + BRIDGE_LOCK_ASSERT(sc); + ifp = sc->sc_ifp; + + sc->sc_ifp->if_ipackets++; + sc->sc_ifp->if_ibytes += m->m_pkthdr.len; + + /* + * Look up the bridge_iflist. + */ + bif = bridge_lookup_member_if(sc, src_if); + if (bif == NULL) { + /* Interface is not a bridge member (anymore?) */ + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + } + + eh = mtod(m, struct ether_header *); + + /* + * If the interface is learning, and the source + * address is valid and not multicast, record + * the address. + */ + if ((bif->bif_flags & IFBIF_LEARNING) != 0 && + ETHER_IS_MULTICAST(eh->ether_shost) == 0 && + (eh->ether_shost[0] == 0 && + eh->ether_shost[1] == 0 && + eh->ether_shost[2] == 0 && + eh->ether_shost[3] == 0 && + eh->ether_shost[4] == 0 && + eh->ether_shost[5] == 0) == 0) { + (void) bridge_rtupdate(sc, eh->ether_shost, + src_if, 0, IFBAF_DYNAMIC); + } + + if ((bif->bif_flags & IFBIF_STP) != 0 && + bif->bif_state == BSTP_IFSTATE_LEARNING) { + m_freem(m); + BRIDGE_UNLOCK(sc); + return; + } + + /* + * At this point, the port either doesn't participate + * in spanning tree or it is in the forwarding state. + */ + + /* + * If the packet is unicast, destined for someone on + * "this" side of the bridge, drop it. + */ + if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + if (src_if == dst_if) { + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + } else { + /* ...forward it to all interfaces. */ + sc->sc_ifp->if_imcasts++; + dst_if = NULL; + } + + /* run the packet filter */ + if (inet_pfil_hook.ph_busy_count >= 0 +#ifdef INET6 + || inet6_pfil_hook.ph_busy_count >= 0 +#endif + ) { + BRIDGE_UNLOCK(sc); + if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) + return; + if (m == NULL) + return; + BRIDGE_LOCK(sc); + } + + if (dst_if == NULL) { + /* tap off packets passing the bridge */ + BPF_MTAP(ifp, m); + + bridge_broadcast(sc, src_if, m, 1); + return; + } + + /* + * At this point, we're dealing with a unicast frame + * going to a different interface. + */ + if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + bif = bridge_lookup_member_if(sc, dst_if); + if (bif == NULL) { + /* Not a member of the bridge (anymore?) */ + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_DISABLED: + case BSTP_IFSTATE_BLOCKING: + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + } + + /* tap off packets passing the bridge */ + BPF_MTAP(ifp, m); + + BRIDGE_UNLOCK(sc); + + if (inet_pfil_hook.ph_busy_count >= 0 +#ifdef INET6 + || inet6_pfil_hook.ph_busy_count >= 0 +#endif + ) { + if (bridge_pfil(&m, sc->sc_ifp, dst_if, PFIL_OUT) != 0) + return; + if (m == NULL) + return; + } + + bridge_enqueue(sc, dst_if, m); +} + +/* + * bridge_input: + * + * Receive input from a member interface. Queue the packet for + * bridging if it is not for us. + */ +struct mbuf * +bridge_input(struct ifnet *ifp, struct mbuf *m) +{ + struct bridge_softc *sc = ifp->if_bridge; + struct bridge_iflist *bif; + struct ifnet *bifp; + struct ether_header *eh; + struct mbuf *mc, *mc2; + + if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return (m); + + bifp = sc->sc_ifp; + + BRIDGE_LOCK(sc); + bif = bridge_lookup_member_if(sc, ifp); + if (bif == NULL) { + BRIDGE_UNLOCK(sc); + return (m); + } + + eh = mtod(m, struct ether_header *); + + if (memcmp(eh->ether_dhost, IFP2ENADDR(bifp), + ETHER_ADDR_LEN) == 0) { + /* + * If the packet is for us, set the packets source as the + * bridge, and return the packet back to ether_input for + * local processing. + */ + + /* XXX Do we tap the packet for the member interface too? + * BPF_MTAP(&m->m_pkthdr.rcvif, m); + */ + + /* Mark the packet as arriving on the bridge interface */ + m->m_pkthdr.rcvif = bifp; + BPF_MTAP(bifp, m); + bifp->if_ipackets++; + + BRIDGE_UNLOCK(sc); + return (m); + } + + if (m->m_flags & (M_BCAST|M_MCAST)) { + /* Tap off 802.1D packets; they do not get forwarded. */ + if (memcmp(eh->ether_dhost, bstp_etheraddr, + ETHER_ADDR_LEN) == 0) { + m = bstp_input(ifp, m); + if (m == NULL) { + BRIDGE_UNLOCK(sc); + return (NULL); + } + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + BRIDGE_UNLOCK(sc); + return (m); + } + } + + /* + * Make a deep copy of the packet and enqueue the copy + * for bridge processing; return the original packet for + * local processing. + */ + mc = m_dup(m, M_DONTWAIT); + if (mc == NULL) { + BRIDGE_UNLOCK(sc); + return (m); + } + + /* Perform the bridge forwarding function with the copy. */ + bridge_forward(sc, mc); + + /* + * Reinject the mbuf as arriving on the bridge so we have a + * chance at claiming multicast packets. We can not loop back + * here from ether_input as a bridge is never a member of a + * bridge. + */ + KASSERT(bifp->if_bridge == NULL, + ("loop created in bridge_input")); + mc2 = m_copypacket(m, M_DONTWAIT); + if (mc2 != NULL) { + mc2->m_pkthdr.rcvif = bifp; + (*bifp->if_input)(bifp, mc2); + } + + /* Return the original packet for local processing. */ + return (m); + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + BRIDGE_UNLOCK(sc); + return (m); + } + } + + /* + * Unicast. Make sure it's not for us. + */ + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + /* It is destined for us. */ + if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_dhost, + ETHER_ADDR_LEN) == 0) { + if (bif->bif_flags & IFBIF_LEARNING) + (void) bridge_rtupdate(sc, + eh->ether_shost, ifp, 0, IFBAF_DYNAMIC); + m->m_pkthdr.rcvif = bif->bif_ifp; + BRIDGE_UNLOCK(sc); + return (m); + } + + /* We just received a packet that we sent out. */ + if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_shost, + ETHER_ADDR_LEN) == 0) { + BRIDGE_UNLOCK(sc); + m_freem(m); + return (NULL); + } + } + + /* Perform the bridge forwarding function. */ + bridge_forward(sc, m); + + return (NULL); +} + +/* + * bridge_broadcast: + * + * Send a frame to all interfaces that are members of + * the bridge, except for the one on which the packet + * arrived. + * + * NOTE: Releases the lock on return. + */ +void +bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, + struct mbuf *m, int runfilt) +{ + struct bridge_iflist *bif; + struct mbuf *mc; + struct ifnet *dst_if; + int error = 0, used = 0; + + BRIDGE_LOCK_ASSERT(sc); + BRIDGE_LOCK2REF(sc, error); + if (error) { + m_freem(m); + return; + } + + /* Filter on the bridge interface before broadcasting */ + if (runfilt && (inet_pfil_hook.ph_busy_count >= 0 +#ifdef INET6 + || inet6_pfil_hook.ph_busy_count >= 0 +#endif + )) { + if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) + return; + if (m == NULL) + return; + } + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + dst_if = bif->bif_ifp; + if (dst_if == src_if) + continue; + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_DISABLED: + continue; + } + } + + if ((bif->bif_flags & IFBIF_DISCOVER) == 0 && + (m->m_flags & (M_BCAST|M_MCAST)) == 0) + continue; + + if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) + continue; + + if (LIST_NEXT(bif, bif_next) == NULL) { + mc = m; + used = 1; + } else { + mc = m_copypacket(m, M_DONTWAIT); + if (mc == NULL) { + sc->sc_ifp->if_oerrors++; + continue; + } + } + + /* + * Filter on the output interface. Pass a NULL bridge interface + * pointer so we do not redundantly filter on the bridge for + * each interface we broadcast on. + */ + if (runfilt && (inet_pfil_hook.ph_busy_count >= 0 +#ifdef INET6 + || inet6_pfil_hook.ph_busy_count >= 0 +#endif + )) { + if (bridge_pfil(&m, NULL, dst_if, PFIL_OUT) != 0) + return; + if (m == NULL) + return; + } + + bridge_enqueue(sc, dst_if, mc); + } + if (used == 0) + m_freem(m); + + BRIDGE_UNREF(sc); +} + +/* + * bridge_rtupdate: + * + * Add a bridge routing entry. + */ +int +bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, + struct ifnet *dst_if, int setflags, uint8_t flags) +{ + struct bridge_rtnode *brt; + struct timeval tv; + int error; + + BRIDGE_LOCK_ASSERT(sc); + + /* + * A route for this destination might already exist. If so, + * update it, otherwise create a new one. + */ + getmicrotime(&tv); + if ((brt = bridge_rtnode_lookup(sc, dst)) == NULL) { + if (sc->sc_brtcnt >= sc->sc_brtmax) + return (ENOSPC); + + /* + * Allocate a new bridge forwarding node, and + * initialize the expiration time and Ethernet + * address. + */ + brt = uma_zalloc(bridge_rtnode_zone, M_NOWAIT | M_ZERO); + if (brt == NULL) + return (ENOMEM); + + brt->brt_expire = tv.tv_sec + sc->sc_brttimeout; + brt->brt_flags = IFBAF_DYNAMIC; + memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); + + if ((error = bridge_rtnode_insert(sc, brt)) != 0) { + uma_zfree(bridge_rtnode_zone, brt); + return (error); + } + } + + brt->brt_ifp = dst_if; + if (setflags) { + brt->brt_flags = flags; + brt->brt_expire = (flags & IFBAF_STATIC) ? 0 : + tv.tv_sec + sc->sc_brttimeout; + } + + return (0); +} + +/* + * bridge_rtlookup: + * + * Lookup the destination interface for an address. + */ +struct ifnet * +bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr) +{ + struct bridge_rtnode *brt; + + BRIDGE_LOCK_ASSERT(sc); + + if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) + return (NULL); + + return (brt->brt_ifp); +} + +/* + * bridge_rttrim: + * + * Trim the routine table so that we have a number + * of routing entries less than or equal to the + * maximum number. + */ +void +bridge_rttrim(struct bridge_softc *sc) +{ + struct bridge_rtnode *brt, *nbrt; + + BRIDGE_LOCK_ASSERT(sc); + + /* Make sure we actually need to do this. */ + if (sc->sc_brtcnt <= sc->sc_brtmax) + return; + + /* Force an aging cycle; this might trim enough addresses. */ + bridge_rtage(sc); + if (sc->sc_brtcnt <= sc->sc_brtmax) + return; + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { + bridge_rtnode_destroy(sc, brt); + if (sc->sc_brtcnt <= sc->sc_brtmax) + return; + } + } +} + +/* + * bridge_timer: + * + * Aging timer for the bridge. + */ +void +bridge_timer(void *arg) +{ + struct bridge_softc *sc = arg; + + BRIDGE_LOCK_ASSERT(sc); + + bridge_rtage(sc); + + if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) + callout_reset(&sc->sc_brcallout, + bridge_rtable_prune_period * hz, bridge_timer, sc); +} + +/* + * bridge_rtage: + * + * Perform an aging cycle. + */ +void +bridge_rtage(struct bridge_softc *sc) +{ + struct bridge_rtnode *brt, *nbrt; + struct timeval tv; + + BRIDGE_LOCK_ASSERT(sc); + + getmicrotime(&tv); + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { + if (tv.tv_sec >= brt->brt_expire) + bridge_rtnode_destroy(sc, brt); + } + } +} + +/* + * bridge_rtflush: + * + * Remove all dynamic addresses from the bridge. + */ +void +bridge_rtflush(struct bridge_softc *sc, int full) +{ + struct bridge_rtnode *brt, *nbrt; + + BRIDGE_LOCK_ASSERT(sc); + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) + bridge_rtnode_destroy(sc, brt); + } +} + +/* + * bridge_rtdaddr: + * + * Remove an address from the table. + */ +int +bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr) +{ + struct bridge_rtnode *brt; + + BRIDGE_LOCK_ASSERT(sc); + + if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) + return (ENOENT); + + bridge_rtnode_destroy(sc, brt); + return (0); +} + +/* + * bridge_rtdelete: + * + * Delete routes to a speicifc member interface. + */ +void +bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) +{ + struct bridge_rtnode *brt, *nbrt; + + BRIDGE_LOCK_ASSERT(sc); + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if (brt->brt_ifp == ifp && (full || + (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) + bridge_rtnode_destroy(sc, brt); + } +} + +/* + * bridge_rtable_init: + * + * Initialize the route table for this bridge. + */ +int +bridge_rtable_init(struct bridge_softc *sc) +{ + int i; + + sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, + M_DEVBUF, M_NOWAIT); + if (sc->sc_rthash == NULL) + return (ENOMEM); + + for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) + LIST_INIT(&sc->sc_rthash[i]); + + sc->sc_rthash_key = arc4random(); + + LIST_INIT(&sc->sc_rtlist); + + return (0); +} + +/* + * bridge_rtable_fini: + * + * Deconstruct the route table for this bridge. + */ +void +bridge_rtable_fini(struct bridge_softc *sc) +{ + + free(sc->sc_rthash, M_DEVBUF); +} + +/* + * The following hash function is adapted from "Hash Functions" by Bob Jenkins + * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). + */ +#define mix(a, b, c) \ +do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ +} while (/*CONSTCOND*/0) + +static __inline uint32_t +bridge_rthash(struct bridge_softc *sc, const uint8_t *addr) +{ + uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key; + + b += addr[5] << 8; + b += addr[4]; + a += addr[3] << 24; + a += addr[2] << 16; + a += addr[1] << 8; + a += addr[0]; + + mix(a, b, c); + + return (c & BRIDGE_RTHASH_MASK); +} + +#undef mix + +/* + * bridge_rtnode_lookup: + * + * Look up a bridge route node for the specified destination. + */ +struct bridge_rtnode * +bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr) +{ + struct bridge_rtnode *brt; + uint32_t hash; + int dir; + + BRIDGE_LOCK_ASSERT(sc); + + hash = bridge_rthash(sc, addr); + LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { + dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN); + if (dir == 0) + return (brt); + if (dir > 0) + return (NULL); + } + + return (NULL); +} + +/* + * bridge_rtnode_insert: + * + * Insert the specified bridge node into the route table. We + * assume the entry is not already in the table. + */ +int +bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) +{ + struct bridge_rtnode *lbrt; + uint32_t hash; + int dir; + + BRIDGE_LOCK_ASSERT(sc); + + hash = bridge_rthash(sc, brt->brt_addr); + + lbrt = LIST_FIRST(&sc->sc_rthash[hash]); + if (lbrt == NULL) { + LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); + goto out; + } + + do { + dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN); + if (dir == 0) + return (EEXIST); + if (dir > 0) { + LIST_INSERT_BEFORE(lbrt, brt, brt_hash); + goto out; + } + if (LIST_NEXT(lbrt, brt_hash) == NULL) { + LIST_INSERT_AFTER(lbrt, brt, brt_hash); + goto out; + } + lbrt = LIST_NEXT(lbrt, brt_hash); + } while (lbrt != NULL); + +#ifdef DIAGNOSTIC + panic("bridge_rtnode_insert: impossible"); +#endif + + out: + LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); + sc->sc_brtcnt++; + + return (0); +} + +/* + * bridge_rtnode_destroy: + * + * Destroy a bridge rtnode. + */ +void +bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) +{ + BRIDGE_LOCK_ASSERT(sc); + + LIST_REMOVE(brt, brt_hash); + + LIST_REMOVE(brt, brt_list); + sc->sc_brtcnt--; + uma_zfree(bridge_rtnode_zone, brt); +} + +/* + * Send bridge packets through pfil if they are one of the types pfil can deal + * with, or if they are ARP or REVARP. (pfil will pass ARP and REVARP without + * question.) If *bifp or *ifp are NULL then packet filtering is skipped for + * that interface. + */ +static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, + struct ifnet *ifp, int dir) +{ + int snap, error, i; + struct ether_header *eh1, eh2; + struct ip_fw_args args; + struct ip *ip; + struct llc llc1; + u_int16_t ether_type; + + snap = 0; + error = -1; /* Default error if not error == 0 */ + + i = min((*mp)->m_pkthdr.len, max_protohdr); + if ((*mp)->m_len < i) { + *mp = m_pullup(*mp, i); + if (*mp == NULL) { + printf("%s: m_pullup failed\n", __func__); + return -1; + } + } + + eh1 = mtod(*mp, struct ether_header *); + ether_type = ntohs(eh1->ether_type); + + /* + * Check for SNAP/LLC. + */ + if (ether_type < ETHERMTU) { + struct llc *llc2 = (struct llc *)(eh1 + 1); + + if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && + llc2->llc_dsap == LLC_SNAP_LSAP && + llc2->llc_ssap == LLC_SNAP_LSAP && + llc2->llc_control == LLC_UI) { + ether_type = htons(llc2->llc_un.type_snap.ether_type); + snap = 1; + } + } + + /* + * If we're trying to filter bridge traffic, don't look at anything + * other than IP and ARP traffic. If the filter doesn't understand + * IPv6, don't allow IPv6 through the bridge either. This is lame + * since if we really wanted, say, an AppleTalk filter, we are hosed, + * but of course we don't have an AppleTalk filter to begin with. + * (Note that since pfil doesn't understand ARP it will pass *ALL* + * ARP traffic.) + */ + switch (ether_type) { + case ETHERTYPE_ARP: + case ETHERTYPE_REVARP: + return 0; /* Automatically pass */ + case ETHERTYPE_IP: +# ifdef INET6 + case ETHERTYPE_IPV6: +# endif /* INET6 */ + break; + default: + /* + * ipfw allows layer2 protocol filtering using + * 'mac-type' so we will let the packet past, if + * ipfw is disabled then drop it. + */ + if (!IPFW_LOADED || pfil_ipfw == 0) + goto bad; + } + + /* Strip off the Ethernet header and keep a copy. */ + m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2); + m_adj(*mp, ETHER_HDR_LEN); + + /* Strip off snap header, if present */ + if (snap) { + m_copydata(*mp, 0, sizeof(struct llc), (caddr_t) &llc1); + m_adj(*mp, sizeof(struct llc)); + } + + /* + * Check the IP header for alignment and errors + */ + if (dir == PFIL_IN) { + switch (ether_type) { + case ETHERTYPE_IP: + error = bridge_ip_checkbasic(mp); + break; +# ifdef INET6 + case ETHERTYPE_IPV6: + error = bridge_ip6_checkbasic(mp); + break; +# endif /* INET6 */ + default: + error = 0; + } + if (error) + goto bad; + } + + if (IPFW_LOADED && pfil_ipfw != 0 && dir == PFIL_OUT && ifp != NULL) { + error = -1; + args.rule = ip_dn_claim_rule(*mp); + if (args.rule != NULL && fw_one_pass) + goto ipfwpass; /* packet already partially processed */ + + args.m = *mp; + args.oif = ifp; + args.next_hop = NULL; + args.eh = &eh2; + i = ip_fw_chk_ptr(&args); + *mp = args.m; + + if (*mp == NULL) + return error; + + if (DUMMYNET_LOADED && (i == IP_FW_DUMMYNET)) { + + /* put the Ethernet header back on */ + M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT); + if (*mp == NULL) + return error; + bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); + + /* + * Pass the pkt to dummynet, which consumes it. The + * packet will return to us via bridge_dummynet(). + */ + args.oif = ifp; + ip_dn_io_ptr(*mp, DN_TO_IFB_FWD, &args); + return error; + } + + if (i != IP_FW_PASS) /* drop */ + goto bad; + } + +ipfwpass: + error = 0; + + /* + * Run the packet through pfil + */ + switch (ether_type) + { + case ETHERTYPE_IP : + /* + * before calling the firewall, swap fields the same as + * IP does. here we assume the header is contiguous + */ + ip = mtod(*mp, struct ip *); + + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + + /* + * Run pfil on the member interface and the bridge, both can + * be skipped by clearing pfil_member or pfil_bridge. + * + * Keep the order: + * in_if -> bridge_if -> out_if + */ + if (pfil_bridge && dir == PFIL_OUT && bifp != NULL) + error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, + dir, NULL); + + if (*mp == NULL || error != 0) /* filter may consume */ + break; + + if (pfil_member && ifp != NULL) + error = pfil_run_hooks(&inet_pfil_hook, mp, ifp, + dir, NULL); + + if (*mp == NULL || error != 0) /* filter may consume */ + break; + + if (pfil_bridge && dir == PFIL_IN && bifp != NULL) + error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, + dir, NULL); + + /* Restore ip and the fields ntohs()'d. */ + if (*mp != NULL && error == 0) { + ip = mtod(*mp, struct ip *); + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + } + + break; +# ifdef INET6 + case ETHERTYPE_IPV6 : + if (pfil_bridge && dir == PFIL_OUT && bifp != NULL) + error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp, + dir, NULL); + + if (*mp == NULL || error != 0) /* filter may consume */ + break; + + if (pfil_member && ifp != NULL) + error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp, + dir, NULL); + + if (*mp == NULL || error != 0) /* filter may consume */ + break; + + if (pfil_bridge && dir == PFIL_IN && bifp != NULL) + error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp, + dir, NULL); + break; +# endif + default : + error = 0; + break; + } + + if (*mp == NULL) + return error; + if (error != 0) + goto bad; + + error = -1; + + /* + * Finally, put everything back the way it was and return + */ + if (snap) { + M_PREPEND(*mp, sizeof(struct llc), M_DONTWAIT); + if (*mp == NULL) + return error; + bcopy(&llc1, mtod(*mp, caddr_t), sizeof(struct llc)); + } + + M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT); + if (*mp == NULL) + return error; + bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); + + return 0; + + bad: + m_freem(*mp); + *mp = NULL; + return error; +} + +/* + * Perform basic checks on header size since + * pfil assumes ip_input has already processed + * it for it. Cut-and-pasted from ip_input.c. + * Given how simple the IPv6 version is, + * does the IPv4 version really need to be + * this complicated? + * + * XXX Should we update ipstat here, or not? + * XXX Right now we update ipstat but not + * XXX csum_counter. + */ +static int +bridge_ip_checkbasic(struct mbuf **mp) +{ + struct mbuf *m = *mp; + struct ip *ip; + int len, hlen; + u_short sum; + + if (*mp == NULL) + return -1; + + if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { + if ((m = m_copyup(m, sizeof(struct ip), + (max_linkhdr + 3) & ~3)) == NULL) { + /* XXXJRT new stat, please */ + ipstat.ips_toosmall++; + goto bad; + } + } else if (__predict_false(m->m_len < sizeof (struct ip))) { + if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { + ipstat.ips_toosmall++; + goto bad; + } + } + ip = mtod(m, struct ip *); + if (ip == NULL) goto bad; + + if (ip->ip_v != IPVERSION) { + ipstat.ips_badvers++; + goto bad; + } + hlen = ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) { /* minimum header length */ + ipstat.ips_badhlen++; + goto bad; + } + if (hlen > m->m_len) { + if ((m = m_pullup(m, hlen)) == 0) { + ipstat.ips_badhlen++; + goto bad; + } + ip = mtod(m, struct ip *); + if (ip == NULL) goto bad; + } + + if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { + sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); + } else { + if (hlen == sizeof(struct ip)) { + sum = in_cksum_hdr(ip); + } else { + sum = in_cksum(m, hlen); + } + } + if (sum) { + ipstat.ips_badsum++; + goto bad; + } + + /* Retrieve the packet length. */ + len = ntohs(ip->ip_len); + + /* + * Check for additional length bogosity + */ + if (len < hlen) { + ipstat.ips_badlen++; + goto bad; + } + + /* + * Check that the amount of data in the buffers + * is as at least much as the IP header would have us expect. + * Drop packet if shorter than we expect. + */ + if (m->m_pkthdr.len < len) { + ipstat.ips_tooshort++; + goto bad; + } + + /* Checks out, proceed */ + *mp = m; + return 0; + + bad: + *mp = m; + return -1; +} + +# ifdef INET6 +/* + * Same as above, but for IPv6. + * Cut-and-pasted from ip6_input.c. + * XXX Should we update ip6stat, or not? + */ +static int +bridge_ip6_checkbasic(struct mbuf **mp) +{ + struct mbuf *m = *mp; + struct ip6_hdr *ip6; + + /* + * If the IPv6 header is not aligned, slurp it up into a new + * mbuf with space for link headers, in the event we forward + * it. Otherwise, if it is aligned, make sure the entire base + * IPv6 header is in the first mbuf of the chain. + */ + if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { + struct ifnet *inifp = m->m_pkthdr.rcvif; + if ((m = m_copyup(m, sizeof(struct ip6_hdr), + (max_linkhdr + 3) & ~3)) == NULL) { + /* XXXJRT new stat, please */ + ip6stat.ip6s_toosmall++; + in6_ifstat_inc(inifp, ifs6_in_hdrerr); + goto bad; + } + } else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) { + struct ifnet *inifp = m->m_pkthdr.rcvif; + if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { + ip6stat.ip6s_toosmall++; + in6_ifstat_inc(inifp, ifs6_in_hdrerr); + goto bad; + } + } + + ip6 = mtod(m, struct ip6_hdr *); + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + ip6stat.ip6s_badvers++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + goto bad; + } + + /* Checks out, proceed */ + *mp = m; + return 0; + + bad: + *mp = m; + return -1; +} +# endif /* INET6 */ Property changes on: stable/5/sys/net/if_bridge.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/5/usr.sbin/portsnap/make_index/make_index.c =================================================================== --- stable/5/usr.sbin/portsnap/make_index/make_index.c (nonexistent) +++ stable/5/usr.sbin/portsnap/make_index/make_index.c (revision 150838) @@ -0,0 +1,506 @@ +/*- + * Copyright 2005 Colin Percival + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +struct port; + +typedef union { + char * name; + struct port * p; +} DEP; + +typedef struct port { + char * pkgname; + char * portdir; + char * prefix; + char * comment; + char * pkgdescr; + char * maintainer; + char * categories; + size_t n_edep; + DEP * edep; + size_t n_pdep; + DEP * pdep; + size_t n_fdep; + DEP * fdep; + size_t n_bdep; + DEP * bdep; + size_t n_rdep; + DEP * rdep; + char * www; + int recursed; +} PORT; + +static void usage(void); +static char * strdup2(const char *str); +static DEP * makelist(char * str, size_t * n); +static PORT * portify(char * line); +static int portcompare(char * a, char * b); +static void heapifyports(PORT **pp, size_t size, size_t pos); +static PORT * findport(PORT ** pp, size_t st, size_t en, char * name); +static void translateport(PORT ** pp, size_t pplen, PORT * p); +static DEP * recurse_one(DEP * d, size_t * nd); +static void recurse(PORT * p); +static void heapifypkgs(DEP * d, size_t size, size_t pos); +static void sortpkgs(DEP * d, size_t nd); +static void printport(PORT * p); + +static void +usage(void) +{ + + fprintf(stderr, "usage: make_index file\n"); + exit(1); + /* NOTREACHED */ +} + +static char * +strdup2(const char *str) +{ + char * r; + + r = strdup(str); + if (r == NULL) + err(1, "strdup"); + return r; +} + +/* Take a space-separated list and return an array of (char *) */ +static DEP * +makelist(char * str, size_t * n) +{ + DEP * d; + size_t i; + + /* No depends at all? */ + if (str[0] == 0) { + *n = 0; + return NULL; + } + + /* Count the number of fields */ + *n = 1; + for (i = 0; str[i] != 0; i++) + if (str[i] == ' ') + (*n)++; + + /* Allocate and fill an array */ + d = malloc(*n * sizeof(DEP)); + if (d == NULL) + err(1, "malloc(DEP)"); + for (i = 0; i < *n; i++) { + d[i].name = strdup2(strsep(&str, " ")); + + /* Strip trailing slashes */ + if (d[i].name[strlen(d[i].name) - 1] == '/') + d[i].name[strlen(d[i].name) - 1] = 0; + } + + return d; +} + +/* Take a port's describe line and split it into fields */ +static PORT * +portify(char * line) +{ + PORT * p; + size_t i, n; + + /* Verify that line has the right number of fields */ + for (n = i = 0; line[i] != 0; i++) + if (line[i] == '|') + n++; + if (n != 12) + errx(1, "Port describe line is corrupt:\n%s\n", line); + + p = malloc(sizeof(PORT)); + if (p == NULL) + err(1, "malloc(PORT)"); + + p->pkgname = strdup2(strsep(&line, "|")); + p->portdir = strdup2(strsep(&line, "|")); + p->prefix = strdup2(strsep(&line, "|")); + p->comment = strdup2(strsep(&line, "|")); + p->pkgdescr = strdup2(strsep(&line, "|")); + p->maintainer = strdup2(strsep(&line, "|")); + p->categories = strdup2(strsep(&line, "|")); + p->edep = makelist(strsep(&line, "|"), &p->n_edep); + p->pdep = makelist(strsep(&line, "|"), &p->n_pdep); + p->fdep = makelist(strsep(&line, "|"), &p->n_fdep); + p->bdep = makelist(strsep(&line, "|"), &p->n_bdep); + p->rdep = makelist(strsep(&line, "|"), &p->n_rdep); + p->www = strdup2(strsep(&line, "|")); + + p->recursed = 0; + + /* + * line will now be equal to NULL -- we counted the field + * separators at the top of the function. + */ + + return p; +} + +/* Returns -1, 0, or 1 based on a comparison of the portdir strings */ +static int +portcompare(char * a, char * b) +{ + size_t i; + + /* Find first non-matching position */ + for (i = 0; ; i++) { + if (a[i] != b[i]) + break; + if (a[i] == 0) /* End of strings */ + return 0; + } + + /* One string is a prefix of the other */ + if (a[i] == 0) + return -1; + if (b[i] == 0) + return 1; + + /* One string has a category which is a prefix of the other */ + if (a[i] == '/') + return -1; + if (b[i] == '/') + return 1; + + /* The two strings are simply different */ + if (a[i] < b[i]) + return -1; + else + return 1; +} + +/* Heapify (PORT *) number pos in a pseudo-heap pp[0]..pp[size - 1] */ +static void +heapifyports(PORT **pp, size_t size, size_t pos) +{ + size_t i = pos; + PORT * tmp; + +top: + /* Find the largest value out of {pos, 2*pos+1, 2*pos+2} */ + if ((2 * pos + 1 < size) && + (portcompare(pp[i]->portdir, pp[2 * pos + 1]->portdir) < 0)) + i = 2 * pos + 1; + if ((2 * pos + 2 < size) && + (portcompare(pp[i]->portdir, pp[2 * pos + 2]->portdir) < 0)) + i = 2 * pos + 2; + + /* If necessary, swap elements and iterate down the tree. */ + if (i != pos) { + tmp = pp[pos]; + pp[pos] = pp[i]; + pp[i] = tmp; + pos = i; + goto top; + } +} + +/* Translate a port directory name into a (PORT *), and free the name */ +static PORT * +findport(PORT ** pp, size_t st, size_t en, char * name) +{ + size_t mid; + int r; + + if (st == en) + errx(1, "Unresolved dependency: %s", name); + + mid = (st + en) / 2; + r = portcompare(pp[mid]->portdir, name); + + if (r == 0) { + free(name); + return pp[mid]; + } else if (r < 0) + return findport(pp, mid + 1, en, name); + else + return findport(pp, st, mid, name); +} + +/* Translate all depends from names into PORT *s */ +static void +translateport(PORT ** pp, size_t pplen, PORT * p) +{ + size_t i; + + for (i = 0; i < p->n_edep; i++) + p->edep[i].p = findport(pp, 0, pplen, p->edep[i].name); + for (i = 0; i < p->n_pdep; i++) + p->pdep[i].p = findport(pp, 0, pplen, p->pdep[i].name); + for (i = 0; i < p->n_fdep; i++) + p->fdep[i].p = findport(pp, 0, pplen, p->fdep[i].name); + for (i = 0; i < p->n_bdep; i++) + p->bdep[i].p = findport(pp, 0, pplen, p->bdep[i].name); + for (i = 0; i < p->n_rdep; i++) + p->rdep[i].p = findport(pp, 0, pplen, p->rdep[i].name); +} + +/* Recurse on one specific depends list */ +static DEP * +recurse_one(DEP * d, size_t * nd) +{ + size_t i, j, k, n, N; + + N = n = *nd; + for (i = 0; i < n; i++) { + recurse(d[i].p); + for (j = 0; j < d[i].p->n_rdep; j++) { + for (k = 0; k < N; k++) { + if (d[i].p->rdep[j].p == d[k].p) + break; + } + if (k == N) { + N++; + if (N >= *nd) { + *nd += *nd; + d = realloc(d, *nd * sizeof(DEP)); + if (d == NULL) + err(1, "realloc(d)"); + } + d[k].p = d[i].p->rdep[j].p; + } + } + } + *nd = N; + + return d; +} + +/* Recurse on the depends lists */ +static void +recurse(PORT * p) +{ + switch (p->recursed) { + case 0: + /* First time we've seen this port */ + p->recursed = 1; + break; + case 1: + /* We're in the middle of recursing this port */ + errx(1, "Circular dependency loop found: %s" + " depends upon itself.\n", p->pkgname); + case 2: + /* This port has already been recursed */ + return; + } + + p->edep = recurse_one(p->edep, &p->n_edep); + p->pdep = recurse_one(p->pdep, &p->n_pdep); + p->fdep = recurse_one(p->fdep, &p->n_fdep); + p->bdep = recurse_one(p->bdep, &p->n_bdep); + p->rdep = recurse_one(p->rdep, &p->n_rdep); + + /* Finished recursing on this port */ + p->recursed = 2; +} + +/* Heapify an element in a package list */ +static void +heapifypkgs(DEP * d, size_t size, size_t pos) +{ + size_t i = pos; + PORT * tmp; + +top: + /* Find the largest value out of {pos, 2*pos+1, 2*pos+2} */ + if ((2 * pos + 1 < size) && + (strcmp(d[i].p->pkgname, d[2 * pos + 1].p->pkgname) < 0)) + i = 2 * pos + 1; + if ((2 * pos + 2 < size) && + (strcmp(d[i].p->pkgname, d[2 * pos + 2].p->pkgname) < 0)) + i = 2 * pos + 2; + + /* If necessary, swap elements and iterate down the tree. */ + if (i != pos) { + tmp = d[pos].p; + d[pos].p = d[i].p; + d[i].p = tmp; + pos = i; + goto top; + } +} + +/* Sort a list of dependent packages in alphabetical order */ +static void +sortpkgs(DEP * d, size_t nd) +{ + size_t i; + PORT * tmp; + + if (nd == 0) + return; + + for (i = nd; i > 0; i--) + heapifypkgs(d, nd, i - 1); /* Build a heap */ + for (i = nd - 1; i > 0; i--) { + tmp = d[0].p; /* Extract elements */ + d[0].p = d[i].p; + d[i].p = tmp; + heapifypkgs(d, i, 0); /* And re-heapify */ + } +} + +/* Output an index line for the given port. */ +static void +printport(PORT * p) +{ + size_t i; + + sortpkgs(p->edep, p->n_edep); + sortpkgs(p->pdep, p->n_pdep); + sortpkgs(p->fdep, p->n_fdep); + sortpkgs(p->bdep, p->n_bdep); + sortpkgs(p->rdep, p->n_rdep); + + printf("%s|%s|%s|%s|%s|%s|%s|", + p->pkgname, p->portdir, p->prefix, p->comment, p->pkgdescr, + p->maintainer, p->categories); + for (i = 0; i < p->n_bdep; i++) + printf("%s%s", i ? " " : "", p->bdep[i].p->pkgname); + printf("|"); + for (i = 0; i < p->n_rdep; i++) + printf("%s%s", i ? " " : "", p->rdep[i].p->pkgname); + printf("|"); + printf("%s|", p->www); + for (i = 0; i < p->n_edep; i++) + printf("%s%s", i ? " " : "", p->edep[i].p->pkgname); + printf("|"); + for (i = 0; i < p->n_pdep; i++) + printf("%s%s", i ? " " : "", p->pdep[i].p->pkgname); + printf("|"); + for (i = 0; i < p->n_fdep; i++) + printf("%s%s", i ? " " : "", p->fdep[i].p->pkgname); + printf("\n"); +} + +/* + * Algorithm: + * 1. Suck in all the data, splitting into fields. + * 2. Sort the ports according to port directory. + * 3. Using a binary search, translate each dependency from a + * port directory name into a pointer to a port. + * 4. Recursively follow dependencies, expanding the lists of + * pointers as needed (using realloc). + * 5. Iterate through the ports, printing them out (remembering + * to list the dependent ports in alphabetical order). + */ + +int +main(int argc, char *argv[]) +{ + FILE * f; + char * line; + size_t linelen; + PORT ** pp; /* Array of pointers to PORTs */ + PORT * tmp; + size_t pplen; /* Allocated size of array */ + size_t i; + + if (argc != 2) + usage(); + if ((f = fopen(argv[1], "r")) == NULL) + err(1, "fopen(%s)", argv[1]); + + pplen = 1024; + if ((pp = malloc(pplen * sizeof(PORT *))) == NULL) + err(1, "malloc(pp)"); + + /* + * 1. Suck in all the data, splitting into fields. + */ + for(i = 0; (line = fgetln(f, &linelen)) != NULL; i++) { + if (line[linelen - 1] != '\n') + errx(1, "Unterminated line encountered"); + line[linelen - 1] = 0; + + /* Enlarge array if needed */ + if (i >= pplen) { + pplen *= 2; + if ((pp = realloc(pp, pplen * sizeof(PORT *))) == NULL) + err(1, "realloc(pp)"); + } + + pp[i] = portify(line); + } + /* Reallocate to the correct size */ + pplen = i; + if ((pp = realloc(pp, pplen * sizeof(PORT *))) == NULL) + err(1, "realloc(pp)"); + + /* Make sure we actually reached the EOF */ + if (!feof(f)) + err(1, "fgetln(%s)", argv[1]); + /* Close the describes file */ + if (fclose(f) != 0) + err(1, "fclose(%s)", argv[1]); + + /* + * 2. Sort the ports according to port directory. + */ + for (i = pplen; i > 0; i--) + heapifyports(pp, pplen, i - 1); /* Build a heap */ + for (i = pplen - 1; i > 0; i--) { + tmp = pp[0]; /* Extract elements */ + pp[0] = pp[i]; + pp[i] = tmp; + heapifyports(pp, i, 0); /* And re-heapify */ + } + + /* + * 3. Using a binary search, translate each dependency from a + * port directory name into a pointer to a port. + */ + for (i = 0; i < pplen; i++) + translateport(pp, pplen, pp[i]); + + /* + * 4. Recursively follow dependencies, expanding the lists of + * pointers as needed (using realloc). + */ + for (i = 0; i < pplen; i++) + recurse(pp[i]); + + /* + * 5. Iterate through the ports, printing them out (remembering + * to list the dependent ports in alphabetical order). + */ + for (i = 0; i < pplen; i++) + printport(pp[i]); + + return 0; +} Property changes on: stable/5/usr.sbin/portsnap/make_index/make_index.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/5/usr.sbin/portsnap/phttpget/phttpget.c =================================================================== --- stable/5/usr.sbin/portsnap/phttpget/phttpget.c (nonexistent) +++ stable/5/usr.sbin/portsnap/phttpget/phttpget.c (revision 150838) @@ -0,0 +1,701 @@ +/*- + * Copyright 2005 Colin Percival + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char * env_HTTP_PROXY; +static char * env_HTTP_PROXY_AUTH; +static const char * env_HTTP_USER_AGENT; +static const char * proxyport; +static char * proxyauth; + +static struct timeval timo = { 15, 0}; + +static void +usage(void) +{ + + fprintf(stderr, "usage: phttpget server [file ...]\n"); + exit(EX_USAGE); +} + +/* + * Base64 encode a string; the string returned, if non-NULL, is + * allocated using malloc() and must be freed by the caller. + */ +static char * +b64enc(const char *ptext) +{ + static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + const char *pt; + char *ctext, *pc; + size_t ptlen, ctlen; + uint32_t t; + unsigned int j; + + /* + * Encoded length is 4 characters per 3-byte block or partial + * block of plaintext, plus one byte for the terminating NUL + */ + ptlen = strlen(ptext); + if (ptlen > ((SIZE_MAX - 1) / 4) * 3 - 2) + return NULL; /* Possible integer overflow */ + ctlen = 4 * ((ptlen + 2) / 3) + 1; + if ((ctext = malloc(ctlen)) == NULL) + return NULL; + ctext[ctlen - 1] = 0; + + /* + * Scan through ptext, reading up to 3 bytes from ptext and + * writing 4 bytes to ctext, until we run out of input. + */ + for (pt = ptext, pc = ctext; ptlen; ptlen -= 3, pc += 4) { + /* Read 3 bytes */ + for (t = j = 0; j < 3; j++) { + t <<= 8; + if (j < ptlen) + t += *pt++; + } + + /* Write 4 bytes */ + for (j = 0; j < 4; j++) { + if (j <= ptlen + 1) + pc[j] = base64[(t >> 18) & 0x3f]; + else + pc[j] = '='; + t <<= 6; + } + + /* If we're done, exit the loop */ + if (ptlen <= 3) + break; + } + + return (ctext); +} + +static void +readenv(void) +{ + char *proxy_auth_userpass, *proxy_auth_userpass64, *p; + char *proxy_auth_user = NULL; + char *proxy_auth_pass = NULL; + + env_HTTP_PROXY = getenv("HTTP_PROXY"); + if (env_HTTP_PROXY != NULL) { + if (strncmp(env_HTTP_PROXY, "http://", 7) == 0) + env_HTTP_PROXY += 7; + p = strchr(env_HTTP_PROXY, '/'); + if (p != NULL) + *p = 0; + p = strchr(env_HTTP_PROXY, ':'); + if (p != NULL) { + *p = 0; + proxyport = p + 1; + } else + proxyport = "3128"; + } + + env_HTTP_PROXY_AUTH = getenv("HTTP_PROXY_AUTH"); + if ((env_HTTP_PROXY != NULL) && + (env_HTTP_PROXY_AUTH != NULL) && + (strncasecmp(env_HTTP_PROXY_AUTH, "basic:" , 6) == 0)) { + /* Ignore authentication scheme */ + (void) strsep(&env_HTTP_PROXY_AUTH, ":"); + + /* Ignore realm */ + (void) strsep(&env_HTTP_PROXY_AUTH, ":"); + + /* Obtain username and password */ + proxy_auth_user = strsep(&env_HTTP_PROXY_AUTH, ":"); + proxy_auth_pass = strsep(&env_HTTP_PROXY_AUTH, ":"); + } + + if ((proxy_auth_user != NULL) && (proxy_auth_pass != NULL)) { + asprintf(&proxy_auth_userpass, "%s:%s", + proxy_auth_user, proxy_auth_pass); + if (proxy_auth_userpass == NULL) + err(1, "asprintf"); + + proxy_auth_userpass64 = b64enc(proxy_auth_userpass); + if (proxy_auth_userpass64 == NULL) + err(1, "malloc"); + + asprintf(&proxyauth, "Proxy-Authorization: Basic %s\r\n", + proxy_auth_userpass64); + if (proxyauth == NULL) + err(1, "asprintf"); + + free(proxy_auth_userpass); + free(proxy_auth_userpass64); + } else + proxyauth = NULL; + + env_HTTP_USER_AGENT = getenv("HTTP_USER_AGENT"); + if (env_HTTP_USER_AGENT == NULL) + env_HTTP_USER_AGENT = "phttpget/0.1"; +} + +static int +makerequest(char ** buf, char * path, char * server, int connclose) +{ + int buflen; + + buflen = asprintf(buf, + "GET %s%s/%s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: %s\r\n" + "%s" + "%s" + "\r\n", + env_HTTP_PROXY ? "http://" : "", + env_HTTP_PROXY ? server : "", + path, server, env_HTTP_USER_AGENT, + proxyauth ? proxyauth : "", + connclose ? "Connection: Close\r\n" : ""); + if (buflen == -1) + err(1, "asprintf"); + return(buflen); +} + +static int +readln(int sd, char * resbuf, int * resbuflen, int * resbufpos) +{ + ssize_t len; + + while (strnstr(resbuf + *resbufpos, "\r\n", + *resbuflen - *resbufpos) == NULL) { + /* Move buffered data to the start of the buffer */ + if (*resbufpos != 0) { + memmove(resbuf, resbuf + *resbufpos, + *resbuflen - *resbufpos); + *resbuflen -= *resbufpos; + *resbufpos = 0; + } + + /* If the buffer is full, complain */ + if (*resbuflen == BUFSIZ) + return -1; + + /* Read more data into the buffer */ + len = recv(sd, resbuf + *resbuflen, BUFSIZ - *resbuflen, 0); + if ((len == -1) && (errno != EINTR)) + return -1; + + if (len != -1) + *resbuflen += len; + } + + return 0; +} + +static int +copybytes(int sd, int fd, off_t copylen, char * resbuf, int * resbuflen, + int * resbufpos) +{ + ssize_t len; + + while (copylen) { + /* Write data from resbuf to fd */ + len = *resbuflen - *resbufpos; + if (copylen < len) + len = copylen; + if (len > 0) { + if (fd != -1) + len = write(fd, resbuf + *resbufpos, len); + if (len == -1) + err(1, "write"); + *resbufpos += len; + copylen -= len; + continue; + } + + /* Read more data into buffer */ + len = recv(sd, resbuf, BUFSIZ, 0); + if (len == -1) { + if (errno == EINTR) + continue; + return -1; + } else if (len == 0) { + return -2; + } else { + *resbuflen = len; + *resbufpos = 0; + } + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + struct addrinfo hints; /* Hints to getaddrinfo */ + struct addrinfo *res; /* Pointer to server address being used */ + struct addrinfo *res0; /* Pointer to server addresses */ + char * resbuf = NULL; /* Response buffer */ + int resbufpos = 0; /* Response buffer position */ + int resbuflen = 0; /* Response buffer length */ + char * eolp; /* Pointer to "\r\n" within resbuf */ + char * hln; /* Pointer within header line */ + char * servername; /* Name of server */ + char * fname = NULL; /* Name of downloaded file */ + char * reqbuf = NULL; /* Request buffer */ + int reqbufpos = 0; /* Request buffer position */ + int reqbuflen = 0; /* Request buffer length */ + ssize_t len; /* Length sent or received */ + int nreq = 0; /* Number of next request to send */ + int nres = 0; /* Number of next reply to receive */ + int pipelined = 0; /* != 0 if connection in pipelined mode. */ + int sd = -1; /* Socket descriptor */ + int sdflags = 0; /* Flags on the socket sd */ + int fd = -1; /* Descriptor for download target file */ + int error; /* Error code */ + int statuscode; /* HTTP Status code */ + off_t contentlength; /* Value from Content-Length header */ + int chunked; /* != if transfer-encoding is chunked */ + off_t clen; /* Chunk length */ + int firstreq = 0; /* # of first request for this connection */ + + /* Check that the arguments are sensible */ + if (argc < 2) + usage(); + + /* Read important environment variables */ + readenv(); + + /* Get server name and adjust arg[cv] to point at file names */ + servername = argv[1]; + argv += 2; + argc -= 2; + + /* Allocate response buffer */ + resbuf = malloc(BUFSIZ); + if (resbuf == NULL) + err(1, "malloc"); + + /* Look up server */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(env_HTTP_PROXY ? env_HTTP_PROXY : servername, + env_HTTP_PROXY ? proxyport : "http", &hints, &res0); + if (error) + errx(1, "%s: host = %s, port = %s", + env_HTTP_PROXY ? env_HTTP_PROXY : servername, + env_HTTP_PROXY ? proxyport : "http", + gai_strerror(error)); + if (res0 == NULL) + errx(1, "could not look up %s", servername); + res = res0; + + /* Do the fetching */ + while (nres < argc) { + /* Make sure we have a connected socket */ + for (; sd == -1; res = res->ai_next) { + /* No addresses left to try :-( */ + if (res == NULL) + errx(1, "Could not connect to %s", servername); + + /* Create a socket... */ + sd = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (sd == -1) + continue; + + /* ... set 15-second timeouts ... */ + setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, + (void *)&timo, (socklen_t)sizeof(timo)); + setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, + (void *)&timo, (socklen_t)sizeof(timo)); + + /* ... and connect to the server. */ + if(connect(sd, res->ai_addr, res->ai_addrlen)) { + close(sd); + sd = -1; + continue; + } + + firstreq = nres; + } + + /* + * If in pipelined HTTP mode, put socket into non-blocking + * mode, since we're probably going to want to try to send + * several HTTP requests. + */ + if (pipelined) { + sdflags = fcntl(sd, F_GETFL); + if (fcntl(sd, F_SETFL, sdflags | O_NONBLOCK) == -1) + err(1, "fcntl"); + } + + /* Construct requests and/or send them without blocking */ + while ((nreq < argc) && ((reqbuf == NULL) || pipelined)) { + /* If not in the middle of a request, make one */ + if (reqbuf == NULL) { + reqbuflen = makerequest(&reqbuf, argv[nreq], + servername, (nreq == argc - 1)); + reqbufpos = 0; + } + + /* If in pipelined mode, try to send the request */ + if (pipelined) { + while (reqbufpos < reqbuflen) { + len = send(sd, reqbuf + reqbufpos, + reqbuflen - reqbufpos, 0); + if (len == -1) + break; + reqbufpos += len; + } + if (reqbufpos < reqbuflen) { + if (errno != EAGAIN) + goto conndied; + break; + } else { + free(reqbuf); + reqbuf = NULL; + nreq++; + } + } + } + + /* Put connection back into blocking mode */ + if (pipelined) { + if (fcntl(sd, F_SETFL, sdflags) == -1) + err(1, "fcntl"); + } + + /* Do we need to blocking-send a request? */ + if (nres == nreq) { + while (reqbufpos < reqbuflen) { + len = send(sd, reqbuf + reqbufpos, + reqbuflen - reqbufpos, 0); + if (len == -1) + goto conndied; + reqbufpos += len; + } + free(reqbuf); + reqbuf = NULL; + nreq++; + } + + /* Scan through the response processing headers. */ + statuscode = 0; + contentlength = -1; + chunked = 0; + do { + /* Get a header line */ + error = readln(sd, resbuf, &resbuflen, &resbufpos); + if (error) + goto conndied; + hln = resbuf + resbufpos; + eolp = strnstr(hln, "\r\n", resbuflen - resbufpos); + resbufpos = (eolp - resbuf) + 2; + *eolp = '\0'; + + /* Make sure it doesn't contain a NUL character */ + if (strchr(hln, '\0') != eolp) + goto conndied; + + if (statuscode == 0) { + /* The first line MUST be HTTP/1.x xxx ... */ + if ((strncmp(hln, "HTTP/1.", 7) != 0) || + ! isdigit(hln[7])) + goto conndied; + + /* + * If the minor version number isn't zero, + * then we can assume that pipelining our + * requests is OK -- as long as we don't + * see a "Connection: close" line later + * and we either have a Content-Length or + * Transfer-Encoding: chunked header to + * tell us the length. + */ + if (hln[7] != '0') + pipelined = 1; + + /* Skip over the minor version number */ + hln = strchr(hln + 7, ' '); + if (hln == NULL) + goto conndied; + else + hln++; + + /* Read the status code */ + while (isdigit(*hln)) { + statuscode = statuscode * 10 + + *hln - '0'; + hln++; + } + + if (statuscode < 100 || statuscode > 599) + goto conndied; + + /* Ignore the rest of the line */ + continue; + } + + /* Check for "Connection: close" header */ + if (strncmp(hln, "Connection:", 11) == 0) { + hln += 11; + if (strstr(hln, "close") != NULL) + pipelined = 0; + + /* Next header... */ + continue; + } + + /* Check for "Content-Length:" header */ + if (strncmp(hln, "Content-Length:", 15) == 0) { + hln += 15; + contentlength = 0; + + /* Find the start of the length */ + while (!isdigit(*hln) && (*hln != '\0')) + hln++; + + /* Compute the length */ + while (isdigit(*hln)) { + if (contentlength >= OFF_MAX / 10) { + /* Nasty people... */ + goto conndied; + } + contentlength = contentlength * 10 + + *hln - '0'; + hln++; + } + + /* Next header... */ + continue; + } + + /* Check for "Transfer-Encoding: chunked" header */ + if (strncmp(hln, "Transfer-Encoding:", 18) == 0) { + hln += 18; + if (strstr(hln, "chunked") != NULL) + chunked = 1; + + /* Next header... */ + continue; + } + + /* We blithely ignore any other header lines */ + + /* No more header lines */ + if (strlen(hln) == 0) { + /* + * If the status code was 1xx, then there will + * be a real header later. Servers may emit + * 1xx header blocks at will, but since we + * don't expect one, we should just ignore it. + */ + if (100 <= statuscode && statuscode <= 199) { + statuscode = 0; + continue; + } + + /* End of header; message body follows */ + break; + } + } while (1); + + /* No message body for 204 or 304 */ + if (statuscode == 204 || statuscode == 304) { + nres++; + continue; + } + + /* + * There should be a message body coming, but we only want + * to send it to a file if the status code is 200 + */ + if (statuscode == 200) { + /* Generate a file name for the download */ + fname = strrchr(argv[nres], '/'); + if (fname == NULL) + fname = argv[nres]; + else + fname++; + if (strlen(fname) == 0) + errx(1, "Cannot obtain file name from %s\n", + argv[nres]); + + fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (fd == -1) + errx(1, "open(%s)", fname); + }; + + /* Read the message and send data to fd if appropriate */ + if (chunked) { + /* Handle a chunked-encoded entity */ + + /* Read chunks */ + do { + error = readln(sd, resbuf, &resbuflen, + &resbufpos); + if (error) + goto conndied; + hln = resbuf + resbufpos; + eolp = strstr(hln, "\r\n"); + resbufpos = (eolp - resbuf) + 2; + + clen = 0; + while (isxdigit(*hln)) { + if (clen >= OFF_MAX / 16) { + /* Nasty people... */ + goto conndied; + } + if (isdigit(*hln)) + clen = clen * 16 + *hln - '0'; + else + clen = clen * 16 + 10 + + tolower(*hln) - 'a'; + hln++; + } + + error = copybytes(sd, fd, clen, resbuf, + &resbuflen, &resbufpos); + if (error) { + goto conndied; + } + } while (clen != 0); + + /* Read trailer and final CRLF */ + do { + error = readln(sd, resbuf, &resbuflen, + &resbufpos); + if (error) + goto conndied; + hln = resbuf + resbufpos; + eolp = strstr(hln, "\r\n"); + resbufpos = (eolp - resbuf) + 2; + } while (hln != eolp); + } else if (contentlength != -1) { + error = copybytes(sd, fd, contentlength, resbuf, + &resbuflen, &resbufpos); + if (error) + goto conndied; + } else { + /* + * Not chunked, and no content length header. + * Read everything until the server closes the + * socket. + */ + error = copybytes(sd, fd, OFF_MAX, resbuf, + &resbuflen, &resbufpos); + if (error == -1) + goto conndied; + pipelined = 0; + } + + if (fd != -1) { + close(fd); + fd = -1; + } + + fprintf(stderr, "http://%s/%s: %d ", servername, argv[nres], + statuscode); + if (statuscode == 200) + fprintf(stderr, "OK\n"); + else if (statuscode < 300) + fprintf(stderr, "Successful (ignored)\n"); + else if (statuscode < 400) + fprintf(stderr, "Redirection (ignored)\n"); + else + fprintf(stderr, "Error (ignored)\n"); + + /* We've finished this file! */ + nres++; + + /* + * If necessary, clean up this connection so that we + * can start a new one. + */ + if (pipelined == 0) + goto cleanupconn; + continue; + +conndied: + /* + * Something went wrong -- our connection died, the server + * sent us garbage, etc. If this happened on the first + * request we sent over this connection, give up. Otherwise, + * close this connection, open a new one, and reissue the + * request. + */ + if (nres == firstreq) + errx(1, "Connection failure"); + +cleanupconn: + /* + * Clean up our connection and keep on going + */ + shutdown(sd, SHUT_RDWR); + close(sd); + sd = -1; + if (fd != -1) { + close(fd); + fd = -1; + } + if (reqbuf != NULL) { + free(reqbuf); + reqbuf = NULL; + } + nreq = nres; + res = res0; + pipelined = 0; + resbufpos = resbuflen = 0; + continue; + } + + free(resbuf); + freeaddrinfo(res0); + + return 0; +} Property changes on: stable/5/usr.sbin/portsnap/phttpget/phttpget.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/5/usr.sbin/portsnap/portsnap/portsnap.8 =================================================================== --- stable/5/usr.sbin/portsnap/portsnap/portsnap.8 (nonexistent) +++ stable/5/usr.sbin/portsnap/portsnap/portsnap.8 (revision 150838) @@ -0,0 +1,226 @@ +.\"- +.\" Copyright 2004-2005 Colin Percival +.\" All rights reserved +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted providing that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 13, 2005 +.Dt PORTSNAP 8 +.Os FreeBSD +.Sh NAME +.Nm portsnap +.Nd fetch and extract compressed snapshots of the ports tree +.Sh SYNOPSIS +.Nm +.Op Fl I +.Op Fl d Ar workdir +.Op Fl f Ar conffile +.Op Fl k Ar KEY +.Op Fl p Ar portsdir +.Op Fl s Ar server +.Cm command ... +.Op Ar path +.Sh DESCRIPTION +The +.Nm +tool is used to fetch and update compressed snapshots +of the +.Fx +ports tree, and extract and update an +uncompressed ports tree. +.Sh OPTIONS +The following options are supported: +.Bl -tag -width "-f conffile" +.It Fl d Ar workdir +Store working files (e.g. downloaded updates) in +.Ar workdir . +(default: +.Pa /var/db/portsnap , +or as given in the configuration file.) +.It Fl f Ar conffile +Read the configuration from from +.Ar conffile . +(default: +.Pa /etc/portsnap.conf ) +.It Fl I +For the +.Cm update +command, update INDEX files, but not the rest of the ports tree. +.It Fl k Ar KEY +Expect a public key with given SHA256 hash. +(default: read value from configuration file.) +.It Fl p Ar portsdir +When extracting or updating an uncompressed snapshot, +operate on the directory +.Ar portsdir . +(default: +.Pa /usr/ports/ , +or as given in the configuration file.) +.It Fl s Ar server +Fetch files from the specified server or server pool. +(default: portsnap.FreeBSD.org , or as given in the +configuration file.) +.It path +For +.Cm extract +command only, operate only on parts of the ports tree starting with +.Ar path . +(e.g. +.Nm +.cm extract +.Ar sysutils/port +would extract sysutils/portsman, sysutils/portsnap, +sysutils/portupgrade, etc.) +.El +.Sh COMMANDS +The +.Cm command +can be any one of the following: +.Pp +.Bl -tag -width "-f conffile" +.It fetch +Fetch a compressed snapshot of the ports tree, or update +the existing snapshot. +This command should only be used interactively; for +non-interactive use, you should use the +.Cm cron +command. +.It cron +Sleep a random amount of time, then operate as if the +.Cm fetch +command was specified. +As the name suggests, this command is designed for running +from +.Xr cron 8 ; +the random delay serves to minimize the probability that +a large number of machines will simultaneously attempt to +fetch updates. +.It extract +Extract a ports tree, replacing existing files and directories. +NOTE: This will remove anything occupying the location where +files or directories are being extracted; in particular, any +changes made locally to the ports tree (for example, adding new +patches) will be silently obliterated. +.Pp +Only run this command to initialize your portsnap-maintained +ports tree for the first time, if you wish to start over with +a clean, completely unmodified tree, or if you wish to extract +a specific part of the tree (using the +.Ar path +option). +.It update +Update a ports tree extracted using the +.Cm extract +command. +You must run this command to apply changes to your ports tree +after downloading updates via the +.Cm fetch +or +.Cm cron +commands. +Again, note that in the parts of the ports tree which are being +updated, any local changes or additions will be removed. +.El +.Sh TIPS +.Bl -bullet +.It +If your clock is set to local time, adding the line +.Pp +.Dl 0 3 * * * root /usr/sbin/portsnap cron +.Pp +to /etc/crontab is a good way to make sure you always have +an up-to-date snapshot of the ports tree available which +can quickly be extracted into +.Pa /usr/ports . +If your clock is set to UTC, please pick a random time other +than 3AM, to avoid overly imposing an uneven load on the +server(s) hosting the snapshots. +.It +Running +.Nm +.Cm update +from +.Xr cron 8 +is a bad idea -- if you're ever installing or updating a +port at the time the cron job runs, you'll probably end up +in a mess when +.Nm +updates or removes files which are being used by the port +build. +However, running +.Nm +.Fl I +.Cm update +is probably safe, and can be used together with +.Xr portversion 1 +to identify installed software which is out of date. +.It +If you wish to use +.Nm +to keep a large number of machines up to date, you may wish +to set up a caching HTTP proxy. +Since +.Nm +uses +.Xr fetch 1 +to download updates, setting the +.Ev HTTP_PROXY +environment variable will direct it to fetch updates from +the given proxy. +This is much more efficient than +.Em mirroring +the files on the portsnap server, since the vast majority +of files are not needed by any particular client. +.El +.Sh PRIVACY NOTICE +As an unavoidable part of its operation, a machine running +.Nm +will make its public IP address and the list of files it fetches +available to the server from which it fetches updates. +Using these it may be possible to recognize a machine over an extended +period of time, determine when it is updated, and identify which +portions of the FreeBSD ports tree, if any, are being ignored using +"REFUSE" directives in +.Pa portsnap.conf . +In addition, the FreeBSD release level is transmitted to the server. +.Pp +Statistical data generated from information collected in this manner +may be published, but only in aggregate and after anonymizing the +individual systems. +.Sh FILES +.Bl -tag -width "/etc/portsnap.conf" +.It /etc/portsnap.conf +Default location of the portsnap configuration file. +.It /var/db/portsnap +Default location where compressed snapshots are stored. +.It /usr/ports +Default location where the ports tree is extracted. +.El +.Sh SEE ALSO +.Xr fetch 1 +.Xr fetch 3 +.Xr portsnap.conf 5 +.Xr sha256 8 +.Sh AUTHORS +.An Colin Percival Aq cperciva@FreeBSD.org Property changes on: stable/5/usr.sbin/portsnap/portsnap/portsnap.8 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property