Pergunta

Eu tenho um requisito um pouco estranho para ser capaz de ouvir uma série de interfaces de rede de Java em uma máquina Linux e determinar se um deles recebe pacotes UDP de um certo tipo. Os dados de saída que precisam I é o endereço IP da interface em questão. Existe alguma maneira de fazer isso em Java?

escutando no endereço wildcard (new DatagramSocket (port)) não ajuda, porque enquanto eu faço obter os pacotes de transmissão, não posso determinar o endereço IP local da interface que veio. Ouvir transmissões ao ser obrigado a uma determinada interface (new DatagramSocket (port, endereço)) não receber os pacotes em tudo. Este caso merece um exemplo de código que mostra o que estou tentando fazer:

Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
  NetworkInterface ni = (NetworkInterface) interfaces.nextElement();
  Enumeration addresses = ni.getInetAddresses(); 
  while (addresses.hasMoreElements()) { 
    InetAddress address = (InetAddress)addresses.nextElement();
    if (address.isLoopbackAddress() || address instanceof Inet6Address) 
      continue; //Not interested in loopback or ipv6 this time, thanks
    DatagramSocket socket = new DatagramSocket(PORT, address);
     //Try to read the broadcast messages from socket here
  }
}

Eu também tentei para inicializar o soquete com o endereço de transmissão construídas com base no início do IP real da interface e o resto de acordo com a máscara de rede correta:

byte [] mask = { (byte)255, 0, 0, 0 };
byte[] addrBytes = InetAddress.getByName("126.5.6.7").getAddress();
for (int i=0; i < 4; i++) {
  addrBytes[i] |= ((byte)0xFF) ^ mask[i];
}
InetAddress bcastAddr = InetAddress.getByAddress(addrBytes);

Isso só lança uma BindException ao construir o DatagramSocket.

EDIT: BindException (java.net.BindException: Não é possível atribuir endereço solicitado) de chamar o construtor de DatagramSocket com um endereço de broadcast (por exemplo, 126.255.255.255) só vem com o mais recente Ubuntu 9.04 (provavelmente não Ubuntu, mas questão específica kernel-versão embora). Com o Ubuntu 8.10 isso funcionou, bem como com o lançamento do Red Hat (RHEL 4.x) Eu estou lidando.

Aparentemente não receber os pacotes enquanto vinculado a um determinado IP local é o comportamento correto , embora no Windows este obras. Eu preciso fazê-lo funcionar no Linux (RHEL e Ubuntu). Com baixo nível C-código há uma setsockopt solução alternativa (SO_BINDTODEVICE) que não consigo encontrar em Java APIs. Este não exatamente me fazer explodir com optimismo embora: -)

Foi útil?

Solução

Este foi um problema kernel do Linux IPV6 no final. Normalmente eu ter desabilitado IPV6, porque faz com que todos os tipos de dores de cabeça. No entanto, em Ubuntu 9.04 é tão difícil para desabilitar o IPv6, que eu desisti, e que pouco me.

Para ouvir mensagens de difusão de uma determinada interface, eu vou primeiro criar a "versão broadcast" de endereço IP da interface:

byte [] mask = { (byte)255, 0, 0, 0 };
byte[] addrBytes = InetAddress.getByName("126.5.6.7").getAddress();
for (int i=0; i < 4; i++) {
  addrBytes[i] |= ((byte)0xFF) ^ mask[i];
}
InetAddress bcastAddr = InetAddress.getByAddress(addrBytes);

Com certeza, isso realmente não ligam me a uma certa interface se muitas interfaces ter um IP que começa com a mesma parte de rede, mas para mim esta solução é suficiente.

Então eu criar o DatagramSocket com esse endereço (e a porta desejada), e ele funciona. Mas não sem passar as seguintes propriedades do sistema para a JVM:

-Djava.net.preferIPv6Addresses=false -Djava.net.preferIPv4Stack=true 

Eu não tenho nenhuma idéia de como IPV6 consegue quebrar ouvir transmissões, mas ele faz, e os parâmetros acima corrigi-lo.

Outras dicas

Para reafirmar o seu problema, você precisa determinar quais os pacotes UDP interface de transmissão foram recebidas por diante.

  • Se você ligar o endereço curinga receber as transmissões, mas não há nenhuma maneira de determinar qual endereço de rede o pacote foi recebido por diante.
  • Se você se ligam a um determinado interface que você sabe qual endereço de interface que está a receber, mas não receberá mais as transmissões (pelo menos na pilha TCP / IP Linux.).

Como já foi mencionado, existem terceiros matérias-sockets bibliotecas para Java, como Rocksaw ou Jpcap , o que pode ajudar a determinar o endereço do a interface real.

Não sei se isso ajuda, mas eu sei que para obter uma lista de todas as interfaces de rede:

Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();

Talvez você possa ligar-se a cada um de forma independente?

Só encontrei alguns bons exemplos sobre o uso de getNetworkInterfaces ().

Para o melhor de meu conhecimento a única maneira de fazer isso seria com o

IP_RECVDSTADDR

opção tomada. Esta opção é suposto para se certificar de que o endereço dst da interface do pacote veio em está disponível quando ligado para o endereço curinga. Por isso, deve trabalhar com transmissão também eu assumo.

Aqui está um exemplo C Peguei fora da internet:

Como chegar destino UDP endereço em pacotes de entrada

Gostaria de ler sobre recvmsg e, em seguida, tentar descobrir se esta interface está disponível em Java.

Editar:

Eu só percebi que você pode ter mais uma opção se ele é suportado em Java. Você ainda pode precisar a opção de socket IP_RECVDSTADDR (não tenho certeza), mas em vez de usar recvmsg você poderia usar um socket raw e obter o endereço de destino do cabeçalho IP.

Abra sua tomada como a utilização de SOCK_RAW e você vai ter o cabeçalho IP completo no início de cada mensagem, incluindo endereços de origem e de destino.

Aqui está um exemplo do uso de UDP com um soquete cru em C no Linux:

TCP / IP Avançado - Exemplos O RAW SOCKET PROGRAMA

Eu ficaria surpreso se este método não funciona em Java também.

Edit2

Uma mais idéia. Existe uma razão que você não pode usar Multicast ou uma razão específica que você escolheu Transmissão sobre Multicast? Tanto quanto eu entendo com Multicast que você sempre sabe qual interface os pacotes são recebidos em desde que você sempre se ligam a uma interface específica quando juntar um grupo Multicast (especialmente com IP4 onde se ligam à interface via um de seus endereços IP).

Não é possível comentar, assim que adicionar isso como uma resposta em seu lugar.

Isso é interessante. Embora sou curioso porque você faz

byte[] addrBytes = InetAddress.getByName("126.5.6.7").getAddress();

em vez de apenas

byte[] addrBytes = {126, 5, 6, 7);

ou é que os endereços de interface chegar até você as String?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top