Skip to content

Commit

Permalink
Add the --macaddress option to specify the MAC address of the tap int…
Browse files Browse the repository at this point in the history
…erface.

Signed-off-by: Brian Koehmstedt <bkoehm@gmail.com>
  • Loading branch information
Brian Koehmstedt committed Feb 24, 2021
1 parent 6dc0186 commit a37ca4a
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile.am
Expand Up @@ -5,7 +5,7 @@ AM_CFLAGS = @GLIB_CFLAGS@ @SLIRP_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@
noinst_LIBRARIES = libparson.a

AM_TESTS_ENVIRONMENT = PATH="$(abs_top_builddir):$(PATH)"
TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh tests/test-slirp4netns-outbound-addr.sh tests/test-slirp4netns-disable-dns.sh tests/test-slirp4netns-seccomp.sh
TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh tests/test-slirp4netns-outbound-addr.sh tests/test-slirp4netns-disable-dns.sh tests/test-slirp4netns-seccomp.sh tests/test-slirp4netns-macaddress.sh

EXTRA_DIST = \
slirp4netns.1.md \
Expand Down
82 changes: 82 additions & 0 deletions main.c
Expand Up @@ -160,6 +160,14 @@ static int configure_network(const char *tapname,
return -1;
}

if (cfg->vmacaddress_len > 0) {
ifr.ifr_ifru.ifru_hwaddr = cfg->vmacaddress;
if (ioctl(sockfd, SIOCSIFHWADDR, &ifr) < 0) {
perror("cannot set MAC address");
return -1;
}
}

sai->sin_family = AF_INET;
sai->sin_port = 0;
sai->sin_addr = cfg->recommended_vguest;
Expand Down Expand Up @@ -286,6 +294,11 @@ static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket,
}
}
#endif
if (cfg->vmacaddress_len > 0) {
unsigned char *mac = (unsigned char *)cfg->vmacaddress.sa_data;
printf("* MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0],
mac[1], mac[2], mac[3], mac[4], mac[5]);
}
if (!cfg->disable_host_loopback) {
printf(
"WARNING: 127.0.0.1:* on the host is accessible as %s (set "
Expand Down Expand Up @@ -358,6 +371,8 @@ static void usage(const char *argv0)
printf("--disable-dns disables 10.0.2.3 (or configured internal "
"ip) to host dns redirect (experimental)\n");
#endif
printf("--macaddress=MAC specify the MAC address of the TAP (only "
"valid with -c)\n");
/* others */
printf("-h, --help show this help and exit\n");
printf("-v, --version show version and exit\n");
Expand Down Expand Up @@ -399,6 +414,7 @@ struct options {
bool enable_sandbox; // --enable-sandbox
bool enable_seccomp; // --enable-seccomp
bool disable_dns; // --disable-dns
char *macaddress; // --macaddress
};

static void options_init(struct options *options)
Expand Down Expand Up @@ -442,6 +458,10 @@ static void options_destroy(struct options *options)
free(options->outbound_addr6);
options->outbound_addr6 = NULL;
}
if (options->macaddress != NULL) {
free(options->macaddress);
options->macaddress = NULL;
}
}

// * caller does not need to call options_init()
Expand All @@ -457,6 +477,7 @@ static void parse_args(int argc, char *const argv[], struct options *options)
char *optarg_api_socket = NULL;
char *optarg_outbound_addr = NULL;
char *optarg_outbound_addr6 = NULL;
char *optarg_macaddress = NULL;
#define CIDR -42
#define DISABLE_HOST_LOOPBACK -43
#define NETNS_TYPE -44
Expand All @@ -466,6 +487,7 @@ static void parse_args(int argc, char *const argv[], struct options *options)
#define OUTBOUND_ADDR -48
#define OUTBOUND_ADDR6 -49
#define DISABLE_DNS -50
#define MACADDRESS -51
#define _DEPRECATED_NO_HOST_LOOPBACK \
-10043 // deprecated in favor of disable-host-loopback
#define _DEPRECATED_CREATE_SANDBOX \
Expand All @@ -490,6 +512,7 @@ static void parse_args(int argc, char *const argv[], struct options *options)
{ "outbound-addr", required_argument, NULL, OUTBOUND_ADDR },
{ "outbound-addr6", required_argument, NULL, OUTBOUND_ADDR6 },
{ "disable-dns", no_argument, NULL, DISABLE_DNS },
{ "macaddress", required_argument, NULL, MACADDRESS },
{ 0, 0, 0, 0 },
};
options_init(options);
Expand Down Expand Up @@ -589,6 +612,9 @@ static void parse_args(int argc, char *const argv[], struct options *options)
case DISABLE_DNS:
options->disable_dns = true;
break;
case MACADDRESS:
optarg_macaddress = optarg;
break;
default:
goto error;
break;
Expand All @@ -612,6 +638,15 @@ static void parse_args(int argc, char *const argv[], struct options *options)
if (optarg_outbound_addr6 != NULL) {
options->outbound_addr6 = strdup(optarg_outbound_addr6);
}
if (optarg_macaddress != NULL) {
if (!options->do_config_network) {
fprintf(stderr, "--macaddr cannot be specified when --configure or "
"-c is not specified\n");
goto error;
} else {
options->macaddress = strdup(optarg_macaddress);
}
}
#undef CIDR
#undef DISABLE_HOST_LOOPBACK
#undef NETNS_TYPE
Expand All @@ -622,6 +657,7 @@ static void parse_args(int argc, char *const argv[], struct options *options)
#undef OUTBOUND_ADDR
#undef OUTBOUND_ADDR6
#undef DISABLE_DNS
#undef MACADDRESS
if (argc - optind < 2) {
goto error;
}
Expand Down Expand Up @@ -772,6 +808,37 @@ static int get_interface_addr(const char *interface, int af, void *addr)
return -1;
}

