通过源码理解rarp协议(基于linux1.2.13)

2024-03-27 21:18

本文主要是介绍通过源码理解rarp协议(基于linux1.2.13),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

rarp是通过mac地址查询ip的协议,主要用于有mac的主机,但是没有ip的情况。我们先看看rarp协议的协议定义(来自网上的图)。

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,存到sipmemcpy(&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){	// 拿到对应的ipsip=entry->ip;sti();// 回复,类似是响应ARPOP_RREPLYarp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, dev->dev_addr);}elsesti();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);elsememset(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的早期实现。

这篇关于通过源码理解rarp协议(基于linux1.2.13)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/853405

相关文章

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

Java对接MQTT协议的完整实现示例代码

《Java对接MQTT协议的完整实现示例代码》MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛,:本文主要介绍Ja... 目录前言前置依赖1. MQTT配置类代码解析1.1 MQTT客户端工厂1.2 MQTT消息订阅适配器1.

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