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

Reply via email to