ARP输入 之 arp_process
概述
arp_process为ARP输入包的核心处理流程;
若输入为ARP请求且查路由成功,则进行如下判断:输入到本地,则进行应答;否则,允许转发,则转发,本文代码不包含转发流程;
若输入为ARP应答或者查路由失败,则更新邻居项;
源码分析
static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in_device *in_dev = __in_dev_get_rcu(dev);
struct arphdr *arp;
unsigned char *arp_ptr;
struct rtable *rt;
unsigned char *sha;
unsigned char *tha = NULL;
__be32 sip, tip;
u16 dev_type = dev->type;
int addr_type;
struct neighbour *n;
struct dst_entry *reply_dst = NULL;
bool is_garp = false; /* arp_rcv below verifies the ARP header and verifies the device
* is ARP'able.
*/
/* 获取ip配置块 */
if (!in_dev)
goto out_free_skb; /* 获取arp头 */
arp = arp_hdr(skb); /* 根据设备类型做检查 */
switch (dev_type) {
default:
if (arp->ar_pro != htons(ETH_P_IP) ||
htons(dev_type) != arp->ar_hrd)
goto out_free_skb;
break;
case ARPHRD_ETHER:
case ARPHRD_FDDI:
case ARPHRD_IEEE802:
/*
* ETHERNET, and Fibre Channel (which are IEEE 802
* devices, according to RFC 2625) devices will accept ARP
* hardware types of either 1 (Ethernet) or 6 (IEEE 802.2).
* This is the case also of FDDI, where the RFC 1390 says that
* FDDI devices should accept ARP hardware of (1) Ethernet,
* however, to be more robust, we'll accept both 1 (Ethernet)
* or 6 (IEEE 802.2)
*/
if ((arp->ar_hrd != htons(ARPHRD_ETHER) &&
arp->ar_hrd != htons(ARPHRD_IEEE802)) ||
arp->ar_pro != htons(ETH_P_IP))
goto out_free_skb;
break;
case ARPHRD_AX25:
if (arp->ar_pro != htons(AX25_P_IP) ||
arp->ar_hrd != htons(ARPHRD_AX25))
goto out_free_skb;
break;
case ARPHRD_NETROM:
if (arp->ar_pro != htons(AX25_P_IP) ||
arp->ar_hrd != htons(ARPHRD_NETROM))
goto out_free_skb;
break;
} /* Understand only these message types */ /* 操作码不是应答也不是请求 */
if (arp->ar_op != htons(ARPOP_REPLY) &&
arp->ar_op != htons(ARPOP_REQUEST))
goto out_free_skb; /*
* Extract fields
*/
/* 获取arp指针 */
arp_ptr = (unsigned char *)(arp + );
/* 源mac */
sha = arp_ptr;
/* 源ip */
arp_ptr += dev->addr_len;
memcpy(&sip, arp_ptr, );
arp_ptr += ; /* 设备类型 */
switch (dev_type) {
#if IS_ENABLED(CONFIG_FIREWIRE_NET)
case ARPHRD_IEEE1394:
break;
#endif
default:
/* 目的mac */
tha = arp_ptr;
arp_ptr += dev->addr_len;
}
/* 目的ip */
memcpy(&tip, arp_ptr, );
/*
* Check for bad requests for 127.x.x.x and requests for multicast
* addresses. If this is one such, delete it.
*/
/* 目的ip是组播||回环地址但是没有启用route_localnet */
if (ipv4_is_multicast(tip) ||
(!IN_DEV_ROUTE_LOCALNET(in_dev) && ipv4_is_loopback(tip)))
goto out_free_skb; /*
* For some 802.11 wireless deployments (and possibly other networks),
* there will be an ARP proxy and gratuitous ARP frames are attacks
* and thus should not be accepted.
*/
/* 源ip和目的ip相同,设置了免费arp丢包 */
if (sip == tip && IN_DEV_ORCONF(in_dev, DROP_GRATUITOUS_ARP))
goto out_free_skb; /*
* Special case: We must set Frame Relay source Q.922 address
*/
/* 设备类型为q.922,则设置源地址为广播地址 */
if (dev_type == ARPHRD_DLCI)
sha = dev->broadcast; /*
* Process entry. The idea here is we want to send a reply if it is a
* request for us or if it is a request for someone else that we hold
* a proxy for. We want to add an entry to our cache if it is a reply
* to us or if it is a request for our address.
* (The assumption for this last is that if someone is requesting our
* address, they are probably intending to talk to us, so it saves time
* if we cache their address. Their address is also probably not in
* our cache, since ours is not in their cache.)
*
* Putting this another way, we only care about replies if they are to
* us, in which case we add them to the cache. For requests, we care
* about those for us and those for our proxies. We reply to both,
* and in the case of requests for us we add the requester to the arp
* cache.
*/ if (arp->ar_op == htons(ARPOP_REQUEST) && skb_metadata_dst(skb))
reply_dst = (struct dst_entry *)
iptunnel_metadata_reply(skb_metadata_dst(skb),
GFP_ATOMIC); /* Special case: IPv4 duplicate address detection packet (RFC2131) */
/* 源ip为0,用于检测地址冲突 */
if (sip == ) {
/* ARP请求 && 地址是本地地址 && 不忽略该ARP请求,则发送ARP应答 */
if (arp->ar_op == htons(ARPOP_REQUEST) &&
inet_addr_type_dev_table(net, dev, tip) == RTN_LOCAL &&
!arp_ignore(in_dev, sip, tip))
arp_send_dst(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip,
sha, dev->dev_addr, sha, reply_dst);
goto out_consume_skb;
} /* ARP请求 && 查路由成功 */
if (arp->ar_op == htons(ARPOP_REQUEST) &&
ip_route_input_noref(skb, tip, sip, , dev) == ) { /* 获取路由缓存 */
rt = skb_rtable(skb);
addr_type = rt->rt_type; /* 输入到本地 */
if (addr_type == RTN_LOCAL) {
int dont_send; /* 忽略检查 */
dont_send = arp_ignore(in_dev, sip, tip); /* 不忽略,配置了过滤,则判断过滤 */
if (!dont_send && IN_DEV_ARPFILTER(in_dev))
dont_send = arp_filter(sip, tip, dev);
/* 允许输入 */
if (!dont_send) {
/* 查找邻居项,更新状态 */
n = neigh_event_ns(&arp_tbl, sha, &sip, dev); /* 邻居项存在,则回复ARP应答 */
if (n) {
arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
sip, dev, tip, sha,
dev->dev_addr, sha,
reply_dst);
neigh_release(n);
}
}
goto out_consume_skb;
} else if (IN_DEV_FORWARD(in_dev)) {
/* ARP代理 */
}
} /* ARP应答或者查路由失败 */ /* Update our ARP tables */
/* 查找邻居项 */
n = __neigh_lookup(&arp_tbl, &sip, dev, ); addr_type = -;
/* 邻居项存在,或者启用了接收非请求应答 */
if (n || IN_DEV_ARP_ACCEPT(in_dev)) {
/* 检查是否为免费ARP */
is_garp = arp_is_garp(net, dev, &addr_type, arp->ar_op,
sip, tip, sha, tha);
} /* 启用了接收非请求应答 */
if (IN_DEV_ARP_ACCEPT(in_dev)) {
/* Unsolicited ARP is not accepted by default.
It is possible, that this option should be enabled for some
devices (strip is candidate)
*/
/*
邻居项不存在,免费ARP || 邻居项不存在,不是免费ARP,则类型为应答,且为单播
创建邻居项
*/
if (!n &&
(is_garp ||
(arp->ar_op == htons(ARPOP_REPLY) &&
(addr_type == RTN_UNICAST ||
(addr_type < &&
/* postpone calculation to as late as possible */
inet_addr_type_dev_table(net, dev, sip) ==
RTN_UNICAST)))))
n = __neigh_lookup(&arp_tbl, &sip, dev, );
} /* 存在邻居表项 */
if (n) {
int state = NUD_REACHABLE;
int override; /* If several different ARP replies follows back-to-back,
use the FIRST one. It is possible, if several proxy
agents are active. Taking the first reply prevents
arp trashing and chooses the fastest router.
*/
/* 当前时间超过了下次更新时间 或者 为免费ARP */
override = time_after(jiffies,
n->updated +
NEIGH_VAR(n->parms, LOCKTIME)) ||
is_garp; /* Broadcast replies and request packets
do not assert neighbour reachability.
*/
/* 不是ARP应答,或者不是本机包,设置状态为STALE */
if (arp->ar_op != htons(ARPOP_REPLY) ||
skb->pkt_type != PACKET_HOST)
state = NUD_STALE;
/* 更新邻居项 */
neigh_update(n, sha, state,
override ? NEIGH_UPDATE_F_OVERRIDE : , );
neigh_release(n);
} out_consume_skb:
consume_skb(skb); out_free_dst:
dst_release(reply_dst);
return NET_RX_SUCCESS; out_free_skb:
kfree_skb(skb);
return NET_RX_DROP;
}
ARP输入 之 arp_process的更多相关文章
- ARP输入 之 arp_rcv
概述 arp_rcv是ARP包的入口函数,ARP模块在二层注册了类型为ETH_P_ARP的数据包回调函数arp_rcv,当收到ARP包时,二层进行分发,调用arp_rcv: arp_rcv对ARP输入 ...
- 《TCP/IP作品详细解释2:达到》注意事项--ARP:地址解析协议
Net/3于ARP和实施密切与路由表相关联的,下图显示了我们的叙述性说明ARP使用样品. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVE9ERDkxMQ ...
- UIP源码之ARP过程分析
之前我们使用UIP实现了tcp和udp通讯今天来说说UIP的实现流程,当然,这篇文章里面只会涉及tcp和udp,暂时还没办法说DHCP,因为UIP的DHCP实现使用了协程的概念,下一章将协程之后再说D ...
- ARP报文发送的可视化实现
一.安装VS2013,下载wpdpack,为VS2010配置WinpCap环境: ⑴首先在View中选择Property Manager,然后展开工程,再展开Debug|Win32 ,接着右击 Mir ...
- Python黑帽编程 3.1 ARP欺骗
Python灰帽编程 3.1 ARP欺骗 ARP欺骗是一种在局域网中常用的攻击手段,目的是让局域网中指定的(或全部)的目标机器的数据包都通过攻击者主机进行转发,是实现中间人攻击的常用手段,从而实现数据 ...
- 常用网络工具 ipconfig arp traceroute
如今的计算机是离不开网络的计算机了,因而我们对网络要有一基础的认识.连不上网,程序运行不正常之类的,多少都与网络有关.本文将介绍常用的工具. 网络出问题 ipconfig ping 网络连不上,首先要 ...
- 在浏览器中输入URL按下回车键后发生了什么
在浏览器中输入URL按下回车键后发生了什么 [1]解析URL[2]DNS查询,解析域名,将域名解析为IP地址[3]ARP广播,根据IP地址来解析MAC地址[4]分别从应用层到传输层.网络层和数据链路层 ...
- 输入URL到展现页面的全过程
最近在看一本关于网络协议的书<图解HTTP> 当我们在浏览器的地址栏输入 http://www.pwstrick.com ,然后回车,回车这一瞬间到看到页面到底发生了什么呢? 1. 域名 ...
- Python黑客编程ARP欺骗
Python灰帽编程 3.1 ARP欺骗 ARP欺骗是一种在局域网中常用的攻击手段,目的是让局域网中指定的(或全部)的目标机器的数据包都通过攻击者主机进行转发,是实现中间人攻击的常用手段,从而实现数据 ...
随机推荐
- Javascript中new的作用
关于js中new关键字的理解,先来看个例子:像这样创建实例时使用new与不使用new有什么区别????function ParasiticPerson(name, age, job) { var ...
- GoBelieve service部署问题整理
- java代码实现mock数据
废话不多说,直接上代码. /** * 发get请求,获取文本 * * @param getUrl * @return 网页context */ public static String sendGet ...
- JSP 内置对象的说明
1 page:指的是当前的JSP页面本身,它是java.lang.object类的对象 2 config对象:它是ServletConfig类的一个实例 3 exception对象:它是java.la ...
- mysql管理工具之pt
之前我一直用Seconds_behind_master来衡量主从的延迟,今天看到文档,才觉得多么不可靠!以下是官方文档的描述: In essence, this field measures the ...
- 什么时候用assert
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制.在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证 ...
- iFrame跨域的方式
4种通过iframe跨域与其他页面通信的方式 不同域下的iframe不能进行操作. 1.location.hash: 在url中,http://www.baidu.com#helloword的#hel ...
- TensorFlow中numpy与tensor数据相互转化
numpy与tensor数据相互转化: *Numpy2Tensor 虽然TensorFlow网络在输入Numpy数据时会自动转换为Tensor来处理,但是我们自己也可以去显式的转换: data_ten ...
- Graphic系统综合练习案例-绘制饼状图
这里用一个案例来将之前学过的关于绘制相关的东东加强巩固一下,纯绘制,木有加点击效果,先来看下最终效果: github中这种百分比饼图的效果非常非常之多,实际在项目中开发当产品有这样类似的需求时做为开发 ...
- LVS Nginx和HAproxy的区别,怎么选择最好
LVS Nginx和HAproxy有什么区别呢? LVS:Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统. Nginx:Nginx是一款轻量级的w ...