Linux Netfilter挂载钩子发送简单的UDP报文

1、思路

分配空间--->填充udp、ip、ethernet报文头以及发送数据--->发送构造完成的报文

2、需要明白的接口

alloc_skb 分配skb空间

skb_reserve 在skb头部预留(将数据指针与skb尾指针后移)

skb_push 向前移动数据头指针(skb_reserve为这个操作预留空间)

skb_reset_transport_header 重置传输层报文头指针(存在偏移与不偏移两种方式)

skb_set_transport_header 重置并设置传输层报文头指针

skb_reset_network_header 重置ip层报文头指针

skb_reset_mac_header 重置链路层报文头指针

3、udp发送报文接口实现

#define ICMP 1
#define ETH "eth0"
#define S_PORT 9988
#define D_PORT 8899
u_long S_IP = 0xC0A8034D; //"192.168.3.77"
u_long D_IP = 0xC0A80305; //"192.168.3.5"
unsigned char S_MAC[ETH_ALEN]={0x00,0x0c,0x29,0x41,0x3e,0x66};/*本地mac地址*/
unsigned char D_MAC[ETH_ALEN]={0x14,0xa5,0x1a,0xba,0xf1,0x04};/*网关mac地址*/

static int my_diyudp_and_send(char *eth, u_char *smac, u_char *dmac,
            u_char *pkt, int pkt_len,u_long sip, u_long dip, u_short sport, u_short dport)
{
    int ret = -1;
    unsigned int pktSize;
    struct sk_buff *skb = NULL;
    struct net_device *dev = NULL;
    struct ethhdr *ethheader = NULL;
    struct iphdr *ipheader = NULL;
    struct udphdr *udpheader = NULL;
    u_char *pdata = NULL;

    /*参数合法性检查*/
    if(NULL == smac || NULL == dmac)
        goto out;

    /*通过出口接口名称获取接口设备信息*/
    dev = dev_get_by_name(&init_net, eth);
    if(NULL == dev)
    {
        printk(KERN_ERR "unknow device name:%s\n", eth);
        goto out;
    }

    /*计算报文长度*/
    pktSize = pkt_len + sizeof(struct iphdr) + sizeof(struct udphdr) + LL_RESERVED_SPACE(dev);
    skb = alloc_skb(pktSize, GFP_ATOMIC);
    if(NULL == skb)
    {
        printk(KERN_ERR "malloc skb fail\n");
        goto out;
    }

    /*在头部预留需要的空间*/
     skb_reserve (skb, pktSize);

    skb->dev = dev;
    skb->pkt_type = PACKET_OTHERHOST;
    skb->protocol = __constant_htons(ETH_P_IP);
    skb->ip_summed = CHECKSUM_NONE;//udp校验和初始化
    skb->priority = 0;

    pdata = skb_push(skb, pkt_len);
    if(NULL != pkt)
        memcpy(pdata, pkt, pkt_len);

    /*填充udp头部*/
    udpheader = (struct udphdr*)skb_push(skb, sizeof(struct udphdr));
    memset(udpheader, 0, sizeof(struct udphdr));
    udpheader->source = htons(sport);
    udpheader->dest = htons(dport);
    skb->csum = 0;
    udpheader->len = htons(sizeof(struct udphdr) + pkt_len);
    udpheader->check = 0;
    skb_reset_transport_header(skb);

    /*填充IP头*/
    ipheader = (struct iphdr*)skb_push(skb, sizeof(struct iphdr));
    ipheader->version = 4;
    ipheader->ihl = sizeof(struct iphdr) >> 2;//ip头部长度
    ipheader->frag_off = 0;
    ipheader->protocol = IPPROTO_UDP;
    ipheader->tos = 0;
    ipheader->saddr = htonl(sip);
    ipheader->daddr = htonl(dip);
    ipheader->ttl = 0x40;
    ipheader->tot_len = htons(pkt_len + sizeof(struct iphdr) + sizeof(struct udphdr));
    ipheader->check = 0;
    ipheader->check = ip_fast_csum((unsigned char *)ipheader, ipheader->ihl);
    skb_reset_network_header(skb);

    skb->csum = skb_checksum(skb, ipheader->ihl*4, skb->len-ipheader->ihl*4, 0);
    udpheader->check = csum_tcpudp_magic(sip, dip, skb->len-ipheader->ihl*4, IPPROTO_UDP, skb->csum);

    /*填充MAC*/
    ethheader = (struct ethhdr*)skb_push(skb, 14);
    memcpy(ethheader->h_dest, dmac, ETH_ALEN);
    memcpy(ethheader->h_source, smac, ETH_ALEN);
    ethheader->h_proto = __constant_htons(ETH_P_IP);
    skb_reset_mac_header(skb);

    /*send pkt
        dev_queue_xmit发送之后会释放相应的空间。
        因此注意不能做重复释放
    */
    if(0 > dev_queue_xmit(skb))
    {
        printk(KERN_ERR "send pkt error");
        goto out;
    }
    ret = 0;

    printk(KERN_INFO "send success\n");
out:
    if(ret != 0 && NULL != skb)
    {
        dev_put(dev);
        kfree_skb(skb);
    }
    return NF_ACCEPT;
}

