mirror of https://github.com/ipxe/ipxe.git
IP6 support
parent
f1e1dfae3d
commit
d1d334b8e1
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef _GPXE_ICMP6_H
|
||||||
|
#define _GPXE_ICMP6_H
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* ICMP6 protocol
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ip.h>
|
||||||
|
#include <gpxe/ip6.h>
|
||||||
|
#include <gpxe/ndp.h>
|
||||||
|
|
||||||
|
#define ICMP6_NSOLICIT 135
|
||||||
|
#define ICMP6_NADVERT 136
|
||||||
|
|
||||||
|
extern struct tcpip_protocol icmp6_protocol;
|
||||||
|
|
||||||
|
struct icmp6_header {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t code;
|
||||||
|
uint16_t csum;
|
||||||
|
/* Message body */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct neighbour_solicit {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t code;
|
||||||
|
uint16_t csum;
|
||||||
|
uint32_t reserved;
|
||||||
|
struct in6_addr target;
|
||||||
|
/* "Compulsory" options */
|
||||||
|
uint8_t opt_type;
|
||||||
|
uint8_t opt_len;
|
||||||
|
#warning hack alert
|
||||||
|
uint8_t opt_ll_addr[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct neighbour_advert {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t code;
|
||||||
|
uint16_t csum;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t reserved;
|
||||||
|
struct in6_addr target;
|
||||||
|
uint8_t opt_type;
|
||||||
|
uint8_t opt_len;
|
||||||
|
#warning hack alert
|
||||||
|
uint8_t opt_ll_addr[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ICMP6_FLAGS_ROUTER 0x80
|
||||||
|
#define ICMP6_FLAGS_SOLICITED 0x40
|
||||||
|
#define ICMP6_FLAGS_OVERRIDE 0x20
|
||||||
|
|
||||||
|
int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest );
|
||||||
|
|
||||||
|
#endif /* _GPXE_ICMP6_H */
|
|
@ -10,6 +10,7 @@
|
||||||
#define IP_IGMP 2
|
#define IP_IGMP 2
|
||||||
#define IP_TCP 6
|
#define IP_TCP 6
|
||||||
#define IP_UDP 17
|
#define IP_UDP 17
|
||||||
|
#define IP_ICMP6 58
|
||||||
|
|
||||||
/* IP address constants */
|
/* IP address constants */
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ struct in6_addr {
|
||||||
uint8_t u6_addr8[16];
|
uint8_t u6_addr8[16];
|
||||||
uint16_t u6_addr16[8];
|
uint16_t u6_addr16[8];
|
||||||
uint32_t u6_addr32[4];
|
uint32_t u6_addr32[4];
|
||||||
} in16_u;
|
} in6_u;
|
||||||
#define s6_addr in6_u.u6_addr8
|
#define s6_addr in6_u.u6_addr8
|
||||||
#define s6_addr16 in6_u.u6_addr16
|
#define s6_addr16 in6_u.u6_addr16
|
||||||
#define s6_addr32 in6_u.u6_addr32
|
#define s6_addr32 in6_u.u6_addr32
|
||||||
|
@ -67,7 +68,7 @@ struct sockaddr_in6 {
|
||||||
*/
|
*/
|
||||||
sa_family_t sin_family;
|
sa_family_t sin_family;
|
||||||
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
|
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
|
||||||
uint16_t sin_port;
|
uint16_t sin_port;
|
||||||
uint32_t sin6_flowinfo; /* Flow number */
|
uint32_t sin6_flowinfo; /* Flow number */
|
||||||
struct in6_addr sin6_addr; /* 128-bit destination address */
|
struct in6_addr sin6_addr; /* 128-bit destination address */
|
||||||
uint32_t sin6_scope_id; /* Scope ID */
|
uint32_t sin6_scope_id; /* Scope ID */
|
||||||
|
|
|
@ -8,18 +8,33 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ip.h>
|
#include <ip.h>
|
||||||
|
#include <gpxe/in.h>
|
||||||
|
|
||||||
/* IP6 constants */
|
/* IP6 constants */
|
||||||
|
|
||||||
#define IP6_VERSION 0x6
|
#define IP6_VERSION 0x6
|
||||||
#define IP6_HOP_LIMIT 64
|
#define IP6_HOP_LIMIT 255
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet buffer contents
|
||||||
|
* This is duplicated in tcp.h and here. Ideally it should go into pkbuff.h
|
||||||
|
*/
|
||||||
|
#define MAX_HDR_LEN 100
|
||||||
|
#define MAX_PKB_LEN 1500
|
||||||
|
#define MIN_PKB_LEN MAX_HDR_LEN + 100 /* To account for padding by LL */
|
||||||
|
|
||||||
|
#define IP6_EQUAL( in6_addr1, in6_addr2 ) \
|
||||||
|
( strncmp ( ( char* ) &( in6_addr1 ), ( char* ) &( in6_addr2 ),\
|
||||||
|
sizeof ( struct in6_addr ) ) == 0 )
|
||||||
|
|
||||||
|
#define IS_UNSPECIFIED( addr ) \
|
||||||
|
( ( (addr).in6_u.u6_addr32[0] == 0x00000000 ) && \
|
||||||
|
( (addr).in6_u.u6_addr32[1] == 0x00000000 ) && \
|
||||||
|
( (addr).in6_u.u6_addr32[2] == 0x00000000 ) && \
|
||||||
|
( (addr).in6_u.u6_addr32[3] == 0x00000000 ) )
|
||||||
/* IP6 header */
|
/* IP6 header */
|
||||||
|
|
||||||
struct ip6_header {
|
struct ip6_header {
|
||||||
uint32_t vers:4,
|
uint32_t ver_traffic_class_flow_label;
|
||||||
traffic_class:8,
|
|
||||||
flow_label:20;
|
|
||||||
uint16_t payload_len;
|
uint16_t payload_len;
|
||||||
uint8_t nxt_hdr;
|
uint8_t nxt_hdr;
|
||||||
uint8_t hop_limit;
|
uint8_t hop_limit;
|
||||||
|
@ -27,12 +42,32 @@ struct ip6_header {
|
||||||
struct in6_addr dest;
|
struct in6_addr dest;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* IP6 pseudo header */
|
||||||
|
struct ipv6_pseudo_header {
|
||||||
|
struct in6_addr src;
|
||||||
|
struct in6_addr dest;
|
||||||
|
uint8_t zero_padding;
|
||||||
|
uint8_t nxt_hdr;
|
||||||
|
uint16_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Next header numbers */
|
||||||
|
#define IP6_HOPBYHOP 0x00
|
||||||
|
#define IP6_ROUTING 0x43
|
||||||
|
#define IP6_FRAGMENT 0x44
|
||||||
|
#define IP6_AUTHENTICATION 0x51
|
||||||
|
#define IP6_DEST_OPTS 0x60
|
||||||
|
#define IP6_ESP 0x50
|
||||||
|
#define IP6_ICMP6 0x58
|
||||||
|
#define IP6_NO_HEADER 0x59
|
||||||
|
|
||||||
struct pk_buff;
|
struct pk_buff;
|
||||||
struct net_device;
|
struct net_device;
|
||||||
struct net_protocol;
|
struct net_protocol;
|
||||||
|
|
||||||
extern struct net_protocol ipv6_protocol;
|
extern struct net_protocol ipv6_protocol;
|
||||||
|
extern struct tcpip_net_protocol ipv6_tcpip_protocol;
|
||||||
|
extern char * inet6_ntoa ( struct in6_addr in6 );
|
||||||
|
|
||||||
extern int ipv6_tx ( struct pk_buff *pkb, uint16_t trans_proto, struct in6_addr *dest );
|
|
||||||
|
|
||||||
#endif /* _GPXE_IP6_H */
|
#endif /* _GPXE_IP6_H */
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <byteswap.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <gpxe/icmp6.h>
|
||||||
|
#include <gpxe/ip6.h>
|
||||||
|
#include <gpxe/in.h>
|
||||||
|
#include <gpxe/netdevice.h>
|
||||||
|
#include <gpxe/pkbuff.h>
|
||||||
|
#include <gpxe/tcpip.h>
|
||||||
|
|
||||||
|
#define NDP_STATE_INVALID 0
|
||||||
|
#define NDP_STATE_INCOMPLETE 1
|
||||||
|
#define NDP_STATE_REACHABLE 2
|
||||||
|
#define NDP_STATE_DELAY 3
|
||||||
|
#define NDP_STATE_PROBE 4
|
||||||
|
#define NDP_STATE_STALE 5
|
||||||
|
|
||||||
|
static struct ndp_entry * ndp_find_entry ( struct in6_addr *in6 );
|
||||||
|
int ndp_resolve ( struct net_device *netdev, struct in6_addr *src,
|
||||||
|
struct in6_addr *dest, void *dest_ll_addr );
|
||||||
|
int ndp_process_advert ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
|
||||||
|
struct sockaddr_tcpip *st_dest );
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <byteswap.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <gpxe/in.h>
|
||||||
|
#include <gpxe/ip6.h>
|
||||||
|
#include <gpxe/if_ether.h>
|
||||||
|
#include <gpxe/pkbuff.h>
|
||||||
|
#include <gpxe/ndp.h>
|
||||||
|
#include <gpxe/icmp6.h>
|
||||||
|
#include <gpxe/tcpip.h>
|
||||||
|
#include <gpxe/netdevice.h>
|
||||||
|
|
||||||
|
struct tcpip_protocol icmp6_protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send neighbour solicitation packet
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
* @v src Source address
|
||||||
|
* @v dest Destination address
|
||||||
|
*
|
||||||
|
* This function prepares a neighbour solicitation packet and sends it to the
|
||||||
|
* network layer.
|
||||||
|
*/
|
||||||
|
int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src,
|
||||||
|
struct in6_addr *dest ) {
|
||||||
|
union {
|
||||||
|
struct sockaddr_in6 sin6;
|
||||||
|
struct sockaddr_tcpip st;
|
||||||
|
} st_dest;
|
||||||
|
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||||
|
struct neighbour_solicit *nsolicit;
|
||||||
|
struct pk_buff *pkb = alloc_pkb ( sizeof ( *nsolicit ) + MIN_PKB_LEN );
|
||||||
|
pkb_reserve ( pkb, MAX_HDR_LEN );
|
||||||
|
nsolicit = pkb_put ( pkb, sizeof ( *nsolicit ) );
|
||||||
|
|
||||||
|
/* Fill up the headers */
|
||||||
|
memset ( nsolicit, 0, sizeof ( *nsolicit ) );
|
||||||
|
nsolicit->type = ICMP6_NSOLICIT;
|
||||||
|
nsolicit->code = 0;
|
||||||
|
nsolicit->target = *dest;
|
||||||
|
nsolicit->opt_type = 1;
|
||||||
|
nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8;
|
||||||
|
memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr,
|
||||||
|
netdev->ll_protocol->ll_addr_len );
|
||||||
|
/* Partial checksum */
|
||||||
|
nsolicit->csum = 0;
|
||||||
|
nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) );
|
||||||
|
|
||||||
|
/* Solicited multicast address */
|
||||||
|
st_dest.sin6.sin_family = AF_INET6;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001;
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3];
|
||||||
|
st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
|
||||||
|
|
||||||
|
/* Send packet over IP6 */
|
||||||
|
tcpip_tx ( pkb, &icmp6_protocol, &st_dest.st );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process ICMP6 headers
|
||||||
|
*
|
||||||
|
* @v pkb Packet buffer
|
||||||
|
* @v st_src Source address
|
||||||
|
* @v st_dest Destination address
|
||||||
|
*/
|
||||||
|
static int icmp6_rx ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
|
||||||
|
struct sockaddr_tcpip *st_dest ) {
|
||||||
|
struct icmp6_header *icmp6hdr = pkb->data;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if ( pkb_len ( pkb ) < sizeof ( *icmp6hdr ) ) {
|
||||||
|
DBG ( "Packet too short (%d bytes)\n", pkb_len ( pkb ) );
|
||||||
|
free_pkb ( pkb );
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Verify checksum */
|
||||||
|
|
||||||
|
/* Process the ICMP header */
|
||||||
|
switch ( icmp6hdr->type ) {
|
||||||
|
case ICMP6_NADVERT:
|
||||||
|
return ndp_process_advert ( pkb, st_src, st_dest );
|
||||||
|
}
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) {
|
||||||
|
|
||||||
|
struct sockaddr_in6 server;
|
||||||
|
memcpy ( &server, server_p, sizeof ( server ) );
|
||||||
|
struct pk_buff *rxpkb = alloc_pkb ( 500 );
|
||||||
|
pkb_reserve ( rxpkb, MAX_HDR_LEN );
|
||||||
|
struct neighbour_advert *nadvert = pkb_put ( rxpkb, sizeof ( *nadvert ) );
|
||||||
|
nadvert->type = 136;
|
||||||
|
nadvert->code = 0;
|
||||||
|
nadvert->flags = ICMP6_FLAGS_SOLICITED;
|
||||||
|
nadvert->csum = 0xffff;
|
||||||
|
nadvert->target = server.sin6_addr;
|
||||||
|
nadvert->opt_type = 2;
|
||||||
|
nadvert->opt_len = 1;
|
||||||
|
memcpy ( nadvert->opt_ll_addr, ll_addr, 6 );
|
||||||
|
struct ip6_header *ip6hdr = pkb_push ( rxpkb, sizeof ( *ip6hdr ) );
|
||||||
|
ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );
|
||||||
|
ip6hdr->hop_limit = 255;
|
||||||
|
ip6hdr->nxt_hdr = 58;
|
||||||
|
ip6hdr->payload_len = htons ( sizeof ( *nadvert ) );
|
||||||
|
ip6hdr->src = server.sin6_addr;
|
||||||
|
ip6hdr->dest = server.sin6_addr;
|
||||||
|
hex_dump ( rxpkb->data, pkb_len ( rxpkb ) );
|
||||||
|
net_rx ( rxpkb, netdev, htons ( ETH_P_IPV6 ), ll_addr );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** ICMP6 protocol */
|
||||||
|
struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
|
||||||
|
.name = "ICMP6",
|
||||||
|
.rx = icmp6_rx,
|
||||||
|
.tcpip_proto = IP_ICMP6, // 58
|
||||||
|
.csum_offset = 2,
|
||||||
|
};
|
323
src/net/ipv6.c
323
src/net/ipv6.c
|
@ -1,42 +1,337 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <vsprintf.h>
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
|
#include <gpxe/in.h>
|
||||||
|
#include <gpxe/ip6.h>
|
||||||
|
#include <gpxe/ndp.h>
|
||||||
|
#include <gpxe/list.h>
|
||||||
|
#include <gpxe/icmp6.h>
|
||||||
|
#include <gpxe/tcpip.h>
|
||||||
|
#include <gpxe/socket.h>
|
||||||
#include <gpxe/pkbuff.h>
|
#include <gpxe/pkbuff.h>
|
||||||
#include <gpxe/netdevice.h>
|
#include <gpxe/netdevice.h>
|
||||||
#include <gpxe/in.h>
|
|
||||||
#include <gpxe/if_ether.h>
|
#include <gpxe/if_ether.h>
|
||||||
#include <gpxe/tcpip.h>
|
|
||||||
|
struct net_protocol ipv6_protocol;
|
||||||
|
|
||||||
|
/* Unspecified IP6 address */
|
||||||
|
static struct in6_addr ip6_none = {
|
||||||
|
.in6_u.u6_addr32[0] = 0,
|
||||||
|
.in6_u.u6_addr32[1] = 0,
|
||||||
|
.in6_u.u6_addr32[2] = 0,
|
||||||
|
.in6_u.u6_addr32[3] = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** An IPv6 routing table entry */
|
||||||
|
struct ipv6_miniroute {
|
||||||
|
/* List of miniroutes */
|
||||||
|
struct list_head list;
|
||||||
|
/* Network device */
|
||||||
|
struct net_device *netdev;
|
||||||
|
/* Destination prefix */
|
||||||
|
struct in6_addr prefix;
|
||||||
|
/* Prefix length */
|
||||||
|
int prefix_len;
|
||||||
|
/* IPv6 address of interface */
|
||||||
|
struct in6_addr address;
|
||||||
|
/* Gateway address */
|
||||||
|
struct in6_addr gateway;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** List of IPv6 miniroutes */
|
||||||
|
static LIST_HEAD ( miniroutes );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmit IP6 packets
|
* Add IPv6 interface
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
* @v prefix Destination prefix
|
||||||
|
* @v address Address of the interface
|
||||||
|
* @v gateway Gateway address (or ::0 for no gateway)
|
||||||
|
*/
|
||||||
|
int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
|
||||||
|
int prefix_len, struct in6_addr address,
|
||||||
|
struct in6_addr gateway ) {
|
||||||
|
struct ipv6_miniroute *miniroute;
|
||||||
|
|
||||||
|
miniroute = malloc ( sizeof ( *miniroute ) );
|
||||||
|
if ( !miniroute ) {
|
||||||
|
DBG ( "Not enough memory\n" );
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
miniroute->netdev = netdev;
|
||||||
|
miniroute->prefix = prefix;
|
||||||
|
miniroute->prefix_len = prefix_len;
|
||||||
|
miniroute->address = address;
|
||||||
|
miniroute->gateway = gateway;
|
||||||
|
|
||||||
|
/* Add miniroute to list of miniroutes */
|
||||||
|
if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
|
||||||
|
list_add_tail ( &miniroute->list, &miniroutes );
|
||||||
|
} else {
|
||||||
|
list_add ( &miniroute->list, &miniroutes );
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove IPv6 interface
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
*/
|
||||||
|
void del_ipv6_address ( struct net_device *netdev ) {
|
||||||
|
struct ipv6_miniroute *miniroute;
|
||||||
|
|
||||||
|
list_for_each_entry ( miniroute, &miniroutes, list ) {
|
||||||
|
if ( miniroute->netdev == netdev ) {
|
||||||
|
list_del ( &miniroute->list );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate TCPIP checksum
|
||||||
|
*
|
||||||
|
* @v pkb Packet buffer
|
||||||
|
* @v tcpip TCP/IP protocol
|
||||||
|
*
|
||||||
|
* This function constructs the pseudo header and completes the checksum in the
|
||||||
|
* upper layer header.
|
||||||
|
*/
|
||||||
|
static void ipv6_tx_csum ( struct pk_buff *pkb, struct tcpip_protocol *tcpip ) {
|
||||||
|
struct ip6_header *ip6hdr = pkb->data;
|
||||||
|
struct ipv6_pseudo_header pshdr;
|
||||||
|
uint16_t *csum = ( ( ( void * ) ip6hdr ) + sizeof ( *ip6hdr ) +
|
||||||
|
tcpip->csum_offset );
|
||||||
|
|
||||||
|
/* Calculate pseudo header */
|
||||||
|
memset ( &pshdr, 0, sizeof ( pshdr ) );
|
||||||
|
pshdr.src = ip6hdr->src;
|
||||||
|
pshdr.dest = ip6hdr->dest;
|
||||||
|
pshdr.len = htons ( pkb_len ( pkb ) - sizeof ( *ip6hdr ) );
|
||||||
|
pshdr.nxt_hdr = ip6hdr->nxt_hdr;
|
||||||
|
|
||||||
|
/* Update checksum value */
|
||||||
|
*csum = tcpip_continue_chksum ( *csum, &pshdr, sizeof ( pshdr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump IP6 header for debugging
|
||||||
|
*
|
||||||
|
* ip6hdr IPv6 header
|
||||||
|
*/
|
||||||
|
void ipv6_dump ( struct ip6_header *ip6hdr ) {
|
||||||
|
DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
|
||||||
|
inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
|
||||||
|
ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmit IP6 packet
|
||||||
|
*
|
||||||
|
* pkb Packet buffer
|
||||||
|
* tcpip TCP/IP protocol
|
||||||
|
* st_dest Destination socket address
|
||||||
|
*
|
||||||
|
* This function prepends the IPv6 headers to the payload an transmits it.
|
||||||
*/
|
*/
|
||||||
static int ipv6_tx ( struct pk_buff *pkb,
|
static int ipv6_tx ( struct pk_buff *pkb,
|
||||||
struct tcpip_protocol *tcpip_protocol,
|
struct tcpip_protocol *tcpip,
|
||||||
struct sockaddr_tcpip *st_dest ) {
|
struct sockaddr_tcpip *st_dest ) {
|
||||||
return -ENOSYS;
|
struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
|
||||||
|
struct in6_addr next_hop;
|
||||||
|
struct ipv6_miniroute *miniroute;
|
||||||
|
struct net_device *netdev = NULL;
|
||||||
|
uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
|
||||||
|
const uint8_t *ll_dest = ll_dest_buf;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Construct the IPv6 packet */
|
||||||
|
struct ip6_header *ip6hdr = pkb_push ( pkb, sizeof ( *ip6hdr ) );
|
||||||
|
memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
|
||||||
|
ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
|
||||||
|
ip6hdr->payload_len = htons ( pkb_len ( pkb ) - sizeof ( *ip6hdr ) );
|
||||||
|
ip6hdr->nxt_hdr = tcpip->tcpip_proto;
|
||||||
|
ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
|
||||||
|
|
||||||
|
/* Determine the next hop address and interface
|
||||||
|
*
|
||||||
|
* TODO: Implement the routing table.
|
||||||
|
*/
|
||||||
|
next_hop = dest->sin6_addr;
|
||||||
|
list_for_each_entry ( miniroute, &miniroutes, list ) {
|
||||||
|
if ( ( strncmp ( &ip6hdr->dest, &miniroute->prefix,
|
||||||
|
miniroute->prefix_len ) == 0 ) ||
|
||||||
|
( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
|
||||||
|
netdev = miniroute->netdev;
|
||||||
|
ip6hdr->src = miniroute->address;
|
||||||
|
if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
|
||||||
|
next_hop = miniroute->gateway;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* No network interface identified */
|
||||||
|
if ( !netdev ) {
|
||||||
|
DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
|
||||||
|
rc = -EHOSTUNREACH;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Complete the transport layer checksum */
|
||||||
|
if ( tcpip->csum_offset > 0 ) {
|
||||||
|
ipv6_tx_csum ( pkb, tcpip );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print IPv6 header */
|
||||||
|
ipv6_dump ( ip6hdr );
|
||||||
|
|
||||||
|
/* Resolve link layer address */
|
||||||
|
if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
|
||||||
|
ll_dest_buf[0] = 0x33;
|
||||||
|
ll_dest_buf[1] = 0x33;
|
||||||
|
ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
|
||||||
|
ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
|
||||||
|
ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
|
||||||
|
ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
|
||||||
|
} else {
|
||||||
|
/* Unicast address needs to be resolved by NDP */
|
||||||
|
if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
|
||||||
|
ll_dest_buf ) ) != 0 ) {
|
||||||
|
DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transmit packet */
|
||||||
|
return net_tx ( pkb, netdev, &ipv6_protocol, ll_dest );
|
||||||
|
|
||||||
|
err:
|
||||||
|
free_pkb ( pkb );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process next IP6 header
|
||||||
|
*
|
||||||
|
* @v pkb Packet buffer
|
||||||
|
* @v nxt_hdr Next header number
|
||||||
|
* @v src Source socket address
|
||||||
|
* @v dest Destination socket address
|
||||||
|
*
|
||||||
|
* Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
|
||||||
|
*/
|
||||||
|
static int ipv6_process_nxt_hdr ( struct pk_buff *pkb, uint8_t nxt_hdr,
|
||||||
|
struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
|
||||||
|
switch ( nxt_hdr ) {
|
||||||
|
case IP6_HOPBYHOP:
|
||||||
|
case IP6_ROUTING:
|
||||||
|
case IP6_FRAGMENT:
|
||||||
|
case IP6_AUTHENTICATION:
|
||||||
|
case IP6_DEST_OPTS:
|
||||||
|
case IP6_ESP:
|
||||||
|
DBG ( "Function not implemented for header %d\n", nxt_hdr );
|
||||||
|
return -ENOSYS;
|
||||||
|
case IP6_ICMP6:
|
||||||
|
break;
|
||||||
|
case IP6_NO_HEADER:
|
||||||
|
DBG ( "No next header\n" );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Next header is not a IPv6 extension header */
|
||||||
|
return tcpip_rx ( pkb, nxt_hdr, src, dest );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process incoming IP6 packets
|
* Process incoming IP6 packets
|
||||||
*
|
*
|
||||||
* Placeholder function. Should rewrite in net/ipv6.c
|
* @v pkb Packet buffer
|
||||||
|
* @v netdev Network device
|
||||||
|
* @v ll_source Link-layer source address
|
||||||
|
*
|
||||||
|
* This function processes a IPv6 packet
|
||||||
*/
|
*/
|
||||||
static int ipv6_rx ( struct pk_buff *pkb __unused,
|
static int ipv6_rx ( struct pk_buff *pkb,
|
||||||
struct net_device *netdev __unused,
|
struct net_device *netdev,
|
||||||
const void *ll_source __unused ) {
|
const void *ll_source ) {
|
||||||
return -ENOSYS;
|
|
||||||
|
struct ip6_header *ip6hdr = pkb->data;
|
||||||
|
union {
|
||||||
|
struct sockaddr_in6 sin6;
|
||||||
|
struct sockaddr_tcpip st;
|
||||||
|
} src, dest;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if ( pkb_len ( pkb ) < sizeof ( *ip6hdr ) ) {
|
||||||
|
DBG ( "Packet too short (%d bytes)\n", pkb_len ( pkb ) );
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Verify checksum */
|
||||||
|
|
||||||
|
/* Print IP6 header for debugging */
|
||||||
|
ipv6_dump ( ip6hdr );
|
||||||
|
|
||||||
|
/* Check header version */
|
||||||
|
if ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 != 0x60000000 ) {
|
||||||
|
DBG ( "Invalid protocol version\n" );
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the payload length */
|
||||||
|
if ( ntohs ( ip6hdr->payload_len ) > pkb_len ( pkb ) ) {
|
||||||
|
DBG ( "Inconsistent packet length (%d bytes)\n",
|
||||||
|
ip6hdr->payload_len );
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore the traffic class and flow control values */
|
||||||
|
|
||||||
|
/* Construct socket address */
|
||||||
|
memset ( &src, 0, sizeof ( src ) );
|
||||||
|
src.sin6.sin_family = AF_INET6;
|
||||||
|
src.sin6.sin6_addr = ip6hdr->src;
|
||||||
|
memset ( &dest, 0, sizeof ( dest ) );
|
||||||
|
dest.sin6.sin_family = AF_INET6;
|
||||||
|
dest.sin6.sin6_addr = ip6hdr->dest;
|
||||||
|
|
||||||
|
/* Strip header */
|
||||||
|
pkb_unput ( pkb, pkb_len ( pkb ) - ntohs ( ip6hdr->payload_len ) -
|
||||||
|
sizeof ( *ip6hdr ) );
|
||||||
|
pkb_pull ( pkb, sizeof ( *ip6hdr ) );
|
||||||
|
|
||||||
|
/* Send it to the transport layer */
|
||||||
|
return ipv6_process_nxt_hdr ( pkb, ip6hdr->nxt_hdr, &src.st, &dest.st );
|
||||||
|
|
||||||
|
drop:
|
||||||
|
DBG ( "Packet dropped\n" );
|
||||||
|
free_pkb ( pkb );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
|
||||||
|
*/
|
||||||
|
char * inet6_ntoa ( struct in6_addr in6 ) {
|
||||||
|
static char buf[40];
|
||||||
|
uint16_t *bytes = ( uint16_t* ) &in6;
|
||||||
|
sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
|
||||||
|
bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * ipv6_ntoa ( const void *net_addr ) {
|
static const char * ipv6_ntoa ( const void *net_addr ) {
|
||||||
// return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
|
return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
|
||||||
return "no support yet";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** IPv6 protocol */
|
/** IPv6 protocol */
|
||||||
struct net_protocol ipv6_protocol __net_protocol = {
|
struct net_protocol ipv6_protocol __net_protocol = {
|
||||||
.name = "IP6",
|
.name = "IPv6",
|
||||||
.net_proto = htons ( ETH_P_IPV6 ),
|
.net_proto = htons ( ETH_P_IPV6 ),
|
||||||
.net_addr_len = sizeof ( struct in6_addr ),
|
.net_addr_len = sizeof ( struct in6_addr ),
|
||||||
.rx = ipv6_rx,
|
.rx = ipv6_rx,
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <byteswap.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <gpxe/if_ether.h>
|
||||||
|
#include <gpxe/pkbuff.h>
|
||||||
|
#include <gpxe/ndp.h>
|
||||||
|
#include <gpxe/icmp6.h>
|
||||||
|
#include <gpxe/ip6.h>
|
||||||
|
#include <gpxe/netdevice.h>
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* Neighbour Discovery Protocol
|
||||||
|
*
|
||||||
|
* This file implements address resolution as specified by the neighbour
|
||||||
|
* discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
|
||||||
|
* family.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* A neighbour entry */
|
||||||
|
struct ndp_entry {
|
||||||
|
/** Target IP6 address */
|
||||||
|
struct in6_addr in6;
|
||||||
|
/** Link layer protocol */
|
||||||
|
struct ll_protocol *ll_protocol;
|
||||||
|
/** Link-layer address */
|
||||||
|
uint8_t ll_addr[MAX_LL_ADDR_LEN];
|
||||||
|
/** State of the neighbour entry */
|
||||||
|
int state;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Number of entries in the neighbour cache table */
|
||||||
|
#define NUM_NDP_ENTRIES 4
|
||||||
|
|
||||||
|
/** The neighbour cache table */
|
||||||
|
static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
|
||||||
|
#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
|
||||||
|
|
||||||
|
static unsigned int next_new_ndp_entry = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find entry in the neighbour cache
|
||||||
|
*
|
||||||
|
* @v in6 IP6 address
|
||||||
|
*/
|
||||||
|
static struct ndp_entry *
|
||||||
|
ndp_find_entry ( struct in6_addr *in6 ) {
|
||||||
|
struct ndp_entry *ndp;
|
||||||
|
|
||||||
|
for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
|
||||||
|
if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) &&
|
||||||
|
( ndp->state != NDP_STATE_INVALID ) ) {
|
||||||
|
return ndp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add NDP entry
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
* @v in6 IP6 address
|
||||||
|
* @v ll_addr Link-layer address
|
||||||
|
* @v state State of the entry - one of the NDP_STATE_XXX values
|
||||||
|
*/
|
||||||
|
void add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
|
||||||
|
void *ll_addr, int state ) {
|
||||||
|
struct ndp_entry *ndp;
|
||||||
|
ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
|
||||||
|
|
||||||
|
/* Fill up entry */
|
||||||
|
ndp->ll_protocol = netdev->ll_protocol;
|
||||||
|
memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
|
||||||
|
if ( ll_addr ) {
|
||||||
|
memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
|
||||||
|
} else {
|
||||||
|
memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
|
||||||
|
}
|
||||||
|
ndp->state = state;
|
||||||
|
DBG ( "New neighbour cache entry (%d): IP6 %s => %s %s\n",
|
||||||
|
( ndp - ndp_table ) / sizeof ( *ndp ),
|
||||||
|
inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
|
||||||
|
netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the link-layer address
|
||||||
|
*
|
||||||
|
* @v netdev Network device
|
||||||
|
* @v dest Destination address
|
||||||
|
* @v src Source address
|
||||||
|
* @ret dest_ll_addr Destination link-layer address or NULL
|
||||||
|
* @ret rc Status
|
||||||
|
*
|
||||||
|
* This function looks up the neighbour cache for an entry corresponding to the
|
||||||
|
* destination address. If it finds a valid entry, it fills up dest_ll_addr and
|
||||||
|
* returns 0. Otherwise it sends a neighbour solicitation to the solicited
|
||||||
|
* multicast address.
|
||||||
|
*/
|
||||||
|
int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
|
||||||
|
struct in6_addr *src, void *dest_ll_addr ) {
|
||||||
|
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||||
|
struct ndp_entry *ndp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
ndp = ndp_find_entry ( dest );
|
||||||
|
/* Check if the entry is valid */
|
||||||
|
if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
|
||||||
|
DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
|
||||||
|
inet6_ntoa ( *dest ), ll_protocol->name,
|
||||||
|
ll_protocol->ntoa ( ndp->ll_addr ) );
|
||||||
|
memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the entry was already created */
|
||||||
|
if ( ndp ) {
|
||||||
|
DBG ( "Awaiting neighbour advertisement (cache entry %d)\n",
|
||||||
|
( ( ndp - ndp_table ) / sizeof ( *ndp ) ) );
|
||||||
|
/* For test */
|
||||||
|
// ndp->state = NDP_STATE_REACHABLE;
|
||||||
|
// memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
|
||||||
|
// assert ( ndp->ll_protocol->ll_addr_len == 6 );
|
||||||
|
// icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
|
||||||
|
// assert ( ndp->state == NDP_STATE_REACHABLE );
|
||||||
|
/* Take it out till here */
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
|
||||||
|
|
||||||
|
/* Add entry in the neighbour cache */
|
||||||
|
add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
|
||||||
|
|
||||||
|
/* Send neighbour solicitation */
|
||||||
|
if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process neighbour advertisement
|
||||||
|
*
|
||||||
|
* @v pkb Packet buffer
|
||||||
|
* @v st_src Source address
|
||||||
|
* @v st_dest Destination address
|
||||||
|
*/
|
||||||
|
int ndp_process_advert ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
|
||||||
|
struct sockaddr_tcpip *st_dest ) {
|
||||||
|
struct neighbour_advert *nadvert = pkb->data;
|
||||||
|
struct ndp_entry *ndp;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if ( pkb_len ( pkb ) < sizeof ( *nadvert ) ) {
|
||||||
|
DBG ( "Packet too short (%d bytes)\n", pkb_len ( pkb ) );
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert ( nadvert->code == 0 );
|
||||||
|
assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
|
||||||
|
assert ( nadvert->opt_type == 2 );
|
||||||
|
|
||||||
|
/* Update the neighbour cache, if entry is present */
|
||||||
|
ndp = ndp_find_entry ( &nadvert->target );
|
||||||
|
if ( ndp ) {
|
||||||
|
|
||||||
|
assert ( nadvert->opt_len ==
|
||||||
|
( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
|
||||||
|
|
||||||
|
if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
|
||||||
|
memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
|
||||||
|
ndp->ll_protocol->ll_addr_len );
|
||||||
|
ndp->state = NDP_STATE_REACHABLE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG ( "Unsolicited advertisement (dropping packet)\n" );
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue