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 -- (hex) XEROX CORPORATION
(base ) XEROX CORPORATION
M/S -50C
PHILLIPS ROAD
WEBSTER NY
UNITED STATES -- (hex) XEROX CORPORATION
(base ) XEROX CORPORATION
ZEROX SYSTEMS INSTITUTE
M/S -50C PHILLIPS ROAD
WEBSTER NY
UNITED STATES -- (hex) XEROX CORPORATION
(base ) XEROX CORPORATION
XEROX SYSTEMS INSTITUTE
M/S -50C PHILLIPS ROAD
WEBSTER NY
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文件内容如下:

 --   (hex)        XEROX CORPORATION
-- (hex) XEROX CORPORATION
-- (hex) XEROX CORPORATION
-- (hex) XEROX CORPORATION
-- (hex) XEROX CORPORATION
-- (hex) XEROX CORPORATION
-- (hex) XEROX CORPORATION

更进一步抽取mac和org信息,可以对mac_hex_org.txt文件进行提前,采用一个简单的shell脚本,提前mac列和org列,分别保存在MAC.log和ORG.log文件中。shell脚本mac_org.sh如下:

 #!/bin/sh
SRC_FILE=mac_hex_org.txt
MAC_FILE=MAC.log
ORG_FILE=ORG.log
cat ${SRC_FILE} |grep -v "^#" | while read line;
do
echo "${line:0:8}" >> ${MAC_FILE}
echo "${line:18}">>${ORG_FILE}
done

执行mac_org.sh生产MAC.log和ORG.log文件。两个文件的每行对应关系就是mac前缀与公司名称的关系。文件内容如下所示:

--
--
--
--
--
--
--
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文件。转换程序如下所示:

 #include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h> #define MAC_PREFIX_LEN 10 //mac前缀长度
#define ORG_NAME_LEN 96 //公司名称长度
#define MAC_LOG_FILE "MAC.log" //mac前缀文件
#define ORG_LOG_FILE "ORG.log" //公司名称文件
#define MAC_ORG_FILE "mac2org.log" //mac前缀对应公司名称文件 #define PRINT_ERROR_POS() do{ \
printf("File: "__FILE__", Line:%d\n", __LINE__); \
}while(); //mac前缀和公司名称对应关系
typedef struct mac_org
{
uint32_t key; //mac前缀作为key
char org_name[ORG_NAME_LEN]; //公司名称
}mac_org; void print_mac_org(const mac_org *macorg)
{
printf("mac key:%d,org_name:%s\n",macorg->key, macorg->org_name);
} //将mac前缀转换为数字,前缀格式为:00-00-00
uint32_t macprefix2uint(const char *mac_prefix)
{
char mac[] = {};
sscanf(mac_prefix, "%c%c-%c%c-%c%c",&mac[],&mac[],&mac[],
&mac[],&mac[],&mac[]);
return strtoul(mac,,);
}
//将mac前缀文件和org文件组织成mac_org结构,并将结果存入文件
int store_mac_org()
{
FILE *mac_fp = NULL;
FILE *org_fp = NULL;
FILE *fp = NULL;
char mac_buf[MAC_PREFIX_LEN] = {};
char org_buf[ORG_NAME_LEN] = {};
uint32_t mac_len;
uint32_t org_len;
mac_org tmp; memset(&tmp, , sizeof(mac_org));
if ((mac_fp = fopen(MAC_LOG_FILE, "r")) == NULL)
{
fprintf(stderr,"Failed open mac log file: %s,errno: %u,reason: %s\n",
MAC_LOG_FILE, errno, strerror(errno));
PRINT_ERROR_POS();
return -;
}
if ((org_fp = fopen(ORG_LOG_FILE, "r")) == NULL)
{
fprintf(stderr,"Failed open mac log file: %s,errno: %u,reason: %s\n",
ORG_LOG_FILE, errno, strerror(errno));
PRINT_ERROR_POS();
return -;
}
if ((fp = fopen(MAC_ORG_FILE, "wb")) == NULL)
{
fprintf(stderr,"Failed open mac log file: %s,errno: %u,reason: %s\n",
MAC_ORG_FILE, errno, strerror(errno));
PRINT_ERROR_POS();
return -;
}
while(fgets(mac_buf, MAC_PREFIX_LEN, mac_fp) != NULL &&
fgets(org_buf, ORG_NAME_LEN, org_fp) != NULL)
{
//去掉换行符'\n'
mac_len = strlen(mac_buf);
org_len = strlen(org_buf);
if (mac_buf[mac_len-] == '\n')
{
mac_buf[mac_len-] = ;
}
if (org_buf[org_len-] == '\n')
{
org_buf[org_len-] = ;
}
//设置记录值
tmp.key = macprefix2uint(mac_buf);
strcpy(tmp.org_name,org_buf);
//将该记录写入文件
if(fwrite((void *)&tmp, sizeof(mac_org), , fp) == )
{
fprintf(stderr, "Failed to write macorg to %s,errno:%u,reason:%s\n",
MAC_ORG_FILE, errno, strerror(errno));
PRINT_ERROR_POS();
return -;
}
}
fclose(mac_fp);
fclose(org_fp);
fclose(fp);
return ;
} //mac前缀格式是00-00-00
int main()
{
//判断文件是否存在
if(access(MAC_ORG_FILE, F_OK) != )
{
if (store_mac_org() == -)
{
fprintf(stderr, "Failed to create mac2org file.\n");
return -;
}
else
{
printf("Successed to create mac2org file.\n");
}
}
return ;
}

