Skip to content

Commit 8cc216f

Browse files
author
jasmin
committed
IPv6: pseudo random id generator rootless-containers#259 rootless-containers#253
1 parent 66c7cac commit 8cc216f

File tree

5 files changed

+266
-9
lines changed

5 files changed

+266
-9
lines changed

Makefile.am

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ libparson_a_CFLAGS = $(AM_CFLAGS) -I$(abs_top_builddir)/vendor/parson
3535
libparson_a_SOURCES = vendor/parson/parson.c
3636

3737
slirp4netns_SOURCES = main.c slirp4netns.c api.c sandbox.c seccompfilter.c
38-
slirp4netns_LDADD = libparson.a @GLIB_LIBS@ @SLIRP_LIBS@ @LIBSECCOMP_LIBS@ -lpthread
38+
slirp4netns_LDADD = libparson.a @GLIB_LIBS@ @SLIRP_LIBS@ @LIBSECCOMP_LIBS@ -lpthread -lcrypto
3939
man1_MANS = slirp4netns.1
4040

4141
generate-man:

configure.ac

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ PKG_CHECK_MODULES(GLIB, glib-2.0)
3636
PKG_CHECK_MODULES(SLIRP, slirp >= 4.1.0)
3737
PKG_CHECK_MODULES(LIBCAP, libcap)
3838
PKG_CHECK_MODULES(LIBSECCOMP, libseccomp)
39+
PKG_CHECK_MODULES(LIBCRYPTO, libcrypto)
3940

4041
AC_CONFIG_FILES([Makefile])
4142
AC_OUTPUT

main.c

+254-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <sys/socket.h>
1313
#include <sys/types.h>
1414
#include <sys/wait.h>
15+
#include <sys/timex.h>
1516
#include <linux/if_tun.h>
1617
#include <arpa/inet.h>
1718
#include <net/if.h>
@@ -23,18 +24,26 @@
2324
#include "slirp4netns.h"
2425
#include <ifaddrs.h>
2526
#include <seccomp.h>
27+
#include <openssl/sha.h>
2628

2729
#define DEFAULT_MTU (1500)
2830
#define DEFAULT_CIDR ("10.0.2.0/24")
31+
#define DEFAULT_CIDR6 ("fd00::/64")
2932
#define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2
33+
#define DEFAULT_VHOST_OFFSET6 ("2")
3034
#define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15
35+
#define DEFAULT_VDHCPSTART_OFFSET6 ("15")
3136
#define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3
37+
#define DEFAULT_VNAMESERVER_OFFSET6 ("3")
3238
#define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100
39+
#define DEFAULT_RECOMMENDED_VGUEST_OFFSET6 ("100")
3340
#define DEFAULT_NETNS_TYPE ("pid")
3441
#define NETWORK_PREFIX_MIN (1)
3542
// >=26 is not supported because the recommended guest IP is set to network addr
3643
// + 100 .
3744
#define NETWORK_PREFIX_MAX (25)
45+
#define NETWORK_PREFIX_MIN6 (8)
46+
#define NETWORK_PREFIX_MAX6 (64)
3847

3948
static int nsenter(pid_t target_pid, char *netns, char *userns,
4049
bool only_userns)
@@ -211,6 +220,103 @@ struct in6_ifreq {
211220
unsigned int ifr6_ifindex;
212221
};
213222

