2009-05-11 27 views
6

Tôi đang viết một máy chủ TCP cần biết giao diện nào đến từ mỗi kết nối. Tôi không thể sử dụng địa chỉ/mạng con để suy ra giao diện nào được sử dụng, vì có thể có các giao diện có cùng giá trị địa chỉ/mạng con. Nó dựa trên Linux và không cần mã di động.Tôi làm cách nào để có được tên giao diện/chỉ mục được liên kết với một ổ cắm TCP?

Tất cả những gì tôi có thể tìm là các hàm để có được tất cả giao diện hoặc một giao diện duy nhất theo chỉ mục. Tôi không thể tìm thấy bất kỳ cách nào để có được giao diện liên kết với một ổ cắm TCP được chấp nhận.

Bất kỳ ý tưởng nào? Một cái gì đó tôi đã bỏ lỡ?

EDIT: Để nhắc lại, địa chỉ IP không phải là duy nhất trong trường hợp của tôi. Cả địa chỉ đích (chính máy chủ) cũng không phải địa chỉ nguồn (máy khách). Vâng, đây là một sơ đồ IP rất cực đoan.

+0

Các chỉ có câu trả lời chính xác ở đây, cho rằng bạn đang tìm mã để thêm vào máy chủ của bạn, là một từ User1. Tôi đã thử nghiệm nó, và nó hoạt động - bạn nên chấp nhận nó. Xem thêm http: // stackoverflow.com/questions/43659634/find-the-interface-sử dụng-by-a-connected-socket/43663713 # 43663713 – EML

Trả lời

0

Nhìn vào địa chỉ đích.

Mỗi giao diện thường được liên kết với một địa chỉ duy nhất. Nếu nhiều giao diện được liên kết với nhau, nó có thể không quan trọng cái nào nó được sử dụng.

Ngoại lệ duy nhất cho điều này, là khi sử dụng ipv6 anycast, nhưng thậm chí sau đó, bạn sẽ không bình thường có nhiều giao diện trên cùng một máy chủ với cùng một ip.

+0

Trong trường hợp này, các giao diện không bị ràng buộc với các địa chỉ duy nhất. Chúng không được liên kết. Chúng được kết nối với các mạng khác nhau, mặc dù chúng có các IP trùng lặp/giống nhau. –

+0

Nếu bạn có kết nối TCP, bạn phải có địa chỉ IP. Bạn không cần phải gắn một ổ cắm vào một địa chỉ cụ thể, nhưng giao diện của bạn cần một – JimB

+0

Có, kết nối có địa chỉ IP, nhưng nó không phải là duy nhất. Nó không phải là duy nhất cho giao diện đó, và nó không phải là duy nhất cho mạng đó. Tôi thực sự cần một cách để có được giao diện từ chính nó. –

1

Tôi nghĩ rằng việc sử dụng getockname() sau khi chấp nhận() nhập kết nối đến có thể là những gì bạn đang theo dõi. Hai hàm getockname() và getpeername() nhận được các địa chỉ cục bộ và từ xa tương ứng mà một socket được gắn kết. Cả hai nên hợp lệ cho một socket TCP được kết nối hoàn toàn.

Chỉnh sửa: Mặc dù điều này có vẻ đúng đối với OpenBSD theo trang người đàn ông, trang người dùng Linux khác đáng kể và do đó getockname() sau khi chấp nhận() trên Linux gần như chắc chắn không có gì đáng ngạc nhiên. Dạy tôi sử dụng trí nhớ của tôi thay vì kiểm tra mọi thứ. sigh

+0

thực sự, tôi đã thử nó và giải pháp này hoạt động trong linux và windows – thang

+0

Sai 'tên' - điều này trả về * địa chỉ *. OP muốn tên giao diện, nghĩa là. eth0/etc. Như các tài liệu glibc nói, "các chức năng và biểu tượng để xử lý các địa chỉ ổ cắm được đặt tên không nhất quán, đôi khi sử dụng thuật ngữ 'tên', và đôi khi sử dụng 'địa chỉ'". – EML

2

Bảng định tuyến hạt nhân quyết định giao diện nào sẽ gửi gói dữ liệu, do đó khả năng liên kết thiết bị. Một cái nhìn lướt qua "Lập trình Socket Linux, Warren W. Gay" gợi ý rằng việc chỉ định một giao diện là xấu, và do sự năng động của hạt nhân (tường lửa, chuyển tiếp) nó phức tạp hơn.

Tôi sẽ đề nghị thay đổi sơ đồ IP của bạn sao cho thông tin IP cho bạn biết (các) giao diện của bạn thông qua tìm kiếm theo cách tương tự ifconfig, nếu không bạn sẽ tự chụp mình trong thiết kế chân.

1) Lấy thông tin IP so với phiên TCP 2) Lookup mà giao diện (s) điều này có thể có giá trị

tôi sẽ tiếp tục tìm kiếm trong API hạt nhân mặc dù. Bạn không cần phải biết điều này, trừu tượng là có cho vô số lý do tốt.

tắm tưởng cân nhắc về vấn đề này, có vẻ như rằng nếu cả hai giao diện sử dụng cùng một IP sau đó phải có một địa chỉ khách hàng sự khác biệt phạm vi định tuyến (nếu không cả hai giao diện sẽ được sử dụng). Máy chủ của bạn có thể kiểm tra bảng định tuyến dựa trên máy khách IP

+0

IP của khách hàng cũng có thể chồng lên nhau. Sự khác biệt cho phép các IP này chồng lên nhau là VLAN id. Theo như tôi biết, điều đó cũng không thể tiếp cận được. –

+0

