熟悉了mbuf的结构后,我们进入接口层,所谓接口,就是指在一个特定网络上硬件与设备驱动器之间的接口。BSD设计将网络协议和连接到一个系统的网络设备的驱动器间提供一个与硬件无关的编程接口。
提到接口,首先需要介绍ifnet结构,结构ifnet中包含所有接口的通用信息。在系统初始化期间,分别为每个网络设备分配一个独立的ifnet结构。每个ifnet结构有一个列表,它包含这个设备的一个或多个协议地址。函数if_attach在系统初始化期间构造这个链表。if_addrlist指向这个接口的ifaddr结构列表。每个ifaddr结构存储一个要用这个接口通信的协议的地址信息。如果读过我以前的mbuf的分析,是不是感觉数据结构上非常相似呀,幸福的家庭都是一样的:)
/*实现信息*/
char *if_name; /* 字符串,标识接口的类型 */
struct ifnet *if_next; /* 把所有接口的ifnet结构链接成一个链表,这个链表在系统初始化期间构造 */
struct ifaddr *if_addrlist; /* 指向这个接口的ifaddr结构列表 */
int if_pcount; /* 监听者的数目 */
caddr_t if_bpf; /* 分组过滤器结构 */
u_short if_index; /* 在内核中唯一地标识这个接口 */
short if_unit; /* 标识多个相同类型的实例 */
short if_timer; /* 以秒为单位记录时间,直到内核为此接口调用函数if_watchdog为止 */
short if_flags; /* 接口的操作状态和属性,譬如接口正在工作和用于广播 */
struct if_data {
/* 硬件信息 */
u_char ifi_type; /* 指明接口支持的硬件地址类型,如以太网,令牌环 */
u_char ifi_addrlen; /* 数据链路地址的长度 */
u_char ifi_hdrlen; /* 由硬件附加给任何分组的首部的长度 */
u_long ifi_mtu; /* 接口在一次输出操作中能传输的最大数据单元的字节数,如以太网是1500 */
u_long ifi_metric; /* routing metric,通常为0 */
u_long ifi_baudrate; /* 指定接口的传输速率 */
/* 接口统计 */
u_long ifi_ipackets; /* packets received on interface */
u_long ifi_ierrors; /* input errors on interface */
u_long ifi_opackets; /* packets sent on interface */
u_long ifi_oerrors; /* output errors on interface */
u_long ifi_collisions; /* collisions on csma interfaces */
u_long ifi_ibytes; /* total number of octets received */
u_long ifi_obytes; /* total number of octets sent */
u_long ifi_imcasts; /* packets received via multicast */
u_long ifi_omcasts; /* packets sent via multicast */
u_long ifi_iqdrops; /* dropped on input, this interface */
u_long ifi_noproto; /* destined for unsupported protocol */
struct timeval ifi_lastchange;/* last updated */
} if_data;
/* 函数指针,在系统初始化时,每个设备驱动程序初始化它自己的ifnet结构 */
int (*if_init) /* 初始化接口 */
__P((int));
int (*if_output) /* 对输出分组进行排队 */
__P((struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *));
int (*if_start) /* 启动分组的传输 */
__P((struct ifnet *));
int (*if_done) /* 传输完成后的清除 */
__P((struct ifnet *));
int (*if_ioctl) /* IO控制命令 */
__P((struct ifnet *, int, caddr_t));
int (*if_reset)
__P((int)); /* 复位接口设备 */
int (*if_watchdog) /* 周期性监控接口 */
__P((int));
/* 输出队列 */
struct ifqueue {
struct mbuf *ifq_head; /* 指向队列的第一个分组 */
struct mbuf *ifq_tail; /* 指向队列最后一个分组 */
int ifq_len; /* 当前队列中分组的数目 */
int ifq_maxlen; /* 是队列中允许的缓存最大个数 */
int ifq_drops; /* 因为队列满而丢弃的分组数 */
} if_snd;
};
我们要看的下一个结构是接口地址结构,ifaddr。每个接口维护一个ifaddr结构的链表,因为一些数据链路,如以太网,支持多于一个的协议。一个单独的ifaddr结构描述每个分配给接口的地址,通常每个协议一个地址。支持多地址的另一个原因是很多协议,包括TCP/IP,支持为单个物理接口指派多个地址。
结构ifaddr通过ifa_next把分配给一个接口的所有地址链接起来,它还包括一个指回接口的ifnet结构的指针ifa_ifp。下图显示了结构ifnet与ifaddr之间的关系:
u_char sa_len; /* sockaddr的长度 */
u_char sa_family; /* 地址族,如AF_INET */
char sa_data[14]; /* 具体地址数组 */
};
struct ifaddr {
struct sockaddr *ifa_addr; /* 指向接口的一个协议地址 */
struct sockaddr *ifa_dstaddr; /* 指向一个点对点链路上的另一端的接口协议地址或指向一个广播网中分配给接口的广播地址(如以太网) */
#define ifa_broadaddr ifa_dstaddr
struct sockaddr *ifa_netmask; /* 指向一个位掩码,地址中表示网络部分的比特在掩码中被设置为1,地址中表示主机的部分被设置为0 */
struct ifnet *ifa_ifp; /* 指回接口的ifnet结构的指针 */
struct ifaddr *ifa_next; /* 接口的下一个地址 */
void (*ifa_rtrequest)(); /* 支持接口的路由查找 */
u_short ifa_flags; /* 支持接口的路由查找 */
short ifa_refcnt; /* 统计对结构ifaddr的引用 */
int ifa_metric; /* 支持接口的路由查找 */
#ifdef notdef
struct rtentry *ifa_rt;
#endif
};