24-Openwrt dnsmasq

dnsmasq是openwrt一个重要的进程,里面提供了两个重要的功能。一个是dhcp server,给lan口使用的,另一个是dns功能,维护路由器的dns信息,而且支持ipv4和ipv6。

1、 dnsmasq启动过程

从/etc/init.d/dnsmasq start脚本启动

root@Openwrt:/# cat /etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option filterwin2k '0'
        option localise_queries '1'
        option rebind_protection '0'
        option rebind_localhost '1'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option nonegcache '0'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.auto'
        option nonwildcard '1'
        option localservice '1'

config dhcp 'lan'
        option interface 'lan'
        option start '100'
        option limit '150'
        option force '1'
        option ignore '0'
        option leasetime '12h'

start_service函数里面会读取/etc/config/dhcp和/etc/config/networek下面的配置文件,然后集成出一份新的配置文件/var/etc/dnsmasq.conf,如下:

# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
dhcp-authoritative
domain-needed
log-queries
localise-queries
read-ethers
bogus-priv
expand-hosts
bind-interfaces
local-service
domain=lan
server=/lan/
dhcp-leasefile=/tmp/dhcp.leases
resolv-file=/tmp/resolv.conf.auto
addn-hosts=/tmp/hosts
conf-dir=/tmp/dnsmasq.d
dhcp-broadcast=tag:needs-broadcast

dhcp-range=lan,192.168.18.100,192.168.18.249,255.255.255.0,12h

比如uci里面添加了option logqueries 1,那么就会在/var/etc/dnsmasq.conf里面添加log-queries,这时候c代码里面会解析。

{ "log-queries", 2, 0, 'q' },
{ "log-facility", 1, 0 ,'8' },

uci的所有配置在官网可以看到http://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html

-q, --log-queries
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie --log-queries=extra then the log has extra information at the start of each line. This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor.
-8, --log-facility=<facility>
Set the facility to which dnsmasq will send syslog entries, this defaults to DAEMON, and to LOCAL0 when debug mode is in operation. If the facility given contains at least one '/' character, it is taken to be a filename, and dnsmasq logs to the given file, instead of syslog. If the facility is '-' then dnsmasq logs to stderr. (Errors whilst reading configuration will still go to syslog, but all output from a successful startup, and all output whilst running, will go exclusively to the file.) When logging to a file, dnsmasq will close and reopen the file when it receives SIGUSR2. This allows the log file to be rotated without stopping dnsmasq.

启动进程

root@openwrt:/# ps | grep dns
21019 nobody    1024 S    /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf -k -x /var/run/dnsmasq/dnsmasq.pid
21241 root      1520 S    grep dns

dnsmasq.c的main()函数启动,一系列的初始化后,最终在while(1)里面处理逻辑,如下:

