免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 8873 | 回复: 25
打印 上一主题 下一主题

关于原始套接字的又一个棘手问题:如何设置TCP确认号 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-10-29 17:24 |只看该作者 |倒序浏览
在开源程序urlfilterd里面,当一个包被DROP时(比如PC的浏览器访问了被家长控制的网页),我利用原始套接字构造了一个IP包(回给浏览器)(请看下面Manju updated begin 和Manju updated end.之间的代码)。问题出在我通过语句:

clew_tcphdr->ack_seq = htonl(payload_len) + tcp->seq;

设置确认号ack_seq。printf打印出来的结果表明该值设置成功,但是抓包发现该值payload_len并没有加上去,导致对端(PC浏览器)不断重传上一次的包。

请问:利用原始套接字,如何才能设置TCP的确认号?

望不吝赐教为盼!

部分代码如下:

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u8        ihl:4,
                version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
        __u8        version:4,
                  ihl:4;
#else
#error        "Please fix <asm/byteorder.h>"
#endif
        __u8        tos;
        __u16        tot_len;
        __u16        id;
        __u16        frag_off;
        __u8        ttl;
        __u8        protocol;
        __u16        check;
        __u32        saddr;
        __u32        daddr;
        /*The options start here. */
};

struct tcphdr {
        __be16        source;
        __be16        dest;
        __be32        seq;
        __be32        ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u16        res1:4,
                doff:4,
                fin:1,
                syn:1,
                rst:1,
                psh:1,
                ack:1,
                urg:1,
                ece:1,
                cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
        __u16        doff:4,
                res1:4,
                cwr:1,
                ece:1,
                urg:1,
                ack:1,
                psh:1,
                rst:1,
                syn:1,
                fin:1;
#else
#error        "Adjust your <asm/byteorder.h> defines"
#endif       
        __be16        window;
        __sum16        check;
        __be16        urg_ptr;
};

