Greetings!
I've recently upgraded from 4.1.13 to 4.1.22 (as included in the
NetBSD 4 branch), and my router started panicing when IPv6 ICMP
messages attempted to pass through it.
Here's the panic:
trap type 0x34: cpu 0, pc=1043d14 npc=1043d18
pstate=ffffffff99820006<PRIV,IE>
kernel trap 34: mem address not aligned
Stopped at netbsd:fr_makefrip+0xd74: ldx [%l2
+ 0x18], %g1
This is on sparc64, an architecture that cares about alignment. The
problem turns out to be in the inline function frpr_icmp6, at line
760 (in the NetBSD copy, at least) of fil.c. It's the 'if' line in
this stanza of code that's new since 4.1.13:
/*
* If the destination of this packet doesn't match the
* source of the original packet then this packet is
* not correct.
*/
ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN);
if (IP6_NEQ(&fin->fin_fi.fi_dst,
(i6addr_t *)&ip6->ip6_src))
fin->fin_flx |= FI_BAD;
I note that ip6 is declared as type ip6_t, which is really ip6_hdr,
which looks like this (at least in NetBSD):
struct ip6_hdr {
union {
struct ip6_hdrctl {
u_int32_t ip6_un1_flow; /* 20 bits of flow-
ID */
u_int16_t ip6_un1_plen; /* payload length */
u_int8_t ip6_un1_nxt; /* next header */
u_int8_t ip6_un1_hlim; /* hop limit */
} ip6_un1;
u_int8_t ip6_un2_vfc; /* 4 bits version, top 4
bits class */
} ip6_ctlun;
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
} __attribute__((__packed__));
Sure enough, that 8-bit ip6_un2_vfc un-aligns ip6_src. So, we can't
refer the ip6_src the way IP6_NEQ attempts to. What's the preferred
way to get the ip6_src member from that packed struct? I've put the
following hack in place in my kernel so that my router can keep
running, but I don't like calling memcpy for each IPv6 ICMP packet
that comes through.
Index: fil.c
===================================================================
RCS file: /cvsroot/src/sys/dist/ipf/netinet/fil.c,v
retrieving revision 1.28.2.3
diff -u -r1.28.2.3 fil.c
--- fil.c 30 May 2007 21:48:21 -0000 1.28.2.3
+++ fil.c 5 Jun 2007 09:26:58 -0000
@@ -726,6 +726,7 @@
if (fin->fin_dlen > 1) {
ip6_t *ip6;
+ struct in6_addr ip6_src;
icmp6 = fin->fin_dp;
@@ -757,8 +758,8 @@
* not correct.
*/
ip6 = (ip6_t *)((char *)icmp6 +
ICMPERR_ICMPHLEN);
- if (IP6_NEQ(&fin->fin_fi.fi_dst,
- (i6addr_t *)&ip6->ip6_src))
+ memcpy(&ip6_src, &ip6->ip6_src, sizeof(struct
in6_addr));
+ if (IP6_NEQ(&fin->fin_fi.fi_dst, &ip6_src))
fin->fin_flx |= FI_BAD;
minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof
(ip6_t);
So, what's the preferred fix for this alignment problem?
Thanks a lot for all your work, Darren!
- Geoff