本文共 10155 字,大约阅读时间需要 33 分钟。
1、前言
OUI是指Organizationally unique identifier (组织唯一标识符),签发给各类组织的唯一标识符。MAC地址共有6个字节48位组成,前3个字节体现了OUI,其表明了NIC的制造组织。通常情况下,该标识符是唯一的。详细介绍参考:。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/sh2 SRC_FILE=mac_hex_org.txt3 MAC_FILE=MAC.log4 ORG_FILE=ORG.log5 cat ${SRC_FILE} |grep -v "^#" | while read line;6 do7 echo "${line:0:8}" >> ${MAC_FILE}8 echo "${line:18}">>${ORG_FILE}9 done
执行mac_org.sh生产MAC.log和ORG.log文件。两个文件的每行对应关系就是mac前缀与公司名称的关系。文件内容如下所示:
00-00-0000-00-0100-00-0200-00-0300-00-0400-00-0500-00-06
XEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONXEROX CORPORATIONOMRON TATEISI ELECTRONICS CO.MATRIX CORPORATIONCISCO 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 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 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-00106 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 else117 {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 #include6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 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-00128 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 else156 {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计算,完全可以全部加载到内存进行二分查找。
转载地址:http://ddmia.baihongyu.com/