Blog.chinaunix.net/uid-2637960… \

Look at the source code:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <linux/sockios.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <net/if.h>


#define DESTPORT 80
#define LOCALPORT 0x8888

/* Buffer Size */
#define SND_BUF_SIZE 1024*5

/* Buffer */
static int g_iSendBufSize = SND_BUF_SIZE;

static int g_iRecvBufSize = SND_BUF_SIZE;

static unsigned long seqno, ackno;    //save sequence no and ackment no 

extern int errno;

/* Prseuheader */
struct prseuheader
{
    unsigned long s_addr;
    unsigned long d_addr;
    unsigned char zero;
    unsigned char prototp;
    unsigned short len;
};

/* IP Head */
struct IP_Head
{
    unsigned char length:4;
    unsigned char version:4;
    unsigned char tos;
    unsigned short total_length;
    unsigned short id;
    unsigned short flagoff;
    unsigned char ttl;
    unsigned char protocol;
    unsigned short chksum;
    unsigned int source;
    unsigned int dest;
};

/* TCP Head */
struct TCP_Head
{
    unsigned short source_port;
    unsigned short dest_port;
    unsigned int seqno;
    unsigned int ackno;
    unsigned char rev1:4;
    unsigned char len:4;
    unsigned char fin:1;
    unsigned char syn:1;
    unsigned char rst:1;
    unsigned char psh:1;
    unsigned char ack:1;
    unsigned char urg:1;
    unsigned char rev2:2;
    unsigned short winsize;
    unsigned short chksum;
    unsigned short urgent;
};

/* Check sum */
unsigned short
checksum (unsigned short *buffer, int size)
{
    unsigned long cksum = 0;

    while (size > 1)
    {
        cksum += *buffer++;
        size -= 2;
    }
    if (size)
    {
        cksum += *(u_char *) buffer;
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);
    return (unsigned short) (~cksum);
}

//Create SOCK_RAW
int
creat_raw ()
{
    int sk;

    sk = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
    if (sk < 0)
    {
        strerror (errno);
        return -1;
    }
    return sk;
}

//Set sock option
int
set_promisc (int sk)
{
//    struct ifreq ifr;
//
//    strcpy (ifr.ifr_name, "eth0");
//    if ((ioctl (sk, SIOCGIFFLAGS, &ifr) == -1))
//    {
//        strerror (errno);
//    }
//    ifr.ifr_flags |= IFF_PROMISC;
//    if (ioctl (sk, SIOCSIFFLAGS, &ifr) == -1)
//    {
//        strerror (errno);
//    }
    return 0;
}

/* fill ip head */
int
fill_iph (unsigned char buffer[])
{
    struct IP_Head *pIph;

    int total_len = sizeof (struct IP_Head) + sizeof (struct TCP_Head);


    pIph = (struct IP_Head *) buffer;

    pIph->length = 5;
    pIph->version = 4;
    pIph->tos = 0;
    pIph->total_length = htons (total_len);
    pIph->id = 0;
    pIph->flagoff = htons (0x4000);
    pIph->ttl = 255;
    pIph->protocol = 6;
    pIph->chksum = 0;
    pIph->source = inet_addr ("192.168.1.2");    //Local PC 写上自己网卡的IP 
    pIph->dest = inet_addr ("119.75.217.56");    //remote PC 服务器的IP
    pIph->chksum =
        checksum ((unsigned short *) pIph, sizeof (struct IP_Head));

    return 0;
}

/* fill tcp head */
int
fill_tcp (unsigned char buffer[], unsigned char control)
{
    struct TCP_Head *pTcph;

    pTcph = (struct TCP_Head *) (buffer + sizeof (struct IP_Head));

    pTcph->source_port = htons (LOCALPORT);
    pTcph->dest_port = htons (DESTPORT);
    pTcph->seqno = htonl (seqno);
    pTcph->ackno = htonl (ackno);
    pTcph->rev1 = 0;
    pTcph->len = 5;
    pTcph->fin = control & 0x01 ? 1 : 0;
    pTcph->syn = control & 0x02 ? 1 : 0;
    pTcph->rst = control & 0x04 ? 1 : 0;
    pTcph->psh = control & 0x10 ? 1 : 0;
    pTcph->ack = control & 0x20 ? 1 : 0;
    pTcph->urg = control & 0x40 ? 1 : 0;
    pTcph->rev2 = 0;
    pTcph->winsize = htons (1000);
    pTcph->chksum = 0;
    pTcph->urgent = 0;

    return 0;
}

