Что произойдет, если вы привязываетесь к определенному интерфейсу, а затем IP-адрес меняется? - PullRequest
0 голосов
/ 06 июля 2018

С помощью следующего фрагмента я привязываюсь к определенному интерфейсу

iface, err := net.InterfaceByName(ifaceName)

if err != nil {
    return nil, fmt.Errorf("ERR: Error using interface %q: %q", ifaceName, err.Error())
}

addrs, err := iface.Addrs()

if err != nil {
    return nil, fmt.Errorf("ERR: Error using interface %q: %q", ifaceName, err.Error())
}

if len(addrs) < 1 {
    return nil, fmt.Errorf("ERR: Interface %q has no addresses?", ifaceName)
}

ipAddr := addrs[0].(*net.IPNet).IP
udpAddr := &net.UDPAddr { IP: ipAddr }

, а затем используйте его в качестве локального адреса для прослушивания. Кто-нибудь знает, что произойдет, если IP-адрес интерфейса изменится?

1 Ответ

0 голосов
/ 06 июля 2018

Я считаю, что на этот вопрос лучше всего ответить с точки зрения сетевого взаимодействия с Linux, а не с языком go, поскольку серверу go для прослушивания сокета потребуется выполнить следующее:

Я описываю случай для TCP-сервера, но UDP-сервер будет аналогичным (в определенной степени!). Вы можете исследовать его самостоятельно, настроив простой сервер и запустив его с помощью strace, например:

strace -e trace=network nc -l -s 192.168.0.1 7777 (часть strace будет отображать системные вызовы, а часть nc -l будет прослушивать данный адрес)

Вывод (я советую вам попробовать нечто подобное на простом go-сервере!):

bind(3, {sa_family=AF_INET, sin_port=htons(7777), sin_addr=inet_addr("192.168.0.1")}, 16) = 0
listen(3, 1)                            = 0
accept4(3, 

Мы видим, что сервер блокирует входящий вызов и ожидает входящих соединений. То, что здесь происходит, более точно указано в справочных страницах Linux:

Если в очереди нет ожидающих соединений, а сокет не помеченный как неблокирующий, accept () блокирует вызывающего до связь присутствует. Если розетка помечена как неблокирующая и нет ожидающие соединения присутствуют в очереди, accept () завершается неудачно с ошибка EAGAIN или EWOULDBLOCK.

Теперь вопрос в том, как пакет, полученный интерфейсом хоста, попадает в этот сокет. Он должен пройти через стек TCP / IP и, наконец, попасть в наше приложение. Это не тривиальный процесс, и вы можете найти подробности о нем во многих руководствах по работе с сетями Linux ). По сути, происходит следующее (это ОЧЕНЬ ВЫСОКИЙ уровень описания / обобщения):

  1. Пакет поступает на сетевую карту, классифицируется и достигает правильного верхнего уровня (например, IP в случае пакетов IP)
  2. Пакет поступает на уровень IP, где заголовок IP проверяется среди другие вещи, а затем заголовок IP удаляется и пакет переносимый этим IP-пакетом передается на верхний уровень (например, в случае TCP - на уровень TCP)
  3. tcp_v4_rcv функция вызывается с уровня IP (в случае TCPv4), и пакет прибывает на уровень TCP. Здесь заголовок проверен и затем ищется открытый сокет для этого входящего пакета (звоните __tcp_v4_lookup).

На этом этапе, если сокет TCP для этого пакета не найден, пакет отбрасывается. В противном случае упакованный пакет передается процессу.

Чтобы прямо ответить на ваш вопрос: если IP-адрес изменяется, и старый сокет TCP был привязан к старому IP-адресу, пакет, полученный для этого сокета (который все еще будет открыт, если сервер работает и не обновляется), будет быть отброшенным где-нибудь в пути через сетевые части ядра Linux.

Вот Если вы действительно хотите углубиться в подробности, хорошее описание того, что происходит, можно найти в отличной статье Внутри Linux Packet Filter part 2 о том, что происходит над уровнем IP )

...