2010-11-09 55 views
11

Giả sử tôi đang chạy một chương trình có tên IpAddresses.c. Tôi muốn chương trình đó nhận tất cả các địa chỉ IP mà thiết bị này có theo từng giao diện. Giống như ifconfig. Làm thế nào tôi có thể làm điều đó?Làm cách nào để biết địa chỉ IP cho giao diện trong C?

Tôi không biết nhiều về ioctl, nhưng tôi đọc nó có thể giúp tôi.

+3

Nền tảng của bạn là gì? –

Trả lời

19

Chỉ cần sử dụng getifaddrs(). Dưới đây là một ví dụ:

#include <arpa/inet.h> 
#include <sys/socket.h> 
#include <ifaddrs.h> 
#include <stdio.h> 

int main() 
{ 
    struct ifaddrs *ifap, *ifa; 
    struct sockaddr_in *sa; 
    char *addr; 

    getifaddrs (&ifap); 
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 
     if (ifa->ifa_addr->sa_family==AF_INET) { 
      sa = (struct sockaddr_in *) ifa->ifa_addr; 
      addr = inet_ntoa(sa->sin_addr); 
      printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr); 
     } 
    } 

    freeifaddrs(ifap); 
    return 0; 
} 

Và đây là kết quả tôi nhận được trên máy tính của tôi:

Interface: lo Address: 127.0.0.1 
Interface: eth0 Address: 69.72.234.7 
Interface: eth0:1  Address: 10.207.9.3 
+0

tại sao bạn có 2 eth0? btw, thnks cho câu trả lời của bạn – gvalero87

+0

@ gvalero87 eth0 đầu tiên nói với card mạng để giao tiếp qua Internet. Đó là eth0 thứ hai giao tiếp qua một kết nối riêng (đường quang) cho một bên thứ ba. Đây là cài đặt trong bảng định tuyến mà quản trị viên mạng đặt cùng nhau. – chrisaycock

0

Kiểm tra (Windows cụ thể) IP Helper API - may mắn là bạn không cần ioctl cho điều này trên Windows.

0

Bạn có thể thử một cái gì đó như thế:

struct ifreq ifr[MAX_INTERFACES]; 
struct ifconf ifc; 
memset(ifr, 0, sizeof(ifr)); 
ifc.ifc_len = sizeof(ifr); 
ifc.ifc_req = ifr; 

// Get the list of interfaces 
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { 
    fprintf(stderr, "ioctl SIOCGIFCONF failed: %d", errno); 
} 

for (int i=0; i < ifc.ifc_len/sizeof(struct ifreq); ++i) { 
    if (ifr[i].ifr_addr.sa_family != AF_INET) { 
     continue; 
    } 

    // Maybe some more filtering based on SIOCGIFFLAGS 

    // Your code 
} 
+3

Lưu ý rằng mã này sẽ không thành công trên BSD khi kích thước của cấu trúc ifreq thay đổi. Bạn không thể chỉ giả sử 'ifc.ifc_len/sizeof (struct ifreq)' sẽ cung cấp cho bạn số lượng giao diện. Thay vào đó, bạn cần phải lặp qua các phần tử trong danh sách như sau: 'struct ifreq * ifr_iterator = ifc.ireq; size_t len; trong khi (i ifr_addr.sa_len; ifr_iterator = (struct ifreq *) ((char *) ifr_iterator + len); i + = len; } ' – Joakim

+0

@Joakim vì vậy điều đó có nghĩa rằng' '#define ifc_req ifc_ifcu.ifcu_req/* mảng của cấu trúc ret'd * /' 'về mặt kỹ thuật không phải là một mảng vì kích thước khác nhau? Có vẻ như 5.9 đã thêm nhiều thứ hơn. Ngoài ra ifconfig.c mã dường như không có SIOCGIFCONF – GorillaApe

+0

@GorillaApe Trên một hệ thống nó có thể là một mảng, trên một không. Có những cách triển khai khác nhau là quan điểm của tôi. Trên Linux, đây là một mảng với mỗi mục có kích thước cố định, nhưng trên BSD kích thước của mỗi mục có thể khác nhau, do đó, cách chính xác để làm việc đó là luôn sử dụng trường độ dài. Nếu bạn muốn mã di động. – Joakim

5

Dưới đây là một số Linux mẫu mã có thể giúp bạn ra ngoài.

#include <stdio.h> 
#include <net/if.h> 
#include <netinet/in.h> 
#include <sys/ioctl.h> 
#include <sys/types.h> 
#include <sys/socket.h> 

#define INT_TO_ADDR(_addr) \ 
(_addr & 0xFF), \ 
(_addr >> 8 & 0xFF), \ 
(_addr >> 16 & 0xFF), \ 
(_addr >> 24 & 0xFF) 

int main() 
{ 
    struct ifconf ifc; 
    struct ifreq ifr[10]; 
    int sd, ifc_num, addr, bcast, mask, network, i; 

    /* Create a socket so we can use ioctl on the file 
    * descriptor to retrieve the interface info. 
    */ 

    sd = socket(PF_INET, SOCK_DGRAM, 0); 
    if (sd > 0) 
    { 
     ifc.ifc_len = sizeof(ifr); 
     ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr; 

     if (ioctl(sd, SIOCGIFCONF, &ifc) == 0) 
     { 
      ifc_num = ifc.ifc_len/sizeof(struct ifreq); 
      printf("%d interfaces found\n", ifc_num); 

      for (i = 0; i < ifc_num; ++i) 
      { 
       if (ifr[i].ifr_addr.sa_family != AF_INET) 
       { 
        continue; 
       } 

       /* display the interface name */ 
       printf("%d) interface: %s\n", i+1, ifr[i].ifr_name); 

       /* Retrieve the IP address, broadcast address, and subnet mask. */ 
       if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0) 
       { 
        addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr; 
        printf("%d) address: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(addr)); 
       } 
       if (ioctl(sd, SIOCGIFBRDADDR, &ifr[i]) == 0) 
       { 
        bcast = ((struct sockaddr_in *)(&ifr[i].ifr_broadaddr))->sin_addr.s_addr; 
        printf("%d) broadcast: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(bcast)); 
       } 
       if (ioctl(sd, SIOCGIFNETMASK, &ifr[i]) == 0) 
       { 
        mask = ((struct sockaddr_in *)(&ifr[i].ifr_netmask))->sin_addr.s_addr; 
        printf("%d) netmask: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(mask)); 
       }     

       /* Compute the current network value from the address and netmask. */ 
       network = addr & mask; 
       printf("%d) network: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(network)); 
      }      
     } 

     close(sd); 
    } 

    return 0; 
} 

+0

Cảm ơn bạn đã thay thế ioctl. Tuy nhiên tôi không bao giờ biết, khi tôi nên sử dụng 'ioctl' và khi' getifaddrs'. – Youda008

+0

Tôi tin rằng 'ioctl' có thể hơi linh động hơn ngay cả khi nó không phù hợp với bất kỳ tiêu chuẩn duy nhất nào.Nó được hỗ trợ trên hầu hết các hệ thống giống Unix và Unix và lệnh gọi hàm ioctl' xuất hiện lần đầu trong Phiên bản 7 AT & T UNIX. Tôi tin rằng 'getifaddrs' được hỗ trợ trong BSD và Linux và lần đầu tiên xuất hiện trong glibc 2.3. – jschmier

+0

Điều này cũng hoạt động trên Android không có thứ 'ifaddrs' vì lý do nào đó nhưng không có trên OS X. [Đây là ví dụ] (https://gist.github.com/OrangeTide/909204) hoạt động trên cả hai. – Grishka

2

Các giải pháp sử dụng getifaddrs() là rất tốt. Tôi sẽ đề nghị chỉ có một cải tiến:

--- chrisaycock 
+++ normando 
@@ -11,7 +11,7 @@ 

    getifaddrs (&ifap); 
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 
-  if (ifa->ifa_addr->sa_family==AF_INET) { 
+  if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) { 
      sa = (struct sockaddr_in *) ifa->ifa_addr; 
      addr = inet_ntoa(sa->sin_addr); 
      printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr); 

Chỉ vì bản thân tôi có Lỗi phân đoạn.

+0

Tôi gặp lỗi phân đoạn. Mã này sửa chữa nó. –

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