***************************************************************************************************************************
作者:EasyWave 時間:2013.01.19
類別:Linux 應用執行個體源碼 聲明:轉載,請保留連結
注意:如有錯誤,歡迎指正。這些是我學習的日誌文章......
***************************************************************************************************************************
這段時間,因為項目的需要,瞭解了一下USBtoNet的驅動,同時採用IOCTL來設定MAC的地址,檢測網卡的串連狀態等等,因此,就從網路上瞭解了一下關於網路編程方面的知識.一般來說:Linux網路程式與核心互動的方法是通過ioctl來實現的,ioctl與網路通訊協定棧進行互動,可得到網路介面的資訊,網卡裝置的映射屬性和配置網路介面.並且還能夠查看,修改,刪除ARP快取的資訊,所以,我們先來瞭解一下ioctl函數的具體實現.
函數形式:
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
類別 |
Request |
說明 |
資料類型 |
套 接 口 |
SIOCATMARK SIOCSPGRP SIOCGPGRP |
是否位於帶外標記 設定套介面的進程ID或進程組ID 擷取套介面的進程ID或進程組ID |
int int int |
文 件 |
FIONBIN FIOASYNC FIONREAD FIOSETOWN FIOGETOWN |
設定/清除非阻塞I/O標誌 設定/清除訊號驅動非同步I/O標誌 擷取接收緩衝區中的位元組數 設定檔案的進程ID或進程組ID 擷取檔案的進程ID或進程組ID |
int int int int int |
接 口 |
SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx |
擷取所有介面的清單 設定介面地址 擷取介面地址 設定介面標誌 擷取介面標誌 設定點到點地址 擷取點到點地址 擷取廣播位址 設定廣播位址 擷取子網路遮罩 設定子網路遮罩 擷取介面的測度 設定介面的測度 擷取介面MTU (還有很多取決於系統的實現) |
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 struct ifreq |
ARP |
SIOCSARP SIOCGARP SIOCDARP |
建立/修改ARP表項 擷取ARP表項 刪除ARP表項 |
struct arpreq struct arpreq struct arpreq |
路 由 |
SIOCADDRT SIOCDELRT |
增加路徑 刪除路徑 |
struct rtentry struct rtentry |
|
|
|
|
相關資料結構:
1):網路介面請求結構ifreq
struct ifreq { #define IFHWADDRLEN 6 //6個位元組的硬體地址,即MAC union { char ifrn_name[IFNAMESIZ]; //網路介面名稱 }ifr_ifrn; union { struct sockaddr ifru_addr; //本地IP地址 struct sockaddr ifru_dstaddr;//目標IP地址 struct sockaddr ifru_broadaddr;//廣播IP地址 struct sockaddr ifru_netmask;//本地子網路遮罩地址 struct sockaddr ifru_hwaddr;//本地MAC地址 short ifru_flags;//網路介面標記 int ifru_ivalue;//不同的請求含義不同 struct ifmap ifru_map;//網卡地址映射 int ifru_mtu;//傳輸單元最大值 char ifru_slave[IFNAMSIZ];//預留位置 char ifru_newname[IFNAMSIZE];//新名稱 void __user* ifru_data;//使用者資料 struct if_settings ifru_settings;//裝置通訊協定設定 }ifr_ifru; } #define ifr_name ifr_ifrn.ifrn_name;//介面名稱 #define ifr_hwaddr ifr_ifru.ifru_hwaddr;//MAC #define ifr_addr ifr_ifru.ifru_addr;//本地IP #define ifr_dstaddr ifr_ifru.dstaddr;//目標IP #define ifr_broadaddr ifr_ifru.broadaddr;//廣播IP #define ifr_netmask ifr_ifru.ifru_netmask;//子網路遮罩 #define ifr_flags ifr_ifru.ifru_flags;//標誌 #define ifr_metric ifr_ifru.ifru_ivalue;//介面側度 #define ifr_mtu ifr_ifru.ifru_mtu;//傳輸單元最大值 #define ifr_map ifr_ifru.ifru_map;//裝置地址映射 #define ifr_slave ifr_ifru.ifru_slave;//副裝置 #define ifr_data ifr_ifru.ifru_data;//介面使用 #define ifr_ifrindex ifr_ifru.ifru_ivalue;//網路介面序號 #define ifr_qlen ifr_ifru.ifru_ivalue;//傳輸單元長度 #define ifr_newname ifr_ifru.ifru_newname;//新名稱 #define ifr_seeting ifr_ifru.ifru_settings;//裝置通訊協定設定
2):網卡裝置屬性ifmap
struct ifmap { //網卡裝置的映射屬性 unsigned long mem_start;//開始地址 unsigned long mem_end;//結束位址 unsigned short base_addr;//基地址 unsigned char irq;//中斷號 unsigned char dma;//DMA unsigned char port;//連接埠 }
3):網路設定介面ifconf
struct ifconf { //網路設定結構體是一種緩衝區 int ifc_len;//緩衝區ifr_buf的大小 union { char__user *ifcu_buf; //繪沖區指標 struct ifreq__user* ifcu_req;//指向ifreq指標 }ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf;//緩衝區地址 #define ifc_req ifc_ifcu.ifcu_req;//ifc_req地址
4):ARP快取操作arpreq
struct arpreq{ struct sockaddr arp_pa;//協議地址 struct sockaddr arp_ha;//硬體地址 int arp_flags;//標記 struct sockaddr arp_netmask;//協議地址的子網路遮罩 char arp_dev[16];//查詢網路介面的名稱 }
ARP快取操作,包含IP地址和硬體地址的映射表, 操作ARP快取的命令字 SIOCDARP,SIOCGARP,SIOCSARP分別是刪除ARP快取的一條記錄,獲得ARP快取的一條記錄和修改ARP快取的一條記錄
相關例子[以下為網路摘錄:已經上機驗證過]:
#include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <fcntl.h> #include <net/if.h> int main(int argc, char*argv[]) { int s,sv6; int err; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket error"); return -1; } struct ifreq ifr; ifr.ifr_ifindex = 2; //獲得第2個網路介面的名稱 err = ioctl(s, SIOCGIFNAME, &ifr); if (err) { perror("index error"); } else { printf("the %dst interface is:%s\n", ifr.ifr_ifindex, ifr.ifr_name); } memcpy(ifr.ifr_name, "eth0", 5); err = ioctl(s, SIOCGIFFLAGS, &ifr); if (!err) { printf("SIOCGIFFLAGS:%d\n", ifr.ifr_flags); } err = ioctl(s, SIOCGIFMTU, &ifr); if (!err) { printf("SIOCGIFMTU:%d\n", ifr.ifr_mtu); } err = ioctl(s, SIOCGIFHWADDR, &ifr); if (!err) { unsigned char* hw = ifr.ifr_hwaddr.sa_data; printf("SIOCGIFHWADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]); } err = ioctl(s, SIOCGIFMAP, &ifr); if (!err) { printf( "SIOCGIFMAP,mem_start:%d,mem_end:%d,base_addr:%d,ifr_map:%d,dma:%d,port:%d\n", ifr.ifr_map.mem_start, ifr.ifr_map.mem_end, ifr.ifr_map.base_addr, ifr.ifr_map.irq, ifr.ifr_map.dma, ifr.ifr_map.port); } err = ioctl(s, SIOCGIFINDEX, &ifr); if (!err) { printf("SIOCGIFINDEX:%d\n", ifr.ifr_ifindex); } err = ioctl(s, SIOCGIFTXQLEN, &ifr); if (!err) { printf("SIOCGIFTXQLEN:%d\n", ifr.ifr_qlen); } struct sockaddr_in *sin = (struct sockaddr_in*) &ifr.ifr_addr; //儲存的是二進位IP char ip[16]; //字元數組,存放字串 memset(ip, 0, 16); err = ioctl(s, SIOCGIFADDR, &ifr); if (!err) { inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16); //轉換的字串儲存到ip數組中,第二個參數是要轉換的二進位IP指標,第三個參數是轉換完成存放IP的緩衝區,最後一個參數是緩衝區的長度 printf("SIOCGIFADDR:%s\n", ip); } err = ioctl(s, SIOCGIFDSTADDR, &ifr); if (!err) { inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16); printf("SIOCGIFDSTADDR:%s\n", ip); } err = ioctl(s, SIOCGIFNETMASK, &ifr); if (!err) { inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16); printf("SIOCGIFNETMASK:%s\n", ip); } memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, "eth0", 5); ioctl(s, SIOCGIFBRDADDR, &ifr); struct sockaddr_in *broadcast = (struct sockaddr_in*) &ifr.ifr_broadaddr; inet_ntop(AF_INET, &broadcast->sin_addr.s_addr, ip, 16); //inet_ntop將二進位IP轉換成點分十進位的字串 printf("BROADCAST IP:%s\n", ip); close(s); }
執行個體二:[修改代碼後,已經上機驗證過] MAC地址的設定和擷取
#include <stdio.h>#include <string.h>#include <errno.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <net/if.h>typedef unsigned char u8#define LOGD(...) do {printf(__VA_ARGS__); printf("\n");} while(0)int set_mac(u8* addr, int len);int get_mac(u8* addr, int len);int main(int argc, char*argv[]){ int ret = 0; u8 addr[6] = {0x00, 0x00, 0x00, 0x61, 0x20, 0x58}; ret = set_mac(addr, 6); if (ret < 0) { LOGD("set_mac() error"); return 0; } LOGD("set_mac() done"); ret = get_mac(addr, 6); if (ret < 0) { LOGD("get_mac() error"); return 0; } LOGD("get_mac() done"); char buf[32] = {0}; snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); LOGD("%s", buf); return 0;}int set_mac(u8* addr, int len){ int s; int ret; struct ifreq ifr; if (len < 6) { LOGD("set_mac(), invalid length"); return -1; } s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { LOGD("socket() error: %s", strerror(errno)); return -1; } strcpy(ifr.ifr_ifrn.ifrn_name, "eth0"); ifr.ifr_ifru.ifru_hwaddr.sa_family = 1; memcpy(ifr.ifr_ifru.ifru_hwaddr.sa_data, addr, 6); ret = ioctl(s, SIOCSIFHWADDR, &ifr); if (ret != 0) { LOGD("ioctl(SIOCSIFHWADDR) error: %d(%s)", errno, strerror(errno)); return -1; } return 0;}int get_mac(u8* addr, int len){ int s; int ret; struct ifreq ifr; if (len < 6) { LOGD("get_mac(), invalid length"); return -1; } s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { LOGD("socket() error: %s", strerror(errno)); return -1; } strcpy(ifr.ifr_ifrn.ifrn_name, "eth0"); ret = ioctl(s, SIOCGIFHWADDR, &ifr); if (ret != 0) { LOGD("ioctl(SIOCSIFHWADDR) error: %s", strerror(errno)); return -1; } memcpy(addr, ifr.ifr_ifru.ifru_hwaddr.sa_data, 6); return 0;}
執行個體三:網卡串連狀態[修改後,已經上機驗證]
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <errno.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/socket.h>#include <linux/if.h>typedef unsigned short u16;typedef unsigned int u32;typedef unsigned char u8;typedef unsigned long longu64#include <linux/sockios.h>#include <linux/ethtool.h>int get_netlink_status(const char *if_name);int main(int argc, char* argv[]){ if(argc != 2) { fprintf(stderr, "usage: %s <ethname>.\n", argv[0]); return -1; } if(getuid() != 0) { fprintf(stderr, "Netlink Status Check Need Root User.\n"); return 1; } printf("Net link status: %s.\n", get_netlink_status(argv[1])==1?"up":"down"); return 0;}// if_name like "ra0", "eth0". Notice: call this function// return value:// -1 -- error , details can check errno// 1 -- interface link up// 0 -- interface link down.int get_netlink_status(const char *if_name){ int skfd; struct ifreq ifr; struct ethtool_value edata; edata.cmd = ETHTOOL_GLINK; edata.data = 0; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name) - 1); ifr.ifr_data = (char *) &edata; if (( skfd = socket( AF_INET, SOCK_DGRAM, 0 )) == 0) return -1; if(ioctl( skfd, SIOCETHTOOL, &ifr ) == -1) { close(skfd); return -1; } close(skfd); return edata.data;}
參考了網路上前人寫過的網路編程方面的代碼,通過自己動手驗證調試OK之後,就放到部落格中來了,感謝網路上那些共用知識的人。。。。