0%

嵌入式 ARM 板子 Busybox 域名解析失败

嵌入式 ARM 平台 busybox 可以 ping ip,但是不能 ping domain name

本篇主要涉及

  • gcc v4.9
  • libnss 动态链接库
  • busybox 动态与静态编译
  • busybox 动态编译下使用域名解析
  • busybox 静态编译下使用域名解析

环境

  • Linux v4.9.22
  • Busybox v1.27.2
  • Gcc v4.9

现象

ping IP 可以正常工作, ping domain name 时报错如下

# ping www.baidu.com
ping: bad address 'www.baidu.com'
# nslookup www.baidu.com
Server:    192.168.110.1
Address 1: 192.168.110.1

nslookup: can't resolve 'www.baidu.com'

确认环境设置

需要确认路由表及 nameserver,如下:

# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.110.1   0.0.0.0         UG    0      0        0 eth0
192.168.110.0   *               255.255.254.0   U     0      0        0 eth0

# cat /etc/resolv.conf
nameserver 192.168.110.1
nameserver 114.114.114.114
nameserver 202.101.172.35

相同的配置在使用 uclibc 编译的开发板上工作正常

添加默认路由表命令如下

route add default gw 192.168.110.1 dev eth0

相关信息

  1. Busybox ping IP works, but hostname nslookup fails with “bad address”
  2. ping: bad address ‘www.baidu.com’
  3. FS#41769 - [busybox] DNS lookups don’t work
  4. Name resolution in busybox
  5. libc6 version 2.19 breaks NSS loading for static binaries
  6. Linux 文件系统域名解析实现
  7. arm 开发板无法使用 dns 解析问题

包括 busybox 下文档 busybox_footer.pod 下解释

LIBC NSS

GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior
of the C library for the local environment, and to configure how it reads
system data, such as passwords and group information. This is implemented
using an /etc/nsswitch.conf configuration file, and using one or more of the
/lib/libnss_* libraries. BusyBox tries to avoid using any libc calls that make
use of NSS. Some applets however, such as login and su, will use libc functions
that require NSS.

If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to
directly access the /etc/passwd, /etc/group, and /etc/shadow files without
using NSS. This may allow you to run your system without the need for
installing any of the NSS configuration files and libraries.

When used with glibc, the BusyBox ‘networking’ applets will similarly require
that you install at least some of the glibc NSS stuff (in particular,
/etc/nsswitch.conf, /lib/libnss_dns, /lib/libnss_files, and /lib/libresolv*).

Shameless Plug: As an alternative, one could use a C library such as uClibc. In
addition to making your system significantly smaller, uClibc does not require the
use of any NSS support files or libraries.

从以上可以获知:

  1. libnss 设计就是动态库,不建议静态编译。要静态编译,请先重新编译 glibc,把 –enable-libnss-static 放上
  2. glibc 需要 libnss 库支持,编译工具链中直接包含了相关的文件,是动态库
    ✔ ~/gcc-linaro-4.9.4-2017.01-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib > ll libnss_dns*
     libnss_dns-2.19-2014.08-1-git.so
     libnss_dns.so.2 -> libnss_dns-2.19-2014.08-1-git.so
    ✔ ~/gcc-linaro-4.9.4-2017.01-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib > ll libnss_files*
     libnss_files-2.19-2014.08-1-git.so
     libnss_files.so.2 -> libnss_files-2.19-2014.08-1-git.so
    ✔ ~/gcc-linaro-4.9.4-2017.01-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib > ll libresolv*
     libresolv-2.19-2014.08-1-git.so
     libresolv.so.2 -> libresolv-2.19-2014.08-1-git.so
  3. busybox 需要动态编译来完成对 libnss 的使用

NSS(Name Service Switch 与 nsswitch.conf

Name Service Switch

NSS(Name Service Switch)类 unix 操作系统 中的一种工具,它为通用配置数据库和名称解析机制提供了各种来源。这些源文件包括本地操作系统文件(例如 /etc/passwd/etc/group/etc/hosts )、 域名系统 (DNS)网络信息服务 (NIS)LDAP

nsswitch.conf(name service switch configuration,名称服务切换配置) 文件位于 /etc 目录下,由它规定通过哪些途径以及按照什么顺序以及通过这些途径来查找特定类型的信息,还可以指定某个方法奏效或失效时系统将采取什么动作

本篇文章中涉及到 DNS 解析顺序

解决方案

动态编译

busybox 使用动态编译,然后将 libnss 相关文件拷贝到 rootfs/lib 下,并配置文件 /etc/hosts, /etc/resolv.conf, /etc/nsswitch.conf

静态编译

参考文章:nslookup 在静态编译的 busybox 上如何正常解析域名

使用 getaddrinfo 进行域名解析。这个函数是 libnss 里的一个 API (可以在 glibc 源码中查看 nss 目录)。解决办法是绕过它,自己实现域名解析

搜索 busybox 源文件

busybox-1.27.2 > grep "getaddrinfo" * -Rn
libbb/inet_common.c:160:    s = getaddrinfo(name, NULL, &req, &ai);
libbb/inet_common.c:162:        bb_error_msg("getaddrinfo: %s: %d", name, s);
libbb/xconnect.c:5: * Connect to host at port using address resolution from getaddrinfo
libbb/xconnect.c:247:    /* Next two if blocks allow to skip getaddrinfo()
libbb/xconnect.c:249:     * getaddrinfo() initializes DNS resolution machinery,
libbb/xconnect.c:285:    rc = getaddrinfo(host, NULL, &hint, &result);
networking/nslookup.c:90:    rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
networking/nslookup.c:200:    /* getaddrinfo and friends are free to request a resolver
networking/nslookup.c:202:     * after getaddrinfo (in server_print). This reportedly helps

inet_common.cxconnect.c 以及 nslookup.c 这三个文件中的 getaddrinfo 使用 getaddrinfo.h 覆盖解决此问题

Ref

  1. LINUX 系统下的 NSS 服务和 /ETC/NSSWITCH.CONF 文件学习
  2. DNS RESPONSE MESSAGE FORMAT
  3. DNS Query Code in C with linux sockets