Commit 96a47764 authored by Daniele Lacamera's avatar Daniele Lacamera

IPV6: Several fixes for SLAAC (RFC4862)

parent 12d1a5b4
......@@ -46,5 +46,6 @@ int pico_devices_loop(int loop_score, int direction);
struct pico_device*pico_get_device(const char*name);
int32_t pico_device_broadcast(struct pico_frame *f);
int pico_device_link_state(struct pico_device *dev);
int pico_device_ipv6_random_ll(struct pico_device *dev);
#endif
......@@ -67,6 +67,8 @@ void pico_vde_destroy(struct pico_device *dev)
{
struct pico_device_vde *vde = (struct pico_device_vde *) dev;
vde_close(vde->conn);
usleep(100000);
sync();
}
void pico_vde_set_packetloss(struct pico_device *dev, uint32_t in_pct, uint32_t out_pct)
......
......@@ -335,6 +335,12 @@ int pico_ipv6_is_multicast(const uint8_t addr[PICO_SIZE_IP6])
return 0;
}
int pico_ipv6_is_allhosts_multicast(const uint8_t addr[PICO_SIZE_IP6])
{
struct pico_ip6 allhosts = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }};
return !memcmp(allhosts.addr, addr, PICO_SIZE_IP6);
}
int pico_ipv6_is_solicited(const uint8_t addr[PICO_SIZE_IP6])
{
struct pico_ip6 solicited_node = {{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00 }};
......
......@@ -100,6 +100,7 @@ int pico_string_to_ipv6(const char *ipstr, uint8_t *ip);
int pico_ipv6_to_string(char *ipbuf, const uint8_t ip[PICO_SIZE_IP6]);
int pico_ipv6_is_unicast(struct pico_ip6 *a);
int pico_ipv6_is_multicast(const uint8_t addr[PICO_SIZE_IP6]);
int pico_ipv6_is_allhosts_multicast(const uint8_t addr[PICO_SIZE_IP6]);
int pico_ipv6_is_global(const uint8_t addr[PICO_SIZE_IP6]);
int pico_ipv6_is_uniquelocal(const uint8_t addr[PICO_SIZE_IP6]);
int pico_ipv6_is_sitelocal(const uint8_t addr[PICO_SIZE_IP6]);
......
......@@ -76,6 +76,17 @@ static void pico_ipv6_nd_queued_trigger(void)
}
}
static void ipv6_duplicate_detected(struct pico_ipv6_link *l)
{
struct pico_device *dev;
int is_ll = pico_ipv6_is_linklocal(l->address.addr);
dev = l->dev;
dbg("IPV6: Duplicate address detected. Removing link.\n");
pico_ipv6_link_del(l->dev, l->address);
if (is_ll)
pico_device_ipv6_random_ll(dev);
}
static struct pico_ipv6_neighbor *pico_nd_add(struct pico_ip6 *addr, struct pico_device *dev)
{
struct pico_ipv6_neighbor *n = PICO_ZALLOC(sizeof(struct pico_ipv6_neighbor));
......@@ -219,14 +230,14 @@ static int neigh_options(struct pico_frame *f, struct pico_icmp6_opt_lladdr *opt
if (type == expected_opt) {
memcpy(opt, (struct pico_icmp6_opt_lladdr *)option, sizeof(struct pico_icmp6_opt_lladdr));
break;
return 0;
} else if (optlen > 0) {
option += len * 8;
} else { /* no target link-layer address option */
return -1;
}
}
return 0;
return -1;
}
static int neigh_adv_complete(struct pico_ipv6_neighbor *n, struct pico_icmp6_opt_lladdr *opt)
......@@ -335,21 +346,57 @@ static void neighbor_from_sol(struct pico_ip6 *ip, struct pico_icmp6_opt_lladdr
}
}
static int neigh_sol_detect_dad(struct pico_frame *f)
{
struct pico_ipv6_hdr *ipv6_hdr = NULL;
struct pico_icmp6_hdr *icmp6_hdr = NULL;
struct pico_ipv6_link *link = NULL;
ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
link = pico_ipv6_link_istentative(&icmp6_hdr->msg.info.neigh_adv.target);
if (link) {
if (pico_ipv6_is_unicast(&ipv6_hdr->src))
{
/* RFC4862 5.4.3 : sender is performing address resolution,
* our address is not yet valid, discard silently.
*/
dbg("DAD:Sender performing AR\n");
}
else if (pico_ipv6_is_unspecified(ipv6_hdr->src.addr) &&
!pico_ipv6_is_allhosts_multicast(ipv6_hdr->dst.addr))
{
/* RFC4862 5.4.3 : sender is performing DaD */
dbg("DAD:Sender performing DaD\n");
ipv6_duplicate_detected(link);
}
return 0;
}
return -1; /* Current link is not tentative */
}
static int neigh_sol_process(struct pico_frame *f)
{
struct pico_ipv6_hdr *ipv6_hdr = NULL;
struct pico_icmp6_hdr *icmp6_hdr = NULL;
struct pico_ipv6_link *link = NULL;
int valid_lladdr;
struct pico_icmp6_opt_lladdr opt = {
0
};
ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
icmp6_hdr = (struct pico_icmp6_hdr *)f->transport_hdr;
neigh_options(f, &opt, PICO_ND_OPT_LLADDR_SRC);
valid_lladdr = neigh_options(f, &opt, PICO_ND_OPT_LLADDR_SRC);
neighbor_from_sol(&ipv6_hdr->src, &opt, f->dev);
if (!pico_ipv6_link_get(&icmp6_hdr->msg.info.neigh_adv.target)) { /* Not for us. */
if ((valid_lladdr < 0) && (neigh_sol_detect_dad(f) == 0))
return 0;
link = pico_ipv6_link_get(&icmp6_hdr->msg.info.neigh_adv.target);
if (!link) { /* Not for us. */
return -1;
}
pico_icmp6_neighbor_advertisement(f, &icmp6_hdr->msg.info.neigh_adv.target);
return 0;
}
......@@ -549,6 +596,7 @@ static int radv_process(struct pico_frame *f)
return 0;
}
static int pico_nd_router_adv_recv(struct pico_frame *f)
{
if (icmp6_initial_checks(f) < 0)
......@@ -581,8 +629,7 @@ static int pico_nd_neigh_adv_recv(struct pico_frame *f)
link = pico_ipv6_link_istentative(&icmp6_hdr->msg.info.neigh_adv.target);
if (link)
link->isduplicate = 1;
ipv6_duplicate_detected(link);
return neigh_adv_process(f);
}
......
......@@ -86,7 +86,7 @@ static int device_init_mac(struct pico_device *dev, uint8_t *mac)
return 0;
}
static int device_init_nomac(struct pico_device *dev)
int pico_device_ipv6_random_ll(struct pico_device *dev)
{
#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
......@@ -109,13 +109,20 @@ static int device_init_nomac(struct pico_device *dev)
} while (pico_ipv6_link_get(&linklocal));
if (pico_ipv6_link_add(dev, linklocal, netmask6)) {
PICO_FREE(dev->q_in);
PICO_FREE(dev->q_out);
return -1;
}
}
#endif
return 0;
}
static int device_init_nomac(struct pico_device *dev)
{
if (pico_device_ipv6_random_ll(dev) < 0) {
PICO_FREE(dev->q_in);
PICO_FREE(dev->q_out);
return -1;
}
dev->eth = NULL;
return 0;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment