3692 lines
126 KiB
C
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(¶m, 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, ¶m.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, ¶m, 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(¶m->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, ¶m->ip6_hdr->src, sizeof(struct Ip_in6_addr));
|
|
ipcom_memcpy(&ipsec_param.key.dst.in6, ¶m->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, ¶m, 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, ¶m->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
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
|