Linux 内核下发送 SYN 段

之前发过一个练手的程序 udp packet 的收发(sk_buff+Netfiler)。这次在内核下发送 SYN 段的方法大致与上次一样。上次是在内核下收发 UDP 包,这次发送的是 TCP 包。主要熟悉了 TCP 头和选项在 Linux 内核是如何填充的。

主要参考了 tcp_output.c 这个文件里面的代码。程序中主要要清楚一些结构的用法:sk_buftcphdriphdr 等。还有一些函数的调用,如:ip_hdr()tcp_hdr()等。

代码如下:

/***********************************************************************
* File: sendSYN.c
* Author: Sam
* Create Date: 2011/12/8
* Abstract Description:
*         Send a SYN to a web server.
* Note:
*         Client information:
*             Ubuntu 10.04, AMD Althon*2;
*             IP address: *.*.*.*;
*             Hardware address:*:*:*:*:*:*.
*         Server information:
*             Xubuntu 11.10, Intel Atom;
*             IP address: *.*.*.*;
*             Hardware address: *:*:*:*:*:*.
***********************************************************************/


#ifndef __KERNEL__
    #define __KERNEL__
#endif
#ifndef MODULE
    #define MODULE
#endif

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>


#define IF_NAME    "eth0"
#define CLIENTIP   "*.*.*.*"
#define SERVERIP   "*.*.*.*"
#define CLIENTPORT 80
#define SERVERPORT 80
#define SERVER_MAC {*, *, *, *, *, *}
#define CLIENT_MAC {*, *, *, *, *, *}
#define MSS        1460


struct socket *sock;

static void sock_init()
{
        struct ifreq ifr;

        sock_create_kern(PF_INET, SOCK_STREAM, 0, &sock);
        strcpy(ifr.ifr_ifrn.ifrn_name, IF_NAME);
        kernel_sock_ioctl(sock, SIOCSIFNAME, (unsigned long) &ifr);
}

