summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2005-07-02 23:13:31 +0000
committerthompsa <thompsa@FreeBSD.org>2005-07-02 23:13:31 +0000
commitd7e928629d1fd82199b336e5a513e6308c7ec45b (patch)
tree89cd1b3b1b8fc11081f836fcc5b77a4e00d644f2
parentdd5c43b57252ea9dec17cd9c876da75544db45c4 (diff)
downloadFreeBSD-src-d7e928629d1fd82199b336e5a513e6308c7ec45b.zip
FreeBSD-src-d7e928629d1fd82199b336e5a513e6308c7ec45b.tar.gz
Check the alignment of the IP header before passing the packet up to the
packet filter. This would cause a panic on architectures that require strict alignment such as sparc64 (tier1) and ia64/ppc (tier2). This adds two new macros that check the alignment, these are compile time dependent on __NO_STRICT_ALIGNMENT which is set for i386 and amd64 where alignment isn't need so the cost is avoided. IP_HDR_ALIGNED_P() IP6_HDR_ALIGNED_P() Move bridge_ip_checkbasic()/bridge_ip6_checkbasic() up so that the alignment is checked for ipfw and dummynet too. PR: ia64/81284 Obtained from: NetBSD Approved by: re (dwhite), mlaier (mentor)
-rw-r--r--sys/amd64/include/_types.h2
-rw-r--r--sys/i386/include/_types.h2
-rw-r--r--sys/net/if_bridge.c75
-rw-r--r--sys/netinet/ip_var.h6
-rw-r--r--sys/netinet6/ip6_var.h6
5 files changed, 64 insertions, 27 deletions
diff --git a/sys/amd64/include/_types.h b/sys/amd64/include/_types.h
index ab19b6c..4217c82 100644
--- a/sys/amd64/include/_types.h
+++ b/sys/amd64/include/_types.h
@@ -43,6 +43,8 @@
#error this file needs sys/cdefs.h as a prerequisite
#endif
+#define __NO_STRICT_ALIGNMENT
+
/*
* Basic types upon which most other types are built.
*/
diff --git a/sys/i386/include/_types.h b/sys/i386/include/_types.h
index 7df734d..b272cf0 100644
--- a/sys/i386/include/_types.h
+++ b/sys/i386/include/_types.h
@@ -43,6 +43,8 @@
#error this file needs sys/cdefs.h as a prerequisite
#endif
+#define __NO_STRICT_ALIGNMENT
+
/*
* Basic types upon which most other types are built.
*/
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 35b7568..0b2515c 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -2249,7 +2249,28 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp,
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) {
+ error = -1;
args.rule = ip_dn_claim_rule(*mp);
if (args.rule != NULL && fw_one_pass)
goto ipfwpass; /* packet already partially processed */
@@ -2286,26 +2307,22 @@ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp,
}
ipfwpass:
+ error = 0;
+
/*
- * Check basic packet sanity and run pfil through pfil.
+ * Run the packet through pfil
*/
switch (ether_type)
{
case ETHERTYPE_IP :
- error = (dir == PFIL_IN) ? bridge_ip_checkbasic(mp) : 0;
/*
* before calling the firewall, swap fields the same as
* IP does. here we assume the header is contiguous
*/
- if (error == 0) {
- ip = mtod(*mp, struct ip *);
+ ip = mtod(*mp, struct ip *);
- ip->ip_len = ntohs(ip->ip_len);
- ip->ip_off = ntohs(ip->ip_off);
- } else {
- error = -1;
- break;
- }
+ 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
@@ -2314,21 +2331,21 @@ ipfwpass:
* Keep the order:
* in_if -> bridge_if -> out_if
*/
- if (error == 0 && pfil_bridge && dir == PFIL_OUT)
+ if (pfil_bridge && dir == PFIL_OUT)
error = pfil_run_hooks(&inet_pfil_hook, mp, bifp,
dir, NULL);
- if (*mp == NULL) /* filter may consume */
+ if (*mp == NULL || error != 0) /* filter may consume */
break;
- if (error == 0 && pfil_member)
+ if (pfil_member)
error = pfil_run_hooks(&inet_pfil_hook, mp, ifp,
dir, NULL);
- if (*mp == NULL) /* filter may consume */
+ if (*mp == NULL || error != 0) /* filter may consume */
break;
- if (error == 0 && pfil_bridge && dir == PFIL_IN)
+ if (pfil_bridge && dir == PFIL_IN)
error = pfil_run_hooks(&inet_pfil_hook, mp, bifp,
dir, NULL);
@@ -2342,23 +2359,21 @@ ipfwpass:
break;
# ifdef INET6
case ETHERTYPE_IPV6 :
- error = (dir == PFIL_IN) ? bridge_ip6_checkbasic(mp) : 0;
-
- if (error == 0 && pfil_bridge && dir == PFIL_OUT)
+ if (pfil_bridge && dir == PFIL_OUT)
error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp,
dir, NULL);
- if (*mp == NULL) /* filter may consume */
+ if (*mp == NULL || error != 0) /* filter may consume */
break;
- if (error == 0 && pfil_member)
+ if (pfil_member)
error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp,
dir, NULL);
- if (*mp == NULL) /* filter may consume */
+ if (*mp == NULL || error != 0) /* filter may consume */
break;
- if (error == 0 && pfil_bridge && dir == PFIL_IN)
+ if (pfil_bridge && dir == PFIL_IN)
error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp,
dir, NULL);
break;
@@ -2421,7 +2436,14 @@ bridge_ip_checkbasic(struct mbuf **mp)
if (*mp == NULL)
return -1;
- if (__predict_false(m->m_len < sizeof (struct ip))) {
+ 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;
@@ -2509,18 +2531,17 @@ bridge_ip6_checkbasic(struct mbuf **mp)
* 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 *
+ /* 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))) {
+ } 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++;
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index c7541bc..e64570b 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -136,6 +136,12 @@ struct ipstat {
/* mbuf flag used by ip_fastfwd */
#define M_FASTFWD_OURS M_PROTO1 /* changed dst to local */
+#ifdef __NO_STRICT_ALIGNMENT
+#define IP_HDR_ALIGNED_P(ip) 1
+#else
+#define IP_HDR_ALIGNED_P(ip) ((((intptr_t) (ip)) & 3) == 0)
+#endif
+
struct ip;
struct inpcb;
struct route;
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 4d0be83..227f75e 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -282,6 +282,12 @@ struct ip6aux {
#define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */
#define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */
+#ifdef __NO_STRICT_ALIGNMENT
+#define IP6_HDR_ALIGNED_P(ip) 1
+#else
+#define IP6_HDR_ALIGNED_P(ip) ((((intptr_t) (ip)) & 3) == 0)
+#endif
+
extern struct ip6stat ip6stat; /* statistics */
extern int ip6_defhlim; /* default hop limit */
extern int ip6_defmcasthlim; /* default multicast hop limit */
OpenPOWER on IntegriCloud