|
News,
Internal,
Projects,
Home Software, Support, Documentation |
FreeBSD의 bridge(4)는 OpenBSD/NetBSD와는 달리 많은 문제점이 있었습니다. 그 중에서도 가장 큰문제는 bridge모드로 사용중일때 pf/ipf에서는 stateful inspection이 불가능하고 inbound packet만 검사하는 단점이 있었습니다. 다음의 patch는 IPv6지원, SNAP/LLC지원, inbound, outbound 모두 검사함으로써 pf/ipf의 stateful inspection을 가능하도록 합니다. 또한 pf는 IP fragment packet이 들어오면 이를 reassemble하기 때문에 bridge(4)에서 fragmentation이 가능하게 하고 IP_DF가 설정되면 ICMP error를 전송함으로써 path MTU discovery가 제대로 동작하도록 합니다. #cd /usr/src #patch -p0 < /tmp/bridge.patch 후에 일반적인 커널빌드절차를 거치면 됩니다. 패치는 5.2-CURRENT용이지만 코드를 보실수 있는 분은 5.2.1R로 의 변환은 쉽게할 수 있을겁니다. 현재 pf/ipf는 어느정도 시험을 마쳤지만 ipfw의 경우는 시험이 제대로 이루어지지 않았습니다. 이론상은 ipfw는 아무영향도 없어야 합니다. <사용법> 패치된 커널로 부트후 #kldload bridge #kldload pf/ipf/ipfw #방화벽에 해당하는 룰을 설정합니다. #pfctl -e -Fa -f /tmp/test.rule or #ipf -Fa -f /tmp/test.rule or #sh /tmp/test.rule.ipfw #sysctl net.link.ether.bridge.config="fxp0, fxp1" #sysctl net.link.ether.bridge.enable=1 #시험 예전의 bridge에있던 net.link.ether.bridge.bdg_ipf는 사라졌기 때문에 pf/ipf를 사용하는 분은 따로 설정할 필요가 없습니다. 동작여부나 문제점을 알려주시면 감사하겠습니다. -- Pyun YongHyeon <http://www.kr.freebsd.org/~yongari>
--- sys/net/if_ethersubr.c.orig Mon Mar 15 16:56:50 2004
+++ sys/net/if_ethersubr.c Thu Apr 1 20:17:12 2004
@@ -595,14 +595,8 @@
* while doing its job. This is reflected by it
* returning a NULL mbuf pointer.
*/
- if (m == NULL) {
- if (bif == BDG_BCAST || bif == BDG_MCAST)
- if_printf(ifp,
- "bridge dropped %s packet\n",
- bif == BDG_BCAST ? "broadcast" :
- "multicast");
+ if (m == NULL)
return;
- }
/*
* But in some cases the bridge may return the
* packet for us to free; sigh.
--- sys/net/bridge.c.orig Thu Feb 26 16:11:22 2004
+++ sys/net/bridge.c Thu Apr 1 20:15:43 2004
@@ -87,7 +87,14 @@
* - be very careful when bridging VLANs
* - loop detection is still not very robust.
*/
+
#include "opt_pfil_hooks.h"
+#include "opt_inet6.h"
+#include "opt_random_ip_id.h"
+/* XXX kernel module hack */
+#define PFIL_HOOKS 1
+#define INET6 1
+#define RANDOM_IP_ID 1
#include <sys/param.h>
#include <sys/mbuf.h>
@@ -100,6 +107,9 @@
#include <sys/sysctl.h>
#include <net/if.h>
+#ifdef PFIL_HOOKS
+#include <net/if_llc.h>
+#endif
#include <net/if_types.h>
#include <net/if_var.h>
@@ -108,10 +118,12 @@
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h> /* for struct arpcom */
+#include <netinet/ip_icmp.h>
#ifdef PFIL_HOOKS
#include <net/pfil.h>
#include <netinet/ip_var.h>
+#include <machine/in_cksum.h>
#endif
#include <net/route.h>
@@ -119,6 +131,11 @@
#include <netinet/ip_dummynet.h>
#include <net/bridge.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+
/*--------------------*/
#define ETHER_ADDR_COPY(_dst,_src) bcopy(_src, _dst, ETHER_ADDR_LEN)
@@ -243,9 +260,6 @@
static int bdginit(void);
static void parse_bdg_cfg(void);
-static int bdg_ipf; /* IPFilter enabled in bridge */
-SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, ipf, CTLFLAG_RW,
- &bdg_ipf, 0,"Pass bridged pkts through IPFilter");
static int bdg_ipfw;
SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, ipfw, CTLFLAG_RW,
&bdg_ipfw,0,"Pass bridged pkts through firewall");
@@ -293,6 +307,16 @@
static struct callout bdg_callout;
+#ifdef PFIL_HOOKS
+#define BRIDGE_IN PFIL_IN
+#define BRIDGE_OUT PFIL_OUT
+struct mbuf *bridge_filter(struct mbuf *, struct ifnet *, int);
+#endif
+void bridge_broadcast(struct ifnet *, struct ifnet *, struct mbuf *);
+void bridge_fragment(struct ifnet *, struct mbuf *);
+void bridge_send_icmp_err(struct ifnet *, struct ether_header *,
+ struct mbuf *, int, struct llc *, int, int);
+
/*
* Add an interface to a cluster, possibly creating a new entry in
* the cluster table. This requires reallocation of the table and
@@ -632,8 +656,6 @@
SYSCTL_OID_COMPAT(parent, nbr, name, (access), \
ptr, arg, handler, fmt, descr)
-SYSCTL_INT_COMPAT(_net_link_ether, OID_AUTO, bridge_ipf, CTLFLAG_RW,
- &bdg_ipf, 0,"Pass bridged pkts through IPFilter");
SYSCTL_INT_COMPAT(_net_link_ether, OID_AUTO, bridge_ipfw, CTLFLAG_RW,
&bdg_ipfw,0,"Pass bridged pkts through firewall");
SYSCTL_STRUCT_COMPAT(_net_link_ether, PF_BDG, bdgstats, CTLFLAG_RD,
@@ -909,260 +931,677 @@
else \
bdg_predict++; \
} while (0);
- struct ether_header *eh;
- struct ifnet *src;
- struct ifnet *ifp, *last;
- int shared = bdg_copy; /* someone else is using the mbuf */
- struct ifnet *real_dst = dst; /* real dst from ether_output */
- struct ip_fw_args args;
- struct ether_header save_eh;
- struct mbuf *m;
-
- DDB(quad_t ticks; ticks = rdtsc();)
-
- args.rule = ip_dn_claim_rule(m0);
- if (args.rule)
- shared = 0; /* For sure this is our own mbuf. */
- else
- bdg_thru++; /* count 1st time through bdg_forward */
-
- /*
- * The packet arrives with the Ethernet header at the front.
- */
- eh = mtod(m0, struct ether_header *);
-
- src = m0->m_pkthdr.rcvif;
- if (src == NULL) { /* packet from ether_output */
- BDG_LOCK();
- dst = bridge_dst_lookup(eh, BDG_CLUSTER(real_dst));
- BDG_UNLOCK();
- }
-
- if (dst == BDG_DROP) { /* this should not happen */
- printf("xx bdg_forward for BDG_DROP\n");
- m_freem(m0);
- bdg_dropped++;
- return NULL;
- }
- if (dst == BDG_LOCAL) { /* this should not happen as well */
- printf("xx ouch, bdg_forward for local pkt\n");
- return m0;
- }
- if (dst == BDG_BCAST || dst == BDG_MCAST) {
- /* need a copy for the local stack */
- shared = 1;
- }
-
- /*
- * Do filtering in a very similar way to what is done in ip_output.
- * Only if firewall is loaded, enabled, and the packet is not
- * from ether_output() (src==NULL, or we would filter it twice).
- * Additional restrictions may apply e.g. non-IP, short packets,
- * and pkts already gone through a pipe.
- */
- if (src != NULL && (
-#ifdef PFIL_HOOKS
- (inet_pfil_hook.ph_busy_count >= 0 && bdg_ipf != 0) ||
-#endif
- (IPFW_LOADED && bdg_ipfw != 0))) {
-
- int i;
+ struct ether_header *eh;
+ struct ifnet *src;
+ int shared = bdg_copy; /* someone else is using the mbuf */
+ struct ifnet *real_dst = dst; /* real dst from ether_output */
+ struct ip_fw_args args;
+ struct ether_header save_eh;
+ struct mbuf *m = NULL;
+
+ DDB(quad_t ticks; ticks = rdtsc();)
+
+ args.rule = ip_dn_claim_rule(m0);
+ if (args.rule)
+ shared = 0; /* For sure this is our own mbuf. */
+ else
+ bdg_thru++; /* count 1st time through bdg_forward */
- if (args.rule != NULL && fw_one_pass)
- goto forward; /* packet already partially processed */
/*
- * i need some amt of data to be contiguous, and in case others need
- * the packet (shared==1) also better be in the first mbuf.
+ * The packet arrives with the Ethernet header at the front.
*/
- i = min(m0->m_pkthdr.len, max_protohdr) ;
- if (shared || m0->m_len < i) {
- m0 = m_pullup(m0, i);
- if (m0 == NULL) {
- printf("%s: m_pullup failed\n", __func__); /* XXXDPRINTF*/
+ eh = mtod(m0, struct ether_header *);
+
+ src = m0->m_pkthdr.rcvif;
+ if (src == NULL) { /* packet from ether_output */
+ BDG_LOCK();
+ dst = bridge_dst_lookup(eh, BDG_CLUSTER(real_dst));
+ BDG_UNLOCK();
+ }
+
+ if (dst == BDG_DROP) { /* this should not happen */
+ printf("xx bdg_forward for BDG_DROP\n");
+ m_freem(m0);
bdg_dropped++;
return NULL;
- }
- eh = mtod(m0, struct ether_header *);
+ }
+ if (dst == BDG_LOCAL) { /* this should not happen as well */
+ printf("xx ouch, bdg_forward for local pkt\n");
+ return m0;
+ }
+ if (dst == BDG_BCAST || dst == BDG_MCAST) {
+ /* need a copy for the local stack */
+ shared = 1;
}
/*
- * Processing below expects the Ethernet header is stripped.
- * Furthermore, the mbuf chain might be replaced at various
- * places. To deal with this we copy the header to a temporary
- * location, strip the header, and restore it as needed.
+ * Do filtering in a very similar way to what is done in ip_output.
+ * Only if firewall is loaded, enabled, and the packet is not
+ * from ether_output() (src==NULL, or we would filter it twice).
+ * Additional restrictions may apply e.g. non-IP, short packets,
+ * and pkts already gone through a pipe.
*/
- bcopy(eh, &save_eh, ETHER_HDR_LEN); /* local copy for restore */
- m_adj(m0, ETHER_HDR_LEN); /* temporarily strip header */
-
#ifdef PFIL_HOOKS
- /*
- * NetBSD-style generic packet filter, pfil(9), hooks.
- * Enables ipf(8) in bridging.
- */
- if (inet_pfil_hook.ph_busy_count >= 0 &&
- m0->m_pkthdr.len >= sizeof(struct ip) &&
- ntohs(save_eh.ether_type) == ETHERTYPE_IP) {
- /*
- * before calling the firewall, swap fields the same as IP does.
- * here we assume the pkt is an IP one and the header is contiguous
- */
- struct ip *ip = mtod(m0, struct ip *);
+ if (src != NULL)
+ if ((m0 = bridge_filter(m0, src, BRIDGE_IN)) == NULL) {
+ bdg_dropped++;
+ return (NULL);
+ }
+#endif
+ if (src != NULL && IPFW_LOADED && bdg_ipfw != 0) {
- ip->ip_len = ntohs(ip->ip_len);
- ip->ip_off = ntohs(ip->ip_off);
+ int i;
- if (pfil_run_hooks(&inet_pfil_hook, &m0, src, PFIL_IN) != 0) {
- /* NB: hook should consume packet */
- return NULL;
- }
- if (m0 == NULL) /* consumed by filter */
+ if (args.rule != NULL && fw_one_pass)
+ goto forward; /* packet already partially processed */
+ /*
+ * i need some amt of data to be contiguous, and in case others
+ * need the packet (shared==1) also better be in the first mbuf.
+ */
+ i = min(m0->m_pkthdr.len, max_protohdr);
+ if (shared || m0->m_len < i) {
+ m0 = m_pullup(m0, i);
+ if (m0 == NULL) {
+ /* XXXDPRINTF */
+ printf("%s: m_pullup failed\n", __func__);
+ bdg_dropped++;
+ return NULL;
+ }
+ eh = mtod(m0, struct ether_header *);
+ }
+
+ /*
+ * Processing below expects the Ethernet header is stripped.
+ * Furthermore, the mbuf chain might be replaced at various
+ * places. To deal with this we copy the header to a temporary
+ * location, strip the header, and restore it as needed.
+ */
+ bcopy(eh, &save_eh, ETHER_HDR_LEN); /* local copy for restore */
+ m_adj(m0, ETHER_HDR_LEN); /* temporarily strip header */
+
+ /*
+ * Prepare arguments and call the firewall.
+ */
+ if (!IPFW_LOADED || bdg_ipfw == 0) {
+ EH_RESTORE(m0); /* restore Ethernet header */
+ goto forward; /* not using ipfw, accept the packet */
+ }
+
+ /*
+ * XXX The following code is very similar to the one in
+ * if_ethersubr.c:ether_ipfw_chk()
+ */
+
+ args.m = m0; /* the packet we are looking at */
+ args.oif = NULL; /* this is an input packet */
+ args.next_hop = NULL; /* we do not support forward yet */
+ args.eh = &save_eh; /* MAC header for bridged/MAC packets */
+ i = ip_fw_chk_ptr(&args);
+ m0 = args.m; /* in case the firewall used the mbuf */
+
+ if (m0 != NULL)
+ EH_RESTORE(m0); /* restore Ethernet header */
+
+ if ((i & IP_FW_PORT_DENY_FLAG) || m0 == NULL) /* drop */
+ return m0;
+
+ if (i == 0) /* a PASS rule. */
+ goto forward;
+ if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG)) {
+ /*
+ * Pass the pkt to dummynet, which consumes it.
+ * If shared, make a copy and keep the original.
+ */
+ if (shared) {
+ m = m_copypacket(m0, M_DONTWAIT);
+ if (m == NULL) { /* copy failed, give up */
+ bdg_dropped++;
+ return NULL;
+ }
+ } else {
+ m = m0; /* pass the original to dummynet */
+ m0 = NULL; /* and nothing back to the caller */
+ }
+
+ args.oif = real_dst;
+ ip_dn_io_ptr(m, (i & 0xffff),DN_TO_BDG_FWD, &args);
+ return m0;
+ }
+ /*
+ * XXX at some point, add support for divert/forward actions.
+ * If none of the above matches, we have to drop the packet.
+ */
+ bdg_ipfw_drops++;
return m0;
- /*
- * If we get here, the firewall has passed the pkt, but the mbuf
- * pointer might have changed. Restore ip and the fields ntohs()'d.
- */
- ip = mtod(m0, struct ip *);
- ip->ip_len = htons(ip->ip_len);
- ip->ip_off = htons(ip->ip_off);
}
-#endif /* PFIL_HOOKS */
+forward:
/*
- * Prepare arguments and call the firewall.
+ * now real_dst is used to determine the cluster where to forward.
+ * For packets coming from ether_input, this is the one of the 'src'
+ * interface, whereas for locally generated packets (src==NULL) it
+ * is the cluster of the original destination interface, which
+ * was already saved into real_dst.
*/
- if (!IPFW_LOADED || bdg_ipfw == 0) {
- EH_RESTORE(m0); /* restore Ethernet header */
- goto forward; /* not using ipfw, accept the packet */
+ if (src != NULL)
+ real_dst = src;
+
+ if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_UNKNOWN) {
+ bridge_broadcast(src, real_dst, m0);
+ /*
+ * m0 still valid... m0 should be return back to caller
+ * in order to make ARP work. Yech!
+ */
+ } else if (dst && bridge_ifok(dst, src, real_dst)) {
+#ifdef PFIL_HOOKS
+ if ((m0 = bridge_filter(m0, dst, BRIDGE_OUT)) == NULL) {
+ bdg_dropped++;
+ return (NULL);
+ }
+#endif
+ /*
+ * pf's scrub rule can generate IP packets larger than
+ * that of interface MTU.
+ */
+ if ((m0->m_pkthdr.len - sizeof(struct ether_header)) >
+ dst->if_mtu)
+ bridge_fragment(dst, m0);
+ else {
+ if (IF_HANDOFF(&dst->if_snd, m0, dst))
+ BDG_STAT(dst, BDG_OUT);
+ else
+ bdg_dropped++;
+ }
+ m0 = NULL;
}
- /*
- * XXX The following code is very similar to the one in
- * if_ethersubr.c:ether_ipfw_chk()
- */
+ DDB(bdg_fw_ticks += (u_long)(rdtsc() - ticks) ; bdg_fw_count++ ;
+ if (bdg_fw_count != 0) bdg_fw_avg = bdg_fw_ticks/bdg_fw_count; )
- args.m = m0; /* the packet we are looking at */
- args.oif = NULL; /* this is an input packet */
- args.next_hop = NULL; /* we do not support forward yet */
- args.eh = &save_eh; /* MAC header for bridged/MAC packets */
- i = ip_fw_chk_ptr(&args);
- m0 = args.m; /* in case the firewall used the mbuf */
-
- if (m0 != NULL)
- EH_RESTORE(m0); /* restore Ethernet header */
-
- if ( (i & IP_FW_PORT_DENY_FLAG) || m0 == NULL) /* drop */
- return m0;
-
- if (i == 0) /* a PASS rule. */
- goto forward;
- if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG)) {
- /*
- * Pass the pkt to dummynet, which consumes it.
- * If shared, make a copy and keep the original.
- */
- if (shared) {
- m = m_copypacket(m0, M_DONTWAIT);
- if (m == NULL) { /* copy failed, give up */
- bdg_dropped++;
- return NULL;
- }
- } else {
- m = m0 ; /* pass the original to dummynet */
- m0 = NULL ; /* and nothing back to the caller */
- }
+ return m0;
+#undef EH_RESTORE
+}
- args.oif = real_dst;
- ip_dn_io_ptr(m, (i & 0xffff),DN_TO_BDG_FWD, &args);
- return m0;
+#ifdef PFIL_HOOKS
+/*
+ * Filter IP packets by peeking into the ethernet frame. This violates
+ * the ISO model, but allows us to act as a IP filter at the data link
+ * layer. As a result, most of this code will look familiar to those
+ * who've read net/if_ethersubr.c and netinet/ip_input.c
+ */
+struct mbuf *
+bridge_filter(struct mbuf *m, struct ifnet *ifp, int dir)
+{
+ struct ether_addr *src, *dst;
+ struct ether_header eh;
+ struct llc llc;
+ int hassnap = 0;
+ struct ip *ip;
+ int hlen;
+ u_int16_t etype;
+
+ m_copydata(m, 0, sizeof(struct ether_header), (caddr_t)&eh);
+ dst = (struct ether_addr *)&eh.ether_dhost[0];
+ src = (struct ether_addr *)&eh.ether_shost[0];
+
+ etype = ntohs(eh.ether_type);
+
+ if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6) {
+ if (etype > ETHERMTU ||
+ m->m_pkthdr.len < (LLC_SNAPFRAMELEN +
+ sizeof(struct ether_header)))
+ return (m);
+
+ m_copydata(m, sizeof(struct ether_header),
+ LLC_SNAPFRAMELEN, (caddr_t)&llc);
+
+ if (llc.llc_dsap != LLC_SNAP_LSAP ||
+ llc.llc_ssap != LLC_SNAP_LSAP ||
+ llc.llc_control != LLC_UI ||
+ llc.llc_snap.org_code[0] ||
+ llc.llc_snap.org_code[1] ||
+ llc.llc_snap.org_code[2])
+ return (m);
+
+ etype = ntohs(llc.llc_snap.ether_type);
+ if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6)
+ return (m);
+ hassnap = 1;
}
- /*
- * XXX at some point, add support for divert/forward actions.
- * If none of the above matches, we have to drop the packet.
- */
- bdg_ipfw_drops++;
- return m0;
- }
-forward:
- /*
- * Again, bring up the headers in case of shared bufs to avoid
- * corruptions in the future.
- */
- if (shared) {
- int i = min(m0->m_pkthdr.len, max_protohdr);
- m0 = m_pullup(m0, i);
- if (m0 == NULL) {
- bdg_dropped++;
- return NULL;
+ m_adj(m, sizeof(struct ether_header));
+ if (hassnap)
+ m_adj(m, LLC_SNAPFRAMELEN);
+
+ switch (etype) {
+
+ case ETHERTYPE_IP:
+ if (m->m_pkthdr.len < sizeof(struct ip))
+ goto dropit;
+
+ /* Copy minimal header, and drop invalids */
+ if (m->m_len < sizeof(struct ip) &&
+ (m = m_pullup(m, sizeof(struct ip))) == NULL) {
+ ipstat.ips_toosmall++;
+ return (NULL);
+ }
+ ip = mtod(m, struct ip *);
+
+ if (ip->ip_v != IPVERSION) {
+ ipstat.ips_badvers++;
+ goto dropit;
+ }
+
+ hlen = ip->ip_hl << 2; /* get whole header length */
+ if (hlen < sizeof(struct ip)) {
+ ipstat.ips_badhlen++;
+ goto dropit;
+ }
+
+ if (hlen > m->m_len) {
+ if ((m = m_pullup(m, hlen)) == NULL) {
+ ipstat.ips_badhlen++;
+ return (NULL);
+ }
+ ip = mtod(m, struct ip *);
+ }
+
+ if ((ip->ip_sum = in_cksum(m, hlen)) != 0) {
+ ipstat.ips_badsum++;
+ goto dropit;
+ }
+
+ if (ntohs(ip->ip_len) < hlen)
+ goto dropit;
+
+ if (m->m_pkthdr.len < ntohs(ip->ip_len))
+ goto dropit;
+ if (m->m_pkthdr.len > ntohs(ip->ip_len)) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = ntohs(ip->ip_len);
+ m->m_pkthdr.len = ntohs(ip->ip_len);
+ } else
+ m_adj(m, ntohs(ip->ip_len) - m->m_pkthdr.len);
+ }
+#ifndef __FreeBSD__
+#ifdef IPSEC
+ if ((sc->sc_if.if_flags & IFF_LINK2) == IFF_LINK2 &&
+ bridge_ipsec(dir, AF_INET, hlen, m))
+ return (NULL);
+#endif /* IPSEC */
+#endif
+
+#ifdef __FreeBSD__
+ ip->ip_len = ntohs(ip->ip_len);
+ ip->ip_off = ntohs(ip->ip_off);
+ m->m_pkthdr.rcvif = ifp;
+ if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, dir) != 0) {
+ printf("pfil drop\n");
+ goto dropit;
+ }
+ if (m == NULL)
+ return (NULL);
+ ip = mtod(m, struct ip *);
+ ip->ip_len = htons(ip->ip_len);
+ ip->ip_off = htons(ip->ip_off);
+#else
+#if NPF > 0
+ /* Finally, we get to filter the packet! */
+ m->m_pkthdr.rcvif = ifp;
+ if (pf_test(dir, ifp, &m) != PF_PASS)
+ goto dropit;
+ if (m == NULL)
+ goto dropit;
+#endif /* NPF */
+#endif
+
+ /* Rebuild the IP header */
+ if (m->m_len < hlen && ((m = m_pullup(m, hlen)) == NULL))
+ return (NULL);
+ if (m->m_len < sizeof(struct ip))
+ goto dropit;
+ ip = mtod(m, struct ip *);
+ ip->ip_sum = 0;
+ if (hlen == sizeof(struct ip))
+ ip->ip_sum = in_cksum_hdr(ip);
+ else
+ ip->ip_sum = in_cksum(m, hlen);
+ break;
+
+#ifdef INET6
+ case ETHERTYPE_IPV6: {
+ struct ip6_hdr *ip6;
+
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ if ((m = m_pullup(m, sizeof(struct ip6_hdr)))
+ == NULL) {
+ ip6stat.ip6s_toosmall++;
+ return (NULL);
+ }
+ }
+
+ 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 dropit;
+ }
+
+#ifndef __FreeBSD__
+#ifdef IPSEC
+ hlen = sizeof(struct ip6_hdr);
+
+ if ((sc->sc_if.if_flags & IFF_LINK2) == IFF_LINK2 &&
+ bridge_ipsec(dir, AF_INET6, hlen, m))
+ return (NULL);
+#endif /* IPSEC */
+#endif
+
+#ifdef __FreeBSD__
+ m->m_pkthdr.rcvif = ifp;
+ if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, dir) != 0)
+ goto dropit;
+ if (m == NULL)
+ return (NULL);
+#else
+#if NPF > 0
+ if (pf_test6(dir, ifp, &m) != PF_PASS)
+ goto dropit;
+ if (m == NULL)
+ return (NULL);
+#endif /* NPF */
+#endif
+
+ break;
}
- /* NB: eh is not used below; no need to recalculate it */
- }
+#endif /* INET6 */
- /*
- * now real_dst is used to determine the cluster where to forward.
- * For packets coming from ether_input, this is the one of the 'src'
- * interface, whereas for locally generated packets (src==NULL) it
- * is the cluster of the original destination interface, which
- * was already saved into real_dst.
- */
- if (src != NULL)
- real_dst = src;
+ default:
+ goto dropit;
+ break;
+ }
- last = NULL;
- if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_UNKNOWN) {
- /*
- * Scan all ports and send copies to all but the last.
- */
- IFNET_RLOCK(); /* XXX replace with generation # */
+ /* Reattach SNAP header */
+ if (hassnap) {
+ M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
+ if (m == NULL)
+ goto dropit;
+ bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
+ }
+
+ /* Reattach ethernet header */
+ M_PREPEND(m, sizeof(eh), M_DONTWAIT);
+ if (m == NULL)
+ goto dropit;
+ bcopy(&eh, mtod(m, caddr_t), sizeof(eh));
+
+ return (m);
+
+dropit:
+ if (m != NULL)
+ m_freem(m);
+ return (NULL);
+}
+#endif
+
+void
+bridge_broadcast(struct ifnet *src_if, struct ifnet *dst_if, struct mbuf *m)
+{
+ struct ifnet *ifp, *prev = NULL;
+ struct mbuf *mc = NULL;
+
+ IFNET_RLOCK();
TAILQ_FOREACH(ifp, &ifnet, if_link) {
- if (bridge_ifok(ifp, src, real_dst)) {
- if (last) {
- /*
- * At this point we know two interfaces need a copy
- * of the packet (last + ifp) so we must create a
- * copy to handoff to last.
- */
- m = m_copypacket(m0, M_DONTWAIT);
- if (m == NULL) {
- IFNET_RUNLOCK();
- printf("%s: , m_copypacket failed!\n", __func__);
- bdg_dropped++;
- return m0; /* the original is still there... */
- }
- if (IF_HANDOFF(&last->if_snd, m, last))
- BDG_STAT(last, BDG_OUT);
- else
+ if (!bridge_ifok(ifp, src_if, dst_if))
+ continue;
+ if (prev) {
+#ifdef PFIL_HOOKS
+ IFNET_RUNLOCK(); /* XXX */
+ if ((mc = bridge_filter(mc, prev, BRIDGE_OUT)) == NULL) {
+ IFNET_RLOCK();
+ prev = NULL;
+ continue;
+ }
+ IFNET_RLOCK();
+#endif
+ if ((mc->m_pkthdr.len - sizeof(struct ether_header)) >
+ prev->if_mtu)
+ bridge_fragment(prev, mc);
+ else {
+ if (IF_HANDOFF(&prev->if_snd, mc, prev))
+ BDG_STAT(prev, BDG_OUT);
+ else
+ bdg_dropped++;
+ }
+ }
+ mc = m_dup(m, M_DONTWAIT);
+ if (mc == NULL) {
+ printf("%s: , m_dup failed!\n", __func__);
bdg_dropped++;
+ continue;
}
- last = ifp;
- }
+ prev = ifp;
}
IFNET_RUNLOCK();
- } else {
- if (bridge_ifok(dst, src, real_dst))
- last = dst;
- }
- if (last) {
- if (shared) { /* need to copy */
- m = m_copypacket(m0, M_DONTWAIT);
- if (m == NULL) {
- printf("%s: sorry, m_copypacket failed!\n", __func__);
- bdg_dropped++ ;
- return m0; /* the original is still there... */
- }
- } else { /* consume original */
- m = m0, m0 = NULL;
+
+ if (prev != NULL) {
+#ifdef PFIL_HOOKS
+ if ((mc = bridge_filter(mc, prev, BRIDGE_OUT)) == NULL)
+ return;
+#endif
+ if ((mc->m_pkthdr.len - sizeof(struct ether_header)) >
+ prev->if_mtu)
+ bridge_fragment(prev, mc);
+ else {
+ if (IF_HANDOFF(&prev->if_snd, mc, prev))
+ BDG_STAT(prev, BDG_OUT);
+ else
+ bdg_dropped++;
+ }
}
- if (IF_HANDOFF(&last->if_snd, m, last))
- BDG_STAT(last, BDG_OUT);
+}
+
+void
+bridge_fragment(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ether_header eh;
+ struct llc llc;
+ struct mbuf *m0;
+ int len, error = 0;
+ int hassnap = 0;
+ u_int16_t etype;
+ struct ip *ip;
+
+ m_copydata(m, 0, sizeof(struct ether_header), (caddr_t)&eh);
+ etype = ntohs(eh.ether_type);
+
+#if NVLAN > 0
+ if (etype == ETHERTYPE_8021Q &&
+ (ifp->if_capabilities & IFCAP_VLAN_MTU) &&
+ ((m->m_pkthdr.len - sizeof(struct ether_vlan_header)) <=
+ ifp->if_mtu)) {
+ IF_HANDOFF(&ifp->if_snd, m, ifp);
+ return;
+ }
+#endif
+
+ if (etype != ETHERTYPE_IP) {
+ if (etype > ETHERMTU ||
+ m->m_pkthdr.len < (LLC_SNAPFRAMELEN +
+ sizeof(struct ether_header)))
+ goto dropit;
+
+ m_copydata(m, sizeof(struct ether_header),
+ LLC_SNAPFRAMELEN, (caddr_t)&llc);
+
+ if (llc.llc_dsap != LLC_SNAP_LSAP ||
+ llc.llc_ssap != LLC_SNAP_LSAP ||
+ llc.llc_control != LLC_UI ||
+ llc.llc_snap.org_code[0] ||
+ llc.llc_snap.org_code[1] ||
+ llc.llc_snap.org_code[2] ||
+ llc.llc_snap.ether_type != htons(ETHERTYPE_IP))
+ goto dropit;
+
+ hassnap = 1;
+ }
+
+ m_adj(m, sizeof(struct ether_header));
+ if (hassnap)
+ m_adj(m, LLC_SNAPFRAMELEN);
+
+ if (m->m_len < sizeof(struct ip) &&
+ (m = m_pullup(m, sizeof(struct ip))) == NULL)
+ goto dropit;
+ ip = mtod(m, struct ip *);
+
+ /* Respect IP_DF, return a ICMP_UNREACH_NEEDFRAG. */
+ if (ip->ip_off & htons(IP_DF)) {
+ bridge_send_icmp_err(ifp, &eh, m, hassnap, &llc,
+ ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG);
+ return;
+ }
+
+ /* ip_fragment() requires host byte ordering, Sigh */
+ ip->ip_len = ntohs(ip->ip_len);
+ ip->ip_off = ntohs(ip->ip_off);
+ error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist,
+ m->m_pkthdr.csum_flags & ~ifp->if_hwassist);
+ if (error)
+ goto dropit;
+
+ for (; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ if (error == 0) {
+ if (hassnap) {
+ M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
+ if (m == NULL) {
+ error = ENOBUFS;
+ continue;
+ }
+ bcopy(&llc, mtod(m, caddr_t),
+ LLC_SNAPFRAMELEN);
+ }
+ M_PREPEND(m, sizeof(eh), M_DONTWAIT);
+ if (m == NULL) {
+ error = ENOBUFS;
+ continue;
+ }
+ len = m->m_pkthdr.len;
+ bcopy(&eh, mtod(m, caddr_t), sizeof(eh));
+ if (IF_HANDOFF(&ifp->if_snd, m, ifp) == 0) {
+ bdg_dropped++;
+ continue;
+ }
+ BDG_STAT(ifp, BDG_OUT);
+ } else
+ m_freem(m);
+ }
+
+ if (error == 0)
+ ipstat.ips_fragmented++;
+
+ return;
+
+ dropit:
+ if (m != NULL)
+ m_freem(m);
+}
+
+void
+bridge_send_icmp_err(struct ifnet *ifp, struct ether_header *eh,
+ struct mbuf *n, int hassnap, struct llc *llc, int type, int code)
+{
+ struct ifnet *dst_if;
+ struct ip *ip;
+ struct icmp *icp;
+ struct in_addr t;
+ struct mbuf *m, *n2;
+ int hlen;
+ u_int8_t ether_tmp[ETHER_ADDR_LEN];
+
+ /* Swap ethernet addresses */
+ bcopy(&eh->ether_dhost, ðer_tmp, sizeof(ether_tmp));
+ bcopy(&eh->ether_shost, &eh->ether_dhost, sizeof(ether_tmp));
+ bcopy(ðer_tmp, &eh->ether_shost, sizeof(ether_tmp));
+
+ dst_if = bridge_dst_lookup(eh, BDG_CLUSTER(ifp));
+ if (dst_if == BDG_DROP || dst_if == BDG_BCAST || dst_if == BDG_MCAST ||
+ dst_if == BDG_LOCAL || dst_if == BDG_UNKNOWN) {
+ m_freem(n);
+ return;
+ }
+ if (!bridge_ifok(dst_if, ifp, ifp)) {
+ m_freem(n);
+ return;
+ }
+ /* process unicast only */
+ n2 = m_copypacket(n, M_DONTWAIT);
+ if (!n2) {
+ m_freem(n);
+ return;
+ }
+
+ /* icmp_do_error() requries host byte ordering as well */
+ ip = mtod(n, struct ip *);
+ ip->ip_len = ntohs(ip->ip_len);
+ ip->ip_off = ntohs(ip->ip_off);
+ m = icmp_do_error(n, type, code, 0, ifp);
+ if (m == NULL) {
+ m_freem(n2);
+ return;
+ }
+
+ n = n2;
+
+ ip = mtod(m, struct ip *);
+ hlen = ip->ip_hl << 2;
+ t = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = t;
+ /* reset to network byte ordering */
+ ip->ip_len = htons(ip->ip_len);
+ ip->ip_off = htons(ip->ip_off);
+
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - hlen);
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= htons(IP_DF);
+ ip->ip_id = htons(ip_randomid());
+ ip->ip_ttl = MAXTTL;
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(m, hlen);
+
+ /* Reattach SNAP header */
+ if (hassnap) {
+ M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
+ if (m == NULL)
+ goto dropit;
+ bcopy(llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
+ }
+
+ /* Reattach ethernet header */
+ M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
+ if (m == NULL)
+ goto dropit;
+ bcopy(eh, mtod(m, caddr_t), sizeof(*eh));
+ if (IF_HANDOFF(&dst_if->if_snd, m, dst_if))
+ BDG_STAT(dst_if, BDG_OUT);
else
- bdg_dropped++;
- }
+ bdg_dropped++;
+ m_freem(n);
+ return;
- DDB(bdg_fw_ticks += (u_long)(rdtsc() - ticks) ; bdg_fw_count++ ;
- if (bdg_fw_count != 0) bdg_fw_avg = bdg_fw_ticks/bdg_fw_count; )
- return m0;
-#undef EH_RESTORE
+ dropit:
+ m_freem(n);
}
/*
--- sys/netinet/ip_icmp.h.orig Thu Feb 26 16:11:23 2004
+++ sys/netinet/ip_icmp.h Thu Apr 1 15:13:54 2004
@@ -195,6 +195,8 @@
(type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
#ifdef _KERNEL
+struct mbuf *
+ icmp_do_error(struct mbuf *, int, int, n_long, struct ifnet *);
void icmp_error(struct mbuf *, int, int, n_long, struct ifnet *);
void icmp_input(struct mbuf *, int);
#endif
--- sys/netinet/ip_icmp.c.orig Thu Feb 26 16:11:23 2004
+++ sys/netinet/ip_icmp.c Thu Apr 1 15:12:25 2004
@@ -138,11 +138,19 @@
* in response to bad packet ip.
*/
void
-icmp_error(n, type, code, dest, destifp)
- struct mbuf *n;
- int type, code;
- n_long dest;
- struct ifnet *destifp;
+icmp_error(struct mbuf *n, int type, int code, n_long dest,
+ struct ifnet *destifp)
+{
+ struct mbuf *m;
+
+ m = icmp_do_error(n, type, code, dest, destifp);
+ if (m != NULL)
+ icmp_reflect(m);
+}
+
+struct mbuf *
+icmp_do_error(struct mbuf *n, int type, int code, n_long dest,
+ struct ifnet *destifp)
{
register struct ip *oip = mtod(n, struct ip *), *nip;
register unsigned oiplen = oip->ip_hl << 2;
@@ -246,10 +254,12 @@
m_tag_unlink(n, mtag);
m_tag_prepend(m, mtag);
}
- icmp_reflect(m);
+ m_freem(n);
+ return (m);
freeit:
m_freem(n);
+ return (NULL);
}
/*
--- sys/modules/bridge/Makefile.orig Wed Sep 24 02:54:04 2003
+++ sys/modules/bridge/Makefile Thu Apr 1 15:47:28 2004
@@ -3,7 +3,8 @@
.PATH: ${.CURDIR}/../../net
KMOD= bridge
SRCS= bridge.c
-SRCS+= opt_pfil_hooks.h
+SRCS+= opt_pfil_hooks.h opt_inet6.h opt_random_ip_id.h
+CFLAGS += -g
#
# By default don't enable pfil hooks support. This means you
@@ -11,7 +12,15 @@
# uncomment the line below
#
opt_pfil_hooks.h:
-# echo "#define PFIL_HOOKS 1" > opt_pfil_hooks.h
+ echo "#define PFIL_HOOKS 1" > opt_pfil_hooks.h
touch opt_pfil_hooks.h
+
+opt_inet6.h:
+ echo "#define INET6 1" > opt_inet6.h
+ touch opt_inet6.h
+
+opt_random_ip_id.h:
+ echo "#define RANDOM_IP_ID 1" > opt_random_ip_id.h
+ touch opt_random_ip_id.h
.include <bsd.kmod.mk>
_______________________________________________ 한국 FreeBSD 사용자 그룹(KFUG) questions 메일링 리스트 questions at kr.FreeBSD.org http://www.kr.FreeBSD.org/mailman/listinfo/questions
|
Copyright © 1998-2005 Korea FreeBSD Users Group. All rights reserved. webmaster at kr.FreeBSD.org $Date: 2004/04/02 14:12:10 $ |
|