https://blog.csdn.net/liao152/article/details/45252387
下面来点干货,是一个项目中,需要对请求的dns进行黑白名单划分,并且对解析结果(ip地址)同样进行黑白名单划分。
1, 系统中预置一个黑名单列表,一个白名单列表(采用字典树);
2, 系统中预置一个黑名单ipset,一个白名单ipset;
3, 解析dns请求报文,并对解析的dns进行匹配,如果匹配黑名单,则重定向该域名请求到特定的dns服务器;同理,如果匹配到白名单,则重定向到另一个dns服务器。
4,解析dns的应答报文,对解析的dns进行匹配,如果匹配到黑/白名单,那么:1,如果应答记录中是cname,那么将此cname域名添加到对应的域名记录中;2,如果应答记录中是ip地址,那么将该IP地址添加到对应的ipset中。
5, 业务报文到达时,通过匹配黑白名单的ipset可以进行进一步分流。
下面的代码是该实现中,dns解析部分:
dns.c
点击(此处)折叠或打开
- /*********************************************************************************************
- * Description: kernel module to parse dns request and replay packages
- *
- * Author: Husker <huskerTang@gmail.com>
- *
- * Date: 2019-2-12
- ***********************************************************************************************/
- #include <linux/fs.h>
- #include <linux/module.h>
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- #include <linux/types.h>
- #include <linux/version.h>
- #include <linux/timer.h>
- #include <linux/ip.h>
- #include <linux/slab.h>
- #include <linux/list.h>
- #include "dns.h"
- static struct kmem_cache* dns_pkg_cache; //__read_mostly;
- static struct kmem_cache* dns_query_cache; // __read_mostly;
- static struct kmem_cache* dns_record_cache; // __read_mostly;
- void DNS_finit(void)
- {
- if (dns_pkg_cache)
- {
- kmem_cache_destroy(dns_pkg_cache);
- }
- if (dns_query_cache)
- {
- kmem_cache_destroy(dns_query_cache);
- }
- if (dns_record_cache)
- {
- kmem_cache_destroy(dns_record_cache);
- }
- }
- int DNS_init(void)
- {
- dns_pkg_cache = kmem_cache_create("dns_pkg_cache",
- sizeof(struct dns_package),
- 0, 0, NULL);
- if (dns_pkg_cache == NULL)
- {
- printk(KERN_ERR "[DNS] failed to create dns package cache\n");
- goto ERROR_OUT;
- }
- dns_query_cache = kmem_cache_create("dns_query_cache",
- sizeof(struct dns_query),
- 0, 0, NULL);
- if (dns_query_cache == NULL)
- {
- printk(KERN_ERR "[DNS] failed to create dns query node cache\n");
- goto ERROR_OUT;
- }
- dns_record_cache = kmem_cache_create("dns_record_cache",
- sizeof(struct dns_resource),
- 0, 0, NULL);
- if (dns_record_cache == NULL)
- {
- printk(KERN_ERR "[DNS] failed to create dns resource recorder cache\n");
- goto ERROR_OUT;
- }
- return 0;
- ERROR_OUT:
- DNS_finit();
- return -1;
- }
- static int parse_domain_name(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, uint8_t* name, uint16_t* nmlen)
- {
- int ret;
- uint8_t tnm[128] = {0};
- uint16_t tnm_len = 128;
- int cnt = 0;
- int total = 0;
- uint16_t offset = 0;
- uint8_t* ptr;
- uint16_t len = 0;
- if (DNS_HDR_LEN >= pkglen || pstart < pkg || pstart >= (pkg + pkglen) || !name || !nmlen || *nmlen <= 0)
- {
- return -1;
- }
- total = pstart - pkg;
- ptr = tnm;
- while (*pstart != 0)
- {
- if (*pstart >= DNS_COMMPRESS_FLAG)
- {
- offset = (*pstart) * 256 + *(pstart + 1) - 0xc000;
- if (offset >= pkglen)
- {
- return -1;
- }
- tnm_len -= len;
- ret = parse_domain_name(pkg, pkglen, pkg + offset, ptr, &tnm_len);
- if (ret < 0 )
- {
- return ret;
- }
- cnt += 2;
- len += tnm_len;
- goto success_out;
- }
- else
- {
- uint16_t dm_part_len = *pstart;
- if ( (pstart + dm_part_len >= pkg + pkglen) || (128 - len <= 0))
- {
- return -1;
- }
- memcpy(ptr, pstart + 1, dm_part_len);
- ptr += dm_part_len;
- len += dm_part_len;
- // add '.' to every part of domain
- *ptr = '.';
- ptr++;
- len++;
- cnt += dm_part_len + 1;
- pstart += dm_part_len + 1;
- }
- }
- if (*pstart == 0)
- {
- cnt++;
- }
- success_out:
- if (tnm[len - 1] == '.' )
- {
- tnm[len - 1] = 0;
- len--;
- }
- if (*nmlen <= len )
- {
- return -1;
- }
- memcpy(name, tnm, len);
- name[len] = 0;
- *nmlen = len;
- return cnt;
- }
- static int parse_query(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, struct dns_query* query)
- {
- int ret;
- uint16_t nmlen = 128;
- uint8_t* ptr = pstart;
- int cnt = 0;
- // parse name
- ret = parse_domain_name(pkg, pkglen, ptr, query->name, &nmlen);
- if (ret < 0 )
- {
- return ret;
- }
- ptr += ret;
- cnt += ret;
- // parse domain type
- if (ptr + 1 >= pkg + pkglen )
- {
- return -1;
- }
- query->type = (*ptr) * 256 + *(ptr + 1);
- ptr += 2;
- cnt += 2;
- // parse domain class
- if (ptr + 1 >= pkg + pkglen )
- {
- return -1;
- }
- query->class = (*ptr) * 256 + *(ptr + 1);
- ptr += 2;
- cnt += 2;
- return cnt;
- }
- static int parse_resource(uint8_t* pkg, uint16_t pkglen, uint8_t* pstart, struct dns_resource* res)
- {
- int ret;
- uint16_t nmlen = 128;
- uint8_t* ptr = pstart;
- int cnt = 0;
- // parse name
- ret = parse_domain_name(pkg, pkglen, ptr, res->name, &nmlen);
- if (ret < 0 )
- {
- return ret;
- }
- ptr += ret;
- cnt += ret;
- // parse domain type
- if (ptr + 1 >= pkg + pkglen )
- {
- return -1;
- }
- res->type = (*ptr) * 256 + *(ptr + 1);
- ptr += 2;
- cnt += 2;
- // parse domain class
- if (ptr + 1 >= pkg + pkglen )
- {
- return -1;
- }
- res->class = (*ptr) * 256 + *(ptr + 1);
- ptr += 2;
- cnt += 2;
- // parse ttl
- if (ptr + 3 >= pkg + pkglen )
- {
- return -1;
- }
- res->ttl = ((*ptr) << 24) + (*(ptr + 1) << 16) + (*(ptr + 2) << 8) + (*(ptr + 3));
- ptr += 4;
- cnt += 4;
- // parse date len
- if (ptr + 1 >= pkg + pkglen)
- {
- return -1;
- }
- res->data_len = (*ptr) * 256 + *(ptr + 1);
- if (res->data_len > 256)
- {
- return -1;
- }
- ptr += 2;
- cnt += 2;
- // parse date
- if (ptr + res->data_len > pkg + pkglen)
- {
- return -1;
- }
- if (res->type == TYPE_CNAME)
- {
- uint16_t cnlen = 256;
- int offset = 0;
- offset = parse_domain_name(pkg, pkglen, ptr, res->data, &cnlen);
- if (offset < 0)
- {
- return -1;
- }
- ptr += offset;
- cnt += offset;
- }
- else
- {
- memcpy(res->data, ptr, res->data_len);
- ptr += res->data_len;
- cnt += res->data_len;
- }
- return cnt;
- }
- static void free_pkg_recoders(struct list_head* hd)
- {
- struct dns_resource* rcd;
- struct dns_resource* tmp;
- if (list_empty(hd))
- {
- return;
- }
- list_for_each_entry_safe(rcd, tmp, hd, list)
- {
- list_del(&rcd->list);
- kmem_cache_free(dns_record_cache, rcd);
- }
- }
- static void free_pkg_querys(struct list_head* hd)
- {
- struct dns_query* query;
- struct dns_query* tmp;
- if (list_empty(hd))
- {
- return;
- }
- list_for_each_entry_safe(query, tmp, hd, list)
- {
- list_del(&query->list);
- kmem_cache_free(dns_query_cache, query);
- }
- }
- void DNS_free_package(struct dns_package* spkg)
- {
- free_pkg_querys(&spkg->query_hd);
- free_pkg_recoders(&spkg->answer_hd);
- free_pkg_recoders(&spkg->auth_hd);
- free_pkg_recoders(&spkg->addit_hd);
- kmem_cache_free(dns_pkg_cache, spkg);
- }
- struct dns_package* DNS_new_package(void)
- {
- struct dns_package* spkg = NULL;
- spkg = (struct dns_package*)kmem_cache_alloc(dns_pkg_cache, GFP_ATOMIC);
- if (NULL == spkg)
- {
- return NULL;
- }
- memset(spkg, 0, sizeof(struct dns_package));
- INIT_LIST_HEAD(&spkg->query_hd);
- INIT_LIST_HEAD(&spkg->answer_hd);
- INIT_LIST_HEAD(&spkg->auth_hd);
- INIT_LIST_HEAD(&spkg->addit_hd);
- return spkg;
- }
- int DNS_parse_package(uint8_t* pkg, uint16_t pkglen, struct dns_package* spkg)
- {
- struct dns_header* dnshdr;
- struct dns_query* query;
- struct dns_resource* rcd;
- uint8_t* ptr;
- int offset = 0;
- int i;
- if (!pkg || pkglen <= DNS_HDR_LEN)
- {
- return -1;
- }
- memcpy(&spkg->hdr, pkg, sizeof(struct dns_header));
- dnshdr = &spkg->hdr;
- dnshdr->id = ntohs(dnshdr->id);
- dnshdr->q_count = ntohs(dnshdr->q_count);
- dnshdr->ans_count = ntohs(dnshdr->ans_count);
- dnshdr->auth_count = ntohs(dnshdr->auth_count);
- dnshdr->add_count = ntohs(dnshdr->add_count);
- if (dnshdr->q_count > 64 || dnshdr->add_count > 64 || dnshdr->ans_count > 64 || dnshdr->auth_count > 64)
- {
- printk( KERN_DEBUG "too much recoders, query, add, ans, auth\n",
- dnshdr->q_count, dnshdr->add_count, dnshdr->ans_count, dnshdr->auth_count);
- return -1;
- }
- ptr = pkg + DNS_HDR_LEN;
- // parse querys
- for(i = 0; i < dnshdr->q_count; i++)
- {
- query = (struct dns_query*)kmem_cache_alloc(dns_query_cache, GFP_ATOMIC);
- if (NULL == query)
- {
- return -1;
- }
- offset = parse_query(pkg, pkglen, ptr, query);
- if (offset <= 0 )
- {
- printk(KERN_DEBUG "fail to parse query recorder,err:%d, %d\n", offset, i);
- return offset;
- }
- ptr += offset;
- list_add(&query->list, &spkg->query_hd);
- }
- // parse answers
- for (i = 0; i < dnshdr->ans_count; i++)
- {
- rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, GFP_ATOMIC);
- if (NULL == rcd)
- {
- return -1;
- }
- offset = parse_resource(pkg, pkglen, ptr, rcd);
- if (offset <= 0 )
- {
- printk(KERN_DEBUG "fail to parse answers, %d\n", i);
- return offset;
- }
- ptr += offset;
- list_add(&rcd->list, &spkg->answer_hd);
- }
- // parse auth
- for (i = 0; i < dnshdr->auth_count; i++)
- {
- rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, GFP_ATOMIC);
- if (NULL == rcd)
- {
- return -1;
- }
- offset = parse_resource(pkg, pkglen, ptr, rcd);
- if (offset <= 0 )
- {
- printk(KERN_DEBUG "fail to parse auths, %d\n", i);
- return offset;
- }
- ptr += offset;
- list_add(&rcd->list, &spkg->auth_hd);
- }
- for (i = 0; i < dnshdr->add_count; i++)
- {
- rcd = (struct dns_resource*)kmem_cache_alloc(dns_record_cache, GFP_ATOMIC);
- if (NULL == rcd)
- {
- return -1;
- }
- offset = parse_resource(pkg, pkglen, ptr, rcd);
- if (offset <= 0 )
- {
- printk( KERN_DEBUG "fail to parse additions, %d\n", i);
- return offset;
- }
- ptr += offset;
- list_add(&rcd->list, &spkg->addit_hd);
- }
- return 0;
- }
dns.h
点击(此处)折叠或打开
- /*********************************************************************************************
- * Description: kernel module to parse a dns request or replay
- *
- * Author: Husker <huskerTang@gmail.com>
- *
- * Date: 2018-10-8
- ***********************************************************************************************/
- #ifndef __DNS_H__
- #define __DNS_H__
- typedef enum
- {
- TYPE_A = 1, // Ipv4 address
- TYPE_NS = 2, // Authoritative name server
- TYPE_MD = 3, // Mail destination (Obsolete - use MX)
- TYPE_MF = 4, // Mail forwarder (Obsolete - use MX)
- TYPE_CNAME = 5, // Canonical name for an alias
- TYPE_SOA = 6, // Marks the start of a zone of authority
- TYPE_MB = 7, // Mailbox domain name (EXPERIMENTAL)
- TYPE_PTR = 12, // domain name pointer
- TYPE_MX = 15, // Mail server
- TYPE_TXT = 16,
- TYPE_AAAA = 28,
- TYPE_SRV = 33,
- TYPE_SPF = 99,
- } qtype_t;
- #define DNS_HDR_LEN 12
- #define DNS_COMMPRESS_FLAG 0xc0
- #pragma pack(push, 1)
- struct dns_header
- {
- uint16_t id; // identification number
- #ifdef __BIG_ENDIAN
- uint8_t qr: 1;
- uint8_t opcode: 4;
- uint8_t aa: 1;
- uint8_t tc: 1;
- uint8_t rd: 1;
- uint8_t ra: 1;
- uint8_t z: 3;
- uint8_t rcode: 4;
- #else
- uint8_t rd: 1;
- uint8_t tc: 1;
- uint8_t aa: 1;
- uint8_t opcode: 4;
- uint8_t qr: 1;
- uint8_t rcode: 4;
- uint8_t z: 3;
- uint8_t ra: 1;
- #endif
- uint16_t q_count;
- uint16_t ans_count;
- uint16_t auth_count;
- uint16_t add_count;
- };
- #pragma pack(pop)
- struct dns_query
- {
- struct list_head list;
- uint8_t name[128];
- uint16_t type;
- uint16_t class;
- };
- struct dns_resource
- {
- struct list_head list;
- uint8_t name[128];
- uint16_t type;
- uint16_t class;
- uint32_t ttl;
- uint16_t data_len;
- uint8_t data[256];
- };
- struct dns_package
- {
- struct dns_header hdr;
- struct list_head query_hd;
- struct list_head answer_hd;
- struct list_head auth_hd;
- struct list_head addit_hd;
- };
- void DNS_free_package(struct dns_package* spkg);
- struct dns_package* DNS_new_package(void);
- int DNS_parse_package(uint8_t* pkg, uint16_t pkglen, struct dns_package* spkg);
- int DNS_init(void);
- void DNS_finit(void);
- #endif /* _DNS_H_ */