static int pkt_decision(struct nfq_data * payload)
{
        char *data;
        char *match, *folder, *url;
        PURL current;
        int payload_offset, data_len;
        struct iphdr *iph;
        struct tcphdr *tcp;
        match = folder = url = NULL;

        data_len = nfq_get_payload(payload, &data);
        if( data_len == -1 )
        {
//printf("data_len == -1!!!!!!!!!!!!!!!, EXIT\n");
                exit(1);
        }
//printf("data_len=%d ", data_len);

        iph = (struct iphdr *)data;
        tcp = (struct tcphdr *)(data + (iph->ihl<<2));

        payload_offset = ((iph->ihl)<<2) + (tcp->doff<<2);
   unsigned int payload_len = (unsigned int)ntohs(iph->tot_len) - ((iph->ihl)<<2) - (tcp->doff<<2);
        match = (char *)(data + payload_offset);

        if(strstr(match, "GET ") == NULL && strstr(match, "POST ") == NULL && strstr(match, "HEAD ") == NULL)
        {
//printf("****NO HTTP INFORMATION!!!\n");
                return PKT_ACCEPT;
        }
//printf("####payload = %s\n\n", match);
        for (current = purl; current != NULL; current = current->next)
        {
                if (current->folder[0] != '\0')
                {
                        folder = strstr(match, current->folder);
                }

                if ( (url = strstr(match, current->website)) != NULL )
                {
                        if (strcmp(listtype, "Exclude") == 0)
                        {
                                if ( (folder != NULL) || (current->folder[0] == '\0') )
                                {
//printf("####This page is blocked by Exclude list!\n");

              // Manju updated begin...
               if (iph->version == 4)
               {
                  printf("%s@%u:%u %u %x %x %u %u\n", __FUNCTION__, __LINE__,
                     iph->version, iph->tot_len, iph->saddr, iph->daddr,
                     tcp->source, tcp->dest); // Manju debug

                  struct sockaddr_in addr;
                  
                  bzero(&addr, sizeof(struct sockaddr_in));
                  addr.sin_family = AF_INET;
                  addr.sin_port = tcp->source;
                  addr.sin_addr.s_addr = iph->saddr;

                  int on=1;
                  
                  int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
                  if (sockfd >= 0)
                  {
                     setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
                     setsockopt(sockfd, IPPROTO_TCP, IP_HDRINCL, &on, sizeof(on));

                     int head_len = sizeof(struct iphdr) + sizeof(struct tcphdr);

                     char http_buf[] = {"HTTP/1.1 302 Found\r\nLocation: http://192.168.0.1/sky_http_limit.html\r\n"};
                     
                     char buf[256];
                     bzero(buf, sizeof(buf));
                     strncpy(buf+head_len, http_buf, sizeof(buf)-head_len);
                     
                     struct iphdr *clew_iphdr = (struct iphdr *)buf;
                     clew_iphdr->version = iph->version;
                     clew_iphdr->ihl = sizeof(struct iphdr)>>2;
                     clew_iphdr->tos = 0;
                     clew_iphdr->tot_len = htons(head_len+strlen(http_buf)+1);
                     clew_iphdr->id = 0;
                     clew_iphdr->frag_off = 0;
                     clew_iphdr->ttl = MAXTTL;
                     clew_iphdr->protocol = IPPROTO_TCP;
                     clew_iphdr->check = 0;
                     clew_iphdr->daddr = iph->saddr;
                     clew_iphdr->saddr = iph->daddr;

                     struct tcphdr *clew_tcphdr = (struct tcphdr *)(buf + sizeof(struct iphdr));
                     clew_tcphdr->source = tcp->dest;
                     clew_tcphdr->dest = tcp->source;
                     clew_tcphdr->seq = tcp->ack_seq;
                     clew_tcphdr->ack_seq = htonl(payload_len) + tcp->seq;
                     printf("clew_tcphdr->ack_seq=%x %x %x %x\n", ntohl(clew_tcphdr->ack_seq), payload_len, ntohl(payload_len), tcp->seq); // Manju debug
                     clew_tcphdr->doff = 5;
                     clew_tcphdr->ack = 1;
                     clew_tcphdr->window = 6501;
                     clew_tcphdr->check = 0;

                     sendto(sockfd, buf, head_len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
                     clew_tcphdr->psh = 1;
                     sendto(sockfd, buf, head_len+strlen(http_buf)+1, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
                     clew_tcphdr->psh = 0;
                     clew_tcphdr->fin = 1;
                     sendto(sockfd, buf, head_len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

                     //close(sockfd);
                  }
               }

               // Manju updated end.

                                       
                                        return PKT_DROP;
                                }
                                else
                                {
//printf("###Website hits but folder no hit in Exclude list! packets pass\n");
                                        return PKT_ACCEPT;
                                }
                        }
                        else
                        {
                                if ( (folder != NULL) || (current->folder[0] == '\0') )
                                {
//printf("####This page is accepted by Include list!");
                                        return PKT_ACCEPT;
                                }
                                else
                                {
//printf("####Website hits but folder no hit in Include list!, packets drop\n");
                                        return PKT_DROP;
                                }
                        }
                }
        }

        if (url == NULL)
        {
                if (strcmp(listtype, "Exclude") == 0)
                {
//printf("~~~~No Url hits!! This page is accepted by Exclude list!\n");
                        return PKT_ACCEPT;
                }
                else
                {
//printf("~~~~No Url hits!! This page is blocked by Include list!\n");
                        return PKT_DROP;
                }
        }

//printf("~~~None of rules can be applied!! Traffic is allowed!!\n");
        return PKT_ACCEPT;
}

论坛徽章:
0
2 [报告]
发表于 2010-10-29 17:45 |只看该作者
附件是用wireshark抓的包。

response_with_ip_packet_fail_marked.JPG (269.01 KB, 下载次数: 40)

response_with_ip_packet_fail_marked.JPG

bug_html-info.rar

77.74 KB, 下载次数: 32

论坛徽章:
0
3 [报告]
发表于 2010-10-30 01:29 |只看该作者
clew_tcphdr->ack_seq = htonl (payload_len + ntohl(tcp->seq)+1);

论坛徽章:
0
4 [报告]
发表于 2010-11-01 10:17 |只看该作者
谢谢“奇门遁甲-lu”!
我按照您的建议改动了,但还是老样子。
附件是新抓的包。
串口打印结果如下:
pkt_decision@179:4 636 c0a80102 774bda2d 1446 80
clew_tcphdr->ack_seq=de5bc12f 254 254 de5bbeda

response_with_ip_packet_fail_101101.rar

1.46 KB, 下载次数: 24

论坛徽章:
0
5 [报告]
发表于 2010-11-01 11:38 |只看该作者
晕,还有checksum的问题

论坛徽章:
0
6 [报告]
发表于 2010-11-01 11:44 |只看该作者
我把checksum设置为0,系统可以自动算出来。通过分析所抓的包,校验和并没有出错。
不过若没有自己计算校验和,是否系统将确认号也代劳了?

论坛徽章:
0
7 [报告]
发表于 2010-11-01 13:47 |只看该作者
本帖最后由 奇门遁甲-lu 于 2010-11-01 22:21 编辑

分析你的数据包:

   按我的方法改了后,ack_seq是对的,但是发现你发出去的数据包src_port 不对,

正确应为80,实际发现是1;
已经有  setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))了,
表明IP头部要自己构造,自然,更上层的网络层头部也要自己构造了,所以
可以不要 setsockopt(sockfd, IPPROTO_TCP, IP_HDRINCL, &on, sizeof(on));
自己构造tcp头部,包括tcp_checksum;另外你的程序3次sendto,
应该每次要修改相应的seq, ack_seq;

论坛徽章:
0
8 [报告]
发表于 2010-11-01 15:34 |只看该作者
首先谢谢你的耐心分析!

我是根据PC访问Router的一个页面,而Router将页面转向到一个页面的流程发包的(只发了前三个包,后面TCP结束的包要交互,没有构造也没有发,希望这样做也能成功转向)。

我抓了这个转向过程的包放在附件中,有兴趣请查看。

html-redirect.rar

36.37 KB, 下载次数: 19

论坛徽章:
0
9 [报告]
发表于 2010-11-01 15:56 |只看该作者
我按照你的建议修改了程序,但还是没有成功。代码如下:

               // Bug 8889 Manju begin...
               
               if (iph->version == 4)
               {
                  struct sockaddr_in addr;
                  
                  bzero(&addr, sizeof(struct sockaddr_in));
                  addr.sin_family = AF_INET;
                  addr.sin_port = tcp->source;
                  addr.sin_addr.s_addr = iph->saddr;

                  int on=1;
                  
                  int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
                  if (sockfd >= 0)
                  {
                     setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

                     int head_len = sizeof(struct iphdr) + sizeof(struct tcphdr);

                     char http_buf[] = {"HTTP/1.1 302 Found\r\nLocation: http://192.168.0.1/sky_http_limit.html\r\n"};
                     
                     char buf[256];
                     bzero(buf, sizeof(buf));
                     strncpy(buf+head_len, http_buf, sizeof(buf)-head_len);
                     
                     struct iphdr *clew_iphdr = (struct iphdr *)buf;
                     clew_iphdr->version = iph->version;
                     clew_iphdr->ihl = sizeof(struct iphdr)>>2;
                     clew_iphdr->tos = 0;
                     clew_iphdr->tot_len = htons(head_len+strlen(http_buf)+1);
                     clew_iphdr->id = 0;
                     clew_iphdr->frag_off = 0;
                     clew_iphdr->ttl = MAXTTL;
                     clew_iphdr->protocol = IPPROTO_TCP;
                     clew_iphdr->check = 0;
                     clew_iphdr->daddr = iph->saddr;
                     clew_iphdr->saddr = iph->daddr;

                     struct tcphdr *clew_tcphdr = (struct tcphdr *)(buf + sizeof(struct iphdr));
                     clew_tcphdr->source = tcp->dest;
                     clew_tcphdr->dest = tcp->source;
                     clew_tcphdr->seq = tcp->ack_seq;
                     clew_tcphdr->ack_seq = htonl(ntohl(tcp->seq) + payload_len + 1);
                     printf("%u@%s:clew_tcphdr->ack_seq=%x %x %x %x %x\n", __LINE__, __FUNCTION__,
                              ntohl(clew_tcphdr->ack_seq),
                              payload_len,
                              clew_tcphdr->source,
                              tcp->dest,
                              tcp->seq); // Manju debug
                     clew_tcphdr->doff = 5;
                     clew_tcphdr->ack = 1;
                     clew_tcphdr->window = 6501;
                     clew_tcphdr->check = 0;

                     sendto(sockfd, buf, head_len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
                     
                     clew_tcphdr->psh = 1;
                     sendto(sockfd, buf, head_len+strlen(http_buf)+1, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
                     
                     clew_tcphdr->seq = htonl(ntohl(clew_tcphdr->seq) + strlen(http_buf) + 1);
                     printf("%u@%s:clew_tcphdr->seq=%x\n", __LINE__, __FUNCTION__,
                              clew_tcphdr->seq); // Manju debug
                     clew_tcphdr->psh = 0;
                     clew_tcphdr->fin = 1;
                     sendto(sockfd, buf, head_len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

                     close(sockfd);
                  }
               }

               // Bug 8889 Manju end.

主要的改动是:
1. 去掉了setsockopt(sockfd, IPPROTO_TCP, IP_HDRINCL, &on, sizeof(on));
2. 发最后一个包时修改了序列号;

附件是新抓的包,问题有:
1. ack_seq依然没有按预想的被设置;
2. 回给PC端的TCP Source端口依然是1而不是80;

以上的值在(由//Manju debug注释的)两条打印语句中是正确的(串口打印结果):
220@pkt_decision:clew_tcphdr->ack_seq=5b229e05 254 50 50 5b229bb0
237@pkt_decision:clew_tcphdr->seq=507ed8dc

这个程序是在Broadcom BCM963xx Linux上运行的,简略的系统信息如下:
Linux version 2.6.30 (gcc version 4.4.2 (Buildroot 2010.02-git) ) #2 Fri Oct 29 13:55:47 CST 2010
CPU revision is: 0002a070 (Broadcom4350)

这个可能和你的系统不一样,不知道这个会不会有影响!
谢谢!

response_with_ip_packet_fail_20101101_01.rar

1.28 KB, 下载次数: 30

论坛徽章:
0
10 [报告]
发表于 2010-11-01 15:57 |只看该作者
你看一下你前面两个附件的pcap,
没发现构造的tcp头部源端口不对吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP