Làm thế nào Apache có thể được cấu hình để nghe trên một địa chỉ IPv6 không thuộc giao diện?


3

Tôi đang cố gắng thiết lập nhiều vhost SSL SSL, mỗi cái trên một địa chỉ IPv6 khác nhau.

VPS CentOS7 của tôi có khối định tuyến / 64 IPv6 được gán cho nó, giả sử 2001:db8:acac:acac::/64, và tôi đã có thể thấy các gói đến ( tcpdump -nn -i eth0 'ip6 and src or dst net 2001:db8:acac:acac::/64'hiển thị các gói tốt).

Tôi biết rằng tôi có thể gán nhiều địa chỉ riêng lẻ như tôi muốn cho eth0 ( ip -6 addr add 2001:db8:acac:acac::1234 dev eth0), nhưng tôi muốn giao diện cho phép các ứng dụng liên kết với bất kỳ địa chỉ nào trong số 2 ^ 64 địa chỉ.

Theo lời khuyên (xem các liên kết ở dưới cùng), tôi đã thêm quy tắc ( ip -6 rule add from 2001:db8:acac:acac::/64 iif eth0 lookup 200) và tuyến đường ( ip route add local 2001:db8:acac:acac::/64 dev lo table 200) và bây giờ tôi có thể có ping6bất kỳ địa chỉ IP nào trong khối / 64 và tôi có thể kết nối với các dịch vụ nghe trên ký tự đại diện (ví dụ: :::22ssh) bằng bất kỳ địa chỉ nào trong khối / 64.

Câu hỏi là: làm thế nào tôi có thể làm cho một chương trình liên kết với một địa chỉ duy nhất trong khối / 64? Vì không có giao diện nào sở hữu bất kỳ địa chỉ nào trong khối, tôi thấy các mục sau trong nhật ký apache:

... AH00072: make_sock: could not bind to address [2001:db8:acac:acac::1234]:443

Tôi đã thấy đề cập đến IP_TRANSPARENTnhư một giải pháp khả thi, nhưng không thể tìm thấy đề cập này trong nguồn Apache, chỉ trong bits/in.h, bao gồm bởi netinet/in.h.

Có ai đã làm điều này để làm việc, cho Apache hoặc cho các ứng dụng khác (cụ thể là: dovecot, postfix, bind) chưa?


Các bài viết liên quan đọc trước khi đăng câu hỏi này:


Trong trường hợp của IPv4, câu trả lời sẽ là gán toàn bộ phạm vi cho logiao diện. Thật không may, nó chỉ hoạt động với IPv4 chứ không phải IPv6 - điều này thật mỉa mai vì có nhiều địa chỉ IP cho một máy chủ có thể sẽ xảy ra thường xuyên hơn với IPv6 so với IPv4. Tôi e rằng câu trả lời sẽ là một giải pháp sạch sẽ yêu cầu một hạt nhân được sửa đổi.
kasperd

1
Tôi đã xem các apache2nguồn và tôi không tìm thấy IP_FREEBINDhoặc IP_TRANSPARENTbất cứ nơi nào, vì vậy có vẻ như điều đó có thể không được hỗ trợ. Trong trường hợp đó, gán địa chỉ cho một số giao diện có thể là lựa chọn duy nhất.
kasperd

1
Tôi chỉ xem trang anothr ( Linux & IPv6: Làm thế nào để liên kết với một địa chỉ IPv6 tùy ý? ) Và nó nói SOL_IPkhông sử dụng SOL_SOCKET. Và bây giờ tôi có thể liên kết với bất kỳ địa chỉ!
Ashley GC

1
Tôi đã có memset(&serv_addr, 0, sizeof(serv_addr));nên làm điều đó, nhưng phát hiện tốt. Tôi đã đăng mã kiểm tra đầy đủ trong một câu trả lời.
Ashley GC

1
Tôi đã hack apr và httpd để thêm IP_FREEBINDchức năng, bây giờ tôi đang cố gắng đẩy một số bản vá vào các nguồn chính thức. Đầu tiên là vào apr: Apache BugZilla bug 58725 .
Ashley GC

Câu trả lời:


0

Bạn có thể gán địa chỉ đó cho hoàn toàn bất kỳ giao diện nào. Ví dụ: gán nó cho lo (ngoài :: 1). IPv6 khá tốt khi có nhiều địa chỉ trên bất kỳ giao diện nào. Sau đó, sau khi bạn tạo địa chỉ cục bộ, bạn có thể nghe nó.

CẬP NHẬT: Như tôi thấy, ý tưởng này không khác nhiều so với ý tưởng được đề cập trong liên kết đầu tiên của bạn về việc gán khối địa chỉ cho lo. Về cơ bản điều này là giống nhau, nhưng khối bị suy biến thành một địa chỉ duy nhất.


Tôi thường sử dụng dummy0giao diện cho các mục đích như vậy. Nhưng tôi phải thừa nhận rằng tôi không biết chính xác sự khác biệt giữa việc sử dụng lodummy0cho việc này.
kasperd

Tôi chưa thử với ipv6 nhưng với ipv4, bất kỳ mạng nào được bật losẽ khiến máy phản hồi với mọi IP trong mạng, vì vậy nếu bạn thêm, hãy nói 192.100.51.2/24với lomáy sẽ trả lời tất cả 256 192.100.51.*địa chỉ, không chỉ.2
Eric Renouf

Bạn đã làm sai. Một địa chỉ duy nhất là / 32 trong IPv4 và / 128 trong IPv6. Vì vậy, trong trường hợp của bạn phải là 192.100.51.2 / 32, hoặc, trong câu hỏi - 2001: db8: acac: acac :: 1234/128. Nếu bạn bỏ qua mặt nạ, Linux sẽ giả sử số lượng lớn nhất có thể. // Cập nhật: chỉ cần thử nghiệm này, nó hoạt động.
Nikita Kipriyanov

1
@NikitaKipriyanov Câu hỏi đặt ra là làm thế nào để có một quy trình chỉ nghe một địa chỉ IPv6 trong khi máy chủ đang lắng nghe tất cả. Và IP_TRANSPARENTkhông phải là một tùy chọn vì nó sẽ yêu cầu thay đổi đối với từng ứng dụng (và sẽ chỉ hoạt động đối với các ứng dụng chạy bằng root).
kasperd

1
@NikitaKipriyanov Nhưng điều đó vẫn không khiến các ứng dụng có thể liên kết với các địa chỉ mà không cần gán từng địa chỉ cho một giao diện. Làm thế nào bạn có thể đảm bảo rằng một quy trình có thể liên kết với bất kỳ địa chỉ nào trong toàn bộ / 64?
kasperd

0

Mặc dù tôi chưa thể khiến apache liên kết với một địa chỉ không thuộc sở hữu của giao diện, tôi đã tạo một chương trình C thử nghiệm hoạt động và có thể liên kết với bất kỳ địa chỉ IPv6 nào, bất kể địa chỉ đó có thuộc sở hữu của một giao diện:

/*
  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);
     }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.