📄 if_ethersubr.c
字号:
/*略过BSD版权说明: * ethernet网络层代码详解 * 解释:xie_minix */#include "opt_atalk.h" -------#include "opt_inet.h" ^#include "opt_inet6.h" 此段由编译器编译时候产生,用于一些开关的设置#include "opt_ipx.h" |#include "opt_bdg.h" |#include "opt_netgraph.h" --------#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/socket.h>#include <sys/sockio.h>#include <sys/sysctl.h>#include <net/if.h>#include <net/netisr.h>#include <net/route.h>#include <net/if_llc.h>#include <net/if_dl.h>#include <net/if_types.h>#include <net/bpf.h>#include <net/ethernet.h>#if defined(INET) || defined(INET6)#include <netinet/in.h>#include <netinet/in_var.h>#include <netinet/if_ether.h>#endif/*vlan的代码我也去掉了,因为我不打算分析他*/#include "vlan.h"#if NVLAN > 0#include <net/if_vlan_var.h>#endif /* NVLAN > 0 *//* netgraph 相关函数用于PPPoE协议即ADSL,对不起大家,我去掉了这些函数的说明,如果那位感兴趣,可以加上 */void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh);void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh);int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);void (*ng_ether_attach_p)(struct ifnet *ifp);void (*ng_ether_detach_p)(struct ifnet *ifp);static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, struct sockaddr *));u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };#define senderr(e) do { error = (e); goto bad;} while (0)#define IFP2AC(IFP) ((struct arpcom *)IFP)/* * 以太网输出子程序.在此程序中,我省去了很多对我们没有很大用处的东西,如IPX协议,APPLETALK协议,IPV6等,还有 * 一些BPF过滤,防火墙等代码. */intether_output(ifp, m, dst, rt0) register struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; /* * 该结构来自于sys/socket.h 核心用于存储大多数地址. *struct sockaddr { u_char sa_len; /* 总长度 sa_family_t sa_family; /* 地址族 char sa_data[14]; /* 地址值 };*/ struct rtentry *rt0; /*路由结构说明,以下结构在我写的"路由基树(radix)代码头文件及原理分析"中有详细说明struct rtentry { struct radix_node rt_nodes[2]; /* radix树节点结构,详见"路由基树(radix)代码头文件及原理分析" /#define rt_key(r) ((struct sockaddr *)((r)->rt_nodes->rn_key))#define rt_mask(r) ((struct sockaddr *)((r)->rt_nodes->rn_mask)) struct sockaddr *rt_gateway; /* 网关 * long rt_refcnt; /* # 保留参考 * u_long rt_flags; /* 已经启动up/或接口已经关闭down?, 是主机路由还是网络路由 * struct ifnet *rt_ifp; /* 使用的接口 / struct ifaddr *rt_ifa; /* 接口的硬件地址 * struct sockaddr *rt_genmask; /* 克窿路由产生的 * caddr_t rt_llinfo; /* 指向链路层信息 * struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols * struct rtentry *rt_gwroute; /* 网关路由入口 / int (*rt_output) __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); /* 输出程序 * struct rtentry *rt_parent; /* 该路由的克隆父路由 / void *rt_filler2; };*/{ short type; int error = 0, hdrcmplt = 0; u_char esrc[6], edst[6]; register struct rtentry *rt; register struct ether_header *eh; int off, loop_copy = 0; int hlen; /* 链路层头长度 */ struct arpcom *ac = IFP2AC(ifp);/*强制结构类型转换*/ /*接口打开了吗*/ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))/*接口启动了吗?*/ senderr(ENETDOWN); rt = rt0;/*rt也指向路由表*/ if (rt) {/*如果路由指针为真,也就是说TCP连接时保留了对方的路由*/ if ((rt->rt_flags & RTF_UP) == 0) {/*如果路由关闭重新取路由*/ rt0 = rt = rtalloc1(dst, 1, 0UL);/*在路由选路中有(route.c中),作用是查询该地址的路由*/ if (rt0)/*路由找到了*/ rt->rt_refcnt--;/*参考记数减1,到0时该路由将被删除*/ else senderr(EHOSTUNREACH);/*否则发送主机没找到*/ } if (rt->rt_flags & RTF_GATEWAY) {/*路由标识中有网关,既该地址是间接路由,发到网关即可*/ if (rt->rt_gwroute == 0)/*该网关硬件地址没有,找吧*/ goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {/*网关没启动*/ rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL);/*查找网关硬件地址*/ if ((rt = rt->rt_gwroute) == 0)/*还没找到硬件地址*/ senderr(EHOSTUNREACH); } } if (rt->rt_flags & RTF_REJECT) if (rt->rt_rmx.rmx_expire == 0 || time_second < rt->rt_rmx.rmx_expire) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } hlen = ETHER_HDR_LEN;/*=14*/ switch (dst->sa_family) {/*查看地址族*/#ifdef INET case AF_INET:/*是IP协议*/ if (!arpresolve(ac, rt, m, dst, edst, rt0))/*调用ARP的地址解释例程*/ return (0); /* 还是解释不了 */ off = m->m_pkthdr.len - m->m_len;/*总长度=包的长度-目前mbuf的长度*/ type = htons(ETHERTYPE_IP);/*主机到网络方式*/ break;#endif case pseudo_AF_HDRCMPLT: hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; (void)memcpy(esrc, eh->ether_shost, sizeof (esrc)); /*上面这一段比较有意思,是以太网的头部的源地址和目的地址对调,一般用于ARP的代理*/ case AF_UNSPEC:/*一般由ARP的ARP请求发送例程调用*/ loop_copy = -1; eh = (struct ether_header *)dst->sa_data;/*构造发送的以太网头部*/ (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); type = eh->ether_type; break; default:/*如果到了这,那肯定出错了*/ printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, dst->sa_family); senderr(EAFNOSUPPORT); } /* * 加上局域网头部. 如果在第一个mbuf中没有空间,那么再分配另外一个mbuf */ M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);/*分配一mbuf,前面保留以太网头部的长度空间*/ if (m == 0) senderr(ENOBUFS); eh = mtod(m, struct ether_header *);/*定位以太网头*/ (void)memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); (void)memcpy(eh->ether_dhost, edst, sizeof (edst));/*拷贝目的地址*/ if (hdrcmplt) (void)memcpy(eh->ether_shost, esrc, sizeof(eh->ether_shost)); else (void)memcpy(eh->ether_shost, ac->ac_enaddr,/*拷贝源地址,在xie_ipfw中要从ether_input中的源地址拷贝过来*/ sizeof(eh->ether_shost)); /* * 如果一个单工接口, 并且有一包到达地址是本卡的地址或广播地址,或环回. * XXX 为了使一个单工设备做起来象一双工设备 */ if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); (void) if_simloop(ifp, n, dst->sa_family, hlen); } else if (bcmp(eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN) == 0) { (void) if_simloop(ifp, m, dst->sa_family, hlen); return (0); /* XXX */ } } /* PPPoe协议,我们可不管他 */ if (ng_ether_output_p != NULL) { if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) {bad: if (m != NULL) m_freem(m); return (error); } if (m == NULL) return (0); } /* 以太网头准备完毕,调用以太网祯输出*/ return ether_output_frame(ifp, m);}/*以太网链路层输出例程发送一帧数据 */intether_output_frame(ifp, m) struct ifnet *ifp; struct mbuf *m;{ int s, error = 0; s = splimp();/*在对队列和输出开始操作前屏蔽网络中断*/ if (IF_QFULL(&ifp->if_snd)) {/*发送队列满了吗?*/ IF_DROP(&ifp->if_snd); splx(s); m_freem(m); return (ENOBUFS); } ifp->if_obytes += m->m_pkthdr.len; if (m->m_flags & M_MCAST) ifp->if_omcasts++; IF_ENQUEUE(&ifp->if_snd, m);/*把mbuf编入队列*/ if ((ifp->if_flags & IFF_OACTIVE) == 0)/*接口正在输出吗?*/ (*ifp->if_start)(ifp); splx(s); return (error);}/* 处理一收到的以太网包,此包在一个无以太网头部的mbuf链中 * (其实在此过程中并没处理封包,而是传导到ether_demux中处理,在这可以安排自己的代码) * 调用是由网卡驱动程序调用,可参考我的网卡驱动程序详解,在这我去掉了一些不相关的代码 * 如:IPX, APPALTALK,IPV6,IPFW,DUMMYNET等 */voidether_input(ifp, eh, m) struct ifnet *ifp; /*接收到以太网封包的网络适配器的ifnet*/ struct ether_header *eh; /*以太网头部,成员为*/ /* 10M以太网络头部结构. struct ether_header { u_char ether_dhost[ETHER_ADDR_LEN]; 目的主机地址 u_char ether_shost[ETHER_ADDR_LEN]; 源主机地址 u_short ether_type; 以太网络类型 }; */ struct mbuf *m;{/*---------------------------------------从这到下面都是过滤用的---------------------------------------------*/ /* PPPoE支持(ADSL) */ if (ng_ether_input_p != NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -