UNIX: разработка сетевых приложений
Если функция19-20возвращает ошибкуioctlи функция еще не возвращалась успешно (то естьEINVALвсе еще равно нулю), значит, мы еще не выделили буфер достаточного размера, поэтому мы продолжаем выполнять цикл.lastlenЕсли функция22-23завершается успешно и возвращаемая длина равнаioctl, значит, длина не изменилась (наш буфер имеет достаточный размер), и мы с помощью функцииlastlenвыходим из цикла, так как у нас имеется вся информация.breakВ каждом проходе цикла мы увеличиваем размер буфера для хранения еще 10 структур26-27.ifreqИнициализация указателей связного спискаПоскольку мы будем возвращать указатель на начало связного списка структур29-31, мы используем две переменныеifi_infoиifiheadдля хранения указателей на список по мере его создания.ifipnextСледующая часть нашей функции
, содержащая начало основного цикла, показана в листинге 17.5.get_ifi_infoЛистинг 17.5. Конфигурация интерфейса процесса
//lib/get_ifi_info.c34 for (ptr = buf; ptr < buf + ifc.ifc_len; ) {35 ifr = (struct ifreq*)ptr;36 #ifdef HAVE_SOCKADDR_SA_LEN37 len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);38 #else39 switch (ifr->ifr_addr.sa_family) {40 #ifdef IPV641 case AF_INET6:42 len = sizeof(struct sockaddr_in6);43 break;44 #endif45 case AF_INET:46 default:47 len = sizeof(struct sockaddr);48 break;49 }50 #endif /* HAVE_SOCKADDR_SA_LEN */51 ptr += sizeof(ifr->ifr_name) + len; /* для следующей строки */52 #ifdef HAVE_SOCKADDR_DL_STRUCT53 /* предполагается, что AF_LINK идет перед AF_INET и AF_INET6 */54 if (ifr->ifr_addr.sa_family == AF_LINK) {55 struct sockaddr_dl *sdl = (struct sockaddr_dl*)&ifr->ifr_addr;56 sdlname = ifr->ifr_name;57 idx = sdl->sdl_index;58 haddr = sdl->sdl_data + sdl->sdl_nlen;59 hlen = sdl->sdl_alen;60 }61 #endif62 if (ifr->ifr_addr.sa_family != family)63 continue; /* игнорируется, если семейство адреса не то */64 myflags = 0;65 if ((cptr = strchr(ifr->ifr_name, ':')) != NULL)66 *cptr = 0; /* замена двоеточия нулем */67 if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {68 if (doaliases == 0)69 continue; /* этот интерфейс уже обработан */70 myflags = IFI_ALIAS;71 }72 memcpy(lastname, ifr->ifr_name, IFNAMSIZ);73 ifrcopy = *ifr;74 Ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);75 flags = ifrcopy.ifr_flags;76 if ((flags & IFF_UP) == 0)77 continue; /* игнорируется, если интерфейс не используется */Переход к следующей структуре адреса сокетаПри последовательном просмотре всех структур i35-51указывает на текущую структуру, а мы увеличиваемfreq ifrна единицу, чтобы он указывал на следующую. Необходимо предусмотреть особенность более новых систем, предоставляющих поле длины для структур адреса сокета, и вместе с тем учесть, что более старые системы этого поля не предоставляют. Хотя в листинге 17.1 структура адреса сокета, содержащаяся в структуреptr, объявляется как общая структура адреса сокета, в новых системах она может относиться к произвольному типу. Действительно, в 4.4BSD структура адреса сокета канального уровня также возвращается для каждого интерфейса [128, с. 118]. Следовательно, если поддерживается элемент длины, то мы должны использовать его значение для переустановки нашего указателя на следующую структуру адреса сокета. В противном случае мы определяем длину, исходя из семейства адресов, используя размер общей структуры адреса сокета (16 байт) в качестве значения по умолчанию.ifreqПРИМЕЧАНИЕВ системах, поддерживающих IPv6, не оговаривается, возвращается ли адрес IPv6 вызовом SIOCGIFCONF. Для более новых систем мы вводим оператор case, в котором предусмотрена возможность возвращения адресов IPv6. Проблема состоит в том, что объединение в структуре ifreq определяет возвращаемые адреса как общие 16-байтовые структуры sockaddr, подходящие для 16-байтовых структур sockaddr_in IPv4, но для 24-байтовых структур sockaddr_in6 IPv6 они слишком малы. В случае возвращения адресов IPv6 возможно некорректное поведение существующего кода, созданного в предположении, что в каждой структуре ifreq содержится структура sockaddr фиксированного размера. В системах, где структура sockaddr имеет поле sa_len, никаких проблем не возникает, потому что такие системы легко могут указывать размер структур sockaddr.
Если система возвращает структуры52-60семействаsockaddrвAF_LINK, мы копируем индекс интерфейса и данные об аппаратном адресе из таких структур.SIOCGIFCONFМы игнорируем все адреса из семейств, отличных от указанного вызывающим процессом в аргументе функции62-63.get_ini_info