执行程序:

查看mac2org.log文件大小和内容如下:文件是二进制形式存入。

4、根据mac前缀在mac2org.log查找org

  mac2org.log文件结构很明确,而且文件大小仅为1.8MB,完全可以将文件内容全部读到内存进行查找。而且mac2org.log记录是根据mac前缀有小到大的,即读到内存中的buffer中,mac_org记录是有序的,可以采用折半查找进行,以mac前缀转换的整数为key。查找程序如下所示:

 /**根据mac前缀(形如00-00-00)查找organzation
先将mac_org.log读取到内存,然后进行折半查找
@auther: Anker @date:2013-12-18
**/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h> #define MAC_PREFIX_LEN 10 //mac前缀长度
#define ORG_NAME_LEN 96 //公司名称长度
#define MAC_TYPE_COUNT 18860 //记录个数
#define MAC_ORG_FILE "mac2org.log" //mac前缀对应公司名称文件 #define PRINT_ERROR_POS() do{ \
printf("File: "__FILE__", Line:%d\n", __LINE__); \
}while(); //mac前缀和公司名称对应关系
typedef struct mac_org
{
uint32_t key; //mac前缀作为key
char org_name[ORG_NAME_LEN]; //公司名称
}mac_org; void print_mac_org(const mac_org *macorg)
{
printf("mac key:%d,org_name:%s\n",macorg->key, macorg->org_name);
} //将mac前缀转换为数字,前缀格式为:00-00-00
uint32_t macprefix2uint(const char *mac_prefix)
{
char mac[] = {};
sscanf(mac_prefix, "%c%c-%c%c-%c%c",&mac[],&mac[],&mac[],
&mac[],&mac[],&mac[]);
return strtoul(mac,,);
} //二分查找过程
int32_t binary_search(mac_org *macorg, int32_t n, uint32_t key)
{
//在有序表macorg[0..n-1]中进行二分查找,成功时返回结点的位置,失败时返回-1
int32_t low = , high = n-, mid; //置当前查找区间上、下界的初值
if(macorg[low].key == key)
{
   return low;
}
if(macorg[high].key == key)
{
  return high;
}
while(low <= high)
{
  //当前查找区间macorg[low..high]非空
  mid = low + ((high - low) / );
  //使用 (low + high) / 2 会有整数溢出的问题
   //(问题会出现在当low + high的结果大于表达式结果类型所能表示的最大值时,
   //这样,产生溢出后再/2是不会产生正确结果的,而low+((high-low)/2)不存在这个问题
  if(macorg[mid].key == key)
  {
   return mid; //查找成功返回
   }
  if(macorg[mid].key > key)
  {
   high = mid - ; //继续在macorg[low..mid-1]中查找
  }
  else
  {
   low = mid + ; //继续在macorg[mid+1..high]中查找
  }
}
return -; //当low>high时表示查找区间为空,查找失败
}//BinSeareh //给定一个mac前缀,获取对应的公司名称
int get_org_by_mac(const char *mac_prefix, mac_org **rmg)
{
mac_org buffer[MAC_TYPE_COUNT];
size_t read_num;
uint32_t key = macprefix2uint(mac_prefix);
int pos = -;
FILE *fp;
if((fp = fopen(MAC_ORG_FILE, "rb")) == NULL)
{
fprintf(stderr, "Failed to open mac log file: %s,errno:%u,reason:%s\n",
MAC_ORG_FILE, errno, strerror(errno));
PRINT_ERROR_POS();
goto FAILED;
}
fflush(stdin);
read_num = fread((void *)buffer, sizeof(mac_org), MAC_TYPE_COUNT, fp);
if (read_num == && errno != )
{
fprintf(stderr, "Failed to read mac log file: %s,errno:%u,reason:%s\n",
MAC_ORG_FILE, errno, strerror(errno));
PRINT_ERROR_POS();
goto FAILED;
}
pos = binary_search(buffer, read_num, key);
if (pos != -)
{
*rmg = (mac_org *)malloc(sizeof(mac_org));
if (rmg == NULL)
{
fprintf(stderr, "Failed to malloc memory,errno:%u,reason:%s\n",
errno, strerror(errno));
PRINT_ERROR_POS();
goto FAILED;
}
memset(*rmg, , sizeof(mac_org));
memcpy(*rmg, &buffer[pos], sizeof(mac_org));
}
fclose(fp);
return ;
FAILED:
if(fp)
{
  fclose(fp);
}
return -;
} //mac前缀格式是00-00-00
int main(int argc,char **argv)
{
time_t time1,time2;
time(&time1);
mac_org *pmacorg = NULL;
char *mac_prefix = NULL;
if (argc != )
{
   fprintf(stderr,"Paramer error,please input mac prefix.\n");
  return -;
}
if(access(MAC_ORG_FILE, F_OK) != )
{
  printf("Can not found mac2org file:%s.\n", MAC_ORG_FILE);
   return -;
}
mac_prefix = argv[];
if (get_org_by_mac(mac_prefix, &pmacorg) == -)
{
   fprintf(stderr, "Failed to search mac.\n");
  PRINT_ERROR_POS();
  return -;
}
if (!pmacorg)
{
  printf("Can not find the mac prefix:%s\n", mac_prefix);
}
else
{
  time(&time2);
   printf("Successed to find the mac info, cost time:%lds\n", time2 - time1);
  print_mac_org(pmacorg);
  free(pmacorg);
}
return ;
}

测试结果如下所示:

采用折半查找,针对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

解析oui.txt文件,通过MAC前缀获取Organization的更多相关文章

  1. 用 jupyter notebook 打开 oui.txt 文件出现的问题及解决方案

    问题背景:下载了2018 IEEE 最新的 oui.txt 文件.里面包含了 设备 MAC 地址的前六位对应的厂商.要做的工作是,将海量设备的 MAC 地址与 oui.txt 文件的信息比对,统计出 ...

  2. Python 网络爬虫 010 (高级功能) 解析 robots.txt 文件

    解析 robots.txt 文件 使用的系统:Windows 10 64位 Python 语言版本:Python 2.7.10 V 使用的编程 Python 的集成开发环境:PyCharm 2016 ...

  3. Windows中的txt文件到Mac打开是乱码 解决办法

    在Mac下打开“文本编辑”程序之后,选择菜单“文本编辑” -> “偏好设置”.2)在“偏好设置”中选择第二个标签页“打开和存储”,选择“纯文本文件编码”中的“打开文件”和“存储文件”修改成为“中 ...

  4. 完美解决 .txt文件在Mac上不能打开的问题

  5. C# 之三类文件的读写( .XML,.INI 和 .TXT 文件)

    笔记之用,关于三类.xml, .ini, .txt 文件的 C# 读写,请多多指教! 1,第一类:.xml 文件的读写 先贴上xml文件,下面对这个文件进行操作: <?xml version=& ...

  6. 读取txt文件赋值到DataGridView中

    先查看txt是每条信息之间是通过什么分割,我是通过换行符(\n)分割的, 然后再看每一条信息中字段是通过什么分割,我的字段是通过 tab键(\t)分割. 第一步 先获取到txt文件的路径: //获取绝 ...

  7. C++文件处理(一):读/写txt文件

    C++文件处理与C语言不同,C++文件处理使用的是:流(stream) C++头文件fstream定义了三个类型来支持文件IO

  8. c++--------获取某个路径下所有文件的文件名,读写TXT文件到新的文件

    好久没写io操作了,手生了好多,为了防止自己老年痴呆,最简单实用的c++代码也push上来吧, 环境:mac,xcode(注意mac环境下Windows的函数不能用) 功能:打开一个文件目录,把所有文 ...

  9. python获取知乎日报另存为txt文件

    前言 拿来练手的,比较简单(且有bug),欢迎交流~ 功能介绍 抓取当日的知乎日报的内容,并将每篇博文另存为一个txt文件,集中放在一个文件夹下,文件夹名字为当日时间. 使用的库 re,Beautif ...

