解析oui.txt文件,通過MAC前綴獲取Organization


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、參考網址

http://my.oschina.net/duangr/blog/183789


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM