摩托车网站建设,工会网站开发需求分析,qq空间注册申请,wordpress 添加搜索栏在C/C中#xff0c;我们可以使用socket函数来创建套接字。我们需要指定地址族为AF_PACKET#xff0c;协议为htons(ETH_P_ALL)来捕获所有传入和传出的数据包。
可以使用sendto和recvfrom函数来发送和接收数据包。我们需要构建一个合法的链路层数据包#xff0c;在数据包的头…在C/C中我们可以使用socket函数来创建套接字。我们需要指定地址族为AF_PACKET协议为htons(ETH_P_ALL)来捕获所有传入和传出的数据包。
可以使用sendto和recvfrom函数来发送和接收数据包。我们需要构建一个合法的链路层数据包在数据包的头部添加目标MAC地址和源MAC地址并指定以太网类型为htons(ETH_P_IP)。
然后可以在发送前设置IP头部和ICMP头部。为了构建一个有效的ICMP Echo Request消息需要正确设置ICMP类型、代码、校验和和标识符字段。
最后通过接收数据包并解析来获取目标设备的响应。需要注意处理ICMP Echo Reply消息并检查序列号和校验和的正确性。
原始套接字的概念
原始套接字Raw Socket是一种在网络编程中使用的特殊套接字类型它允许应用程序直接访问和操作网络协议栈中的网络层和传输层协议。与其他套接字类型如TCP套接字和UDP套接字相比原始套接字提供更底层的网络访问能力。
原始套接字允许应用程序发送和接收自定义的网络数据包绕过操作系统的网络协议栈的上层处理。应用程序可以直接构建和解析网络协议的头部并将数据发送到特定的目标主机或接口。这种直接的访问能力使得原始套接字成为一种强大的工具能够实现各种高级网络功能和协议如网络监测、网络仿真、路由协议实现等。
使用原始套接字时应用程序需要具有足够的权限来使用它通常需要以超级用户如root用户的身份运行。这是因为原始套接字具有更高的网络访问权限可以直接操作底层网络接口和协议不受操作系统的保护和限制。
原始套接字的使用需要对网络协议和协议栈的内部工作原理有一定的了解以正确构建和解析网络数据包的格式。它通常用于开发高级网络应用程序、网络调试工具、网络安全评估工具等场景中对网络进行更底层的控制和操作。而在普通的网络编程中一般使用更高级的套接字类型如TCP套接字和UDP套接字来实现应用层协议的交互。
链路层ping实现原理
链路层ping工具使用原始套接字实现涉及以下原理 原始套接字原始套接字是一种可以直接访问网络协议栈的套接字类型。它允许应用程序直接发送和接收网络数据包绕过传输层和应用层协议的处理。 ICMP协议链路层ping工具使用的是ICMP协议Internet Control Message Protocol。ICMP协议是在网络层之上使用的一种控制协议用于在IP网络中传递错误消息和有关网络状态的信息。 ICMP Echo请求和响应链路层ping工具通常发送ICMP Echo请求也称为ping请求到目标主机并期望收到相应的ICMP Echo响应。当目标主机收到Echo请求时它将生成一个对应的Echo响应并将其发送回源主机。 构造和解析ICMP数据包链路层ping工具需要构造符合ICMP协议规范的数据包并能够解析收到的ICMP响应数据包。ICMP数据包包含ICMP报文头部和负载数据其中ICMP Echo请求和响应的报文类型为8和0。 IP数据包发送和接收链路层ping工具使用原始套接字发送和接收IP数据包。在构造ICMP数据包时会将ICMP报文作为负载放入IP数据报中并设置目标IP地址、源IP地址、协议类型等IP头部字段。 校验和计算在构造ICMP数据包时需要计算校验和字段。校验和计算是为了确保数据在传输过程中的完整性接收端可以通过校验和验证数据包的正确性。 Root权限由于原始套接字需要对网络接口进行底层访问因此使用原始套接字通常需要root权限。
链路层ping的需求
链路层ping工具的需求可以归纳为以下几个方面 网络连通性测试链路层ping工具可以用于测试两个网络节点之间的连通性。它能够发送ICMP Echo请求到目标节点并获取对应的ICMP Echo响应从而确定节点之间是否能够正常通信。 网络性能测试链路层ping工具可以测量网络链路的性能指标如往返延迟RTTRound-Trip Time和丢包率。通过发送ICMP Echo请求并测量请求到响应的时间差可以估计网络链路的延迟。同时通过记录响应丢失的数量可以计算丢包率。 网络故障排除当网络出现故障时链路层ping工具可以用于排查故障原因。通过在网络各个节点之间进行ping测试可以确定具体哪个节点之间出现问题从而有针对性地进行故障排除。 确定网络拓扑通过链路层ping工具在网络中的不同节点之间进行ping测试可以帮助确定网络的拓扑结构包括节点之间的连接方式和路径。 定位网络性能瓶颈链路层ping工具可以用于定位网络中的性能瓶颈。通过在不同节点之间进行ping测试并分析各个测试点的延迟和丢包率可以确定网络的性能瓶颈所在从而采取相应的优化措施。 监控网络稳定性链路层ping工具可以用于监测网络的稳定性。通过定期进行ping测试并记录网络的性能指标可以获得网络的运行情况并及时发现潜在的问题。 自动化网络管理链路层ping工具可以通过脚本或自动化程序进行批量测试和监控从而实现对大规模网络的管理和优化。
Linux C/C 原始套接字打造链路层ping工具
使用原始套接字AF_PACKET、SOCK_raw实现的链接层ping实用程序.
struct ping_config {unsigned long count;unsigned long size;unsigned long interval; /* in milliseconds */int listen;const char *ifname;int ifindex;unsigned char ifaddr[ETH_ALEN];struct list_head hosts;unsigned char replyto[ETH_ALEN];
};struct ping_host {unsigned char host[ETH_ALEN];struct list_head node;
};enum llcmp_types {LLCMP_ECHO_REQUEST 128,LLCMP_ECHO_REPLY 129,
};/* 类似于ICMPv6回显请求/回复的结构 */
struct ping_header {struct ethhdr ethhdr;uint8_t reserved1; uint8_t reserved2; __be16 payload_len; uint8_t type;uint8_t reserved3; uint16_t reserved4; __be16 identifier;__be16 seqno;uint8_t replyto[6];
} __attribute__((packed));struct ping_config ping_config;...static void sigint_handler(int signo) {term 1;
}static int init_socket(void)
{struct ifreq ifreq;const char *ifname ping_config.ifname;int ret;struct epoll_event event;/* create socket */sd socket(AF_PACKET, SOCK_RAW, htons(LLCMP_ETHER_TYPE));if (sd 0) {fprintf(stderr,Error: Cant open a raw socket for ether type 0x%04x\n,LLCMP_ETHER_TYPE);return -EPERM;}/* bind socket to specific interface */ret setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, ifname,strlen(ifname));if (ret 0) {fprintf(stderr, Error: Cant bind to device %s\n, ifname);goto err;}/* get MAC address and index of interface */memset(ifreq, 0, sizeof(ifreq));strcpy(ifreq.ifr_name, ifname);ret ioctl(sd, SIOCGIFHWADDR, ifreq);if (ret 0) {fprintf(stderr, Error: Cant get mac address of interface\n);goto err;}eth_copy(ping_config.ifaddr,(unsigned char *)ifreq.ifr_hwaddr.sa_data);ret ioctl(sd, SIOCGIFINDEX, ifreq);if (ret 0) {fprintf(stderr, Error: Cant get interface index\n);goto err;}ping_config.ifindex ifreq.ifr_ifindex;/* add socket to epoll */memset(event, 0, sizeof(event));event.events EPOLLIN;event.data.fd sd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sd, event)) {fprintf(stderr, Error: Cant add socket to epoll.\n);ret -EPERM;goto err;}return 0;err:close(sd);return ret;
}static int config_add_host(const char *host) {struct ping_host *ping_host;ping_host malloc(sizeof(*ping_host));if (!ping_host) {fprintf(stderr,Error: Cant allocate host: out-of-memory\n);return -ENOMEM;}if (eth_str2bin(host, ping_host-host) 0) {free(ping_host);fprintf(stderr, Error: invalid MAC address: %s\n, host);return -EINVAL;}if (eth_is_zero(ping_host-host)) {free(ping_host);fprintf(stderr,Error: zero MAC address not allowed\n);return -EINVAL;}list_add_tail(ping_host-node, ping_config.hosts);return 0;
}static void config_free_hosts(void) {struct ping_host *host, *tmp;list_for_each_entry_safe(host, tmp, ping_config.hosts, node) {list_del(host-node);free(host);}
}static int init_args(int argc, char *argv[])
{
...struct option long_opts[] {{help, no_argument, 0, h},{listen, no_argument, 0, l},{interface, required_argument, 0, I},{count, required_argument, 0, c},{size, required_argument, 0, s},{interval, required_argument, 0, i},{replyto, required_argument, 0, r},{0, 0, 0, 0}};while(1) {opt getopt_long(argc, argv, hlc:s:i:I:r:, long_opts, opt_idx);if (opt -1)break;errno 0;switch(opt) {case h:usage(argv[0]);exit(2);break;case l:ping_config.listen 1;break;case c:ping_config.count strtoul(optarg, endptr, 10);if (errno ! 0 || endptr optarg ||ping_config.count 0)goto parse_err;break;case s:ping_config.size strtoul(optarg, endptr, 10);if (errno ! 0 || endptr optarg)goto parse_err;break;case i:if (sscanf(optarg, %f, interval) EOF ||interval 0 || interval UINT16_MAX)goto parse_err;ping_config.interval (unsigned long)(1000 * interval);if (ping_config.interval 0)goto parse_err;break;case I:ping_config.ifname optarg;break;case r:if (eth_str2bin(optarg, ping_config.replyto) 0) {fprintf(stderr, Error: invalid MAC address: %s\n\n,argv[optind]);exit(2);}if (eth_is_zero(ping_config.replyto)) {fprintf(stderr,Error: zero MAC address not allowed\n\n);exit(2);}break;default:fprintf(stderr, \n);usage(argv[0]);exit(2);}}...
}static int init_request_buffer(void)
{
...request_buffer malloc(len);if (!request_buffer)return -ENOMEM;memset(request_buffer, 0, len);eth_copy(request_buffer-ethhdr.h_source, ping_config.ifaddr);request_buffer-ethhdr.h_proto htons(LLCMP_ETHER_TYPE);request_buffer-payload_len htons(ping_config.size);request_buffer-type LLCMP_ECHO_REQUEST;request_buffer-identifier identifier;if (eth_is_zero(ping_config.replyto))eth_copy(request_buffer-replyto, ping_config.ifaddr);elseeth_copy(request_buffer-replyto, ping_config.replyto);return 0;
}static int init_ping(int argc, char *argv[])
{int ret;INIT_LIST_HEAD(ping_config.hosts);ret init_args(argc, argv);if (ret 0)return -EINVAL;epoll_fd epoll_create1(0);if (epoll_fd 0) {fprintf(stderr, Cant create epoll file descriptor.\n);return -EPERM;}ret init_socket();if (ret 0)goto err1;if (signal(SIGINT, sigint_handler) SIG_ERR) {fprintf(stderr, Cant establish SIGINT handler.\n);ret -EPERM;goto err2;}if (!ping_config.listen) {ret init_request_buffer();if (ret 0) {fprintf(stderr,Cant allocate request buffer: %s\n,(ret -EBUSY) ?no entropy : out-of-memory);goto err2;}}return 0;err2:close(sd);
err1:close(epoll_fd);return ret;
}static int send_packet(struct ping_header *plhdr)
{
...ret sendto(sd, plhdr, sizeof(*plhdr) ntohs(plhdr-payload_len), 0,(struct sockaddr*)addr, sizeof(addr));if (ret 0) {fprintf(stderr, Error: Could not send packet: %s\n, strerror(errno));return ret;}return 0;
}static int send_echo_request(void)
{
...list_for_each_entry(host, ping_config.hosts, node) {eth_copy(request_buffer-ethhdr.h_dest, host-host);ret send_packet(request_buffer);if (ret 0)return ret;}rtcidx (rtcidx 1) % REQUEST_TIME_CACHE_SIZE;clock_gettime(CLOCK_MONOTONIC, request_time_cache[rtcidx]);return 0;
}static void print_echo_request(struct ping_header *plhdr)
{
...eth_bin2str(plhdr-ethhdr.h_source, src);eth_bin2str(plhdr-ethhdr.h_dest, dst);eth_bin2str(plhdr-replyto, replyto);if (plhdr-type LLCMP_ECHO_REQUEST)snprintf(type, sizeof(type), echo request);else if (plhdr-type LLCMP_ECHO_REPLY)snprintf(type, sizeof(type), echo reply);elsesnprintf(type, sizeof(type), unknown);printf(%s %s, LLCMP, %s, reply-to %s, id 0x%04x, seq %u, length %u(%u)\n,src, dst, type, replyto, ntohs(plhdr-identifier),ntohs(plhdr-seqno), paylen, pktlen);
}static int recv_echo_request(struct ping_header *plhdr)
{print_echo_request(plhdr);plhdr-type LLCMP_ECHO_REPLY;eth_copy(plhdr-ethhdr.h_dest, plhdr-replyto);eth_copy(plhdr-ethhdr.h_source, ping_config.ifaddr);eth_copy(plhdr-replyto, ping_config.ifaddr);return send_packet(plhdr);
}static int check_seqno_range(const uint16_t recv_seqno)
{
...if (send_seqno REQUEST_TIME_CACHE_SIZE)last_seqno send_seqno - REQUEST_TIME_CACHE_SIZE 1;/* 不支持seqno环绕 */if (send_seqno recv_seqno ||recv_seqno last_seqno)return -ERANGE;return 0;
}static int echo_reply_timediffus_get(unsigned long *timediff,const uint16_t seqno)
{
...idx (seqno - 1) % REQUEST_TIME_CACHE_SIZE;clock_gettime(CLOCK_MONOTONIC, now);diff timespec_diffus(request_time_cache[idx], now);diff diff 0 ? 0 : diff;if (diff ULONG_MAX)return -ERANGE;*timediff (unsigned long)diff;return 0;
}static int recv_echo_reply(struct ping_header *plhdr)
{
...eth_bin2str(plhdr-ethhdr.h_source, src);eth_bin2str(plhdr-replyto, replyto);if (echo_reply_timediffus_get(timediff_us, ntohs(plhdr-seqno)) 0)printf(%u(%u) bytes from %s (via %s): llcmp_seq%u\n,paylen, pktlen, replyto, src, ntohs(plhdr-seqno));elseprintf(%u(%u) bytes from %s (via %s): llcmp_seq%u time%lu.%03lu ms\n,paylen, pktlen, replyto, src, ntohs(plhdr-seqno),timediff_us / 1000, timediff_us % 1000);return 0;
}static int recv_check_header(struct ping_header *plhdr, int len)
{
...if (ntohs(plhdr-payload_len) len - sizeof(*plhdr)) {fprintf(stderr,Warning: received malformed packet: payload_len too large (%i %zu bytes)\n,ntohs(plhdr-payload_len), (size_t)len - sizeof(*plhdr));return -EINVAL;}if (ntohs(plhdr-ethhdr.h_proto) ! LLCMP_ETHER_TYPE) {fprintf(stderr,Warning: received malformed packet: invalid ether type (0x%04x, expected 0x%04x)\n,ntohs(plhdr-ethhdr.h_proto), LLCMP_ETHER_TYPE);return -EINVAL;}/* 源代码检查忽略自己的帧 */if (eth_is_own(plhdr-ethhdr.h_source))return -EADDRNOTAVAIL;/* 目的地检查只接受多播和自己的地址 */if (!eth_is_own(plhdr-ethhdr.h_dest) !eth_is_multicast(plhdr-ethhdr.h_dest))return -EADDRNOTAVAIL;return 0;
}static int recv_packet(int sd)
{
.../* 忽略不适合我们的东西 */if (ret -EADDRNOTAVAIL)return 0;/* 畸形数据包 */else if (ret 0)return ret;switch (plhdr-type) {case LLCMP_ECHO_REQUEST:/* 仅适用于监听 */if (!ping_config.listen)return 0;ret recv_echo_request(plhdr);break;case LLCMP_ECHO_REPLY:/* 仅适用于请求者 */if (ping_config.listen)return 0;/* 忽略不是来自我们会话的回复 */if (plhdr-identifier ! request_buffer-identifier)return 0;ret recv_echo_reply(plhdr);break;default:fprintf(stderr,Warning: unknown ping type: %u\n, plhdr-type);return -EINVAL;}return ret;
}static int get_next_timeout()
{
...add.tv_sec ping_config.interval / 1000;add.tv_nsec (ping_config.interval % 1000) * (1000*1000);next timespec_sum(request_time_cache[rtcidx], add);diff timespec_diffus(now, next) / 1000;if (diff 0)return 0;return (diff INT_MAX) ? INT_MAX : (int)diff;
}int main(int argc, char *argv[])
{
...while(!term) {if (!ping_config.listen) {if (!ev_count) {if (count count ntohs(request_buffer-seqno))break;send_echo_request();}timeout get_next_timeout();}ev_count epoll_wait(epoll_fd, events, MAX_EVENTS,timeout);for(int i 0; i ev_count; i)recv_packet(events[i].data.fd);}...return 0;
}...If you need the complete source code, please add the WeChat number (c17865354792)
dping利用其自己的非正式以太类型0x4304该类型允许在低级别上测试链路而不依赖于像IPv4/IPv6这样的网络协议。在发送回显请求或回显回复之前不需要ARP或ICMPv6邻居发现。
要使用dping请在侦听模式下启动一个dping实例–listen。然后您可以通过在指定了目标主机的MAC地址的不同主机上启动另一个dping实例来ping它。 示例 dping可以用于单独测试链路的单播和多播能力。 运行效果
单播测试 Listener on ens33/MAC address : Sender on ens37/MAC address : 广播测试
回复功能可用于强制侦听器回复广播地址。这样就可以在不依赖单播的情况下单独测试接口的广播功能。
Listener on ens33/MAC address : Sender on ens37/MAC address : 数据包格式
链路层控制消息协议
数据包格式主要类似于ICMPv6即IPv6的互联网控制消息协议。目前LLCMP回声请求和LLCMP回声回复在dping中实现 0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
--------------------------------
| Ethernet Destination ... |
--------------------------------
| ... Ethernet Destination | Ethernet Source ... |
--------------------------------
| ... Ethernet Source |
--------------------------------
| LLCMP Ethernet Type | Reserved1 | Reserved2 |
--------------------------------
| LLCMP Payload Length | LLCMP Type | Reserved3 |
--------------------------------
| LLCMP Identifier | LLCMP Sequence Number |
--------------------------------
| LLCMP Reply-To ... |
--------------------------------
| ... LLCMP Reply-To |
----------------以太网目标
接收器的6字节MAC地址。除00:00:00:00:00:00外其他时间都允许。
以太网源
6字节的发送方MAC地址。只允许单播MAC地址第一个八位字节的最低有效位设置为0而不是00:00:00:00:00。
LLCMP以太网类型
0x4304网络字节顺序/big-endian非官方以太网类型。
预留1:
保留稍后可能有LLCMP跃点限制
预留2:
为稍后的服务质量保留可能是LLCMP流量类
LLCMP有效载荷长度
LLCMP标头之外的字节数。通常为“帧大小-14字节以太网头-16字节LLCMP头”。待办事项可能将LLCMP标头大小定义为6字节因此排除回声请求/回复特定字段“标识符”、“序列号”和“回复”
LLCMP类型
当前实施/定义
LLCMP回声请求128
LLCMP回声回复129
预留3
保留以后可能是特定LLCMP类型的代码/子类型或标志
LLCMP标识符
随机但对于特定LLCMP回显请求/回复会话固定为2个字节
LLCMP序列号
一个2字节的序列号网络字节顺序/big-endian用于特定的LLCMP回显请求/回复交换。从1开始每次回显请求增加1。
LLCMP回复
6字节MAC地址。在LLCMP回声请求中指定接收器应在其LLCMP回声回复中使用的以太网目的地。在LLCMP中回声回复等于LLCMP回声回复的以太网源。也可用于检测第2层源NAT/代理。
总结
原始套接字是一种特殊的套接字类型允许应用程序直接访问和操作网络协议栈中的网络层和传输层协议。使用原始套接字可以实现更底层的网络访问和控制例如构建和解析自定义的网络数据包。
在实现链路层ping工具时原始套接字可以用于发送和接收ICMP Echo请求和响应从而测试网络的连通性、性能和稳定性。
通过原始套接字我们可以 bypass 操作系统的一些网络协议栈处理直接操作链路层数据以及分析 network path 过程中的网络延迟和网络损耗进而诊断、分析和解决网络问题。使用原始套接字还需要注意权限管理和安全性并且需要对网络协议和协议栈有一定的了解。
Welcome to follow WeChat official account【程序猿编码】
参考RFC 792、RFC 791、RFC 4861、RFC 4443