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

Reply via email to