4、需要注意

1)对报文头中的2字节、4字节等字段需要进行主机序转网络序的转换

2)调用构造报文函数的位置(在最开始实践的时候由于把所有包都丢弃,然后发送udp,导致ssh也不能登录)

5、函数调用

static unsigned int my_hook_test(unsigned int hooknum, struct sk_buff *skb,
    const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
    const struct iphdr *iph = ip_hdr(skb);
    //filter icmp
    if(iph->protocol == ICMP)
    {
        printk(KERN_INFO "recv pkt(%u):protocol:%u, Src:%u.%u.%u.%u, Dst:%u.%u.%u.%u\n",
            pktcnt, iph->protocol, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
        my_diyudp_and_send(ETH, S_MAC, D_MAC, "Hello From Slackware", strlen("Hello From Slackware"), S_IP, D_IP, S_PORT, D_PORT);
        return NF_DROP;
    }

    return NF_ACCEPT;
}

//挂载钩子,挂在出口处理的位置
static struct nf_hook_ops nfhello = {
        .hook = my_hook_test,
        .owner = THIS_MODULE,
        .pf = PF_INET,
        .hooknum = NF_INET_LOCAL_OUT,//挂载在出口处理报文的节点
        .priority = NF_IP_PRI_FIRST,//最高优先级
};

static int my_netfilter_init(void)
{
    printk(KERN_INFO "init my nodule\n");
    /*注册钩子*/
    nf_register_hook(&nfhello);

    return 0;
}

static void my_netfilter_exit(void)
{
    printk(KERN_INFO "Goodbye my module\n");
    /*卸载钩子*/
    nf_unregister_hook(&nfhello);
}

module_init(my_netfilter_init);
module_exit(my_netfilter_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhaojie");

MODULE_DESCRIPTION("Hello netfilter");

6、运行结果

原文地址:https://www.cnblogs.com/linuxroute/p/9386480.html

时间: 2024-10-13 21:58:19

Linux Netfilter挂载钩子发送简单的UDP报文的相关文章

Linux Netfilter注册钩子点

注册钩子点首先要包含响应的头文件,因为这应该已经属于对kernel的编程了. 1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/init.h> 其次,要定义钩子函数 1 static unsigned int example(unsigned int hooknum,struct sk_buff* skb,const struct net_device *in,const

Linux netfilter 学习笔记 之十二 ip层netfilter的NAT模块代码分析

本节主要是分析NAT模块相关的hook函数与target函数,主要是理清NAT模块实现的原理等. 1.NAT相关的hook函数分析 NAT模块主要是在NF_IP_PREROUTING.NF_IP_POSTROUTING.NF_IP_LOCAL_OUT.NF_IP_LOCAL_IN四个节点上进行NAT操作,在上一节中我们知道nat表中只有PREROUTING.POSTROUTING.LOCAL_OUT三条链,而没有NF_IP_LOCAL_IN链,所以不能创建在LOCAL_IN hook点的SNAT

彻底实现Linux TCP的Pacing发送逻辑-高精度hrtimer版

代码的实现是简单的,背后的思绪是复杂的.        如果单纯的将<彻底实现Linux TCP的Pacing发送逻辑-普通timer版>中的timer_list换成hrtimer,必然招致失败.因为在hrtimer的function中,调用诸如tcp_write_xmit这样的长路径函数是一种用丝袜装榴莲的行为.好吧,在无奈中我只能参考TSQ的做法.旧恨心魔!在Linux的TCP实现中,TSQ保证了一个单独的流不会过多地占据发送缓存,从而保证的多个数据流的相对公平.这个机制是用tasklet

Linux Netfilter开发小结

前置知识: IP包: struct ip { #if BYTE_ORDER == LITTLE_ENDIAN unsigned char ip_hl:4, /* header length */ ip_v:4; /* version */ #endif unsigned char ip_tos; /* type of service */ short ip_len; /* total length */ unsigned short ip_id;/* identification */ shor

在开发板Linux上挂载"驱动"挂载不成功,出现提示server 172.27.52.100 not responding, still trying

1.在开发板具体操作步骤如下: 1.1 :设置IP ifconfig eth0 172.27.52.200 1.2 :ping通 虚拟机Linux 主机Linux ping XXX.XXX.XXX.XXX 1.3.挂接 mount -t nfs -o nolock  XXX.XXX.XXX.XXX:/work/nfs_root/first_fs  /mnt // 例如:mount -t nfs -o nolock  172.27.52.100:/work/nfs_root/first_fs  /

[Nodejs]初探nodejs学习笔记- 如何使用nodejs搭建简单的UDP聊天功能

何为UDP(User Datagram Protocol)? 从baidu摘过来一段:UDP,用户数据报协议,与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层.根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议.UDP协议的主要作用是将网络数据流量压缩成数据包的形式.一个典型的数据包就是一个二进制数据的传输单位.每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据. UDP报文没有可靠性保证.顺序保证和流量控制字段等,可

Linux 磁盘挂载和mount共享

其中根据文章http://blog.163.com/[email protected]/blog/static/16581022720108162830579/也配置 原文:http://blog.csdn.net/catoop/article/details/7334901 针对Linux服务器的磁盘挂载mount和共享做简单操作说明: 1.  查看已使用的磁盘情况 df –h 2.  查看所有磁盘 fdisk –l 3.  查看指定磁盘"/dev/xvde"的分区情况 fdisk

Linux系统挂载NTFS文件系统(转载)

转自:http://hermesbox.blogbus.com/logs/47386987.html 今天尝试并成功的将一块500G的移动硬盘挂载到了RHEL5的系统上,甚感欣慰.想到也许以后自己或其他同学们会有类似经历,于是尽量细致的记录于此.     无论是一块安装了Windows/Linux双系统的硬盘,还是通过USB连接的移动硬盘/U盘,都是可以挂载到Linux系统中的.不过由于Windows本身常用的文件系统包括fat32和NTFS,因此还是需要区别的.废话少说,进入正题. 系统环境如

工位上的Python——一个简单的UDP广播实例

最近状态神勇,头脑清晰,趁此良机,多多学习,多多看书,把以前看不懂的地方重新看了下,收获匪浅,现把两个简单的小例子献给大家: 先是一个简单的UDP广播接收的小服务器,使用UDP广播,需要注意下协议的使用,已经最最重要的socket选项的设置,设置为传说中的"socket.SO_BROADCAST",不需要有监听,接收客户端的消息使用recvfrom,发送消息使用sendto: 代码如下: !/usr/bin/env python  #coding:utf-8 import socket