随机推荐

  1. 无线端安全登录与鉴权一之Kerberos

    无线端登录与鉴权是安全登录以及保证用户数据安全的第一步,也是最重要的一步.之前做过一个安全登录与鉴权的方案,借这个机会,系统的思考一下,与大家交流交流 先介绍一下TX系统使用的Kerberos方案,参 ...

  2. python实例[判断操作系统类型]

    参考文献:http://bbs.chinaunix.net/thread-1848086-1-1.html 经常地我们需要编写跨平台的脚本,但是由于不同的平台的差异性,我们不得不获得当前所工作的平台( ...

  3. JSONPATH使用方法

    如下的json: { "store": { "book": [ { "category": "reference", & ...

  4. html5模拟平抛运动

    <html> <head> <meta charset=utf-8> <title>html5炮弹</title> <script&g ...

  5. ShellExecuteA URLDownloadToFileA

    ExeFile(Handle,nil,PChar('cmd.exe'),PChar('/c C:\123.exe'),nil,SW_SHOWNORMAL); c_md5 := 'cmd.exe /c ...

  6. Win7电脑开启局域网连接和共享过程中出现的"您可能没有权限使用网络资源"的解决办法

    Win7电脑开启局域网连接和共享 http://bbs.ithome.com/thread-334567-1-1.html http://jingyan.baidu.com/article/6dad5 ...

  7. WebLogic使用总结(二)——WebLogic卸载

    一.WebLogic 12c的卸载 WebLogic的卸载是非常容易的,找到WebLogic的卸载程序,如下图所示: 启动卸载程序,如下图所示:

  8. .Net Discovery系列之十一-深入理解平台机制与性能影响 (中)

    上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马—JIT.   有关JIT的机制分析   ● 机制分析   以C#为例, ...

  9. JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数

    本篇体验JavaScript事件的基本面,包括: ■ 事件必须在页面元素加载之后起效■ 点击事件的一个简单例子■ 为元素注册多个点击事件■ 获取事件参数 ■ 跨浏览器事件处理 □ 事件必须在页面元素加 ...

  10. iPhone开发--正则表达式获取字符串中的内容

    缘起: 想获取字符串中指定的字符,考虑用正则表达式,遂写了如下的代码: NSString *htmlStr = @"oauth_token=1a1de4ed4fca40599c5e5cfe0 ...