/****************************************************************************** * Copyright (c) 2013 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include #include #include #include #include "ethernet.h" #include "ipv6.h" #include "icmpv6.h" #include "ndp.h" #include "dhcpv6.h" static int ra_received = 0; /** * NET: * @param fd socket fd */ void send_router_solicitation (int fd) { ip6_addr_t dest_addr; uint8_t *ether_packet; struct packeth headers; ether_packet = malloc(ETH_MTU_SIZE); if (!ether_packet) { fprintf(stderr, "send_router_solicitation: Out of memory\n"); return; } headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); headers.icmp6h = (struct icmp6hdr *) (ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr)); /* Destination is "All routers multicast address" (link-local) */ dest_addr.part.prefix = 0xff02000000000000ULL; dest_addr.part.interface_id = 2; /* Fill IPv6 header */ fill_ip6hdr (ether_packet + sizeof(struct ethhdr), ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation), 0x3a, //ICMPV6 get_ipv6_address(), &dest_addr); /* Fill ICMPv6 message */ headers.icmp6h->type = ICMPV6_ROUTER_SOLICITATION; headers.icmp6h->code = 0; headers.icmp6h->icmp6body.router_solicit.lladdr.type = 1; headers.icmp6h->icmp6body.router_solicit.lladdr.length = 1; memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac), get_mac_address(), 6); send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation)); free(ether_packet); } /** * NET: Process prefix option in Router Advertisements * * @param ip6_packet pointer to an IPv6 packet */ static void handle_prefixoption (uint8_t *option) { ip6_addr_t prefix; struct ip6addr_list_entry *new_address; struct option_prefix *prefix_option; struct prefix_info *prfx_info; prefix_option = (struct option_prefix *) option; memcpy( &(prefix.addr), &(prefix_option->prefix.addr), IPV6_ADDR_LENGTH); /* Link-local adresses in RAs are nonsense */ if (ip6_is_linklocal(&prefix)) return; if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime) return; /* Add address created from prefix to IPv6 address list */ new_address = ip6_prefix2addr (prefix); if (!new_address) return; /* Process only prefixes we don't already have an adress from */ if (!unknown_prefix (&new_address->addr)) { return; } /* Fill struct prefix_info from data in RA and store it in new_address */ prfx_info = ip6_create_prefix_info(); if (!prfx_info) return; memcpy (&(new_address->prfx_info), prfx_info, sizeof(struct prefix_info)); /* Add prefix received in RA to list of known prefixes */ ip6addr_add (new_address); } /** * NET: Process source link layer addresses in Router Advertisements * * @param ip6_packet pointer to an IPv6 packet */ static void handle_source_lladdr ( struct option_ll_address *option, struct router *rtr) { memcpy (&(rtr->mac), &(option->mac), 6); } /** * NET: Process ICMPv6 options in Router Advertisements * * @param ip6_packet pointer to an IPv6 packet */ static void process_ra_options (uint8_t *option, int32_t option_length, struct router *r) { while (option_length > 0) { switch (*option) { case ND_OPTION_SOURCE_LL_ADDR: handle_source_lladdr ((struct option_ll_address *) option, r); break; case ND_OPTION_PREFIX_INFO: handle_prefixoption(option); break; default: break; } //option+1 is the length field. length is in units of 8 bytes option_length = option_length - (*(option+1) * 8); option = option + (*(option+1) * 8); } return; } /** * NET: Process Router Advertisements * * @param ip6_packet pointer to an IPv6 packet */ static void handle_ra (struct icmp6hdr *icmp6h, uint8_t *ip6_packet) { uint8_t *first_option; int32_t option_length; struct ip6hdr *ip6h; struct router_advertisement *ra; struct router *rtr; ip6_addr_t *rtr_ip; uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0}; ip6h = (struct ip6hdr *) ip6_packet; ra = (struct router_advertisement *) &icmp6h->icmp6body.ra; rtr_ip = (ip6_addr_t *) &ip6h->src; rtr = find_router (&(ip6h->src)); if (!rtr) { rtr = router_create (rtr_mac, rtr_ip); router_add (rtr); } /* store info from router advertisement in router struct */ rtr->lifetime = ra->router_lifetime; rtr->reachable_time = ra->reachable_time; rtr->retrans_timer = ra->retrans_timer; /* save flags concerning address (auto-) configuration */ ip6_state.managed_mode = ra->flags.managed; ip6_state.other_config = ra->flags.other; /* Process ICMPv6 options in Router Advertisement */ first_option = (uint8_t *) icmp6h + ICMPv6_HEADER_SIZE + 12; option_length = (uint8_t *) icmp6h + ip6h->pl - first_option; process_ra_options( (uint8_t *) first_option, option_length, rtr); ra_received = 1; } int is_ra_received(void) { return ra_received; } /** * NET: * * @param fd socket fd * @param ip6_addr_t *dest_ip6 */ void send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6) { ip6_addr_t snma; uint8_t *ether_packet; struct packeth headers; ether_packet = malloc(ETH_MTU_SIZE); if (!ether_packet) { fprintf(stderr, "send_neighbour_solicitation: Out of memory\n"); return; } memset(ether_packet, 0, ETH_MTU_SIZE); headers.ethh = (struct ethhdr *) ether_packet; headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); headers.icmp6h = (struct icmp6hdr *) (ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr)); /* Fill IPv6 header */ snma.part.prefix = IPV6_SOLIC_NODE_PREFIX; snma.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID; snma.addr[13] = dest_ip6->addr[13]; snma.addr[14] = dest_ip6->addr[14]; snma.addr[15] = dest_ip6->addr[15]; fill_ip6hdr((uint8_t *) headers.ip6h, ICMPv6_HEADER_SIZE + sizeof(struct neighbour_solicitation), 0x3a, //ICMPv6 get_ipv6_address(), &snma); /* Fill ICMPv6 message */ headers.icmp6h->type = ICMPV6_NEIGHBOUR_SOLICITATION; headers.icmp6h->code = 0; memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.target), dest_ip6, IPV6_ADDR_LENGTH ); headers.icmp6h->icmp6body.nghb_solicit.lladdr.type = 1; headers.icmp6h->icmp6body.nghb_solicit.lladdr.length = 1; memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac), get_mac_address(), 6); send_ip (fd, ether_packet + sizeof(struct ethhdr), sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + sizeof(struct neighbour_solicitation)); free(ether_packet); } /** * NET: * * @param fd socket fd * @param ip6_packet pointer to an IPv6 packet * @param icmp6hdr pointer to the icmp6 header in ip6_packet * @param na_flags Neighbour advertisment flags */ static void send_neighbour_advertisement (int fd, struct neighbor *target) { struct na_flags na_adv_flags; uint8_t *ether_packet; struct packeth headers; ether_packet = malloc(ETH_MTU_SIZE); if (!ether_packet) { fprintf(stderr, "send_neighbour_advertisement: Out of memory\n"); return; } headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); headers.icmp6h = (struct icmp6hdr *) (ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr)); /* Fill IPv6 header */ fill_ip6hdr(ether_packet + sizeof(struct ethhdr), ICMPv6_HEADER_SIZE + sizeof(struct neighbour_advertisement), 0x3a, //ICMPv6 get_ipv6_address(), (ip6_addr_t *) &(target->ip.addr)); /* Fill ICMPv6 message */ memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target), &(target->ip.addr), IPV6_ADDR_LENGTH ); headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 1; headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1; memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), get_mac_address(), 6); na_adv_flags.is_router = 0; na_adv_flags.na_is_solicited = 1; na_adv_flags.override = 1; headers.icmp6h->type = ICMPV6_NEIGHBOUR_ADVERTISEMENT; headers.icmp6h->code = 0; headers.icmp6h->icmp6body.nghb_adv.router = na_adv_flags.is_router; headers.icmp6h->icmp6body.nghb_adv.solicited = na_adv_flags.na_is_solicited; headers.icmp6h->icmp6body.nghb_adv.override = na_adv_flags.override; headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 2; headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1; memset( &(headers.icmp6h->icmp6body.nghb_adv.target), 0, IPV6_ADDR_LENGTH ); if( na_adv_flags.na_is_solicited ) { memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target), get_ipv6_address(), IPV6_ADDR_LENGTH); } memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), get_mac_address(), 6); send_ip (fd, ether_packet + sizeof(struct ethhdr), sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + sizeof(struct neighbour_advertisement)); free(ether_packet); } /** * NET: * * @param fd socket fd * @param ip6_packet pointer to an IPv6 packet */ static int8_t handle_na (int fd, uint8_t *packet) { struct neighbor *n = NULL; struct packeth headers; ip6_addr_t ip; headers.ethh = (struct ethhdr *) packet; headers.ip6h = (struct ip6hdr *) (packet + sizeof(struct ethhdr)); headers.icmp6h = (struct icmp6hdr *) (packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr)); memcpy(&(ip.addr), &(headers.ip6h->src), IPV6_ADDR_LENGTH); n = find_neighbor (&ip); if (!n) { n= (struct neighbor *) neighbor_create( packet, &headers ); if (!n) return 0; if (!neighbor_add(n)) return 0; } else { memcpy (&(n->mac), &(headers.ethh->src_mac[0]), 6); n->status = NB_REACHABLE; if (n->eth_len > 0) { struct ethhdr * ethh = (struct ethhdr *) &(n->eth_frame); memcpy(ethh->dest_mac, &(n->mac), 6); send_ether (fd, &(n->eth_frame), n->eth_len + sizeof(struct ethhdr)); n->eth_len = 0; } } return 1; } /** * NET: Handles ICMPv6 messages * * @param fd socket fd * @param ip6_packet pointer to an IPv6 packet * @param packetsize size of ipv6_packet */ int8_t handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t *ip6_packet) { struct icmp6hdr *received_icmp6 = NULL; struct ip6hdr *received_ip6 = NULL; struct neighbor target; received_ip6 = (struct ip6hdr *) ip6_packet; received_icmp6 = (struct icmp6hdr *) (ip6_packet + sizeof(struct ip6hdr)); memcpy( &(target.ip.addr), &(received_ip6->src), IPV6_ADDR_LENGTH ); memcpy( &(target.mac), etherhdr->src_mac, 6); /* process ICMPv6 types */ switch(received_icmp6->type) { case ICMPV6_NEIGHBOUR_SOLICITATION: send_neighbour_advertisement(fd, &target); break; case ICMPV6_NEIGHBOUR_ADVERTISEMENT: handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct ethhdr)); break; case ICMPV6_ROUTER_ADVERTISEMENT: handle_ra(received_icmp6, (uint8_t *) received_ip6); break; default: return -1; } return 1; }