Este sítio web funciona com IPv6. Se o globo estiver girando, você também já usa IPv6!

#define PORTA 1000
#define SERVIDOR "fe80::1234:4568"
#define INTERFACE "eth0"
sockaddr_in6 end_servidor;
bzero(&end_servidor, sizeof(end_servidor));
#ifdef SIN6_LEN // necessário em BSD, desnecessário em Linux
end_servidor.sin6_len = SIN6_LEN;
#endif
end_servidor.sin6_family = AF_INET6;
end_servidor.sin6_port = htons(PORTA);
inet_pton(AF_INET6, SERVIDOR, &end_servidor.sin6_addr); //se o endereço não fosse
//link-local, bastaria preencher
// sin6_scope_id com zero
end_servidor.sin6_scope_id = if_nametoindex(INTERFACE);
// descritor de arquivo
int fd;
if((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
// soquete inválido (IPv6 não suportado na máquina)
exit(1);
}
if (connect(fd, (sockaddr*) &end_servidor, sizeof(end_servidor))) {
// conexão falhou
exit(2);
}
Daqui para diante, absolutamente nada muda seja qual for o protocolo de rede; as funções de comunicação como read(), write(), send(), recv(), writev(), readv(), shutdown() e close() funcionam da mesma forma.
#define PORTA 1000
// criamos um endereço IPv6 "coringa" (0000::0000), para atrelar
// todas as interfaces com bind()
sockaddr_in6 ia6_any;
bzero(&ia6_any, sizeof(ia6_any));
#ifdef SIN6_LEN
// necessário em BSD, desnecessário em Linux
ia6_any.sin6_len = SIN6_LEN;
#endif
ia6_any.sin6_family = AF_INET6;
ia6_any.sin6_port = htons(PORTA);
ia6_any.sin6_addr = in6addr_any;
ia6_any.sin6_scope_id = 0;
// descritor de arquivo
int fd;
if((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
// soquete inválido (IPv6 não suportado na máquina)
exit(1);
}
if (bind(fd, (sockaddr*) &ia6_any, sizeof(ia6_any))) {
// bind() falhou, provavelmente outro já está bind()ado à porta
exit(1);
}
listen(fd, 5);
for(;;) {
sockaddr_in6 end_cliente;
socklen_t len_cliente = sizeof(end_cliente);
int fd_conexao = accept(fd, (sockaddr*) &end_cliente, &len_cliente);
// comunica-se com o cliente
close(fd_conexao);
}
Assim como no código de exemplo para cliente TCP, a comunicação em si faz uso das mesmas diretivas (send(), recv() etc.) seja qual for o protocolo de rede.
#define PORTA 1000
// criamos um endereço IPv6 "coringa" (0000::0000), para atrelar
// todas as interfaces com bind()
sockaddr_in6 ia6_any;
bzero(&ia6_any, sizeof(ia6_any));
#ifdef SIN6_LEN
// necessário em BSD, desnecessário em Linux
ia6_any.sin6_len = SIN6_LEN;
#endif
ia6_any.sin6_family = AF_INET6;
ia6_any.sin6_port = htons(PORTA);
ia6_any.sin6_addr = in6addr_any;
ia6_any.sin6_scope_id = 0;
// descritor de arquivo
int fd;
if((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
// soquete inválido (IPv6 não suportado na máquina)
exit(1);
}
// Este segmento torna o soquete não bloqueante, ou seja, recv(), recvfrom()
// e recvmsg() retornam imediamente com erro se não houver dados a receber.
//
// A maioria dos programas usa select() ao invés de soquetes não bloqueantes.
//
int opcoes = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, opcoes | O_NONBLOCK) == -1) {
// soquetes não bloqueantes não são suportados
exit(1);
}
if (bind(fd, (sockaddr*) &ia6_any, sizeof(ia6_any))) {
// bind() falhou, provavelmente outro já está bind()ado à porta
exit(1);
}
#define INTERFACE "eth0"
#define DESTINO "fe80::1234:5678"
char buffer[1500]; // contém a mensagem
int len_buffer; // contém o comprimento da mensagem
sockaddr_in6 end_destino;
bzero(&end_destino, sizeof(end_destino));
#ifdef SIN6_LEN
// necessário em BSD, desnecessário em Linux
end_destino.sin6_len = SIN6_LEN;
#endif
end_destino.sin6_family = AF_INET6;
end_destino.sin6_port = htons(PORTA);
inet_pton(AF_INET6, DESTINO, &end_destino.sin6_addr);
// necessário se utilizarmos endereços link-local, como o exemplo.
// Do contrário, deve-se passar zero
end_destino.sin6_scope_id = if_nametoindex(INTERFACE);
int vol = sendto(fd, buffer, len_buffer, 0, (sockaddr*) &end_destino,
sizeof(end_destino));
if (vol < 0) {
// falha
if (errno !=
EAGAIN && errno != EINTR && errno != EWOULDBLOCK) {
// erro fatal
exit(1);
} else {
// erro não fatal
}
}
De acordo com a especificação do Sockets BSD e do POSIX, uma remessa ou recebimento de datagrama nunca deve falhar (exceto por EWOULDBLOCK, se o soquete for não-bloqueante), Mas é possível que haja alguma implementação imperfeita que não respeite essa regra; portanto é prudente prevermos a situação de falha mesmo em protocolos orientados a datagrama.
char buffer[1500];
int len_buffer;
sockaddr_in6 end_origem;
u_int socklen = sizeof(end_origem);
len_buffer = recvfrom(fd, buffer, sizeof(buffer), 0, (sockaddr*) &end_origem,
&socklen);
if (len_buffer < 0) {
// falha
if(errno !=
EAGAIN && errno != EINTR && errno != EWOULDBLOCK) {
// erro fatal
exit(1);
} else {
// erro não fatal, pode tentar novamente mais tarde
}
}
recvmsg() entra em substituição a recvfrom(). O código a seguir substitui o exemplo 6.2.3.
char buffer[1500];
int len_buffer;
sockaddr_in6 end_origem;
// cria estrutura iovec, que deve apontar para um buffer que
// receberá o conteúdo do pacote em si
iovec iov;
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
// buffer para receber o endereço de destino
char buf_end_destino[CMSG_SPACE(sizeof(in6_pktinfo))];
// finalmente cria a estrutura msghdr e relaciona seus componentes
// aos elementos criados mais acima
msghdr msg;
msg.msg_name = &end_origem;
msg.msg_namelen = sizeof(end_origem);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = buf_end_destino;
msg.msg_controllen = sizeof(buf_end_destino);
msg.msg_flags = 0;
// configuramos o descritor de arquivo para fornecer o endereço de
// destino do pacote
// Linux *não* suporta mais as opções IP(V6)_RECVDSTADDR e IP(V6)_RECVIF
// porém suporta a opção IP(V6)_PKTINFO que fornece as duas informações
// ao mesmo tempo
int ligado = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &ligado, sizeof(ligado)) < 0)
{
// setsockopt() falhou (opção não suportada pela implementação?)
exit(1);
}
len_buffer = recvmsg(fd, &msg, 0);
if (len_buffer < 0) {
// falha
if(errno !=
EAGAIN && errno != EINTR && errno != EWOULDBLOCK) {
// erro fatal
exit(1);
} else {
// erro não fatal, pode tentar novamente mais tarde
exit(1);
}
}
// buffer[] conterá o payload do pacote, através de msg.msg_iov
// end_origem conterá o endereço de origem, através de msg.msg_name
// resta obter o endereço de destino e a interface de onde veio o pacote
in6_pktinfo info;
in6_addr end_destino;
int interface;
// obtém o dado auxiliar requisitado
for(cmsghdr* cmptr = CMSG_FIRSTHDR(&msg); cmptr != 0;
cmptr = CMSG_NXTHDR(&msg, cmptr)) {
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == IPV6_PKTINFO) {
memcpy(&info, CMSG_DATA(cmptr), sizeof(info));
}
}
end_destino = info.ipi6_addr;
interface = info.ipi6_ifindex;
Como se pode notar por este código-fonte, o recebimento de dados auxiliares utiliza muitos ponteiros e é fácil cometer erros ou esquecer alguma atribuição.
a) Servidores IPv6 recebem conexões e pacotes IPv4 sem necessidade de qualquer previsão especial no código. O endereço de origem será um endereço IPv4 mapeado em IPv6 (::FFFF:0:0/96);
b) Se o servidor precisar discriminar clientes IPv4 de IPv6, pode usar a macro IN6_IS_ADDR_V4MAPPED() para testar se o endereço de origem é IPv4;
c) Clientes IPv6 devem utilizar os endereços IPv4 mapeados em IPv6 para conectar-se a servidores IPv4.
sendto() falha e o soquete é inutilizado após este erro.
O problema está no arquivo net/ipv6/addrconf.c do kernel, mais especificamente na função ipv6_addr_type(), que falha em identificar o escopo do endereço se o flag for diferente de zero.
Felizmente, os endereços FF0x::/24 funcionam a contento.
#define PORTA 1000
#define MULTICAST_LAN "FF02::1"
#define INTERFACE “eth0”
#define MULTICAST_PARTICULAR "FF02::1234:1234"
// criamos um endereço IPv6 "coringa" (0000::0000), para atrelar
// todas as interfaces com bind()
sockaddr_in6 ia6_any;
bzero(&ia6_any, sizeof(ia6_any));
#ifdef SIN6_LEN
// necessário em BSD, desnecessário em Linux
ia6_any.sin6_len = SIN6_LEN;
#endif
ia6_any.sin6_family = AF_INET6;
ia6_any.sin6_port = htons(PORTA);
ia6_any.sin6_addr = in6addr_any;
ia6_any.sin6_scope_id = 0;
// descritor de arquivo
int fd;
if((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
// soquete inválido (IPv6 não suportado na máquina)
exit(1);
}
// Este segmento torna o soquete não bloqueante, ou seja, recv(), recvfrom()
// e recvmsg() retornam imediamente com erro se não houver dados a receber.
//
// A maioria dos programas usa select() ao invés de soquetes não bloqueantes.
//
int opcoes = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, opcoes | O_NONBLOCK) == -1) {
// soquetes não bloqueantes não são suportados
exit(1);
}
// Passar o endereço de multicast ao bind() é uma forma simples
// de evitar que pacotes "normais" sejam recebidos. Infelizmente
// algumas implementações falham quando isso é feito; e mesmo que
// funcione, continua sendo necessário chamar setsockopt(IPV6_ADD_MEMBERSHIP)
//
// Assim, o procedimento portável é passar o endereço in6_addrany para
// o bind().
if (bind(fd, (sockaddr*) &ia6_any, sizeof(ia6_any))) {
// bind() falhou, provavelmente outro já está bind()ado à porta
exit(1);
}
// este segmento desliga o loopback de pacotes multicast, e costuma ser
// utilizada. Dificilmente um aplicativo tem interesse em ouvir pacotes
// multicast emitidos por ele mesmo.
u_int mcast_loop = 0;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &mcast_loop,
sizeof(mcast_loop)))
{
exit(1);
}
in6_addr mcast_geral, mcast_particular;
inet_pton(AF_INET6, MULTICAST_LAN, &mcast_geral);
inet_pton(AF_INET6, MULTICAST_PARTICULAR, &mcast_particular);
// entra no grupo multicast "todas as máquinas da LAN" (FF02::1)
ipv6_mreq mcast_req;
mcast_req.ipv6mr_multiaddr = mcast_geral;
mcast_req.ipv6mr_interface = if_nametoindex(INTERFACE);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mcast_req, sizeof(mcast_req)))
{
// não conseguiu entrar no grupo
exit(1);
}
// entra num outro grupo de multicast arbitrariamente definido pela aplicação
mcast_req.ipv6mr_multiaddr = mcast_particular;
mcast_req.ipv6mr_interface = if_nametoindex(INTERFACE);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mcast_req, sizeof(mcast_req)))
{
// não conseguiu entrar no grupo
exit(1);
}
// entra num outro grupo de multicast arbitrariamente definido pela aplicação
mcast_req.ipv6mr_multiaddr = mcast_particular;
mcast_req.ipv6mr_interface = if_nametoindex(INTERFACE);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mcast_req, sizeof(mcast_req)))
{
// não conseguiu entrar no grupo
exit(1);
}
IN6_IS_ADDR_MULTICAST:
if (IN6_IS_ADDR_MULTICAST(&end_origem.sin6_addr))) {
// multicast
...
}
ioctl(SIOCGIFADDR), como seria possível em IPv4, pois uma interface IPv6 pode ter múltiplos endereços.
Em IPv4, a sintaxe interface: número-de-alias (e.g. eth0:2), pode ser usada para especificar qual o endereço alternativo desejado na chamada a ioctl(SIOCGIFADDR).
Tal sintaxe não é suportada no IPv6 do Linux, portanto realmente não há meio de utilizarmos ioctl() para obter os diversos endereços IPv6 de uma interface.
static std::string HEXDIGITS = "0123456789abcdef";
int hex2bin(const std::string& s)
{
int ret = 0;
for(unsigned int i = 0; i < s.size(); ++i) {
ret <<= 4;
ret |= (unsigned char) HEXDIGITS.find(tolower(s[i]));
}
return ret;
}
int get_ifv6_info(const char *if_nome, in6_addr *addr)
{
std::ifstream arq("/proc/net/if_inet6");
std::string _bruto, _endereco, _indice, _netmask, _escopo, _estado, _nome;
std::string endereco;
int indice;
// formato: endereço (32 digitos hexa), indice, comprimento da mascara,
// escopo (0x20 = link-local)
// estado (0x80=on), nome da interface
while(getline(arq, _bruto, '\n') && _bruto.size() >= 53) {
_endereco = _bruto.substr(0, 32);
_indice = _bruto.substr(33, 2);
_netmask = _bruto.substr(36, 2);
_escopo = _bruto.substr(39, 2);
_estado = _bruto.substr(42, 2);
_nome = _bruto.substr(49, 4);
if (if_nome == _nome) {
for(int n = 0; n < 32; n += 4) {
endereco += _endereco.substr(n, 4)+":";
}
endereco.erase(endereco.size()-1, 1);
inet_pton(AF_INET6, endereco.c_str(), addr);
if (IN6_IS_ADDR_LINKLOCAL(addr)) {
indice = hex2bin(_indice);
return indice;
}
}
}
return -1;
}
Para obter o endereço IPv6, a função deve ser chamada da seguinte forma:
int iface_nr; const char* iface_nome = "eth0"; sockaddr_in6 ia6_local; iface_nr = get_ifv6_info(iface_nome, &ia6_local.sin6_addr);A função retorna diretamente o número seqüencial da interface (que também poderia ser obtido pela função padrão
if_nametoindex()) e indiretamente o endereço IPv6, preenchido em ia6_local.sin6_addr que foi passado como parâmetro.
ifconfig, como no exemplo:
# ifconfig eth0 add 2001::1/64
Conforme pode ser visto em NETTOOLS, internamente o ifconfig preenche uma estrutura in6_ifreq com os dados da interface e do endereço, e utiliza a chamada ioctl(fd, SIOCSIFADDR, in6_ifreq*) para adicionar um endereço e ioctl(fd, SIOCDIFADDR, in6_ifreq*) para remover um endereço.
// texto para endereço const char endereco_texto = "FE80::1234:5678"; sockaddr_in6 endereco; inet_pton(AF_INET6, endereco_texto, &endereco.sin6_addr); // endereço para texto char endereco_texto_2[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &endereco.sin6_addr, endereco_texto_2, sizeof(endereco_texto_2));
const char *nome = "www.dominio.com.br";
char buffer[INET6_ADDRSTRLEN];
hostent* h = gethostbyname2(nome, AF_INET6);
if (!h) {
// falha na resolução do nome
exit(1);
}
for(char** ppc = h->h_addr_list; (*ppc) != NULL; ++ppc) {
inet_ntop(h->h_addrtype, *ppc, buffer, sizeof(buffer));
printf("Endereço IPv6: %s\n", buffer);
}
hostent* h = gethostbyname2(nome, AF_INET);
if (!h) {
// falha na resolução do nome
exit(1);
}
for(char** ppc = h->h_addr_list; (*ppc) != NULL; ++ppc) {
inet_ntop(h->h_addrtype, *ppc, buffer, sizeof(buffer));
printf("Endereço IPv4: %s\n", buffer);
}
Licença:
Creative Commons Atribuição 2.5 Brasil (salvo seja especificada outra)
Válido:
XHTML 1.0 -
CSS 3