//send tcp packet
int
sendto_packet (int sk, unsigned char buffer[])
{
    int iRet;

    struct IP_Head *pIph;

    struct TCP_Head *pTcph;

    struct prseuheader theheader;

    char tcpbuff[32];            //it include prseuheader and tcp head

    int total_len = sizeof (struct IP_Head) + sizeof (struct TCP_Head);

    struct sockaddr_ll addr;

    pIph = (struct IP_Head *) buffer;
    pTcph = (struct TCP_Head *) (buffer + sizeof (struct IP_Head));

    bzero (&addr, sizeof (struct sockaddr_ll));
    addr.sll_family = htons (PF_PACKET);
    addr.sll_protocol = htons (ETH_P_IP);
    addr.sll_ifindex = if_nametoindex ("eth0");//换成你的网口的名字
    addr.sll_addr[0] = 0x08;//
    addr.sll_addr[1] = 0x10;//
    addr.sll_addr[2] = 0x74;//换成你的网关的MAC地址就行了
    addr.sll_addr[3] = 0xC9;//
    addr.sll_addr[4] = 0x0B;//
    addr.sll_addr[5] = 0x16;//

    bzero (tcpbuff, 32);
    theheader.s_addr = pIph->source;
    theheader.d_addr = pIph->dest;
    theheader.zero = 0;
    theheader.prototp = 6;
    theheader.len = htons (20);    //the size of TCP head

    memcpy (tcpbuff, &theheader, 12);
    memcpy (tcpbuff + 12, pTcph, 20);
    pTcph->chksum = 0;
    pTcph->chksum = checksum ((unsigned short *) tcpbuff, 32);

    iRet =
        sendto (sk, buffer, total_len, 0, (struct sockaddr *) &addr,
                sizeof (struct sockaddr_ll));
    if (iRet < 0)
    {
        strerror (errno);
        return -1;
    }

    return 0;
}

//receive tcp packet and display IP Head and TCP Head
int
recvfrom_packet (int sk, unsigned char buffer[])
{
    int iRet;

    int lenfrom = sizeof (struct sockaddr_in);

    struct sockaddr_in addr;

    struct in_addr in;

    struct IP_Head *pIph;

    struct TCP_Head *pTcph;

    fd_set fdR;

    struct timeval timeout;

    //Set Non-block
    bzero (&addr, sizeof (struct sockaddr_in));
    while (1)
    {
        iRet =
            recvfrom (sk, buffer, g_iRecvBufSize, 0,
                     (struct sockaddr *) &addr, &lenfrom);
        if (iRet < 0)
        {
            printf ("error recvfrom\n");
            return -1;
        }
        else
        {
            pIph = (struct IP_Head *) buffer;
            pTcph = (struct TCP_Head *) (buffer + pIph->length * 4);
            if ((pIph->protocol!=6) || ntohs (pTcph->dest_port) != 0x8888)
            {
                continue;
            }
            //Display IP Head 
            printf ("Parse IP......\n");
            printf ("length:%x\n", pIph->length);
            printf ("version:%x\n", pIph->version);
            printf ("tos:%x\n", pIph->tos);
            printf ("total_length:%d\n", ntohs (pIph->total_length));
            printf ("id:%d\n", ntohs (pIph->id));
            printf ("flagoff:%x\n", ntohs (pIph->flagoff));
            printf ("ttl:%d\n", pIph->ttl);
            printf ("protocol:%d\n", pIph->protocol);
            printf ("cksum:%x\n", ntohs (pIph->chksum));
            in.s_addr = pIph->source;
            printf ("SIP:%s\n", inet_ntoa (in));
            in.s_addr = pIph->dest;
            printf ("DIP:%s\n", inet_ntoa (in));

            //Display TCP Head
            printf ("Parse TCP......\n");
            printf ("source_port:%d\n", ntohs (pTcph->source_port));
            printf ("dest_port:%d\n", ntohs (pTcph->dest_port));
            printf ("seqno:%d\n", ntohl (pTcph->seqno));
            printf ("ackno:%d\n", ntohl (pTcph->ackno));
            printf ("len:%d\n", pTcph->len);
            printf ("fin:%d\n", pTcph->fin);
            printf ("syn:%d\n", pTcph->syn);
            printf ("rst:%d\n", pTcph->rst);
            printf ("psh:%d\n", pTcph->psh);
            printf ("ack:%d\n", pTcph->ack);
            printf ("urg:%d\n", pTcph->urg);
            printf ("winsize:%d\n", ntohs (pTcph->winsize));
            printf ("urgent:%d\n", ntohs (pTcph->urgent));
            printf ("\n");

            seqno = ntohl (pTcph->seqno);
            ackno = ntohl (pTcph->ackno);
            
            return;
        }
    }
}

//Main 
int
main ()
{
    int sk;

    unsigned char buffers[g_iSendBufSize];    //send buffer 

    unsigned char bufferr[g_iRecvBufSize];    //receive buffer 

    int iRet;

    unsigned long temp;


    sk = creat_raw ();
    if (sk < 0)
    {
        printf ("Creat socket error.\n");
        return -1;
    }


    iRet = set_promisc (sk);
    if (iRet < 0)
    {
        printf ("Set socket promisc error.\n");
        close (sk);
        return -1;
    }

    //the first
    bzero (buffers, g_iSendBufSize);
    bzero (bufferr, g_iRecvBufSize);

    fill_iph (buffers);
    seqno = 0;
    ackno = 0;
    fill_tcp (buffers, 0x02);
    iRet = sendto_packet (sk, buffers);
    if (iRet < 0)
    {
        printf ("Sendto_packet error.\n");
        close (sk);
        return -1;
    }

    iRet = recvfrom_packet (sk, bufferr);
    if (iRet < 0)
    {
        printf ("Recvfrom_packet error.\n");
        printf ("time is over or error opertion \n");
        close (sk);
        return -1;
    }


    //the third
    bzero (buffers, g_iSendBufSize);
    bzero (bufferr, g_iRecvBufSize);

    fill_iph (buffers);
    temp = seqno;
    seqno = ackno;
    ackno = temp + 1;
    fill_tcp (buffers, 0x20);
    iRet = sendto_packet (sk, buffers);
    if (iRet < 0)
    {
        printf ("Sendto_packet error.\n");
        close (sk);
        return -1;
    }

    close (sk);
    return 0;
}
Copy the code

