四、以太网接口的配置
系统提供了系统调用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,不同类型的地址长度不一样。
到此,关于以太网接口的所有函数都以及粗略的做了介绍。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mythfish/archive/2008/10/27/3161588.aspx