2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +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
* HW offloading is disabled by default.
* 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
---------------------

View File

@ -107,6 +107,9 @@ AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
[], [], [[#include <sys/stat.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 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_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>

View File

@ -17,6 +17,7 @@
#include <config.h>
#include "socket-util.h"
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
@ -365,7 +366,7 @@ parse_bracketed_token(char **pp)
static bool
parse_sockaddr_components(struct sockaddr_storage *ss,
const char *host_s,
char *host_s,
const char *port_s, uint16_t default_port,
const char *s)
{
@ -382,20 +383,38 @@ parse_sockaddr_components(struct sockaddr_storage *ss,
}
memset(ss, 0, sizeof *ss);
if (strchr(host_s, ':')) {
if (host_s && strchr(host_s, ':')) {
struct sockaddr_in6 *sin6
= ALIGNED_CAST(struct sockaddr_in6 *, ss);
char *addr = strsep(&host_s, "%");
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
if (!ipv6_parse(host_s, &sin6->sin6_addr)) {
VLOG_ERR("%s: bad IPv6 address \"%s\"", s, host_s);
if (!addr || !*addr || !ipv6_parse(addr, &sin6->sin6_addr)) {
VLOG_ERR("%s: bad IPv6 address \"%s\"", s, addr ? addr : "");
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 {
sin->sin_family = AF_INET;
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);
goto exit;
}
@ -421,7 +440,7 @@ inet_parse_active(const char *target_, uint16_t default_port,
{
char *target = xstrdup(target_);
const char *port;
const char *host;
char *host;
char *p;
bool ok;
@ -548,7 +567,7 @@ inet_parse_passive(const char *target_, int default_port,
{
char *target = xstrdup(target_);
const char *port;
const char *host;
char *host;
char *p;
bool ok;
@ -559,8 +578,7 @@ inet_parse_passive(const char *target_, int default_port,
VLOG_ERR("%s: port must be specified", target_);
ok = false;
} else {
ok = parse_sockaddr_components(ss, host ? host : "0.0.0.0",
port, default_port, target_);
ok = parse_sockaddr_components(ss, host, port, default_port, target_);
}
if (!ok) {
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
* 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);
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, ']');
} else {
OVS_NOT_REACHED();

View File

@ -3,8 +3,11 @@
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
format. Wrap IPv6 addresses in square brackets,
e.g. \fBtcp:[::1]:6653\fR. For \fBssl\fR, the \fB\-\-private\-key\fR,
\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory.
e.g. \fBtcp:[::1]:6653\fR. On Linux, use \fB%\fIdevice\fR to
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
If \fIport\fR is not specified, it defaults to 6653.
.TP

View File

@ -1,10 +1,12 @@
.IP "\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]"
.IQ "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
Listens for OpenFlow connections on \fIport\fR. The default
\fIport\fR is 6653. By default, connections
are allowed from any IPv4 address. Specify \fIip\fR as an IPv4
address or a bracketed IPv6 address (e.g. \fBptcp:6653:[::1]\fR). DNS
names may not be used. For \fBpssl\fR, the
\fIport\fR is 6653. By default, connections are allowed from any IPv4
address. Specify \fIip\fR as an IPv4 address or a bracketed IPv6
address (e.g. \fBptcp:6653:[::1]\fR). On Linux, use \fB%\fIdevice\fR
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
options are mandatory.
.IP

View File

@ -1,15 +1,13 @@
.IP "\fBssl:\fIip\fB:\fIport\fR"
The specified SSL \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
format. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square
brackets, e.g.: \fBssl:[::1]:6640\fR.
The \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
options are mandatory when this form is used.
.
.IP "\fBtcp:\fIip\fB:\fIport\fR"
Connect to the given TCP \fIport\fR on \fIip\fR, where \fIip\fR can be IPv4
or IPv6 address. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with
square brackets, e.g.: \fBtcp:[::1]:6640\fR.
.IQ "\fBtcp:\fIip\fB:\fIport\fR"
The given SSL or plain TCP \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 format. If \fIip\fR is an IPv6 address, then
wrap \fIip\fR with square brackets, e.g.: \fBssl:[::1]:6640\fR. On
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
\fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR
options are mandatory.
.
.IP "\fBunix:\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]"
Listen on the given SSL \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
specifying \fIip\fR limits connections to those from 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.:
\fBpssl:6640:[::1]\fR. The \fB\-\-private\-key\fR,
\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory
when this form is used.
.
.IP "\fBptcp:\fIport\fR[\fB:\fIip\fR]"
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.
.IQ "\fBptcp:\fIport\fR[\fB:\fIip\fR]"
Listen on the given SSL or 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 specifying
\fIip\fR limits connections to those from 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.: \fBpssl:6640:[::1]\fR. On Linux,
use \fB%\fIdevice\fR to designate a scope for IPv6 link-level
addresses, e.g. \fBpssl:6653:[fe80::1234%eth0]\fR. For \fBpssl\fR,
the \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and
\fB\-\-ca\-cert\fR options are mandatory.
.
.IP "\fBpunix:\fIfile\fR"
On POSIX, listen on the Unix domain server socket named \fIfile\fR for a