Korea FreeBSD Users Group News, Internal, Projects, Home
Software, Support, Documentation

[KFUG] bridge(4)



[ ³¯Â¥¼ø »öÀÎ ] [ ´ñ±Û¼ø »öÀÎ ] [ ÃÖ»óÀ§ »öÀÎ] [ °Ë»ö]

[ÀÌÀü ±Û] [´ÙÀ½ ±Û] [ÀÌÀü ´ñ±Û] [´ÙÀ½ ´ñ±Û]


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, &ether_tmp, sizeof(ether_tmp));
+	bcopy(&eh->ether_shost, &eh->ether_dhost, sizeof(ether_tmp));
+	bcopy(&ether_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 $
Powered by FreeBSD