raw socket

ipv4:
1.创建收发二层报文的raw socket:
socket(PF_PACKET, SOCK_RAW, 0); 或者
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

2.创建收发三层(IP层)报文的raw socket:
socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));  设置了这个opt,则自己填写IP header;否则没有设置
这个opt,kernel会自动填写IP header,只需要从IP的报文payload开始填写。


ipv6:
现有的raw socket是基于ipv4的,若改成ipv6,需要修改socket。
1.创建收发二层报文的raw socket:
socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL));

2.创建收发三层报文的raw socket:
尝试了很久,没有成功过。其实如果只是想指定源IP地址和目的IP地址,可以直接bind一下指定源IP,就可以达到目的。


注意:
Ipv6的raw socket和Ipv4区别还是蛮大的。
1.ipv6没有类似ipv4的IP_HDRINCL的option,从三层ip头开始编写报文选项。
2.sendto的第五个参数不一样,不能用sockaddr_in6结构,要用sockaddr_pkt,或是sockaddr_ll结构,否则sendto函数会报错invalid parameter。



编写raw socket的几个关键数据结构:
1. 以太网头:struct ethhdr
2. ip头: struct ip6_hdr 或 struct ip

3. udp头:struct udphdr 


static void AppendIpv6Header(U8 *packet, DhcpMessageIpv6T *msg)
{
    struct ip6_hdr *ip6hdr = DML_CAST_PTR(struct ip6_hdr *, packet);

    /* Parameter checks */
    DML_ASSERTR(packet != NULL);
    DML_ASSERTR(msg != NULL);

    /* Fill in the IPv6 header. */
    ip6hdr->ip6_flow = htonl ((6 << 28) | (0 << 20) | 0);    // IPv6 version (4 bits), Traffic class (8 bits), Flow label (20 bits)
    ip6hdr->ip6_plen = htons(UDPHDR_LEN + msg->len);
    ip6hdr->ip6_nxt = IPPROTO_UDP;
    ip6hdr->ip6_hops = MAXTTL;
    memcpy(ip6hdr->ip6_src.s6_addr, msg->srcIp6, sizeof(msg->srcIp6));
    memcpy(ip6hdr->ip6_dst.s6_addr, msg->dstIp6, sizeof(msg->dstIp6));
}

static void AppendUdpHeader(U8 *packet, DhcpMessageIpv6T *msg)
{
    struct udphdr *udphdr = DML_CAST_PTR(struct udphdr *, packet);
    U32 udpLength = 0;

    /* Parameter checks */
    DML_ASSERTR(packet != NULL);
    DML_ASSERTR(msg != NULL);

    /* Calculate the UDP datagram length */
    udpLength = UDPHDR_LEN + msg->len;

    /* Fill in the UDP header. */
    udphdr->dest   = htons(msg->dstPort);
    udphdr->source = htons(msg->srcPort);
    udphdr->len    = htons(udpLength);
    udphdr->check  = 0;
}

static DmlErrorT OpenSendL2SocketIpv6(DhcpCRawSocketIpv6T *dhcpSock)
{
    S32 sock;
    U32 on = 1;
    
    /* Parameter checks */
    DML_ASSERTRC(dhcpSock  != NULL, DML_PARAM);
    DML_ASSERTRC(dhcpSock->sendL2Sock == INVALID_SOCKET, DML_ERROR);

    /*
     * Create a socket for sending/receiving broadcast DHCP messages 
     */
    sock = socket (PF_INET6, SOCK_RAW, htons(ETH_P_ALL));
    if (sock < 0)
    {
        DML_LOG(DML_EVT_COMP_DHCP_CLIENT, DML_LOG_LVL_CRIT, "Could not create RAW Socket, errno=%s", strerror(errno));
        return DML_ERROR;
    }

    /* Enable sending broadcast frames on this socket. */
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,  &on, sizeof(on)) < 0) 
    {
        DML_LOG(DML_EVT_COMP_DHCP_CLIENT, DML_LOG_LVL_CRIT, "Could set BROADCAST option on socket, errno=%s", strerror(errno));
        close(sock);
        return DML_ERROR;
    }

    dhcpSock->sendL2Sock = sock;
    DML_LOG(DML_EVT_COMP_DHCP_CLIENT, DML_LOG_LVL_TRACE,"Open DHCP send socket - %d\n", sock);

    return DML_OK;
}

DmlErrorT DhcpCRawSockSendL2Ipv6(DhcpCRawSocketIpv6T *dhcpSock, DhcpMessageIpv6T *msg)
{
    S32 rc;
    U8 datagram[DHCP_MAX_MSG_SIZE];
    U8 *datagramPtr = datagram;
    U8 *udpPtr = NULL;
    U32 datagramLen = 0;
    struct ethhdr *eth = NULL;
    struct sockaddr_pkt sockAddr;

    /* Parameter checks */
    DML_ASSERTRC(dhcpSock  != NULL, DML_PARAM);
    DML_ASSERTRC(msg       != NULL, DML_PARAM);

    /* Fill in the Ethernet header. */
    eth = DML_CAST_PTR(struct ethhdr *, datagramPtr);
    memcpy(eth->h_dest, msg->dstMac, sizeof(eth->h_dest));
    memcpy(eth->h_source, msg->srcMac, sizeof(eth->h_source));
    eth->h_proto = htons(ETH_P_IPV6);
    datagramPtr += ETHHDR_LEN;
    datagramLen += ETHHDR_LEN;

    /* Fill in the IP header. */
    AppendIpv6Header(datagramPtr, msg);
    datagramPtr += IPV6HDR_LEN;
    datagramLen += IPV6HDR_LEN;

    /* Fill in the UDP header. 
     *
     * Note, save a pointer to the UDP header because the checksum
     * will have to be computed over the entire frame after the data
     * is copied in. */
    udpPtr = datagramPtr;
    AppendUdpHeader(datagramPtr, msg);
    datagramPtr += UDPHDR_LEN;
    datagramLen += UDPHDR_LEN;

    /* Check if there is enough room to copy the DHCP message into the
     * datagram buffer. */
    if ((sizeof(datagram) - datagramLen) <= msg->len)
    {
        DML_LOG(DML_EVT_COMP_DHCP_CLIENT, DML_LOG_LVL_ERR, 
                "Datagram buffer size %u too small for DHCP message length %u", sizeof(datagram), msg->len);
        return DML_ERROR;
    }

    /* Copy in the DHCP message into the datagram buffer */
    memcpy(datagramPtr, &msg->pkt, msg->len);
    datagramPtr += msg->len;
    datagramLen += msg->len;

    /* Update the UDP checksum */
    UpdateUdpChecksumIpv6(udpPtr, msg);

    /* Set up a socket address */
    memset (&sockAddr, 0, sizeof sockAddr);
    sockAddr.spkt_family = AF_PACKET;
    strncpy ((char *)sockAddr.spkt_device, dhcpSock->name, sizeof sockAddr.spkt_device);
    sockAddr.spkt_protocol = htons(ETH_P_IPV6);
    DML_LOG(DML_EVT_COMP_DHCP_CLIENT, DML_LOG_LVL_TRACE,"send to broadcast socket %d to %s\n",
            dhcpSock->bcastSock, dhcpSock->name);
    /* Send the raw IP packet */
    rc = sendto(dhcpSock->bcastSock, datagram, datagramLen, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
    if (rc != (S32)(datagramLen))
    {
        DML_LOG(DML_EVT_COMP_DHCP_CLIENT, DML_LOG_LVL_ERR, "sendto() failed: %s\n", strerror(errno));
        return DML_ERROR;
    }

    return DML_OK;  
}

 
 

你可能感兴趣的:(raw socket)