BSD net源码分析(2-1)

来源:互联网 发布:mac电脑照片怎么整理 编辑:程序博客网 时间:2024/05/16 17:06


本节介绍以太网接口相关内容

一、以太网接口的初始化
    内核初始化函数cpu_startup查找连接的网络设备,当识别到网络后,设备专用的初始化函数被调用。
    设备驱动程序为每个接口初始化一个专用的ifnet结构,并调用if_attach把这个结构插入到接口链表中。
    le_softc结构存储了以太网接口的所有信息,其中的第一个成员arpcom包含了所有以太网接口通用的信息。其他部分为以太网接口的特性参数。
struct    le_softc {
    struct    arpcom sc_ac;    /* common Ethernet structures */
#define    sc_if    sc_ac.ac_if    /* network-visible interface */
#define    sc_addr    sc_ac.ac_enaddr    /* hardware Ethernet address */
    struct    lereg0 *sc_r0;    /* DIO registers */
    struct    lereg1 *sc_r1;    /* LANCE registers */
    struct    lereg2 *sc_r2;    /* dual-port RAM */
    int    sc_rmd;        /* predicted next rmd to process */
    int    sc_tmd;        /* next available tmd */
    int    sc_txcnt;    /* # of transmit buffers in use */
    /* stats */
    int    sc_runt;
    int    sc_jab;
    int    sc_merr;
    int    sc_babl;
    int    sc_cerr;
    int    sc_miss;
    int    sc_rown;
    int    sc_xown;
    int    sc_xown2;
    int    sc_uflo;
    int    sc_rxlen;
    int    sc_rxoff;
    int    sc_txoff;
    int    sc_busy;
    short    sc_iflags;
} le_softc[NLE];

再来分析一下arpcom结构的内容。
/*
 * Structure shared between the ethernet driver modules and
 * the address resolution code.  For example, each ec_softc or il_softc
 * begins with this structure.
 */
struct    arpcom {
    struct     ifnet ac_if;        /* network-visible interface */
    u_char    ac_enaddr[6];        /* ethernet hardware address */
    struct    in_addr ac_ipaddr;    /* copy of ip address- XXX */
    struct    ether_multi *ac_multiaddrs; /* list of ether multicast addrs */
    int    ac_multicnt;        /* length of ac_multiaddrs list */   
};

ac_if 所有网络接口共有的信息
ac_enaddr 以太网的硬件地址,在cpu_startup时设备驱动程序从硬件上复制而来的
    /*
     * Read the ethernet address off the board, one nibble at a time.
     */
    cp = (char *)(lestd[3] + (int)hd->hp_addr);
    for (i = 0; i < sizeof(le->sc_addr); i++) {
        le->sc_addr[i] = (*++cp & 0xF) << 4;
        cp++;
        le->sc_addr[i] |= *++cp & 0xF;
        cp++;
    }
在函数leattch中通过以上代码复制硬件上的地址。
ac_ipaddr 上一次给该接口分配的ip地址
ac_multicastaddr 以太网多播地址的列表
ac_multicnt 地址列表的长度
/**********************************************************/
    ifp->if_unit = hd->hp_unit;
    ifp->if_name = "le";
    ifp->if_mtu = ETHERMTU;
    ifp->if_init = leinit;
    ifp->if_reset = lereset;
    ifp->if_ioctl = leioctl;
    ifp->if_output = ether_output;
    ifp->if_start = lestart;
#ifdef MULTICAST
    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
#else
    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
#endif
#if NBPFILTER > 0
    bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
    if_attach(ifp);
/**************************************************************/
    leattach函数同时也初始化了ifnet结构的其他参数。
    接口的函数指针分别指向以太网设备的专用函数。
    bpfattach 登记了bpf接口
    ifattach 将接口插入到接口链表。
ifattach函数是如何将接口链到链表,下面进行分析。
该函数的主要完成为接口创建一个链路层地址ifaddr结构,并将接口插入接口链表。
ifaddr结构还包含了两个sockaddr_dl结构。第一个包含了一个表示逻辑地址,包含一个名称和这个接口在内核中的索引值,和该接口的物理地址。第二个为接口的地址掩码。
/*
 * Structure of a Link-Level sockaddr:
 */
struct sockaddr_dl {
    u_char    sdl_len;    /* Total length of sockaddr */
    u_char    sdl_family;    /* AF_DLI */
    u_short    sdl_index;    /* if != 0, system given index for interface */
    u_char    sdl_type;    /* interface type */
    u_char    sdl_nlen;    /* interface name length, no trailing 0 reqd. */
    u_char    sdl_alen;    /* link level address length */
    u_char    sdl_slen;    /* link layer selector length */
    char    sdl_data[12];    /* minimum work area, can be larger;
                   contains both if name and ll address */
};
此时结构中的sdl_family的值时AF_LINK,表示链路层地址。
sdl_type 与ifnet中的接口类型相同。
sdl_data的前sdl_nlen个字节存放逻辑地址,剩余部分存放物理地址
/*************************************************************/
    /*
     * create a Link Level name for this device
     */
    unitname = sprint_d((u_int)ifp->if_unit, workbuf, sizeof(workbuf));
    namelen = strlen(ifp->if_name);
    unitlen = strlen(unitname);
#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
    masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) +
                   unitlen + namelen;
    socksize = masklen + ifp->if_addrlen;
#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
    socksize = ROUNDUP(socksize);
    if (socksize < sizeof(*sdl))
        socksize = sizeof(*sdl);
    ifasize = sizeof(*ifa) + 2 * socksize;
/*************************************************************/
创建接口的接口名,并计算sockaddr_dl的长度。

/***********************************************************/
    if (ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK)) {
        bzero((caddr_t)ifa, ifasize);
        sdl = (struct sockaddr_dl *)(ifa + 1);
        sdl->sdl_len = socksize;
        sdl->sdl_family = AF_LINK;
        bcopy(ifp->if_name, sdl->sdl_data, namelen);
        bcopy(unitname, namelen + (caddr_t)sdl->sdl_data, unitlen);
        sdl->sdl_nlen = (namelen += unitlen);
        sdl->sdl_index = ifp->if_index;
        sdl->sdl_type = ifp->if_type;
        ifnet_addrs[if_index - 1] = ifa;
        ifa->ifa_ifp = ifp;
        ifa->ifa_next = ifp->if_addrlist;
        ifa->ifa_rtrequest = link_rtrequest;
        ifp->if_addrlist = ifa;
        ifa->ifa_addr = (struct sockaddr *)sdl;
        sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
        ifa->ifa_netmask = (struct sockaddr *)sdl;
        sdl->sdl_len = masklen;
        while (namelen != 0)
            sdl->sdl_data[--namelen] = 0xff;
/***********************************************************/
分配内存,并初始化各个值。最后设置链路层地址的mask,通过mask可以直接获取接口的逻辑地址。

函数的最后调用ether_ifattach函数对以太网ifnet结构通用的信息进行初始化。
并将物理地址从arpcom复制到链路层地址中。

以上就是整个以太网接口的初始化过程了,在第一次调用leattach后,系统会维护一个ifnet链表,连接所有的接口结构体,和一个ifnet_addrs地址指针数组,存放接口的链路层地址的指针。

接口结构被初始化后,内核初始化函数调用ifinit函数初始化各个接口的输出队列的最大长度。队列的大小关系到调用列程能发送的最大数据报。ifinit还调用了if_slowtimo函数启动接口监视定时,定时器的时长为if_timer的值,定时器到期后调用接口的if_watchdog函数。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mythfish/archive/2008/10/22/3126071.aspx

原创粉丝点击