Я пытаюсь настроить несколько хостов Apache SSL vhosts, каждый на своем IPv6-адресе.
Моему CentOS7 VPS назначен маршрутизируемый / 64 IPv6-блок, скажем 2001:db8:acac:acac::/64
, и я уже вижу входящие пакеты (tcpdump -nn -i eth0 'ip6 and src or dst net 2001:db8:acac:acac::/64'
показывает пакеты нормально).
Я знаю, что могу присвоить eth0 столько индивидуальных адресов, сколько захочу (ip -6 addr add 2001:db8:acac:acac::1234 dev eth0
), но я хочу, чтобы интерфейс позволял приложениям связываться с любым из 2 ^ 64 адресов.
Следуя совету (см. Ссылки внизу), я добавил правило (ip -6 rule add from 2001:db8:acac:acac::/64 iif eth0 lookup 200
) и маршрут (ip route add local 2001:db8:acac:acac::/64 dev lo table 200
) и теперь я могу ping6
любой IP-адрес в блоке / 64, и я могу подключиться к службам, прослушивающим подстановочный знак (например, :::22
для ssh), используя любой адрес в блоке / 64.
Вопрос is: как я могу привязать программу к не замужем адрес в блоке / 64? Поскольку ни один интерфейс не владеет адресами в блоке, я вижу в журналах apache следующее:
... AH00072: make_sock: could not bind to address [2001:db8:acac:acac::1234]:443
Я видел упоминания о IP_TRANSPARENT
как возможное решение, но не могу найти это упомянуто в источнике Apache, только в bits/in.h
, включенный netinet/in.h
.
У кого-нибудь это работает, либо для Apache, либо для других приложений (в частности: dovecot, postfix, bind)?
Перед отправкой этого вопроса прочтите соответствующие статьи:
Пока я еще не смог сделать Apache привязать к одному адресу, который не принадлежит интерфейсу, я создал тестовую программу на C, которая работает и может привязываться к любому адресу IPv6, независимо от того, принадлежит ли адрес интерфейсу:
/*
Test program to bind to an IPv6 address not owned by an interface.
This code is from public domain sources, and is released into the public domain.
*/
#include <arpa/inet.h>
#include <error.h>
#include <errno.h>
#include <net/if.h>
#include <netinet/in.h> // also includes bits/in.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h> // also includes bits/ioctls.h
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
// A real address to use as a sanity check and make sure the program can
// bind to an address which *is* owned by an interface
#define REALADDR {{{0x2a,0x00, ...}}}
// A fake address to show the program can bind to an address which is *not*
// owned by any interface
#define SOMEADDR {{{0x20,0x01, 0x0d,0xb8, 0x00,0x00, 0x00,0x00, \
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x01 }}}
int main(int argc, char *argv[])
{
struct sockaddr_in6 serv_addr;
int listenfd = 0, connfd = 0, i;
char sendBuff[1025];
time_t ticks;
listenfd = socket(AF_INET6, SOCK_STREAM, 0);
printf("socket fd is %d\n", listenfd);
memset(&serv_addr, 0, sizeof(serv_addr));
memset(sendBuff, 0, sizeof(sendBuff));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(5000);
struct in6_addr someaddr = SOMEADDR;
serv_addr.sin6_addr = someaddr;
// Here's the magic:
int opt = 1;
if (setsockopt(listenfd, SOL_IP, IP_FREEBIND, &opt, sizeof(opt)) < 0)
error(1, errno, "setsockopt(IP_FREEBIND) failed");
printf("Binding to ");
for (i = 0; i < 14; i += 2)
printf("%x:", (serv_addr.sin6_addr.s6_addr[i] << 8) +
serv_addr.sin6_addr.s6_addr[i+1]);
printf("%x\n", (serv_addr.sin6_addr.s6_addr[14] << 8) +
serv_addr.sin6_addr.s6_addr[15]);
if (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
error(1, errno, "bind failed");
if (listen(listenfd, 10) < 0)
error(1, errno, "listen failed");
while(1)
{
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
printf("accept returned %d\n", connfd);
// Send some data - the current date and time.
ticks = time(NULL);
snprintf(sendBuff, sizeof(sendBuff), "Now is %.24s\r\n", ctime(&ticks));
write(connfd, sendBuff, strlen(sendBuff));
close(connfd);
sleep(1);
}
}
Вы можете назначить этот адрес абсолютно любому интерфейсу. Например, присвойте ему lo (в дополнение к :: 1). IPv6 довольно хорош при наличии нескольких адресов на любом интерфейсе. Затем, после того как вы сделали адрес локальным, вы можете прослушивать его.
UPD: Как я вижу, эта идея не сильно отличается от той, что была упомянута в вашей первой ссылке о назначении блока адресов для lo. По сути, это то же самое, но блок вырожден в один адрес.