为了简化流程,暂不做三次握手的过程,直接发单个HTTP GET请求的数据包.
思路就是,以太网头 + ip头 + tcp头 + http数据
用telnet测试
away@aways-iMac:~$ telnet baidu.com 80
Trying 123.125.115.110...
Connected to baidu.com.
Escape character is '^]'.
GET / HTTP/1.0
HTTP/1.1 200 OK
Date: Fri, 04 Jan 2019 01:36:33 GMT
Server: Apache
Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
ETag: "51-47cf7e6ee8400"
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Sat, 05 Jan 2019 01:36:33 GMT
Connection: Close
Content-Type: text/html
<html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>
Connection closed by foreign host.
实现代码如下
// MyTcp.h
#ifndef my_tcp_h
#define my_tcp_h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if_packet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <string>
#include <iostream>
struct psdhdr {
uint32_t src_addr;
uint32_t dst_addr;
uint8_t zero;
uint8_t protocol;
uint16_t tcp_len;
};
class MyTcp
{
public:
MyTcp();
~MyTcp();
void setNic(std::string nic, int src_port);
void setDst(std::string dst_ip, int dst_port);
int send(std::string data);
private:
int sockfd;
struct sockaddr_ll dst_sock_addr;
struct ether_header eth;
struct iphdr iph;
struct tcphdr tcph;
struct psdhdr psdh;
int src_addr;
int dst_addr;
uint16_t getCkSum(uint16_t *addr, int len);
};
#endif
//MyTcp.cpp
#include "MyTcp.h"
MyTcp::MyTcp()
{
sockfd = socket(AF_PACKET, SOCK_RAW, 0);
if (sockfd == -1)
{
perror("socket");
exit(1);
}
}
MyTcp::~MyTcp()
{
}
void MyTcp::setNic(std::string nic, int src_port)
{
struct ifreq if_index, if_mac, if_ip;
bzero(&if_index, sizeof(struct ifreq));
bzero(&if_mac, sizeof(struct ifreq));
bzero(&if_ip, sizeof(struct ifreq));
strcpy(if_index.ifr_name, nic.c_str());
strcpy(if_mac.ifr_name, nic.c_str());
strcpy(if_ip.ifr_name, nic.c_str());
if (ioctl(sockfd, SIOCGIFINDEX, &if_index) < 0)
{
perror("siocgifindex");
exit(1);
}
if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
{
perror("siocgifhwaddr");
exit(1);
}
if (ioctl(sockfd, SIOCGIFADDR, &if_ip) < 0)
{
perror("siocgifaddr");
exit(1);
}
for (int i = 0; i< 6; i++)
eth.ether_shost[i] = if_mac.ifr_hwaddr.sa_data[i];
eth.ether_type = htons(ETH_P_IP);
src_addr = ((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr.s_addr;
tcph.source = htons(src_port);
bzero(&dst_sock_addr, sizeof(dst_sock_addr));
dst_sock_addr.sll_ifindex = if_index.ifr_ifindex;
dst_sock_addr.sll_halen = ETH_ALEN;
dst_sock_addr.sll_protocol = ETH_P_IP;
}
void MyTcp::setDst(std::string dst_ip, int dst_port)
{
dst_addr = inet_addr(dst_ip.c_str());
eth.ether_dhost[0] = 0xd0;
eth.ether_dhost[1] = 0x17;
eth.ether_dhost[2] = 0xc2;
eth.ether_dhost[3] = 0xd3;
eth.ether_dhost[4] = 0xb5;
eth.ether_dhost[5] = 0xc9;
iph.ihl = 5;
iph.version = 4;
iph.tos = IPTOS_LOWDELAY;
iph.id = htons(54321);
iph.ttl = 64;
iph.protocol = IPPROTO_TCP;
iph.saddr = src_addr;
iph.daddr = dst_addr;
iph.tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr));
iph.check = 0;
tcph.dest = htons(dst_port);
tcph.seq = 0x0;
tcph.ack_seq = 0x0;
tcph.doff = 5;
tcph.res1 = 0;
tcph.urg = 0;
tcph.ack = 0;
tcph.psh = 1;
tcph.rst = 0;
tcph.syn = 0;
tcph.fin = 0;
tcph.window = htons(155);
tcph.check = 0;
tcph.urg_ptr = 0;
psdh.src_addr = src_addr;
psdh.dst_addr = dst_addr;
psdh.zero = 0;
psdh.protocol = IPPROTO_TCP;
// psdh.tcp_len = htons(sizeof(struct tcphdr));
}
int MyTcp::send(std::string data)
{
int packet_len, psd_packet_len;
char * packet, * psd_packet;
packet_len = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr) + data.size();
psd_packet_len = sizeof(struct psdhdr) + sizeof(struct tcphdr) + data.size();
packet = (char *)malloc(packet_len);
if (packet == NULL)
{
perror("out of memory");
exit(1);
}
psd_packet = (char *)malloc(psd_packet_len);
if (psd_packet == NULL)
{
free(packet);
perror("out of memory");
exit(1);
}
iph.tot_len = htons(packet_len - sizeof(eth));
iph.check = getCkSum((uint16_t*)&iph, sizeof(iphdr)); // /2?
psdh.tcp_len = htons(sizeof(tcph) + data.size());
memset(psd_packet, 0, psd_packet_len);
memcpy(psd_packet, (char *)&psdh, sizeof(psdh));
memcpy(psd_packet + sizeof(psdh), (char *)&tcph, sizeof(tcph));
if (!data.empty())
{
memcpy(psd_packet + sizeof(psdh) + sizeof(tcph), data.c_str(), data.size());
}
tcph.check = getCkSum((uint16_t *)psd_packet, psd_packet_len);
std::cout << "tcp cksum: " << tcph.check <<std::endl;
memset(packet, 0, packet_len);
memcpy(packet, (char *)ð, sizeof(eth));
memcpy(packet + sizeof(eth), (char *)&iph, sizeof(iph));
memcpy(packet + sizeof(eth) + sizeof(iph), (char *)&tcph, sizeof(tcph));
if (!data.empty())
{
memcpy(packet + packet_len - data.size(), data.c_str(), data.size());
}
if (sendto(sockfd, packet, packet_len, 0, (struct sockaddr *)&dst_sock_addr,sizeof(dst_sock_addr)) < 0)
{
// perror("send failed");
std::cout << "send failed\n";
}
free(packet);
free(psd_packet);
return 0;
}
uint16_t MyTcp::getCkSum(uint16_t *addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w ;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}
// main.cpp
#include "MyTcp.h"
int main()
{
MyTcp mytcp;
mytcp.setNic("ens33", 75737);
mytcp.setDst("192.168.88.222", 80);
mytcp.send("GET / HTTP/1.0\r\n\r\n");
}
抓包结果如下:
tcpdump -i ens33 host 192.168.88.222 and host 192.168.88.223 -e -v and tcp
11:07:29.909750 00:0c:29:a2:2f:82 (oui Unknown) > d0:17:c2:d3:b5:c9 (oui Unknown), ethertype IPv4 (0x0800), length 72: (tos 0x10, ttl 64, id 54321, offset 0, flags [none], proto TCP (6), length 58)
192.168.88.223.10201 > 192.168.88.222.http: Flags [P], cksum 0x7558 (correct), seq 0:18, win 155, length 18: HTTP, length: 18
GET / HTTP/1.0