/* local vars */
static int default_route_set[NUM_PPP]; /* Have set up a default route */
static int ipv6cp_is_up;
+static bool ipv6cp_noremote;
/* Hook for a plugin to know when IPv6 protocol has come up */
void (*ipv6_up_hook)(void) = NULL;
{ "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
"Use uniquely-available persistent value for link local address", 1 },
+#ifdef __linux__
+ { "ipv6cp-noremote", o_bool, &ipv6cp_noremote,
+ "Allow peer to have no interface identifier", 1 },
+#endif
+
{ "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
"Set timeout for IPv6CP", OPT_PRIO },
{ "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
{
ipv6cp_options *wo = &ipv6cp_wantoptions[u];
- if (eui64_iszero(wo->hisid)) {
+ if (eui64_iszero(wo->hisid) && !ipv6cp_noremote) {
/* make up an arbitrary identifier for the peer */
eui64_magic_nz(wo->hisid);
}
default_route_set[u] = 1;
notice("local LL address %s", llv6_ntoa(wo->ourid));
- notice("remote LL address %s", llv6_ntoa(wo->hisid));
+ if (!eui64_iszero(wo->hisid))
+ notice("remote LL address %s", llv6_ntoa(wo->hisid));
return 1;
}
ho->hisid = wo->hisid;
if(!no_ifaceid_neg) {
- if (eui64_iszero(ho->hisid)) {
+ if (eui64_iszero(ho->hisid) && !ipv6cp_noremote) {
error("Could not determine remote LL address");
ipv6cp_close(f->unit, "Could not determine remote LL address");
return;
}
}
script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
- script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
+ if (!eui64_iszero(ho->hisid))
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
#ifdef IPV6CP_COMP
/* set tcp compression */
default_route_set[f->unit] = 1;
notice("local LL address %s", llv6_ntoa(go->ourid));
- notice("remote LL address %s", llv6_ntoa(ho->hisid));
+ if (!eui64_iszero(ho->hisid))
+ notice("remote LL address %s", llv6_ntoa(ho->hisid));
}
np_up(f->unit, PPP_IPV6);
IPv6 interface identifier, even if the remote IPv6 interface
identifier was specified in an option.
.TP
+.B ipv6cp\-noremote
+Allow pppd to operate without having an IPv6 link local address for the peer.
+This option is only available under Linux. Normally, pppd will request the
+peer's IPv6 interface identifier (used for composing IPv6 link local address),
+and if the peer does not supply it, pppd will generate one for the peer.
+With this option, if the peer does not supply its IPv6 interface identifier,
+pppd will not ask the peer for it, and will not set the destination IPv6
+link local address of the ppp interface. In this situation, the ppp interface
+can be used for routing by creating device routes, but the peer itself cannot
+be addressed directly for IPv6 traffic until the peer starts announcing ICMPv6
+Router Advertisement or ICMPv6 Neighbor Advertisement packets. Note that IPv6
+router must announce ICMPv6 Router Advertisement packets.
+.TP
.B ipv6cp\-max\-configure \fIn
Set the maximum number of IPv6CP configure-request transmissions to
\fIn\fR (default 10).
IN6_LLADDR_FROM_EUI64(nlreq.addrs[0].addr, our_eui64);
nlreq.addrs[1].rta.rta_len = sizeof(nlreq.addrs[1]);
nlreq.addrs[1].rta.rta_type = IFA_ADDRESS;
- IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, his_eui64);
+
+ /*
+ * To set only local address, older kernel expects that local address is
+ * in IFA_ADDRESS field (not IFA_LOCAL). New kernels with support for peer
+ * address, ignore IFA_ADDRESS if is same as IFA_LOCAL. So for backward
+ * compatibility when setting only local address, set it via both IFA_LOCAL
+ * and IFA_ADDRESS fields. Same logic is implemented in 'ip address' command
+ * from iproute2 project.
+ */
+ if (!eui64_iszero(his_eui64))
+ IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, his_eui64);
+ else
+ IN6_LLADDR_FROM_EUI64(nlreq.addrs[1].addr, our_eui64);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
error("sif6addr: ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
return 0;
}
+ }
+ if (!ret && !eui64_iszero(his_eui64)) {
/*
* Linux kernel does not provide AF_INET6 ioctl SIOCSIFDSTADDR for
* setting remote peer host address, so set only route to remote host.