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

read(), write(), bind(), select(), poll(). Eles não serão abordados neste trabalho.
struct in6_addr {
union {
uint8_t u6_addr8[16];
uint16_t u6_addr16[8];
uint32_t u6_addr32[4];
} in6_u;
#define
s6_addr
in6_u.u6_addr8
#define s6_addr16
in6_u.u6_addr16
#define
s6_addr32
in6_u.u6_addr32
};
Esta estrutura é análoga a in_addr do IPv4, e contém o endereço IPv6 de 128 bits. Graças aos artifícios union e #define, é possível lidar com o endereço de diversas formas: como 16 bytes, 8 palavras ou 4 palavras duplas.
Unions e macros são artifícios próprios da linguagem C; outras linguagens como C++ permitem uma manipulação mais civilizada do endereço IPv6. Do ponto de vista do kernel, in6_addr é uma seqüência de 128 bits de comprimento, sem qualquer estrutura interna.
Conforme OPENGROUP, o membro s6_addr é obrigatoriamente definido; portanto é o mais portável.
A variável in6addr_any contém o endereço IPv6 "indefinido" ou nulo (completamente zerado). Esta constante é uma variável, não uma macro. O sistema encarrega-se de inicializá-la.
A variável in6addr_loopback contém o endereço IPv6 ::1, local da máquina, equivalente ao endereço 127.0.0.1 do IPv4.
Também é definida a macro IN6ADDR_ANY_INIT, que pode ser usada para inicializar uma estrutura in6_addr com o endereço nulo.
Assim como em IPv4, o endereço nulo IPv6 é passado ao bind() em programas de servidor que desejem ouvir todas as interfaces existentes.
struct sockaddr_in6 {
uint8_t
sin6_len;
sa_family_t sin6_family;
in_port_t
sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
Estrutura análoga ao sockaddr_in do IPv4. Há dois membros completamente novos na estrutura:
a) sin6_flowinfo: os primeiros 4 bits são reservados, os 4 bits seguintes são os bits de prioridade, os 24 bits finais correspondem aos bits da etiqueta de fluxo do cabeçalho IPv6.
Estes campos servem, em tese, para controle de prioridade e garantias de fluxo, e provavelmente vai interagir com protocolos de reserva de banda e.g. RSVP no futuro. Conforme 1.3.11, ainda é assunto em discussão.
b) sin6_scope_id: Identifica a interface pela qual deve trafegar o pacote. As interfaces são identificadas por um número inteiro positivo; o número zero delega a escolha ao algoritmo de roteamento.
# ping6 fe80::2e0:18ff:fe1c:62b0 connect: Invalid argument # ping6 -I eth0 fe80::2e0:18ff:fe1c:62b0 PING fe80::2e0:18ff:fe1c:62b0(fe80::2e0:18ff:fe1c:62b0) from ::1 eth0: 56 data bytes 64 bytes from fe80::2e0:18ff:fe1c:62b0: icmp_seq=1 ttl=64 time=59 usec 64 bytes from fe80::2e0:18ff:fe1c:62b0: icmp_seq=2 ttl=64 time=58 usecO comando
ping6 funciona apenas quando especificamos a inteface de rede. Isto é um resultado direto da existência do campo sin6_scope_id. As implementações mais antigas (sem sin6_scope_id) optavam pela primeira interface de rede não-local, o que via de regra funcionava (pois a maioria dos computadores têm apenas uma placa de rede) mas cria incerteza em computadores multihomed. É possível que alguma implementação moderna ainda siga esta regra se o campo sin6_scope_id for passado como zero.
Segundo USAGI, em versões futuras o utilitário ping6 para Linux pretende suportar a sintaxe endereço%interface, como no exemplo:
# ping6 fe80::2e0:18ff:fe1c:62b0%eth0Junto com o endereço stateless, codifica-se a interface de saída. Porém essa sintaxe não é suportada hoje, e também não há garantia que seja portável no futuro. Segundo o documento de OPENGROUP, pode haver mais campos dependentes de plataforma nas estruturas de endereço e soquete IPv6. O programador deve sempre preencher inteiramente as estruturas com zero antes de utilizá-las, pois zerar cada campo individualmente pode deixar algum campo não-padrão com conteúdo aleatório. Analogamente a IPv4, o campo
sin6_family deve ser preenchido pelo programador com a constante AF_INET6. O campo sin6_len, se existir, deve ser preenchido com a constante SIN6_LEN.
A existência do campo sin6_len pode ser determinada em C pela presença das macros HAVE_SA_LEN ou SIN6_LEN. A título de exemplo, as diversas versões de BSD apresentam esse campo, porém o Linux não o apresenta. Todo programa com pretensões de portabilidade deve fazer o teste de macro, e, caso este resulte positivo, preencher o campo, como no exemplo a seguir:
struct sockaddr_in6 endereco; #ifdef HAVE_SA_LEN endereco.sin6_len = SIN6_LEN; #endifO mesmo vale para IPv4, em relação ao campo
sin_len e sua constante SIN_LEN.
O número de porta continua com 16 bits, como em IPv4, pois os protocolos TCP e UDP não mudaram.
Todos os campos apresentam os bytes em network byte order, ou seja, o primeiro byte (com endereço mais baixo) é o mais significativo.
A título de exemplo: na arquitetura Intel 80386 e no Alpha, o primeiro byte de um inteiro é o menos significativo. Nas arquiteturas PowerPC e Sun Sparc, o primeiro byte é o mais significativo, exatamente como na network byte order. Nestas últimas, as funções htons() etc. são inócuas. Os campos sin6_len e sin6_family têm apenas um byte, logo a ordem dos bytes é irrelevante para eles. O padrão POSIX diz que os valores atribuídos a eles não devem ser processados pelas funções htons() etc.
Também é interessante observar que, ao contrário dos endereços e portas, esses valores nunca são transcritos para o pacote IP, portanto não há porque serem expressos em network byte order. Mesmo que esses campos venham a ter tamanho maior no futuro, devem continuar expressos na ordem de bytes nativa da máquina.
As funções para conversão de/para a ordem nativa da máquina continuam as mesmas: htons(), htonl(), ntohs(), ntohl().
As funções para tratamento de números de 64 e 128 bits v.g. htonll(), ntohll(), htonllll() e ntohllll(), embora ainda não estejam disponíveis hoje (Fevereiro/2003) já aparecem em documentações on-line e devem materializar-se num futuro próximo.
No comando socket(), o primeiro parâmetro é o protocolo. A constante AF_INET6 deve ser passada para comunicação IPv6, ao invés do tradicional AF_INET utilizado em IPv4.
inet_aton(), inet_ntoa() e inet_addr() tratam apenas endereços IPv4 e são consideradas obsoletas.
As novas funções, que lidam tanto com IPv4 e IPv6, são:
a) inet_pton
int inet_pton(int familia, const char *origem, void *destino)Converte um endereço em forma de texto (origem) para uma estrutura de endereçamento (apontada por destino). A família deve ser
AF_INET ou AF_INET6, conforme o tipo de endereço desejado. A estrutura passada em destino deve ser compatível com a família (in_addr ou in6_addr).
Esta função retorna um valor negativo se a família for inválida, zero se o endereço for inválido, e positivo se a conversão foi bem-sucedida.
b) inet_ntop
const char* inet_ntop(int familia, const void* origem, char* destino,
size_t tamanho);
Converte uma estrutura de endereço para um endereço em forma textual. O tamanho do buffer de destino deve ser passado por tamanho. O parâmetro origem deve ser um ponteiro para uma estrutura compatível com a familia (in_addr ou in6_addr).
O buffer de destino deve no mínimo comportar o maior endereço textual IPv4 ou IPv6 possível (de acordo com a família com que está-se a lidar). Ao invés de estimar o tamanho, o programador deve alocar o buffer nos tamanhos INET_ADDRSTRLEN ou INET6_ADDRSTRLEN.
Esta função retorna o endereço de destino em caso de sucesso, ou NULL se a conversão não foi bem-sucedida.
Note que as funções descritas aceitam ponteiros void* como ponteiros para estruturas de endereço, o que economiza uma coerção em ANSI C.
SOCK_STREAM e SOCK_DGRAM continuam sendo usadas como o segundo argumento de socket().
As demais funções, como bind(), listen(), connect() etc. também continuam em uso da forma usual.
sendmsg(). Por outro lado, para habilitar a recepção dos mesmos, deve-se habilitar as opções de soquete IPV6_HOPOPTS e IPV6_DSTOPTS, conforme se deseje receber cabeçalhos hop-by-hop e de destino, respectivamente. Tais opções também são abordadas brevemente na seção 4.11.
A mecânica de coleta de dados auxiliares é abordada na seção 4.7.
/etc/hosts, o NIS e o DNS. No Windows, o WINS toma o lugar do NIS.
Em alguns sistemas POSIX, é possível ao administrador configurar quais bancos de dados serão consultados através do arquivo de configuração /etc/nsswitch.conf, bem como adicionar plug-ins de resolução de nomes.
Devido a complexidade da tarefa, a conversão de nomes é implementada fora do kernel, tipicamente como uma biblioteca.
Não há suporte a buscas mais avançadas, e.g. busca de registros MX do DNS que apenas servidores de e-mail utilizam. Tais buscas têm de ser implementadas pelo próprio aplicativo ou acessadas através de funções dependentes de plataforma.
A estrutura hostent, utilizada pelas funções de resolução de nomes, tem o seguinte formato:
struct hostent {
char *h_name; // nome oficial do host
char **h_aliases; // nomes alternativos
int h_addrtype; // tipo de endereço do host
int h_length; // comprimento do endereço do host
// (4 para IPv4, 16 para IPv6)
char **h_addr_list; // endereços do host, em forma binária
}
#define h_addr h_addr_list[0] // primeiro endereço do host
Dada sua flexibilidade e inexistência de tamanhos fixos, seu formato não precisou ser alterado para suportar IPv6.
gethostbyname(), resolve apenas endereços IPv4. A função gethostbyname2() resolve tanto endereços IPv4 como IPv6 e deve ser utilizada em lugar da anterior por qualquer aplicativo novo.
struct hostent *gethostbyname2(const char *name, int af);onde o segundo parâmetro af especifica a família de endereços desejada:
AF_INET ou AF_INET6. Conforme a escolha, os registros DNS A ou AAAA serão consultados respectivamente, e a estrutura hostent será preenchida com o tipo de endereço desejado.
De acordo com GILLIGAN (1997), ao chamar gethostbyname2(), o aplicativo precisa saber de antemão (mas nem sempre tem como saber) se o endereço procurado é IPv4 ou IPv6. A procura simultânea por endereços IPv4 e IPv6 pode ser ativada por um flag.
Se for passado um endereço IP em forma de texto para gethostbyname2(), a chamada não falhará, mas simplesmente devolverá esse endereço como resultado.
Portanto, o aplicativo não precisa prever explicitamente essa situação (e.g. se o usuário informa um endereço ao invés de um nome).
res_init(); _res.options |= RES_USE_INET6;Com a ativação desse flag, a função
gethostbyname() primeiro procurará por endereços IPv6; se não encontrar, procura por endereços IPv4.
Já a função gethostbyname2() não muda seu comportamento, mesmo com o flag ativado; procura apenas pela família de endereços solicitada através do segundo parâmetro.
Com o flag RES_USE_INET6 ativado, ambas as funções retornam apenas endereços IPv6 em hostent; os endereços IPv4 são retornados mapeados em IPv6 (vide 2.1.1). O aplicativo deve estar preparado para isso.
gethostbyaddr() sempre solicitou a família de endereços como argumento de chamada, portanto seu protótipo continua válido para IPv6:
struct hostent *gethostbyaddr(const char * src, int len, int af);
Segundo GILLIGAN (1997) "uma possível fonte de confusão é a manipulação de endereços IPv4 mapeados em IPv6, e endereços IPv4 compatíveis com IPv6." O mesmo documento indica as seguintes regras, avaliadas na ordem a seguir, para eliminar a confusão:
a) Se af = AF_INET6 e len = 16 (tamanho do endereço IPv6 na memória); e o endereço IPv6 é um endereço IPv4 mapeado ou compatível com IPv6: faça a procura como IPv4.
b) Se af = AF_INET, faça a procura como IPv4.
c) Se af = AF_INET6, faça a procura como IPv6.
d) Se a função retornou sucesso, e af = AF_INET, e o flag RES_USE_INET6 estiver ativado (vide 4.5.2) então o endereço retornado em hostent será IPv6 (IPv4 mapeado em IPv6) e o membro h_length será modificado para 16.
getservbyname() permanecem inalteradas.
IN6_IS_ADDR_UNSPECIFIED Não especificado (totalmente zero) IN6_IS_ADDR_LOOPBACK Endereço ::1 (loopback) IN6_IS_ADDR_MULTICAST Endereço de multicast IN6_IS_ADDR_LINKLOCAL Endereço de rede local (stateless) IN6_IS_ADDR_SITELOCAL Endereço do sítio local IN6_IS_ADDR_V4MAPPED Endereço IPv4 mapeado em IPv6 (::FFFF:0:0/96) IN6_IS_ADDR_V4COMPAT Endereço IPv4 compatível com IPv6 (::/96) IN6_IS_ADDR_MC_NODELOCAL Multicast e local ao nó (simultaneamente) IN6_IS_ADDR_MC_LINKLOCAL Multicast e de rede local IN6_IS_ADDR_MC_SITELOCAL Multicast e de sítio local IN6_IS_ADDR_MC_ORGLOCAL Multicast e de organização local IN6_IS_ADDR_MC_GLOBAL Multicast global
ioctl() e soquetes crus.
O exemplo clássico é a obtenção do endereço de destino de uma conexão TCP ou UDP. Para a esmagadora maioria dos aplicativos, interessa apenas o endereço de origem, portanto comandos como accept() e recvfrom() apenas dão conhecimento deste último. Porém, quando o aplicativo precisa saber o endereço de destino do pacote, pode:
a) Utilizar um soquete de acesso direto à rede e analisar os pacotes de chegada por conta própria. Tem as sérias desvantagens da complexidade e de impor uma carga pesada ao computador;
b) Atrelar (bind()) um descritor de arquivo a cada interface de rede; se o pacote chegou por determinada interface, o endereço de destino era o endereço da interface. Esse raciocínio não funciona em interfaces que respondem por vários endereços IP (situação comum tanto em IPv4 como em IPv6).
c) Utilizar a interface de dados auxiliares, que entregará ao aplicativo apenas os dados que interessam, e apenas ao soquete interessado.
recvmsg() e sendmsg() é um ponteiro para a estrutura msghdr:
struct msghdr {
void *msg_name;
socklen_t msg_namelen;
struct iovec* msg_iov;
SIze_t msg_iovlen;
void *msg_control;
socklen_t msg_controllen;
int msg_flags;
};
O campo msg_name deve apontar para um buffer capaz de conter uma estrutura de endereço como sockaddr_in6; o campo msg_namelen deve conter o tamanho desse buffer.
O campo msg_iov deve apontar para uma matriz de estruturas iovec (descritas mais adiante); o campo msg_iovlen deve conter o número de elementos na matriz.
O campo msg_control deve apontar para um buffer que receberá uma ou mais estruturas cmsghdr (descrita mais adiante); o campo msg_controllen deve conter o tamanho desse buffer. Deve haver espaço suficiente para receber todos os dados auxiliares que o aplicativo solicitou via s=etsockopt()=.
A estrutura iovec tem o seguinte formato:
struct iovec {
void* iov_base;
int iov_len;
};
A estrutura iovec contém ou conterá o payload do pacote IP transmitido ou recebido, tal qual os buffers utilizados em read(), write() etc. A grande diferença é que pode-se definir várias estruturas iovec, cada uma contendo uma fração do payload. Tipicamente, apenas um iovec grande é utilizado.
O valor de iov_len deve conter o tamanho do buffer. Esse valor não é alterado quando o sistema preenche o buffer; o programa deve obter o número de octetos recebidos pelo valor de retorno de recvmsg() (tal qual read(), recv() e recvfrom()).
Finalmente, a estrutura cmsghdr:
struct cmsghdr {
int cmsg_len;
int cmsg_level;
int cmsg_type;
char data[];
};
A estrutura cmsghdr recebe os dados auxiliares. A parte inicial fixa é seguida por um número variável de bytes, representados em C pela matriz indefinida data[]. Portanto, o tamanho total da estrutura é variável.
O campo cmsg_len contém o tamanho total da estrutura. Os campos cmsg_level e cmsg_type contém o tipo de dado auxiliar.
Adjacente a este cabeçalho fixo, estão contidos os dados auxiliares propriamente ditos, cujo tamanho e formato interno são dependentes do tipo de dado auxiliar.
Diversas estruturas cmsghdr podem estar encadeadas no buffer apontado por msghdr.msg_control; o aplicativo deve deduzir o início da próxima estrutura a partir do tamanho da anterior e das características de alinhamento da máquina. Para facilitar esta tarefa, as seguintes macros são definidas:
cmsghdr* CMSG_FIRSTHDR(msghdr *);
Determina a primeira estrutura cmsghdr* contida em msghdr.
cmsghdr* CMSG_NXTHDR(msghdr*, cmsghdr*);
Determina a próxima estrutura cmsghdr*. Retorna NULL se não há mais nenhuma.
unsigned char* CMSG_DATA(cmsghdr*);
Obtém o dado auxiliar contido em cmsghdr(), ou seja, cmsghdr.data.
unsigned int CMSG_SPACE(unsigned int);
Calcula o tamanho do buffer necessário para conter uma estrutura cmsghdr, levando em conta o alinhamento etc. Informa-se o tamanho do dado auxiliar passado ou esperado. Por exemplo, para obter o endereço de destino, o tamanho será sizeof(in_addr) ou sizeof(in6_addr) dependendo do protocolo.
Para calcular o tamanho total de um buffer que vá receber diversos dados auxiliares, deve-se calcular assim:
CMSG_SPACE(sizeof(estrutura1)) + CMSG_SPACE(sizeof(estrutura2))
e não assim:
CMSG_SPACE(sizeof(estrutura1)+sizeof(estrutura2))
pois cada dado auxiliar tem de ser alinhado e completado com bytes de enchimento.
unsigned int CMSG_LEN(unsigned int);
Idem a CMSG_SPACE() porém retorna apenas o comprimento útil, sem os bytes de enchimento ao final.
O uso das três primeiras macros é revelado facilmente pelo seu protótipo. Quanto às duas últimas, servem para calcular o tamanho do buffer que receberá a estrutura cmsghdr, tamanho do dado auxiliar em questão.
As seguintes constantes são definidas para dados auxiliares IPV6:
cmsg_level cmsg_type
IPPROTO_IPV6 IPV6_DSTOPTS
IPV6_HOPLIMIT
IPV6_HOPOPTS
IPV6_NEXTHOP
IPV6_PKTINFO
IPV6_RECVDSTADDR (obsoleta, não suportada em Linux)
IPV6_RECVIF (obsoleta, não suportada em Linux)
IPV6_RTHDR
O formato interno do cabeçalho de extensão IPv6 permite identificar seu subtipo. As extensões definidas presentemente e seus códigos podem ser encontrados em STEVENS & THOMAS (1998).
ioctl() é utilizada para operações que não cabem na metáfora abrir/ler/gravar/fechar arquivo, nem possuem uma chamada especial. (fcntl e setsockopt são exemplos de chamadas especiais, que originalmente eram supridas por ioctl()).
A tendência do padrão POSIX e dos sistemas operacionais é desencorajar o uso de ioctl() e oferecer cada vez mais chamadas especiais e expandir a interface /proc, porém ioctl() continua sendo útil - e muitas vezes as "chamadas especiais" são meras macros de acesso facilitado a ioctl().
A maioria das operações ioctl() de rede atua apenas sobre o soquete passado como parâmetro. As operações de manipulação de interfaces e rotas, que atuam em características do sistema como um todo, usam um soquete qualquer como "trampolim", tradicionalmente UDP e da família de endereços sobre a qual se deseja atuar (e.g. família AF_INET6 para operações IPv6).
SIOCSPGRP - configura o processo que deve receber sinais SIGIO e
SIGURG. FIONBIO - liga/desliga o flag de I/O não-bloqueante.
AF_INET6.
| Operação | Comportamento em IPv6 |
|---|---|
| SIOCGIFADDR | Não suportada em IPv6 |
| SIOCSIFADDR | Adiciona, ao invés de substituir, um endereço |
| SIOCDIFADDR | (nova) Elimina um endereço da interface |
| SIOCGIFCONF | Não suportada |
| outras | Não suportadas |
in6_ifreq ao invés de ifreq. A interface é identificada por um número positivo (in6_ifreq.ifr6_ifindex) ao invés de um nome como em IPv4 (ifreq.ifr_name).
Infelizmente, a chamada SIOCGIFCONF, que serve para obter a lista de todas as interfaces, não existe em IPv6. A tendência é oferecer esta informação via interface /proc.
Operações de broadcast são inválidas em IPv6 pois este não suporta broadcast.
SIOCADDRT e SIOCDELRT são válidas para IPv4 e IPv6; o que define se a operação é IPv4 ou IPv6 é a família do soquete-trampolim utlizado.
Previsivelmente, as estruturas passadas via ioctl() são diferentes conforme a família. Ambas estão no arquivo de inclusão <net/route.h>.
| Protocolo | Estrutura |
|---|---|
| IPv4 | rtentry |
| IPv6 | in6_rtmsg |
ioctl() para consulta da tabela de roteamento. A consulta tem de ser feita via interface /proc.
if_nametoindex(const char*) executa esta tarefa. Verifique a seção 4.11.1 para mais detalhes.
AF_ROUTE. São uma alternativa aos comandos ioctl() para manipulação da tabela de roteamento. AF_ROUTE é apresentada como sinônimo de AF_DATALINK. Embora esta última seja suportada pelo Linux, não são suportadas as funções de manipulação de rotas.
sysctl() permite acesso aos mesmos recursos da árvore /proc/sys, de forma mais apropriada a um programa em C, ou seja, sem necessidade de interprertar textos ASCII.
Internamente, esta função utiliza soquetes de roteamento ou netlink para comunicar-se com o kernel.
Os mesmos recursos disponíveis através dos arquivos em /proc/sys também são acessíveis e modificáveis via sysctl(). Porém, o identificador de cada recurso é uma constante inteira, e não um nome de arquivo. As constantes podem ser consultadas em <sys/sysctl.h> e <linux/sysctl.h>. Em outros sistemas operacionais, consulte <sys/sysctl.h>, que por sua vez inclui arquivos dependentes de plataforma.
A utilização dessa interface tem duas peculiaridades:
a) A interface /proc é ligeiramente diferente em cada sistema operacional, muito embora os Unixes tentem ser semelhantes nas operações acessíveis via sysctl(). Usar /proc ou sysctl() pode trazer problemas de portabilidade;
b) Em Linux, apenas números inteiros (e não strings, nem estruturas) podem ser acessados via sysctl(), portanto apenas parâmetros numéricos presentes em /proc/sys podem ser manipulados via sysctl(). Portanto, toda a discussão em STEVENS (1998) sobre o acesso às tabelas de roteamento via sysctl() é inválida para o Linux.
sysctl() duplicaria esforços de programação sem que isso considerável vantagem aos aplicativos-usuários.
setsockopt().
As macros de comando são todas análogas a IPv4:
IPV6_ADD_MEMBERSHIP ou IPV6_JOIN_GROUP IPV6_DROP_MEMBERSHIP ou IPV6_LEAVE_GROUP IPV6_MULTICAST_IF IPV6_MULTICAST_HOPS (correspondente a IP_MULTICAST_TTL) IPV6_MULTICAST_LOOP IP*_MEMBERSHIP são consideradas obsoletas.
A principal mudança é na forma de especificar uma interface. Em IPv4, a interface multicast é especificada através de um endereço IP. Em IPv6, a interface é specificada através de um número inteiro.
Estrutura de requisição de multicast, utilizada pelos comandos IPV6_*_GROUP:
struct ipv6_mreq {
struct in6_addr ipv6mr_multiaddr;
unsigned int ipv6mr_interface;
};
Em algumas implementações mais antigas, bem como na implementação de TORVALDS et al. (2002), o segundo campo é apresentado com o nome Ipv6mr_ifindex.
O comando IPV6_MULTICAST_IF recebe um parâmetro u_int (número inteiro) ao invés de um endereço IP.
Os comandos IPV6_MULTICAST_HOPS e IPV6_MULTICAST_LOOP recebem, respectivamente, int e u_int como parâmetros, ao invés de u_char; na verdade, é uma simples mudança no tamanho do número.
if_nametoindex(const char*) foi definida para facilitar esta tarefa.
if_nametoindex() descobre o número da interface são dependentes de implementação. No Linux, são dois:
a) Consultar o arquivo /proc/net/if_inet6;
b) Utilizar a chamada ioctl(fd_trampolim, SIOGIFINDEX, &ifreq).
IPV6_ADDRFORM |
Permite que um soquete seja convertido de/para IPv4. (nova) |
IPV6_CHECKSUM |
Especifica a localização do checksum dentro de um pacote IPv6 "cru". Para outros protocolos, inclusive ICMPv6, o kernel sempre encarrega-se de calcular o checksum. (nova) |
IPV6_DSTOPTS |
Cabeçalhos opcionais para o destino devem ser recebidos como dados auxiliares via recvmsg(). (nova) |
IPV6_HOPLIMIT |
Recebe o campo hop count como dado auxiliar. (nova) |
IPV6_HOPOPTS |
Cabeçalhos opcionais hop-by-hop devem ser passados como dados auxiliares. (nova) |
| IPV6_NEXTHOP | Permite especificar o endereço next hop via sendmsg() (nova) |
IPV6_PKTINFO |
Recebe endereço de destino e número da interface de chegada como dados auxiliares |
IPV6_RECVDSTADDR |
Recebe endereço de destino como dado auxiliar (obsoleto, não mais suportado pelo Linux) |
IPV6_RECVIF |
Recebe número da interface de chegada como dado auxiliar (obsoleto, não mais suportado em Linux) |
IPV6_PKTOPTIONS |
Permite que TCP troque dados auxiliares. Normalmente, dados auxiliares são tratados via sendmsg() e recvmsg() o que pressupõe comunicação em datagramas (UDP ou soquete "cru"). |
IPV6_RTHDR |
Cabeçalho de roteamento IPv6 será recebido como dado auxiliar. (nova) |
IPV6_UNICAST_HOPS |
Permite especificar o limite de saltos (hop limit) (análoga a IP_TTL em IPv4) |
IPV6_AUTHHDR |
Cabeçalho IPSEC AH (será obsoletado) |
IPV6_ROUTER_ALERT |
Repassa todos os soquetes contendo um alerta de roteamento para o soquete |
IPV6_MTU_DISCOVER |
Controla o recurso de descoberta de MTU (path MTU discovery) no soquete. |
IPV6_MTU |
Obtém ou configura o MTU para o soquete. Limitado ao MTU da interface. |
IPV6_RECVERR |
Controla recebimento de erros assíncronos como dados auxiliares. |
socket() uma constante IPPROTO_x onde x é o protocolo de quarta camada em que o programa está interessado.
As diferenças entre soquetes crus IPv4 e IPv6 são:
a) O soquete é criado com a família AF_INET6 ao invés de AF_INET;
b) Os valores de IPPROTO_ICMP e IPPROTO_ICMPV6 são diferentes, pois são protocolos diferentes (vide 1.3.8);
c) Em IPv4, o programa pode utilizar a opção IP_HDRINCL, que delega ao programa a geração e acesso ao cabeçalho de terceira camada (IP). Esta opção não é válida em IPv6. Programas que precisem lidar com o cabeçalho IP devem usar soquetes de acesso à camada de enlace;
d) Caso haja necessidade de ler/gravar dados do cabeçalho IP, inclusive cabeçalhos auxiliares, isso deve ser feito via opções IP ou acesso a dados auxiliares (vide respectivos tópicos);
e) Todos os campos nos cabeçalhos de protocolo apresentam-se em network byte order;
f) Como o checksum dos protocolos de quarta camada inclui dados da terceira camada em IPv6, o kernel oferece a comodidade em fazê-lo em lugar do programa;
Para os soquetes crus ICMPv6, esse cálculo sempre é feito pelo kernel. Para os demais protocolos, a opção setsockopt() IPV6_CHECKSUM permite comutar o recurso.
g) Como o protocolo ICMPv6 acumula muito mais funções que ICMPv4, existe uma opção de soquete que permite filtrar os pacotes por subtipo. A estrutura de filtragem é icmp6_filter. As macros que manipulam esta estrutura são
ICMP6_FILTER_SETPASSALL(struct icmp6_filter* filtro) ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter* filtro) ICMP6_FILTER_SETPASS(int subtipo, struct icmp6_filter* filtro) ICMP6_FILTER_SETBLOCK(int subtipo, struct icmp6_filter* filtro) int ICMP6_FILTER_WILLPASS(int subtipo, const struct icmp6_filter *filtro) int ICMP6_FILTER_WILLBLOCK(int subtipo, const struct icmp6_filter *filtro)Os valores aceitáveis para o subtipo podem ser consultados em
<netinet/icmp6.h>. A estrutura é passada para o kernel via comando
setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, filtro, sizeof(icmp6_filter))
libpcap seja o padrão de fato para ocultar essa multiplicidade. A rigor o acesso à camada de enlace não sofre qualquer alteração pela inclusão do IPv6, que está numa camada superior. As possíveis mudanças num aplicativo que use esse recurso são:
a) Se o aplicativo está interessado em pacotes IPv6, deve utilizar a opção de filtragem adequada à interface.
b) Pacotes IPv6 devem ser enquadrados na estrutura ip6_hdr contida no arquivo de inclusão <netinet/ip6.h>.
bind() (INADDR_ANY em IPv4, in6addr_any em IPv6). Apenas uma chamada a bind() é necessária.
É permitido que vários aplicativos ouçam a mesma porta, desde que cada um esteja atrelado a uma interface diferente. Não pode haver dois aplicativos ouvindo na mesma porta e na mesma interface. E naturalmente, se um aplicativo está ouvindo em todas as interfaces, nenhum outro aplicativo poderá atrelar-se à mesma porta.
Isso tudo é verdadeiro tanto para IPv4 quanto para IPv6, considerados isoladamente. Porém, IPv4 e IPv6 alimentam as mesmas camadas de transporte, e pode haver conversão entre endereços IPv4 e IPv6; portanto, é necessário estabelecer uma regra adicional de convivência.
Para o caso de dois aplicativos, um IPv4 e outro IPv6, ambos ouvindo na mesma porta e na(s) mesma(s) interface(s), a implementação pode escolher uma das regras a seguir:
a) Os servidores convivem; conexões IPv6 vão para o servidor IPv6, conexões IPv4 para o servidor IPv4; ou
b) Os servidores não podem conviver. O primeiro a ser iniciado tomará conta da porta, os próximos falharão ao tentar usar a mesma porta.
IN6_IS_ADDR_V4MAPPED().
read(), write() etc. sobre o descritor.
No entanto, há um caso especial criado pela convivência entre IPv4 e IPv6. Se um processo-mestre com descritor IPv6 aberto repassar esse descritor a um processo-escravo que espera um soquete IPv4, e este último tentar executar alguma chamada como setsockopt() que se aplique apenas a IPv4, tal chamada falhará.
A solução definitiva é consertar o programa do processo-escravo; mas nem sempre isso é possível. No caso de soquetes IPv6 que utilizem exclusivamente endereços IPv4 mapeados em IPv6 (::FFFF:0:0/96), a comunicação subjacente é IPv4, e esse soquete pode ser convertido para IPv4 durante seu uso. Se temos como modificar o programa-mestre mas não o programa-escravo, esta é a saída.
Para converter um soquete IPv6 para IPv4, GILLIGAN (1997) prescreve o seguinte comando setsockopt():
int addrform = PF_INET;
setsockopt(descritor, IPPROTO_IPV6, IPV6_ADDRFORM, (char*)
&addrform, sizeof(addrform));
A opção IPV6_ADDRFORM do comando setsockopt() presta-se a conversão de soquetes. O parâmetro passado (no exemplo, através da variável addrform) indica para que protocolo se quer converter o soquete - no caso, PF_INET (IPv4).
Uma vez convertido, o soquete pode sofrer qualquer operação IPv4 sem problemas. Isso permite inclusive que ele seja passado a outros processos que esperem exclusivamente soquetes IPv4.
É prudente frisar que essa conversão não faz nenhum milagre, e de forma nenhuma deve ser entendida como um proxy IPv4-IPv6. Um soquete IPv6 que utilize endereços nativos IPv6 não pode ser convertido. Soquetes crus IPv6 também não o podem, pois seu modus operandi é muito especializado.
Não se pode "pingar" um endereço IPv4 da seguinte forma:
# ping6 ::FFFF:200.215.89.83
pois não existe conversão subjacente de IPv6 para IPv4 para soquetes crus.
Por outro lado, para converter um soquete IPv4 para IPv6 (se o processo-escravo espera um descritor de arquivo IPv6), o comando é:
int addrform = PF_INET6;
setsockopt(descritor, IPPROTO_IP, IPV6_ADDRFORM, (char*) &addrform,
sizeof(addrform));
Licença:
Creative Commons Atribuição 2.5 Brasil (salvo seja especificada outra)
Válido:
XHTML 1.0 -
CSS 3