在LVS中的fullnat模式中,为了获取client的IP地址,会使用到LVS中的toa模块,该模块的作用就是替代系统中的getname系统调用,在rs端通过getname的系统调用获取到client的IP地址。在toa模块中使用到kallsyms_lookup_name函数,该函数的作用就是获取sock_def_readable函数的地址。但是在一些内核发行版中kallsyms_lookup_name函数并没有被EXPORT出来,所以在编译toa模块时,会出现下面的警告:
WARNING: "kallsyms_lookup_name" [/data/toa_v1/toa.ko] undefined!
使用insmod安装出现下面的提示信息: toa: Unknown symbol
kallsyms_lookup_name 安装失败。如果内核版本是直接维护的话,可以重新编译内核模块,EXPORT kallsyms_lookup_name函数,如果不方便更换内核,可以使用下面的两个方法进行规避:
1. kallsyms_lookup_name函数没有被EXPORT情况下的处理方法:
1)可以使用下面的方法获取到sock_def_readable结构体的地址,
grep sock_def_readable /proc/kallsyms
ffffffff8145a610 t sock_def_readable
grep sock_def_readable /boot/System.map-2.6.32-573.26.1.el6.x86_64
ffffffff8145a610 t sock_def_readable
获取到具体的地址后,可以在toa的代码中硬编码,
sk_data_ready_addr = ffffffff8145a610;
2) 在kallsyms_lookup_name没有被EXPORT时,可以使用kallsyms_on_each_symbol函数替换,该函数在大部分内核版本上都会被EXPORT,
点击(此处)折叠或打开
-
static int find_fn (void *data, const char *name, struct module *mod, unsigned long addr)
-
{
-
if (name != NULL && strcmp (name, "sock_def_readable") == 0) {
-
sk_data_ready_addr = addr;
-
return 1;
-
}
-
return 0;
-
}
-
-
static void get_fn_addr (void)
-
{
-
kallsyms_on_each_symbol (find_fn, 0);
- }
在static int __init toa_init(void)
{
…..
get_fn_addr();
…….
}
2. 在centos的2.6.32-573.26.1.el6.x86_64内核版本上,还出现了pagefault的情况,
<6>TOA: toa loaded
<1>BUG: unable to handle kernel paging request at ffffffff81677818
<1>IP: [] toa_exit+0x9/0xc9 [toa]
<4>PGD 1a8f067 PUD 1a93063 PMD 80000000016001e1
<4>Oops: 0003 [#1] SMP
<4>last sysfs file: /sys/devices/system/cpu/online
<4>CPU 0
<4>Modules linked in: toa(-)(U) xt_conntrack ipt_MASQUERADE iptable_filter iptable_nat ipt_addrtype ip_tables nf_nat nf_conntrack_ipv4
<4>
<4>Pid: 14149, comm: rmmod Not tainted 2.6.32-573.26.1.el6.x86_64 #1 Red Hat KVM
<4>RIP: 0010:[] [] toa_exit+0x9/0xc9 [toa]
<4>RSP: 0018:ffff8801389cbed8 EFLAGS: 00010282
该问题出现在hook_toa_functions函数中下面的代码中,
inet_stream_ops_p = (struct proto_ops *)&inet_stream_ops;
inet_stream_ops_p->getname = inet_getname_toa;
可以通过下面的方法解决:
点击(此处)折叠或打开
-
void set_addr_rw(unsigned long addr)
-
{
-
unsigned int level;
-
pte_t *pte = lookup_address(addr, &level);
-
-
if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
-
}
-
-
void set_addr_ro(unsigned long addr)
-
{
-
unsigned int level;
-
pte_t *pte = lookup_address(addr, &level);
-
-
pte->pte = pte->pte &~_PAGE_RW;
-
}
-
在hook_toa_functions函数中
-
inet_stream_ops_p = (struct proto_ops *)&inet_stream_ops;
-
set_addr_rw((unsigned long)(&inet_stream_ops.getname));
-
inet_stream_ops_p->getname = inet_getname_toa;
- set_addr_ro((unsigned long)(&inet_stream_ops.getname));