2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 18:07:40 +00:00

Support IPv6 link-local address scopes on Linux.

I hadn't even heard of this feature before, but it seems to be at least
semi-standard to support Linux link-local address scopes via a % suffix,
e.g. fe80::1234%eth0 for a link-local address scoped to eth0.  This commit
adds support.

I'd appreciate feedback from folks who understand this feature better than
me.

Reported-by: Ali Volkan Atli <Volkan.Atli@argela.com.tr>
Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Darrell Ball <dlu998@gmail.com>
Tested-by: Numan Siddique <nusiddiq@redhat.com>
Acked-by: Numan Siddique <nusiddiq@redhat.com>
This commit is contained in:
Ben Pfaff 2017-07-14 14:33:46 -07:00
parent fd245f1da9
commit 5d77b36b5c
7 changed files with 92 additions and 44 deletions

2
NEWS
View File

@ -69,6 +69,8 @@ Post-v2.7.0
- Add experimental support for hardware offloading - Add experimental support for hardware offloading
* HW offloading is disabled by default. * HW offloading is disabled by default.
* HW offloading is done through the TC interface. * HW offloading is done through the TC interface.
- IPv6 link local addresses are now supported on Linux. Use % to designate
the scope device.
v2.7.0 - 21 Feb 2017 v2.7.0 - 21 Feb 2017
--------------------- ---------------------

View File