/*
* Convert a MAC string (macaddr) to bytes (data).
* macaddr must be a null-terminated string in the format of
* xx:xx:xx:xx:xx:xx. The data buffer needs to be at least 6 bytes.
* Typically the data is put into sockaddr sa_data, which is 14 bytes.
*/
static int slirp4netns_macaddr_hexstring_to_data(char *macaddr, char *data)
{
int temp;
char *macaddr_ptr;
char *data_ptr;
for (macaddr_ptr = macaddr, data_ptr = data;
macaddr_ptr != NULL && data_ptr < data + 6;
macaddr_ptr = strchr(macaddr_ptr, ':'), data_ptr++) {
if (macaddr_ptr != macaddr) {
macaddr_ptr++; // advance over the :
}
if (sscanf(macaddr_ptr, "%x", &temp) != 1 || temp < 0 || temp > 255) {
fprintf(stderr, "\"%s\" is an invalid MAC address.\n", macaddr);
return -1;
}
*data_ptr = temp;
}
if (macaddr_ptr != NULL) {
fprintf(stderr, "\"%s\" is an invalid MAC address. Is it too long?\n",
macaddr);
return -1;
}
return (int)(data_ptr - data);
}

static int slirp4netns_config_from_options(struct slirp4netns_config *cfg,
struct options *opt)
{
Expand Down Expand Up @@ -851,6 +918,21 @@ static int slirp4netns_config_from_options(struct slirp4netns_config *cfg,
goto finish;
}
#endif

cfg->vmacaddress_len = 0;
memset(&cfg->vmacaddress, 0, sizeof(cfg->vmacaddress));
if (opt->macaddress != NULL) {
cfg->vmacaddress.sa_family = AF_LOCAL;
int macaddr_len;
if ((macaddr_len = slirp4netns_macaddr_hexstring_to_data(
opt->macaddress, cfg->vmacaddress.sa_data)) < 0) {
fprintf(stderr, "macaddress has to be a valid MAC address (hex "
"string, 6 bytes, each byte separated by a ':').");
rc = -1;
goto finish;
}
cfg->vmacaddress_len = macaddr_len;
}
finish:
return rc;
}
Expand Down
4 changes: 4 additions & 0 deletions slirp4netns.1
Expand Up @@ -123,6 +123,10 @@ specify outbound interface slirp should bind to (ipv6 traffic only)
\fB\fC\-\-disable\-dns\fR (since v1.1.0)
disable built\-in DNS (10.0.2.3 by default)

.PP
\fB\fC\-\-macaddress\fR (since v1.1.9)
specify MAC address of the TAP interface (only valid with \-c)

.PP
\fB\fC\-h\fR, \fB\fC\-\-help\fR (since v0.2.0)
show help and exit
Expand Down
3 changes: 3 additions & 0 deletions slirp4netns.1.md
Expand Up @@ -90,6 +90,9 @@ specify outbound interface slirp should bind to (ipv6 traffic only)
`--disable-dns` (since v1.1.0)
disable built-in DNS (10.0.2.3 by default)

`--macaddress` (since v1.1.9)
specify MAC address of the TAP interface (only valid with -c)

`-h`, `--help` (since v0.2.0)
show help and exit

Expand Down
2 changes: 2 additions & 0 deletions slirp4netns.h
Expand Up @@ -25,6 +25,8 @@ struct slirp4netns_config {
#if SLIRP_CONFIG_VERSION_MAX >= 3
bool disable_dns;
#endif
struct sockaddr vmacaddress; // MAC address of interface
int vmacaddress_len; // MAC address byte length
};
int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket,
struct slirp4netns_config *cfg);
Expand Down
29 changes: 29 additions & 0 deletions tests/test-slirp4netns-macaddress.sh
@@ -0,0 +1,29 @@
#!/bin/bash
set -xeuo pipefail

. $(dirname $0)/common.sh
MACADDRESS="0e:d4:18:d9:38:fb"

unshare -r -n sleep infinity &
child=$!

wait_for_network_namespace $child

function cleanup {
kill -9 $child $slirp_pid
}
trap cleanup EXIT

slirp4netns -c --macaddress $MACADDRESS $child tun11 &
slirp_pid=$!

wait_for_network_device $child tun11

result=$(nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -o "ether $MACADDRESS")

if [ -z "$result" ]; then
printf "expecting %s MAC address on the interface but didn't get it" "$MACADDRESS"
exit 1
fi

cleanup

0 comments on commit a37ca4a

Please sign in to comment.