使用本机C的IPv6原始套接字编程

时间:2011-11-02 12:50:36

标签: sockets ipv6

我正在研究IPv6,需要从头开始制作IPv6数据包并将其放入缓冲区。不幸的是,我对C没有多少经验。从一个教程我通过定义

成功地完成了与IPv4相同的事情
struct ipheader {
 unsigned char      iph_ihl:5, /* Little-endian */
                iph_ver:4;
 unsigned char      iph_tos;
 unsigned short int iph_len;
 unsigned short int iph_ident;
 unsigned char      iph_flags;
 unsigned short int iph_offset;
 unsigned char      iph_ttl;
 unsigned char      iph_protocol;
 unsigned short int iph_chksum;
 unsigned int       iph_sourceip;
 unsigned int       iph_destip;
};

/* Structure of a TCP header */
struct tcpheader {
 unsigned short int tcph_srcport;
 unsigned short int tcph_destport;
 unsigned int       tcph_seqnum;
 unsigned int       tcph_acknum;
 unsigned char      tcph_reserved:4, tcph_offset:4;
 // unsigned char tcph_flags;
  unsigned int
       tcp_res1:4,       /*little-endian*/
       tcph_hlen:4,      /*length of tcp header in 32-bit words*/
       tcph_fin:1,       /*Finish flag "fin"*/
       tcph_syn:1,       /*Synchronize sequence numbers to start a connection*/
       tcph_rst:1,       /*Reset flag */
       tcph_psh:1,       /*Push, sends data to the application*/
       tcph_ack:1,       /*acknowledge*/
       tcph_urg:1,       /*urgent pointer*/
       tcph_res2:2;
 unsigned short int tcph_win;
 unsigned short int tcph_chksum;
 unsigned short int tcph_urgptr;
};

并像这样填写数据包内容:

// IP structure
   ip->iph_ihl = 5;
   ip->iph_ver = 6;
   ip->iph_tos = 16;
   ip->iph_len = sizeof (struct ipheader) + sizeof (struct tcpheader);
   ip->iph_ident = htons(54321);
   ip->iph_offset = 0;
   ip->iph_ttl = 64;
   ip->iph_protocol = 6; // TCP
   ip->iph_chksum = 0; // Done by kernel

   // Source IP, modify as needed, spoofed, we accept through command line argument
   ip->iph_sourceip = inet_addr("1922.168.1.128");
   // Destination IP, modify as needed, but here we accept through command line argument
   ip->iph_destip = inet_addr(1922.168.1.1);

   // The TCP structure. The source port, spoofed, we accept through the command line
   tcp->tcph_srcport = htons(atoi("1024"));
   // The destination port, we accept through command line
   tcp->tcph_destport = htons(atoi("4201"));
   tcp->tcph_seqnum = htons(1);
   tcp->tcph_acknum = 0;
   tcp->tcph_offset = 5;
   tcp->tcph_syn = 1;
   tcp->tcph_ack = 0;
   tcp->tcph_win = htons(32767);
   tcp->tcph_chksum = 0; // Done by kernel
   tcp->tcph_urgptr = 0;
   // IP checksum calculation
   ip->iph_chksum = csum((unsigned short *) buffer, (sizeof (struct ipheader) + sizeof (struct tcpheader)));

然而对于IPv6我没有找到类似的方法。我已经找到的是来自IETF的这个结构,

struct ip6_hdr {

   union {

      struct ip6_hdrctl {
         uint32_t ip6_un1_flow; /* 4 bits version, 8 bits TC, 20 bits
                                      flow-ID */
         uint16_t ip6_un1_plen; /* payload length */
         uint8_t ip6_un1_nxt; /* next header */
         uint8_t ip6_un1_hlim; /* hop limit */
      } ip6_un1;

      uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits
                                      tclass */
   } ip6_ctlun;
   struct in6_addr ip6_src; /* source address */
   struct in6_addr ip6_dst; /* destination address */
};

但我不知道如何填写信息,例如,如何从2001发送TCP / SYN:220:806:22:aacc:ff:fe00:1端口1024到2001:220:806: 21 :: 4端口1025?

有人可以帮助我吗?还是有任何参考资料?

非常感谢你。

这是我到目前为止所做的,但是Wireshark捕获的代码和真实数据包之间存在不匹配(如下面的评论中所述)。我不确定是否可以在评论部分发布长代码,所以我只是编辑我的问题。

任何人都可以提供帮助吗?

#define PCKT_LEN 2000

int main(void) {
   unsigned char buffer[PCKT_LEN];
   int s;
   struct sockaddr_in6 din;
   struct ipv6_header *ip = (struct ipv6_header *) buffer;
   struct tcpheader *tcp = (struct tcpheader *) (buffer + sizeof (struct ipv6_header));

   memset(buffer, 0, PCKT_LEN);
   din.sin6_family = AF_INET6;
   din.sin6_port = htons(0);
   inet_pton(AF_INET6, "::1", &(din.sin6_addr)); // For routing 

   ip->version = 6;
   ip->traffic_class = 0;
   ip->flow_label = 0;
   ip->length = 40;
   ip->next_header = 6;
   ip->hop_limit = 64;
   inet_pton(AF_INET6, "::1", &(ip->dst)); // IPv6 
   inet_pton(AF_INET6, "::1", &(ip->src)); // IPv6

   tcp->tcph_srcport = htons(atoi("11111"));
   tcp->tcph_destport = htons(atoi("13"));
   tcp->tcph_seqnum = htons(0);
   tcp->tcph_acknum = 0;
   tcp->tcph_offset = 5;
   tcp->tcph_syn = 1;
   tcp->tcph_ack = 0;
   tcp->tcph_win = htons(32752);
   tcp->tcph_chksum = 0; // Done by kernel
   tcp->tcph_urgptr = 0;

   s = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
   if (s < 0) {
      perror("socket()");
      return 1;
   }
   unsigned short int packet_len = sizeof (struct ipv6_header) + sizeof (struct tcpheader);
   if (sendto(s, buffer, packet_len, 0, (struct sockaddr*) &din, sizeof (din)) == -1) {
      perror("sendto()");
      close(s);
      return 1;
   }
   close(s);
   return 0;
}

2 个答案:

答案 0 :(得分:2)

也许this文章可以帮助您入门?

修改 使用上面链接的维基百科文章,我创建了这个结构(不知道某些字段的含义):

struct ipv6_header
{
    unsigned int
        version : 4,
        traffic_class : 8,
        flow_label : 20;
    uint16_t length;
    uint8_t  next_header;
    uint8_t  hop_limit;
    struct in6_addr src;
    struct in6_addr dst;
};

与示例中为IPv4构建头结构的方式没有什么不同。只需按正确的顺序创建一个包含字段的struct,并使用正确的值填充它。

对TCP标头执行相同的操作。

答案 1 :(得分:0)

不幸的是,ipv6 RFC不能提供与ipv4相同的原始套接字接口。从我所看到的创建ipv6数据包开始,您必须更深入一级并使用AF_PACKET套接字发送包括您的ipv6数据包在内的以太网帧。