RAW协议源码解析
发布日期: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创建:使用socket函数创建一个RAW类型的socket。
  • 数据包构建:手动填充IP头字段,并将数据添加到数据部分。
  • 数据发送:使用sendto函数发送数据包。
  • 数据接收:使用recvfrom函数接收并解析接收到的数据包。
  • 通过这些步骤,开发者可以实现自定义的网络数据处理逻辑,充分发挥RAW协议的灵活性和功能性。

    上一篇:packet协议源码解析
    下一篇:应用层发送一个数据包的时候,是如何到达网卡的(上)

    发表评论

    最新留言

    感谢大佬
    [***.8.128.20]2025年04月03日 22时10分21秒