Our IPv6CP implementation has never handled IFID collisions. The old
code (from before r1.112 of if_spppsubr.c) punted and left the address
unchanged. The new code (as of r1.112) is supposed to handle collisions,
but instead goes into an endless conf-nak loop with the peer.
The problem is that I made wrong assumptions about in6_update_ifa().
It doesn't change existing addresses. It only updates parameters of
existing addresses (such as the destination address on the p2p link).
IPv6CP currently keeps suggesting the existing address which collides
with the peer's address.
Additionally, we need to update several routing table entries when
changing the link-local address (e.g. pinging the peer doesn't work
if we don't do this).
With the diff below we change our link-local address if it collides
with the address used by the peer.
In this logged session, I start out using the link-local address
used by the peer (fe80::218:82ff:fe22:a563) and switch over to
an address suggested by the peer.
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp open(initial)
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp initial->starting
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp up(starting)
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp starting->req-sent
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp input(req-sent): <conf-req id=0x1
len=14
01-0a-02-18-82-ff-fe-22-a5-63-28-29-2a-2b-2c-2d-2e-2f-30-31-32-33-34-35-36-37-00-00-00-00-00-00-00-00>
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp parse opts: ifid
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp parse opt values: ifid
fe80::218:82ff:fe22:a563 [conf-nak] send conf-nak suggest
fe80::18:82ff:fe22:722f
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp input(req-sent): <conf-nak id=0x19
len=14
01-0a-02-0a-e4-65-99-3e-f1-4e-9f-c0-00-03-00-7f-00-1a-3f-00-00-00-03-10-00-00-00-00-00-00-00-00-00-00>
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp nak opts: ifid [suggestaddr
fe80::20a:e465:993e:f14e] [agree]
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp input(req-sent): <conf-req id=0x2
len=14
01-0a-02-18-82-ff-fe-22-a5-63-dd-de-df-e0-e1-e2-e3-e4-e5-e6-e7-e8-e9-ea-eb-ec-00-00-00-00-00-00-00-00>
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp parse opts: ifid
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp parse opt values: ifid
fe80::218:82ff:fe22:a563 [conf-ack] send conf-ack
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp req-sent->ack-sent
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp input(ack-sent): <conf-ack id=0x1a
len=14
01-0a-02-0a-e4-65-99-3e-f1-4e-00-04-00-01-ff-ff-ff-ff-ff-ff-ff-ff-80-00-00-1f-00-00-00-00-00-00-00-00>
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp ack-sent->opened
Dec 17 16:01:38 jack /bsd: pppoe0: ipv6cp tlu
This shows the corresponding routing table changes:
Destination Gateway Flags Refs
Use Mtu Prio Iface
::/104 ::1 UGRS 0
0 - 8 lo0
::/96 ::1 UGRS 0
0 - 8 lo0
+default fe80::218:82ff:fe22:a563%pppoe0 UG
0 0 - 56 pppoe0
::1 ::1 UH 14
4 33192 4 lo0
::127.0.0.0/104 ::1 UGRS 0
0 - 8 lo0
::224.0.0.0/100 ::1 UGRS 0
0 - 8 lo0
::255.0.0.0/104 ::1 UGRS 0
0 - 8 lo0
::ffff:0.0.0.0/96 ::1 UGRS 0
0 - 8 lo0
2002::/24 ::1 UGRS 0
0 - 8 lo0
2002:7f00::/24 ::1 UGRS 0
0 - 8 lo0
2002:e000::/20 ::1 UGRS 0
0 - 8 lo0
2002:ff00::/24 ::1 UGRS 0
0 - 8 lo0
-fe80::/10 ::1 UGRS 0
0 - 8 lo0
+fe80::/10 ::1 UGRS 2
0 - 8 lo0
fe80::%em0/64 link#1 UC 0
0 - 4 em0
fe80::20a:e4ff:fe3e:f14e%em0 00:0a:e4:3e:f1:4e HL 0
0 - 4 lo0
fe80::%lo0/64 fe80::1%lo0 U 0
0 - 4 lo0
fe80::1%lo0 link#4 UHL 0
0 - 4 lo0
-fe80::%pppoe0/64 fe80::218:82ff:fe22:a563%pppoe0
0 0 - 4 pppoe0
-fe80::218:82ff:fe22:a563%pppoe0 link#6 HL 0
0 - 4 lo0
+fe80::%pppoe0/64 fe80::20a:e468:123e:f14e%pppoe0 U
1 0 - 4 pppoe0
+fe80::20a:e468:123e:f14e%pppoe0 link#6 UHL 0
0 - 4 lo0
+fe80::218:82ff:fe22:a563%pppoe0 link#6 UHL 0
0 - 4 pppoe0
fec0::/10 ::1 UGRS 0
0 - 8 lo0
ff01::/16 ::1 UGRS 1
0 - 8 lo0
ff01::%em0/32 link#1 UC 0
0 - 4 em0
ff01::%lo0/32 fe80::1%lo0 UC 0
0 - 4 lo0
-ff01::%pppoe0/32 fe80::218:82ff:fe22:a563%pppoe0 C
0 0 - 4 pppoe0
+ff01::%pppoe0/32 fe80::20a:e468:123e:f14e%pppoe0 UC
0 0 - 4 pppoe0
ff02::/16 ::1 UGRS 1
0 - 8 lo0
ff02::%em0/32 link#1 UC 0
0 - 4 em0
ff02::%lo0/32 fe80::1%lo0 UC 0
0 - 4 lo0
-ff02::%pppoe0/32 fe80::218:82ff:fe22:a563%pppoe0 C
0 0 - 4 pppoe0
+ff02::%pppoe0/32 fe80::20a:e468:123e:f14e%pppoe0 UC
0 0 - 4 pppoe0
To make the netinet6 code use the IFID determined by IPv6CP, I need to
have in6_ifattach_linklocal() accept a provided IFID. I decided to replace
the altifp parameter, which wasn't used anywhere, with a new ifid parameter
that has the same purpose but suits the new sppp caller better (it's an
address, not an ifp).
Other bug fixes contained in here are:
- Make IPv6CP always use the latest suggested address,
(ipv6cp.req_ifid.ifra_addr.sin6_addr), so we use it even if the task
that configures the new address on the interface didn't run yet.
- Clear the ifindex (KAME hack) in addresses suggested for IPv6CP.
An ifindex in the suggested address might confuse the peer.
- Make in6_ifdetach() remove route to interface local allnodes
(also here: http://marc.info/?l=openbsd-tech&m=138729124402857&w=2)
This makes the above routing table changes work properly.
I'm considering committing the above fixes separately.
Ok?
Index: net/if_spppsubr.c
===================================================================
RCS file: /cvs/src/sys/net/if_spppsubr.c,v
retrieving revision 1.113
diff -u -p -r1.113 if_spppsubr.c
--- net/if_spppsubr.c 20 Nov 2013 08:21:33 -0000 1.113
+++ net/if_spppsubr.c 17 Dec 2013 14:56:06 -0000
@@ -69,6 +69,10 @@
#include <netinet/if_ether.h>
#endif
+#ifdef INET6
+#include <netinet6/in6_ifattach.h>
+#endif
+
#include <net/if_sppp.h>
# define UNTIMEOUT(fun, arg, handle) \
@@ -3222,7 +3226,10 @@ sppp_ipv6cp_RCR(struct sppp *sp, struct
addlog("\n");
/* pass 2: parse option values */
- sppp_get_ip6_addrs(sp, &myaddr, NULL, NULL);
+ if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN)
+ myaddr = sp->ipv6cp.req_ifid.ifra_addr.sin6_addr;
+ else
+ sppp_get_ip6_addrs(sp, &myaddr, NULL, NULL);
if (debug)
log(LOG_DEBUG, "%s: ipv6cp parse opt values: ",
SPP_ARGS(ifp));
@@ -3455,7 +3462,10 @@ sppp_ipv6cp_scr(struct sppp *sp)
int i = 0;
if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_IFID)) {
- sppp_get_ip6_addrs(sp, &ouraddr, NULL, NULL);
+ if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN)
+ ouraddr = sp->ipv6cp.req_ifid.ifra_addr.sin6_addr;
+ else
+ sppp_get_ip6_addrs(sp, &ouraddr, NULL, NULL);
opt[i++] = IPV6CP_OPT_IFID;
opt[i++] = 10;
bcopy(&ouraddr.s6_addr[8], &opt[i], 8);
@@ -4768,6 +4778,25 @@ sppp_update_ip6_addr(void *arg1, void *a
return;
}
+ /*
+ * Changing the link-local address requires purging all
+ * existing addresses and routes for the interface first.
+ */
+ if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN) {
+ in6_ifdetach(ifp);
+ error = in6_ifattach_linklocal(ifp, &ifra->ifra_addr.sin6_addr);
+ if (error)
+ log(LOG_ERR, SPP_FMT
+ "could not update IPv6 address (error %d)\n",
+ SPP_ARGS(ifp), error);
+ splx(s);
+ return;
+ }
+
+ /*
+ * Code below changes address parameters only, not the address itself.
+ */
+
/* Destination address can only be set for /128. */
if (!in6_are_prefix_equal(&ia->ia_prefixmask.sin6_addr, &mask, 128)) {
ifra->ifra_dstaddr.sin6_len = 0;
@@ -4843,6 +4872,7 @@ sppp_suggest_ip6_addr(struct sppp *sp, s
myaddr.s6_addr[14] ^= (random & 0xff);
myaddr.s6_addr[15] ^= ((random & 0xff00) >> 8);
}
+ myaddr.s6_addr16[1] = 0; /* KAME hack: clear ifindex */
bcopy(&myaddr, suggest, sizeof(myaddr));
}
#endif /*INET6*/
Index: netinet6/in6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/in6.c,v
retrieving revision 1.126
diff -u -p -r1.126 in6.c
--- netinet6/in6.c 28 Nov 2013 10:16:44 -0000 1.126
+++ netinet6/in6.c 17 Dec 2013 14:24:05 -0000
@@ -2220,7 +2220,7 @@ in6_if_up(struct ifnet *ifp)
/*
* special cases, like 6to4, are handled in in6_ifattach
*/
- in6_ifattach(ifp, NULL);
+ in6_ifattach(ifp);
dad_delay = 0;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
Index: netinet6/in6_ifattach.c
===================================================================
RCS file: /cvs/src/sys/netinet6/in6_ifattach.c,v
retrieving revision 1.64
diff -u -p -r1.64 in6_ifattach.c
--- netinet6/in6_ifattach.c 28 Nov 2013 10:16:44 -0000 1.64
+++ netinet6/in6_ifattach.c 17 Dec 2013 14:27:28 -0000
@@ -67,7 +67,7 @@ int ip6_auto_linklocal = 1; /* enable by
int get_last_resort_ifid(struct ifnet *, struct in6_addr *);
int get_hw_ifid(struct ifnet *, struct in6_addr *);
-int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
+int get_ifid(struct ifnet *, struct in6_addr *);
int in6_ifattach_loopback(struct ifnet *);
#define EUI64_GBIT 0x01
@@ -257,11 +257,9 @@ found:
* Get interface identifier for the specified interface. If it is not
* available on ifp0, borrow interface identifier from other information
* sources.
- *
- * altifp - secondary EUI64 source
*/
int
-get_ifid(struct ifnet *ifp0, struct ifnet *altifp, struct in6_addr *in6)
+get_ifid(struct ifnet *ifp0, struct in6_addr *in6)
{
struct ifnet *ifp;
@@ -272,13 +270,6 @@ get_ifid(struct ifnet *ifp0, struct ifne
goto success;
}
- /* try secondary EUI64 source. this basically is for ATM PVC */
- if (altifp && get_hw_ifid(altifp, in6) == 0) {
- nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n",
- ifp0->if_xname, altifp->if_xname));
- goto success;
- }
-
/* next, try to get it from some other hardware interface */
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp == ifp0)
@@ -318,11 +309,11 @@ success:
}
/*
- * altifp - secondary EUI64 source
+ * ifid - used as EUI64 if not NULL, overrides other EUI64 sources
*/
int
-in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp)
+in6_ifattach_linklocal(struct ifnet *ifp, struct in6_addr *ifid)
{
struct in6_ifaddr *ia;
struct in6_aliasreq ifra;
@@ -348,8 +339,15 @@ in6_ifattach_linklocal(struct ifnet *ifp
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1);
+ } else if (ifid) {
+ ifra.ifra_addr.sin6_addr = *ifid;
+ ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+ ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
+ ifra.ifra_addr.sin6_addr.s6_addr[8] &= ~EUI64_GBIT;
+ ifra.ifra_addr.sin6_addr.s6_addr[8] |= EUI64_UBIT;
} else {
- if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) {
+ if (get_ifid(ifp, &ifra.ifra_addr.sin6_addr) != 0) {
nd6log((LOG_ERR,
"%s: no ifid available\n", ifp->if_xname));
return (-1);
@@ -565,11 +563,9 @@ in6_nigroup(struct ifnet *ifp, const cha
* XXX multiple loopback interface needs more care. for instance,
* nodelocal address needs to be configured onto only one of them.
* XXX multiple link-local address case
- *
- * altifp - secondary EUI64 source
*/
void
-in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
+in6_ifattach(struct ifnet *ifp)
{
struct in6_ifaddr *ia;
struct in6_addr in6;
@@ -634,7 +630,7 @@ in6_ifattach(struct ifnet *ifp, struct i
if (ip6_auto_linklocal) {
ia = in6ifa_ifpforlinklocal(ifp, 0);
if (ia == NULL) {
- if (in6_ifattach_linklocal(ifp, altifp) == 0) {
+ if (in6_ifattach_linklocal(ifp, NULL) == 0) {
/* linklocal address assigned */
} else {
/* failed to assign linklocal address. bark? */
@@ -687,6 +683,26 @@ in6_ifdetach(struct ifnet *ifp)
* (Or can we just delay calling nd6_purge until at this point?)
*/
nd6_purge(ifp);
+
+ /* remove route to interface local allnodes multicast (ff01::1) */
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = in6addr_intfacelocal_allnodes;
+ sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ rt = rtalloc1(sin6tosa(&sin6), 0, ifp->if_rdomain);
+ if (rt && rt->rt_ifp == ifp) {
+ struct rt_addrinfo info;
+
+ bzero(&info, sizeof(info));
+ info.rti_flags = rt->rt_flags;
+ info.rti_info[RTAX_DST] = rt_key(rt);
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ rtrequest1(RTM_DELETE, &info, rt->rt_priority, NULL,
+ ifp->if_rdomain);
+ rtfree(rt);
+ }
/* remove route to link-local allnodes multicast (ff02::1) */
bzero(&sin6, sizeof(sin6));
Index: netinet6/in6_ifattach.h
===================================================================
RCS file: /cvs/src/sys/netinet6/in6_ifattach.h,v
retrieving revision 1.5
diff -u -p -r1.5 in6_ifattach.h
--- netinet6/in6_ifattach.h 31 Aug 2006 12:37:31 -0000 1.5
+++ netinet6/in6_ifattach.h 9 Dec 2013 19:33:15 -0000
@@ -34,10 +34,10 @@
#define _NETINET6_IN6_IFATTACH_H_
#ifdef _KERNEL
-void in6_ifattach(struct ifnet *, struct ifnet *);
+void in6_ifattach(struct ifnet *);
void in6_ifdetach(struct ifnet *);
int in6_nigroup(struct ifnet *, const char *, int, struct sockaddr_in6 *);
-int in6_ifattach_linklocal(struct ifnet *, struct ifnet *);
+int in6_ifattach_linklocal(struct ifnet *, struct in6_addr *);
#endif /* _KERNEL */
#endif /* _NETINET6_IN6_IFATTACH_H_ */