Недавно подключался к корпоративной сети в "полевых" условиях и наткнулся на несовместимость NetworkManager (0.9.4.0-10) и resolvconf (1.67) в Debian Wheezy. Проблема
известная, но патча или какого-то грамотного workaround'а я не увидел.
Если кратко, то записи о nameserver'ах в /etc/resolv.conf прописываются в неверном порядке если для подключения к сетям использовать NetworkManager. Если подробнее, то NetworkManager неправильно взаимодействует с resolvconf и передает неверное с точки зрения resolvconf имя интерфейса (точнее сказать не передает совсем). Resolvconf
требует указывать название интерфейса и название программы, которая обновляет информацию о резолверах (например /sbin/resolvconf -a ppp0.pppd). При этом добавление записи в /etc/resolv.conf выполняется согласно порядку интерфейса в /etc/resolvconf/interface-order.
Например вы подключились через 3G модем к сотовому оператору и название вашего интерфейса ppp0. Сотовый оператор выдает два nameserver'а (x.x.x.1 и x.x.x.2). Далее вы подключаетесь к корпоративной сети VPN и вам выдает еще два nameserver'a (y.y.y.1 и y.y.y.2). Чтобы все работало правильно нужно чтобы /etc/resolv.conf выглядел так:
nameserver y.y.y.1
nameserver y.y.y.2
nameserver x.x.x.1
nameserver x.x.x.2
Но вместо этого в /etc/resolv.conf записи выглядят так:
nameserver x.x.x.1
nameserver x.x.x.2
nameserver y.y.y.1
nameserver y.y.y.2
Поскольку корпоративные nameserver'а оказались в конце списка, то внутренние ресурсы оказываются недоступными.
В процессе изучения проблемы оказалось, что NetworkManager использует вызов /sbin/resolvconf -a NetworkManager (указывает все nameserver'а без указания принадлежности к интерфейсу). Один из вариантов - изменить файл /etc/resolvconf/interface-order, чтобы явно задать приоритет NetworkManager, но поскольку это NM не следует соглашению, то я решил поправить его код.
Нужный мне участок кода быстро нашелся в файле NetworkManager/src/dns-manager/nm-dns-manager.c. Для добавления и удаления записей через resolvconf используется функция dispatch_resolvconf()
#ifdef RESOLVCONF_PATH
static gboolean
dispatch_resolvconf (const char *domain,
char **searches,
char **nameservers,
const char *iface,
GError **error)
{
char *cmd;
FILE *f;
gboolean retval = FALSE;
if (! g_file_test (RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE))
return FALSE;
if (domain || searches || nameservers) {
cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH);
if ((f = popen (cmd, "w")) == NULL)
g_set_error (error,
NM_DNS_MANAGER_ERROR,
NM_DNS_MANAGER_ERROR_SYSTEM,
"Could not write to %s: %s\n",
RESOLVCONF_PATH,
g_strerror (errno));
else {
retval = write_resolv_conf (f, domain, searches, nameservers, error);
retval &= (pclose (f) == 0);
}
} else {
cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
nm_log_info (LOGD_DNS, "(%s): removing resolv.conf from %s", iface, RESOLVCONF_PATH);
if (nm_spawn_process (cmd) == 0)
retval = TRUE;
}
g_free (cmd);
return retval;
}
#endif
В выделенных строках видно какую команду выполняет NetworkManager. Чтобы исправить ситуацию достаточно добавить название интерфейса, который получает функция. Патч получился тривиальный
--- NetworkManager/src/dns-manager/nm-dns-manager.c.orig 2014-01-16 12:10:52.265616559 +0300
+++ NetworkManager/src/dns-manager/nm-dns-manager.c 2014-01-16 12:15:09.069611996 +0300
@@ -416,7 +416,7 @@ dispatch_resolvconf (const char *domain,
return FALSE;
if (domain || searches || nameservers) {
- cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
+ cmd = g_strconcat (RESOLVCONF_PATH, " -a ", iface, ".NetworkManager", NULL);
nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH);
if ((f = popen (cmd, "w")) == NULL)
g_set_error (error,
@@ -430,7 +430,7 @@ dispatch_resolvconf (const char *domain,
retval &= (pclose (f) == 0);
}
} else {
- cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
+ cmd = g_strconcat (RESOLVCONF_PATH, " -d ", iface, ".NetworkManager", NULL);
nm_log_info (LOGD_DNS, "(%s): removing resolv.conf from %s", iface, RESOLVCONF_PATH);
if (nm_spawn_process (cmd) == 0)
retval = TRUE;
После пересборки пакетов и обновления системы приоритет nameserver'ов правильный.
P.S. К сожалению этот патч не будет работать на последней версии NetworkManager, поскольку там изменился прототип функции dispatch_resolvconf() и имя интерфейса более не передается. Позже попробую закинуть им баг на эту тему.