223+
static const char *pseudo_random_global_id(const char *device)
224+
{
225+
static char id[40];
226+
char tmp[40];
227+
unsigned char eui64[16];
228+
unsigned char hash[SHA_DIGEST_LENGTH];
229+
struct ntptimeval ntv;
230+
struct ifreq ifr;
231+
const unsigned char *mac;
232+
int sockfd;
233+
234+
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
235+
if (sockfd < 0) {
236+
perror("cannot create socket");
237+
return NULL;
238+
}
239+
240+
memset(&ifr, 0, sizeof(ifr));
241+
ifr.ifr_flags = IFF_UP | IFF_RUNNING;
242+
243+
if (device == NULL) {
244+
/* TODO: which device should we get the mac address from? */
245+
device = "lo";
246+
}
247+
strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
248+
249+
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
250+
perror("cannot get dev hwaddr");
251+
return NULL;
252+
}
253+
254+
mac = (unsigned char*)ifr.ifr_ifru.ifru_addr.sa_data;
255+
256+
/* https://tools.ietf.org/html/rfc4193
257+
*
258+
* 3.2.2. Sample Code for Pseudo-Random Global ID Algorithm
259+
*/
260+
261+
/*
262+
* 1) Obtain the current time of day in 64-bit NTP format [NTP].
263+
*/
264+
if (ntp_gettime(&ntv) == -1) {
265+
perror("cannot get ntp time");
266+
return NULL;
267+
}
268+
269+
/*
270+
* 2) Obtain an EUI-64 identifier from the system running this
271+
* algorithm. If an EUI-64 does not exist, one can be created from
272+
* a 48-bit MAC address as specified in [ADDARCH]. If an EUI-64
273+
* cannot be obtained or created, a suitably unique identifier,
274+
* local to the node, should be used (e.g., system serial number).
275+
*/
276+
eui64[8] = mac[0];
277+
eui64[9] = mac[1];
278+
eui64[10] = mac[2];
279+
eui64[11] = 0xff;
280+
eui64[12] = 0xfe;
281+
eui64[13] = mac[3];
282+
eui64[14] = mac[4];
283+
eui64[15] = mac[5];
284+
285+
/*
286+
* 3) Concatenate the time of day with the system-specific identifier
287+
* in order to create a key.
288+
*/
289+
memcpy(&eui64[0], (void *)&ntv.time.tv_sec, 4);
290+
memcpy(&eui64[4], (void *)&ntv.time.tv_usec, 4);
291+
292+
/*
293+
* 4) Compute an SHA-1 digest on the key as specified in [FIPS, SHA1];
294+
* the resulting value is 160 bits.
295+
*/
296+
SHA1(eui64, sizeof(eui64), hash);
297+
298+
/*
299+
* 5) Use the least significant 40 bits as the Global ID.
300+
*/
301+
int i = 0, j = 0;
302+
for (; j < 5; i += 2, j++) {
303+
sprintf(&tmp[i], "%02x", hash[j]);
304+
if (j > 0 && (j % 2)) {
305+
tmp[i + 2] = ':';
306+
i++;
307+
}
308+
}
309+
310+
/*
311+
* 6) Concatenate FC00::/7, the L bit set to 1, and the 40-bit Global
312+
* ID to create a Local IPv6 address prefix.
313+
*/
314+
315+
sprintf(id, "fd00:%s::", tmp);
316+
317+
return id;
318+
}
319+
214320
static int configure_network6(const char *tapname,
215321
struct slirp4netns_config *cfg)
216322
{
@@ -219,6 +325,7 @@ static int configure_network6(const char *tapname,
219325
struct in6_ifreq ifr6;
220326
struct sockaddr_in6 sai;
221327
int sockfd;
328+
const char *id;
222329

223330
sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
224331
if (sockfd < 0) {
@@ -230,20 +337,21 @@ static int configure_network6(const char *tapname,
230337
ifr.ifr_flags = IFF_UP | IFF_RUNNING;
231338
strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1);
232339

340+
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
341+
perror("cannot get dev hwaddr");
342+
return -1;
343+
}
344+
233345
if (ioctl(sockfd, SIOGIFINDEX, &ifr) < 0) {
234346
perror("cannot get dev index");
235347
return -1;
236348
}
237349

238350
memset(&sai, 0, sizeof(struct sockaddr));
239351
sai.sin6_family = AF_INET6;
352+
sai.sin6_addr = cfg->recommended_vguest6;
240353
sai.sin6_port = 0;
241354

242-
if (inet_pton(AF_INET6, "fd00::100", &sai.sin6_addr) != 1) {
243-
perror("cannot create device address");
244-
return -1;
245-
}
246-
247355
memcpy((char *)&ifr6.ifr6_addr, &sai.sin6_addr, sizeof(struct in6_addr));
248356

249357
ifr6.ifr6_ifindex = ifr.ifr_ifindex;
@@ -321,6 +429,7 @@ static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket,
321429
struct slirp4netns_config *cfg, pid_t target_pid)
322430
{
323431
int rc, tapfd;
432+
char ipv6[INET6_ADDRSTRLEN];
324433
if ((tapfd = recvfd(sock)) < 0) {
325434
return tapfd;
326435
}
@@ -333,6 +442,18 @@ static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket,
333442
printf("* Gateway: %s\n", inet_ntoa(cfg->vhost));
334443
printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver));
335444
printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest));
445+
if (cfg->enable_ipv6) {
446+
inet_ntop(AF_INET6, &cfg->vnetwork6, ipv6, INET6_ADDRSTRLEN);
447+
printf("* IPv6 Network: %s\n", ipv6);
448+
inet_ntop(AF_INET6, &cfg->vnetmask6, ipv6, INET6_ADDRSTRLEN);
449+
printf("* IPv6 Netmask: %s\n", ipv6);
450+
inet_ntop(AF_INET6, &cfg->vhost6, ipv6, INET6_ADDRSTRLEN);
451+
printf("* IPv6 Gateway: %s\n", ipv6);
452+
inet_ntop(AF_INET6, &cfg->vnameserver6, ipv6, INET6_ADDRSTRLEN);
453+
printf("* IPv6 DNS: %s\n", ipv6);
454+
inet_ntop(AF_INET6, &cfg->recommended_vguest6, ipv6, INET6_ADDRSTRLEN);
455+
printf("* IPv6 Recommended IP: %s\n", ipv6);
456+
}
336457
if (api_socket != NULL) {
337458
printf("* API Socket: %s\n", api_socket);
338459
}
@@ -453,6 +574,7 @@ static void version()
453574
struct options {
454575
char *tapname; // argv[2]
455576
char *cidr; // --cidr
577+
char *cidr6; // --cidr
456578
char *api_socket; // -a
457579
char *netns_type; // argv[1]
458580
char *netns_path; // --netns-path
@@ -813,6 +935,82 @@ static int parse_cidr(struct in_addr *network, struct in_addr *netmask,
813935
return rc;
814936
}
815937

938+
static int parse_cidr6(struct in6_addr *network, struct in6_addr *netmask,
939+
const char *cidr)
940+
{
941+
int rc = 0;
942+
regex_t r;
943+
regmatch_t matches[4];
944+
size_t nmatch = sizeof(matches) / sizeof(matches[0]);
945+
const char *cidr_regex = "^(([a-fA-F0-9]{1,4}):){1,4}:/([0-9]{1,3})";
946+
char snetwork[INET6_ADDRSTRLEN], sprefix[INET6_ADDRSTRLEN];
947+
int prefix;
948+
const char *random;
949+
rc = regcomp(&r, cidr_regex, REG_EXTENDED);
950+
if (rc != 0) {
951+
fprintf(stderr, "internal regex error\n");
952+
rc = -1;
953+
goto finish;
954+
}
955+
rc = regexec(&r, cidr, nmatch, matches, 0);
956+
if (rc != 0) {
957+
fprintf(stderr, "invalid CIDR: %s\n", cidr);
958+
rc = -1;
959+
goto finish;
960+
}
961+
rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr);
962+
if (rc < 0) {
963+
fprintf(stderr, "invalid CIDR: %s\n", cidr);
964+
goto finish;
965+
}
966+
rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr);
967+
if (rc < 0) {
968+
fprintf(stderr, "invalid CIDR: %s\n", cidr);
969+
goto finish;
970+
}
971+
random = pseudo_random_global_id(NULL);
972+
if (random == NULL) {
973+
fprintf(stderr, "cannot create pseudo random global id\n");
974+
rc = -1;
975+
goto finish;
976+
}
977+
strcpy(snetwork, random);
978+
strcat(snetwork, "0");
979+
980+
if (inet_pton(AF_INET6, snetwork, network) != 1) {
981+
fprintf(stderr, "invalid network address: %s\n", snetwork);
982+
rc = -1;
983+
goto finish;
984+
}
985+
errno = 0;
986+
prefix = strtoul(sprefix, NULL, 10);
987+
if (errno) {
988+
fprintf(stderr, "invalid prefix length: %s\n", sprefix);
989+
rc = -1;
990+
goto finish;
991+
}
992+
if (prefix < NETWORK_PREFIX_MIN6 || prefix > NETWORK_PREFIX_MAX6) {
993+
fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN6,
994+
NETWORK_PREFIX_MAX6);
995+
rc = -1;
996+
goto finish;
997+
}
998+
999+
for (int i = 0; i < 4; i++, prefix -= 32) {
1000+
if (prefix >= 32) {
1001+
netmask->__in6_u.__u6_addr32[i] = 0xffffffff;
1002+
} else if (prefix > 0) {
1003+
netmask->__in6_u.__u6_addr32[i] = htonl(~((1 << (32 - prefix)) - 1));
1004+
} else {
1005+
netmask->__in6_u.__u6_addr32[i] = 0;
1006+
}
1007+
}
1008+
1009+
finish:
1010+
regfree(&r);
1011+
return rc;
1012+
}
1013+
8161014
static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg,
8171015
const char *cidr)
8181016
{
@@ -833,6 +1031,49 @@ static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg,
8331031
return rc;
8341032
}
8351033