Simulated TCP three handshake, write more rough.

sk = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL)); \

It is mainly to create a PF_PACKET socket, which is the socket of the data link layer, bypass the protocol stack of the system, and then construct the IP layer and TCP layer. Run iptables -I INPUT -j DROP to discard all data. Otherwise, the system protocol stack will return an RST when the server sends a SYN ACK. \

You can open wireshark to view the wireshark effect.

The Packet socket is used to send and receive raw data frames at the MAC layer, thus allowing the user to complete the implementation of various layers above the MAC in user space. It brings great convenience to both the development and testing people. Packet_socket =socket(PF_PACKET,int socket_type,int protocol); socket(PF_PACKET,int socket_type,int protocol); Socket_type can be divided into two types: SOCK_RAW, which is the raw grouping containing the header information of the MAC layer. Of course, this type of socket needs to add its own MAC header (the type is defined in Linux /if_ether.h, ethhdr), and the other type is SOCK_DGRAM, which has the MAC header processing, i.e. the received frame has the header removed. The user does not need to add a header field when sending. Protocol indicates the upper-layer Protocol number sent. For example, if IP is 0x0800, htons(ETH_P_ALL) (whose macro is defined as 0) indicates that all protocols are sent and received. After the socket is created, data can be sent and received using the same recvFROM and sendto functions as UDP. The destination address structure is sockaddr_ll, which is different from the address structure definition of the transport layer. The length of the address is 20 bytes (18 bytes are used in the link layer address of TCP/IP). The address structure of the transport layer is 16 bytes long. Struct Sockaddr_ll {unsigned short sll_family; /* always AF_PACKET */ unsigned short sll_protocol; /* Physical layer protocol */ int sll_ifindex; /* Interface number */ unsigned short sll_hatype; /* Header type */ unsigned char sll_pktType; /* Group type */ unsigned char sll_halen; /* Address length */ unsigned char sll_addr[8]; /* Physical layer address */}; Sll_protocol is a standard Ethernet frame protocol type sorted by network layer defined in the Linux /if_ether.h header file. Sll_ifindex is the interface index number (see netDevice (2)); 0 matches all interfaces (only valid ones are used for binding, of course). Sll_hatype is an ARP hardware address type defined in Linux /if_arp.h. Sll_pkttype contains the group type. Valid grouping types are: PACKET_HOST for local host packets, PACKET_BROADCAST for physical layer broadcast packets, and PACKET_MULTICAST for sending packets to a physical layer multicast address. The PACKET_OTHERHOST sent by a device driver in promiscuous mode to another host is looped back to the PACKET_OUTGOING used by the packet socket. These types are only meaningful for received packets. Sll_addr and SLL_halen include physical layer (such as IEEE 802.3) addresses and address lengths. The exact interpretation depends on the device. When using this socket on hosts with multiple network interfaces, you can use bind to specify the receiving or sending interface. This is the same operation as a TCP socket, but has different connotations. During the binding, sll_protocal and sll_ifindex in the address structure are bound to the protocol number and interface index number respectively. If the interface index number of sll_ifindex is 0, all valid interfaces are used. The sll_ifindex value of the interface can be obtained by ioctl, for example, strcpy(ifr.ifr_name,”eth0″) is the index of the interface named “eth0”; ioctl(fd_packet,SIOCGIFINDEX,&ifr); The obtained value is stored in ifR_ifindex of the IFR structure. Ifr structure type is “struct ifreq” BTW. To obtain the physical address of the interface, also use ioctl to obtain iocTL (FD_packet,SIOCGIFHWADDR,& IFR). The data is stored in ifR_hwAddr. Sa_data of the IFR. Also note that the address length information returned when recvfrom is called is 18 bytes, because sll_ADDR [8] in the sockadDR_LL structure is 8 bytes, and only the first 6 bytes of that are used for the MAC address. The destination address structure needs to be cast to struct sockADDR when sendto is sent, and the specified length must be 20 bytes, not 18 or some other value. In use, I can prepare to receive data frames of this type after specifying the protocol type, but there is a problem that always bothers me, that is, I cannot filter out broadcast frames, and I have to judge whether the destination address is myself after receiving the frame, and then how to judge when USING SOCK_DGRAM? I am exploring and will share with you as soon as there is any new progress. \