
RAW协议源码解析
socket创建:使用 数据包构建:手动填充IP头字段,并将数据添加到数据部分。 数据发送:使用 数据接收:使用
发布日期:2021-05-09 16:03:14
浏览次数:22
分类:精选文章
本文共 4948 字,大约阅读时间需要 16 分钟。
RAW协议是一种在IP层上实现的协议,主要用于在内核中填充MAC头和源IP字段。该协议与TCP类似,内核在创建socket时会生成sock结构体,该结构体存储了一系列操作函数的指针。尽管目前尚未实现通过参数让内核填充IP头的功能,但内核仍能完成MAC头和源IP字段的填充。
RAW协议的实现细节
socket结构体:内核在创建socket时会初始化sock结构体,这个结构体包含了一系列操作函数的指针。这些函数用于处理socket的连接、接收和发送等操作。
IP头填充:在RAW协议中,IP头的填充需要由用户代码完成。内核只负责填充MAC头和源IP字段,而IP头的其他字段(如目标IP、上层协议等)需要通过用户提供的数据进行填充。
示例代码分析:
-
发送数据:
#include
#include #include #include #include #include int main() { int s; struct sockaddr_in daddr; char packet[50]; struct iphdr *ip = (struct iphdr *)packet; if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket error"); exit(EXIT_FAILURE); } daddr.sin_family = AF_INET; daddr.sin_port = 0; inet_pton(AF_INET, DEST, (struct in_addr *)&daddr.sin_addr.s_addr); memset(daddr.sin_zero, 0, sizeof(daddr.sin_zero)); memset(packet, 'A', sizeof(packet)); ip->ihl = 5; ip->version = 4; ip->tos = 0; ip->tot_len = htons(40); ip->frag_off = 0; ip->ttl = 64; ip->protocol = IPPROTO_RAW; ip->check = 0; ip->saddr = daddr.sin_addr.s_addr; ip->daddr = daddr.sin_addr.s_addr; if (sendto(s, (char *)packet, sizeof(packet), 0, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { perror("sendto error"); exit(EXIT_FAILURE); } } 该代码展示了如何通过RAWsocket发送IP数据包。用户需要手动填充IP头字段,并使用
sendto
函数将数据包发送到目标地址。 -
接收数据:
#include
#include #include #include #include #include int main() { int s; struct sockaddr_in saddr; char packet[50]; if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket error"); exit(EXIT_FAILURE); } memset(packet, 0, sizeof(packet)); socklen_t *len = (socklen_t *)sizeof(saddr); int fromlen = sizeof(saddr); while (1) { if (recvfrom(s, (char *)&packet, sizeof(packet), 0, (struct sockaddr *)&saddr, &fromlen) < 0) { perror("packet receive error"); break; } int i = sizeof(struct iphdr); while (i < sizeof(packet)) { fprintf(stderr, "%c", packet[i]); i++; } printf("\n"); } } 该代码展示了如何通过RAWsocket接收IP数据包。
recvfrom
函数用于接收数据包,并将接收到的数据解析并显示。
RAW协议的内核实现
raw_rcv函数:
int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, long saddr, long daddr) { skb->sk = sk; skb->len = ntohs(skb->ip_hdr->tot_len); skb->h.raw = (unsigned char *)skb->ip_hdr; skb->dev = dev; skb->saddr = daddr; skb->daddr = saddr; if (sock_queue_rcv_skb(sk, skb) < 0) { ip_statistics.IpInDiscards++; skb->sk = NULL; kfree_skb(skb, FREE_READ); return(0); } ip_statistics.IpInDelivers++; release_sock(sk); return(0);}
该函数负责将接收到的数据包拆分并挂载到socket的接收队列中。
raw_sendto函数:
static int raw_sendto(struct sock *sk, unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { struct sk_buff *skb; struct device *dev = NULL; struct sockaddr_in sin; int tmp; int err; if (flags & MSG_OOB) { return -EOPNOTSUPP; } if (flags & ~MSG_DONTROUTE) { return -EINVAL; } if (usin) { if (addr_len < sizeof(sin)) { return -EINVAL; } memcpy(&sin, usin, sizeof(sin)); if (sin.sin_family != AF_INET) { return -EINVAL; } } else { sin.sin_family = AF_INET; sin.sin_port = sk->protocol; sin.sin_addr.s_addr = sk->daddr; } if (sin.sin_port == 0) { sin.sin_port = sk->protocol; } if (sin.sin_addr.s_addr == INADDR_ANY) { sin.sin_addr.s_addr = ip_my_addr(); } if (sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) { return -EACCES; } skb = sock_alloc_send_skb(sk, len + sk->prot->max_header, noblock, &err); if (skb == NULL) { return err; } skb->sk = sk; skb->free = 1; skb->localroute = sk->localroute | (flags & MSG_DONTROUTE); tmp = sk->prot->build_header(skb, sk->saddr, sin.sin_addr.s_addr, &dev, sk->protocol, sk->opt, skb->mem_len, sk->ip_tos, sk->ip_ttl); if (tmp < 0) { kfree_skb(skb, FREE_WRITE); release_sock(sk); return tmp; } memcpy_fromfs(skb->data + tmp, from, len); if (sk->protocol == IPPROTO_RAW) { unsigned char *buff; struct iphdr *iph; buff = skb->data; buff += tmp; iph = (struct iphdr *)buff; iph->saddr = sk->saddr; } skb->len = tmp + len; if (sk->prot->queue_xmit(sk, dev, skb, 1)) { release_sock(sk); } return len;}
该函数负责将数据包构建并发送到目标地址。它首先构建IP头,然后将应用程序提供的数据添加到数据部分,并通过sendto
函数发送数据包。
RAW协议的应用
RAW协议通常用于在应用层直接处理IP数据包,而无需经过传输层(如TCP或UDP)。这使得它在某些特定场景下非常有用,例如在需要直接控制数据包的路由或进行特定协议处理时。
通过以上代码,可以看出RAW协议的实现主要包括以下几个步骤:
socket
函数创建一个RAW类型的socket。sendto
函数发送数据包。recvfrom
函数接收并解析接收到的数据包。通过这些步骤,开发者可以实现自定义的网络数据处理逻辑,充分发挥RAW协议的灵活性和功能性。
发表评论
最新留言
感谢大佬
[***.8.128.20]2025年04月03日 22时10分21秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
leetcode——相交链表
2019-03-07
const修饰指针(常量指针与指针常量的区别)
2019-03-07
设计模式-创建型02-工厂模式-工厂方法模式01
2019-03-07
设计模式-行为型04-迭代子模式(Iterator)
2019-03-07
设计模式-行为型09-访问者模式(Visitor)
2019-03-07
JAVA设计模式--七大原则之迪米特法则(最少知道原则)(06)
2019-03-07
springboot使用@EnableCaching实现缓存的使用
2019-03-07
微信小程序sort时间排序
2019-03-07
13个JavaScript单行式代码
2019-03-07
20个常用的JavaScript简写技巧
2019-03-07
5个很常用的CSS3网页小实例
2019-03-07
前端基础知识整理汇总(上)
2019-03-07
微信小程序 - 实现左滑动删除功能
2019-03-07
OSI七层参考模型和数据封装
2019-03-07
<s>
2019-03-07
minus
2019-03-07
Rust
2019-03-07
DTD - 实体
2019-03-07
OBDC无法创建sql server连接
2019-03-07