admin管理员组

文章数量:1530061

需求背景:

目前路由器的配置界面是通过输入网关IP来访问,市面上其他的路由器可以通过域名来访问,例如腾达路由器的配置界面地址 routendawifi。
所以我们也希望支持域名访问路由器的配置界面。如在浏览器中输入www.9344base 就可以访问路由器界面。

实现原理:

一次正常的域名访问流程如下:
(1) 客户端向DNS服务器发送域名解析请求,该请求会先发到路由器。
(2) 路由器收到DNS请求后会对其进行转发,发给外网的DNS服务器。
(3) 外网的DNS服务器收到请求后,进行解析,之后发出DNS应答,其中就包括了域名对应的IP。
(4) 路由器收到DNS服务器发来的应答包,将其转发给客户端。
(5) 客户端收到应答,按照对应IP进行访问。

我们的修改发生在第2步,当路由器收到客户端的DNS请求包后,筛选出特定域名的请求包,不对其进行转发,而是将其丢弃,然后生成一个DNS应答包发给客户端,该应答包中IP就是网关IP或者A20的IP。这样客户端收到应答包后就可以把特定域名和网关对应起来了。

这部分的实现主要是在iptables 和netfilter中,netfilter中对DNS数据包进行筛选、丢弃和生成。

源码介绍

对源码的修改主要分两部分:iptables 部分和netfilter 部分,关于iptables和netfilter关系以及match、target、hook点、iptables 表、iptables链的详细介绍参考如下资料 :
http://blog.chinaunix/uid-20775448-id-3504538.html

(1) iptables 部分

源码位置:apps/iptables/iptables-1.4.5-qos/extensions
主要添加了libipt_DNS.c 和 libipt_dnsurl.c 两个文件。其中libipt_DNS.c 是DNS功能的iptables 中的target模块。
核心代码如下:

static int DNS_parse(int c, char **argv, int invert, unsigned int *flags,
                     const void *entry, struct xt_entry_target **target)
{
    struct ipt_dns_info *dnsinfo = (struct ipt_dns_info *)(*target)->data;
    const struct in_addr *ip;

        xtables_check_inverse(optarg, &invert, &optind, 0); 
    switch (c) {
    case '1':
        ip = xtables_numeric_to_ipaddr(argv[optind-1]);
        if (!ip)
            xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
                   argv[optind-1]);
        dnsinfo->ip = ip->s_addr;
    
        //printf("ip = %x\n", dnsinfo->ip);
        break;

    default:
        return 0;
    }
    
    return 1;
}

主要功能就是将iptables 命令传入的IP参数进行解析,然后填充到ipt_dns_info结构体中,之后将该结构体传入内核,交个DNS 的netfilter target模块使用。
libipt_dnsurl.c 文件 是DNS功能的iptables 中的match模块,核心代码如下:

static int parse(int c, char **argv, int invert, unsigned int *flags,
      const void *entry,
      struct xt_entry_match **match)
{
    struct xt_dnsurl_info *info = (struct xt_dnsurl_info *)(*match)->data;

    xtables_check_inverse(optarg, &invert, &optind, 0); 
    if (invert) xtables_error(PARAMETER_PROBLEM, "Sorry, you can't have an inverted comment");

//  printf("dnsurl parse\n");

    if (c == '1')
    {   
        strcpy(info->url, argv[optind-1]);
        info->url_len = strlen(argv[optind-1]);
        if(!strcmp(argv[optind-1],"enforcedns"))
        {
            info->enforce_dns = 1;
        }
    }
    else
    {
        return 0;
    }

    if (*flags)
        xtables_error(PARAMETER_PROBLEM, "multiurl can only have one option");

    *flags = 2;


    return 1;
}

主要作用是将iptables 命令传入的URL参数进行解析,然后填充到xt_dnsurl_info 结构体中,之后将该结构体传入内核,交个DNS 的netfilter match模块使用。

