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 8899u_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、运行结果
Linux Netfilter挂载钩子发送简单的UDP报文
原文地址:https://www.cnblogs.com/linuxroute/p/9386480.html