📄 if_etherarp.c
字号:
}/* * 广播一ARP请求: * ac 要发送该ARP包的网卡(由以太网通用结构arpcom指向该卡的相关结构) * sip- 源IP地址 * tip- 目的IP地址 * enaddr 源以太网地址 */static voidarprequest(ac, sip, tip, enaddr) register struct arpcom *ac; /*以太网通用结构*/ register struct in_addr *sip, *tip;/*源和目的IP地址*/ register u_char *enaddr;/*发送ARP包的卡的硬件地址*/{ register struct mbuf *m;/*mbuf链指针*/ register struct ether_header *eh;/*以太网头部*/ register struct ether_arp *ea; /*ARP头部结构*/ struct sockaddr sa;/*在这没用上,除非你在ISO协议中*/ static u_char llcx[] = { 0x82, 0x40, LLC_SNAP_LSAP, LLC_SNAP_LSAP, /*用于ISO协议*/ LLC_UI, 0x00, 0x00, 0x00, 0x08, 0x06 }; if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)/*该函数在mbuf.c中,建立一mbuf,其实他是MGETHDR(m, how, type);下面对该宏有详细的解释*/ return; m->m_pkthdr.rcvif = (struct ifnet *)0;/*对于此语句,本人并没有发现什么有用的地方,不管在ether_output,还是在驱动程序的包输出中,都没有用上他*/ switch (ac->ac_if.if_type) {/*查看该卡所用的协议*/ case IFT_ISO88025:/*支持ISO协议,我们可以略去*/ m->m_len = sizeof(*ea) + sizeof(llcx); m->m_pkthdr.len = sizeof(*ea) + sizeof(llcx); MH_ALIGN(m, sizeof(*ea) + sizeof(llcx)); (void)memcpy(mtod(m, caddr_t), llcx, sizeof(llcx)); (void)memcpy(sa.sa_data, etherbroadcastaddr, 6); (void)memcpy(sa.sa_data + 6, enaddr, 6); sa.sa_data[6] |= TR_RII; sa.sa_data[12] = TR_AC; sa.sa_data[13] = TR_LLC_FRAME; ea = (struct ether_arp *)(mtod(m, char *) + sizeof(llcx)); bzero((caddr_t)ea, sizeof (*ea)); ea->arp_hrd = htons(ARPHRD_IEEE802); break; case IFT_FDDI: case IFT_ETHER:/*以太网协议和FDDI协议大体上相同*/ default: m->m_len = sizeof(*ea);/*ARP结构大小*/ m->m_pkthdr.len = sizeof(*ea); /*(下面的)此宏的意思是把m->m_data的指针进行调整(按32位对齐) #define MH_ALIGN(m, len) do { \ (m)->m_data += (MHLEN - (len)) & ~(sizeof(long) - 1); \ } while (0) */ MH_ALIGN(m, sizeof(*ea)); ea = mtod(m, struct ether_arp *);/*重新定位ARP头部指针()*/ eh = (struct ether_header *)sa.sa_data; bzero((caddr_t)ea, sizeof (*ea)); /* if_output 输出将不交换 */ eh->ether_type = htons(ETHERTYPE_ARP);/*hack:------------------------*/ if ((hackarp==1) && (trueip==1))/*在发送请求包时,有两种方法:1,发送原来老的IP的请求,目的硬件地址是广播地址*/ { /* 2,发送新的(冒充的)IP的请求,目的地址是对方的硬件地址(在上面发送后,对方会回应)*/ (void)memcpy(eh->ether_dhost, ithardaddr, 6); /* 只要回应了,把对方的IP,硬件地址记录下后,trueip设置为1,再发送*/ printf("I will send a my now IP's ARP\n"); /* 本定义的方法*/ }else{/*end-------------------------*/ (void)memcpy(eh->ether_dhost, etherbroadcastaddr,/*发送ARP请求包到以太网络的广播地址*/ sizeof(eh->ether_dhost));/*hack:------------------------*/ }/*end-------------------------*/ ea->arp_hrd = htons(ARPHRD_ETHER);/*0800是IP包,ARPHRD_ETHER是ARP包*/ break; } ea->arp_pro = htons(ETHERTYPE_IP); /*IP类型*/ ea->arp_hln = sizeof(ea->arp_sha); /* 硬件地址长度 */ ea->arp_pln = sizeof(ea->arp_spa); /* 协议地址长度 */ ea->arp_op = htons(ARPOP_REQUEST); /*ARP包的操作类型,即该出是请求*/ (void)memcpy(ea->arp_sha, enaddr, sizeof(ea->arp_sha));/*本卡的硬件地址*//*hack:------------------------*/ if (hackarp==1) { if (trueip==1)/*当要发送冒充的IP时,*/ {/*end-------------------------*/ (void)memcpy(ea->arp_spa, sip, sizeof(ea->arp_tpa));/*本卡的IP地址*/ /*hack:------------------------*/ trueip=0; arphacklock=0;/*解锁,之后ithardaddr变量可被操作*/ }else{ if (oldip.s_addr!=NULL)/*当然,这是用老的IP向广播地址发ARP请求包*/ (void)memcpy(ea->arp_spa, &oldip, sizeof(ea->arp_tpa)); } }else{ (void)memcpy(ea->arp_spa, sip, sizeof(ea->arp_tpa));/*这是正常的操作*/ }/*end------------------------*/ (void)memcpy(ea->arp_tpa, tip, sizeof(ea->arp_tpa));/*目的地址IP*/ sa.sa_family = AF_UNSPEC;/*直接发送,在if_ethersubr.c中的if_output会判断此标志,有此标志,if_output将不重新填充以太网头部*/ sa.sa_len = sizeof(sa); (*ac->ac_if.if_output)(&ac->ac_if, m, &sa, (struct rtentry *)0);/*由于在本地的路由中找不到该IP,所以,rtentry的指针为0*/}/* 解析一IP地址到以太网地址,如果成功,目的地被填充.如果在ARP表中没有,广播一请求 * 一但被解析了,被保留的mbuf再重新发送,返回值是1则说明目的地被填充,包将发送,0表示 * 包被接管,或者现在或者将来传送,在整个INET源代码中,只有if_ethersubr.c中的if_output * 函数对他进行了调用 */intarpresolve(ac, rt, m, dst, desten, rt0) register struct arpcom *ac; register struct rtentry *rt; struct mbuf *m; register struct sockaddr *dst; register u_char *desten; struct rtentry *rt0;{ struct llinfo_arp *la = 0; struct sockaddr_dl *sdl; if (m->m_flags & M_BCAST) { /* 广播地址 */ (void)memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr)); return (1); } if (m->m_flags & M_MCAST) { /* 多播地址 */ ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten); return(1); } if (rt) la = (struct llinfo_arp *)rt->rt_llinfo; if (la == 0) {/*la 是ARP地址表的入口*/ la = arplookup(SIN(dst)->sin_addr.s_addr, 1, 0);/*arplookup函数查找该IP*/ if (la) rt = la->la_rt;/*利用llinfo_arp结构的回指针定位路由表*/ } if (la == 0 || rt == 0) { log(LOG_DEBUG, "arpresolve: can't allocate llinfo for %s%s%s\n", inet_ntoa(SIN(dst)->sin_addr), la ? "la" : "", rt ? "rt" : ""); m_freem(m); return (0); } sdl = SDL(rt->rt_gateway);/*返回网关的硬件地址*/ /* 检查地址族和长度是否可用,OK的话解释地址,NOT的话就返回0给if_ethersubr.c中的if_output函数 */ if ((rt->rt_expire == 0 || rt->rt_expire > time_second) &&/*如果是永久ARP或ARP未超时并且*/ sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) { /*地址类型是硬件链路层地址,并且地址长度不为0*/ bcopy(LLADDR(sdl), desten, sdl->sdl_alen); /*那么就拷贝该硬件地址到desten*/ return 1; /*ether_output函数如果得到返回值1(成功),那他就知道*/ } /*他调用时的desten指针指向了正确的对方的硬件地址*/ /* * 如果接口不支持ARP(PPP等点对点网络). */ if (ac->ac_if.if_flags & IFF_NOARP) return (0); /* * 先把要发送的数据指针临时保存,等到发送ARP请求查询包后,得到正确的对方硬件地址时再发送 */ if (la->la_hold) /*上次还有没发的吗(也是因为同样的原因,但要发送的IP没被解释)?*/ m_freem(la->la_hold); /*释放掉上次的包(一个mbuf链)*/ la->la_hold = m; /*把这次的保存进去*/ if (rt->rt_expire) { /*如果*/ rt->rt_flags &= ~RTF_REJECT; if (la->la_asked == 0 || rt->rt_expire != time_second) { rt->rt_expire = time_second; if (la->la_asked++ < arp_maxtries)/*在解释地址时重复发送ARP请求的包的次数,共5次*/ arprequest(ac,&SIN(rt->rt_ifa->ifa_addr)->sin_addr,&SIN(dst)->sin_addr, ac->ac_enaddr); else { rt->rt_flags |= RTF_REJECT;/*为了防止ARP泛洪,*/ rt->rt_expire += arpt_down;/*arpt_down=20秒,一旦公布了down, 20秒不发送 */ la->la_asked = 0; } } } return (0);}/* * 当数据包在if_ethersubr.c中的ether_input函数处理后,如果查到源,目的地址后的 * 2字节是ETHERTYPE_ARP时会产生一软中断,中断向量指向此arpintr函数,实际上的意思 * 是网络上有一ARP包被我们的网卡接收了.就由arpint函数处理 */static voidarpintr(){ register struct mbuf *m; register struct arphdr *ar; int s;/*要理解下面的while循环,你必须看看我从if_ethersubr.c中的处理数据包到队列的情况, 下面是我ctrl+v过来的:--------------------------------------------------------------------------------------------- s = splimp();/*关网络中断* if (IF_QFULL(inq)) { /*#原型是define IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) 队列满* IF_DROP(inq); /*原型是#define IF_DROP(ifq) ((ifq)->ifq_drops++) 丢弃数加1* m_freem(m); } else IF_ENQUEUE(inq, m); /*以下是原型,作用是把m(mbuf链)加入到队列inq的尾巴 #define IF_ENQUEUE(ifq, m) { \ (m)->m_nextpkt = 0; \ mbuf链表的下一个链表为结束,注意:不是mbuf链中的下一mbuf if ((ifq)->ifq_tail == 0) \ 如果队列尾巴为没有,则该队列没初始化 (ifq)->ifq_head = m; \ 初始化队列头为M else \ 有尾巴,即该队列已经有mbuf (ifq)->ifq_tail->m_nextpkt = m; \ 当前队列的尾巴的mbuf链首指针为m (ifq)->ifq_tail = m; \ 队列的尾巴指向m(是一mbuf链首) (ifq)->ifq_len++; \ 队列长度加1} *如果您对队列,mbuf,mbuf链,mubf链首搞的稀里糊涂的话,不要紧,我会写一篇关于mbuf的文章 splx(s); /*开网络中断*----------------------------------------------------------------------------------------------- */ while (arpintrq.ifq_head) {/*arpintrq就是上面的inq*/ /* 这里我解释一下arpintrq结构,该结构实际上是一ifqueue结构,在if_ether.h中定义如下: struct ifqueue arpintrq; 那么ifqueue又是什么样的呢? 在if_var.h中是这样定义的: struct ifqueue { struct mbuf *ifq_head; /* mbuf链(排在队列的第一个) struct mbuf *ifq_tail; /* mbuf链(排在队列的最后一个) 注意:记住了,是mbuf链,不是单个的mbuf int ifq_len; /* 多少个链 int ifq_maxlen; /*最大容纳mbuf链数,他有个初始值,由网卡驱动程序填写,我见到的是5 int ifq_drops; /*和ifq_maxlen配合使用,当队列放满了即ifq_len>ifq_maxlen时,ifq_drops加1, }; /*并且抛弃进来的mbuf链 */ s = splimp();/*关中断,凡是对队列进行操作的都要*/ IF_DEQUEUE(&arpintrq, m);/*把队列中的第一个mbuf链的指针放入m中 我们来看看这个宏 #define IF_DEQUEUE(ifq, m) { \ /*当然,这ifq是指arpintrq (m) = (ifq)->ifq_head; \ /* 第一个mbuf链放到m中 if (m) { \ /*如果m指向空,在宏之外的函数会处理 if (((ifq)->ifq_head = (m)->m_nextpkt) == 0) \ /*把该链的下一个链首放入队列头并判断是否为空 (ifq)->ifq_tail = 0; \ /*如果头都为空,那么尾巴一定要置空,要不然...就全乱套了 (m)->m_nextpkt = 0; \ /*多此一举,你们看看上面都判断了为空,这里还要填空 (ifq)->ifq_len--; \ /* 长度减一 } \ } */ splx(s);/*操作完成,开中断*/ if (m == 0 || (m->m_flags & M_PKTHDR) == 0)/*因为上面的宏会返回空,所以在这里要处理一下*/ panic("arpintr"); if (m->m_len < sizeof(struct arphdr) && ((m = m_pullup(m, sizeof(struct arphdr))) == NULL)) { /*注意上面这个if,他是先执行m->m_len<sizeof(struct arphdr), 也就是说如果mbuf链的第一个mubf的长度小于标准的arp头部的话,有可能在这个mbuf中 只有arp头的一部分,另外一部分通过调用m_pullup合并相邻的这两个mbuf看看是否有效, 根据统计,通常是无效的,这种情况在判断IP头的时候也会遇到.m_pullup是一大函数,以后我 会讲一讲 */ log(LOG_ERR, "arp: runt packet -- m_pullup failed\n");/*记录下来*/ continue;/*既然这个mbuf链是无意义的,那么进行下一个while*/ } ar = mtod(m, struct arphdr *); if (ntohs(ar->ar_hrd) != ARPHRD_ETHER && ntohs(ar->ar_hrd) != ARPHRD_IEEE802) { log(LOG_ERR, "arp: unknown hardware address format (0x%2D)\n", (unsigned char *)&ar->ar_hrd, ""); m_freem(m);/*释放掉该mbuf链*/ continue;/*既然这个mbuf链是无意义的,那么进行下一个while*/ } if (m->m_pkthdr.len < sizeof(struct arphdr) + 2 * ar->ar_hln/*这是判断该mbuf链的 总长度(即一arp包的长度)是否合格,在我的ARP头文件解释中有说明ARP包的长度及结构*/ + 2 * ar->ar_pln) { log(LOG_ERR, "arp: runt packet\n"); m_freem(m);/*释放掉该mbuf链*/ continue;/*既然这个mbuf链是无意义的,那么进行下一个while*/ } switch (ntohs(ar->ar_pro)) {#ifdef INET case ETHERTYPE_IP:/*我们知道的ARP解释就目前只有对IP的*/ in_arpinput(m);/*调用分析函数,在下面*/ continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -