summaryrefslogtreecommitdiff
path: root/pppd
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@ozlabs.org>2022-01-12 20:41:33 +1100
committerGitHub <noreply@github.com>2022-01-12 20:41:33 +1100
commitdb3aa5f130886194d00a0c24db1e75261f9f9798 (patch)
tree24eb336696281e51d033f3ba1a09869d16b05ba6 /pppd
parentc5ff7d10cfd97fae1783fce445c575373b65856c (diff)
parent189bad1c0baeb4fd65ff566d2723b5bc95205a65 (diff)
downloadppp-db3aa5f130886194d00a0c24db1e75261f9f9798.tar.gz
Merge pull request #328 from pali/ipv6
ipv6cp: Add support for new ipv6cp-noremote, ipv6cp-nosend and ipv6cp-use-remotenumber options
Diffstat (limited to 'pppd')
-rw-r--r--pppd/ipv6cp.c104
-rw-r--r--pppd/ipv6cp.h1
-rw-r--r--pppd/pppd.826
-rw-r--r--pppd/sys-linux.c16
4 files changed, 121 insertions, 26 deletions
diff --git a/pppd/ipv6cp.c b/pppd/ipv6cp.c
index cef1e38..130819d 100644
--- a/pppd/ipv6cp.c
+++ b/pppd/ipv6cp.c
@@ -153,6 +153,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/param.h>
@@ -179,6 +180,7 @@ int no_ifaceid_neg = 0;
/* 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;
@@ -258,10 +260,18 @@ static option_t ipv6cp_option_list[] = {
&ipv6cp_wantoptions[0].default_route },
{ "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
- "Use (default) IPv4 address as interface identifier", 1 },
-
+ "Use (default) IPv4 addresses for both local and remote interface identifiers", 1 },
{ "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
- "Use uniquely-available persistent value for link local address", 1 },
+ "Use uniquely-available persistent value for local interface identifier", 1 },
+ { "ipv6cp-use-remotenumber", o_bool, &ipv6cp_wantoptions[0].use_remotenumber,
+ "Use remotenumber value for remote interface identifier", 1 },
+
+#ifdef __linux__
+ { "ipv6cp-noremote", o_bool, &ipv6cp_noremote,
+ "Allow peer to have no interface identifier", 1 },
+#endif
+ { "ipv6cp-nosend", o_bool, &ipv6cp_wantoptions[0].neg_ifaceid,
+ "Don't send local interface identifier to peer", OPT_A2CLR },
{ "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
"Set timeout for IPv6CP", OPT_PRIO },
@@ -678,6 +688,7 @@ bad:
static int
ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject)
{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
u_char citype, cilen, *next;
u_short cishort;
@@ -726,7 +737,7 @@ ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject)
try.neg_ifaceid = 0;
} else if (go->accept_local && !eui64_iszero(ifaceid) && !eui64_equals(ifaceid, go->hisid)) {
try.ourid = ifaceid;
- } else if (eui64_iszero(ifaceid) && !go->opt_local) {
+ } else if (eui64_iszero(ifaceid) && !go->opt_local && wo->neg_ifaceid) {
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->hisid)) /* bad luck */
eui64_magic(ifaceid);
@@ -780,7 +791,7 @@ ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject)
eui64_get(ifaceid, p);
if (go->accept_local && !eui64_iszero(ifaceid) && !eui64_equals(ifaceid, go->hisid)) {
try.ourid = ifaceid;
- } else if (eui64_iszero(ifaceid) && !go->opt_local) {
+ } else if (eui64_iszero(ifaceid) && !go->opt_local && wo->neg_ifaceid) {
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->hisid)) /* bad luck */
eui64_magic(ifaceid);
@@ -1074,6 +1085,23 @@ endswitch:
/*
+ * eui48_to_eui64 - Convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+ */
+static void
+eui48_to_eui64(eui64_t *p_eui64, const u_char addr[6])
+{
+ p_eui64->e8[0] = addr[0] | 0x02;
+ p_eui64->e8[1] = addr[1];
+ p_eui64->e8[2] = addr[2];
+ p_eui64->e8[3] = 0xFF;
+ p_eui64->e8[4] = 0xFE;
+ p_eui64->e8[5] = addr[3];
+ p_eui64->e8[6] = addr[4];
+ p_eui64->e8[7] = addr[5];
+}
+
+
+/*
* ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
*
* walks the list of valid ethernet interfaces, starting with devnam
@@ -1092,18 +1120,7 @@ ether_to_eui64(eui64_t *p_eui64)
return 0;
}
- /*
- * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
- */
- p_eui64->e8[0] = addr[0] | 0x02;
- p_eui64->e8[1] = addr[1];
- p_eui64->e8[2] = addr[2];
- p_eui64->e8[3] = 0xFF;
- p_eui64->e8[4] = 0xFE;
- p_eui64->e8[5] = addr[3];
- p_eui64->e8[6] = addr[4];
- p_eui64->e8[7] = addr[5];
-
+ eui48_to_eui64(p_eui64, addr);
return 1;
}
@@ -1140,6 +1157,46 @@ ipv6_check_options(void)
}
}
+ if (!wo->opt_remote && wo->use_remotenumber && *remote_number) {
+ /* remote number can be either MAC address, IPv4 address, IPv6 address or telephone number */
+ struct in_addr addr;
+ struct in6_addr addr6;
+ unsigned long long tel;
+ unsigned char mac[6];
+ const char *str;
+ char *endptr;
+ if (sscanf(remote_number, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2],
+ &mac[3], &mac[4], &mac[5]) == 6) {
+ eui48_to_eui64(&wo->hisid, mac);
+ } else if (inet_pton(AF_INET, remote_number, &addr) == 1) {
+ eui64_setlo32(wo->hisid, ntohl(addr.s_addr));
+ } else if (inet_pton(AF_INET6, remote_number, &addr6) == 1) {
+ /* use low 64 bits of IPv6 address for interface identifier */
+ wo->hisid.e8[0] = addr6.s6_addr[8];
+ wo->hisid.e8[1] = addr6.s6_addr[9];
+ wo->hisid.e8[2] = addr6.s6_addr[10];
+ wo->hisid.e8[3] = addr6.s6_addr[11];
+ wo->hisid.e8[4] = addr6.s6_addr[12];
+ wo->hisid.e8[5] = addr6.s6_addr[13];
+ wo->hisid.e8[6] = addr6.s6_addr[14];
+ wo->hisid.e8[7] = addr6.s6_addr[15];
+ } else {
+ str = remote_number;
+ /* telephone number may start with leading '+' sign, so skip it */
+ if (str[0] == '+')
+ str++;
+ errno = 0;
+ tel = strtoull(str, &endptr, 10);
+ if (!errno && *str && !*endptr && tel) {
+ wo->hisid.e32[0] = htonl(tel >> 32);
+ wo->hisid.e32[1] = htonl(tel & 0xFFFFFFFF);
+ }
+ }
+ if (!eui64_iszero(wo->hisid))
+ wo->opt_remote = 1;
+ }
+
if (!wo->opt_local) { /* init interface identifier */
if (wo->use_ip && eui64_iszero(wo->ourid)) {
eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
@@ -1170,7 +1227,7 @@ ipv6_demand_conf(int u)
{
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);
}
@@ -1194,7 +1251,8 @@ ipv6_demand_conf(int u)
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;
}
@@ -1227,7 +1285,7 @@ ipv6cp_up(fsm *f)
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;
@@ -1244,7 +1302,8 @@ ipv6cp_up(fsm *f)
}
}
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 */
@@ -1306,7 +1365,8 @@ ipv6cp_up(fsm *f)
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);
diff --git a/pppd/ipv6cp.h b/pppd/ipv6cp.h
index 5e1db6b..3ce9df4 100644
--- a/pppd/ipv6cp.h
+++ b/pppd/ipv6cp.h
@@ -156,6 +156,7 @@ typedef struct ipv6cp_options {
int opt_remote; /* histoken set by option */
int use_ip; /* use IP as interface identifier */
int use_persistent; /* use uniquely persistent value for address */
+ int use_remotenumber; /* use remote number value for address */
int neg_vj; /* Van Jacobson Compression? */
u_short vj_protocol; /* protocol value to use in VJ option */
eui64_t ourid, hisid; /* Interface identifiers */
diff --git a/pppd/pppd.8 b/pppd/pppd.8
index b31594a..1f2b859 100644
--- a/pppd/pppd.8
+++ b/pppd/pppd.8
@@ -212,11 +212,14 @@ Set the local and/or remote 64-bit interface identifier. Either one may be
omitted. The identifier must be specified in standard ASCII notation of
IPv6 addresses (e.g. ::dead:beef). If the
\fIipv6cp\-use\-ipaddr\fR
-option is given, the local identifier is the local IPv4 address (see above).
+option is given, the local identifier is the local IPv4 address and the
+remote identifier is the remote IPv4 address (see above).
+If the \fIipv6cp-use-remotenumber\fR option is given, the remote identifier
+is set to the value from \fIremotenumber\fR option.
On systems which supports a unique persistent id, such as EUI\-48 derived
from the Ethernet MAC address, \fIipv6cp\-use\-persistent\fR option can be
-used to replace the \fIipv6 <local>,<remote>\fR option. Otherwise the
-identifier is randomized.
+used to set local identifier. Otherwise both local and remote identifiers
+are randomized.
.TP
.B active\-filter \fIfilter\-expression
Specifies a packet filter to be applied to data packets to determine
@@ -536,6 +539,23 @@ With this option, pppd will accept the peer's idea of its (remote)
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\-nosendip
+Don't send our local IPv6 interface identifier to peer during IPv6 interface
+identifier negotiation.
+.TP
.B ipv6cp\-max\-configure \fIn
Set the maximum number of IPv6CP configure-request transmissions to
\fIn\fR (default 10).
diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c
index 1e00366..49abe2c 100644
--- a/pppd/sys-linux.c
+++ b/pppd/sys-linux.c
@@ -2941,7 +2941,19 @@ static int sif6addr_rtnetlink(unsigned int iface, eui64_t our_eui64, eui64_t his
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;
@@ -3059,7 +3071,9 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
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.