(2)netfilter 部分

主要添加了netfilter 的match模块和target模块。
target模块源码位于 linux/kernels/mips-linux-2.6.31/net/ipv4/netfilter/ nf_nat_rule.c文件中,核心源码如下:

static unsigned int 
ipt_dns_target(struct sk_buff *skb, const struct xt_target_param *par)
{
    const struct ipt_dns_info *dnsinfo = par->targinfo;

    struct iphdr *iph = ip_hdr(skb);
    struct udphdr *udph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
    int ret = 0;
    int addr = 0;
    u_int16_t port = 0;
    u_int16_t *flag = (void *)udph + 10; 
    char buff[50] = {0};
    char *buff_p = buff;
    enum ip_conntrack_info ctinfo;
    struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
    u_int16_t *udp_check = (void *)udph + 6;
    u_int8_t *url_len = (void *)udph + 20; 
    char *url = url_len+1;
    
    /*将DNS数据包的目的IP和源IP进行互换,源端口和目的端口互换,这样就可以将收的数据包又返回给发送者*/
    addr = iph->daddr;iph->daddr = iph->saddr; iph->saddr = addr; 
    port = udph->dest;
    udph->dest = udph->source;
    udph->source = port;
    /*修改flag将请求模式改成应答模式*/
    *flag ++= 0x8580;
     /*修改资源记录数为1,修改问题数为1*/
    *flag ++= 0x0001;
    *flag = 0x0001;

     /*填充应答包中资源记录部分数据,即要给客户端的DNS IP数据*/
    *buff_p++=0xc0;
    *buff_p++=0x0c;
    *(int *)buff_p = 0x00010001;
    buff_p +=4;
    *(int *)buff_p = 0x0;
    buff_p +=4;
    *(u_int16_t *)buff_p = 0x4;
    buff_p +=2;
     /*查询的URL对应的IP*/
    *(int *)buff_p = dnsinfo->ip;

     /*完成对UDP包的修改,进行相关的校验*/
    nf_nat_mangle_udp_packet_fw(skb, ct, ctinfo,
                                  udph->len-8, 0,
                                  &buff, 16);

    return 1;
}

主要作用是将match筛选过的数据包进行修改,填充DNS应答包,这样就可以把客户端发来的DNS请求包变成应答包,然后再返还给客户端,从而模拟了一次完整的DNS请求应答过程。
Match模块源码位于:linux/kernels/mips-linux-2.6.31/net/ipv4/netfilter/ ipt_multiurl.c文件中。核心代码如下:

static bool dnsmatch(const struct sk_buff *skb, 
      const struct xt_match_param *param)
{   
    const struct xt_dnsurl_info *info = param->matchinfo;    

    struct iphdr *iph = ip_hdr(skb);
        struct udphdr *udph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
     u_int8_t *url_len = (void *)udph + 20; 
    char *url = url_len+1;
    if(info->enforce_dns)
    {   
        return 1;
    }   
    /*判断DNS请求包中的URL长度是不是和特定URL的长度一致*/
    if( *url_len == info->url_len)
    {   
            /*判断DNS请求包中的URL内容是不是和特定URL一致,如果一致将返回1,表明该数据包通过筛选*/
        if (!memcmp(url, info->url, info->url_len))
        {
    //      printk("yaomoon: match dnsurl url_len = %d\n",info->url_len);
            return 1;
        }
    }   
    if( (*url_len == 3)&&(*(url_len+4) == info->url_len))
    {   
        url_len +=4;
        url = url_len+1;
        if (!memcmp(url, info->url, info->url_len))
        {
    //      printk("yaomoon: match dnsurl url_len = %d\n",info->url_len);
            return 1;
        }
    }

    return 0;

}

主要作用就是筛选DNS请求包,DNS请求包中包含所请求的URL内容和长度,我们可以通过判断出是不是我们要改的DNS请求包。

本文标签: 路由器界面功能域名