понедельник, 20 января 2014 г.

Неправильный порядок nameserver'ов в /etc/resolv.conf при работе NetworkManager через resolvconf

Недавно подключался к корпоративной сети в "полевых" условиях и наткнулся на несовместимость 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() и имя интерфейса более не передается. Позже попробую закинуть им баг на эту тему.

Комментариев нет:

Отправить комментарий