Как найти сетевое пространство имен однорангового узла veth ifindex?

У меня была аналогичная проблема, но ядро ​​не могло загрузиться и / или загрузчик сказал, что нет доступного загрузочного образа.

Оказалось, что ISO-образ, который я записал на флеш-накопитель, был короче примерно на 100 МБ, несомненно, из-за неконтролируемой и неполной загрузки. Прошло так много времени с тех пор, как у меня была ошибка загрузки, что я перестал проверять sha256sum много лет назад.

Исправление было простым: снова загрузите .iso и сравните значение sha256 с опубликованным правильным значением. Перепрошить и загрузиться.

5
28.05.2018, 22:46
3 ответа

Большое спасибо @A.B, который восполнил некоторые недостающие части для меня, особенно в отношении семантики netnsids. Его PoC очень поучителен. Однако важным недостающим элементом в его PoC является то, как соотнести локальный netnsidс его глобально уникальным номером инода сетевого пространства имен, потому что только тогда мы можем однозначно соединить правильные соответствующие пары veth.

Подводя итоги и приведя небольшой пример Python, как собирать информацию программно, не полагаясь на ip netnsи необходимость монтирования :RTNETLINK фактически возвращает netnsid при запросе сетевых интерфейсов. Это атрибут IFLA_LINK_NETNSID, который появляется в информации о ссылке только при необходимости. Если его там нет, то он не нужен --и мы должны предположить, что индекс равноправного узла относится к интерфейсу локальной сети пространства имен -.

Важным уроком, который следует усвоить, является то, что netnsid/ IFLA_LINK_NETSID— это только локально определенное в пространстве имен сети, где вы получили его, когда запрашивали у RTNETLINK информацию о ссылке. netnsidс тем же значением, полученным в другом сетевом пространстве имен, может идентифицировать другое одноранговое пространство имен, поэтому будьте осторожны и не используйте netnsidза пределами своего пространства имен. Но какое уникально идентифицируемое сетевое пространство имен(inodeс номером )соответствует какому netnsid?

Как оказалось, самая последняя версияlsnsпо состоянию на март 2018 года вполне способна отображать правильный netnsidрядом с номером инода сетевого пространства имен! Таким образом, — это способ сопоставления локальных netnsidс инодами пространства имен, но на самом деле это наоборот! И это скорее оракул (со строчной буквой ell ), чем поиск :RTM _GETNSID требуется идентификатор пространства имен сети либо в виде PID, либо FD (в пространство имен сети ), а затем возвращается netnsid. См.https://stackoverflow.com/questions/50196902/retrieving-the-netnsid-of-a-network-namespace-in-pythonдля примера того, как запросить оракул сетевого пространства имен Linux.

Следовательно, вам необходимо перечислить доступные сетевые пространства имен (через /procи/или /var/run/netns),затем для данного vethсетевого интерфейса, присоединенного к сетевому пространству имен, где вы его нашли, запросите netnsidвсе сетевые пространства имен, которые вы перечислили в начале (, потому что вы никогда заранее не знаете, какое есть какое ), и, наконец, сопоставьте netnsidоднорангового узла vethс номером inode пространства имен в соответствии с локальной картой, которую вы создали на шаге 3 после присоединения к пространству имен veth.

import psutil
import os
import pyroute2
from pyroute2.netlink import rtnl, NLM_F_REQUEST
from pyroute2.netlink.rtnl import nsidmsg
from nsenter import Namespace

# phase I: gather network namespaces from /proc/[0-9]*/ns/net
netns = dict()
for proc in psutil.process_iter():
    netnsref= '/proc/{}/ns/net'.format(proc.pid)
    netnsid = os.stat(netnsref).st_ino
    if netnsid not in netns:
        netns[netnsid] = netnsref

# phase II: ask kernel "oracle" about the local IDs for the
# network namespaces we've discovered in phase I, doing this
# from all discovered network namespaces
for id, ref in netns.items():
    with Namespace(ref, 'net'):
        print('inside net:[{}]...'.format(id))
        ipr = pyroute2.IPRoute()
        for netnsid, netnsref in netns.items():
            with open(netnsref, 'r') as netnsf:
                req = nsidmsg.nsidmsg()
                req['attrs'] = [('NETNSA_FD', netnsf.fileno())]
                resp = ipr.nlm_request(req, rtnl.RTM_GETNSID, NLM_F_REQUEST)
                local_nsid = dict(resp[0]['attrs'])['NETNSA_NSID']
            if local_nsid != 2**32-1:
                print('  net:[{}] <--> nsid {}'.format(netnsid, local_nsid))
7
27.01.2020, 20:37

