通过源码理解rarp协议(基于linux1.2.13)
时间:2022-07-25
本文章向大家介绍通过源码理解rarp协议(基于linux1.2.13),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
rarp是通过mac地址查询ip的协议,主要用于有mac的主机,但是没有ip的情况。我们先看看rarp协议的协议定义(来自网上的图[1])。
rarp协议的格式和arp协议是一样的,他们都是通过一种地址查询另外一种地址。操作系统内维护了一个转换表。定义如下。
struct rarp_table
{
struct rarp_table *next; /* Linked entry list */
unsigned long ip; /* ip address of entry */
unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */
unsigned char hlen; /* Length of hardware address */
unsigned char htype; /* Type of hardware in use */
struct device *dev; /* Device the entry is tied to */
};
初始化的时候是空的,这个表格的数据来源于,用户通过操作系统提供的接口设置。我们看如何操作这个表。
int rarp_ioctl(unsigned int cmd, void *arg)
{
struct arpreq r;
struct sockaddr_in *si;
int err;
switch(cmd)
{
case SIOCDRARP:
if (!suser())
return -EPERM;
err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
if(err)
return err;
memcpy_fromfs(&r, arg, sizeof(r));
if (r.arp_pa.sa_family != AF_INET)
return -EPFNOSUPPORT;
si = (struct sockaddr_in *) &r.arp_pa;
rarp_destroy(si->sin_addr.s_addr);
return 0;
case SIOCGRARP:
err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
if(err)
return err;
return rarp_req_get((struct arpreq *)arg);
case SIOCSRARP:
if (!suser())
return -EPERM;
err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
if(err)
return err;
return rarp_req_set((struct arpreq *)arg);
default:
return -EINVAL;
}
/*NOTREACHED*/
return 0;
}
通过ioctl函数,我们可以对表格进行增删改查。我们只关注新增的逻辑。因为其他的是类似的。下面是arpreq 的定义
struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
};
static int rarp_req_set(struct arpreq *req)
{
struct arpreq r;
struct rarp_table *entry;
struct sockaddr_in *si;
int htype, hlen;
unsigned long ip;
struct rtable *rt;
memcpy_fromfs(&r, req, sizeof(r));
/*
* We only understand about IP addresses...
*/
if (r.arp_pa.sa_family != AF_INET)
return -EPFNOSUPPORT;
switch (r.arp_ha.sa_family)
{
case ARPHRD_ETHER:
htype = ARPHRD_ETHER;
hlen = ETH_ALEN;
break;
default:
return -EPFNOSUPPORT;
}
si = (struct sockaddr_in *) &r.arp_pa;
ip = si->sin_addr.s_addr;
if (ip == 0)
{
printk("RARP: SETRARP: requested PA is 0.0.0.0 !n");
return -EINVAL;
}
//
rt = ip_rt_route(ip, NULL, NULL);
if (rt == NULL)
return -ENETUNREACH;
/*
* Is there an existing entry for this address? Find out...
*/
cli();
// 判断之前是不是已经存在
for (entry = rarp_tables; entry != NULL; entry = entry->next)
if (entry->ip == ip)
break;
/*
* If no entry was found, create a new one.
*/
// 不存在则创建一个表项
if (entry == NULL)
{
entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table),
GFP_ATOMIC);
// 还没初始化则初始化
if(initflag)
{
rarp_init();
initflag=0;
}
entry->next = rarp_tables;
rarp_tables = entry;
}
entry->ip = ip;
entry->hlen = hlen;
entry->htype = htype;
memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
entry->dev = rt->rt_dev;
sti();
return 0;
}
我们看到这里会往表里插入一个表项(如果不存在的话),还有另外一个逻辑是rarp_init。
static void rarp_init (void)
{
/* Register the packet type */
rarp_packet_type.type=htons(ETH_P_RARP);
dev_add_pack(&rarp_packet_type);
}
这个函数是往底层注册一个节点,当mac底层收到一个ETH_P_RARP类型的数据包的时候(在mac协议头里定义),就会执行rarp_packet_type中定义的函数。下面是该rarp_packet_type的定义
static struct packet_type rarp_packet_type =
{
0,
0, /* copy */
rarp_rcv,
NULL,
NULL
};
rarp_rcv函数就是收到一个rarp请求的时候(来自其他主机),执行的函数。
int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
/*
* We shouldn't use this type conversion. Check later.
*/
// rarp协议报文
struct arphdr *rarp = (struct arphdr *)skb->h.raw;
// rarp协议数据部分
unsigned char *rarp_ptr = (unsigned char *)(rarp+1);
struct rarp_table *entry;
long sip,tip;
unsigned char *sha,*tha; /* s for "source", t for "target" */
// 硬件地址长度或类型不一致则忽略
if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)
|| dev->flags&IFF_NOARP)
{
kfree_skb(skb, FREE_READ);
return 0;
}
/*
* If it's not a RARP request, delete it.
*/
// 不是请求报文则忽略
if (rarp->ar_op != htons(ARPOP_RREQUEST))
{
kfree_skb(skb, FREE_READ);
return 0;
}
/*
* Extract variable width fields
*/
// rarp协议首地址
sha=rarp_ptr;
// 发送端mac地址长度
rarp_ptr+=dev->addr_len;
// 拿到发送端ip,存到sip
memcpy(&sip,rarp_ptr,4);
// 跳过4字节
rarp_ptr+=4;
// 目的mac地址
tha=rarp_ptr;
// 跳过mac地址长度
rarp_ptr+=dev->addr_len;
// 目的ip地址
memcpy(&tip,rarp_ptr,4);
/*
* Process entry. Use tha for table lookup according to RFC903.
*/
cli();
for (entry = rarp_tables; entry != NULL; entry = entry->next)
// 判断mac地址是否相等
if (!memcmp(entry->ha, tha, rarp->ar_hln))
break;
// 非空则说明找到
if (entry != NULL)
{ // 拿到对应的ip
sip=entry->ip;
sti();
// 回复,类似是响应ARPOP_RREPLY
arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha,
dev->dev_addr);
}
else
sti();
kfree_skb(skb, FREE_READ);
return 0;
}
我们看到这个函数很长,不过逻辑比较简单,就是解析收到的rarp请求中的数据,然后根据其他主机请求的mac地址,从维护的表格中找到对应的ip(如果有的话),然后调用arp_send函数发送回包。下面列一下该函数的代码。
void arp_send(int type, int ptype, unsigned long dest_ip,
struct device *dev, unsigned long src_ip,
unsigned char *dest_hw, unsigned char *src_hw)
{
struct sk_buff *skb;
struct arphdr *arp;
unsigned char *arp_ptr;
/*
* No arp on this interface.
*/
if(dev->flags&IFF_NOARP)
return;
/*
* Allocate a buffer
*/
// 分配一个skb存储数据包
skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+ dev->hard_header_len, GFP_ATOMIC);
// 构造arp协议数据包
skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4);
skb->arp = 1;
skb->dev = dev;
// 不存在缓存,发完可以销毁
skb->free = 1;
// 构造mac头
dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb);
/* Fill out the arp protocol part. */
arp = (struct arphdr *) (skb->data + dev->hard_header_len);
arp->ar_hrd = htons(dev->type);
arp->ar_pro = htons(ETH_P_IP);
arp->ar_hln = dev->addr_len;
arp->ar_pln = 4;
arp->ar_op = htons(type);
arp_ptr=(unsigned char *)(arp+1);
memcpy(arp_ptr, src_hw, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &src_ip,4);
arp_ptr+=4;
if (dest_hw != NULL)
memcpy(arp_ptr, dest_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &dest_ip, 4);
// 调用mac头发送函数发送出去
dev_queue_xmit(skb, dev, 0);
}
这就是rarp的早期实现。
References
[1]
网上的图: https://wenku.baidu.com/view/8fbb89a7f524ccbff12184a0.html#
- flash读取XML 背景自动适应大小
- 记录一个发邮件的cs文件
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(56)-插件---单文件上传与easyui使用fancybox
- xml-rpc(2)-first demo_v2
- xml-rpc(1)-first demo
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(55)-工作流设计-表单布局
- 网站源文件被注入了iframe代码—ARP欺骗的木马病毒攻击
- ASP.NET MVC5+EF6+EasyUI 后台管理系统--工作流演示截图
- 基于CPPN与GAN+VAE生成高分辨率图像
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控
- (收藏)搭建.NET Framework 3.0开发环境 及SharePoint 2007/WSS 3环境
- WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(48)-工作流设计-起草新申请
- 把windows2003“搬”到手机上。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 几种定时任务(Timer、TimerTask、ScheduledFuture)的退出—结合真实案例【JAVA并发】
- gitlab内存消耗大,频繁出现502错误的解决办法
- Java基于POI实现excel任意多级联动下拉列表——支持从数据库查询出多级数据后直接生成【附源码】
- Elasticsearch 通过Scroll遍历索引,构造pandas dataframe 【Python多进程实现】
- 【Java】 NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException、ArrayIndexOutOfBoundsE
- Meow攻击删除不安全(开放的)的Elasticsearch(及MongoDB) 索引,建一堆以Meow结尾的奇奇怪怪的索引(如:m3egspncll-meow)
- MySQL LOAD DATA INFILE—从文件(csv、txt)批量导入数据
- MySQL 快速删除大量数据(千万级别)的几种实践方案——附源码
- 什么样的代码是好代码?
- Elastic search集群新增节点(同一集群,同一物理机)
- Tesseract-OCR 4.1.0 安装和使用— windows及CentOS
- Java 大小端转换(基于ByteBuffer)
- Tika结合Tesseract-OCR 实现光学汉字识别(简体、宋体的识别率百分之百)—附Java源码、测试数据和训练集下载地址
- 阿里《JAVA实习生入职测试题—2019最新》之答案详解(连载一)
- 阿里《JAVA实习生入职测试题—2019最新》之答案详解(连载二)