解析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计算,完全可以全部加载到内存进行二分查找。

相关文章
|
16天前
|
XML JavaScript 前端开发
xml文件使用及解析
xml文件使用及解析
|
29天前
|
算法 Linux C++
【Linux系统编程】解析获取和设置文件信息与权限的Linux系统调用
【Linux系统编程】解析获取和设置文件信息与权限的Linux系统调用
29 0
|
1月前
|
安全 Java 数据库连接
jdbc解析excel文件,批量插入数据至库中
jdbc解析excel文件,批量插入数据至库中
21 0
|
1月前
|
前端开发 UED
前端解析Excel文件
前端解析Excel文件
33 0
mac上datagrip.vmoptions文件编辑错误导致DataGrip软件打不开
mac上datagrip.vmoptions文件编辑错误导致DataGrip软件打不开
|
3月前
|
JavaScript API Windows
Nodejs 文件 与 路径 相关用法实例解析
Nodejs 文件 与 路径 相关用法实例解析
78 0
|
5天前
|
XML C# 数据格式
C# 解析XML文件
C# 解析XML文件
14 1
|
1月前
|
SQL Java 数据库连接
springboot解析txt文件顺便加到数据库中(nohup文件)
springboot解析txt文件顺便加到数据库中(nohup文件)
112 1
|
1月前
|
XML Java 数据格式
使用java解析XML文件的步骤
使用java解析XML文件的步骤
10 0
|
1月前
|
存储 安全 Linux
C++文件格式深度解析:从底层结构到关键特性
C++文件格式深度解析:从底层结构到关键特性
250 3
C++文件格式深度解析:从底层结构到关键特性

推荐镜像

更多