Вот метод, которым я следовал, чтобы понять, как понять эту проблему. Доступные инструменты кажутся пригодными для использования (с некоторой сверткой )для части пространства имен, и (ОБНОВЛЕНО )с помощью /sys/ можно легко получить индекс партнера. Так что это довольно долго, потерпите меня. Он состоит из двух частей (, которые расположены не в логическом порядке, но сначала пространство имен помогает объяснить наименования индексов ), используя обычные инструменты, а не какую-либо пользовательскую программу :

.
  • Сетевое пространство имен
  • Индекс интерфейса

Сетевое пространство имен

Эта информация доступна со свойством link-netnsidв выводе ip linkи может быть сопоставлена ​​с идентификатором в выводе ip netns. Можно «связать» сетевое пространство имен контейнера с ip netns, используя таким образом ip netnsв качестве специализированного инструмента. Конечно, было бы лучше сделать для этого специальную программу (немного информации о системных вызовах в конце каждой части ).

Об описании нсидов вот что man ip netnsрассказывает (курсив мой):

ip netns set NAME NETNSID - assign an id to a peer network namespace

This command assigns a id to a peer network namespace. This id is valid only in the current network namespace. This id will be used by the kernel in some netlink messages. If no id is assigned when the kernel needs it, it will be automatically assigned by the kernel. Once it is assigned, it's not possible to change it.

Хотя создание пространства имен с ip netnsне приведет к немедленному созданию netnsid, оно будет создано (в текущем пространстве имен, возможно, на «хосте» ), когда veth половина настроена на другое пространство имен. Поэтому он всегда устанавливается для типичного контейнера.

Вот пример использования контейнера LXC:

# lxc-start -n stretch-amd64

Появилась новая вет ссылка veth9RPX4M(это можно отследить с помощьюip monitor link). Вот подробная информация:

# ip -o link show veth9RPX4M
44: veth9RPX4M@if43: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master lxcbr0 state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
link/ether fe:25:13:8a:00:f8 brd ff:ff:ff:ff:ff:ff link-netnsid 4

Эта ссылка имеет свойство link-netnsid 4, сообщающее другой стороне, что она находится в сетевом пространстве имен с nsid 4.Как проверить, что это контейнер LXC? Самый простой способ получить эту информацию — заставить ip netnsповерить в то, что он создал сетевое пространство имен контейнера, выполнив операции, указанные на справочной странице .

# mkdir -p /var/run/netns
# touch /var/run/netns/stretch-amd64
# mount -o bind /proc/$(lxc-info -H -p -n stretch-amd64)/ns/net /var/run/netns/stretch-amd64

UPDATE3:Я не понимал, что найти обратно глобальное имя было проблемой. Вот он:

# ls -l /proc/$(lxc-info -H -p -n stretch-amd64)/ns/net
lrwxrwxrwx. 1 root root 0 mai    5 20:40 /proc/17855/ns/net -> net:[4026532831]

# stat -c %i /var/run/netns/stretch-amd64 
4026532831

Теперь информация извлекается с помощью:

# ip netns | grep stretch-amd64
stretch-amd64 (id: 4)

Это подтверждает, что узел veth находится в сетевом пространстве имен с тем же nsid = 4 = link -netnsid.

Контейнер/ip netns«ассоциацию» можно удалить (без удаления пространства имен, пока контейнер работает):

# ip netns del stretch-amd64

Примечание. :Именование nsid относится к сетевому пространству имен, обычно начинается с 0 для первого контейнера, а наименьшее доступное значение используется повторно с новыми пространствами имен.

Информация об использовании системных вызовов, полученная из strace:

  • для части ссылки :требуетсяAF_NETLINKсокет (, открытый с помощью socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)), запрос(sendmsg())информации о ссылке с типом сообщенияRTM_GETLINKи получение(recvmsg())ответа с типом сообщения RTM_NEWLINK.

  • для части netns nsid :тот же метод, сообщение запроса имеет типRTM_GETNSIDс типом ответа RTM_NEWNSID.

Я думаю, что есть библиотеки немного более высокого уровня для обработки этого:libnl . В любом случае это тема для SO .

Индекс интерфейса

Теперь будет легче понять, почему индекс ведет себя случайным образом. Давайте проведем эксперимент:

Сначала введите новое пространство имен сети, чтобы получить чистый (индекс )список:

# ip netns add test
# ip netns exec test bash
# ip netns id
test
# ip -o link 
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

Как заметил OP, lo начинается с индекса 1.

Добавим 5 сетевых пространств имен, создадим пары veth,затем наденьте на них конец:

# for i in {0..4}; do ip netns add test$i; ip link add type veth peer netns test$i ; done
# ip -o link|sed 's/^/    /'
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether e2:83:4f:60:5a:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0
3: veth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether 22:a7:75:8e:3c:95 brd ff:ff:ff:ff:ff:ff link-netnsid 1
4: veth2@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether 72:94:6e:e4:2c:fc brd ff:ff:ff:ff:ff:ff link-netnsid 2
5: veth3@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether ee:b5:96:63:62:de brd ff:ff:ff:ff:ff:ff link-netnsid 3
6: veth4@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether e2:7d:e2:9a:3f:6d brd ff:ff:ff:ff:ff:ff link-netnsid 4

Когда он отображает @if2 для каждого из них, становится совершенно ясно, что индекс и индекс интерфейса пространства имен партнера не являются глобальными, а для каждого пространства имен. Когда он отображает фактическое имя интерфейса, это отношение к интерфейсу в том же пространстве имен (, будь то одноранговый узел, мост, связь... ). Так почему же у veth0 не отображается одноранговый узел? Я считаю, что это ошибка ip link, когда индекс совпадает с самим собой. Простое двукратное перемещение одноранговой ссылки «решает» проблему здесь, потому что это приводит к изменению индекса. Я также уверен, что иногда ip linkвызывает другие недоразумения и вместо отображения @ifXX отображает интерфейс в текущем пространстве имен с тем же индексом.

# ip -n test0 link set veth0 name veth0b netns test
# ip link set veth0b netns test0
# ip -o link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth0@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether e2:83:4f:60:5a:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0
3: veth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether 22:a7:75:8e:3c:95 brd ff:ff:ff:ff:ff:ff link-netnsid 1
4: veth2@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether 72:94:6e:e4:2c:fc brd ff:ff:ff:ff:ff:ff link-netnsid 2
5: veth3@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether ee:b5:96:63:62:de brd ff:ff:ff:ff:ff:ff link-netnsid 3
6: veth4@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\    link/ether e2:7d:e2:9a:3f:6d brd ff:ff:ff:ff:ff:ff link-netnsid 4

ОБНОВЛЕНИЕ:повторное чтение информации в вопросе OP, индекс партнера (, но не nsid ), легко и однозначно доступен с помощьюcat /sys/class/net/interface/iflink.

ОБНОВЛЕНИЕ2:

Все эти iflink 2 могут показаться неоднозначными, но уникальным является сочетание nsid и iflink, а не только iflink. Для приведенного выше примера это:

interface    nsid:iflink
veth0        0:7
veth1        1:2
veth2        2:2
veth3        3:2
veth4        4:2

В этом пространстве имен (, а именно пространстве имен test), никогда не будет двух одинаковых пар nsid :.

Если посмотреть в каждой одноранговой сети противоположную информацию:

namespace    interface    nsid:iflink
test0        veth0        0:2
test1        veth0        0:3
test2        veth0        0:4
test3        veth0        0:5
test4        veth0        0:6

Но имейте в виду, что все 0:есть для каждого отдельного 0, который сопоставляется с одним и тем же одноранговым пространством имен (, а именно :пространством имен test, даже не с хостом ). Их нельзя сравнивать напрямую, потому что они привязаны к своему пространству имен. Таким образом, вся сравнимая и уникальная информация должна быть:

test0:0:2
test1:0:3
test2:0:4
test3:0:5
test4:0:6

Как только подтверждается, что "test0 :0" == "test1 :0" и т. д. (верно в этом примере, все сопоставляются с сетевым пространством имен, называемым testпо ip netns), тогда они могут реально сравнить.

О системных вызовах, все еще просматривая результаты strace, информация извлекается, как указано выше, изRTM_GETLINK.Теперь вся информация должна быть доступна:

локальный :индекс интерфейса сSIOCGIFINDEX/if_nametoindex
peer :и nsid, и индекс интерфейса сRTM_GETLINK.

Все это, вероятно, следует использовать с libnl .

9
27.01.2020, 20:37

Я создал простой скрипт, в котором перечислены все контейнеры с соответствующим veth-интерфейсом:https://github.com/samos123/docker-veth/blob/master/docker-veth.sh

Позвольте мне объяснить, как это работает:

  1. Найти PID контейнера
pid=$(docker inspect --format '{{.State.Pid}}' $containerID)
  1. Введите пространство имен сети, используяnsenter
nsenter -t $pid -n ip a

Вы заметите, что внутри сетевого пространства имен контейнера есть интерфейс eth0@ifX. X сообщает вам индекс интерфейса в хост-сети.Затем этот индекс можно использовать, чтобы выяснить, какой veth принадлежит контейнеру.

Выполните следующие команды, чтобы найти интерфейс veth:

ifindex=$(nsenter -t $pid -n ip link | sed -n -e 's/.*eth0@if\([0-9]*\):.*/\1/p')
veth=$(ip -o link | grep ^$ifindex | sed -n -e 's/.*\(veth[[:alnum:]]*@if[[:digit:]]*\).*/\1/p')
echo $veth

Сообщение в блоге с более подробной информацией:http://samos-it.com/posts/enter-namespace-of-other-containers-from-a-pod.html

2
27.01.2020, 20:37

Теги

Похожие вопросы