抓虫:sw架构防火墙服务启动失败

开始

已知:firewalld服务启动失败 且没什么日志。。。

# systemctl status firewalld
× firewalld.service - firewalld - dynamic firewall daemon
     Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Thu 2024-11-14 18:37:59 CST; 16h ago
       Docs: man:firewalld(1)
   Main PID: 1485 (code=exited, status=3)

11月 14 18:37:58 localhost.localdomain systemd[1]: Starting firewalld - dynamic firewall daemon...
11月 14 18:37:59 localhost.localdomain systemd[1]: Started firewalld - dynamic firewall daemon.
11月 14 18:37:59 localhost.localdomain systemd[1]: firewalld.service: Main process exited, code=exited, status=3/NOTIMPLEMENTED
11月 14 18:37:59 localhost.localdomain systemd[1]: firewalld.service: Failed with result 'exit-code'.
# journalctl -u firewalld
11月 14 18:37:58 localhost.localdomain systemd[1]: Starting firewalld - dynamic firewall daemon...
11月 14 18:37:59 localhost.localdomain systemd[1]: Started firewalld - dynamic firewall daemon.
11月 14 18:37:59 localhost.localdomain systemd[1]: firewalld.service: Main process exited, code=exited, status=3/NOT>
11月 14 18:37:59 localhost.localdomain systemd[1]: firewalld.service: Failed with result 'exit-code'.

调试

先根据systemctl status firewalld输出看一下服务在什么地方,看一下这个服务启动了什么命令

# cat /usr/lib/systemd/system/firewalld.service
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS

申威架构大多追踪软件也不可使用,忽略最后一个参数,执行下试试

这个脚本是python的,先使用python的pdb试一下,如果是python代码的地方报错pdb会停在那里

# file /usr/sbin/firewalld 
/usr/sbin/firewalld: Python script, ASCII text executable
# python3 -m pdb /usr/sbin/firewalld --nofork --nopid
> /usr/sbin/firewalld(27)<module>()
-> import os
(Pdb) r
mnl.c:44: Unable to initialize Netlink socket: 不支持的协议

额。。。并没有停住,看来是python调用c的时候上层封装没有抛异常

再使用gdb调试,知道会向stderr/stdout输出一条消息,所以设置断点,断点的条件是write的第一个参数是1或者2,1代表stdout,2代表stderr。设置断点在输出到stdout/stderr时候。$r4 r4寄存器是申威架构的函数传参约定的第一个参数寄存器,不同架构不一样。

firewalld会有好几次的write输出干扰,前几次的来自于pthread.so也有个write函数,没关系,多试几次就找到真正报错的地方了。

# gdb /usr/bin/python3
(gdb) set args /usr/sbin/firewalld --nofork --nopid
(gdb) b write if $r4 = 1
(gdb) b write if $r4 = 2
(gdb) r
(gdb) c
(gdb) c
(gdb) c
(gdb) c
(gdb) c
...
(gdb) bt
#0  0x00004000009a9f08 in write () from /usr/lib/libc.so.6.1
#1  0x00004000009368e8 in _IO_file_write@@GLIBC_2.1 () from /usr/lib/libc.so.6.1
#2  0x00004000009356fc in new_do_write () from /usr/lib/libc.so.6.1
#3  0x000040000093732c in __GI__IO_file_xsputn () from /usr/lib/libc.so.6.1
#4  0x00004000008ffd74 in buffered_vfprintf () from /usr/lib/libc.so.6.1
#5  0x0000400000902f5c in vfprintf@@GLIBC_2.4 () from /usr/lib/libc.so.6.1
#6  0x0000400000a1aa6c in fprintf@GLIBC_2.0 () from /usr/lib/libc.so.6.1
#7  0x0000400003d2bedc in __netlink_init_error () from /lib/libnftables.so.1.0.0
#8  0x0000400003d4475c in nft_mnl_socket_open () from /lib/libnftables.so.1.0.0
#9  0x0000400003d4c07c in nft_ctx_new () from /lib/libnftables.so.1.0.0
#10 0x0000400002076ff8 in ffi_call_osf () from /usr/lib/libffi.so.8
#11 0x000040000207680c in ffi_call_int () from /usr/lib/libffi.so.8
#12 0x00004000032db6fc in _ctypes_callproc () from /usr/lib/python3.6/lib-dynload/_ctypes.cpython-36m-sw_64-linux-gnu.so
#13 0x00004000032dd0b4 in PyCFuncPtr_call () from /usr/lib/python3.6/lib-dynload/_ctypes.cpython-36m-sw_64-linux-gnu.so
#14 0x00004000004d5784 in _PyObject_FastCallDict () from /usr/lib/libpython3.6m.so.1.0
#15 0x0000400000563928 in call_function () from /usr/lib/libpython3.6m.so.1.0

看到栈7和栈8,就是出错位置。这里执行了mnl_socket_open没有成功

struct mnl_socket *nft_mnl_socket_open(void)
{
	struct mnl_socket *nf_sock;

	nf_sock = mnl_socket_open(NETLINK_NETFILTER);
	if (!nf_sock)
		netlink_init_error();

	if (fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK))
		netlink_init_error();

	return nf_sock;
}

void __noreturn __netlink_init_error(const char *filename, int line,
				     const char *reason)
{
	fprintf(stderr, "%s:%d: Unable to initialize Netlink socket: %s\n",
		filename, line, reason);
	exit(NFT_EXIT_NONL);
}

看到这个函数来自于外部定义,readelf -s -W /lib/libnftables.so.1.0.0的输出中,第一个123是index,后面一个长长的0是elf文件中这个函数的地址,地址是0即代表这个函数是外部的,本elf中没有地址。

结合这个函数的名称,和readelf -d /lib/libnftables.so.1.0.0猜,这个函数来自于ibmnl.so.0

Tips: 找函数位于哪个动态库时应该使用readelf -d而不要使用ldd ldd命令是通过环境变量设置ld只加载不执行实现的,ld加载过程中会加载依赖动态库的依赖动态库,也就是说,firewalld依赖libmnl.so.0ldd也会显示出libmnl.so.0的依赖,但通常我们不关心libmnl.so.0的依赖

# readelf -s -W  /lib/libnftables.so.1.0.0 |grep mnl_socket_open
   123: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mnl_socket_open@LIBMNL_1.0 (6)
[root@localhost nftables-master]# readelf -d  /lib/libnftables.so.1.0.0 
 0x0000000000000001 (NEEDED)             共享库:[libmnl.so.0]

出错的mnl_socket_open(NETLINK_NETFILTER)实际会执行socket(AF_NETLINK, SOCK_RAW | NETLINK_NETFILTER, bus);,而很明显,socket是一个系统调用。。。

Tips: 这里说socket是一个系统调用,不代表c语言里直接执行socket就是一个系统调用的执行,通常glibc会对系统调用封装,比如系统调用的返回值glibc会设置到errno中,errno是用户态每个线程独享的一个成员和内核无关,即c语言里直接执行的socket是glibc对系统调用socket的一个封装。而且,系统调用的传参约定、触发方式和用户态函数并不相同,glibc也提供裸调用syscall转化参数寄存器方法。

struct mnl_socket *mnl_socket_open(int bus)
{
	return __mnl_socket_open(bus, 0);
}

static struct mnl_socket *__mnl_socket_open(int bus, int flags)
{
	struct mnl_socket *nl;

	nl = calloc(1, sizeof(struct mnl_socket));
	if (nl == NULL)
		return NULL;

	nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
	if (nl->fd == -1) {
		free(nl);
		return NULL;
	}

	return nl;
}

验证一下,确实是socket系统调用不支持网络过滤

# strace -e socket /usr/sbin/firewalld --nofork --nopid
socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER) = -1 EPROTONOSUPPORT (不支持的协议)
mnl.c:44: Unable to initialize Netlink socket: 不支持的协议
+++ exited with 3 +++

完结,内核不支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值