原始套接字练习(暂记)

https://blog.csdn.net/ZhipingXi/article/details/54972209

https://blog.csdn.net/dengjin20104042056/article/details/52373903

https://blog.csdn.net/jx232515/article/details/51912700

https://linux.die.net/man/3/ether_ntoa 

#include "MrKSocket.h"
#include <sstream>
#include <string>
#include <vector>
#include <stdio.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <linux/if_packet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ether.h>
#include <linux/udp.h>
#include <cstdlib>
#include <ctime>

using namespace std;

CSocket::CSocket()
{
    m_sockfd = 0;
    m_type = NONE;
    memset(&m_stListen, 0, sizeof(m_stListen));
    m_src_mac = "";
    m_dst_mac = "";
    m_src_ip = "";
    m_dst_ip = "";
    m_dst_port = 0;
    m_src_port = 0;
    m_NIC_name = "";
    m_mtu = 0;
    m_pkt_id = 0;
}

CSocket::~CSocket()
{
    close(m_sockfd);
}

int8_t CSocket::CreateSocket(sockType type)
{
    if(type == UDP)
    {
        m_sockfd = socket(PF_INET, SOCK_DGRAM, 0);
        if(m_sockfd == -1)
        {
            perror("create UDP socket failed");
            return 0;
        }
        m_type = UDP;
    }
    else if(type == MAC_RAW)
    {
        m_sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if(m_sockfd == -1)
        {
            perror("create MAC_RAW socket failed");
            return 0;
        }
        m_type = MAC_RAW;
    }
    uint8_t flag = 1;
    setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(uint8_t));

    return 1;
}

int8_t CSocket::Listening(uint16_t port, string ip)
{
    m_stListen.sin_family = AF_INET;
    m_stListen.sin_addr.s_addr = inet_addr(ip.c_str());
    m_stListen.sin_port = htons(port);

    int16_t res = bind(m_sockfd, (const struct sockaddr*)&m_stListen, sizeof(m_stListen));
    if(res == -1)
    {
        perror("bind failed");
        return 0;
    }
    return 1;
}

void CSocket::ReceiveData()
{
    uint8_t buf[1500] = {0};
    struct sockaddr_in client;
    memset(&client, 0, sizeof(client));
    while(1)
    {
        socklen_t len = sizeof(client);
        int16_t res = recvfrom(m_sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &len);
        if(res >= 0)
        {
            cout << buf << endl;
        }
        else
        {
            cout << "receive data failed" << endl;
        }
    }
}

