arp_send()函数分析

  (代码基于linux2.4.0)

void arp_send(int type,/*arp协议编码,如ARPOP_REPLY(arp响应)、ARPOP_REQUEST(arp请求)等*/
       int ptype, /*以太网协议类型,或者说是接口的硬件类型,如ARP(ETH_P_ARP)、x.25(ETH_P_X25)、ip(ETH_P_IP)等*/
       u32 dest_ip, /*目的ip地址*/
       struct net_device *dev, /*用于发包的网卡设备*/
       u32 src_ip, /*源ip地址*/
       unsigned char *dest_hw, /*目的硬件地址*/
       unsigned char *src_hw,/*源硬件地址*/
       unsigned char *target_hw) /*目的硬件地址,它用于arp响应时填充到arp包中,arp请求应该填0*/
{
 struct sk_buff *skb;/*用于管理封装arp包的存储空间的sk_buff指针*/
 struct arphdr *arp;/*指向arp包头*/
 unsigned char *arp_ptr;/*指向arp数据*/

 /*
  * No arp on this interface.
  */
 
 if (dev->flags&IFF_NOARP)
  return;

 /*
  * 分配缓冲区,
  * ARP数据包格式为:
  * 硬件类型(2bytes)+协议类型(2bytes)+硬件地址长度(1bytes)+协议长度(1bytes)+操作码(2bytes)
  *     +源mac地址(6bytes)+源IP地址(4bytes)+目的mac地址(6bytes)+目的IP地址(4bytes)
  * 长度=以太网包头长度+arp包头长度+arp数据长度(源ip长度4+源硬件地址长度+目的ip长度4+目的硬件地址长度)+15(用作缓冲区字对齐)
  */
 
 skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
    + dev->hard_header_len + 15, GFP_ATOMIC);
 if (skb == NULL)
  return;

 skb_reserve(skb, (dev->hard_header_len+15)&~15);/*在skb中申请以太网硬件头缓冲区,且边界字对齐*/
 skb->nh.raw = skb->data;
 /*在skb中申请arp数据包缓冲区(包括arp头和数据)*/
 arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
 skb->dev = dev;/*指定数据包发送网卡*/
 skb->protocol = __constant_htons (ETH_P_ARP);
 if (src_hw == NULL)
  src_hw = dev->dev_addr;/*如果源硬件mac地址未提供则赋值为发送网卡的硬件地址*/
 if (dest_hw == NULL)
  dest_hw = dev->broadcast;/*如果目标硬件mac地址未提供则赋值为广播地址,通常arp请求时它置为广播地址*/

 /*
  *填充设备MAC地址.MAC帧格式:
  *目的地址(6字节)+ 源地址(6字节)+ 2字节字段(IEEE802.3:数据长度/DIX以太网:数据类型)+ 数据(46~~1500)+FCS
  */
 if (dev->hard_header &&
     dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len) < 0)
  goto out;

 /*
  * Fill out the arp protocol part.
  *
  * The arp hardware type should match the device type, except for FDDI,
  * which (according to RFC 1390) should always equal 1 (Ethernet).
  */
 /*
  * Exceptions everywhere. AX.25 uses the AX.25 PID value not the
  * DIX code for the protocol. Make these device structure fields.
  */
 switch (dev->type) {
 default:
  arp->ar_hrd = htons(dev->type);
  arp->ar_pro = __constant_htons(ETH_P_IP);
  break;

#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 case ARPHRD_AX25:
  arp->ar_hrd = __constant_htons(ARPHRD_AX25);
  arp->ar_pro = __constant_htons(AX25_P_IP);
  break;

#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 case ARPHRD_NETROM:
  arp->ar_hrd = __constant_htons(ARPHRD_NETROM);
  arp->ar_pro = __constant_htons(AX25_P_IP);
  break;
#endif
#endif

#ifdef CONFIG_FDDI
 case ARPHRD_FDDI:
  arp->ar_hrd = __constant_htons(ARPHRD_ETHER);
  arp->ar_pro = __constant_htons(ETH_P_IP);
  break;
#endif
#ifdef CONFIG_TR
 case ARPHRD_IEEE802_TR:
  arp->ar_hrd = __constant_htons(ARPHRD_IEEE802);
  arp->ar_pro = __constant_htons(ETH_P_IP);
  break;
#endif
 }
 
 arp->ar_hln = dev->addr_len;/*对以太网而言,是MAC地址长度,应该为6*/
 arp->ar_pln = 4;/*对IP协议则是IP地址的长度*/
 arp->ar_op = htons(type);/*arp请求或响应*/

 arp_ptr=(unsigned char *)(arp+1);/*跳过ARP头,指向数据部分*/

 memcpy(arp_ptr, src_hw, dev->addr_len);/*填充源硬件地址*/
 arp_ptr+=dev->addr_len;/*指针后移*/
 memcpy(arp_ptr, &src_ip,4);/*填充源IP地址*/
 arp_ptr+=4;/*指针后移*/
 if (target_hw != NULL) /*不为空则填充目标硬件地址,一般用于arp响应,填充解析的硬件地址*/
  memcpy(arp_ptr, target_hw, dev->addr_len);
 else  /*否则填充全0地址,一般用于arp请求,因为目标硬件地址未知*/
  memset(arp_ptr, 0, dev->addr_len);
 arp_ptr+=dev->addr_len; /*指针后移*/
 memcpy(arp_ptr, &dest_ip, 4);/*填充目的IP地址*/
 skb->dev = dev; /*指定发送数据包的网卡设备*/

 dev_queue_xmit(skb);/*函数内部调用以太网卡驱动程序的发送函数将数据包发送到网络上*/
 return;

