diff options
author | thompsa <thompsa@FreeBSD.org> | 2005-07-02 23:13:31 +0000 |
---|---|---|
committer | thompsa <thompsa@FreeBSD.org> | 2005-07-02 23:13:31 +0000 |
commit | d7e928629d1fd82199b336e5a513e6308c7ec45b (patch) | |
tree | 89cd1b3b1b8fc11081f836fcc5b77a4e00d644f2 | |
parent | dd5c43b57252ea9dec17cd9c876da75544db45c4 (diff) | |
download | FreeBSD-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.h | 2 | ||||
-rw-r--r-- | sys/i386/include/_types.h | 2 | ||||
-rw-r--r-- | sys/net/if_bridge.c | 75 | ||||
-rw-r--r-- | sys/netinet/ip_var.h | 6 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 6 |
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 */ |