个人觉得的有用的文档,贴出来和大家分享

linux网卡MAC地址 引起的调研

上一篇 / 下一篇  2008-07-09 18:25:20 / 个人分类:linux

    方法一:

    #include  <stdio.h>  

      #include   <sys/types.h>  

      #include   <sys/socket.h>  

      #include   <sys/ioctl.h>  

      #include   <netinet/in.h>  

      #include   <net/if.h>  

      #include   <net/if_arp.h>  

      #include   <arpa/inet.h>  

      #include   <errno.h>  

       

      #define   ETH_NAME "eth0"  

       

      int   main()  

      {  

      int   sock;  

      struct   sockaddr_in   sin;  

      struct   sockaddr   sa;  

      struct   ifreq   ifr;  

      unsigned   char   mac[6];  

       

      sock   =   socket(AF_INET,   SOCK_DGRAM,   0);  

      if   (sock   ==   -1)  

      {  

      perror("socket");  

      return   -1;  

      }  

       

      strncpy(ifr.ifr_name,   ETH_NAME,   IFNAMSIZ);  

      ifr.ifr_name[IFNAMSIZ   -   1]   =   0;  

       

      memset(mac,   0,   sizeof(mac));  

      if   (ioctl(sock,   SIOCGIFHWADDR,   &ifr)   <   0)  

      {  

      perror("ioctl");  

      return   -1;  

      }  

       

      memcpy(&sa,   &ifr.ifr_addr,   sizeof(sin));  

      memcpy(mac,   sa.sa_data,   sizeof(mac));  

      fprintf(stdout,   "%s   mac:   %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",   ETH_NAME,   mac[0],   mac[1],   mac[2],   mac[3],   mac[4],   mac[5]);  

      return   0;  

      }  

       

     

    文档 <http://topic.csdn.net/t/20030123/14/1386573.html>

     

    需要建立一个socket字,来获得。

    删除网卡驱动重新加载后,获得网卡的MAC地址0000000000

    网卡启动后,获得网卡地址成功。

    这部分的不是很完美,需要进行深入的查看linux网卡驱动。

     

    linux网卡驱动

    看了下linux的网卡驱动。

    linux驱动分为3种:1,字符设备;2,块设备;3网络设备

    网络设备和其他两种设备的区别:

    (1)、网络接口不存在于 Linux 的文件系统中,而是在核心中用一个 device数据结构表示的。

    (2)、网络接口是在系统初始化时实时生成的,对于核心支持的但不存在的物理网络设备,将不可能有与之相对应的 device 结构


    重要数据结构——struct device

     

    /* from  include/linux/netdevice.h */

    struct device  

    {

    (1)、属性

      char   *name;

    设备的名字。如果第一字符为 NULL(即’\0’),register_netdev (drivers/net/net_init.c)将

    会赋给它一个 n 最小的可用网络设备名 ethn。

     

        unsigned long   rmem_end;   /* shmem "recv" end */

        unsigned long   rmem_start;   /* shmem "recv" start */

             unsigned long   mem_end;   /* shared mem end */

             unsigned long   mem_start;   /* shared mem start */

    这些域段标识被设备使用的共享内存的首地址及尾地址。如果设备用来接收和发送的内

    存块不同,则 mem 域段用来标识发送的内存位置,rmem 用来标识接收的内存位置。

    mem_start 和 mem_end 可在系统启动时用内核的命令行指定,用 ifconfig 可以查看它们

    的值。rmem 域段从来不被驱动程序以外的程序所引用。

     

       unsigned long   base_addr;   /* device I/O address */

     unsigned char irq;  /* device IRQ number */

    I/O基地址和中断号。它们都是在设备检测期间被赋值的,但也可以在系统启动时指定

    传入(如传给 LILO)。ifconfig 命令可显示及修改他们的当前值。

     

       volatile unsigned char  start;   /* start an operation */

       volatile unsigned char  interrupt;  /* interrupt arrived */

    这是两个二值的低层状态标志。通常在设备打开时置 start 标志,在设备关闭时清 start

    标志。当 interrupt 置位时,表示有一个中断已到达且正在进行中断服务程序理。

     

       unsigned long   tbusy;   /* transmitter busy must be long for bitops */

    标识“发送忙”。在驱动程序不能接受一个新的需传输的包时,该域段应该为非零。 

     

     struct device   *next;

    指向下一个网络设备,用于维护链表。

     

     unsigned char   if_port;

    记录哪个硬件 I/O端口正在被接口所用,如 BNC, AUI, TP等(drivers/net/de4x5.h)。 

     unsigned char   dma;

    设备用的 DMA通道。

    一些设备可能需要以上两个域段,但非必需的。

     

       unsigned long   trans_start; /* Time (in jiffies) of last Tx */

    上次传输的时间点(in jiffies)

       unsigned long   last_rx; /* Time of last Rx   */

    上次接收的时间点(in jiffies)。如trans_start 可用来帮助内核检测数据传输的死锁

    (lockup)。

     

    unsigned short   flags; /* interface flags (a la BSD) */

    该域描述了网络设备的能力和特性。它包括以下 flags:(include/linux/if.h)

    IFF_UP 

    表示接口在运行中。当接口被激活时,内核将置该标志位。

    IFF_BROADCAST 

    表示设备中的广播地址时有效的。以太网支持广播。

    IFF_DEBUG 

    调试模式,表示设备调试打开。当想控制 printk 及其他一些基于调试目的的信息显

    示时,可利用这个标志位。虽然当前没有正式的驱动程序使用它,但它可以在程序

    中通过 ioctl来设置从而使用它。

    IFF_LOOPBACK 

    表示这是一个回送(loopback)设备,回送接口应该置该标志位。核心是通过检查

    此标志位来判断设备是否是回送设备的,而不是看设备的名字是否是 lo。

    IFF_POINTTOPOINT 

    表示这是一个点对点链接(SLIP and PPP),点对点接口必须置该标志位。Ifconfig

    也可以置此标志位及清除它。若置上该标志位,则 dev->dstaddr应也相应的置为链

    接对方的地址。

    IFF_MASTER   /* master of a load balancer   */

    IFF_SLAVE   /* slave of a load balancer */

    此两个标志位在装入平等化中要用到。

    IFF_NOARP 

    表示不支持ARP协议。通常的网络接口能传输ARP包,如果想让接口不执行ARP,

    可置上该标志位。如点对点接口不需要运行 ARP。

    IFF_PROMISC 

    全局接受模式。在该模式下,设备将接受所有的包,而不关这些包是发给谁的。在

    缺省情况下,以太网接口会使用硬件过滤,以保证只接受广播包及发给本网络接口

    的包。Sniff 的原理就是通过设置网络接口为全局接受模式,接受所有到达本接口

    媒介的包,来“偷听”本子网的“秘密”。

    IFF_MULTICAST 

    能接收多点传送的 IP包,具有多点传输的能力。ether_setup缺省是置该标志位的,

    故若不想支持多点传送,必须在初始化时清除该标志位。

    IFF_ALLMULTI 

    接收所有多点传送的 IP包。

    IFF_NOTRAILERS /*无网络 TRAILER*/

    IFF_RUNNING  /*资源被分配*/

    此标志在 Linux 中没什么用,只是为了与 BSD兼容。

    unsigned short   family; /* address family ID (AF_INET) */

    该域段标识本设备支持的协议地址簇。大部分为 AF_INET(英特网 IP 协议),接口通

    常不需要用这个域段或赋值给它。

     

    unsigned short   metric; /* routing metric (not used) */

     unsigned short   mtu; 

    不包括数据链路层帧首帧尾的最大传输单位(Maximum Transfer Unit)。网络层在包传

    输时要用到。对以太网而言,该域段为 1500,不包括 MAC帧的帧首和帧尾(MAC帧

    格式稍后所示)。

     

     unsigned short type; /* interface hardware type */

    接口的硬件类型,描述了与该网络接口绑在一起的媒介类型。Linux 网络设备支持

    许多不同种类的媒介,如以太网,X.25,令牌环,SLIP,PPP,Apple Localtalk 等。ARP

    在判定接口支持哪种类型的物理地址时要用到该域段。若是以太网接口,则在

    ether_setup中将之设为 ARPHRD_ETHER(Ethernet 10Mbps)。

     

       unsigned short   hard_header_len; /* hardware hdr length */

    在被传送的包中 IP头之前的字节数。对于以太网接口,该域段为 14(ETH_HLEN,

    include\linux\if_ether.h),这个值可由 MAC 帧的格式得出:

    MAC 帧格式:

    目的地址(6字节)+ 源地址(6 字节)+ 数据长度(2 字节)+ 数据(46~~1500)+FCS

     

     void   *priv; /* pointer to private data */

    该指针指向私有数据,通常该数据结构中包括 struct enet_statistics。类似于 struct file的

    private_data 指针,但 priv 指针是在设备初始化时被分配内存空间的(而不是在设备打

    开时),因为该指针指向的内容包括设备接口的统计数据,而这些数据即使在接口卸下

    (down)时也应可以得到的,如用户通过 ifconfig 查看。

     

       unsigned char   pad;      /* make dev_addr aligned to 8 bytes */

       unsigned char   broadcast[MAX_ADDR_LEN]; /* hw bcast add */

    广播地址由六个 0xff 构成,即表示 255.255.255.255。

    memset(dev->broadcast,0xFF, ETH_ALEN); (drivers/net/net_init.c)

     

       unsigned char   dev_addr[MAX_ADDR_LEN]; /* hw address */

    设备的物理地址。当包传送给驱动程序传输时,要用物理地址来产生正确的帧首。

     

       unsigned char   addr_len;  /* hardware address length */

    物理地址的长度。以太网网卡的物理地址为 6字节(ETH_ALEN)。

     

       unsigned long   pa_addr;   /* protocol address   */

       unsigned long   pa_brdaddr; /* protocol broadcast addr */

       unsigned long   pa_mask;  /* protocol netmask  */

    该三个域段分别描述接口的协议地址、协议广播地址和协议的网络掩码。若 dev->family

    为 AF_INET,则它们即为 IP地址。这些域段可用 ifconfig 赋值。

     

     unsigned short pa_alen; /* protocol address length */

    协议地址的长度。AF_INET 的为 4。

     

       unsigned long   pa_dstaddr; /* protocol P-P other side addr */

    点对点协议接口(如 SLIP、PPP)用这个域记录连接另一边的 IP值。

     

     struct dev_mc_list *mc_list;  /* Multicast mac addresses */

       int       mc_count; /* Number of installed mcasts*/

     struct ip_mc_list  *ip_mc_list; /* IP multicast filter chain   */

    这三个域段用于处理多点传输。其中 mc_count表示 mc_list中的项目数。

     

       __u32       tx_queue_len; /* Max frames per queue allowed */

    一个设备的传输队列能容纳的最大的帧数。对以太网,缺省为 100;而 plip 则为节省系

    统资源,仅设为 10。

        

       /* For load balancing driver pair support */

       unsigned long   pkt_queue; /* Packets queued */

       struct device   *slave;   /* Slave device */

       struct net_alias_info *alias_info; /* main dev alias info */

     struct net_alias   *my_alias; /* alias devs */

      

     struct sk_buff_head buffs[DEV_NUMBUFFS];

    指向网络接口缓冲区的指针。

     

    /* 基本操作 */

       int (*init) (struct device *dev);   /* Called only once. */

            int (*open) (struct device *dev);

            int (*stop) (struct device *dev);

           int (*hard_start_xmit) (struct sk_buff *skb,   struct device *dev);

           

          int (*hard_header) (struct sk_buff *skb,   struct device *dev,   unsigned short type,  

                      void *daddr,   void *saddr, unsigned len);

           

           int (*rebuild_header)(void *eth, struct device *dev,  unsigned long raddr, struct sk_buff

    *skb);

             

    1、“模块初始化模式”的分析

    (a) 、概述

    insmod命令将调用相应模块的init_module() ,装载模块。init_module函数在初始化dev->init函数指针后,将调用register_netdev()在系统登记该设备。若登记成功,则模块装载成功,否则返回出错信息。register_netdev首先检查设备名是否已确定,若没赋值则给它一个缺省的值ethN,N为最小的可用以太网设备号 注;然后,网络设备自己init_function,即刚在init_module中赋值的dev->init,将被调用,用来实现对网络接口的实际的初始化工作。若初始化成功,则将该网络接口加到网络设备管理表dev_base的尾部。整个函数调用关系图如下所示。下面我们以用得最广泛以太网卡之一——NE2000 兼容网卡为例子进行分析。NE2000网卡的主要驱动程序在文件drivers/net/ne.c中。

    (b)、函数调用关系

    系统转入核心后,start_kernel 将会创建一个 init 进程,该 init 进程则会通过系统调用 sys_steup 进行所有尚初始化的设备(有一些设备如内存、PCI 等系统已先于此进行了初始化)。device_setup不仅要初始化内核支持的字符设备、块设备,也调用 net_dev_init 初始化所有内核支持的且实际存在的网络设备。net_dev_init 会对每个内核支持的网络设备调用该设备的 init_functions 进行具体的物理设备的初始化工作。整个函数调用关系图如下:

     

    (十)总结——写网络设备驱动程序

    至此我们知道网络设备(或网络接口)是通过一个数据结构 struct device来表示的。在系统中,每一个实际存在的物理网络设备都对应于一个 device 结构。而所有这些 device 结构联成一张链表并由一个全局变量指针 dev_base 指向表头,从而使系统能够随时得到每个网络接口的信息。 概括来说,一个最简单的网络设备驱动程序,至少应该具有以下的内容:

     

    1.该网络设备的检测及初始化函数,供核心启动初始化时调用

    2.该网络设备的初始化函数,供 register_netdev 调用(可以写成与第 1 项的共用,即用同一个);若是写成 module 兼容方式的,还需写该设备的 init_module 和cleanup_module 函数;

    3.提供该网络设备的打开和关闭操作。供设备被打开或被关闭时调用(一般用 shell命令 ifconfig调用);

    4.提供该网络设备的数据传输函数,负责向硬件发送数据包。当上层协议需要传输数据时,供 dev_queue_xmit 调用;

    5.提供该网络设备的中断服务程序,处理数据传输完毕的善后事宜和数据的接收。当物理网络设备有新数据到达或数据传输完毕时,将向系统发送硬件中断请求,该函数就是用来响应该中断请求的。

     

    驱动搞的不很很明白,有了个概念。

     

    下来想看看ifconfig中怎么调用网卡,来获得网卡的MAC地址。

    ifconfig代码分析:

    主要的数据结构printif.c中的结构体

    struct format_handle format_handles[] =

    {

    #ifdef SYSTEM_FORMAT_HANDLER

     SYSTEM_FORMAT_HANDLER

    #endif

    。。。。。。

    其中SYSTEM_FORMAT_HANDLER宏比较诡异,定义如下:

    /* Output format support. */

     

    #define SYSTEM_FORMAT_HANDLER \

     {"linux", fh_nothing}, \

     {"hwaddr?", system_fh_hwaddr_query}, \

     {"hwaddr", system_fh_hwaddr}, \

     {"hwtype?", system_fh_hwtype_query}, \

     {"hwtype", system_fh_hwtype}, \

     {"txqlen?", system_fh_txqlen_query}, \

     {"txqlen", system_fh_txqlen},

     

    其实就是一张表:其中的system_fh_hwaddr_query的实现如下:

    void system_fh_hwaddr_query (format_data_t form, int argc, char *argv[])

    {

    #ifdef SIOCGIFHWADDR

     struct arphrd_symbol *arp;

     

     if (ioctl (form->sfd, SIOCGIFHWADDR, form->ifr) < 0)

       select_arg (form, argc, argv, 1);

     

     arp = arphrd_findvalue (form->ifr->ifr_hwaddr.sa_family);

     select_arg (form, argc, argv, (arp && arp->print_hwaddr) ? 0 : 1);

    #else

     select_arg (form, argc, argv, 1);

    #endif

    }

     

    哎,还是使用socketoctl来实现。至此没有什么结果,单总归是学习了一下。也做个纪念。

     


TAG: linux Linux LINUX Mac MAC 地址 网卡 调研

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

日历

« 2008-09-07  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 59111
  • 日志数: 105
  • 图片数: 2
  • 文件数: 5
  • 书签数: 46
  • 建立时间: 2006-09-20
  • 更新时间: 2008-08-27

RSS订阅

Open Toolbar