bạn có thể đã tìm thấy một tình huống mà bạn không thể sử dụng bằng chứng gián tiếp để xác định giao diện. Tôi không nghĩ rằng sẽ có một cách, như giao diện là loại trừu tượng từ các ứng dụng cao hơn hạt nhân. Bạn có thể có thể sửa đổi hạt nhân của bạn để cung cấp một số dữ liệu thông qua/proc/tcp_sessions_to_devs hoặc một cái gì đó nhưng đó là RẤT - erm - tuyệt vọng? –

+0

Một ý nghĩ khác. Hãy thử nhìn vào các thư viện giao diện sniffing mà Wireshark sử dụng (libpcap) và xem cách nguồn iftop liệt kê các phiên trên một thiết bị. Có thể thú vị. –

4

Nói chung, bạn không cần phải biết giao diện nào sẽ được gửi/nhận trên giao diện nào; đó là công việc của bảng định tuyến của hạt nhân. Thật khó để tìm ra giao diện cho một ổ cắm vì thực sự không có sự liên kết trực tiếp nào. Việc định tuyến các gói tin có thể thay đổi trong vòng đời của socket dựa trên thông tin định tuyến.

Cho ổ cắm datagram (UDP), bạn có thể sử dụng getsockopt(s, IPPROTO_IP, IP_PKTINFO, ...); xem getsockopt(2)ip(7).

Đối với các ổ cắm luồng (TCP), một tùy chọn có thể là mở nhiều khe cắm nghe, một cho mỗi giao diện trên hệ thống và sử dụng setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ...) để liên kết từng giao diện; xem setsockopt(2)socket(7).

+0

Có, thiết lập ổ cắm riêng biệt cho mỗi giao diện sẽ hoạt động, nhưng nhìn thấy khi có hàng chục giao diện và chúng được đưa lên và đưa xuống động, đó là điều tôi muốn tránh. –

0

Rõ ràng không phải cái gì tôi đã nhìn vào rất sâu sắc, hãy để một mình cố gắng, đây có thể là một cho "vì vậy điên nó chỉ có thể làm việc" giỏ ...

Nếu nó thực sự chỉ bao giờ sẽ cho Linux, bạn có thể viết một mô-đun netfilter tùy chỉnh theo dõi các kết nối đến và ghi chú giao diện mà chúng đi vào và viết thông tin đó ở đâu đó để ứng dụng máy chủ của bạn đọc.

0

Đề xuất của Kieron viết một mô đun netfilter có lẽ là một cách để thử, nhưng tôi muốn không viết mô-đun hạt nhân đầu tiên của mình cho giải pháp này.

Tôi đã đưa ra tùy chọn khác sử dụng nguồn NAT và dịch cổng nguồn của kết nối để tương quan với nguồn kết nối. Tôi có thể gán các phạm vi cổng cho mỗi mạng và kiểm tra nó trong máy chủ. Vấn đề duy nhất là nguồn NAT trong iptables được thực hiện trong chuỗi POSTROUTING, và tôi không chắc nó được sử dụng cho các kết nối được chấp nhận bởi máy chủ đó, vì vậy tôi có thể cần phải sử dụng một máy chủ khác.

Không giải pháp dễ dàng ở đây, quá xấu tôi không thể có được giao diện tên/index từ các ổ cắm ...

+0

Một tùy chọn netfilter khác là chuyển tiếp lưu lượng truy cập đến các giao diện cục bộ bổ sung đã được thiết lập với các địa chỉ IP riêng biệt. – Bell

0

tôi thêm một câu trả lời và một giải pháp tiềm năng sau khi xem xét thông qua nguồn gốc của Wireshark và iftop mà dường như có chức năng tương tự gián tiếp.

Dường như với tôi rằng bạn có thể sử dụng libpcap để đánh hơi trên giao diện. Giả sử bạn có thể xác định một phần duy nhất của phiên TCP/IP sau đó bạn có thể theo dõi nó xuống một giao diện khá đơn giản bằng cách sử dụng các bộ lọc và theo dõi phiên.

Không kernel module (và nó đóng tốt đẹp với chủ đề)

http://www.ex-parrot.com/pdw/iftop/ Một số nguồn đơn giản để có một peek tại www.tcpdump.org/ cho libpcap

Tôi nghĩ rằng bạn sẽ có thể để phù hợp với VLAN sử dụng nó quá.

Wireshark cũng có thể hữu ích để gỡ lỗi. Hi vọng điêu nay co ich! Nó đã được trên não của tôi kể từ đó.

+0

Đúng, có vẻ như một tùy chọn khác. Cảm ơn cho những nỗ lực! –

2

Sử dụng getsockname() để nhận IP kết thúc cục bộ của kết nối TCP. Sau đó sử dụng getifaddrs() để tìm giao diện tương ứng:

struct sockaddr_in addr; 
struct ifaddrs* ifaddr; 
struct ifaddrs* ifa; 
socklen_t addr_len; 

addr_len = sizeof (addr); 
getsockname(sock_fd, (struct sockaddr*)&addr, &addr_len); 
getifaddrs(&ifaddr); 

// look which interface contains the wanted IP. 
// When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...) 
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 
{ 
    if (ifa->ifa_addr) 
    { 
     if (AF_INET == ifa->ifa_addr->sa_family) 
     { 
      struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr; 

      if (inaddr->sin_addr.s_addr == addr.sin_addr.s_addr) 
      { 
       if (ifa->ifa_name) 
       { 
        // Found it 
       } 
      } 
     } 
    } 
} 
freeifaddrs(ifaddr); 

Trên đây là chỉ là một ví dụ bẩn, một số sửa đổi là cần thiết:

  1. Thêm thiếu kiểm tra lỗi hỗ trợ
  2. IPv6
+0

Tôi đã thử nghiệm tính năng này và hoạt động - cảm ơn – EML

Các vấn đề liên quan