out:
 kfree_skb(skb);

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个发送和解析ARP数据包的示例代码,代码中使用Python Socket模块进行数据包的发送和接收: ```python import socket import struct def send_arp_request(src_ip, dst_ip, interface): # 以太网帧头部 dst_mac = b'\xff\xff\xff\xff\xff\xff' src_mac = b'\x00\x00\x00\x00\x00\x00' ether_type = b'\x08\x06' # 表示ARP协议类型 # ARP帧头部 hw_type = b'\x00\x01' # Hardware type, 1表示以太网 proto_type = b'\x08\x00' # Protocol type, 0x0800表示IP协议 hw_len = b'\x06' # Hardware address length, 6表示MAC地址长度 proto_len = b'\x04' # Protocol address length, 4表示IP地址长度 opcode = b'\x00\x01' # ARP operation, 1表示ARP请求 src_mac_bytes = bytes.fromhex(''.join(interface.split(':'))) src_ip_bytes = socket.inet_aton(src_ip) dst_ip_bytes = socket.inet_aton(dst_ip) # 构造ARP请求数据包 arp_packet = struct.pack('!6s6s2s2s2s6s4s6s4s', dst_mac, src_mac_bytes, ether_type, hw_type, proto_type, hw_len, proto_len, opcode, src_mac_bytes, src_ip_bytes, dst_mac, dst_ip_bytes) # 使用Socket发送ARP请求数据包 sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.SOCK_RAW) sock.bind((interface, socket.SOCK_RAW)) sock.send(arp_packet) def parse_arp_reply(packet): eth_hdr = packet[:14] # Ethernet帧头部长度为14字节 arp_hdr = packet[14:42] # ARP帧头部长度为28字节 # 解析以太网帧头部信息 eth_dst_mac, eth_src_mac, eth_type = struct.unpack('!6s6s2s', eth_hdr) if eth_type != b'\x08\x06': return None # 解析ARP帧头部信息 arp_hw_type, arp_proto_type, arp_hw_len, arp_proto_len, arp_operation, arp_src_mac, arp_src_ip, arp_dst_mac, arp_dst_ip = struct.unpack('!2s2s1s1s2s6s4s6s4s', arp_hdr) if arp_operation != b'\x00\x02': return None # 返回解析结果 return { 'src_mac': arp_src_mac.hex(':'), 'src_ip': socket.inet_ntoa(arp_src_ip), 'dst_mac': arp_dst_mac.hex(':'), 'dst_ip': socket.inet_ntoa(arp_dst_ip) } ``` 以上代码中,`send_arp_request()`函数用于构造和发送ARP请求数据包,需要指定源IP地址、目标IP地址和网络接口名。在函数内部,我们首先构造了以太网帧头部和ARP帧头部,然后将它们拼接起来,最终得到一个完整的ARP请求数据包。我们使用Socket模块创建了一个原始套接字,并通过`send()`方法将ARP数据包发送出去。 `parse_arp_reply()`函数用于解析收到的ARP应答数据包,需要传入一个二进制数据包。在函数内部,我们首先从数据包中解析出以太网帧头部和ARP帧头部,然后从ARP帧头部中解析出源MAC地址、源IP地址、目标MAC地址和目标IP地址,并以字典形式返回解析结果。 需要注意的是,以上代码仅供参考,实际使用时还需要考虑网络环境、权限等因素,并进行适当的调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值