static void send_packet()
{
        struct net_device *netdev = NULL;
        struct net *net = NULL;
        struct sk_buff *skb = NULL;
        struct ethhdr *eth_header = NULL;
        struct iphdr *ip_header = NULL;
        struct tcphdr *tcp_header = NULL;
        unsigned tcp_options_size, tcp_header_size;
        __be32 dip = in_aton(SERVERIP);
        __be32 sip = in_aton(CLIENTIP);
        u16 data_len = 0;
        u32 skb_len;
        u8 dst_mac[ETH_ALEN] = SERVER_MAC;
        u8 src_mac[ETH_ALEN] = CLIENT_MAC;
        u8 flags = TCPCB_FLAG_SYN;    // Defined in tcp.h->tcp_skb_cb
        __be32 *ptr;    // used to write the TCP options.

        /* construct skb
         * sock_net() is defined in include/net/sock.h
         * dev_get_by_name()函数用来取得设备指针,使用该函数
         * 后一定要使用dev_put()函数取消设备引用. */
        sock_init();
        net = sock_net((const struct sock *) sock->sk);
        netdev = dev_get_by_name(net, IF_NAME);

        tcp_options_size = TCPOLEN_MSS;
        tcp_header_size = tcp_options_size + sizeof(struct tcphdr);
        /* LL_RESERVED_SPACE is defined in include/netdevice.h. */
        skb_len = data_len + sizeof(struct iphdr)
                + tcp_header_size + LL_RESERVED_SPACE(netdev);

        /* 打印IP header, TCP header 和整个skb的长度. */
        printk("----raw packet information----\n");
        printk("iphdr: %d\n", sizeof(struct iphdr));
        printk("tcphdr: %d\n", tcp_header_size);
        printk("data_len: %d\n", data_len);
        printk("skb_len: %d\n", skb_len);

        /* dev_alloc_skb是一个缓冲区分配函数,主要被设备驱动使用.
         * 这是一个alloc_skb的包装函数, 它会在请求分配的大小上增加
         * 16 Bytes的空间以优化缓冲区的读写效率.*/
        skb = dev_alloc_skb(skb_len);
        if (!skb) {
                printk("Fail to alloc skb --01\n");
                return;
        }

        /* fill the skb.具体参照struct sk_buff.
         * skb_reserve()用来为协议头预留空间.
         * PACKET_OTHERHOST: packet type is "to someone else".
         * ETH_P_IP: Internet Protocol packet.
         * CHECKSUM_NONE表示完全由软件来执行校验和. */
        skb_reserve(skb, LL_RESERVED_SPACE(netdev));
        skb->dev       = netdev;
        skb->pkt_type  = PACKET_OTHERHOST;
        skb->protocol  = htons(ETH_P_IP);
        skb->ip_summed = CHECKSUM_NONE;
        skb->priority  = 0;
        skb->len       = data_len + LL_RESERVED_SPACE(netdev);

        /* 分配内存给IP header和TCP header */
        skb_set_network_header(skb, 0);
        skb_put(skb, sizeof(struct iphdr));
        skb_set_transport_header(skb, sizeof(struct iphdr));

        /* Build TCP header in skb */
        skb_put(skb, tcp_header_size);
        //memset(tcp_header, 0, tcp_header_size);
        tcp_header            = tcp_hdr(skb);
        tcp_header->source    = htons(CLIENTPORT);
        tcp_header->dest      = htons(SERVERPORT);
        tcp_header->seq       = 0;
        tcp_header->ack_seq   = 0;
        tcp_header->doff      = tcp_header_size / 4;
        /* Build the flags: */
        *(((__be16 *)tcp_header) + 6) = htons(((tcp_header_size >> 2) << 12) | flags);
        /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
        tcp_header->window    = htons(65535U); 
        tcp_header->check     = 0;
        tcp_header->urg_ptr   = 0;
        /* Build the TCP options. (Read the tcp_transmit_skb and tcp_options_write)
         * __be32 指以32bit为单位,后面的+1指的是tcp_header起始的地址以32bit为单位
         * 加1,即到了options的位置了。这样就可以开始填写TCP options了. */
        ptr     = (__be32 *)(tcp_header + 1);
        *ptr++  = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | MSS);

        /* construct ip header in skb */
        ip_header           = ip_hdr(skb);
        ip_header->version  = 4;
        ip_header->ihl      = sizeof(struct iphdr) / 4;
        ip_header->frag_off = 0;
        ip_header->protocol = IPPROTO_TCP;
        ip_header->tos      = 0;
        ip_header->daddr    = dip;
        ip_header->saddr    = sip;
        ip_header->ttl      = 0x40;
        ip_header->tot_len  = htons(skb->len);
        ip_header->check    = 0;

        /* caculate checksum */
        skb->csum         = skb_checksum(skb, ip_header->ihl*4, skb->len-ip_header->ihl*4, 0);
        ip_header->check  = ip_fast_csum(ip_header, ip_header->ihl);
        tcp_header->check = csum_tcpudp_magic(sip, dip, skb->len-ip_header->ihl*4,
                                                IPPROTO_TCP, skb->csum);

        /* construct ethernet header in skb */
        eth_header = (struct ethhdr *)skb_push(skb, ETH_HLEN);
        memcpy(eth_header->h_dest, dst_mac, ETH_ALEN);        /* destination eth addr */
        memcpy(eth_header->h_source, src_mac, ETH_ALEN);      /* source ether addr    */
        eth_header->h_proto = htons(ETH_P_IP);                /* packet type ID field */

        /* send packet */
        if (dev_queue_xmit(skb) < 0) {
                dev_put(netdev);
                kfree_skb(skb);
                printk("send packet by skb failed.\n");
                return;
        }
        printk("send packet by skb success.\n");
}

int init_module(void)
{
        send_packet();
        printk("TCP SYN segement: init module--------ok\n");
        return 0;
}

void cleanup_module(void)
{
        sock_release(sock);
        printk("TCP SYN segement: exit module--------ok\n");
}

MODULE_LICENSE("GPL");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值