string CSocket::SendArpRequest(string src_ip, string src_mac, string dst_ip)
{
    struct arppacket
    {
        struct ether_header eh;//以太网的头部
        struct ether_arp ea;//arp包数据结构
        uint8_t padding[18];//填充位,ARP包的最小长度是60个字节,不包括以太网的帧的CRC校验和
    } arpreq;

    uint32_t l_src_ip = inet_addr(src_ip.c_str());
    uint32_t l_dst_ip = inet_addr(dst_ip.c_str());

    int16_t sock_arpreq = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP));
    if(sock_arpreq == -1) 
    {
        perror("cannot create ARP socket");
        close(sock_arpreq);
        return NULL;
    }

    int16_t flag = true;
    setsockopt(sock_arpreq, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int16_t));

    memset(&arpreq, 0, sizeof(arpreq));
    // 以太网头
    string dst_mac = "FF:FF:FF:FF:FF:FF";
    memcpy(arpreq.eh.ether_dhost, ether_aton(dst_mac.c_str()), ETH_ALEN);//  6 bytes
    memcpy(arpreq.eh.ether_shost, ether_aton(src_mac.c_str()), ETH_ALEN);//  6 bytes
    arpreq.eh.ether_type = htons(ETHERTYPE_ARP);//协议类型ARP协议  2 bytes

    // arp数据包
    arpreq.ea.arp_hrd = htons(ARPHRD_ETHER);//硬件类型  2 bytes
    arpreq.ea.arp_pro = htons(ETHERTYPE_IP);//协议类型  2 bytes
    arpreq.ea.arp_hln = ETH_ALEN;//MAC地址长度6字节     1 byte
    arpreq.ea.arp_pln = 4;//IP地址长度                  1 byte
    arpreq.ea.arp_op = htons(ARPOP_REQUEST);//操作码,ARP请求包  2 bytes
    memcpy(arpreq.ea.arp_sha, ether_aton(src_mac.c_str()), ETH_ALEN);//源MAC   6 bytes
    memcpy(arpreq.ea.arp_spa, &l_src_ip, 4);//源IP     4 bytes
    memcpy(arpreq.ea.arp_tha, ether_aton(dst_mac.c_str()), ETH_ALEN);//目的MAC  6 bytes
    memcpy(arpreq.ea.arp_tpa, &l_dst_ip, 4); // 目的ip  4 bytes
    struct sockaddr_ll dst_info;
    memset(&dst_info, 0, sizeof(dst_info));
    dst_info.sll_family = PF_PACKET;
    dst_info.sll_ifindex = if_nametoindex(m_NIC_name.c_str());//返回对应接口名的编号

    int res = sendto(sock_arpreq,&arpreq,sizeof(arpreq),0,(struct sockaddr*)&dst_info,sizeof(dst_info));//发送arp请求包
    if(res == -1)
    {
        perror("arp request sendto failed");
        close(sock_arpreq);
        return NULL;
    }

    //接收ARP响应包
    uint8_t buffer[ETH_FRAME_LEN];
    memset(buffer, 0, ETH_FRAME_LEN);
    struct sockaddr_ll client;
    memset(&client, 0, sizeof(client));
    socklen_t len=sizeof(client);
    int sock_arprecv = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));//只接受发往本机的ARP帧
    if(sock_arprecv == -1)
    {
        perror("create ARP recv socket failed");
        close(sock_arpreq);
        return NULL;
    }
    
    while(1)
    {
        res = recvfrom(sock_arprecv,buffer,ETH_FRAME_LEN,0,(struct sockaddr*)&client,&len);
        if(res == -1)
        {
            perror("arp request recvfrom failed");
            break;
        }        
        else
        {
            struct ether_header *eHeader=(struct ether_header*)buffer;
            dst_mac = ether_ntoa((struct ether_addr*)eHeader->ether_shost);
            break;
        }
    }

    close(sock_arpreq);
    close(sock_arprecv);
    return dst_mac;
}

int8_t CSocket::Init(string nic, uint16_t port)
{
    int16_t sock_localhost;
    string local_ipaddr;
    
    struct sockaddr_in *sin;
    struct ifreq ifr_localhost;

    sock_localhost = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_localhost == -1)
    {
        perror("create localhost socket failed");
        return 0;
    }

    // 本地网卡
    m_NIC_name = nic;

    memset(&ifr_localhost, 0, sizeof(ifr_localhost));
    strncpy(ifr_localhost.ifr_name, m_NIC_name.c_str(), sizeof(ifr_localhost.ifr_name)-1);

    // 获取本地mtu
    if(ioctl(sock_localhost, SIOCGIFMTU, &ifr_localhost) == -1)
    {
        perror("cannot get local mtu");
        close(sock_localhost);
        return 0;
    }
    m_mtu = ifr_localhost.ifr_mtu;
    printf("localhost MTU: %d\n", m_mtu);

    // 获取本地ip
    if(ioctl(sock_localhost, SIOCGIFADDR, &ifr_localhost) == -1)
    {
        perror("cannot get local ip");
        close(sock_localhost);
        return 0;
    }
    sin = (struct sockaddr_in*)&ifr_localhost.ifr_addr;
    m_src_ip = inet_ntoa(sin->sin_addr);
    printf("src ip: %s\n",m_src_ip.c_str());

    // 本地端口
    m_src_port = port;
    printf("src port: %d\n",m_src_port);

    // 获取本地mac
    if(ioctl(sock_localhost, SIOCGIFHWADDR, &ifr_localhost) == -1)
    {
        perror("cannot get local mac");
        close(sock_localhost);
        return 0;
    }
    m_src_mac = ether_ntoa((struct ether_addr*)ifr_localhost.ifr_hwaddr.sa_data);
    printf("src mac: %s\n", m_src_mac.c_str());

    // 用随机数初始化数据包id
    srand((int16_t)time(0));
    m_pkt_id = rand()%10000;

    close(sock_localhost);
    return 1;
}
void CSocket::ConnectTo(string ip, uint16_t port)
{
    m_dst_ip = ip;
    m_dst_port = port;
    printf("dst ip: %s\n", m_dst_ip.c_str());
    printf("dst port: %d\n", m_dst_port);

    // 用arp请求获取目的mac
    m_dst_mac = SendArpRequest(m_src_ip,m_src_mac,m_dst_ip);
    printf("dst mac: %s\n", m_dst_mac.c_str());
}

void CSocket::SendData(string msg)
{
    uint16_t dataLen = strlen(msg.c_str());
    if(dataLen % 2 == 1)
    {
        msg.append("\0");
        dataLen++;
    }
    printf("data length: %d\n",dataLen);
    if(m_type == MAC_RAW)
    {
        struct ether_header ethHeader;
        struct ip ipHeader;
        struct udphdr udpHeader;
        struct psdhdr
        {
            struct in_addr src_ip;
            struct in_addr dst_ip;
            uint8_t padding;
            uint8_t proto;
            uint16_t len;
        }psdHeader;

        uint8_t ethHdrLen = sizeof(ethHeader);
        uint8_t ipHdrLen = sizeof(ipHeader);
        uint8_t udpHdrLen = sizeof(udpHeader);
        uint8_t psdHdrLen = sizeof(psdHeader);
        uint32_t l_ip_src = inet_addr(m_src_ip.c_str());
        uint32_t l_ip_dst = inet_addr(m_dst_ip.c_str());

        //  以太网头
        memcpy(ethHeader.ether_dhost, ether_aton(m_dst_mac.c_str()), 6);// 6 bytes
        memcpy(ethHeader.ether_shost, ether_aton(m_src_mac.c_str()), 6);// 6 bytes
        ethHeader.ether_type = htons(ETHERTYPE_IP);// 2 bytes

        // ip头
        ipHeader.ip_v = 4;// 0.5 byte
        ipHeader.ip_hl = 5;// 0.5 byte
        ipHeader.ip_tos = 0;// 1 byte
        ipHeader.ip_len = 0;// 2 byte
        ipHeader.ip_id = htons(m_pkt_id);// 2 bytes
        ipHeader.ip_off = 0;// 2 bytes
        ipHeader.ip_ttl = 64;// 1 byte
        ipHeader.ip_p = 17; // 1 byte
        ipHeader.ip_sum = 0;// 2 bytes     
        memcpy(&ipHeader.ip_src, &l_ip_src, 4);// 4 bytes
        memcpy(&ipHeader.ip_dst, &l_ip_dst, 4);// 4 bytes

        // udp头
        udpHeader.source = htons(m_src_port);// 2 bytes
        udpHeader.dest = htons(m_dst_port);// 2 bytes
        udpHeader.len = htons(udpHdrLen+dataLen);// 2 bytes
        udpHeader.check = 0;// 2 bytes

        // 伪首部  udp
        memcpy(&psdHeader.src_ip, &l_ip_src, 4);// 4 bytes
        memcpy(&psdHeader.dst_ip, &l_ip_dst, 4);// 4 bytes
        psdHeader.padding = 0;// 1 byte
        psdHeader.proto = 17;// 1 byte
        psdHeader.len = htons(udpHdrLen+dataLen);// 2 bytes

        // udp数据校验
        uint8_t* udp_packet = new uint8_t[psdHdrLen+udpHdrLen+dataLen];
        memcpy(udp_packet, &psdHeader, psdHdrLen);
        memcpy(udp_packet+psdHdrLen, &udpHeader, udpHdrLen);
        memcpy(udp_packet+psdHdrLen+udpHdrLen, msg.c_str(), dataLen);
        uint16_t udp_sum = htons(CheckSum((uint16_t*)udp_packet, (psdHdrLen+udpHdrLen+dataLen)/2));
        udpHeader.check = udp_sum;

        struct sockaddr_ll sll;
        struct ifreq req;

        strncpy(req.ifr_name, m_NIC_name.c_str(), IFNAMSIZ);
        int16_t res = ioctl(m_sockfd, SIOCGIFINDEX, &req);
        if(res == -1)
        {
            perror("ioctl");
            close(m_sockfd);
            return;
        }
        memset(&sll, 0, sizeof(sll));
        sll.sll_ifindex = req.ifr_ifindex;

        uint32_t msgLen = ethHdrLen+ipHdrLen+udpHdrLen+dataLen;
        if(msgLen < ETHER_FRAME_MIN)
        {
            msgLen = ETHER_FRAME_MIN;
        }
        
        uint16_t mtuReal = m_mtu - (m_mtu-ipHdrLen) % 8;// 如果分片,每一个包的数据量都是8的倍数
        uint16_t pkt_num = (udpHdrLen+dataLen) / (mtuReal-ipHdrLen) + 1;// 发送的包数量
        printf("packet number: %d\n", pkt_num);
        uint8_t* ipData = new uint8_t[udpHdrLen+dataLen];// 组ip数据包的数据部分
        memset(ipData, 0, udpHdrLen+dataLen);
        memcpy(ipData, &udpHeader, udpHdrLen);
        memcpy(ipData+udpHdrLen, msg.c_str(), dataLen);
        
        for(uint16_t i=1; i<=pkt_num; i++)
        {
            uint16_t ipData_len = 0;// 数据报中ip头之后的数据长度(不含ip头)
            uint16_t ip_off = 0;
            if(i == pkt_num)// 最后一包
            {
                ipData_len = (udpHdrLen+dataLen) % (mtuReal-ipHdrLen);
                ip_off &= 0xdfff;// MF = 0
            }
            else
            {
                ipData_len = mtuReal - ipHdrLen;
                ip_off |= 0x2000;// MF = 1
            }
            // 计算ip数据包长度
            ipHeader.ip_len = htons(ipData_len+ipHdrLen);
            printf("ip packet %d, len: %d\n", i, ipData_len+ipHdrLen);
            // 计算偏移量
            uint16_t offset = (i-1) * (mtuReal-ipHdrLen) >> 3;
            ip_off |= offset;
            ipHeader.ip_off = htons(ip_off);
            printf("offset: %d\n", offset);
            // ip头校验
            ipHeader.ip_sum = 0;// 这里一定要先置0
            uint8_t* ip_packet = new uint8_t[ipHdrLen];
            memset(ip_packet, 0, ipHdrLen);
            memcpy(ip_packet, &ipHeader, ipHdrLen);
            uint16_t ip_sum = CheckSum((uint16_t*)ip_packet, ipHdrLen/2);
            ipHeader.ip_sum = htons(ip_sum);

            uint16_t pkt_len = ethHdrLen+ipHdrLen+ipData_len;
            uint8_t* send_msg = new uint8_t[pkt_len];
            memset(send_msg, 0, pkt_len);
            memcpy(send_msg, &ethHeader, ethHdrLen);
            memcpy(send_msg+ethHdrLen, &ipHeader, ipHdrLen);
            memcpy(send_msg+ethHdrLen+ipHdrLen, ipData+(offset<<3), ipData_len);

            res = sendto(m_sockfd, send_msg, pkt_len, 0 , (struct sockaddr *)&sll, sizeof(sll));
            if(res == -1)
            {
                perror("sendData sendto");
            }
            
            delete ip_packet;
            delete send_msg;
        }

    }
    else if(m_type == UDP)
    {
        struct sockaddr_in dstInfo;
        memset(&dstInfo, 0, sizeof(dstInfo));
        dstInfo.sin_family = AF_INET;
        dstInfo.sin_port = htons(m_dst_port);
        dstInfo.sin_addr.s_addr = inet_addr(m_dst_ip.c_str());

        connect(m_sockfd, (struct sockaddr*)&dstInfo, sizeof(dstInfo));
        int16_t res = send(m_sockfd, msg.c_str(), dataLen, 0);
        //int16_t res = sendto(m_sockfd, msg.c_str(), dataLen, 0, (struct sockaddr*)&dstInfo, sizeof(dstInfo));
        if(res == -1)
        {
            perror("udp sendto failed");
        }
    }
    m_pkt_id++;
}

/*
uint16_t CSocket::CheckSum(uint8_t* buf, uint16_t len)
{
    uint32_t sum;
    for(sum=0; len>0; len--)
    {
        sum += *buf;
        buf++;
    }
    sum = (sum>>16) + (sum&0xffff);
	sum += (sum>>16);
	return ~sum;
}*/

// 校验一定要用16位的指针,求和时要把主机字节序转成网络字节序
uint16_t CSocket::CheckSum(uint16_t* buf, uint16_t len)
{
    uint32_t sum;
    for(sum=0; len>0; len--)
    {
        sum += htons(*buf);
        buf++;
    }
    sum = (sum>>16) + (sum&0xffff);
	sum += (sum>>16);
	return ~sum;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值