Anyone? I have received no feedback on this. I'd really like to get review because fixing IPv6CP has turned out to be tricker than I expected.
On Tue, Dec 17, 2013 at 04:32:03PM +0100, Stefan Sperling wrote: > 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_ */