網路程式設計學習筆記(ioctl操作)
阿新 • • 發佈:2019-02-19
1、ioctl函式
其函式需要的標頭檔案及宣告如下:
#include <unistd.h>
int ioctl(int fd, int request, .../*void *arg/);
第三個引數總是一個指標,但指標的型別依賴於request
把和網路有關的請求分為6類:
(1)套介面操作
(2)檔案操作
(3)介面操作
(4)ARP快取記憶體操作
(5)路由表操作
(6)流系統
2、套介面操作
有三種ioctl請求是針對套介面的,它們都要求ioctl的第三個引數是一個指向整數的指標
類別 | request(請求) | 描述 | 資料型別 |
套介面 | SIOCATMARK SIOCSPGRP SIOCGPGRP | 在帶外標誌上 設定套介面的程序ID或程序組ID 獲取套介面的程序ID或程序組ID |
int int int |
檔案 | FIONBIO FIOASYNC FIONREAD FIOSETOWN FIOGETOWN |
設定/清除非阻塞標誌 設定/清除非同步I/O標誌 獲取接收緩衝區中的位元組數 設定檔案的程序ID或程序組ID 獲取檔案的程序ID或程序組ID |
int int int int int |
介面 | SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC | 獲取所有介面的列表 設定介面地址 獲取介面地址 設定介面標誌 獲取介面標誌 設定點到點地址 獲取點到點地址 獲取廣播地址 設定廣播地址 獲取子網掩碼 設定子網掩碼 獲取介面的測度 設定介面的測度 |
struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq |
ARP | SIOCSARP SIOCGARP SIOCDARD |
建立/修改arp項 獲取arp項 刪除arp項 | struct arpreq struct arpreq struct arpreq |
路由 | SIOCADDRT SIOCDELRT |
增加路徑 刪除路徑 |
struct rtentry struct rtenrty |
流 | I_xxx |
3、獲取介面資訊
有兩個結構體ifconf和ifreq,其定義如下:
struct ifconf
{
int ifc_len;
union {
caddr_t ifcu_buf; //input from user->kernel
struct ifreq *ifcu_req;//return from kernel->user
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf
#define ifc_req ifc_ifcu.ifcu_req
#define IFNAMSIZ 16
struct ifreq
{
char ifr_name[IFNAMSIZ];
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
}ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr
#define ifr_flags ifr_ifru.ifru_flags
#define ifr_metric ifr_ifru.ifru_metric
#define ifr_data ifr_ifru.ifru_data
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define IFI_NAME 16
#define IFI_HADDR 8
#define IFI_ALIAS 1
struct ifi_info
{
char ifi_name[IFI_NAME];
char ifi_haddr[IFI_HADDR];
short ifi_hlen;
short ifi_flags;
short ifi_myflags;
struct sockaddr* ifi_addr;
struct sockaddr* ifi_brdaddr;
struct sockaddr* ifi_dstaddr;
struct ifi_info *ifi_next;
};
struct ifi_info *get_ifi_info(int,int);
struct ifi_info *Get_ifi_info(int, int);
void free_ifi_info(struct ifi_info *);
char *sock_ntop(struct sockaddr*, int);
int main(int argc, char **argv)
{
struct ifi_info *ifi, *ifihead;
struct sockaddr *sa;
char *ptr;
int i, family, doaliases;
if (argc != 3) {
fprintf(stderr, "usage:prifinfo <inet4|inet6> <doaliases>\n");
return -1;
}
if (strcmp(argv[1], "inet4") == 0) {
family = AF_INET;
} else if (strcmp(argv[1], "inet6") == 0) {
family = AF_INET6;
}
doaliases = atoi(argv[2]);
for (ifihead = ifi = get_ifi_info(family, doaliases); ifi != NULL; ifi = ifi->ifi_next) {
printf("%s:<" , ifi->ifi_name);
if (ifi->ifi_flags & IFF_UP) printf("UP ");
if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST ");
if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST ");
if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP ");
if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P ");
printf("> \n");
if ((i = ifi->ifi_hlen) > 0) {
ptr = ifi->ifi_haddr;
do {
printf("%s%x", (i == ifi->ifi_hlen) ? " " : ":", *ptr++);
} while (--i > 0);
printf("\n");
}
if ((sa = ifi->ifi_addr) != NULL) {
printf(" IP addr: %s\n", sock_ntop(sa, sizeof(*sa)));
}
if((sa = ifi->ifi_brdaddr) != NULL) {
printf(" broadcast addr: %s\n", sock_ntop(sa, sizeof(*sa)));
}
if ((sa = ifi->ifi_dstaddr) != NULL) {
printf(" destination addr: %s\n", sock_ntop(sa, sizeof(*sa)));
}
}
free_ifi_info(ifihead);
return 0;
}
struct ifi_info *get_ifi_info(int family, int doaliases)
{
struct ifi_info *ifi, *ifihead, **ifipnext;
int sockfd, len, lastlen, flags, myflags;
char *ptr, *buf, lastname[128], *cptr;
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sinptr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
fprintf(stderr, "socket error:%s\n", strerror(errno));
return NULL;
}
lastlen = 0;
len = 100 * sizeof(struct ifreq);
for (;;) {
buf = malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
if (errno != EINVAL || lastlen != 0) {
fprintf(stderr, "ioctl error:%s\n", strerror(errno));
return NULL;
}
} else {
if (ifc.ifc_len == lastlen) break;
lastlen = ifc.ifc_len;
}
len += 10 * sizeof(struct ifreq);
free(buf);
}
ifihead = NULL;
ifipnext = &ifihead;
lastname[0] = 0;
for (ptr = buf; ptr < buf + ifc.ifc_len;) {
ifr = (struct ifreq*)ptr;
switch(ifr->ifr_addr.sa_family) {
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
case AF_INET:
default:
len = sizeof(struct sockaddr_in);
break;
}
ptr += sizeof(ifr->ifr_name) + len;
if (ifr->ifr_addr.sa_family != family) {
continue;
}
myflags = 0;
if ((cptr = strchr(ifr->ifr_name, ':')) != NULL) {
*cptr = 0;
}
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
if (doaliases == 0) {
continue;
}
myflags = IFI_ALIAS;
}
memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
ifrcopy = *ifr;
ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
flags = ifrcopy.ifr_flags;
if ((flags & IFF_UP) == 0) continue;
ifi = calloc(1, sizeof(struct ifi_info));
*ifipnext = ifi;
ifi->ifi_flags = flags;
ifi->ifi_myflags = myflags;
memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
ifi->ifi_name[IFI_NAME - 1] = 0;
switch(ifr->ifr_addr.sa_family) {
case AF_INET:
sinptr = (struct sockaddr_in*)&ifr->ifr_addr;
if (ifi->ifi_addr == NULL) {
ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
}
if (flags & IFF_BROADCAST) {
ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
sinptr = (struct sockaddr_in*)&ifrcopy.ifr_broadaddr;
ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
}
if (flags & IFF_POINTOPOINT) {
ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sinptr = (struct sockaddr_in*)&ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
}
break;
default:
break;
}
}
free(buf);
return(ifihead);
}
void free_ifi_info(struct ifi_info *ifihead)
{
struct ifi_info *ifi, *ifinext;
for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
if (ifi->ifi_addr != NULL) free(ifi->ifi_addr);
if (ifi->ifi_brdaddr != NULL) free(ifi->ifi_brdaddr);
if (ifi->ifi_dstaddr != NULL) free(ifi->ifi_dstaddr);
ifinext = ifi->ifi_next;
free(ifi);
}
}
char *sock_ntop(struct sockaddr* sa, int len)
{
char portstr[7];
static char str[128];
switch(sa->sa_family) {
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in*)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) return NULL;
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin_port));
strcat(str, portstr);
}
return str;
}
}
return NULL;
}
4、ARP快取記憶體操作
也是由ioctl函式操作的,這些請求用一個arpreq結構,在<net/if_arp.h>中定義
struct arpreq
{
struct sockaddr arp_pa; //protocol address
struct sockaddr arp_ha; //hardware address
int arp_flags;
};
#define ATF_INUSE 0x01
#define ATF_COM 0x02
#define ATF_PERM 0x04
#define ATF_PUBL 0x08
SIOCSARP:把新項回到 ARP快取記憶體中或修改一個已有項。
SIOCDARP:從arp快取記憶體中刪除一項
SIOCGARP:從快取記憶體中取一項
獲取快取:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if_arp.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
char **my_addrs(int *addtype);
int main(int argc, char **argv)
{
int family, sockfd;
char str[INET6_ADDRSTRLEN];
char **pptr;
unsigned char *ptr;
struct arpreq arpreq;
struct sockaddr_in *sin;
pptr = my_addrs(&family);
for (; *pptr != NULL; pptr++) {
printf("%s : ", inet_ntop(family, *pptr, str, sizeof(str)));
switch (family) {
case AF_INET:
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sin = (struct sockaddr_in*)&arpreq.arp_pa;
bzero(sin, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
memcpy(&sin->sin_addr, *pptr, sizeof(struct in_addr));
ioctl(sockfd, SIOCGARP, &arpreq);
ptr = &arpreq.arp_ha.sa_data[0];
printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5));
break;
default:
printf("unsupported address family:%d\n", family);
}
}
return 0;
}
char **my_addrs(int *addrtype)
{
struct hostent *hptr;
struct utsname myname;
if (uname(&myname) < 0) return NULL;
if ((hptr = gethostbyname(myname.nodename)) == NULL) return NULL;
*addrtype = hptr->h_addrtype;
return hptr->h_addr_list;
}
5、路由表操作
有兩種ioctl請求用來操作路由表,這兩個請求要求ioctl的第三個引數必須是一個指向rtentry結構的指標,這個結構在<net/route.h>標頭檔案中定義。這些請求一般由route程式發出,只有超級使用者才能發出這些請求
SIOCADDRT:向路由表中新增一項
SIOCDELRT:從路由表中刪除一項