@ -107,6 +107,9 @@ AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
[], [], [[#include <sys/stat.h>]]) [], [], [[#include <sys/stat.h>]])
AC_CHECK_MEMBERS([struct ifreq.ifr_flagshigh], [], [], [[#include <net/if.h>]]) AC_CHECK_MEMBERS([struct ifreq.ifr_flagshigh], [], [], [[#include <net/if.h>]])
AC_CHECK_MEMBERS([struct mmsghdr.msg_len], [], [], [[#include <sys/socket.h>]]) AC_CHECK_MEMBERS([struct mmsghdr.msg_len], [], [], [[#include <sys/socket.h>]])
AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], [], [],
[[#include <sys/socket.h>
#include <netinet/in.h>]])
AC_CHECK_FUNCS([mlockall strnlen getloadavg statvfs getmntent_r sendmmsg]) AC_CHECK_FUNCS([mlockall strnlen getloadavg statvfs getmntent_r sendmmsg])
AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h stdatomic.h]) AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h stdatomic.h])
AC_CHECK_HEADERS([net/if_mib.h], [], [], [[#include <sys/types.h> AC_CHECK_HEADERS([net/if_mib.h], [], [], [[#include <sys/types.h>

View File

@ -17,6 +17,7 @@
#include <config.h> #include <config.h>
#include "socket-util.h" #include "socket-util.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <net/if.h> #include <net/if.h>
@ -365,7 +366,7 @@ parse_bracketed_token(char **pp)
static bool static bool
parse_sockaddr_components(struct sockaddr_storage *ss, parse_sockaddr_components(struct sockaddr_storage *ss,
const char *host_s, char *host_s,
const char *port_s, uint16_t default_port, const char *port_s, uint16_t default_port,
const char *s) const char *s)
{ {
@ -382,20 +383,38 @@ parse_sockaddr_components(struct sockaddr_storage *ss,
} }
memset(ss, 0, sizeof *ss); memset(ss, 0, sizeof *ss);
if (strchr(host_s, ':')) { if (host_s && strchr(host_s, ':')) {
struct sockaddr_in6 *sin6 struct sockaddr_in6 *sin6
= ALIGNED_CAST(struct sockaddr_in6 *, ss); = ALIGNED_CAST(struct sockaddr_in6 *, ss);
char *addr = strsep(&host_s, "%");
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port); sin6->sin6_port = htons(port);
if (!ipv6_parse(host_s, &sin6->sin6_addr)) { if (!addr || !*addr || !ipv6_parse(addr, &sin6->sin6_addr)) {
VLOG_ERR("%s: bad IPv6 address \"%s\"", s, host_s); VLOG_ERR("%s: bad IPv6 address \"%s\"", s, addr ? addr : "");
goto exit; goto exit;
} }
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
char *scope = strsep(&host_s, "%");
if (scope && *scope) {
if (!scope[strspn(scope, "0123456789")]) {
sin6->sin6_scope_id = atoi(scope);
} else {
sin6->sin6_scope_id = if_nametoindex(scope);
if (!sin6->sin6_scope_id) {
VLOG_ERR("%s: bad IPv6 scope \"%s\" (%s)",
s, scope, ovs_strerror(errno));
goto exit;
}
}
}
#endif
} else { } else {
sin->sin_family = AF_INET; sin->sin_family = AF_INET;
sin->sin_port = htons(port); sin->sin_port = htons(port);
if (!ip_parse(host_s, &sin->sin_addr.s_addr)) { if (host_s && !ip_parse(host_s, &sin->sin_addr.s_addr)) {
VLOG_ERR("%s: bad IPv4 address \"%s\"", s, host_s); VLOG_ERR("%s: bad IPv4 address \"%s\"", s, host_s);
goto exit; goto exit;
} }
@ -421,7 +440,7 @@ inet_parse_active(const char *target_, uint16_t default_port,
{ {
char *target = xstrdup(target_); char *target = xstrdup(target_);
const char *port; const char *port;
const char *host; char *host;
char *p; char *p;
bool ok; bool ok;
@ -548,7 +567,7 @@ inet_parse_passive(const char *target_, int default_port,
{ {
char *target = xstrdup(target_); char *target = xstrdup(target_);
const char *port; const char *port;
const char *host; char *host;
char *p; char *p;
bool ok; bool ok;
@ -559,8 +578,7 @@ inet_parse_passive(const char *target_, int default_port,
VLOG_ERR("%s: port must be specified", target_); VLOG_ERR("%s: port must be specified", target_);
ok = false; ok = false;
} else { } else {
ok = parse_sockaddr_components(ss, host ? host : "0.0.0.0", ok = parse_sockaddr_components(ss, host, port, default_port, target_);
port, default_port, target_);
} }
if (!ok) { if (!ok) {
memset(ss, 0, sizeof *ss); memset(ss, 0, sizeof *ss);
@ -958,6 +976,21 @@ ss_get_port(const struct sockaddr_storage *ss)
} }
} }
/* Returns true if 'name' is safe to include inside a network address field.
* We want to avoid names that include confusing punctuation, etc. */
static bool OVS_UNUSED
is_safe_name(const char *name)
{
if (!name[0] || isdigit((unsigned char) name[0])) {
return false;
}
for (const char *p = name; *p; p++) {
if (!isalnum((unsigned char) *p) && *p != '-' && *p != '_') {
return false;
}
}
return true;
}
/* Formats the IPv4 or IPv6 address in 'ss' into 's'. If 'ss' is an IPv6 /* Formats the IPv4 or IPv6 address in 'ss' into 's'. If 'ss' is an IPv6
* address, puts square brackets around the address. 'bufsize' should be at * address, puts square brackets around the address. 'bufsize' should be at
@ -980,6 +1013,20 @@ ss_format_address(const struct sockaddr_storage *ss, struct ds *s)
inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, tail, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, tail, INET6_ADDRSTRLEN);
s->length += strlen(tail); s->length += strlen(tail);
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
uint32_t scope = sin6->sin6_scope_id;
if (scope) {
char namebuf[IF_NAMESIZE];
char *name = if_indextoname(scope, namebuf);
ds_put_char(s, '%');
if (name && is_safe_name(name)) {
ds_put_cstr(s, name);
} else {
ds_put_format(s, "%"PRIu32, scope);
}
}
#endif
ds_put_char(s, ']'); ds_put_char(s, ']');
} else { } else {
OVS_NOT_REACHED(); OVS_NOT_REACHED();

View File

@ -3,8 +3,11 @@
The specified \fIport\fR on the host at the given \fIip\fR, which must The specified \fIport\fR on the host at the given \fIip\fR, which must
be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address
format. Wrap IPv6 addresses in square brackets, format. Wrap IPv6 addresses in square brackets,
e.g. \fBtcp:[::1]:6653\fR. For \fBssl\fR, the \fB\-\-private\-key\fR, e.g. \fBtcp:[::1]:6653\fR. On Linux, use \fB%\fIdevice\fR to
\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory. designate a scope for IPv6 link-level addresses,
e.g. \fBtcp:[fe80::1234%eth0]:6653\fR. For \fBssl\fR, the
\fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
options are mandatory.
.IP .IP
If \fIport\fR is not specified, it defaults to 6653. If \fIport\fR is not specified, it defaults to 6653.
.TP .TP

View File

@ -1,10 +1,12 @@
.IP "\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]" .IP "\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]"
.IQ "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]" .IQ "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
Listens for OpenFlow connections on \fIport\fR. The default Listens for OpenFlow connections on \fIport\fR. The default
\fIport\fR is 6653. By default, connections \fIport\fR is 6653. By default, connections are allowed from any IPv4
are allowed from any IPv4 address. Specify \fIip\fR as an IPv4 address. Specify \fIip\fR as an IPv4 address or a bracketed IPv6
address or a bracketed IPv6 address (e.g. \fBptcp:6653:[::1]\fR). DNS address (e.g. \fBptcp:6653:[::1]\fR). On Linux, use \fB%\fIdevice\fR
names may not be used. For \fBpssl\fR, the to designate a scope for IPv6 link-level addresses,
e.g. \fBptcp:6653:[fe80::1234%eth0]\fR. DNS names may
not be used. For \fBpssl\fR, the
\fB\-\-private\-key\fR,\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR \fB\-\-private\-key\fR,\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
options are mandatory. options are mandatory.
.IP .IP

View File

@ -1,15 +1,13 @@
.IP "\fBssl:\fIip\fB:\fIport\fR" .IP "\fBssl:\fIip\fB:\fIport\fR"
The specified SSL \fIport\fR on the host at the given \fIip\fR, which .IQ "\fBtcp:\fIip\fB:\fIport\fR"
must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address The given SSL or plain TCP \fIport\fR on the host at the given
format. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square \fIip\fR, which must be expressed as an IP address (not a DNS name) in
brackets, e.g.: \fBssl:[::1]:6640\fR. IPv4 or IPv6 address format. If \fIip\fR is an IPv6 address, then
The \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR wrap \fIip\fR with square brackets, e.g.: \fBssl:[::1]:6640\fR. On
options are mandatory when this form is used. Linux, use \fB%\fIdevice\fR to designate a scope for IPv6 link-level
. addresses, e.g. \fBssl:[fe80::1234%eth0]:6653\fR. For \fBssl\fR, the
.IP "\fBtcp:\fIip\fB:\fIport\fR" \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
Connect to the given TCP \fIport\fR on \fIip\fR, where \fIip\fR can be IPv4 options are mandatory.
or IPv6 address. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with
square brackets, e.g.: \fBtcp:[::1]:6640\fR.
. .
.IP "\fBunix:\fIfile\fR" .IP "\fBunix:\fIfile\fR"
On POSIX, connect to the Unix domain server socket named \fIfile\fR. On POSIX, connect to the Unix domain server socket named \fIfile\fR.

View File

@ -1,22 +1,15 @@
.IP "\fBpssl:\fIport\fR[\fB:\fIip\fR]" .IP "\fBpssl:\fIport\fR[\fB:\fIip\fR]"
Listen on the given SSL \fIport\fR for a connection. By default, .IQ "\fBptcp:\fIport\fR[\fB:\fIip\fR]"
connections are not bound to a particular local IP address and Listen on the given SSL or TCP \fIport\fR for a connection. By
it listens only on IPv4 (but not IPv6) addresses, but default, connections are not bound to a particular local IP address
specifying \fIip\fR limits connections to those from the given and it listens only on IPv4 (but not IPv6) addresses, but specifying
\fIip\fR, either IPv4 or IPv6 address. If \fIip\fR is \fIip\fR limits connections to those from the given \fIip\fR, either
an IPv6 address, then wrap \fIip\fR with square brackets, e.g.: IPv4 or IPv6 address. If \fIip\fR is an IPv6 address, then wrap
\fBpssl:6640:[::1]\fR. The \fB\-\-private\-key\fR, \fIip\fR with square brackets, e.g.: \fBpssl:6640:[::1]\fR. On Linux,
\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory use \fB%\fIdevice\fR to designate a scope for IPv6 link-level
when this form is used. addresses, e.g. \fBpssl:6653:[fe80::1234%eth0]\fR. For \fBpssl\fR,
. the \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and
.IP "\fBptcp:\fIport\fR[\fB:\fIip\fR]" \fB\-\-ca\-cert\fR options are mandatory.
Listen on the given TCP \fIport\fR for a connection. By default,
connections are not bound to a particular local IP address and
it listens only on IPv4 (but not IPv6) addresses, but
\fIip\fR may be specified to listen only for connections to the given
\fIip\fR, either IPv4 or IPv6 address. If \fIip\fR is
an IPv6 address, then wrap \fIip\fR with square brackets, e.g.:
\fBptcp:6640:[::1]\fR.
. .
.IP "\fBpunix:\fIfile\fR" .IP "\fBpunix:\fIfile\fR"
On POSIX, listen on the Unix domain server socket named \fIfile\fR for a On POSIX, listen on the Unix domain server socket named \fIfile\fR for a