diff --git a/share/man/man4/dummynet.4 b/share/man/man4/dummynet.4 index 56d31409b35..50811f80383 100644 --- a/share/man/man4/dummynet.4 +++ b/share/man/man4/dummynet.4 @@ -61,6 +61,7 @@ pipes. .Sh SEE ALSO .Xr setsockopt 2 , .Xr bridge 4 , +.Xr if_bridge 4 , .Xr ip 4 , .Xr ipfw 8 , .Xr sysctl 8 diff --git a/share/man/man4/if_bridge.4 b/share/man/man4/if_bridge.4 index 2e079f56ae2..1164aeb5305 100644 --- a/share/man/man4/if_bridge.4 +++ b/share/man/man4/if_bridge.4 @@ -107,6 +107,9 @@ to enable enable layer2 filtering with , set to .Li 0 to disable it. +This needs to be enabled for +.Xr dummynet 4 +support. When ipfw is enabled pfil_bridge and pfil_member will be disabled so that IPFW is not run twice, these can be re-enabled if desired. .El diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 53bd302549f..ca46cd93f0b 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -129,7 +129,6 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #define sc_if ifb_ac.ac_if /* @@ -178,6 +177,7 @@ 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; @@ -351,6 +351,7 @@ bridge_modevent(module_t mod, int type, void *data) 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: @@ -360,6 +361,7 @@ bridge_modevent(module_t mod, int type, void *data) 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; @@ -1270,6 +1272,33 @@ bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m, (*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; + + bridge_enqueue(sc, ifp, m, 1); +} + /* * bridge_output: * @@ -2195,7 +2224,7 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, # 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. @@ -2214,6 +2243,43 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, m_adj(*mp, sizeof(struct llc)); } + if (IPFW_LOADED && pfil_ipfw != 0 && dir == PFIL_OUT) { + 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: /* * Check basic packet sanity and run pfil through pfil. */ @@ -2246,10 +2312,16 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, dir, NULL); + if (*mp == NULL) /* filter may consume */ + break; + if (error == 0 && pfil_member) error = pfil_run_hooks(&inet_pfil_hook, mp, ifp, dir, NULL); + if (*mp == NULL) /* filter may consume */ + break; + if (error == 0 && pfil_bridge && dir == PFIL_IN) error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, dir, NULL); @@ -2270,10 +2342,16 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp, dir, NULL); + if (*mp == NULL) /* filter may consume */ + break; + if (error == 0 && pfil_member) error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp, dir, NULL); + if (*mp == NULL) /* filter may consume */ + break; + if (error == 0 && pfil_bridge && dir == PFIL_IN) error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp, dir, NULL); @@ -2291,22 +2369,6 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, error = -1; - if (IPFW_LOADED && pfil_ipfw != 0) { - args.m = *mp; - args.oif = NULL; - args.next_hop = NULL; - args.rule = NULL; - args.eh = &eh2; - i = ip_fw_chk_ptr(&args); - *mp = args.m; - - if (*mp == NULL) - return error; - - if (i == IP_FW_DENY) /* drop */ - goto bad; - } - /* * Finally, put everything back the way it was and return */ diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index 959a8444520..705ed85ea91 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -342,6 +342,7 @@ void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int); int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); +void bridge_dummynet(struct mbuf *, struct ifnet *); struct mbuf *bridge_input(struct ifnet *, struct mbuf *); extern void (*bstp_linkstate_p)(struct ifnet *ifp, int state); diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 358ea534af3..6082624e596 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -115,8 +115,9 @@ bdgtakeifaces_t *bdgtakeifaces_ptr; struct bdg_softc *ifp2sc; struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); -int (*bridge_output_p)(struct ifnet *, struct mbuf *, +int (*bridge_output_p)(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); +void (*bridge_dn_p)(struct mbuf *, struct ifnet *); static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; diff --git a/sys/netinet/ip_dummynet.c b/sys/netinet/ip_dummynet.c index 63a57e3d81e..3c2aa70d6c5 100644 --- a/sys/netinet/ip_dummynet.c +++ b/sys/netinet/ip_dummynet.c @@ -128,6 +128,8 @@ static struct dn_flow_set *all_flow_sets = NULL ;/* list of all flow_sets */ static struct callout dn_timeout; +extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); + #ifdef SYSCTL_NODE SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet"); @@ -478,6 +480,14 @@ transmit_event(struct dn_pipe *pipe) break ; #endif + case DN_TO_IFB_FWD: + if (bridge_dn_p != NULL) + ((*bridge_dn_p)(m, pkt->ifp)); + else + printf("dummynet: if_bridge not loaded\n"); + + break; + case DN_TO_BDG_FWD : /* * The bridge requires/assumes the Ethernet header is diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h index 947f0816a9c..72a88631e40 100644 --- a/sys/netinet/ip_dummynet.h +++ b/sys/netinet/ip_dummynet.h @@ -126,6 +126,7 @@ struct dn_pkt_tag { #define DN_TO_ETH_OUT 5 #define DN_TO_IP6_IN 6 #define DN_TO_IP6_OUT 7 +#define DN_TO_IFB_FWD 8 dn_key output_time; /* when the pkt is due for delivery */ struct ifnet *ifp; /* interface, for ip_output */