BSD net源码分析(2-4)

来源:互联网 发布:济南seo招聘 编辑:程序博客网 时间:2024/05/29 05:02
四、以太网接口的配置
系统提供了系统调用ioctl函数为一个进程访问一个设备的标准系统所不支持的特性。

int ioctl(int fd, unsigned long com,...);
fd 是一个描述符,通常是一个设备和网络连接,com 指示ioctl函数所要执行的命令类型,第三个参数是可变的,由第二个参数决定他的内容。
进程通过指定接口的fd,访问接口的特性。以下是网络接口所使用的几个命令以及对应的最终的调用函数。
    命令        第三个参数       函数       说明
SIOCGIFCONF   struct ifconf *   ifconf    获取接口配置清单
SIOCGIFFLAGS  struct ifreq *    ifioctl   获得接口标志
SIOCGIFMETRIC struct ifreq *    ifioctl   获得接口度量
SIOCSIFFLAGS  struct ifreq *    ifioctl   设置接口标志
SIOCSIFMETRIC struct ifreq *    ifioctl   设置接口度量
下面具体介绍一下不同命令的具体处理。

(一)ifioctl函数,ioctl通过将cmd传给ifioctl实现接口的特性访问。
(1)SIOCGIFCONF,OSIOCGIFCONF
通过调用函数ifconf返回一个可变长的ifreq结构的表。
/**********************************/
switch (cmd) {

    case SIOCGIFCONF:
    case OSIOCGIFCONF:
        return (ifconf(cmd, data));
    }
/**********************************/
(2)其他命令
传入的参数是一个指向ifreq的指针。ifunit通过接口名查找接口的ifnet指针,如果未找到,返回ENXIO。
/*****************************************/
ifr = (struct ifreq *)data;
    ifp = ifunit(ifr->ifr_name);
    if (ifp == 0)
        return (ENXIO);
/*****************************************/
        
SIOCGIFFFLAGS 获取接口的标记,并通过传入的指针携带结果返回。
SIOCGIFMETRIC 获取接口的度量,通过传入的地址带回结果
/*****************************************/
case SIOCGIFFLAGS:
        ifr->ifr_flags = ifp->if_flags;
        break;
case SIOCGIFMETRIC:
        ifr->ifr_metric = ifp->if_metric;
        break;
/*****************************************/

SIOCSIFFLAGS 配置接口的标记
SIOCSIFMETRIC 配置接口的度量
接口的配置需要超级权限的用户才能执行,通过suser函数判断是否超级用户。
对SIOCSIFFLAGS 有些标记不能被进程配置,通过忽略标志IFF_CANTCHAGE。
表达式(ifp->if_flags&IFF_CANTCHANGE)清除能被进程改变的接口标志,而表达式(fr->ifr_flags&~IFF_CANTCHANGE)清除在请求中不被进程改变的标志。
最后通过ifp_ioctl指针调用指向设备专用的ioctl函数设置标记。根据不同的标记对接口做不同的操作,这里不详细介绍。
/**************************************************/
    case SIOCSIFFLAGS:
        if (error = suser(p->p_ucred, &p->p_acflag))
            return (error);
        if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) {
            int s = splimp();
            if_down(ifp);
            splx(s);
        }
        if (ifr->ifr_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) {
            int s = splimp();
            if_up(ifp);
            splx(s);
        }
        ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
            (ifr->ifr_flags &~ IFF_CANTCHANGE);
        if (ifp->if_ioctl)
            (void) (*ifp->if_ioctl)(ifp, cmd, data);
        break;

    case SIOCSIFMETRIC:
        if (error = suser(p->p_ucred, &p->p_acflag))
            return (error);
        ifp->if_metric = ifr->ifr_metric;
        break;
/**************************************************/

如果接口ioctl命令不能被识别, ifioctl把命令发送给与所请求插口关联的协议的用户要求函数。
/************************************************/
    default:
        if (so->so_proto == 0)
            return (EOPNOTSUPP);
        return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
            cmd, data, ifp));
/*************************************************/

(2)ifconf函数,为进程提供一个标准的方法发现一个系统中的接口和配置地址。
该函数的返回结果采用两个结构存储内容
/**************************************************/
/*
 * Structure used in SIOCGIFCONF request.
 * Used to retrieve interface configuration
 * for machine (useful for programs which
 * must know all networks accessible).
 */
struct    ifconf {
    int    ifc_len;        /* size of associated buffer */
    union {
        caddr_t    ifcu_buf;
        struct    ifreq *ifcu_req;
    } ifc_ifcu;
#define    ifc_buf    ifc_ifcu.ifcu_buf    /* buffer address */
#define    ifc_req    ifc_ifcu.ifcu_req    /* array of structures returned */
};
/*
 * Interface request structure used for socket
 * ioctl's.  All interface ioctl's must have parameter
 * definitions which begin with ifr_name.  The
 * remainder may be interface specific.
 */
struct    ifreq {
#define    IFNAMSIZ    16
    char    ifr_name[IFNAMSIZ];        /* if name, e.g. "en0" */
    union {
        struct    sockaddr ifru_addr;
        struct    sockaddr ifru_dstaddr;
        struct    sockaddr ifru_broadaddr;
        short    ifru_flags;
        int    ifru_metric;
        caddr_t    ifru_data;
    } ifr_ifru;
#define    ifr_addr    ifr_ifru.ifru_addr    /* address */
#define    ifr_dstaddr    ifr_ifru.ifru_dstaddr    /* other end of p-to-p link */
#define    ifr_broadaddr    ifr_ifru.ifru_broadaddr    /* broadcast address */
#define    ifr_flags    ifr_ifru.ifru_flags    /* flags */
#define    ifr_metric    ifr_ifru.ifru_metric    /* metric */
#define    ifr_data    ifr_ifru.ifru_data    /* for use by interface */
};
/**************************************************/
调用时,ifconf的成员ifc_len表示ifc_buf的长度,函数返回时,内容用ifreq结构填充。
ifreq的成员ifru_data的长度是可变的,他存放不同类型接口的地址sockaddr,不同类型的地址长度不一样。

到此,关于以太网接口的所有函数都以及粗略的做了介绍。
原创粉丝点击