1034+
static int slirp4netns_config_from_cidr6(struct slirp4netns_config *cfg,
1035+
const char *cidr)
1036+
{
1037+
int rc;
1038+
char net[INET6_ADDRSTRLEN];
1039+
char tmp[INET6_ADDRSTRLEN];
1040+
rc = parse_cidr6(&cfg->vnetwork6, &cfg->vnetmask6, cidr);
1041+
if (rc < 0) {
1042+
goto finish;
1043+
}
1044+
1045+
if (inet_ntop(AF_INET6, &cfg->vnetwork6, net, INET6_ADDRSTRLEN) == NULL) {
1046+
return -1;
1047+
}
1048+
1049+
strcpy(tmp, net);
1050+
strcat(tmp, DEFAULT_VHOST_OFFSET6);
1051+
if (inet_pton(AF_INET6, tmp, &cfg->vhost6) != 1) {
1052+
return -1;
1053+
}
1054+
1055+
strcpy(tmp, net);
1056+
strcat(tmp, DEFAULT_VDHCPSTART_OFFSET6);
1057+
if (inet_pton(AF_INET6, tmp, &cfg->vdhcp_start6) != 1) {
1058+
return -1;
1059+
}
1060+
1061+
strcpy(tmp, net);
1062+
strcat(tmp, DEFAULT_VNAMESERVER_OFFSET6);
1063+
if (inet_pton(AF_INET6, tmp, &cfg->vnameserver6) != 1) {
1064+
return -1;
1065+
}
1066+
1067+
strcpy(tmp, net);
1068+
strcat(tmp, DEFAULT_RECOMMENDED_VGUEST_OFFSET6);
1069+
if (inet_pton(AF_INET6, tmp, &cfg->recommended_vguest6) != 1) {
1070+
return -1;
1071+
}
1072+
1073+
finish:
1074+
return rc;
1075+
}
1076+
8361077
static int get_interface_addr(const char *interface, int af, void *addr)
8371078
{
8381079
struct ifaddrs *ifaddr, *ifa;
@@ -909,6 +1150,14 @@ static int slirp4netns_config_from_options(struct slirp4netns_config *cfg,
9091150
cfg->enable_sandbox = opt->enable_sandbox;
9101151
cfg->enable_seccomp = opt->enable_seccomp;
9111152

1153+
if (cfg->enable_ipv6) {
1154+
rc = slirp4netns_config_from_cidr6(cfg, opt->cidr6 == NULL ? DEFAULT_CIDR6 :
1155+
opt->cidr6);
1156+
if (rc < 0) {
1157+
goto finish;
1158+
}
1159+
}
1160+
9121161
#if SLIRP_CONFIG_VERSION_MAX >= 2
9131162
cfg->enable_outbound_addr = false;
9141163
cfg->enable_outbound_addr6 = false;

slirp4netns.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -260,16 +260,16 @@ Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn)
260260
cfg.vnetmask = s4nn->vnetmask;
261261
cfg.vhost = s4nn->vhost;
262262
cfg.in6_enabled = (int)(s4nn->enable_ipv6);
263-
inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6);
263+
cfg.vprefix_addr6 = s4nn->vnetwork6;
264264
cfg.vprefix_len = 64;
265-
inet_pton(AF_INET6, "fd00::2", &cfg.vhost6);
265+
cfg.vhost6 = s4nn->vhost6;
266266
cfg.vhostname = NULL;
267267
cfg.tftp_server_name = NULL;
268268
cfg.tftp_path = NULL;
269269
cfg.bootfile = NULL;
270270
cfg.vdhcp_start = s4nn->vdhcp_start;
271271
cfg.vnameserver = s4nn->vnameserver;
272-
inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6);
272+
cfg.vnameserver6 = s4nn->vnameserver6;
273273
cfg.vdnssearch = NULL;
274274
cfg.vdomainname = NULL;
275275
cfg.if_mtu = s4nn->mtu;

0 commit comments

Comments
 (0)