技术标签: Linux网络编程 c语言 linux 物联网 网络协议 tcp/ip
目录
ARP(Address Resolution Protocol)协议是一种在局域网中解析MAC地址的协议。
当主机要向局域网中的另一台主机发送数据时,需要知道目标主机的MAC地址。ARP协议就是用来解析目标主机的MAC地址的。主机会广播一个ARP请求包,请求目标主机回应自己的MAC地址。目标主机接收到请求后会返回一个ARP响应包,包括自己的MAC地址。这样,请求主机就可以通过MAC地址向目标主机发送数据了。
图 1 ARP工作原理
局域网主机A和主机B通信,如果双方原来没有通信过,主机A只知道主机B的IP地址,不知道主机B的MAC地址,此时主机A和主机B无法正常通信。
局域网通信的基础通信双方都得知道对方的MAC地址,MAC地址通常是存储在主机的ARP表中。
此时主机A会尝试去获取主机B的MAC地址,获取的MAC地址的方式是发送ARP请求(以广播形式发送),主机B如果收到ARP请求会回复ARP响应给主机A。
主机A收到响应后会把主机B的IP和MAC地址记录在ARP表中,此时主机A能够发送数据给主机B,主机B因为收到主机A的ARP请求,通过ARP请求知道主机A的MAC地址,把主机A的IP和MAC地址记录在ARP表中,主机B也能给主机A发送数据,正阳主机A和主机B就能正常通信。
主机A要访问一个公网主机,由于主机A和公网主机IP地址不再同一网段,此时需要用到路由功能,路由功能后续章节会详细介绍。
我们只要记住一个核心点,路由功能核心作用就是找到去往目的IP的网关IP地址。
网关IP地址必须和主机A处于同一局域网。找到网关IP地址的目的是为了获取网关MAC地址,获取到网关MAC地址,可以通过网关MAC地址把数据包发送给网关,让网关转发数据包至公网。
如果通过网关IP获取到网关MAC地址可以参考局域网通信。
跨网段通信流程:
步骤1:通过路由查找到网关IP地址
步骤2:如果不知道网关MAC地址,需要通过ARP协议获取到网关MAC地址。
步骤3:把网关MAC地址填入以太网报文头部,将数据包发给网关。
步骤4:网关转发数据包至公网。
图 2 ARP报文结构
图 3 ARP请求报文
ARP请求各个字段如何填充如上图:
硬件类型:1,ARPHRD_ETHER类型
协议类型:0x8000,IPv4协议
硬件地址长度:6字节,MAC地址长度
协议地址长度:4字节,IP地址长度
操作码:1,ARP请求码
发送方MAC地址:14:23:0a:39:e3:75
发送方IP地址:192.168.1.1
目的MAC地址:00:00:00:00:00:00
目的IP地址:192.168.1.19
图 4 ARP响应报文
ARP响应各个字段如何填充如上图:
硬件类型:1,ARPHRD_ETHER类型
协议类型:0x8000,IPv4协议
硬件地址长度:6字节,MAC地址长度
协议地址长度:4字节,IP地址长度
操作码:2,ARP响应
发送方MAC地址:00:93:37:5b:d9:2f
发送方IP地址:192.168.1.19
目的MAC地址:14:23:0a:39:e3:75
目的IP地址:192.168.1.1
图 5 ARP攻击
ARP攻击(Address Resolution Protocol attack)是一种网络攻击方式,它利用ARP协议进行攻击,通过伪造或欺骗网络中的ARP响应包,将合法网络设备的IP地址映射到攻击者所控制的设备上,从而使攻击者可以窃取或篡改网络中的数据流量。
ARP攻击可以分为两种类型,一种是ARP欺骗(ARP Spoofing),另一种是ARP投毒(ARP Poisoning)。
攻击者会向网络中的设备发送伪造的ARP响应包,使其将攻击者所控制的设备的MAC地址当作目标设备的MAC地址,从而使攻击者可以拦截、篡改或窃取网络中的数据流量。
攻击者会向网络中的设备发送大量的伪造ARP响应包,从而使网络设备的ARP缓存被攻击者所控制的设备所替换,这样一来,攻击者就可以直接访问目标设备,而无需再进行ARP欺骗攻击。
a.使用静态ARP表:将网络设备的IP地址和MAC地址的映射关系手动输入到静态ARP表中,可以防止攻击者篡改ARP响应包。
b.使用端口安全(Port Security):在交换机上配置端口安全,限制每个端口能够连接的MAC地址数量,可以有效地防止ARP攻击。
c.使用ARP防火墙(ARP Firewall):ARP防火墙可以监控网络流量中的ARP请求和响应包,并根据预设的规则进行过滤和阻断,从而有效地防止ARP攻击。
d.加密网络流量:使用加密技术(如SSL、TLS等)对网络流量进行加密,可以防止数据被窃听和篡改,从而有效地保护网络安全。
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <linux/in.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
uint8_t src_mac[ETH_ALEN] = {0x08,0x00,0x27,0x06,0x38,0xba};
uint8_t nexthop_mac[ETH_ALEN] = {0x08,0x00,0x27,0xc1,0xdf,0xea};
#define LOCAL_IP "192.168.1.18"
#define PEER_IP "192.168.1.19"
#define MAX_BUF_SIZE (2048)
#define IP_ADDR_LEN (4)
struct _arphdr {
__be16 ar_hrd;
__be16 ar_pro;
unsigned char ar_hln;
unsigned char ar_pln;
__be16 ar_op;
unsigned char ar_sha[ETH_ALEN];
unsigned char ar_sip[4];
unsigned char ar_tha[ETH_ALEN];
unsigned char ar_tip[4];
};
uint32_t create_pack(char *buf) {
struct ethhdr *eh = (struct ethhdr *)buf;
memcpy(eh->h_dest, nexthop_mac, ETH_ALEN);
memcpy(eh->h_source, src_mac, ETH_ALEN);
eh->h_proto = htons(ETH_P_ARP);
struct _arphdr *ah = (struct _arphdr *)(buf + ETH_HLEN);
ah->ar_hrd = htons(ARPHRD_ETHER);
ah->ar_pro = htons(ETH_P_IP);
ah->ar_hln = ETH_ALEN;
ah->ar_pln = IP_ADDR_LEN;
ah->ar_op = htons(ARPOP_REQUEST);
memcpy(ah->ar_sha, src_mac, ETH_ALEN);
uint32_t src_ip = inet_addr(LOCAL_IP);
memcpy(ah->ar_sip, &src_ip, IP_ADDR_LEN);
memcpy(ah->ar_tha, nexthop_mac, ETH_ALEN);
uint32_t peer_ip = inet_addr(PEER_IP);
memcpy(ah->ar_tip, &peer_ip, IP_ADDR_LEN);
return ETH_HLEN + sizeof(struct _arphdr);
}
int main(int argc , char *argv[]) {
int ret;
int sockfd;
char send_buf[MAX_BUF_SIZE] = {0};
struct sockaddr_ll local;
struct sockaddr_ll peer;
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sockfd == -1) {
perror("socket error");
return -1;
}
bzero(&peer, sizeof(struct sockaddr_ll));
peer.sll_family = PF_PACKET;
peer.sll_protocol = htons(ETH_P_ARP);
peer.sll_ifindex = 2;
peer.sll_hatype = ARPHRD_ETHER;
peer.sll_pkttype = PACKET_OTHERHOST;
peer.sll_halen = ETH_ALEN;
memcpy(peer.sll_addr, nexthop_mac, ETH_ALEN);
uint32_t slen = create_pack(send_buf);
while(1) {
ret = sendto(sockfd, send_buf, slen, 0, (struct sockaddr *)&peer, sizeof(peer));
if (ret <= 0) {
printf("sendto ret:%d, errno:%d(%s)\n", ret, errno, strerror(errno));
break;
}
sleep(1);
}
close(sockfd);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/in.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
uint8_t src_mac[ETH_ALEN] = {0x08,0x00,0x27,0xc1,0xdf,0xea};
uint8_t nexthop_mac[ETH_ALEN] = {0x08,0x00,0x27,0x06,0x38,0xba};
#define LOCAL_IP "192.168.1.19"
#define PEER_IP "192.168.1.18"
#define IP_ADDR_LEN (4)
#define MAX_BUF_SIZE (2048)
struct _arphdr {
__be16 ar_hrd;
__be16 ar_pro;
unsigned char ar_hln;
unsigned char ar_pln;
__be16 ar_op;
unsigned char ar_sha[ETH_ALEN];
unsigned char ar_sip[4];
unsigned char ar_tha[ETH_ALEN];
unsigned char ar_tip[4];
};
void print_arp(char *msg, const char *buf) {
struct _arphdr *ah = (struct _arphdr *)(buf + ETH_HLEN);
printf("%s\n"
"硬件类型:%u\n"
"协议类型:%u\n"
"硬件地址长度:%u\n"
"协议地址长度:%u\n"
"操作码:%u\n"
"发送方硬件地址:%02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n"
"发送方协议地址:%02hx.%02hx.%02hx.%02hx\n"
"接收方硬件地址:%02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n"
"接收方协议地址:%02hx.%02hx.%02hx.%02hx\n",
msg,
ntohs(ah->ar_hrd),
ntohs(ah->ar_pro),
ah->ar_hln,
ah->ar_pln,
ntohs(ah->ar_op),
ah->ar_sha[0], ah->ar_sha[1], ah->ar_sha[2], ah->ar_sha[3], ah->ar_sha[4], ah->ar_sha[5],
ah->ar_sip[0], ah->ar_sip[1], ah->ar_sip[2], ah->ar_sip[3],
ah->ar_tha[0], ah->ar_tha[1], ah->ar_tha[2], ah->ar_tha[3], ah->ar_tha[4], ah->ar_tha[5],
ah->ar_tip[0], ah->ar_tip[1], ah->ar_tip[2], ah->ar_tip[3]);
}
int parse_pack(char *msg, char *buf) {
print_arp(msg, buf);
return 0;
}
uint32_t create_pack(char *sbuf, char *rbuf) {
struct ethhdr *eh = (struct ethhdr *)sbuf;
memcpy(eh->h_dest, nexthop_mac, ETH_ALEN);
memcpy(eh->h_source, src_mac, ETH_ALEN);
eh->h_proto = htons(ETH_P_ARP);
struct _arphdr *rah = (struct _arphdr *)(rbuf + ETH_HLEN);
struct _arphdr *ah = (struct _arphdr *)(sbuf + ETH_HLEN);
ah->ar_hrd = htons(ARPHRD_ETHER);
ah->ar_pro = htons(ETH_P_IP);
ah->ar_hln = ETH_ALEN;
ah->ar_pln = IP_ADDR_LEN;
ah->ar_op = htons(ARPOP_REPLY);
memcpy(ah->ar_sha, src_mac, ETH_ALEN);
uint32_t src_ip = inet_addr(LOCAL_IP);
memcpy(ah->ar_sip, &src_ip, IP_ADDR_LEN);
memcpy(ah->ar_tha, rah->ar_sha, ETH_ALEN);
memcpy(ah->ar_tip, rah->ar_sip, IP_ADDR_LEN);
return ETH_HLEN + sizeof(struct _arphdr);
}
bool isvalid(const char *buf) {
struct ethhdr *eh = (struct ethhdr *)buf;
if (ntohs(eh->h_proto) == ETH_P_ARP) return true;
return false;
}
int main(int argc , char *argv[]) {
int ret;
int sockfd;
char send_buf[MAX_BUF_SIZE] = {0};
char recv_buf[MAX_BUF_SIZE] = {0};
struct sockaddr_ll peer;
socklen_t peerlen = 0;
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sockfd == -1) {
perror("socket error");
return -1;
}
bzero(&peer, sizeof(struct sockaddr_ll));
peer.sll_family = PF_PACKET;
peer.sll_protocol = htons(ETH_P_ARP);
peer.sll_ifindex = 2;
peer.sll_hatype = ARPHRD_ETHER;
peer.sll_pkttype = PACKET_OTHERHOST;
peer.sll_halen = ETH_ALEN;
memcpy(peer.sll_addr, nexthop_mac, ETH_ALEN);
while(1) {
memset(recv_buf, 0, MAX_BUF_SIZE);
ret = recvfrom(sockfd, recv_buf, MAX_BUF_SIZE, 0, NULL, NULL);
if (ret <= 0) {
printf("recvfrom ret:%d error\n", ret);
break;
} else {
bool bret = isvalid(recv_buf);
printf("ret:%d, bret:%d\n", ret, bret);
if (bret == false) {
continue;
}
parse_pack("recv buf", recv_buf);
memset(send_buf, 0, MAX_BUF_SIZE);
int slen = create_pack(send_buf, recv_buf);
slen += 18;
printf("sendbuf len:%d\n", slen);
ret = sendto(sockfd, send_buf, slen, 0, (struct sockaddr *)&peer, sizeof(peer));
if (ret <= 0) {
printf("sendto ret:%d error:%d(%s)\n", ret, errno, strerror(errno));
break;
}
}
}
close(sockfd);
return 0;
}
ip neigh
ip neigh add 192.168.1.19 dev enp0s3 lladdr 08:00:27:c1:df:ea
ip neigh del 192.168.1.19 dev enp0s3 lladdr 08:00:27:c1:df:ea
如果觉得本文对你有帮助,希望给个一键三连,支持博主,谢谢!
文章浏览阅读1.9k次。环境ubuntu16.041、在用户根目录下面新建文件夹bin2、PATH=~/bin:$PATH 也就是让环境变量包含用户根目录下的bin文件夹_安卓内核源码下载
文章浏览阅读1k次。先按下Ctrl + A,再按下x键(注意此处x应为小写,大写无效;Ctrl + A中A大小写均可)。_qemu如何退出
文章浏览阅读337次。食堂君:设计儿童理发店似乎是一个少见的案例,我们能以下案例中,找出一些关于儿童的设计要点。itD首次对儿童理发品牌进行空间设计。设计之初,我们思考的一个要点是:如何将空间元素与斑马线儿童理发品牌自然地连接在一起,并通过整个空间故事让顾客对斑马线产生品牌记忆点?经过前期调研分析,我们发现大多儿童理发品牌的空间多使用动物和汽车元素,且无故事脉络串联,仅填塞各种儿童可能会喜欢的形象,而缺乏空间..._斑马线儿童理发品牌终端空间设计
文章浏览阅读693次。结合前面学习的内容,整理一下caffe的官方示例_clasiffication_final
文章浏览阅读1.8k次。说一下使用Webstorm的原因,在此之前,我一直使用的是HBuilder编辑器,最近使用小程序框架mpvue开发,由于微信开发者工具对vue不太友好,vue编写界面无法查看,大家就推荐了Webstorm。于是,使用了大家推荐的这一款号称前端开发领域最好用的编辑器,果不其然,从此就不想再用别的了。1:安装官网地址:http://www...._idea怎么使用webstorm
文章浏览阅读199次,点赞3次,收藏3次。高校食堂物流管理系统,主要的模块包括管理员;首页、个人中心、管理员管理、用户管理、采购人员管理、财务管理、供应商信息管理、商品管理、出入库管理、基础数据管理,财务;首页、个人中心、财务管理、供应商信息管理、商品管理、出入库管理,仓库管理员;首页、个人中心、采购人员管理、财务管理、供应商信息管理、商品管理、出入库管理,采购;首页、个人中心、采购人员管理、供应商信息管理、商品管理、出入库管理等功能。_高校食堂管理系统的设计与实现
文章浏览阅读710次。名词1名词1解释1...名词2名词2解释1名词2解释2..._html用ul写表格
文章浏览阅读187次。题目链接:点击查看题目大意:给出一个由 n 个数位组成的数字,现在可以通过将其不同的后缀移到前面来组成 n 个新的数字,现在要求出 n 个新数字进行去重后,有多少个新数字分别大于、等于、小于原数字如:1234进行上述转移可以得到的四个新数字分别为:1234,4123,3412,2341题目分析:如果暴力的比较虽然看似只需要枚举 n 个新的字符串,但是每个字符串的比较还需要花费O(n)的..._hdu - 4333
文章浏览阅读2.1w次。http://pengtyao.iteye.com/blog/1462045_用grow模型对下属进行辅导时,为了帮助下属看到更多的可能性,开启思路,管理者常用
文章浏览阅读10w+次,点赞23次,收藏111次。1.网线水晶头为什么要分568A、568B?这要从平行线交叉线说起。平行线:网线2头都做成568B标准,就叫平行线。用于双机不同级连接,比如交换机连电脑。交换机连路由器。交叉线:网线一头做成568B,另一头做成568A,就叫交叉线。用于双机同级连接,比如电脑连电脑,交换机连交换机。现在都是平行线做法,设备能够自己识别。2.568A和568B的区别区别在线序上。568A:白绿,绿,白橙,蓝,白蓝,橙,白棕,棕568B:白橙,橙,白绿,蓝,白蓝,绿,白棕,棕3.为什么不是颜色一致就可以?很多_568b
文章浏览阅读823次。城市按A-Z转载于https://blog.csdn.net/qq_36856163/article/details/97109683citys:{“hot”:[{“id”: 1,“spell”: “ab”,“name”: “北京”}, {“id”: 2,“spell”: “ab”,“name”: “上海”}],“A”: [{“id”: “161”,“spell”: “anshan”,“name”: “鞍山”}, {“id”: “226”,“spell”: “anqing_全国字母检索城市json
文章浏览阅读75次。2018 05 224月30更新版本 Build 3176Version 3.1 3176Sublime Text Build 3176 ( 3.1 )--------------- 安装 Sublime --------------官网: https://www.sublimetext.com/3中文官网: http://sublimetextcn.com/3/..._also available as a portable version