while (1)
{
      int t, timeout = -1;
      
      poll_reset();
      
      /* if we are out of resources, find how long we have to wait
	 for some to come free, we'll loop around then and restart
	 listening for queries */
      if ((t = set_dns_listeners(now)) != 0)
	    timeout = t * 1000;

      /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
      if (daemon->tftp_trans ||
	  (option_bool(OPT_DBUS) && !daemon->dbus))
	    timeout = 250;

      /* Wake every second whilst waiting for DAD to complete */
      else if (is_dad_listeners())
	    timeout = 1000;
  
#ifdef HAVE_DHCP
      if (daemon->dhcp || daemon->relay4)
	{
	  poll_listen(daemon->dhcpfd, POLLIN);
	  if (daemon->pxefd != -1)
	    poll_listen(daemon->pxefd, POLLIN);
	}
#endif
    
#ifdef HAVE_INOTIFY
      if (daemon->inotifyfd != -1)
	    poll_listen(daemon->inotifyfd, POLLIN);
#endif

#if defined(HAVE_LINUX_NETWORK)
      poll_listen(daemon->netlinkfd, POLLIN);
#elif defined(HAVE_BSD_NETWORK)
      poll_listen(daemon->routefd, POLLIN);
#endif
      
      poll_listen(piperead, POLLIN);

#ifdef HAVE_SCRIPT
#    ifdef HAVE_DHCP
      while (helper_buf_empty() && do_script_run(now)); 
#    endif

      /* Refresh cache */
      if (option_bool(OPT_SCRIPT_ARP))
	    find_mac(NULL, NULL, 0, now);
      while (helper_buf_empty() && do_arp_script_run());

      if (!helper_buf_empty())
	    poll_listen(daemon->helperfd, POLLOUT);
#endif
   
      /* must do this just before select(), when we know no
	 more calls to my_syslog() can occur */
      set_log_writer();
      
      if (do_poll(timeout) < 0)
	    continue;
      
      now = dnsmasq_time();

      check_log_writer(0);

      /* prime. */
      enumerate_interfaces(1);

      /* Check the interfaces to see if any have exited DAD state
	 and if so, bind the address. */
      if (is_dad_listeners())
	{
	  enumerate_interfaces(0);
	  /* NB, is_dad_listeners() == 1 --> we're binding interfaces */
	  create_bound_listeners(0);
	  warn_bound_listeners();
	}

#if defined(HAVE_LINUX_NETWORK)
      if (poll_check(daemon->netlinkfd, POLLIN))
	    netlink_multicast();
#elif defined(HAVE_BSD_NETWORK)
      if (poll_check(daemon->routefd, POLLIN))
	    route_sock();
#endif

#ifdef HAVE_INOTIFY
      if  (daemon->inotifyfd != -1 && poll_check(daemon->inotifyfd, POLLIN) && inotify_check(now))
	{
	  if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
	    poll_resolv(1, 1, now);
	} 	  
#endif

      if (poll_check(piperead, POLLIN))
	    async_event(piperead, now);
      
      my_syslog(LOG_ERR, _("11111-check_dns_listeners"));
      check_dns_listeners(now);

#ifdef HAVE_DHCP
      if (daemon->dhcp || daemon->relay4)
	{
	  if (poll_check(daemon->dhcpfd, POLLIN))
	    dhcp_packet(now, 0);
	  if (daemon->pxefd != -1 && poll_check(daemon->pxefd, POLLIN))
	    dhcp_packet(now, 1);
	}
#endif
2、dns解析过程

在uci里面添加option logqueries 1选项,重启dnsmasq,可以看到多出一些log,截取其中的一部分进行解析

query[A] www.baidu.com from 127.0.0.1
cached www.baidu.com is 220.181.38.148

query[AAAA] www.taobao.com from 127.0.0.1
forwarded www.taobao.com to 10.16.8.57
forwarded www.taobao.com to 10.16.8.206
query[AAAA] www.taobao.com.danuoyi.tbcache.com from 127.0.0.1
forwarded www.taobao.com.danuoyi.tbcache.com to 10.16.8.57
query[A] www.taobao.com from 127.0.0.1
forwarded www.taobao.com to 10.16.8.57
reply www.taobao.com is <CNAME>
reply www.taobao.com.danuoyi.tbcache.com is 103.15.99.90
reply www.taobao.com.danuoyi.tbcache.com is 103.15.99.91

第一部分为本地127.0.0.1请求www.baidu.com这个域名,这个域名在cache里面已经有了,所以直接从cache里面把结果返回。

第二部分为请求www.taobao.com的域名信息,这时候cache里面没有,所有就转达给dns服务器,使用ubus call network.interface.wan status可以看到dns服务器就是上面的10.16.8.5710.16.8.206这两个地址,然后得到reply返回值,得到淘宝的IP。

这两个地址是netifd在得到wan口的网关后,写入到resolv.conf.auto中,dnsmasq的配置项resolv-file=/tmp/resolv.conf.auto在要转发的时候就根据这边的地址转发给上级

root@Openwrt:/# cat /tmp/resolv.conf.auto
# Interface wan
nameserver 202.96.128.86
nameserver 202.96.134.33

上面的流程是这样的,对应的代码在哪个函数里面

在main()函数的while里面,加了个打印,可以看到每次dns请求/回复都会经过check_dns_listeners()函数

11111-check_dns_listeners
query[A] www.baidu.com from 127.0.0.1
forwarded www.baidu.com to 10.16.8.57
11111-check_dns_listeners
reply www.baidu.com is 140.143.178.227

check_dns_listeners()函数里面做对应的判断,如果是请求的则调用
receive_query()函数,在cache里面没有找到就调用forward_query()函数转发到dns服务器,查到结果后就使用reply_query()函数返回给对应的IP。

为了获取到哪个IP请求,请求的域名,在forward_query()函数里面打印,为daemon->namebuffdaemon->addrbuff

if (errno == 0)
{
  /* Keep info in case we want to re-send this packet */
  daemon->srv_save = start;
  daemon->packet_len = plen;
  
  my_syslog(LOG_ERR, _("2222-namebuff:%s,addrbuff:%s\r\n"), daemon->namebuff,daemon->addrbuff);

  if (!gotname)
    strcpy(daemon->namebuff, "query");
  if (start->addr.sa.sa_family == AF_INET)
    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
	      (struct all_addr *)&start->addr.in.sin_addr, NULL); 
#ifdef HAVE_IPV6
  else
    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
	      (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
#endif 
  start->queries++;
  forwarded = 1;
  forward->sentto = start;
  if (!forward->forwardall) 
    break;
  forward->forwardall++;
}
3、DHCP请求过程

dhcp的请求过程也比较直观,DHCP请求4步,如下:

 dnsmasq-dhcp[3294]: DHCPDISCOVER(br-lan) 30:ae:7b:e1:d3:8f
 dnsmasq-dhcp[3294]: DHCPOFFER(br-lan) 192.168.18.150 30:ae:7b:e1:d3:8f
 dnsmasq-dhcp[3294]: DHCPREQUEST(br-lan) 192.168.18.150 30:ae:7b:e1:d3:8f
 dnsmasq-dhcp[3294]: DHCPACK(br-lan) 192.168.18.150 30:ae:7b:e1:d3:8f

这边的dhcp网段IP的获取范围就是上面/var/etc/dnsmasq.conf里面的

dhcp-range=lan,192.168.18.100,192.168.18.249,255.255.255.0,12h

dhcp的设备列表都会被写入/tmp/dhcp.leases下

root@Openwrt:/# cat /tmp/dhcp.leases
1653726179 a0:a4:c5:1e:61:c1 192.168.18.127 LAPTOP-SFHGQM4K 01:a0:a4:c5:1e:61:c1
1653726146 30:ae:7b:e1:d3:8f 192.168.18.150 * 30:ae:7b:e1:d3:8f
4、rebind_protection域名劫持保护

有时候发现其他网络都可以正常访问,就公司内网的网络范文不了,那就是域名劫持在作祟。

由于上级dns返回的地址是个私有局域网地址,所以被看作是一次域名劫持,从而丢弃了解析的结果。

所以我们只需要设置rebind protection = 0,也就是反域名劫持保护关闭即可。

5、默认网关域名

lan口的IP是可以一直变的,这样就会导致我们如果路由器丝印上面打印的是IP就会不准确,所以一般会打印一个域名,访问这个域名就会直接范文默认网关。

/var/etc/dnsmasq.conf里面有个配置

addn-hosts=/tmp/hosts

在/etc/init.d/dnsmasq启动脚本里面一般会有如下信息,就是将/etc/config/system里面的hostname内容,还有lan的默认网关地址,通过dhcp_domain_add函数添加到//tmp/hosts/dhcp文件中。

# add own hostname
[ $ADD_LOCAL_HOSTNAME -eq 1 ] && {
        local lanaddr lanaddr6
        local ulaprefix="$(uci_get network @globals[0] ula_prefix)"
        local hostname="$(uci_get system @system[0] hostname Lede)"

        network_get_ipaddr lanaddr "lan" && {
                dhcp_domain_add "" "$hostname" "$lanaddr"
        }

        [ -n "$ulaprefix" ] && network_get_ipaddrs6 lanaddr6 "lan" && {
                for lanaddr6 in $lanaddr6; do
                        case "$lanaddr6" in
                                "${ulaprefix%%:/*}"*)
                                        dhcp_domain_add "" "$hostname" "$lanaddr6"
                                ;;
                        esac
                done
        }
}

