Tarefa
Eu preciso de forma inequívoca e sem suposições "holísticas" encontrar a interface de rede peer de um veth final em outro namespace de rede.
Teoria ./. Realidade
Embora muita documentação e também respostas aqui no SO assumam que os índices ifindex de interfaces de rede são globalmente exclusivos por host em namespaces de rede, isso não ocorre em muitos casos : ifindex/iflink
são ambíguos . Até o loopback já mostra o contrário, tendo um ifindex de 1 em qualquer namespace de rede. Além disso, dependendo do ambiente do contêiner, os ifindex
números são reutilizados em diferentes namespaces . O que torna o rastreamento da fiação veth um pesadelo, especialmente com muitos contêineres e uma ponte de host com veth peers, todos terminando em @if3 ou algo assim ...
Exemplo: link-netnsid
é0
Gire uma instância de contêiner do Docker, apenas para obter um novo veth
par conectando-se do namespace da rede do host ao novo namespace da rede do contêiner...
$ sudo docker run -it debian /bin/bash
Agora, no namespace da rede do host, liste as interfaces de rede (deixei de fora as interfaces que não interessam a esta pergunta):
$ show de link ip 1: lo: mtu 65536 qdisc noqueue estado DESCONHECIDO modo DEFAULT padrão do grupo qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 ... 4: docker0: mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/éter 02:42:34:23:81:f0 brd ff:ff:ff:ff:ff:ff ... 16: vethfc8d91e@if15: mtu 1500 qdisc noqueue master docker0 estado UP mode DEFAULT group default link/éter da:4c:f7:50:09:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
Como você pode ver, embora o iflink
seja inequívoco, mas o link-netnsid
é 0, apesar da extremidade do par estar em um namespace de rede diferente.
Para referência, verifique o netnsid no namespace de rede sem nome do contêiner:
$ sudo lsns -t rede NS TIPO NPROCS PID COMANDO DO USUÁRIO ... ... 4026532469 net 1 29616 root /bin/bash $ sudo nsenter -t 29616 -n show de link ip 1: lo: mtu 65536 qdisc noqueue estado DESCONHECIDO modo DEFAULT padrão do grupo qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 15: eth0@if16: mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
Portanto, para ambas as extremidades veth ip link show
(e RTNETLINK fwif) nos diz que eles estão no mesmo namespace de rede com netnsid 0. O que está errado ou correto sob as suposições de que os netnsids de link são locais em oposição a globais. Não consegui encontrar nenhuma documentação que tornasse explícito qual escopo o link-netnsids deveria ter.
/sys/class/net/...
NÃO para o resgate?
Procurei em /sys/class/net/ if /... mas só consigo encontrar os elementos ifindex e iflink; estes estão bem documentados. "ip link show" também parece mostrar apenas o par ifindex na forma da (in)famosa notação "@if#". Ou eu perdi algum elemento de namespace de rede adicional?
Conclusão/Pergunta
Existem syscalls que permitem recuperar as informações de namespace de rede ausentes para o ponto final de um par veth?
Aqui está o método que segui para descobrir como entender esse problema. As ferramentas disponíveis parecem utilizáveis (com alguma convolução) para a parte do namespace e (ATUALIZADA) usando /sys/ pode obter facilmente o índice do par. Portanto, é bastante longo, tenha paciência comigo. Está em duas partes (que não estão na ordem lógica, mas primeiro o namespace ajuda a explicar a nomenclatura do índice), usando ferramentas comuns, não qualquer programa personalizado:
Espaço de nomes de rede
Essas informações estão disponíveis com a propriedade
link-netnsid
na saída deip link
e podem ser combinadas com o id na saída deip netns
. É possível "associar" o namespace de rede de um container comip netns
, usando assimip netns
como uma ferramenta especializada. Claro que fazer um programa específico para isso seria melhor (algumas informações sobre syscalls no final de cada parte).Sobre a descrição do nsid, eis o que
man ip netns
diz (grifo meu):Embora a criação de um namespace com
ip netns
não crie imediatamente um netnsid, ele será criado (no namespace atual, provavelmente o "host") sempre que um veth half for definido para outro namespace. Portanto, é sempre definido para um contêiner típico.Aqui está um exemplo usando um contêiner LXC:
Um novo link veth
veth9RPX4M
apareceu (isso pode ser rastreado comip monitor link
). Aqui estão as informações detalhadas:Este link tem a propriedade
link-netnsid 4
, informando que o outro lado está no namespace da rede com nsid 4. Como verificar se é o contêiner LXC? A maneira mais fácil de obter essas informações éip netns
acreditar que ele criou o namespace de rede do contêiner, fazendo as operações sugeridas na página de manual .ATUALIZAÇÃO3 : Não entendi que encontrar o nome global era um problema. Aqui está:
Agora a informação é recuperada com:
Ele confirma que o par do veth está no namespace da rede com o mesmo nsid = 4 = link-netnsid.
O contêiner/
ip netns
"associação" pode ser removido (sem remover o namespace, desde que o contêiner esteja em execução):Observação: a nomenclatura nsid é por namespace de rede, geralmente começa com 0 para o primeiro contêiner e o valor mais baixo disponível é reciclado com novos namespaces.
Sobre o uso de syscalls, aqui estão as informações adivinhadas do strace:
para a parte do link: requer um
AF_NETLINK
socket (aberto comsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
), pedindo (sendmsg()
) as informações do link com um tipo de mensagemRTM_GETLINK
e recuperando (recvmsg()
) a resposta com um tipo de mensagemRTM_NEWLINK
.para a parte netns nsid: mesmo método, a mensagem de consulta é digitada com tipo
RTM_GETNSID
de respostaRTM_NEWNSID
.Acho que as bibliotecas de nível um pouco mais alto para lidar com isso estão lá: libnl . De qualquer forma, é um tópico para SO .
índice de interface
Agora será mais fácil entender por que o índice parece ter comportamentos aleatórios. Vamos fazer um experimento:
Primeiro insira um novo espaço de nomes de rede para ter uma lista limpa (índice):
Como observou OP, lo começa com o índice 1.
Vamos adicionar 5 net namespaces, criar pares veth e colocar um veth end neles:
Quando está exibindo @if2 para cada um deles, fica bem claro que é o índice da interface de namespace do peer e o índice não é global, mas por namespace. Quando está exibindo um nome de interface real, é uma relação com uma interface no mesmo espaço de nome (seja peer, bridge, bond ...). Então, por que veth0 não tem um par exibido? Acredito que seja um
ip link
bug quando o índice é igual a ele mesmo. Apenas mover duas vezes o link do par "resolve" aqui, porque forçou uma mudança de índice. Também tenho certeza que às vezesip link
faço outras confusões e, em vez de exibir @ifXX, exibe uma interface no namespace atual com o mesmo índice.ATUALIZAÇÃO : lendo novamente as informações na pergunta do OP, o índice do par (mas não o nsid) está disponível de maneira fácil e inequívoca com .
cat /sys/class/net/
interface
/iflink
ATUALIZAÇÃO2 :
Todos esses iflink 2 podem parecer ambíguos, mas o que é único é a combinação de nsid e iflink, não apenas iflink. Para o exemplo acima, isto é:
Neste namespace (ou seja, namespace
test
), nunca haverá dois mesmos nsid:pair .Se alguém procurasse em cada rede de pares as informações opostas:
Mas tenha em mente que tudo o
0:
que existe para cada um é um 0 separado, que mapeia para o mesmo namespace de mesmo nível (a saber: namespacetest
, nem mesmo o host). Eles não podem ser comparados diretamente porque estão vinculados ao seu namespace. Portanto, todas as informações comparáveis e exclusivas devem ser:Uma vez confirmado que "test0:0" == "test1:0" etc. (verdadeiro neste exemplo, todos são mapeados para o namespace net chamado
test
porip netns
), eles podem ser realmente comparados.Sobre syscalls, ainda olhando para os resultados do strace, as informações são recuperadas como acima de
RTM_GETLINK
. Agora deve haver todas as informações disponíveis:local: índice de interface com
SIOCGIFINDEX
/ peer: nsid e índice de interface com .if_nametoindex
RTM_GETLINK
Tudo isso provavelmente deve ser usado com libnl .
Muito obrigado a @AB que preencheu algumas lacunas para mim, especialmente no que diz respeito à semântica de
netnsid
s. Seu PoC é muito instrutivo. No entanto, a peça crucial que falta em seu PoC é como correlacionar um localnetnsid
ao seu número de inode de namespace de rede globalmente exclusivo, porque somente assim podemos conectar inequivocamente osveth
pares correspondentes corretos.Para resumir e dar um pequeno exemplo Python de como reunir as informações programaticamente sem ter que confiar
ip netns
e montar coisas: RTNETLINK na verdade retorna o netnsid ao consultar interfaces de rede. É oIFLA_LINK_NETNSID
atributo, que só aparece nas informações de um link quando necessário. Se não estiver lá, então não é necessário -- e devemos assumir que o índice de peer refere-se a uma interface de rede local de namespace.A lição importante para levar para casa é que um
netnsid
/IFLA_LINK_NETSID
só é definido localmente dentro do namespace da rede onde você o obteve ao solicitar informações de link ao RTNETLINK. Umnetnsid
com o mesmo valor obtido em um namespace de rede diferente pode identificar um namespace de peer diferente, portanto, tome cuidado para não usar onetnsid
fora de seu namespace.inode
Mas qual namespace ( número) de rede identificável exclusivamente mapeia para qualnetnsid
?Como se vê, uma versão muito recente
lsns
de março de 2018 é bem capaz de mostrar o corretonetnsid
ao lado de seu número de inode de namespace de rede! Portanto, existe uma maneira de mapearnetnsid
s locais para inodes de namespace, mas na verdade é inverso! E é mais um oráculo (com um ell minúsculo) do que uma pesquisa: RTM_GETNSID precisa de um identificador de namespace de rede como um PID ou FD (para o namespace de rede) e então retorna o arquivonetnsid
. Consulte https://stackoverflow.com/questions/50196902/retrieving-the-netnsid-of-a-network-namespace-in-python para obter um exemplo de como perguntar ao oráculo de namespace da rede Linux.Em conseqüência, você precisa enumerar os namespaces de rede disponíveis (via
/proc
e/ou/var/run/netns
), então, para uma determinadaveth
interface de rede anexar ao namespace de rede onde você o encontrou, peça osnetnsid
s de todos os namespaces de rede que você enumerou no início (porque você nunca sabe de antemão qual é qual) e, finalmente, mapeie onetnsid
doveth
peer para o número de inode do namespace de acordo com o mapa local que você criou na etapa 3 após anexar aoveth
namespace do 's.Criei um script simples que lista todos os contêineres com a interface veth associada: https://github.com/samos123/docker-veth/blob/master/docker-veth.sh
Deixe-me explicar como funciona:
nsenter
Você notará que há uma
eth0@ifX
interface dentro do namespace da rede do contêiner. O X informa o índice da interface na rede do host. Este índice pode então ser usado para descobrir qual veth pertence ao contêiner.Execute os seguintes comandos para localizar a interface veth:
Postagem no blog com mais detalhes: http://samos-it.com/posts/enter-namespace-of-other-containers-from-a-pod.html