Files
m241Neelima/components/ip_net2-6.9/iplite2/src/iplite_ip6.c
T
2026-07-30 11:19:44 +05:30

3692 lines
126 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: iplite_ip6.c,v $ $Revision: 1.131.10.1 $
* $Source: /home/interpeak/CVSRoot/iplite2/src/iplite_ip6.c,v $
* $Author: hxie $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#include "iplite_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#if !defined(IPLITE_ONE_FILE) || defined(IPLITE_C)
#ifdef IPCOM_USE_INET6
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_err.h>
#include <ipcom_inet.h>
#include <ipcom_syslog.h>
#include <ipcom_type.h>
#include "iplite_h.h"
#include "iplite_loopback.h"
#include "iplite_sock.h"
#ifdef IPFIREWALL
#include <ipfirewall.h>
#endif
#ifdef IPCOM_USE_TCP
#include "iplite_tcp.h"
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
#define IPLITE_IP6_ADDR_SOLICITED_NODE_CMP(addr1, addr2) \
((IP_GET_32ON16(&(addr1)->in6.addr32[3]) & ip_htonl(0x00ffffff)) == \
(IP_GET_32ON16(&(addr2)->in6.addr32[3]) & ip_htonl(0x00ffffff)))
/* Multicast address types */
#define IPLITE_IP6_IS_ADDR_SOLICITED(addr) \
(IP_GET_32ON16(&(addr)->in6.addr32[0]) == ip_htonl(0xff020000) && \
IP_GET_32ON16(&(addr)->in6.addr32[1]) == 0x00000000 && \
IP_GET_32ON16(&(addr)->in6.addr32[2]) == ip_htonl(0x00000001) && \
(addr)->in6.addr8[12] == 0xff)
/* Reserved address types */
#define IPLITE_IP6_SET_SOLICITED_ADDR(dst, src) do { \
IP_SET_HTONL(&(dst)->in6.addr32[0], 0xff020000); \
IP_SET_HTONL(&(dst)->in6.addr32[1], 0x00000000); \
IP_SET_HTONL(&(dst)->in6.addr32[2], 0x00000001); \
(dst)->in6.addr8[12] = 0xff; \
(dst)->in6.addr8[13] = (src)->in6.addr8[13]; \
(dst)->in6.addr16[7] = (src)->in6.addr16[7]; \
} while(0)
#define IPLITE_IP6_SET_ALL_NODES_ADDR(addr) do { \
IP_SET_HTONL(&(addr)->in6.addr32[0], 0xff020000); \
IP_SET_32ON16(&(addr)->in6.addr32[1], 0); \
IP_SET_32ON16(&(addr)->in6.addr32[2], 0); \
IP_SET_HTONL(&(addr)->in6.addr32[3], 0x00000001); \
} while(0)
#define IPLITE_IP6_SET_ALL_ROUTERS_ADDR(addr) do { \
IP_SET_HTONL(&(addr)->in6.addr32[0], 0xff020000); \
IP_SET_32ON16(&(addr)->in6.addr32[1], 0); \
IP_SET_32ON16(&(addr)->in6.addr32[2], 0); \
IP_SET_HTONL(&(addr)->in6.addr32[3], 0x00000002); \
} while(0)
/* Seconds an IP address is invalid when NDP fails. */
#define IPLITE_NDP_SEC_INVAL 25
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/* Fragmentation header */
typedef IP_PACKED struct Iplite_pkt_ip6_fragment_ext_hdr_struct
{
Iplite_pkt_ip6_ext_hdr hdr;
Ip_u16 offset_res_mflag; /* 13 bits offset, 2 reserved bits and 1 M-flag */
Ip_u16 identification[2];
}
Iplite_pkt_ip6_fragment_ext_hdr;
#ifdef IP_LITTLE_ENDIAN
#define IPLITE_IP6_HDR_FRAGMENT_MFLAG 0x0100
#else
#define IPLITE_IP6_HDR_FRAGMENT_MFLAG 0x0001
#endif /* IP_LITTLE_ENDIAN */
struct Iplite_icmp6_redirect_param
{
/* An IP address that is a better first hop to use for the ICMP Destination Address.*/
IP_CONST struct Ip_in6_addr *target_addr;
/* The IP address of the destination which is redirected to the target. */
IP_CONST struct Ip_in6_addr *dst_addr;
/* The route entry for the final (dst_addr) destination */
};
struct Iplite_icmp6_neighbor_sol_param
{
/* The IP address of the target of the solicitation. It MUST NOT be a multicast address. */
IP_CONST struct Ip_in6_addr *target_addr;
};
struct Iplite_icmp6_mld_param
{
/* Number of milliseconds before a response must be sent */
Ip_u16 max_response_delay;
/* The interface to use when sending the message */
Iplite_netif *netif;
/* The IP address of the target of the solicitation. It MUST be a multicast address. */
IP_CONST struct Ip_in6_addr *target_addr;
};
typedef struct Iplite_icmp6_param_struct
{
/* The interface to use if the packet is sent to a scoped (link-local) address */
Ip_u32 ifindex;
/* The packet that caused the ICMP response */
Ipcom_pkt *recv_pkt;
/* The the ICMPv6 message type */
int type;
/* The value to use in the code field */
int code;
/* The address to use as source address, can be IP_NULL (IPLITE will then use the
link-local address of the outgoing interface) */
IP_CONST struct Ip_in6_addr *from;
/* The address to which the ICMP message should be sent, must not be IP_NULL */
IP_CONST struct Ip_in6_addr *to;
union
{
struct Iplite_icmp6_redirect_param redirect;
struct Iplite_icmp6_neighbor_sol_param neigbor_sol;
Ip_u32 pkt_to_big_mtu;
Ip_u32 param_prob_pointer;
struct Iplite_icmp6_mld_param mld;
} data;
}
Iplite_icmp6_param;
typedef struct Iplite_process_ext_param_struct
{
/* [in/out] The packet for which extension is beeing processed */
Ipcom_pkt **ppkt;
/* [out] The IPv6 header for this packet */
Iplite_pkt_ip6 *ip6_hdr;
/* [out] ICMP type to respond with (only valid if the operation failed) */
Ip_u8 icmp6_type;
/* [out] ICMP code to respond with (only valid if the operation failed) */
Ip_u8 icmp6_code;
}
Iplite_process_ext_param;
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC int
iplite_ip6_assign_addr(Iplite_netif *netif, Iplite_ip6_addr_entry *addr_entry);
IP_STATIC IP_CONST struct Ip_in6_addr *
iplite_ip6_get_link_local_src_addr(Iplite_netif *netif);
IP_STATIC Ip_u16
iplite_ip6_checksum(IP_CONST struct Ip_in6_addr *src, IP_CONST struct Ip_in6_addr *dst, Ip_u16 proto, Ip_u16* data, Ip_u16 bytes);
/* Destination cache. */
IP_STATIC void iplite_ip6_neighbor_cache_init(Iplite_ip6_nc_entry *nc);
IP_STATIC void
iplite_ip6_default_router_set(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *in6_addr, Iplite_pkt_icmp6_nd_router_advert *ra);
IP_STATIC void
iplite_ip6_default_router_delete(Iplite_netif *netif, struct Ip_in6_addr *in6_addr);
IP_STATIC int
iplite_ip6_check_tentative_addresses(Iplite_netif *netif, Ipcom_pkt *pkt,
IP_CONST struct Ip_in6_addr *in6_addr);
IP_STATIC void
iplite_ip6_add_auto_addr(Iplite_netif *netif, Iplite_pkt_icmp6_prefix_opt *prefix_opt);
IP_STATIC Ip_bool
iplite_ip6_icmp6_get_option(Iplite_pkt_icmp6_opt *first_opt, int tot_opt_len,
Ip_u16 type, Iplite_pkt_icmp6_opt **ret, int nr);
IP_STATIC void
iplite_icmp6_input(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *src,
IP_CONST struct Ip_in6_addr *dst, int hlim, Ipcom_pkt *pkt);
IP_STATIC int
iplite_icmp6_send(Iplite_icmp6_param *param);
IP_STATIC int
iplite_ip6_send_router_sol_msg(Iplite_netif *netif);
IP_STATIC int
iplite_ip6_probe(Iplite_ip6_nc_entry *nc);
IP_STATIC void
iplite_ip6_add_fragmentation_hdr(Ipcom_pkt *fragment_list, Ip_u32 frag_id, int next_hdr);
IP_STATIC Ip_bool
iplite_ip6_is_part_of_same_pkt(Ipcom_pkt *frag1, Ipcom_pkt *frag2);
IP_STATIC int
iplite_ip6_get_offset(Ipcom_pkt *frag);
IP_STATIC Ip_bool
iplite_ip6_more_fragments(Ipcom_pkt *frag);
IP_STATIC void
iplite_ip6_update_ip_header(Ipcom_pkt *pkt, Ipcom_pkt *frag_list);
IP_STATIC int
iplite_ip6_process_exthdr(Iplite_process_ext_param *param);
IP_STATIC int
iplite_ip6_mld_report_all(Iplite_netif *netif);
IP_STATIC int
iplite_ip6_mld_report(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *mlt_addr);
IP_STATIC int
iplite_ip6_mld_done(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *mlt_addr);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* iplite_ip6_assign_addr
*===========================================================================
* Description: Adds the address to the list of assigned addresses and updates
* the routing table to route packet from this node to the new
* address via the loopback interface.
* Parameters: netif - The interface to which the address should be added.
* addr_entry - The address entry to add.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
iplite_ip6_assign_addr(Iplite_netif *netif, Iplite_ip6_addr_entry *addr_entry)
{
iplite_kioevent(netif, IP_EIOXNEWADDR, IP_NULL);
if (IP_BIT_ISSET(addr_entry->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE))
{
#if IPLITE_CONF_INET6_DUP_ADDR_DETECT_TRANSMITS > 0
Iplite_icmp6_param icmp6_param;
struct Ip_in6_addr tentative_sol_addr;
IPCOM_LOG2(INFO, "IPv6: performing DAD for %s at %s",
ipcom_inet_ntop(IP_AF_INET6, &addr_entry->addr, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
/* The address is assigned to the interface if it passes duplicate address detection. */
IPLITE_IP6_SET_SOLICITED_ADDR(&tentative_sol_addr, &addr_entry->addr);
addr_entry->valid_from = iplite.seconds + IPLITE_CONF_INET6_BASE_RETRANSMIT_TIME;
ipcom_memset(&icmp6_param, 0, sizeof(icmp6_param));
icmp6_param.ifindex = netif->ipcom.ifindex;
icmp6_param.type = IPLITE_ICMP6_TYPE_NEIGHBOR_SOL;
icmp6_param.from = &ip_in6addr_any;
icmp6_param.to = &tentative_sol_addr;
icmp6_param.data.neigbor_sol.target_addr = (IP_CONST struct Ip_in6_addr *)&addr_entry->addr;
(void)iplite_icmp6_send(&icmp6_param);
#endif /* IPLITE_CONF_INET6_DUP_ADDR_DETECT_TRANSMITS */
}
else
{
IPCOM_LOG2(INFO, "IPv6: assigned %s to %s",
ipcom_inet_ntop(IP_AF_INET6, &addr_entry->addr, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
}
return 0;
}
/*
*===========================================================================
* iplite_ip6_get_link_local_src_addr
*===========================================================================
* Description: Returns one of the link-local addresses on the specified
* interface.
* Parameters: netif - The interface for which a link-local address should
* be returned.
* Returns: A pointer to the matching address or IP_NULL if no address
* is found.
*
*/
IP_STATIC IP_CONST struct Ip_in6_addr *
iplite_ip6_get_link_local_src_addr(Iplite_netif *netif)
{
Iplite_ip6_addr_entry *addr;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
if (IP_BIT_ISFALSE(addr->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE)
&& IP_IN6_IS_ADDR_LINK_LOCAL(&addr->addr))
return &addr->addr;
return IP_NULL;
}
/*
*===========================================================================
* iplite_ip6_checksum
*===========================================================================
* Description: Calculates the internet checksum for a IPv6 packet.
* Parameters: src - The 128-bit IPv6 source address.
* dst - The 128-bit IPv6 destination address.
* proto - The protcol ID of the upper layer protocol.
* bytes - The number of bytes in the payload for which this is
* pseudo header.
* Returns: The one complement internet checksum.
*
*/
IP_STATIC Ip_u16
iplite_ip6_checksum(IP_CONST struct Ip_in6_addr *src, IP_CONST struct Ip_in6_addr *dst,
Ip_u16 proto, Ip_u16 *data, Ip_u16 bytes)
{
Ip_u32 sum;
sum = iplite_ip6_pseudo_header_checksum_update(src, dst, proto, bytes);
sum += ipcom_in_checksum_update(data, bytes);
return ipcom_in_checksum_finish(sum);
}
/*
*===========================================================================
* iplite_ip6_pseudo_header_checksum_update
*===========================================================================
* Description: Calculates the internet checksum on IPv6 pseudo header.
* Parameters: src - The source address.
* dst - The destination address.
* proto - The protcol ID of the upper layer protocol.
* len - The number of octets in the payload for which this is
* pseudo header.
* Returns: The accumulated 32 checksum including the pseudo header.
*
*/
IP_GLOBAL Ip_u32
iplite_ip6_pseudo_header_checksum_update(IP_CONST struct Ip_in6_addr *src,
IP_CONST struct Ip_in6_addr *dst, Ip_u16 proto, Ip_u16 len)
{
Ip_u32 sum;
sum = src->in6.addr16[0];
sum += src->in6.addr16[1];
sum += src->in6.addr16[2];
sum += src->in6.addr16[3];
sum += src->in6.addr16[4];
sum += src->in6.addr16[5];
sum += src->in6.addr16[6];
sum += src->in6.addr16[7];
sum += dst->in6.addr16[0];
sum += dst->in6.addr16[1];
sum += dst->in6.addr16[2];
sum += dst->in6.addr16[3];
sum += dst->in6.addr16[4];
sum += dst->in6.addr16[5];
sum += dst->in6.addr16[6];
sum += dst->in6.addr16[7];
sum += ip_htons(len);
sum += ip_htons(proto);
return sum;
}
/*
*===========================================================================
* iplite_ip6_neighbor_cache_init
*===========================================================================
* Description: Initialize the route entry to be used in NDP.
* Parameters: rt - The route table entry to initialize.
* Returns:
*
*/
IP_STATIC void
iplite_ip6_neighbor_cache_init(Iplite_ip6_nc_entry *nc)
{
ip_assert(nc != IP_NULL);
nc->next_timeout = iplite.seconds + IPLITE_CONF_INET6_NEIGHBOR_CACHE_TO_LIVE;
iplite_ip6_neighbor_cache_update(nc, IPLITE_IP6_ND_INCOMPLETE, IP_NULL);
}
/*
*===========================================================================
* iplite_ip6_default_router_add
*===========================================================================
* Description: Adds or updates a entry in the default router list.
* Parameters: netif - The link this router is on.
* addr - The link local IPv6 address of the router.
* ra - The ICMPv6 router advertisment message.
* Returns:
*
*/
IP_STATIC void
iplite_ip6_default_router_set(Iplite_netif *netif,
IP_CONST struct Ip_in6_addr *in6_addr,
Iplite_pkt_icmp6_nd_router_advert *ra)
{
ip_assert(in6_addr != IP_NULL);
ip_assert(ra != IP_NULL);
ip_assert(netif != IP_NULL);
if (IPLITE_ICMP6_RA_GET_RLIFE(ra) == 0
&& IP_IN6_ARE_ADDR_EQUAL(&netif->dr.addr, in6_addr))
{
netif->dr.is_set = IP_FALSE;
return;
}
IPCOM_LOG3(INFO, "IPv6: RA set %s default gateway: '%s' (%d sec)",
netif->ipcom.name,
ipcom_inet_ntop(IP_AF_INET6, in6_addr, iplite.str, sizeof(iplite.str)),
ip_ntohs(IPLITE_ICMP6_RA_GET_RLIFE(ra)));
netif->dr.is_set = IP_TRUE;
netif->dr.expires_sec = iplite.seconds
+ ip_ntohs(IPLITE_ICMP6_RA_GET_RLIFE(ra));
IPLITE_IP6_SET_ADDR(&netif->dr.addr, in6_addr);
}
/*
*===========================================================================
* iplite_ip6_default_router_delete
*===========================================================================
* Description: Deletes a entry from the default router list and invalidates
* all entries in the destination cache that used the deleted
* router.
* Parameters: netif - The link from which the router should be removed.
* addr - The link local IPv6 address of the router.
* Returns:
*
*/
IP_STATIC void
iplite_ip6_default_router_delete(Iplite_netif *netif, struct Ip_in6_addr *in6_addr)
{
if (IP_IN6_ARE_ADDR_EQUAL(&netif->dr.addr, in6_addr))
netif->dr.is_set = IP_FALSE;
}
/*
*===========================================================================
* iplite_ip6_check_tentative_addresses
*===========================================================================
* Description: Check if this message was sent to any of the current tentative
* addresses on this node.
* Parameters: netif - The interface on which the packet was received.
* addr - The address for which a Neighbor Solication or
* Neighbor Advertisment message has been received.
* Returns: -1 = Duplicate Address Detection failed.
* 0 = Ignore this packet (sent from this node)
* 1 = Continue to process the message.
*/
IP_STATIC int
iplite_ip6_check_tentative_addresses(Iplite_netif *netif, Ipcom_pkt *pkt,
IP_CONST struct Ip_in6_addr *in6_addr)
{
Iplite_ip6_addr_entry *addr;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
{
if (IP_BIT_ISFALSE(addr->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE))
continue;
if (IP_IN6_ARE_ADDR_EQUAL(in6_addr, &addr->addr))
{
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_LOOPED_BACK))
/* Sent by this node on loopback */
return 0;
#ifdef IPCOM_USE_ETHERNET
if (netif->ipcom.type == IP_IFT_ETHER)
{
ip_assert(netif->ipcom.link_hdr_size == 14);
if(ipcom_memcmp(netif->ipcom.link_addr,
&pkt->data[pkt->ipstart - netif->ipcom.link_hdr_size + 6],
netif->ipcom.link_hdr_size) == 0)
/* Ethernet src address is mine -> sent by this node */
return 0;
}
#endif /* IPCOM_USE_ETHERNET */
/* Duplicate Address Detection has failed since the message
* was not sent by this node. Remove the tentive address since
* the node can not use it
*/
IPCOM_LOG2(WARNING, "IPv6: DAD failed for tentative address %s on %s",
ipcom_inet_ntop(IP_AF_INET6, &addr->addr, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
(void)iplite_ip6_remove_addr(netif, &addr->addr);
return -1;
}
}
return 1;
}
/*
*===========================================================================
* iplite_ip6_add_auto_addr
*===========================================================================
* Description: Adds an auto address configuration address.
* Parameters: prefix_opt - The prefix option sent from a router.
* Returns:
*/
IP_STATIC void
iplite_ip6_add_auto_addr(Iplite_netif *netif, Iplite_pkt_icmp6_prefix_opt *prefix_opt)
{
struct Ip_in6_addr new_addr;
Ip_bool do_dad; /* Should duplicate address detection be done */
Ip_u32 preferred;
Ip_u32 valid;
if (IPLITE_ICMP6_GET_OPT_PREFIX_LEN(prefix_opt) != 128 - netif->inet6_if_id_bits)
return;
ip_assert2(netif->link_ip6_create_addr != IP_NULL);
if (netif->link_ip6_create_addr == IP_NULL)
return;
netif->link_ip6_create_addr(netif, &prefix_opt->prefix, &new_addr);
#if IPLITE_CONF_INET6_DUP_ADDR_DETECT_TRANSMITS > 0
/* Duplicate address detection is enabled */
do_dad = IP_TRUE;
if (iplite_ip6_get_link_local_src_addr(netif) != IP_NULL)
/* Duplicate address detection was successful for the link local
* address on this interface and the new address has the same host
* id so it will be unique
*/
do_dad = IP_FALSE;
#else
do_dad = IP_FALSE;
#endif /* IPLITE_CONF_INET6_DUP_ADDR_DETECT_TRANSMITS */
preferred = IP_GET_NTOHL(&prefix_opt->preferred_lifetime);
valid = IP_GET_NTOHL(&prefix_opt->valid_lifetime);
(void)iplite_ip6_add_addr(netif, &new_addr, prefix_opt->prefix_len,
IPLITE_IP6_ADDR_FLAG_AUTO | (do_dad ? IPLITE_IP6_ADDR_FLAG_TENTATIVE : 0),
preferred, valid);
}
/*
*===========================================================================
* iplite_ip6_icmp6_get_option
*===========================================================================
* Description: Processes the ICMPv6 message and put the reply (if any) pkt.
* Parameters: first_opt - Points to the first option in the packet.
* len - Length in byte of ICMPv6 header + data.
* type - The option type to find.
* ret - Will point to the option when the function returns,
* or to IP_NULL if the option was not found.
* nr - The number of the option (if the same option can occure
* multiple times).
* Returns: IP_FALSE if some option was invalid, the whole IP packet must
* be discared.
*
*/
IP_STATIC Ip_bool
iplite_ip6_icmp6_get_option(Iplite_pkt_icmp6_opt *first_opt, int tot_opt_len,
Ip_u16 type, Iplite_pkt_icmp6_opt **ret, int nr)
{
int opt_len;
*ret = first_opt;
while (tot_opt_len > 0)
{
opt_len = IPLITE_ICMP6_GET_OPT_LEN(*ret);
if (opt_len == 0)
return IP_FALSE;
if (IPLITE_ICMP6_GET_OPT_TYPE(*ret) == type && nr-- == 0)
return IP_TRUE;
tot_opt_len -= opt_len << 3;
*ret = (Iplite_pkt_icmp6_opt*) ((Ip_u16*) *ret + (opt_len << 2));
}
*ret = IP_NULL;
return IP_TRUE;
}
/*
*===========================================================================
* iplite_icmp6_input
*===========================================================================
* Description: Processes the ICMPv6 message and send a reply if neccessary.
* The processing of ICMPv6 messages is described in RFC 2461
* Parameters: netif - The network interface that received the message
* src - The source address of the packet.
* dst - The destination address for the packet (is a local address).
* hlim - The hoplimit value from the IPv6 header.
* pkt - The received ICMP6 message, pkt->start is the offset to
* the ICMP6 header.
* Returns:
*
*/
IP_STATIC void
iplite_icmp6_input(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *src,
IP_CONST struct Ip_in6_addr *dst, int hlim, Ipcom_pkt *pkt)
{
Iplite_icmp6_param icmp6_param;
Iplite_pkt_icmp6 *icmp6;
Iplite_pkt_icmp6_opt *opt;
Iplite_ip6_nc_entry *neighbor;
int len;
struct Ip_in6_addr new_src;
IPLITE_STATS(icmp6_input++);
IPCOM_MIB2_NETIF(ipv6IfIcmpInMsgs++);
IPLITE_IP6_SET_ADDR(&new_src, src);
icmp6 = (Iplite_pkt_icmp6*) &pkt->data[pkt->start];
len = pkt->end - pkt->start;
if (iplite_ip6_checksum(src, dst, IPLITE_IP6_HDR_ICMP6, (Ip_u16*) icmp6, (Ip_u16)len))
{
/* Checksum did not match */
IPLITE_STATS(icmp6_input_badchksum++);
goto errout;
}
/* Give a copy of the packet to all matching raw sockets. */
(void)iplite_raw_input(pkt, icmp6->type == IPLITE_ICMP6_TYPE_ECHO_REPLY,
IP_IPPROTO_ICMPV6, src, 0, dst, 0,
(Iplite_socklookup) iplite_ip6_socklookup);
if (icmp6->type == IPLITE_ICMP6_TYPE_ECHO_REPLY)
return;
/* Prepare an icmp6 param structure for output. */
ipcom_memset(&icmp6_param, 0, sizeof(icmp6_param));
icmp6_param.ifindex = netif->ipcom.ifindex;
icmp6_param.recv_pkt = pkt;
icmp6_param.to = &new_src;
switch (icmp6->type)
{
case IPLITE_ICMP6_TYPE_ECHO_REQUEST:
{
struct Ip_in6_addr new_dst;
IPCOM_MIB2_NETIF(ipv6IfIcmpInEchos++);
if (IP_IN6_IS_ADDR_MULTICAST(dst))
icmp6_param.from = iplite_ip6_get_src_addr(netif, dst, 0);
else
{
IPLITE_IP6_SET_ADDR(&new_dst, dst);
icmp6_param.from = &new_dst;
}
icmp6_param.type = IPLITE_ICMP6_TYPE_ECHO_REPLY;
(void)iplite_icmp6_send(&icmp6_param);
return; /* Do NOT break and free packet since it was reused for output! */
}
case IPLITE_ICMP6_TYPE_DST_UNREACHABLE:
{
Iplite_process_ext_param failed_pkt_ext_param;
Ip_u16 sport, dport;
Iplite_socket *sock;
int proto;
IPCOM_MIB2_NETIF(ipv6IfIcmpInDestUnreachs++);
#ifdef IPCOM_USE_MIB2
if (icmp6->code == IPLITE_ICMP6_DST_UNREACH_ADMIN)
IPCOM_MIB2_NETIF(ipv6IfIcmpInAdminProhibs++);
#endif
sport = 0;
dport = 0;
/* Move to start of the IPv6 header of the failing packet */
pkt->start += IPLITE_ICMPV6_HDR_SIZE;
failed_pkt_ext_param.ppkt = &pkt;
proto = iplite_ip6_process_exthdr(&failed_pkt_ext_param);
if (proto <= 0)
break;
if ((proto == IP_IPPROTO_UDP || proto == IP_IPPROTO_TCP)
&& pkt->end - pkt->start >= IPLITE_UDP_HDR_SIZE)
{
/* Note: TCP ports are located at the same offset as UDP ports */
Iplite_pkt_udp *failing_udp;
failing_udp = (Iplite_pkt_udp *)&pkt->data[pkt->start];
sport = ip_ntohs(failing_udp->sport);
dport = ip_ntohs(failing_udp->dport);
}
/* Find the affected socket */
sock = iplite_ip6_socklookup((Ip_u16) proto,
&failed_pkt_ext_param.ip6_hdr->src, sport,
&failed_pkt_ext_param.ip6_hdr->dst, dport, IP_NULL);
if (sock != IP_NULL && IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED))
{
int so_errno;
#ifdef IPCOM_USE_TCP
if(sock->tcb != IP_NULL)
goto done; /* Ignore ICMP for TCP sockets */
#endif
switch (IPLITE_ICMP6_GET_CODE(icmp6))
{
case IPLITE_ICMP6_DST_UNREACH_NOROUTE:
so_errno = IP_ERRNO_ENETUNREACH;
break;
case IPLITE_ICMP6_DST_UNREACH_BEYONDSCOPE:
case IPLITE_ICMP6_DST_UNREACH_ADDR:
so_errno = IP_ERRNO_EHOSTUNREACH;
break;
case IPLITE_ICMP6_DST_UNREACH_ADMIN:
case IPLITE_ICMP6_DST_UNREACH_NOPORT:
so_errno = IP_ERRNO_ECONNREFUSED;
break;
default:
so_errno = 0;
break;
}
if (so_errno)
/* Wake up any process sleeping on the socket */
iplite_sock_data_avail(sock, so_errno, IP_SHUT_RDWR);
}
break;
}
case IPLITE_ICMP6_TYPE_PACKET_TOO_BIG:
/*!! NOT SUPPORTED */
IPCOM_MIB2_NETIF(ipv6IfIcmpInPktTooBigs++);
IP_PANIC2();
break;
case IPLITE_ICMP6_TYPE_ROUTER_ADV:
{
Iplite_pkt_icmp6_nd_router_advert *ra;
int i;
IPCOM_MIB2_NETIF(ipv6IfIcmpInRouterAdvertisements++);
if (!IP_IN6_IS_ADDR_LINK_LOCAL(src) || hlim != 255)
goto errout;
if (pkt->end - pkt->start < (int) sizeof(*ra))
goto errout;
ra = (Iplite_pkt_icmp6_nd_router_advert *) icmp6;
/* Try to get the link address of the sender */
if (!iplite_ip6_icmp6_get_option((Iplite_pkt_icmp6_opt*) (ra + 1),
len - sizeof(Iplite_pkt_icmp6_nd_router_advert),
IPLITE_ICMP6_OPT_SOURCE_LINK_ADDR, &opt, 0))
{
/* Faulty option found */
goto errout;
}
neighbor = iplite_ip6_neighbor_cache_find(netif, src);
if (neighbor == IP_NULL)
goto errout;
if (opt != IP_NULL && !IP_IN6_IS_ADDR_MULTICAST(dst))
{
/* The router included its link layer address */
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_REACHABLE, opt->data);
}
neighbor->is_router = IP_TRUE;
iplite_ip6_default_router_set(netif, src, ra);
/* Check for any address prefix(es) in the message */
for (i = 0; ; i++)
{
Ip_u32 valid_lifetime;
Ip_u32 preferred_lifetime;
Iplite_pkt_icmp6_prefix_opt *prefix_opt;
if (!iplite_ip6_icmp6_get_option((Iplite_pkt_icmp6_opt*) (ra + 1),
len - sizeof(Iplite_pkt_icmp6_nd_router_advert),
IPLITE_ICMP6_OPT_PREFIX_INFO, (Iplite_pkt_icmp6_opt**) &prefix_opt, i))
{
/* Faulty option found*/
goto errout;
}
if (prefix_opt == IP_NULL)
/* No more address prefix options in the message */
break;
if (IP_IN6_IS_ADDR_LINK_LOCAL(&prefix_opt->prefix))
/* Link local address is always assumed to be on the link */
continue;
valid_lifetime = IP_GET_NTOHL(&prefix_opt->valid_lifetime);
preferred_lifetime = IP_GET_NTOHL(&prefix_opt->preferred_lifetime);
if (valid_lifetime < preferred_lifetime)
/* Not a valid lifetime configuration */
continue;
if (IPLITE_ICMP6_GET_OPT_ON_LINK(prefix_opt))
{
/* Addresses that has this prefix is on link */
if (IPLITE_ICMP6_GET_OPT_PREFIX_LEN(prefix_opt) > sizeof(struct Ip_in6_addr) * 8)
goto errout;
if (IPLITE_ICMP6_GET_OPT_AUTO_ADDR_CONF(prefix_opt))
/* This prefix should be used for auto address configuration */
iplite_ip6_add_auto_addr(netif, prefix_opt);
}
}
break;
}
case IPLITE_ICMP6_TYPE_NEIGHBOR_SOL:
{
Iplite_pkt_icmp6_nd_neighbor_solicit *sol;
IPCOM_MIB2_NETIF(ipv6IfIcmpInNeighborSolicits++);
if (hlim != 255)
/* A packet from a neighbor should never pass a router */
goto errout;
if (pkt->end - pkt->start < (int) sizeof(*sol))
goto errout;
sol = (Iplite_pkt_icmp6_nd_neighbor_solicit*) icmp6;
if (iplite_ip6_check_tentative_addresses(netif, pkt, &sol->addr) <= 0)
/* Duplicate address detection failed or this was sent by this node */
break;
/* Check if this message affect this node, the target address must be a unicast address */
if (iplite_ip6_get_addr_type(netif->ipcom.ifindex == IPLITE_IF_LOOPBACK_INDEX ? IP_NULL : netif, &sol->addr)
== IPLITE_ADDR_TYPE_NOT_LOCAL)
/* Message was not to this node */
break;
if (!iplite_ip6_icmp6_get_option((Iplite_pkt_icmp6_opt *) (sol + 1),
len - sizeof(Iplite_pkt_icmp6_nd_neighbor_solicit),
IPLITE_ICMP6_OPT_SOURCE_LINK_ADDR, &opt, 0))
{
goto errout; /* Faulty option found */
}
if (opt != IP_NULL)
{
/* The peer included its link address, update the ND cache */
neighbor = iplite_ip6_neighbor_cache_find(netif, src);
if (neighbor != IP_NULL)
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_REACHABLE, opt->data);
}
/* The message is processed, create a response */
icmp6_param.from = &sol->addr;
icmp6_param.type = IPLITE_ICMP6_TYPE_NEIGHBOR_ADV;
(void)iplite_icmp6_send(&icmp6_param);
break;
}
case IPLITE_ICMP6_TYPE_NEIGHBOR_ADV:
{
Iplite_pkt_icmp6_nd_neighbor_advert *adv;
IPCOM_MIB2_NETIF(ipv6IfIcmpInNeighborAdvertisements++);
if (hlim != 255)
goto errout;
if (pkt->end - pkt->start < (int) sizeof(*adv))
goto errout;
adv = (Iplite_pkt_icmp6_nd_neighbor_advert *)icmp6;
if (iplite_ip6_check_tentative_addresses(netif, pkt, &adv->addr) <= 0)
/* DAD failed or this was sent by this node */
break;
neighbor = iplite_ip6_neighbor_cache_find(netif, &adv->addr);
if (neighbor == IP_NULL)
/* Does not look like this node asked for this information */
break;
if (!iplite_ip6_icmp6_get_option((Iplite_pkt_icmp6_opt*) (adv + 1),
len - sizeof(Iplite_pkt_icmp6_nd_neighbor_advert),
IPLITE_ICMP6_OPT_TARGET_LINK_ADDR, &opt, 0))
{
/* Faulty option found */
goto errout;
}
if (opt != IP_NULL && IPLITE_ICMP6_GET_OPT_LEN(opt) != ((9 + netif->ipcom.link_addr_size) >> 3))
/* Wrong length on the link address */
goto errout;
if (neighbor->status == IPLITE_IP6_ND_INCOMPLETE)
{
neighbor->is_router = IPLITE_ICMP6_IS_ROUTER(adv) ? IP_TRUE : IP_FALSE;
if (opt != IP_NULL || netif->ipcom.link_addr_size == 0)
{
if (IPLITE_ICMP6_IS_SOLICATED(adv))
{
/* This is a response to a neighbor solicitation sent by this node */
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_REACHABLE,
opt ? opt->data : IP_NULL);
}
else
{
/* This node did not ask for this information, but use it anyway */
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_STALE,
opt ? opt->data : IP_NULL);
}
}
break;
}
if (opt != IP_NULL && IPLITE_ICMP6_IS_OVERRIDE(adv))
{
/* This link address should replace the old link address for this IPv6 address */
if (IPLITE_ICMP6_IS_SOLICATED(adv))
{
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_REACHABLE, opt->data);
}
else if (ipcom_memcmp(opt->data, neighbor->link_addr, sizeof (neighbor->link_addr)) == 0) /*lint !e420 */
{
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_STALE, opt->data);
}
}
else
{
if (opt == IP_NULL
|| ipcom_memcmp(opt->data, neighbor->link_addr, sizeof (neighbor->link_addr)) == 0) /*lint !e420 */
{
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_REACHABLE, IP_NULL);
}
else if (neighbor->status == IPLITE_IP6_ND_REACHABLE)
{
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_STALE, IP_NULL);
}
}
if (neighbor->is_router && !IPLITE_ICMP6_IS_ROUTER(adv))
/* The sending host has stopped forwarding packets */
iplite_ip6_default_router_delete(netif, &adv->addr);
neighbor->is_router = IPLITE_ICMP6_IS_ROUTER(adv) ? IP_TRUE : IP_FALSE;
break;
}
case IPLITE_ICMP6_TYPE_REDIRECT:
{
Iplite_pkt_icmp6_nd_redirect *rct;
IPCOM_MIB2_NETIF(ipv6IfIcmpInRedirects++);
if (hlim != 255 || len < 40)
goto errout;
rct = (Iplite_pkt_icmp6_nd_redirect *) icmp6;
neighbor = iplite_ip6_neighbor_cache_find(netif, &rct->target_addr);
if (neighbor == IP_NULL)
goto errout;
/* Update the neighbor cache if the target link address was included */
if (!iplite_ip6_icmp6_get_option((Iplite_pkt_icmp6_opt *)(rct + 1),
len - sizeof(Iplite_pkt_icmp6_nd_redirect),
IPLITE_ICMP6_OPT_TARGET_LINK_ADDR, &opt, 0))
{
/* Invalid option found */
goto errout;
}
if (opt != IP_NULL)
/* The linklayer address of the new next hop was included */
iplite_ip6_neighbor_cache_update(neighbor, IPLITE_IP6_ND_STALE, opt->data);
break;
}
case IPLITE_ICMP6_TYPE_MLD_QUERY:
{
Iplite_pkt_icmp6_mld *mld;
IPCOM_MIB2_NETIF(ipv6IfIcmpInGroupMembQueries++);
if (hlim != 1 || len < 40)
goto errout;
mld = (Iplite_pkt_icmp6_mld*) icmp6;
if (IP_IN6_IS_ADDR_MULTICAST(&mld->addr) == IP_FALSE)
goto errout;
if (IP_IN6_IS_ADDR_UNSPECIFIED(&mld->addr))
(void)iplite_ip6_mld_report_all(netif);
else
(void)iplite_ip6_mld_report(netif, &mld->addr);
break;
}
#ifdef IPCOM_USE_MIB2
/* Non supported options, but need MIB statistics. */
case IPLITE_ICMP6_TYPE_ECHO_REPLY:
IPCOM_MIB2_NETIF(ipv6IfIcmpInEchoReplies++);
break;
case IPLITE_ICMP6_TYPE_MLD_REPORT :
IPCOM_MIB2_NETIF(ipv6IfIcmpInGroupMembResponses++);
break;
case IPLITE_ICMP6_TYPE_TIME_EXCEEDED :
IPCOM_MIB2_NETIF(ipv6IfIcmpInTimeExcds++);
break;
case IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM :
IPCOM_MIB2_NETIF(ipv6IfIcmpInParmProblems++);
break;
case IPLITE_ICMP6_TYPE_MLD_DONE :
IPCOM_MIB2_NETIF(ipv6IfIcmpInGroupMembReductions++);
break;
case IPLITE_ICMP6_TYPE_ROUTER_SOL :
IPCOM_MIB2_NETIF(ipv6IfIcmpInRouterSolicits++);
break;
#endif /* #ifdef IPCOM_USE_MIB2 */
default:
break;
}
done:
/* Packet was processed or type was unknown */
if (pkt != IP_NULL)
ipcom_pkt_free(pkt);
return;
errout:
/* Error of some kind. */
IPLITE_STATS(icmp6_input_err++);
goto done;
}
/*
*===========================================================================
* iplite_icmp6_send
*===========================================================================
* Description: Creates and send a ICMPv6 message.
* Parameters: icmp6_param - The parameters needed to create the ICMPv6 message.
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
iplite_icmp6_send(Iplite_icmp6_param *icmp6_param)
{
struct Ip_in6_addr from;
Iplite_ip6_param param;
Iplite_pkt_icmp6_nd_neighbor_advert *adv;
Iplite_pkt_icmp6_nd_neighbor_solicit *sol;
Iplite_pkt_icmp6_mld *mld;
Ipcom_pkt *pkt;
Ipcom_pkt *failing_pkt;
Iplite_pkt_icmp6 *icmp6;
Iplite_pkt_icmp6_opt *opt;
Iplite_netif *netif;
Ip_u16 link_layer_addr_len;
#ifndef __LCC__
ip_assert(
icmp6_param->type == IPLITE_ICMP6_TYPE_DST_UNREACHABLE
|| icmp6_param->type == IPLITE_ICMP6_TYPE_NEIGHBOR_SOL
|| icmp6_param->type == IPLITE_ICMP6_TYPE_NEIGHBOR_ADV
|| icmp6_param->type == IPLITE_ICMP6_TYPE_ROUTER_SOL
|| icmp6_param->type == IPLITE_ICMP6_TYPE_ECHO_REPLY
/*|| icmp6_param->type == IPLITE_ICMP6_TYPE_TIME_EXCEEDED*/
|| icmp6_param->type == IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM
|| icmp6_param->type == IPLITE_ICMP6_TYPE_MLD_QUERY
|| icmp6_param->type == IPLITE_ICMP6_TYPE_MLD_REPORT
|| icmp6_param->type == IPLITE_ICMP6_TYPE_MLD_DONE
);
#endif
ip_assert(icmp6_param->to != IP_NULL);
ipcom_memset(&param, 0, sizeof(param));
param.to = icmp6_param->to;
param.nxt_hdr = IPLITE_IP6_HDR_ICMP6;
param.hlim = 255;
if (IP_IN6_IS_ADDR_UNSPECIFIED(param.to))
{
if (icmp6_param->type != IPLITE_ICMP6_TYPE_NEIGHBOR_ADV)
/* Only answer requests with the unspecified address as source that
are part of duplicate address detection */
return 0;
IPLITE_IP6_SET_ALL_NODES_ADDR(param.to);
}
netif = iplite_ip6_get_netif(icmp6_param->ifindex, param.to, &param.is_gw, IP_TRUE);
if (netif == IP_NULL)
return -IP_ERRNO_ENETUNREACH;
if (icmp6_param->from != IP_NULL && !IP_IN6_IS_ADDR_MULTICAST(icmp6_param->from))
{
param.from = &from;
IPLITE_IP6_SET_ADDR(&from, icmp6_param->from);
}
else
{
if (netif->ipcom.ifindex == IPLITE_IF_LOOPBACK_INDEX)
{
param.from = iplite_ip6_get_src_addr(netif, icmp6_param->to, 0);
}
else
{
param.from = iplite_ip6_get_link_local_src_addr(netif);
if (param.from == IP_NULL)
/* This interface does not have a link local address
for some reason */
return -IP_ERRNO_EADDRNOTAVAIL;
}
}
if (icmp6_param->type == IPLITE_ICMP6_TYPE_ECHO_REPLY)
{
/* Reuse the received packet */
pkt = icmp6_param->recv_pkt;
}
else
{
pkt = ipcom_pkt_malloc(netif->ipcom.mtu, IP_FLAG_FC_STACKCONTEXT);
if (pkt == IP_NULL)
{
IPLITE_STATS(icmp6_send_nomem++);
IPCOM_MIB2_NETIF(ipv6IfIcmpOutErrors++);
return -IP_ERRNO_ENOMEM;
}
/* Set pkt->start to the beginning of ICMPv6 data */
pkt->start = ipcom_conf_max_link_hdr_size + IPLITE_IP6_HDR_SIZE;
/* Make room for the ICMPv6 header */
pkt->end = pkt->start + IPLITE_ICMPV6_HDR_SIZE;
}
icmp6 = (Iplite_pkt_icmp6*) &pkt->data[pkt->start];
/* Initialize the data field */
switch (icmp6_param->type)
{
case IPLITE_ICMP6_TYPE_PACKET_TOO_BIG:
IP_SET_HTONL(icmp6->data.d32, icmp6_param->data.pkt_to_big_mtu);
break;
case IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM:
IP_SET_HTONL(icmp6->data.d32, icmp6_param->data.param_prob_pointer);
break;
case IPLITE_ICMP6_TYPE_ECHO_REPLY:
/* Contains id and sequence number for echo request/reply
messages */
break;
default:
icmp6->data.d16[0] = 0;
icmp6->data.d16[1] = 0;
break;
}
/* Add message specific parameters */
switch (icmp6_param->type)
{
case IPLITE_ICMP6_TYPE_DST_UNREACHABLE:
case IPLITE_ICMP6_TYPE_PACKET_TOO_BIG:
/*case IPLITE_ICMP6_TYPE_TIME_EXCEEDED:*/
case IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM:
/* Add as much as possible of the failing packet */
failing_pkt = icmp6_param->recv_pkt;
if ((Ip_size_t)(failing_pkt->end - failing_pkt->ipstart + pkt->end) > IPLITE_CONF_MIN_MTU)
{
/* Truncate the failing packet */
failing_pkt->end = failing_pkt->ipstart + IPLITE_CONF_MIN_MTU - pkt->end;
}
ipcom_memcpy(&pkt->data[pkt->end],
&failing_pkt->data[failing_pkt->ipstart],
failing_pkt->end - failing_pkt->ipstart);
pkt->end += failing_pkt->end - failing_pkt->ipstart;
break;
case IPLITE_ICMP6_TYPE_NEIGHBOR_SOL:
/* Add the destination address in the neighbor solicitation message */
sol = (Iplite_pkt_icmp6_nd_neighbor_solicit *) icmp6;
IPLITE_IP6_SET_ADDR(&sol->addr, icmp6_param->data.neigbor_sol.target_addr);
pkt->end += sizeof(struct Ip_in6_addr);
break;
case IPLITE_ICMP6_TYPE_NEIGHBOR_ADV:
adv = (Iplite_pkt_icmp6_nd_neighbor_advert *) icmp6;
IPLITE_ICMP6_SET_SOLICATED_FLAG(adv);
IPLITE_ICMP6_SET_OVERRIDE_FLAG(adv);
IPLITE_IP6_SET_ADDR(&adv->addr, icmp6_param->from);
pkt->end += sizeof(struct Ip_in6_addr);
break;
case IPLITE_ICMP6_TYPE_MLD_QUERY:
case IPLITE_ICMP6_TYPE_MLD_REPORT:
case IPLITE_ICMP6_TYPE_MLD_DONE:
mld = (Iplite_pkt_icmp6_mld *) icmp6;
mld->hdr.data.d16[0] = icmp6_param->data.mld.max_response_delay;
IPLITE_IP6_SET_ADDR(&mld->addr, icmp6_param->data.mld.target_addr);
if (icmp6_param->data.mld.netif != IP_NULL)
netif = icmp6_param->data.mld.netif;
pkt->end += sizeof(struct Ip_in6_addr);
break;
default:
break;
}
/* ICMPv6 options */
if (!IP_IN6_IS_ADDR_UNSPECIFIED(param.from)
&& (icmp6_param->type == IPLITE_ICMP6_TYPE_ROUTER_SOL
|| icmp6_param->type == IPLITE_ICMP6_TYPE_NEIGHBOR_SOL
|| icmp6_param->type == IPLITE_ICMP6_TYPE_NEIGHBOR_ADV))
{
/* Add Source Link Address Option */
opt = (Iplite_pkt_icmp6_opt*) &pkt->data[pkt->end];
/* Length in 32-bits word */
link_layer_addr_len = (Ip_u16) ((2 + netif->ipcom.link_addr_size + 7) / 8);
pkt->end += link_layer_addr_len << 3;
if (icmp6_param->type == IPLITE_ICMP6_TYPE_NEIGHBOR_ADV)
{
IPLITE_ICMP6_SET_OPT_TYPE(opt, IPLITE_ICMP6_OPT_TARGET_LINK_ADDR);
}
else
{
IPLITE_ICMP6_SET_OPT_TYPE(opt, IPLITE_ICMP6_OPT_SOURCE_LINK_ADDR);
}
IPLITE_ICMP6_SET_OPT_LEN(opt, link_layer_addr_len);
ipcom_memcpy(opt->data, netif->ipcom.link_addr, netif->ipcom.link_addr_size);
}
ip_assert(pkt->end <= pkt->maxlen);
IPLITE_ICMP6_SET_TYPE(icmp6, icmp6_param->type);
IPLITE_ICMP6_SET_CODE(icmp6, icmp6_param->code);
icmp6->cksum = 0;
icmp6->cksum = iplite_ip6_checksum(param.from, param.to,
IPLITE_IP6_HDR_ICMP6, (Ip_u16*) icmp6, (Ip_u16) (pkt->end - pkt->start));
#ifdef IPCOM_USE_MIB2
IPCOM_MIB2_NETIF(ipv6IfIcmpOutMsgs++);
switch (icmp6->type)
{
case IPLITE_ICMP6_TYPE_DST_UNREACHABLE:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutDestUnreachs++);
if (icmp6->code == IPLITE_ICMP6_DST_UNREACH_ADMIN)
IPCOM_MIB2_NETIF(ipv6IfIcmpOutAdminProhibs++);
else if (icmp6->code == IPLITE_ICMP6_DST_UNREACH_NOROUTE)
IPCOM_MIB2_NETIF(ipv6IfStatsInNoRoutes++);
break;
case IPLITE_ICMP6_TYPE_PACKET_TOO_BIG:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutPktTooBigs++);
break;
/*case IPLITE_ICMP6_TYPE_TIME_EXCEEDED:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutTimeExcds++);
break;*/
case IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutParmProblems++);
break;
case IPLITE_ICMP6_TYPE_ECHO_REQUEST:
/* Handled separately */
break;
case IPLITE_ICMP6_TYPE_ECHO_REPLY:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutEchoReplies++);
break;
case IPLITE_ICMP6_TYPE_MLD_QUERY:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutGroupMembQueries++);
break;
case IPLITE_ICMP6_TYPE_MLD_REPORT:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutGroupMembResponses++);
break;
case IPLITE_ICMP6_TYPE_MLD_DONE:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutGroupMembReductions++);
break;
case IPLITE_ICMP6_TYPE_ROUTER_SOL:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutRouterSolicits++);
break;
case IPLITE_ICMP6_TYPE_NEIGHBOR_SOL:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutNeighborSolicits++);
break;
case IPLITE_ICMP6_TYPE_NEIGHBOR_ADV:
IPCOM_MIB2_NETIF(ipv6IfIcmpOutNeighborAdvertisements++);
break;
default :
break;
}
#endif /* IPCOM_USE_MIB2 */
return iplite_ip6_output(netif, &param, pkt);
}
/*
*===========================================================================
* iplite_ip6_send_router_sol_msg
*===========================================================================
* Description: Sends a router solicitation message to quickly get information
* about routers on the link.
* Parameters: netif - The interface to which the message should be sent.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
iplite_ip6_send_router_sol_msg(Iplite_netif *netif)
{
struct Ip_in6_addr dst;
Iplite_icmp6_param icmp6_param;
IPLITE_IP6_SET_ALL_ROUTERS_ADDR(&dst);
ipcom_memset(&icmp6_param, 0, sizeof(icmp6_param));
icmp6_param.type = IPLITE_ICMP6_TYPE_ROUTER_SOL;
icmp6_param.ifindex = netif->ipcom.ifindex;
icmp6_param.to = &dst;
icmp6_param.from = iplite_ip6_get_src_addr(netif, &dst, IPLITE_IP6_SCOPE_LINK_LOCAL);
ip_assert(icmp6_param.from != IP_NULL);
if (icmp6_param.from == IP_NULL)
/* This interface does not have a link local address for some reason */
return -IP_ERRNO_EADDRNOTAVAIL;
return iplite_icmp6_send(&icmp6_param);
}
/*
*===========================================================================
* iplite_ip6_probe
*===========================================================================
* Description: Probes to verify or finds the physical address for the
* neighbor entry.
* Parameters: ndp - A entry from the neigbor discovery protocol cache.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
iplite_ip6_probe(Iplite_ip6_nc_entry *nc)
{
Iplite_icmp6_param icmp6_param;
struct Ip_in6_addr dst;
ip_assert(nc != IP_NULL);
if (nc->status == IPLITE_IP6_ND_STALE)
{
iplite_ip6_neighbor_cache_update(nc, IPLITE_IP6_ND_DELAY, IP_NULL);
return 0;
}
ipcom_memset(&icmp6_param, 0, sizeof (icmp6_param));
icmp6_param.type = IPLITE_ICMP6_TYPE_NEIGHBOR_SOL;
icmp6_param.ifindex = nc->netif->ipcom.ifindex;
icmp6_param.data.neigbor_sol.target_addr = &nc->addr;
icmp6_param.to = &nc->addr;
if (nc->status == IPLITE_IP6_ND_INCOMPLETE)
{
IPLITE_IP6_SET_SOLICITED_ADDR(&dst, &nc->addr);
icmp6_param.to = &dst;
return iplite_icmp6_send(&icmp6_param);
}
else if (nc->status == IPLITE_IP6_ND_PROBE)
{
icmp6_param.to = &nc->addr;
return iplite_icmp6_send(&icmp6_param);
}
return 0;
}
/*
*===========================================================================
* iplite_ip6_add_fragmentation_hdr
*===========================================================================
* Description: Adds a fragmentation header to all packets in the fragmentation
* list.
* Parameters: fragment_list - The first fragment in the list.
* frag_id - The fragment ID to use.
* next_header - The orginal next header.
* Returns:
*/
IP_STATIC void
iplite_ip6_add_fragmentation_hdr(Ipcom_pkt *fragment_list, Ip_u32 frag_id, int next_hdr)
{
Ipcom_pkt *frag_pkt;
Iplite_pkt_ip6_fragment_ext_hdr *frg;
for (frag_pkt = fragment_list; frag_pkt != IP_NULL; frag_pkt = frag_pkt->next_fragment)
{
ip_assert((Ip_size_t) frag_pkt->start > IP_IP6F_SIZE);
frag_pkt->start -= IP_IP6F_SIZE;
frg = (Iplite_pkt_ip6_fragment_ext_hdr*) &frag_pkt->data[frag_pkt->start];
frg->hdr.nxt = (Ip_u8)next_hdr;
frg->hdr.len = 0;
frg->offset_res_mflag = (Ip_u16) ip_htons(frag_pkt->offset);
if (frag_pkt->next_fragment != IP_NULL)
frg->offset_res_mflag |= IPLITE_IP6_HDR_FRAGMENT_MFLAG;
IP_SET_HTONL(frg->identification, frag_id);
}
}
/*
*===========================================================================
* iplite_ip6_is_part_of_same_pkt
*===========================================================================
* Description: Returns if the two fragments are parts in the same packet.
* Parameters: frag1 - A IPv6 fragment.
* frag2 - Another IPv6 fragment.
* Returns: IP_TRUE or IP_FALSE.
*
*/
IP_STATIC Ip_bool
iplite_ip6_is_part_of_same_pkt(Ipcom_pkt *frag1, Ipcom_pkt *frag2)
{
Iplite_pkt_ip6 *ip6_hdr1;
Iplite_pkt_ip6 *ip6_hdr2;
Iplite_pkt_ip6_fragment_ext_hdr *frag_hdr1;
Iplite_pkt_ip6_fragment_ext_hdr *frag_hdr2;
frag_hdr1 = (Iplite_pkt_ip6_fragment_ext_hdr *)
&frag1->data[frag1->start];
frag_hdr2 = (Iplite_pkt_ip6_fragment_ext_hdr *)
&frag2->data[frag2->start];
if (IP_GET_32ON16(frag_hdr1->identification) != IP_GET_32ON16(frag_hdr2->identification))
return IP_FALSE;
ip6_hdr1 = (Iplite_pkt_ip6 *) &frag1->data[frag1->ipstart];
ip6_hdr2 = (Iplite_pkt_ip6 *) &frag2->data[frag2->ipstart];
return IP_IN6_ARE_ADDR_EQUAL(&ip6_hdr1->src, &ip6_hdr2->src)
&& IP_IN6_ARE_ADDR_EQUAL(&ip6_hdr1->dst, &ip6_hdr2->dst);
}
/*
*===========================================================================
* iplite_ip6_get_offset
*===========================================================================
* Description: Returns the offset for this segment into the IP packet.
* Parameters: frag - A IPv6 fragment.
* Returns: The offset.
*
*/
IP_STATIC int
iplite_ip6_get_offset(Ipcom_pkt *frag)
{
return IP_IP6F_GET_OFFSET((Ip_pkt_ip6_frag *) &frag->data[frag->start]);
}
/*
*===========================================================================
* iplite_ip6_more_fragments
*===========================================================================
* Description: Returns if this is the last fragment or not.
* Parameters: frag - A IPv4 fragment.
* Returns: IP_TRUE or IP_FALSE.
*
*/
IP_STATIC Ip_bool
iplite_ip6_more_fragments(Ipcom_pkt *frag)
{
Iplite_pkt_ip6_fragment_ext_hdr *frag_hdr;
frag_hdr = (Iplite_pkt_ip6_fragment_ext_hdr *)
&frag->data[frag->start];
return (frag_hdr->offset_res_mflag & IPLITE_IP6_HDR_FRAGMENT_MFLAG) != 0;
}
/*
*===========================================================================
* iplite_ip6_update_ip_header
*===========================================================================
* Description: Sets the next header and plen field in the IPv6 header for
* the reassembled packet.
* Parameters: pkt - The reassembled IPv6 packet.
* frag_list - The individual fragments that was received.
* Returns:
*
*/
IP_STATIC void
iplite_ip6_update_ip_header(Ipcom_pkt *pkt, Ipcom_pkt *frag_list)
{
Iplite_pkt_ip6 *ip6_hdr;
Iplite_pkt_ip6_fragment_ext_hdr *frag_hdr;
int next_header;
frag_hdr = (Iplite_pkt_ip6_fragment_ext_hdr*)
&frag_list->data[frag_list->start - IP_IP6F_SIZE];
next_header = IPLITE_IP6_GET_NXT(&frag_hdr->hdr);
ip6_hdr = (Iplite_pkt_ip6*) &pkt->data[pkt->start];
/* This packet does not contain a fragmentation header */
IPLITE_IP6_SET_NXT(ip6_hdr, next_header);
ip6_hdr->plen = (Ip_u16) ip_htons(pkt->end - pkt->start - IPLITE_IP6_HDR_SIZE);
}
/*
*===========================================================================
* iplite_ip6_fragment_timeout
*===========================================================================
* Description: Called if reassembly of the packet times out.
* Parameters: frag_head - The first packet in the list of fragments.
* Returns:
*
*/
IP_STATIC void
iplite_ip6_fragment_timeout(Ipcom_pkt *frag_head)
{
Ipcom_pkt *f;
#ifdef IPCOM_USE_MIB2
Iplite_netif *netif = iplite_if_indextonetif(frag_head->ifindex);
if (netif != IP_NULL)
IPCOM_MIB2_NETIF(ipv6IfStatsReasmFails++);
#endif /* IPCOM_USE_MIB2 */
/* Unhook the fragment list from the reassembly list */
ip_assert(iplite.ip6.reassembly_list == frag_head);
iplite.ip6.reassembly_list = frag_head->next;
ipcom_pkt_free(frag_head);
/* Add a timeout for the next entry (if any) */
f = iplite.ip6.reassembly_list;
if (f != IP_NULL)
(void)iplite_timeout_schedule(IPLITE_PKT_GET_TIMEOUT(f) * 1000,
(Iplite_timeout_handler) iplite_ip6_fragment_timeout,
f,
IPLITE_PKT_GET_TMO_PTR(f));
}
/*
*===========================================================================
* iplite_ip6_process_ext_options
*===========================================================================
* Description: Processes all options in a extension header.
* Parameters: extension_type - the type of the extension header containing
* the options
* first_opt - pointer to the first option
* opts_len - the total length of all options
* param - Parameters needed to process extension headers.
* Returns: IP_TRUE - continue processing the packet
* IP_FALSE - discard the packet
*/
IP_STATIC Ip_bool
iplite_ip6_process_ext_options(int extension_type, Iplite_pkt_ip6_ext_opt *first_opt,
int opts_len, Iplite_process_ext_param *param)
{
Ip_pkt_ip6_opt *opt;
int opt_type;
int action;
int offset = 0;
(void) extension_type;
while (opts_len > offset)
{
opt = (Ip_pkt_ip6_opt *) ((Ip_u8 *) first_opt + offset);
opt_type = opt->ip6o_type & 0x1F;
action = opt->ip6o_type & 0xC0;
if (opt_type != IP_IP6OPT_PAD1 && (opt->ip6o_len < 2 || opts_len < opt->ip6o_len))
{
param->icmp6_type = IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM;
param->icmp6_code = IPLITE_ICMP6_PARAM_PROB_ERR_HDR;
return IP_FALSE;
}
switch (opt_type)
{
case IP_IP6OPT_PAD1:
offset += 1;
continue;
case IP_IP6OPT_PADN:
break;
default:
/* Unknown option, check the action */
switch (action)
{
case IP_IP6OPT_TYPE_SKIP:
break;
case IP_IP6OPT_TYPE_DISCARD:
return IP_FALSE;
case IP_IP6OPT_TYPE_FORCEICMP:
param->icmp6_type = IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM;
param->icmp6_code = IPLITE_ICMP6_PARAM_PROB_OPT;
return IP_FALSE;
case IP_IP6OPT_TYPE_ICMP:
if (IP_IN6_IS_ADDR_MULTICAST(&param->ip6_hdr->dst))
return IP_FALSE;
param->icmp6_type = IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM;
param->icmp6_code = IPLITE_ICMP6_PARAM_PROB_OPT;
return IP_FALSE;
default:
break;
}
}
offset += opt->ip6o_len + sizeof(Ip_pkt_ip6_opt);
}
return IP_TRUE;
}
/*
*===========================================================================
* iplite_ip6_process_exthdr
*===========================================================================
* Description: Processes all the extension headers up to, but not including,
* any upper layer protocol.
* Parameters: param - Parameters needed to process extension headers.
* Returns: >0 The upper layer protocol starting at (*ppkt)->start.
* <0 Error code.
* 0 Discard packet
*/
IP_STATIC int
iplite_ip6_process_exthdr(Iplite_process_ext_param *param)
{
Ipcom_pkt *reassembly_list;
Iplite_pkt_ip6_ext_hdr *ext_hdr;
int this_hdr;
Ipcom_pkt *pkt;
#ifdef IPCOM_USE_MIB2
Iplite_netif *netif = iplite_if_indextonetif((*param->ppkt)->ifindex);
#endif
param->icmp6_code = 0;
param->icmp6_type = 0;
for (;;)
{
pkt = *param->ppkt;
param->ip6_hdr = (Iplite_pkt_ip6*) &pkt->data[pkt->start];
this_hdr = param->ip6_hdr->nxt;
if (pkt->end < pkt->start + IPLITE_IP6_HDR_SIZE)
{
#ifdef IPCOM_USE_MIB2
if (netif != IP_NULL)
IPCOM_MIB2_NETIF(ipv6IfStatsInTruncatedPkts++);
#endif
return -IP_ERRNO_EINVAL;
}
pkt->start += IPLITE_IP6_HDR_SIZE;
for (;;)
{
int ext_len;
if (pkt->start + sizeof(Iplite_pkt_ip6_ext_hdr) > pkt->end)
{
/* Malformed packet */
#ifdef IPCOM_USE_MIB2
if (netif != IP_NULL)
IPCOM_MIB2_NETIF(ipv6IfStatsInTruncatedPkts++);
#endif
return -IP_ERRNO_EINVAL;
}
switch (this_hdr)
{
case IPLITE_IP6_HDR_HOP_BY_HOP_OPT:
case IPLITE_IP6_HDR_DST_OPT:
case IPLITE_IP6_HDR_FRAGMENT:
#ifdef IPIPSEC2
case IP_IPPROTO_ESP:
case IP_IPPROTO_AH:
#endif
/* Extension header */
break;
default:
/*
* Transport layer header or unsupported extension
* header
*/
return this_hdr;
}
ext_hdr = (Iplite_pkt_ip6_ext_hdr*) &pkt->data[pkt->start];
ext_len = ((ext_hdr->len + 1) << 3);
if (pkt->start + ext_len > pkt->end)
/* Malformed option */
return -IP_ERRNO_EINVAL;
switch (this_hdr)
{
case IPLITE_IP6_HDR_HOP_BY_HOP_OPT:
case IPLITE_IP6_HDR_DST_OPT:
if (iplite_ip6_process_ext_options(this_hdr,
(Iplite_pkt_ip6_ext_opt *) (ext_hdr + 1),
ext_len - sizeof(Iplite_pkt_ip6_ext_opt),
param) == IP_FALSE)
/* Discard this packet */
return 0;
break;
case IPLITE_IP6_HDR_FRAGMENT:
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_IP6_REASSEMBLY);
reassembly_list = iplite_reassembly(&iplite.ip6.reassembly_list,
pkt,
iplite_ip6_is_part_of_same_pkt,
iplite_ip6_get_offset,
iplite_ip6_more_fragments,
(Iplite_timeout_handler) iplite_ip6_fragment_timeout,
IP_IP6F_SIZE);
if (reassembly_list == IP_NULL)
return IPLITE_IP6_HDR_FRAGMENT;
pkt = iplite_create_reassembled_packet(reassembly_list,
IPLITE_IP6_HDR_SIZE,
IP_IP6F_SIZE,
iplite_ip6_update_ip_header,
IP_NULL);
*param->ppkt = pkt;
if (pkt == IP_NULL)
{
#ifdef IPCOM_USE_MIB2
if (netif != IP_NULL)
IPCOM_MIB2_NETIF(ipv6IfStatsReasmFails++);
#endif
return -IP_ERRNO_ENOBUFS;
}
param->ip6_hdr = (Iplite_pkt_ip6 *) &pkt->data[pkt->start];
break;
#ifdef IPIPSEC2
case IP_IPPROTO_ESP:
case IP_IPPROTO_AH:
{
int ipsec_ret;
Ipipsec_param ipsec_param;
/* Fill in ipsec_param. */
ipcom_memcpy(&ipsec_param.key.src.in6, &param->ip6_hdr->src, sizeof(struct Ip_in6_addr));
ipcom_memcpy(&ipsec_param.key.dst.in6, &param->ip6_hdr->dst, sizeof(struct Ip_in6_addr));
ipsec_param.key.proto = (Ip_u8)this_hdr;
ipsec_param.key.domain = IP_AF_INET6;
ipsec_ret = ipipsec_input(pkt, &ipsec_param);
if(ipsec_ret < 0)
return ipsec_ret; /* discard ipsec datagram. */
if(ipsec_ret == 1)
{
/* Tunneled packet. */
if(ipsec_param.key.domain == IP_AF_INET6)
return IP_IPPROTO_IPV6;
else
return IP_IPPROTO_IPIP;
}
this_hdr = ipsec_param.key.proto;
}
continue;
#endif /* IPIPSEC2 */
}
if (this_hdr == IPLITE_IP6_HDR_FRAGMENT)
{
/* All fragments received, continue process the reassebled packet */
#ifdef IPCOM_USE_MIB2
if (netif != IP_NULL)
IPCOM_MIB2_NETIF(ipv6IfStatsReasmOKs++);
#endif
break;
}
this_hdr = ext_hdr->nxt;
pkt->start += ext_len;
}
}
}
/*
*===========================================================================
* iplite_ip6_mld_send_message
*===========================================================================
* Description: Determines if a MLD message should be sent for the
* specified multicast address.
* Parameters:
* Returns: >=0 = success , <0 = error code.
*
*/
IP_STATIC Ip_bool
iplite_ip6_mld_send_message(IP_CONST struct Ip_in6_addr *mlt_addr)
{
struct Ip_in6_addr addr;
ip_assert(IP_IN6_IS_ADDR_MULTICAST(mlt_addr));
if (IPLITE_IP6_MC_SCOPE(mlt_addr) <= IPLITE_IP6_SCOPE_NODE_LOCAL)
return IP_FALSE;
IPLITE_IP6_SET_ALL_NODES_ADDR(&addr);
if (IP_IN6_ARE_ADDR_EQUAL(mlt_addr, &addr))
return IP_FALSE;
IPLITE_IP6_SET_ALL_ROUTERS_ADDR(&addr);
if (IP_IN6_ARE_ADDR_EQUAL(mlt_addr, &addr))
return IP_FALSE;
return IP_TRUE;
}
/*
*===========================================================================
* iplite_ip6_mld_report_all
*===========================================================================
* Description: Sends a multicast listener report message for all multicast
* addresses on the specified link.
* Parameters: netif - The interface to report multicast group memberships
* for.
* Returns: >=0 = success , <0 = error code.
*
*/
IP_STATIC int
iplite_ip6_mld_report_all(Iplite_netif *netif)
{
Iplite_ip6_addr_entry *addr;
int ret;
int num_reports = 0;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
{
if (IP_IN6_IS_ADDR_MULTICAST(&addr->addr))
{
ret = iplite_ip6_mld_report(netif, &addr->addr);
if (ret < 0)
return ret;
num_reports++;
}
}
return num_reports;
}
/*
*===========================================================================
* iplite_ip6_mld_report
*===========================================================================
* Description: Sends a multicast listener report message.
* Parameters: netif - The interface that joined the multicast group.
* mlt_addr - The multicast address that was joined.
* Returns: >=0 = success , <0 = error code.
*
*/
IP_STATIC int
iplite_ip6_mld_report(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *mlt_addr)
{
Iplite_icmp6_param icmp6_param;
if (iplite_ip6_mld_send_message(mlt_addr) == IP_FALSE)
return 0;
ipcom_memset(&icmp6_param, 0, sizeof(icmp6_param));
icmp6_param.type = IPLITE_ICMP6_TYPE_MLD_REPORT;
icmp6_param.to = mlt_addr;
icmp6_param.ifindex = netif->ipcom.ifindex;
icmp6_param.data.mld.target_addr = mlt_addr;
icmp6_param.data.mld.netif = netif;
return iplite_icmp6_send(&icmp6_param);
}
/*
*===========================================================================
* iplite_ip6_mld_done
*===========================================================================
* Description: Sends a multicast listener done message.
* Parameters: netif - The interface that joined the multicast group.
* mlt_addr - The multicast address that was leaved.
* Returns: >=0 = success , <0 = error code.
*
*/
IP_STATIC int
iplite_ip6_mld_done(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *mlt_addr)
{
Iplite_icmp6_param icmp6_param;
struct Ip_in6_addr all_routers_addr;
if (iplite_ip6_mld_send_message(mlt_addr) == IP_FALSE)
return 0;
ipcom_memset(&all_routers_addr, 0, sizeof(all_routers_addr));
all_routers_addr.in6.addr16[0] = ip_htons(0xFF02);
all_routers_addr.in6.addr16[7] = ip_htons(0x0002);
ipcom_memset(&icmp6_param, 0, sizeof(icmp6_param));
icmp6_param.type = IPLITE_ICMP6_TYPE_MLD_DONE;
icmp6_param.to = &all_routers_addr;
icmp6_param.ifindex = netif->ipcom.ifindex;
icmp6_param.data.mld.target_addr = mlt_addr;
icmp6_param.data.mld.netif = netif;
return iplite_icmp6_send(&icmp6_param);
}
/*
*===========================================================================
* iplite_ip6_add_header
*===========================================================================
* Description: Adds an IPv6 header.
* Parameters: param - Parameters to add to the IPv6 header.
* pkt - The packet.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC void
iplite_ip6_add_header(Iplite_ip6_param *param, Ipcom_pkt *pkt)
{
Iplite_pkt_ip6 *ip6_hdr;
Ip_u32 ver_class_flow;
pkt->start -= IPLITE_IP6_HDR_SIZE;
pkt->ipstart = pkt->start;
/* Add the IPv6 header */
ip6_hdr = (Iplite_pkt_ip6 *)&pkt->data[pkt->ipstart];
ver_class_flow = ip_htonl(0x60000000) | param->flowinfo_n;
IP_SET_32ON16(&ip6_hdr->ver_class_flow, ver_class_flow);
/* plen is the size of the IPv6 payload (excluding the size of the IPv6 header) */
ip6_hdr->plen = (Ip_u16) ip_htons(pkt->end - pkt->start - IPLITE_IP6_HDR_SIZE);
ip6_hdr->nxt = param->nxt_hdr;
ip6_hdr->hlim = param->hlim;
IPLITE_IP6_SET_ADDR(&ip6_hdr->src, param->from);
IPLITE_IP6_SET_ADDR(&ip6_hdr->dst, param->to);
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR);
}
/*
*===========================================================================
* iplite_ip6_get_addr_scope
*===========================================================================
* Description: Returns the scope of the passed address.
* Parameters: addr - An IPv6 address
* Returns: One of the IPLITE_IP6_SCOPE_xxx constants.
*
*/
IP_STATIC int
iplite_ip6_get_addr_scope(IP_CONST struct Ip_in6_addr *addr)
{
if (IP_IN6_IS_ADDR_MULTICAST(addr))
{
int multicast_scope = IPLITE_IP6_MC_SCOPE(addr);
switch (multicast_scope)
{
case IPLITE_IP6_SCOPE_NODE_LOCAL:
case IPLITE_IP6_SCOPE_LINK_LOCAL:
case IPLITE_IP6_SCOPE_SITE_LOCAL:
case IPLITE_IP6_SCOPE_GLOBAL:
case IPLITE_IP6_SCOPE_ORG_LOCAL:
return multicast_scope;
default:
return IPLITE_IP6_SCOPE_INVALID;
}
}
/* else: unicast address */
if (IP_IN6_IS_ADDR_LOOPBACK(addr))
return IPLITE_IP6_SCOPE_NODE_LOCAL;
if (IP_IN6_IS_ADDR_LINK_LOCAL(addr))
return IPLITE_IP6_SCOPE_LINK_LOCAL;
if (IP_IN6_IS_ADDR_SITE_LOCAL(addr))
return IPLITE_IP6_SCOPE_SITE_LOCAL;
return IPLITE_IP6_SCOPE_GLOBAL;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* iplite_ip6_add_addr
*===========================================================================
* Description: Adds an IPv6 address to the interface.
* Parameters: addr - The address to add.
* flags - IPLITE_IP6_ADDR_FLAG_xxx style flags.
* preferred - Number of seconds this address should be preferred.
* Set to IPCOM_ADDR_INFINITE for inifinity.
* valid - Number of seconds this address should be valid.
* Set to IPCOM_ADDR_INFINITE for inifinity.
* Returns: 0 = success, <0 = error code.
*
*/
IP_GLOBAL int
iplite_ip6_add_addr(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *addr,
IP_CONST int prefix,
int flags, Ip_u32 preferred, Ip_u32 valid)
{
Iplite_ip6_addr_entry *addr_entry;
int ret = 0;
/* Check if the address already is assigned to the interface */
for (addr_entry = netif->inet6_addr_list; addr_entry != IP_NULL; addr_entry = addr_entry->next)
if (IP_IN6_ARE_ADDR_EQUAL(&addr_entry->addr, addr))
{
if (IP_IN6_IS_ADDR_MULTICAST(addr))
/* Reference count multicast addresses */
addr_entry->ref_count++;
/* Update lifetimes. */
addr_entry->preferred_sec =
(preferred == IPCOM_ADDR_INFINITE ? IPCOM_ADDR_INFINITE : preferred + iplite.seconds);
addr_entry->expires_sec =
(valid == IPCOM_ADDR_INFINITE ? IPCOM_ADDR_INFINITE : valid + iplite.seconds);
return 0;
}
addr_entry = iplite_kmalloc(sizeof(Iplite_ip6_addr_entry));
if (addr_entry == IP_NULL)
return -IP_ERRNO_ENOMEM;
addr_entry->ref_count = 1;
addr_entry->flags = (Ip_u16)flags;
if (IP_IN6_IS_ADDR_MULTICAST(addr))
{
/* join the multicast link address for this address */
ret = netif->link_ioctl(netif, IP_SIOCXADDMULTI_IN6, (void *)addr);
IP_BIT_CLR(addr_entry->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE);
if (ret >= 0)
ret = iplite_ip6_mld_report(netif, addr);
}
else if (!IP_IN6_IS_ADDR_LOOPBACK(addr))
{
struct Ip_in6_addr sol_addr;
/* join the solicited-node multicast link address for this address */
IPLITE_IP6_SET_SOLICITED_ADDR(&sol_addr, addr);
ret = netif->link_ioctl(netif, IP_SIOCXADDMULTI_IN6, &sol_addr);
}
if (ret < 0)
return ret;
if (prefix == -1)
ipcom_memset(addr_entry->prefix, -1, sizeof(addr_entry->prefix));
else
iplite_create_mask(addr_entry->prefix, prefix);
IPLITE_IP6_SET_ADDR(&addr_entry->addr, addr);
/* Address that are explicitly added are permanent */
addr_entry->preferred_sec =
(preferred == IPCOM_ADDR_INFINITE ? IPCOM_ADDR_INFINITE : preferred + iplite.seconds);
addr_entry->expires_sec =
(valid == IPCOM_ADDR_INFINITE ? IPCOM_ADDR_INFINITE : valid + iplite.seconds);
addr_entry->next = netif->inet6_addr_list;
netif->inet6_addr_list = addr_entry;
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_UP))
return iplite_ip6_assign_addr(netif, addr_entry);
/* else: DAD and adding routes will be done when the interface is set UP */
return 0;
}
/*
*===========================================================================
* iplite_ip6_remove_addr
*===========================================================================
* Description: Removes an IPv6 address from a network interface.
* Parameters: netif - Network interface to which the address is assigned.
* addr - The address to remove.
* Returns: >=0 = success (returned value is the ref. count), <0 = error code.
*
*/
IP_GLOBAL int
iplite_ip6_remove_addr(Iplite_netif *netif, struct Ip_in6_addr *in6_addr)
{
Iplite_ip6_addr_entry **addr_it;
Iplite_ip6_addr_entry *del_addr;
int ret;
del_addr = IP_NULL;
ret = 0;
addr_it = &netif->inet6_addr_list;
while (*addr_it != IP_NULL)
{
if (IP_IN6_ARE_ADDR_EQUAL(in6_addr, &(*addr_it)->addr))
{
del_addr = *addr_it;
if (--del_addr->ref_count > 0)
return del_addr->ref_count;
/* Remove the address from the list of assigned addresses */
*addr_it = del_addr->next;
if (IP_IN6_IS_ADDR_MULTICAST(in6_addr))
ret = iplite_ip6_mld_done(netif, in6_addr);
break;
}
addr_it = &(*addr_it)->next;
}
if (del_addr == IP_NULL)
/* Address not found */
return -IP_ERRNO_EADDRNOTAVAIL;
iplite_kioevent(netif, IP_EIOXDELADDR, IP_NULL);
if (IP_IN6_IS_ADDR_MULTICAST(in6_addr))
{
/* leave the multicast link address for this address */
ret = netif->link_ioctl(netif, IP_SIOCXDELMULTI_IN6, in6_addr);
}
else if (!IP_IN6_IS_ADDR_LOOPBACK(in6_addr))
{
/* leave the solicitated node multicast group */
struct Ip_in6_addr tentative_sol_addr;
IPLITE_IP6_SET_SOLICITED_ADDR(&tentative_sol_addr, in6_addr);
ret = netif->link_ioctl(netif, IP_SIOCXDELMULTI_IN6, &tentative_sol_addr);
}
IPCOM_LOG2(INFO, "IPv6: removed %s from %s",
ipcom_inet_ntop(IP_AF_INET6, in6_addr, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
iplite_kfree(del_addr);
return ret;
}
/*
*===========================================================================
* iplite_ip6_create_mapped_addr
*===========================================================================
* Description: Creates a IPv6 mapped IPv4 address.
* Parameters: ipv4_addr - An IPv4 address.
* ipv4_mapped_ipv6_addr - The resulting IPv6 address.
* Returns:
*
*/
IP_GLOBAL void
iplite_ip6_create_mapped_addr(struct Ip_in_addr *ipv4_addr,
struct Ip_in6_addr *ipv4_mapped_ipv6_addr)
{
ipcom_memset(&ipv4_mapped_ipv6_addr->in6.addr16[0], 0, 10);
ipcom_memset(&ipv4_mapped_ipv6_addr->in6.addr16[5], 0xff, 2);
ipcom_memcpy(&ipv4_mapped_ipv6_addr->in6.addr16[6], ipv4_addr, 4);
}
/*
*===========================================================================
* iplite_ip6_neighbor_cache_find
*===========================================================================
* Description: Returns the neighbor cache entry matching the address. A new
* entry is create if the request neigbor entry do not extist.
* Parameters: netif - The link to the neighbor is located on.
* addr - The address of the neighbor to find.
* Returns: The neighbor cache entry or IP_NULL if out of memory or if no
* ND information exist for that address.
*
*/
IP_GLOBAL Iplite_ip6_nc_entry *
iplite_ip6_neighbor_cache_find(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *in6_addr)
{
int i;
Iplite_ip6_nc_entry *nc;
Iplite_netif *this_netif = IP_NULL;
Ip_u8 is_gw;
for (i = 1; this_netif == IP_NULL && i < IPLITE_CONF_MAX_NETIF; i++)
{
this_netif = (netif == IP_NULL) ? iplite.netifs[i] : netif;
if (this_netif == IP_NULL)
continue;
for (nc = this_netif->nc_list; nc != IP_NULL; nc = nc->next)
{
if (nc->status == IPLITE_IP6_ND_UNINITIALIZED)
continue;
if (IP_IN6_ARE_ADDR_EQUAL(in6_addr, &nc->addr))
return nc;
}
this_netif = netif;
}
if (iplite_ip6_get_netif(netif->ipcom.ifindex, in6_addr, &is_gw, IP_TRUE) != netif
|| is_gw)
/* The IPv6 address is not directly reachable through the
specified interface, no neighbor must be created for this
entry. */
return IP_NULL;
/* If we are here it means that no neighbor cache entry was found,
* allocate one and initialize it...
*/
nc = iplite_kmalloc(sizeof (*nc));
if (nc == IP_NULL)
return IP_NULL;
iplite_ip6_neighbor_cache_init(nc);
IPLITE_IP6_SET_ADDR(&nc->addr, in6_addr);
if (netif != IP_NULL)
{
nc->netif = netif;
nc->next = netif->nc_list;
netif->nc_list = nc;
if (netif->ipcom.ifindex == IPLITE_IF_LOOPBACK_INDEX)
{
nc->status = IPLITE_IP6_ND_REACHABLE;
nc->next_timeout = IPCOM_ADDR_INFINITE;
}
}
return nc;
}
/*
*===========================================================================
* iplite_ip6_neighbor_cache_update
*===========================================================================
* Description: Update an existing neigbor cache entry with data just
* received.
* Parameters: neighbor - The neighbor cache entry to update.
* status - The new status.
* link_addr_size - The size, in bytes, of the 'link_addr' buffer.
* link_addr - Pointer to the link address.
* Returns:
*
*/
IP_GLOBAL void
iplite_ip6_neighbor_cache_update(Iplite_ip6_nc_entry *neighbor, int status,
IP_CONST void *link_addr)
{
Iplite_netif *netif;
ip_assert(neighbor != IP_NULL);
switch (status)
{
case IPLITE_IP6_ND_REACHABLE:
/* Status is changed to stale when the 'expires' time is reached */
neighbor->next_timeout = iplite.seconds + IPLITE_CONF_INET6_BASE_REACHABLE_TIME;
neighbor->unanswered_probes = 0;
break;
case IPLITE_IP6_ND_DELAY:
/* Status is changed to probe and a unicast Neighbor Solicitation Message is forced */
neighbor->next_timeout = iplite.seconds + IPLITE_CONF_INET6_DELAY_FIRST_PROBE_TIME;
break;
case IPLITE_IP6_ND_INCOMPLETE:
case IPLITE_IP6_ND_PROBE:
/* Time when the next message is sent unless status changes to reachable */
neighbor->next_timeout = iplite.seconds + IPLITE_CONF_INET6_BASE_RETRANSMIT_TIME;
if (neighbor->pending_pkt != IP_NULL)
{
ipcom_pkt_free(neighbor->pending_pkt);
neighbor->pending_pkt = IP_NULL;
}
break;
case IPLITE_IP6_ND_STALE:
/* Remove stale (unused) entries after this time minutes */
neighbor->next_timeout = iplite.seconds + IPLITE_CONF_INET6_NEIGHBOR_CACHE_TO_LIVE;
break;
default:
/* Invalid status */
IP_PANIC();
return;
}
neighbor->status = (Ip_u16)status;
if (link_addr != IP_NULL)
{
ipcom_memcpy(neighbor->link_addr, link_addr, sizeof (neighbor->link_addr)); /*lint -e420 */ /*lint -e670 */
}
if (status == IPLITE_IP6_ND_REACHABLE && neighbor->pending_pkt != IP_NULL)
{
netif = neighbor->netif;
(void)netif->link_ip6_output(netif, neighbor, neighbor->pending_pkt);
neighbor->pending_pkt = IP_NULL;
}
}
/*
*===========================================================================
* iplite_ip6_is_scoped_addr
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_bool
iplite_ip6_is_scoped_addr(IP_CONST struct Ip_in6_addr *in6_addr)
{
return IPLITE_IP6_IS_SCOPE_LINK_LOCAL(in6_addr);
}
/*
*===========================================================================
* iplite_ip6_get_src_addr
*===========================================================================
* Description: Returns the address that is most resonable to use by default
* for a route. (ex. if the destination is a link local IPv6,
* then use the link local address of the interface).
* The actual address used can be changed by ipcom_bind().
* Parameters: netif - The interface that will use the address.
* dst_addr - The destination address
* Returns: A pointer to the matching address or IP_NULL if no address
* match.
*
*/
IP_GLOBAL IP_CONST struct Ip_in6_addr *
iplite_ip6_get_src_addr(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *dst_addr, int scope)
{
Iplite_ip6_addr_entry *addr;
IP_CONST struct Ip_in6_addr *best_addr = IP_NULL;
int src_scope;
int dst_scope;
if (scope)
dst_scope = scope;
else
{
dst_scope = iplite_ip6_get_addr_scope(dst_addr);
if (dst_scope == IPLITE_IP6_SCOPE_INVALID)
return IP_NULL;
}
find_addr:
if (netif->ipcom.ifindex == IPLITE_IF_LOOPBACK_INDEX &&
iplite_ip6_get_addr_type(IP_NULL, dst_addr) == IPLITE_ADDR_TYPE_UNICAST)
/* Destination was one of the local addresses. */
return dst_addr;
for (src_scope = dst_scope; src_scope <= IPLITE_IP6_SCOPE_GLOBAL; src_scope++)
{
int matched_16bits = -1;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
{
if (src_scope == iplite_ip6_get_addr_scope(&addr->addr)
&& IP_BIT_ISFALSE(addr->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE))
{
int b;
for (b = 0; b < (int) (sizeof(struct Ip_in6_addr) / sizeof(Ip_u16)); b++)
if (dst_addr->in6.addr16[b] != addr->addr.in6.addr16[b])
break;
if (matched_16bits < b)
{
if ((b << 4) >= ipcom_mask_to_prefixlen(addr->prefix, 128))
/* All of the prefix matched, use this address */
return &addr->addr;
/* Use the address with the longest match */
matched_16bits = b;
best_addr = &addr->addr;
}
}
}
if (best_addr != IP_NULL || scope != 0)
return best_addr;
}
if (IP_IN6_IS_ADDR_MULTICAST(dst_addr))
{
if (dst_scope > IPLITE_IP6_SCOPE_LINK_LOCAL)
{
dst_scope--;
goto find_addr;
}
}
/* No address matching the criteria */
return IP_NULL;
}
/*
*===========================================================================
* iplite_ip6_tick
*===========================================================================
* Description: Performes all timer dependent tasks for IPv6.
* Parameters: ticks - Number of ticks passed since last time this function
* was called.
* Returns:
*
*/
IP_GLOBAL void
iplite_ip6_tick(void)
{
Iplite_ip6_nc_entry *nc;
Iplite_ip6_nc_entry *next_nc;
Iplite_ip6_nc_entry *prev_nc;
Iplite_netif *netif;
Iplite_ip6_addr_entry *tentative_addr_it;
int i;
if (iplite.ip6.last_sec != iplite.seconds)
{
/****** Removes addresses that has passed their expired timestamp. ******/
if (iplite.seconds % 61 == 0)
{
Iplite_ip6_addr_entry *addr;
for (i = 1; i < IPLITE_CONF_MAX_NETIF; i++)
{
netif = iplite.netifs[i];
if (netif == IP_NULL)
continue;
addr = netif->inet6_addr_list;
while (addr != IP_NULL)
{
if (addr->expires_sec != IPCOM_ADDR_INFINITE
&& IPLITE_IS_GEQ(iplite.seconds, addr->expires_sec))
{
/* Address has expired, remove it */
(void)iplite_ip6_remove_addr(netif, &addr->addr);
addr = netif->inet6_addr_list;
}
else
addr = addr->next;
}
}
}
/****** Remove default router entries that have passed their expired timestamp. ******/
if (iplite.seconds % 23 == 0)
{
Iplite_ip6_dr_entry *dr;
for (i = 2; i < IPLITE_CONF_MAX_NETIF; i++)
{
if (iplite.netifs[i] == IP_NULL)
continue;
dr = &iplite.netifs[i]->dr;
if (dr->is_set == IP_FALSE)
continue;
if (dr->expires_sec != IPCOM_ADDR_INFINITE
&& IPLITE_IS_GEQ(iplite.seconds, dr->expires_sec))
{
dr->is_set = IP_FALSE;
}
}
}
/****** Neighbour cache timeout ******/
for (i = 2; i < IPLITE_CONF_MAX_NETIF; i++)
{
netif = iplite.netifs[i];
if (netif == IP_NULL)
continue;
/* Go through the list of neighbor caches associated with this if */
for (prev_nc = IP_NULL, nc = netif->nc_list; nc != IP_NULL; nc = next_nc)
{
next_nc = nc->next;
/* Resend neighbor solicitation message that we did not get
* reply from and send neighbor solicitation to entries in the
* neighbor cache that has status set to probe and the probe time
* has expired
*/
if (iplite.seconds < nc->next_timeout
|| nc->next_timeout == IPCOM_ADDR_INFINITE)
{
goto next_entry;
}
switch (nc->status)
{
case IPLITE_IP6_ND_INCOMPLETE:
case IPLITE_IP6_ND_PROBE:
nc->unanswered_probes++;
if (nc->unanswered_probes <= IPLITE_CONF_INET6_MAX_UNICAST_SOLICIT)
{
iplite_ip6_neighbor_cache_update(nc, (int)nc->status, IP_NULL);
(void)iplite_ip6_probe(nc);
goto next_entry;
}
break;
case IPLITE_IP6_ND_DELAY:
/* The status was not updated during the delay period, start probing */
iplite_ip6_neighbor_cache_update(nc, IPLITE_IP6_ND_PROBE, IP_NULL);
(void)iplite_ip6_probe(nc);
goto next_entry;
case IPLITE_IP6_ND_REACHABLE:
iplite_ip6_neighbor_cache_update(nc, IPLITE_IP6_ND_STALE, IP_NULL);
goto next_entry;
case IPLITE_IP6_ND_STALE:
/* remote entry below. */
break;
default:
IP_PANIC();
break;
}
/* The entry has expired, remove it */
if (nc->is_router
&& (nc->status == IPLITE_IP6_ND_INCOMPLETE || nc->status == IPLITE_IP6_ND_PROBE))
{
/* This router is unreachable, remove it.
* The router will announce itself when it comes back online.
*/
iplite_ip6_default_router_delete(netif, &nc->addr);
}
if (prev_nc)
prev_nc->next = nc->next;
else
netif->nc_list = nc->next;
iplite_kfree(nc);
continue;
next_entry:
prev_nc = nc;
}
}
iplite.ip6.last_sec = iplite.seconds;
}
/****** Check if any of the tentative addresses can be assigned to their interface ******/
for (i = 2; i < IPLITE_CONF_MAX_NETIF; i++)
{
netif = iplite.netifs[i];
if (netif == IP_NULL)
continue;
if (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_UP))
continue;
tentative_addr_it = netif->inet6_addr_list;
while (tentative_addr_it != IP_NULL)
{
if (IP_BIT_ISSET(tentative_addr_it->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE)
&& IPLITE_IS_GT(iplite.seconds, tentative_addr_it->valid_from))
{
/* The address has not been removed during duplicate address
* detection, assign the address to the interface.
*/
IPCOM_LOG2(INFO, "IPv6: DAD successful for %s on %s",
ipcom_inet_ntop(IP_AF_INET6, &tentative_addr_it->addr, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
IP_BIT_CLR(tentative_addr_it->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE);
if (iplite_ip6_assign_addr(netif, tentative_addr_it) >= 0
&& IP_IN6_IS_ADDR_LINK_LOCAL(&tentative_addr_it->addr))
{
/* The link local address assigned, look for routers on this link,
this step can be skipped, but routers are detected faster if done.
This is not done on links where this node is acting as a router. */
(void) iplite_ip6_send_router_sol_msg(netif);
}
}
tentative_addr_it = tentative_addr_it->next;
}
}
}
/*
*===========================================================================
* iplite_ip6_get_addr_type
*===========================================================================
* Description: Returns type of the specified address.
* Parameters: netif - The network interface the address should be assigned
* to or IP_NULL if any interface might do.
* ipaddr - The address for which the type will be returned.
* Returns: IPLITE_ADDR_TYPE_xxx IP_CONSTant
*/
IP_GLOBAL int
iplite_ip6_get_addr_type(Iplite_netif *netif, IP_CONST struct Ip_in6_addr *in6_addr)
{
Iplite_ip6_addr_entry *addr;
Iplite_netif *this_netif;
int i;
if (IP_IN6_IS_ADDR_UNSPECIFIED(in6_addr))
return IPLITE_ADDR_TYPE_ANY;
for (i = 1; i < IPLITE_CONF_MAX_NETIF; i++)
{
this_netif = (netif != IP_NULL) ? netif : iplite.netifs[i];
if (this_netif == IP_NULL)
continue;
if (IPLITE_IP6_IS_ADDR_SOLICITED(in6_addr))
{
for (addr = this_netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
{
if (IP_BIT_ISFALSE(addr->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE)
&& IPLITE_IP6_ADDR_SOLICITED_NODE_CMP(in6_addr, &addr->addr))
return IPLITE_ADDR_TYPE_SOLICITED;
}
}
else
{
/* Multicast or unicast address */
for (addr = this_netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
{
if (IP_BIT_ISFALSE(addr->flags, IPLITE_IP6_ADDR_FLAG_TENTATIVE)
&& IP_IN6_ARE_ADDR_EQUAL(in6_addr, &addr->addr))
{
if (IP_IN6_IS_ADDR_MULTICAST(in6_addr))
return IPLITE_ADDR_TYPE_MULTICAST;
else
return IPLITE_ADDR_TYPE_UNICAST;
}
}
}
if (netif != IP_NULL)
break;
}
return IPLITE_ADDR_TYPE_NOT_LOCAL;
}
/*
*===========================================================================
* iplite_ip6_input
*===========================================================================
* Description: Handler for IPv6 packets. (pkt->start is offset to IPv6 header).
* Parameters: pkt - Received IPv6 packet.
* Returns:
*
*/
IP_GLOBAL void
iplite_ip6_input(Iplite_netif *netif, Ipcom_pkt *pkt)
{
Iplite_icmp6_param icmp6_param;
Iplite_process_ext_param ext_param;
Iplite_pkt_ip6 *ip6_hdr;
int proto;
int addr_type;
int ret;
IPLITE_STATS(ip6_input++);
IPCOM_MIB2_NETIF(ipv6IfStatsInReceives++);
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_IP6_INPUT);
#ifdef IPIPSEC2
parse_ip_datagram:
#endif
if (pkt->end - pkt->start < IPLITE_IP6_HDR_SIZE)
{
/* Can not be a valid IPv6 packet */
IPLITE_STATS(ip6_input_hdr_trunc++);
IPCOM_MIB2_NETIF(ipv6IfStatsInHdrErrors++);
goto cleanup;
}
pkt->ipstart = pkt->start;
ip6_hdr = (Iplite_pkt_ip6*) &pkt->data[pkt->start];
if (pkt->end < pkt->ipstart + (int)ip_ntohs(ip6_hdr->plen) + IPLITE_IP6_HDR_SIZE)
{
/* Did not receive whole packet */
IPLITE_STATS(ip6_input_inval_len++);
IPCOM_MIB2_NETIF(ipv6IfStatsInHdrErrors++);
goto cleanup;
}
pkt->end = pkt->ipstart + ip_ntohs(ip6_hdr->plen) + IPLITE_IP6_HDR_SIZE;
#ifdef IPCOM_USE_MIB2
if (pkt->end - pkt->ipstart > (int)netif->ipcom.mtu)
{
IPCOM_MIB2_NETIF(ipv6IfStatsInTooBigErrors++);
}
#endif
addr_type = iplite_ip6_get_addr_type(IP_NULL, &ip6_hdr->dst);
if (addr_type == IPLITE_ADDR_TYPE_NOT_LOCAL
|| addr_type == IPLITE_ADDR_TYPE_ANY)
{
/* Not to me */
IPLITE_STATS(ip6_input_not_to_me++);
IPCOM_MIB2_NETIF(ipv6IfStatsInAddrErrors++);
goto cleanup;
}
if(addr_type == IPLITE_ADDR_TYPE_MULTICAST)
{
/*!! flag packet MULTICAST to save code space on all later checks. */
IPCOM_MIB2_NETIF(ipv6IfStatsInMcastPkts++);
}
/* IPSec hooks should be located in iplite_ip6_process_exthdr() */
ext_param.ppkt = &pkt;
proto = iplite_ip6_process_exthdr(&ext_param);
#ifdef IPIPSEC2
if (proto == IP_IPPROTO_IPV6)
goto parse_ip_datagram;
if (proto == IP_IPPROTO_IPIP)
{
#ifdef IPCOM_USE_INET
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_IPV6);
iplite_ip4_input(netif, pkt);
return;
#else
proto = -IP_ERRNO_EINVAL; /* Drop packet, no IPv4 support. */
#endif
}
#endif /* IPIPSEC2 */
ip6_hdr = ext_param.ip6_hdr;
ipcom_memset(&icmp6_param, 0, sizeof(icmp6_param));
icmp6_param.ifindex = netif->ipcom.ifindex;
icmp6_param.recv_pkt = pkt;
icmp6_param.to = &ip6_hdr->src;
if (proto < 0 || (proto == 0 && ext_param.icmp6_type == 0))
{
/* Error (proto < 0) or silent drop this packet (proto == 0...) */
goto cleanup;
}
if (proto == 0)
{
/* ICMP message */
icmp6_param.type = ext_param.icmp6_type;
icmp6_param.code = ext_param.icmp6_code;
if (addr_type == IPLITE_ADDR_TYPE_NOT_LOCAL)
icmp6_param.from = iplite_ip6_get_src_addr(netif, icmp6_param.to, 0);
else
icmp6_param.from = &ip6_hdr->dst;
icmp6_param.data.param_prob_pointer = pkt->start - pkt->ipstart;
(void)iplite_icmp6_send(&icmp6_param);
IPLITE_STATS(ip6_input_inval_exthdr++);
goto cleanup;
}
if (proto == IPLITE_IP6_HDR_FRAGMENT && addr_type != IPLITE_ADDR_TYPE_NOT_LOCAL)
{
/* More fragments needed */
IPCOM_MIB2_NETIF(ipv6IfStatsReasmReqds++);
return;
}
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV6);
#ifdef IPFIREWALL
if (ipfirewall_input_hook(&netif->ipcom, pkt, IP_NULL) == 1)
goto cleanup; /* Blocked. */
#endif
if (addr_type == IPLITE_ADDR_TYPE_NOT_LOCAL)
goto cleanup;
/* Act on the transport protocol: */
switch (proto)
{
case IPLITE_IP6_HDR_ICMP6:
IPCOM_MIB2_NETIF(ipv6IfStatsInDelivers++);
iplite_icmp6_input(netif, &ip6_hdr->src, &ip6_hdr->dst,
IPLITE_IP6_GET_HLIM(ip6_hdr), pkt);
return;
#ifdef IPCOM_USE_TCP
case IPLITE_IP6_HDR_TCP:
#endif /* IPCOM_USE_TCP */
case IPLITE_IP6_HDR_UDP:
/* Calculate the 'pseudo header' part of the checksum */
pkt->chk = iplite_ip6_pseudo_header_checksum_update(
&ip6_hdr->src, &ip6_hdr->dst,
(Ip_u16) proto, (Ip_u16) (pkt->end - pkt->start));
if (proto == IPLITE_IP6_HDR_UDP)
{
IPCOM_MIB2_NETIF(ipv6IfStatsInDelivers++);
ret = iplite_udp_input(pkt, IP_IN6_IS_ADDR_MULTICAST(&ip6_hdr->dst),
&ip6_hdr->src, &ip6_hdr->dst,
(Iplite_socklookup) iplite_ip6_socklookup);
}
#ifdef IPCOM_USE_TCP
else if (!IP_IN6_IS_ADDR_MULTICAST(&ip6_hdr->dst))
{
IPCOM_MIB2_NETIF(ipv6IfStatsInDelivers++);
ret = iplite_tcp_input(pkt,
&ip6_hdr->src,
&ip6_hdr->dst,
(Iplite_socklookup) iplite_ip6_socklookup);
}
else
{
/* TCP segments can never be sent to a multicast address */
IPLITE_STATS(ip6_input_tcp_multicast++);
goto cleanup;
}
#else
else
{
goto cleanup;
}
#endif /* IPCOM_USE_TCP */
if (ret >= 0)
{
/* UDP or TCP input was successful */
return;
}
else if (ret == -IP_ERRNO_ECONNREFUSED)
{
/* No one listen on the destination port, send back a Port Unreachable error */
icmp6_param.type = IPLITE_ICMP6_TYPE_DST_UNREACHABLE;
icmp6_param.code = IPLITE_ICMP6_DST_UNREACH_NOPORT;
(void)iplite_icmp6_send(&icmp6_param);
}
IPLITE_STATS(ip6_input_prot_err++);
goto cleanup;
case IPLITE_IP6_HDR_NO_NEXT:
IPLITE_STATS(ip6_input_hdr_no_next++);
goto cleanup;
default:
IPLITE_STATS(ip6_input_raw++);
/* Unknown application proto, send to raw sockets */
IPLITE_STATS(raw6_input++);
IPCOM_MIB2_NETIF(ipv6IfStatsInUnknownProtos++);
pkt->chk = iplite_ip6_pseudo_header_checksum_update(&ip6_hdr->src, &ip6_hdr->dst,
(Ip_u16) proto, (Ip_u16) (pkt->end - pkt->start));
ret = iplite_raw_input(pkt, IP_FALSE, (Ip_u16) proto, &ip6_hdr->src, 0, &ip6_hdr->dst, 0,
(Iplite_socklookup) iplite_ip6_socklookup);
if (ret == 0)
{
/* No support for this protocol, send Parameter Problem ICMP message */
icmp6_param.type = IPLITE_ICMP6_TYPE_PARAMETER_PROBLEM;
icmp6_param.code = IPLITE_ICMP6_PARAM_PROB_NXT_HDR;
icmp6_param.from = &ip6_hdr->dst;
icmp6_param.data.param_prob_pointer = pkt->start - pkt->ipstart;
(void)iplite_icmp6_send(&icmp6_param);
}
goto cleanup;
}
cleanup:
IPLITE_STATS(ip6_input_err++);
if (pkt != IP_NULL)
ipcom_pkt_free(pkt);
}
/*
*===========================================================================
* iplite_ip6_sendto
*===========================================================================
* Description: Finds out the source and destination address and dispatches
* the packet to iplite_ip6_output(). The transport header
* checksum is also finished (if used).
* Parameters: sock - The socket to use when sending.
* to - The destination address address, ignored if the socket
* is in connected state.
* pkt - The packet data to send.
* Returns: >0 = application bytes sent, <0 = error code.
*
*/
IP_GLOBAL int
iplite_ip6_sendto(Iplite_socket *sock, IP_CONST struct Ip_sockaddr *to, Ipcom_pkt *pkt)
{
Iplite_ip6_param param;
Iplite_netif *netif;
Ip_u32 scope_id;
int ret;
Ip_u16 *chksum;
IP_CONST struct Ip_sockaddr_in6 *to_in6;
#ifdef IP_PORT_VXWORKS
struct Ip_in6_pktinfo *info = IP_NULL;
#endif
ip_assert(sock->ipcom.domain == IP_AF_INET6);
#ifdef IP_PORT_VXWORKS
info = ipcom_pkt_get_info(pkt, IPLITE_PKT_INFO_PKTINFO);
#endif
param.nxt_hdr = (Ip_u8)sock->proto;
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED | IPLITE_SOCKET_FLAG_CONNECTING))
{
if (to != IP_NULL)
{
ret = -IP_ERRNO_EISCONN;
goto errout;
}
/* Connected socket */
scope_id = sock->d->ip6.scope_id;
param.to = &sock->d->ip6.daddr;
param.flowinfo_n = sock->d->ip6.flowinfo_n;
}
else
{
/* Unconnected socket */
if (to == IP_NULL)
{
ret = -IP_ERRNO_EDESTADDRREQ;
goto errout;
}
if (to->sa_family != IP_AF_INET6)
{
ret = -IP_ERRNO_EINVAL;
goto errout;
}
to_in6 = (struct Ip_sockaddr_in6 *) to;
scope_id = to_in6->sin6_scope_id;
param.to = &to_in6->sin6_addr;
param.flowinfo_n = to_in6->sin6_flowinfo;
if (ip_ntohl(param.flowinfo_n) & 0xff000000)
{
ret = -IP_ERRNO_EINVAL;
goto errout;
}
}
if (IP_IN6_ARE_ADDR_EQUAL(param.to, &sock->d->ip6.cached_to) == IP_FALSE)
/* The destination address is not equal to the cached destination */
sock->d->ip6.is_cache_valid = IP_FALSE;
if (sock->d->ip6.is_cache_valid == IP_FALSE)
{
netif = iplite_ip6_get_netif(scope_id, param.to, &sock->d->ip6.cached_is_gw, IP_TRUE);
#ifdef IP_PORT_VXWORKS
if (info != IP_NULL)
netif = iplite_if_indextonetif(info->ipi6_ifindex);
else
#endif
if (sock->bind_to_ifindex != 0)
netif = iplite_if_indextonetif(sock->bind_to_ifindex); /* Bound to a specific device */
if(netif)
{
sock->d->ip6.cached_ifindex = netif->ipcom.ifindex;
IPLITE_IP6_SET_ADDR(&sock->d->ip6.cached_to, param.to);
sock->d->ip6.is_cache_valid = IP_TRUE;
}
}
else
netif = iplite_if_indextonetif(sock->d->ip6.cached_ifindex);
if (netif == IP_NULL)
{
sock->d->ip6.is_cache_valid = IP_FALSE;
ret = -IP_ERRNO_EHOSTUNREACH;
goto errout;
}
param.ifindex = netif->ipcom.ifindex;
param.is_gw = sock->d->ip6.cached_is_gw;
if (IP_IN6_IS_ADDR_MULTICAST(param.to))
{
/* Multicast address. */
if(IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_LOOP_MULTICAST))
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_LOOP_MCAST);
param.hlim = sock->multi_hop_limit;
if (sock->d->ip6.multicast_if != 0
&& iplite_ip6_is_scoped_addr(param.to) == IP_FALSE)
{
/* A specific interface was set with IP_IP_MULTICAST_IF socket option */
netif = iplite_if_indextonetif(sock->d->ip6.multicast_if);
ip_assert2(netif != IP_NULL);
}
}
else
{
param.hlim = sock->uni_hop_limit;
}
#ifdef IP_PORT_VXWORKS
if (info != IP_NULL)
{
param.from = &info->ipi6_addr;
}
else
#endif
if (IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr)
|| IP_IN6_IS_ADDR_MULTICAST(&sock->d->ip6.saddr))
{
param.from = iplite_ip6_get_src_addr(netif, param.to, 0);
if (param.from == IP_NULL)
{
/* No address that can be use as source */
ret = -IP_ERRNO_ENETUNREACH;
goto errout;
}
}
else
param.from = &sock->d->ip6.saddr;
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM))
{
chksum = (Ip_u16 *)&pkt->data[pkt->start + sock->chk_offset];
pkt->chk += iplite_ip6_pseudo_header_checksum_update(param.from, param.to,
param.nxt_hdr, (Ip_u16) (pkt->end - pkt->start));
*chksum = ipcom_in_checksum_finish(pkt->chk);
if (sock->proto == IP_IPPROTO_UDP)
{
if (*chksum == 0)
*chksum = 0xffff;
}
}
pkt->fd = sock->ipcom.fd;
return iplite_ip6_output(netif, &param, pkt);
errout:
ipcom_pkt_free(pkt);
return ret;
}
/*
*===========================================================================
* iplite_ip6_output
*===========================================================================
* Description: Sends a IPv6 packet. This function will make sure that the
* data is split into fragments equal to or smaller than the
* path MTU.
* Parameters: netif - The network interface to use when sending the packet.
* rtab_entry - The routing entry for the destination.
* param - The IPv6 header parameters for this packet.
* pkt - The packet buffer.
* Returns: 0 = success, <0 = error code.
*
*/
IP_GLOBAL int
iplite_ip6_output(Iplite_netif *netif, Iplite_ip6_param *param, Ipcom_pkt *pkt)
{
Iplite_ip6_nc_entry *nc_entry = IP_NULL;
Ipcom_pkt *pkt_next_fragment;
IP_CONST Iplite_ip6_dr_entry *default_router;
Ip_bool send_neighbor_sol_msg;
int ret;
#ifdef IPIPSEC2
Ipipsec_param ipsec_param;
#endif
IPLITE_STATS(ip6_output++);
IPCOM_MIB2_NETIF(ipv6IfStatsOutRequests++);
ip_assert(netif != IP_NULL);
ip_assert(param != IP_NULL);
ip_assert(pkt != IP_NULL);
IPLITE_DEBUG_LINE(ip_assert(IPCOM_GET_PKT_ID(pkt) != IPCOM_PKT_ID_FREEQ));
IPLITE_DEBUG_LINE(ip_assert(IPCOM_GET_PKT_ID(pkt) != IPCOM_PKT_ID_INQ));
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_IP6_OUTPUT);
IPLITE_DEBUG_LINE(iplite_pkt_check(pkt));
if (IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR))
{
if (pkt->start < IPLITE_IP6_HDR_SIZE)
{
IPLITE_STATS(ip6_output_hdr_space++);
ret = -IP_ERRNO_EFAULT;
goto errout;
}
iplite_ip6_add_header(param, pkt);
}
/* Set ifindex for all packets */
pkt->ifindex = netif->ipcom.ifindex;
#ifdef IPFIREWALL
if(ipfirewall_output_hook(&netif->ipcom, pkt, IP_NULL) == 1)
{
/* Packet blocked. */
ret = -IP_ERRNO_EFIREWALL;
goto errout;
}
#endif /* IPFIREWALL */
#ifdef IPIPSEC2
ipcom_memcpy(&ipsec_param.key.src.in6, param->from, sizeof(struct Ip_in6_addr));
ipcom_memcpy(&ipsec_param.key.dst.in6, param->to, sizeof(struct Ip_in6_addr));
ipsec_param.key.proto = param->nxt_hdr;
ipsec_param.key.domain = IP_AF_INET6;
ipsec_param.ttl = param->hlim;
ret = ipipsec_output(&pkt, &ipsec_param);
if(ret < 0)
{
/* EINVAL means silent drop (acquire flow packet drops). */
if(ret == -IP_ERRNO_EINVAL)
ret = 0;
goto errout;
}
/* IPv6 packet tunneled in an IPv4 packet. */
if(ipsec_param.key.domain == IP_AF_INET)
{
#ifdef IPCOM_USE_INET
Iplite_ip4_param ip4_param;
ipcom_memset(&ip4_param, 0, sizeof(Iplite_ip4_param));
netif = iplite_ip4_get_netif(ipsec_param.key.dst.in.s_addr, &ip4_param.is_gw);
if (netif == IP_NULL)
{
IPCOM_LOG1(INFO, "IPSEC: no route for IPv4 tunneled IPv6 packet, dst=%s" ,
ipcom_inet_ntop(IP_AF_INET, &ipsec_param.key.dst.in.s_addr, iplite.str, sizeof(iplite.str)));
goto errout;
}
/* Output IPv4 packet. */
ip4_param.from.s_addr = ipsec_param.key.src.in.s_addr;
ip4_param.to.s_addr = ipsec_param.key.dst.in.s_addr;
ip4_param.proto = ipsec_param.key.proto;
ip4_param.ttl = param->hlim;
return iplite_ip4_output(netif, &ip4_param, pkt);
#else
goto errout;
#endif /* IPCOM_USE_INET */
}
/* Reroute datagram if packet was tunneled. */
ip_assert(ipsec_param.key.domain == IP_AF_INET6);
if(ret == 1)
{
netif = iplite_ip6_get_netif(0, &ipsec_param.key.dst.in6, &param->is_gw, IP_TRUE);
if (netif == IP_NULL)
{
IPCOM_LOG1(INFO, "IPSEC: no route for tunneled IPv6 packet'%s' ",
ipcom_inet_ntop(IP_AF_INET6, &ipsec_param.key.dst.in6, iplite.str, sizeof(iplite.str)));
goto errout;
}
/* Update IPv6 source and destination. */
param->to = (IP_CONST struct Ip_in6_addr *)&ipsec_param.key.dst.in6;
param->from = (IP_CONST struct Ip_in6_addr *)&ipsec_param.key.src.in6;
}
/* Update IPv6 next header. */
param->nxt_hdr = ipsec_param.key.proto;
#endif /* IPIPSEC2 */
if (IP_IN6_IS_ADDR_MULTICAST(param->to))
{
/* Doesn't need any neighbor discovery entry for multicast addresses */
IPCOM_MIB2_NETIF(ipv6IfStatsOutMcastPkts++);
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_MULTICAST);
}
else
{
if (param->is_gw == 0)
nc_entry = iplite_ip6_neighbor_cache_find(netif, param->to);
else
{
if (IP_IN6_IS_ADDR_UNSPECIFIED(&iplite.ip6.default_gateway) == IP_FALSE)
{
nc_entry = iplite_ip6_neighbor_cache_find(netif, &iplite.ip6.default_gateway);
}
else
{
/* Use default router on this link */
default_router = &netif->dr;
if (default_router->is_set == IP_FALSE)
{
/* No default routers on this link */
IPLITE_STATS(ip6_output_noroute++);
ret = -IP_ERRNO_ENETUNREACH;
goto errout;
}
nc_entry = iplite_ip6_neighbor_cache_find(netif, &default_router->addr);
}
}
if (nc_entry == IP_NULL)
{
ret = -IP_ERRNO_ENOMEM;
IPLITE_STATS(ip6_output_nomem++);
goto errout;
}
}
#ifdef IPCOM_USE_MIB2
if(param->nxt_hdr == IP_IPPROTO_ICMPV6
&& IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR)
&& IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_FORWARDED))
{
if(pkt->data[pkt->start+IPLITE_IP6_HDR_SIZE] == IPLITE_ICMP6_TYPE_ECHO_REQUEST)
{
IPCOM_MIB2_NETIF(ipv6IfIcmpOutMsgs++);
IPCOM_MIB2_NETIF(ipv6IfIcmpOutEchos++);
}
}
#endif
/* Check if the packet must be fragmented */
if (pkt->end - pkt->start > (int)netif->ipcom.mtu)
{
/* Check if we have an IP header, if we do we have to
* remove it, and add it after we add the frag header
*/
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR))
{
pkt->start += IPLITE_IP6_HDR_SIZE;
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR);
}
pkt = iplite_fragment_packet(pkt, IPLITE_IP6_HDR_SIZE, IP_IP6F_SIZE, netif->ipcom.mtu);
if (pkt == IP_NULL)
{
/* The original packet is free'd by iplite_fragment_packet() */
ret = -IP_ERRNO_ENOBUFS;
IPLITE_STATS(ip6_output_enobufs++);
IPCOM_MIB2_NETIF(ipv6IfStatsOutFragFails++);
goto errout;
}
IPCOM_MIB2_NETIF(ipv6IfStatsOutFragOKs++);
iplite_ip6_add_fragmentation_hdr(pkt, iplite.ip6.id++, param->nxt_hdr);
param->nxt_hdr = IPLITE_IP6_HDR_FRAGMENT;
#ifdef IP_PORT_INTEGRITY
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_NONBLOCKING))
{
Ipcom_pkt *p;
int pnum = 0;
/* Count number of fragments */
for (p = pkt; p != IP_NULL; p = p->next_fragment)
{
pnum++;
}
/*
* Check that all packets will fit in the send queue. Otherwise,
* issue EWOULDBLOCK before any transmissions are made. This will
* ensure that all fragments eventually can be buffered.
*/
if (pnum > (netif->snd_max - netif->snd_num))
{
if (pnum > netif->snd_max)
{
IPCOM_LOG1(WARNING, "Interface queue too small for datagram, required size is %d,", pnum);
IPCOM_LOG0(WARNING, "please increase IPLITE_CONF_NETIF_OUTQ_MAX in iplite_config.h");
ret = -IP_ERRNO_ENOBUFS;
}
else
{
ret = -IP_ERRNO_EWOULDBLOCK;
}
goto errout;
}
}
#endif /* IP_PORT_INTEGRITY */
}
while (pkt != IP_NULL)
{
/* Add IPv6 header to all packet fragments */
pkt_next_fragment = pkt->next_fragment;
pkt->next_fragment = IP_NULL;
if (IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR))
iplite_ip6_add_header(param, pkt);
/* Clear the IP header flag to avoid problems for loopback, retransmits etc. */
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR);
if (nc_entry != IP_NULL)
{
if (nc_entry->status == IPLITE_IP6_ND_INCOMPLETE)
{
/* The ND is not finished for the peer */
if (nc_entry->pending_pkt != IP_NULL)
{
/* Do not send another neighbor solicitation message
since that was done when the first packet was queued */
ipcom_pkt_free(nc_entry->pending_pkt);
send_neighbor_sol_msg = IP_FALSE;
}
else
send_neighbor_sol_msg = IP_TRUE;
/* The packet will be sent when the answer on the
Neighbor Solicitation Message arrives */
nc_entry->pending_pkt = pkt;
if (nc_entry->netif == IP_NULL)
nc_entry->netif = netif;
if (pkt_next_fragment != IP_NULL)
ipcom_pkt_free(pkt_next_fragment);
if (send_neighbor_sol_msg)
return iplite_ip6_probe(nc_entry);
return 0;
}
else if (nc_entry->status == IPLITE_IP6_ND_STALE)
{
/* Try to refresh the status of the ND entry for the peer */
ret = iplite_ip6_probe(nc_entry);
if (ret < 0)
{
IPLITE_STATS(ip6_output_err_probe++);
goto errout;
}
}
}
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_MULTICAST)
&& IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_LOOP_MCAST)
&& IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_SIMPLEX))
{
/* Loopback the packet if the destination is one of the
address is joined by this node */
if (iplite_ip6_get_addr_type(IP_NULL, param->to) != IPLITE_ADDR_TYPE_NOT_LOCAL)
{
Iplite_netif *loopback;
loopback = iplite.netifs[IPLITE_IF_LOOPBACK_INDEX];
if(loopback != IP_NULL)
{
Ipcom_pkt *clone;
clone = iplite_pkt_clone(pkt);
if (clone != IP_NULL)
(void) loopback->link_ip6_output(loopback, IP_NULL, clone);
}
}
}
ret = netif->link_ip6_output(netif, nc_entry, pkt);
pkt = pkt_next_fragment;
if (ret < 0)
{
/* The packet was released by link_ip6_output() */
IPLITE_STATS(ip6_output_link_err++);
goto errout;
}
}
return 0;
errout:
IPLITE_STATS(ip6_output_err++);
if (pkt != IP_NULL)
ipcom_pkt_free(pkt);
return ret;
}
/*
*===========================================================================
* iplite_ip6_get_mss
*===========================================================================
* Description: Returns the MSS for the connected socket 'sock'.
* Parameters: sock - The TCP socket to calculate MSS for.
* Returns: The MSS.
*
*/
#ifdef IPCOM_USE_TCP
IP_GLOBAL Ip_u32
iplite_ip6_get_mss(Iplite_socket *sock)
{
Ip_u32 mtu = 1280;
Iplite_netif *netif;
netif = iplite_ip6_get_netif(sock->d->ip6.scope_id, &sock->d->ip6.daddr, IP_NULL, IP_TRUE);
if (netif)
mtu = netif->ipcom.mtu;
#ifdef IPIPSEC2
{
Ipipsec_param ipsec_param;
IPLITE_IP6_SET_ADDR(&ipsec_param.key.src, &sock->d->ip6.saddr);
IPLITE_IP6_SET_ADDR(&ipsec_param.key.dst, &sock->d->ip6.daddr);
ipsec_param.key.proto = sock->proto;
ipsec_param.key.domain = IP_AF_INET6;
ipsec_param.key.ports.udptcp_srcdst_n[0] = ip_htons(sock->sport);
ipsec_param.key.ports.udptcp_srcdst_n[1] = ip_htons(sock->dport);
ipsec_param.key.srcscope_n = sock->d->ip6.scope_id;
ipsec_param.key.dstscope_n = sock->d->ip6.scope_id;
mtu -= ipipsec_output_hdrspace(&ipsec_param);
}
#endif /* IPIPSEC2 */
return mtu - sock->max_hdrspace;
}
#endif /* IPCOM_USE_TCP */
/*
*===========================================================================
* iplite_ip6_get_netif
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Iplite_netif *
iplite_ip6_get_netif(Ip_u32 scope_id, IP_CONST struct Ip_in6_addr *to, Ip_u8 *is_gw, Ip_bool top)
{
int i;
int a;
Iplite_netif *netif;
Iplite_netif *netif_with_gateway = IP_NULL;
Iplite_ip6_addr_entry *addr;
if (top && is_gw != IP_NULL)
*is_gw = 0;
if (to->in6.addr8[0] == 0xff && to->in6.addr8[1] == 0x02)
/* Link local multicast address. */
return iplite.netifs[scope_id];
/* Check if we have a perfect match on any interface -> use loopback. */
for (i = 1; i < IPLITE_CONF_MAX_NETIF; i++)
{
netif = iplite.netifs[i];
if (netif == IP_NULL)
continue;
if (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_UP))
continue;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
if (IP_IN6_ARE_ADDR_EQUAL(to, &addr->addr))
return iplite.netifs[IPLITE_IF_LOOPBACK_INDEX];
}
if (to->in6.addr8[0] == 0xfe && to->in6.addr8[1] == 0x80)
/* Link local address that is not assigned to one of our interfaces */
return iplite.netifs[scope_id];
for (i = 1; i < IPLITE_CONF_MAX_NETIF; i++)
{
/* Check if we are using a global address on our own network */
netif = iplite.netifs[i];
if (netif == IP_NULL)
continue;
if (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_UP))
continue;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
{
for (a = 0; a < 16; a++)
{
if ((addr->addr.in6.addr8[a] & addr->prefix[a])
!= (to->in6.addr8[a] & addr->prefix[a]))
{
break;
}
}
if (a == 16
&& (!IP_IN6_IS_ADDR_LINK_LOCAL(to) || scope_id == (Ip_u32)i))
return netif; /* prefix match. */
}
if (netif_with_gateway == IP_NULL && netif->dr.is_set)
netif_with_gateway = netif;
}
if (top == IP_TRUE
&& IP_IN6_IS_ADDR_UNSPECIFIED(&iplite.ip6.default_gateway) == IP_FALSE)
{
/* let global gateway override any interface gateways. */
netif = iplite_ip6_get_netif(iplite.ip6.default_scope_id, &iplite.ip6.default_gateway, is_gw, IP_FALSE);
if(netif != IP_NULL)
netif_with_gateway = netif;
}
if (top && is_gw != IP_NULL)
*is_gw = 1;
return netif_with_gateway;
}
/*
*===========================================================================
* iplite_ip6_kioevent
*===========================================================================
* Description: IPv6 handler for IO events on network interfaces.
* Parameters: netif - The network interface the event happened on.
* command - The event.
* Returns:
*
*/
IP_GLOBAL void
iplite_ip6_kioevent(Iplite_netif *netif, int command)
{
Iplite_ip6_addr_entry *addr;
struct Ip_in6_addr addr6 = IP_IN6ADDR_ANY_INIT;
struct Ip_in6_addr brdaddr6 = IP_IN6ADDR_ANY_INIT;
int fd, ret;
brdaddr6.in6.addr16[0] = ip_htons(0xff02);
brdaddr6.in6.addr8[15] = 1;
if (netif->link_ip6_create_addr != IP_NULL)
{
addr6.in6.addr16[0] = ip_htons(0xfe80);
netif->link_ip6_create_addr(netif, &addr6, &addr6);
}
switch (command)
{
case IP_EIOXUP:
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
(void)iplite_ip6_assign_addr(netif, addr);
break;
case IP_EIOXDOWN:
for (fd = IPLITE_SOCKET_STARTFD; fd < IPLITE_CONF_MAX_SOCKETS; fd++)
if (iplite.sockets[fd])
(void)iplite_sock_drop_all_multicast_memberships(iplite.sockets[fd],
netif,
IP_FALSE,
IP_TRUE);
break;
case IP_EIOXRUNNING:
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_MULTICAST))
{
/* Add link local broadcast address (FF02::1) */
ret = iplite_ip6_add_addr(netif, &brdaddr6, 16, 0,
IPCOM_ADDR_INFINITE, IPCOM_ADDR_INFINITE);
if (ret < 0)
{
IPCOM_LOG2(WARNING, "IPv6: failed to add link local broadcast address %s on %s",
ipcom_inet_ntop(IP_AF_INET6, &brdaddr6, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
IP_PANIC();
}
}
if (netif->link_ip6_create_addr != IP_NULL)
{
/* Add link-local address to the interface */
ret = iplite_ip6_add_addr(netif, &addr6, 64,
IPLITE_IP6_ADDR_FLAG_TENTATIVE | IPLITE_IP6_ADDR_FLAG_AUTO,
IPCOM_ADDR_INFINITE, IPCOM_ADDR_INFINITE);
if (ret < 0)
{
IPCOM_LOG2(WARNING, "IPv6: failed to add link local address %s on %s",
ipcom_inet_ntop(IP_AF_INET6, &addr6, iplite.str, sizeof(iplite.str)), netif->ipcom.name);
IP_PANIC();
}
}
break;
case IP_EIOXSTOP:
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_MULTICAST))
(void)iplite_ip6_remove_addr(netif, &brdaddr6);
if (netif->link_ip6_create_addr != IP_NULL)
(void)iplite_ip6_remove_addr(netif, &addr6);
break;
default:
break;
}
}
/*
*===========================================================================
* iplite_ip6_addr_to_netif
*===========================================================================
* Description: Returns the interface that has the specified address.
* Parameters: addr - The address the interface must have.
* Returns: Pointer to the interface structure or IP_NULL if no interface
* has the specified address.
*
*/
#ifdef IPCOM_USE_MIB2
IP_GLOBAL Iplite_netif *
iplite_ip6_addr_to_netif(struct Ip_in6_addr *in6_addr)
{
int i;
Iplite_netif *netif;
Iplite_ip6_addr_entry *addr;
for (i = 1; i < IPLITE_CONF_MAX_NETIF; i++)
{
netif = iplite.netifs[i];
if (netif == IP_NULL)
continue;
for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next)
if(IP_IN6_ARE_ADDR_EQUAL(&addr->addr, in6_addr))
return iplite.netifs[i];
}
return IP_NULL;
}
#endif /* #ifdef IPCOM_USE_MIB2 */
#endif /* IPCOM_USE_INET6 */
#endif /* #if !defined(IPLITE_ONE_FILE) || defined(IPLITE_C) */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/