所以我们只需要将uci里面的hostname改成我们想要的域名,不过这个方式不好,因为别的地方可能也会用到这个hostname的值

/etc/config/system
config system
        option hostname 'test12345 test1234567'

所以我们最好自己再添加一个uci值,如ownhostname,然后把/etc/init.d/dnsmasq里面的uci_get改成ownhostname就可以了

config system
        option ownhostname 'test12345 test1234567'

只有重启/etc/init.d/dnsmasq restart,可以看到 /tmp/hosts/dhcp下的内容变了

root@Openwrt:/# cat /tmp/hosts/dhcp
# auto-generated config file from /etc/config/dhcp
192.168.18.1 test12345 test1234567

ping测试下,获取用web访问测试下

C:\Users\lenovo>ping test12345.com

正在 Ping test12345.com [192.168.18.1] 具有 32 字节的数据:
来自 192.168.18.1 的回复: 字节=32 时间=1ms TTL=64
来自 192.168.18.1 的回复: 字节=32 时间=2ms TTL=64
来自 192.168.18.1 的回复: 字节=32 时间=2ms TTL=64
来自 192.168.18.1 的回复: 字节=32 时间=2ms TTL=64

其实还有一种改法,就是将我们需要的域名追加到/etc/hosts下面即可,这是linux默认的一种方式

