1、前言
OUI是指Organizationally unique identifier (組織唯一標識符),簽發給各類組織的唯一標識符。MAC地址共有6個字節48位組成,前3個字節體現了OUI,其表明了NIC的制造組織。通常情況下,該標識符是唯一的。詳細介紹參考:http://standards.ieee.org/develop/regauth/oui/public.html。oui.txt文件中記錄世界所有網卡的制造廠商,共有18859個。文件中記錄mac的前三位與公司的對應關系。本文目地是對oui.txt文件進行解析,生產一個信息的文件,在程序中可以根據制定的mac地址,快速查找其對應的公司名稱。在此將MAC前三個字節簡稱為MAC前綴。
2、初步處理
oui.txt文件內容很有規律,根據MAC前綴由小到大記錄。但是,MAC前綴并不是連續的,中間有些間斷,但是順序是由小到大。原始文件內容格式如下所示:
OUI Organization
company_id Organization
Address
00-00-00 (hex) XEROX CORPORATION
000000 (base 16) XEROX CORPORATION
M/S 105-50C
800 PHILLIPS ROAD
WEBSTER NY 14580
UNITED STATES
00-00-01 (hex) XEROX CORPORATION
000001 (base 16) XEROX CORPORATION
ZEROX SYSTEMS INSTITUTE
M/S 105-50C 800 PHILLIPS ROAD
WEBSTER NY 14580
UNITED STATES
00-00-02 (hex) XEROX CORPORATION
000002 (base 16) XEROX CORPORATION
XEROX SYSTEMS INSTITUTE
M/S 105-50C 800 PHILLIPS ROAD
WEBSTER NY 14580
UNITED STATES
文件中網卡前綴00-00-00和000000兩種形式,為了具備一致性,可以提前像00-00-00 (hex) XEROX CORPORATION的行。linux采用cat命令提取。
命令為:cat oui.txt | grep hex > mac_hex_org.txt
生成的mac_hex_org.txt文件內容如下:
00-00-00 (hex) XEROX CORPORATION
00-00-01 (hex) XEROX CORPORATION
00-00-02 (hex) XEROX CORPORATION
00-00-03 (hex) XEROX CORPORATION
00-00-04 (hex) XEROX CORPORATION
00-00-05 (hex) XEROX CORPORATION
00-00-06 (hex) XEROX CORPORATION
更進一步抽取mac和org信息,可以對mac_hex_org.txt文件進行提前,采用一個簡單的shell腳本,提前mac列和org列,分別保存在MAC.log和ORG.log文件中。shell腳本mac_org.sh如下:
1 #!/bin/sh
2 SRC_FILE=mac_hex_org.txt
3 MAC_FILE=MAC.log
4 ORG_FILE=ORG.log
5 cat ${SRC_FILE} |grep -v "^#" | while read line;
6 do
7 echo "${line:0:8}" >> ${MAC_FILE}
8 echo "${line:18}">>${ORG_FILE}
9 done
執行mac_org.sh生產MAC.log和ORG.log文件。兩個文件的每行對應關系就是mac前綴與公司名稱的關系。文件內容如下所示:
00-00-00
00-00-01
00-00-02
00-00-03
00-00-04
00-00-05
00-00-06
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
XEROX CORPORATION
OMRON TATEISI ELECTRONICS CO.
MATRIX CORPORATION
CISCO SYSTEMS, INC.
3、生產mac-org結構文件
為了在程序快速查找,將MAC.log和ORG.log文件中對應關系轉換為一個結構體,存入mac_org.log文件中。mac前綴是唯一的,對應轉換為10進制的整數,相比字符串,查找更加方便。mac_org結構定義如下:
//mac前綴和公司名稱對應關系
typedef struct mac_org
{
uint32_t key; //mac前綴作為key
char org_name[ORG_NAME_LEN]; //公司名稱
}mac_org;
在程序中分別讀取MAC.log和ORG.log的每一行,轉換為一個mac_log結構,寫入mac_log.log文件。轉換程序如下所示:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <inttypes.h> 4 #include <string.h> 5 #include <time.h> 6 #include <errno.h> 7 #include <unistd.h> 8 9 #define MAC_PREFIX_LEN 10 //mac前綴長度 10 #define ORG_NAME_LEN 96 //公司名稱長度 11 #define MAC_LOG_FILE "MAC.log" //mac前綴文件 12 #define ORG_LOG_FILE "ORG.log" //公司名稱文件 13 #define MAC_ORG_FILE "mac2org.log" //mac前綴對應公司名稱文件 14 15 #define PRINT_ERROR_POS() do{ \ 16 printf("File: "__FILE__", Line:%d\n", __LINE__); \ 17 }while(0); 18 19 //mac前綴和公司名稱對應關系 20 typedef struct mac_org 21 { 22 uint32_t key; //mac前綴作為key 23 char org_name[ORG_NAME_LEN]; //公司名稱 24 }mac_org; 25 26 void print_mac_org(const mac_org *macorg) 27 { 28 printf("mac key:%d,org_name:%s\n",macorg->key, macorg->org_name); 29 } 30 31 //將mac前綴轉換為數字,前綴格式為:00-00-00 32 uint32_t macprefix2uint(const char *mac_prefix) 33 { 34 char mac[8] = {0}; 35 sscanf(mac_prefix, "%c%c-%c%c-%c%c",&mac[0],&mac[1],&mac[2], 36 &mac[3],&mac[4],&mac[5]); 37 return strtoul(mac,0,16); 38 } 39 //將mac前綴文件和org文件組織成mac_org結構,并將結果存入文件 40 int store_mac_org() 41 { 42 FILE *mac_fp = NULL; 43 FILE *org_fp = NULL; 44 FILE *fp = NULL; 45 char mac_buf[MAC_PREFIX_LEN] = {0}; 46 char org_buf[ORG_NAME_LEN] = {0}; 47 uint32_t mac_len; 48 uint32_t org_len; 49 mac_org tmp; 50 51 memset(&tmp, 0, sizeof(mac_org)); 52 if ((mac_fp = fopen(MAC_LOG_FILE, "r")) == NULL) 53 { 54 fprintf(stderr,"Failed open mac log file: %s,errno: %u,reason: %s\n", 55 MAC_LOG_FILE, errno, strerror(errno)); 56 PRINT_ERROR_POS(); 57 return -1; 58 } 59 if ((org_fp = fopen(ORG_LOG_FILE, "r")) == NULL) 60 { 61 fprintf(stderr,"Failed open mac log file: %s,errno: %u,reason: %s\n", 62 ORG_LOG_FILE, errno, strerror(errno)); 63 PRINT_ERROR_POS(); 64 return -1; 65 } 66 if ((fp = fopen(MAC_ORG_FILE, "wb")) == NULL) 67 { 68 fprintf(stderr,"Failed open mac log file: %s,errno: %u,reason: %s\n", 69 MAC_ORG_FILE, errno, strerror(errno)); 70 PRINT_ERROR_POS(); 71 return -1; 72 } 73 while(fgets(mac_buf, MAC_PREFIX_LEN, mac_fp) != NULL && 74 fgets(org_buf, ORG_NAME_LEN, org_fp) != NULL) 75 { 76 //去掉換行符'\n' 77 mac_len = strlen(mac_buf); 78 org_len = strlen(org_buf); 79 if (mac_buf[mac_len-1] == '\n') 80 { 81 mac_buf[mac_len-1] = 0; 82 } 83 if (org_buf[org_len-1] == '\n') 84 { 85 org_buf[org_len-1] = 0; 86 } 87 //設置記錄值 88 tmp.key = macprefix2uint(mac_buf); 89 strcpy(tmp.org_name,org_buf); 90 //將該記錄寫入文件 91 if(fwrite((void *)&tmp, sizeof(mac_org), 1, fp) == 0) 92 { 93 fprintf(stderr, "Failed to write macorg to %s,errno:%u,reason:%s\n", 94 MAC_ORG_FILE, errno, strerror(errno)); 95 PRINT_ERROR_POS(); 96 return -1; 97 } 98 } 99 fclose(mac_fp); 100 fclose(org_fp); 101 fclose(fp); 102 return 0; 103 } 104 105 //mac前綴格式是00-00-00 106 int main() 107 { 108 //判斷文件是否存在 109 if(access(MAC_ORG_FILE, F_OK) != 0) 110 { 111 if (store_mac_org() == -1) 112 { 113 fprintf(stderr, "Failed to create mac2org file.\n"); 114 return -1; 115 } 116 else 117 { 118 printf("Successed to create mac2org file.\n"); 119 } 120 } 121 return 0; 122 }
執行程序:
查看mac2org.log文件大小和內容如下:文件是二進制形式存入。
4、根據mac前綴在mac2org.log查找org
mac2org.log文件結構很明確,而且文件大小僅為1.8MB,完全可以將文件內容全部讀到內存進行查找。而且mac2org.log記錄是根據mac前綴有小到大的,即讀到內存中的buffer中,mac_org記錄是有序的,可以采用折半查找進行,以mac前綴轉換的整數為key。查找程序如下所示:
1 /**根據mac前綴(形如00-00-00)查找organzation
2 先將mac_org.log讀取到內存,然后進行折半查找
3 @auther: Anker @date:2013-12-18
4 **/
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <inttypes.h>
8 #include <string.h>
9 #include <time.h>
10 #include <errno.h>
11 #include <unistd.h>
12
13 #define MAC_PREFIX_LEN 10 //mac前綴長度
14 #define ORG_NAME_LEN 96 //公司名稱長度
15 #define MAC_TYPE_COUNT 18860 //記錄個數
16 #define MAC_ORG_FILE "mac2org.log" //mac前綴對應公司名稱文件
17
18 #define PRINT_ERROR_POS() do{ \
19 printf("File: "__FILE__", Line:%d\n", __LINE__); \
20 }while(0);
21
22 //mac前綴和公司名稱對應關系
23 typedef struct mac_org
24 {
25 uint32_t key; //mac前綴作為key
26 char org_name[ORG_NAME_LEN]; //公司名稱
27 }mac_org;
28
29 void print_mac_org(const mac_org *macorg)
30 {
31 printf("mac key:%d,org_name:%s\n",macorg->key, macorg->org_name);
32 }
33
34 //將mac前綴轉換為數字,前綴格式為:00-00-00
35 uint32_t macprefix2uint(const char *mac_prefix)
36 {
37 char mac[8] = {0};
38 sscanf(mac_prefix, "%c%c-%c%c-%c%c",&mac[0],&mac[1],&mac[2],
39 &mac[3],&mac[4],&mac[5]);
40 return strtoul(mac,0,16);
41 }
42
43 //二分查找過程
44 int32_t binary_search(mac_org *macorg, int32_t n, uint32_t key)
45 {
46 //在有序表macorg[0..n-1]中進行二分查找,成功時返回結點的位置,失敗時返回-1
47 int32_t low = 0, high = n-1, mid; //置當前查找區間上、下界的初值
48 if(macorg[low].key == key)
49 {
50 return low;
51 }
52 if(macorg[high].key == key)
53 {
54 return high;
55 }
56 while(low <= high)
57 {
58 //當前查找區間macorg[low..high]非空
59 mid = low + ((high - low) / 2);
60 //使用 (low + high) / 2 會有整數溢出的問題
61 //(問題會出現在當low + high的結果大于表達式結果類型所能表示的最大值時,
62 //這樣,產生溢出后再/2是不會產生正確結果的,而low+((high-low)/2)不存在這個問題
63 if(macorg[mid].key == key)
64 {
65 return mid; //查找成功返回
66 }
67 if(macorg[mid].key > key)
68 {
69 high = mid - 1; //繼續在macorg[low..mid-1]中查找
70 }
71 else
72 {
73 low = mid + 1; //繼續在macorg[mid+1..high]中查找
74 }
75 }
76 return -1; //當low>high時表示查找區間為空,查找失敗
77 }//BinSeareh
78
79 //給定一個mac前綴,獲取對應的公司名稱
80 int get_org_by_mac(const char *mac_prefix, mac_org **rmg)
81 {
82 mac_org buffer[MAC_TYPE_COUNT];
83 size_t read_num;
84 uint32_t key = macprefix2uint(mac_prefix);
85 int pos = -1;
86 FILE *fp;
87 if((fp = fopen(MAC_ORG_FILE, "rb")) == NULL)
88 {
89 fprintf(stderr, "Failed to open mac log file: %s,errno:%u,reason:%s\n",
90 MAC_ORG_FILE, errno, strerror(errno));
91 PRINT_ERROR_POS();
92 goto FAILED;
93 }
94 fflush(stdin);
95 read_num = fread((void *)buffer, sizeof(mac_org), MAC_TYPE_COUNT, fp);
96 if (read_num == 0 && errno != 0)
97 {
98 fprintf(stderr, "Failed to read mac log file: %s,errno:%u,reason:%s\n",
99 MAC_ORG_FILE, errno, strerror(errno));
100 PRINT_ERROR_POS();
101 goto FAILED;
102 }
103 pos = binary_search(buffer, read_num, key);
104 if (pos != -1)
105 {
106 *rmg = (mac_org *)malloc(sizeof(mac_org));
107 if (rmg == NULL)
108 {
109 fprintf(stderr, "Failed to malloc memory,errno:%u,reason:%s\n",
110 errno, strerror(errno));
111 PRINT_ERROR_POS();
112 goto FAILED;
113 }
114 memset(*rmg, 0, sizeof(mac_org));
115 memcpy(*rmg, &buffer[pos], sizeof(mac_org));
116 }
117 fclose(fp);
118 return 0;
119 FAILED:
120 if(fp)
121 {
122 fclose(fp);
123 }
124 return -1;
125 }
126
127 //mac前綴格式是00-00-00
128 int main(int argc,char **argv)
129 {
130 time_t time1,time2;
131 time(&time1);
132 mac_org *pmacorg = NULL;
133 char *mac_prefix = NULL;
134 if (argc != 2)
135 {
136 fprintf(stderr,"Paramer error,please input mac prefix.\n");
137 return -1;
138 }
139 if(access(MAC_ORG_FILE, F_OK) != 0)
140 {
141 printf("Can not found mac2org file:%s.\n", MAC_ORG_FILE);
142 return -1;
143 }
144 mac_prefix = argv[1];
145 if (get_org_by_mac(mac_prefix, &pmacorg) == -1)
146 {
147 fprintf(stderr, "Failed to search mac.\n");
148 PRINT_ERROR_POS();
149 return -1;
150 }
151 if (!pmacorg)
152 {
153 printf("Can not find the mac prefix:%s\n", mac_prefix);
154 }
155 else
156 {
157 time(&time2);
158 printf("Successed to find the mac info, cost time:%lds\n", time2 - time1);
159 print_mac_org(pmacorg);
160 free(pmacorg);
161 }
162 return 0;
163 }
測試結果如下所示:
采用折半查找,針對18860條記錄,查詢時間不足1秒,非常之快。
5、總結
剛開始拿到oui.txt文件時,看了文件的格式和規律。當時沒有檢查,以為mac前綴是連續的,如是開始第一個想到用hash做,mac前綴作為key,value是mac-key在文件中的偏移量。因為hash是唯一的,轉換為整數,不會有沖突。實現后發現生產的mac_org.log文件1.2G之大,文件中有很多空白地方,排查發現mac前綴并不是連續的,而且MAC前綴還存在重復。如下圖所示:
故不可以采用hash實現。最后還是采用將文件內容記載到內存處理。mac_log結構的占用100字節,18860條共計約1.8MB,如今內存都已GB計算,完全可以全部加載到內存進行二分查找。
6、參考網址
文章列表