The IPv6 source address selection algorithm breaks ties by performing a
bitwise match of each candidate source address against the destination
address. The longest matching source address wins, regardless of which
interface the source address came from.

In my carp setup, this is causing a problem.

Consider the carp address 2001:DB8:10::14, which is configured on
firewall A (carp master state) and firewall B (carp backup state),
each of which has another address in the same prefix on a non-carp
interface (A has 2001:DB8:10::1 and B has 2001:DB8:10::11).

In this setup, connecting from A to B or from B to A via IPv6 is impossible.
A will use 2001:DB8:10::14 as source address when it sends neighbour
solicitations to B (2001:DB8:10::11). Since 2001:DB8:10::14 is a local
address from B's point of view, B doesn't reply to neighbour solicitations
sent by A. The only currently available workaround is to re-configure
addresses in a way that outsmarts the longest match check such that a
carp address always loses the tie. (Another workaround is to use IPv4
which doesn't have this problem.)

The hack below special-cases carp interfaces: If there is a tie, then a
carp interface is not allowed to win even if it has a longer bitwise match.
(The carp interface is in master state here -- carp interfaces in backup
state are never considered for source addresses in the first place.)

This hack makes communication between A and B work over IPv6 in the
above scenario.

I also considered another solution: Make carp backups treat their
local carp address as a non-local address. However, this would
be a much more invasive change and would also affect IPv4.

Index: in6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/in6.c,v
retrieving revision 1.140
diff -u -p -r1.140 in6.c
--- in6.c       26 Aug 2014 21:44:29 -0000      1.140
+++ in6.c       3 Oct 2014 11:54:52 -0000
@@ -2029,8 +2029,25 @@ in6_ifawithscope(struct ifnet *oifp, str
                        }
                        tlen = in6_matchlen(IFA_IN6(ifa), dst);
                        matchcmp = tlen - blen;
-                       if (matchcmp > 0) /* (8) */
+                       if (matchcmp > 0) { /* (8) */
+#if NCARP > 0
+                               /* 
+                                * Don't let carp interfaces win a tie against
+                                * the output interface based on matchlen.
+                                * We should only use a carp address if no
+                                * other interface has a usable address.
+                                * Otherwise, when communicating from a carp
+                                * master to a carp slave, the slave won't
+                                * respond since the carp address is also
+                                * configured as a local address on the slave.
+                                * Note that carp interfaces in backup state
+                                * were already skipped above.
+                                */
+                               if (ifp->if_type == IFT_CARP)
+                                       continue;
+#endif
                                goto replace;
+                       }
                        if (matchcmp < 0) /* (9) */
                                continue;
                        if (oifp == ifp) /* (a) */

Reply via email to