root@Openwrt:/# cat /etc/hosts
127.0.0.1 localhost
192.168.18.1 test12345.com

6、静态分配地址(IP MAC绑定)
uci add dhcp host
uci set dhcp.@host[-1].name="example-host"
uci set dhcp.@host[-1].ip="192.168.1.230"
uci set dhcp.@host[-1].mac="00:a0:24:5a:33:69"
uci commit dhcp

/etc/init.d/dnsmasq restart

重启dnsmasq之后,我们可以看到/var/etc/dnsmasq.conf里面多了如下信息

dhcp-host=be:67:76:30:3c:a6,192.168.1.230,example-host

这样设备重新连接的时候,就会直接分配该固定IP给设备。

这里面其实还隐含了一个功能,就是设备hostname的设置,设备一般会有自己的名字,比如Iphone\HUAWEI_P30等,这时候如果我们想给这个设备重命令,则设置该name字段即可。

uci add dhcp host
uci set dhcp.@host[-1].name="example-host"
uci set dhcp.@host[-1].mac="00:a0:24:5a:33:69"
uci commit dhcp
uci add dhcp domain
uci set dhcp.@domain[-1].name="example-host"
uci set dhcp.@domain[-1].mac="00:a0:24:5a:33:69"
uci commit dhcp

这是dhcp.leases里面的名称就变了

root@Openwrt:/# cat /tmp/dhcp.leases 
1654886650 00:a0:24:5a:33:69 192.168.18.230 example-host 01:00:a0:24:5a:33:69

7、不同的域名指定不同的 DNS 进行解析

DNSmasq 可以设置不同的域名指定不同的 DNS 进行解析,修改 /etc/dnsmasq.conf 文件即可,若不对域名设置 DNS,则从上游 DNS 获取记录。

uci设置:

uci add_list dhcp.@dnsmasq[0].server=/taobao.com/114.114.114.114
uci add_list dhcp.@dnsmasq[0].server=/google.com/8.8.8.8

实际生效的/etc/dnsmasq.conf

#指定淘宝使用114 DNS进行解析
server=/taobao.com/114.114.114.114
#google指定8.8.8.8进行解析
server=/google.com/8.8.8.8
8、域名拦截(广告过滤)

也可以对指定的域名进行解析,相当于就是本地 hosts 指向,可以利用这个功能实现广告屏蔽等效果。DNSmasq 也可以对域名进行泛解析,填写 *.moewah.com, 这样的格式即可。

uci设置:

uci add_list dhcp.@dnsmasq[0].address=/ad.youku.com/127.0.0.1
uci add_list dhcp.@dnsmasq[0].address=/ad.iqiyi.com/127.0.0.1
uci add_list dhcp.@dnsmasq[0].address=/*.moewah.com/127.0.0.1

实际生效的/etc/dnsmasq.conf

#将广告域名指向到127.0.0.1实现广告屏蔽
address=/ad.youku.com/127.0.0.1
address=/ad.iqiyi.com/127.0.0.1
#对moewah.com进行泛解析
address=/*.moewah.com/119.23.184.172

Linux自建DNS服务器:DNSmasq安装与配置:https://www.vvso.cn/xlbk/20396.html

DNSmasq详细解析及详细配置:http://www.taodudu.cc/news/show-3688136.html?action=onClick

https://openwrt.org/zh/docs/guide-user/base-system/dhcp

https://openwrt.org/zh/docs/guide-user/base-system/dhcp.dnsmasq

https://www.freesion.com/article/6254617201/

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值