Geoff Adams wrote:
> 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?
Having read the other replies, this is sounding like a compiler bug.
Rather than do a memcpy to work around this, you might like to try:
if (bcmp(&fin->fin_fi.fi_dst, &ip6->ip6_src. sizeof(i6addr_t)) != 0)
fin->fin_flx |= FI_BAD:
Darren