1912 lines
64 KiB
C
1912 lines
64 KiB
C
|
|
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmip6_pkt.c,v $ $Revision: 1.52.10.1 $
|
|
* $Source: /home/interpeak/CVSRoot/ipmip6/src/ipmip6_pkt.c,v $
|
|
* $Author: jhorteli $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Joel Hortelius <joel.hortelis@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
#include "ipmip6_config.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock6.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_list.h>
|
|
#include <ipcom_syslog.h>
|
|
|
|
#include <ipmip.h>
|
|
#include <private/ipmip_util.h>
|
|
#include <private/ipmip_dhcp.h>
|
|
#include <private/ipmip_dhcp6.h>
|
|
|
|
#include "ipmip6_h.h"
|
|
#include <private/ipmip6_context.h>
|
|
#include <private/ipmip6_if.h>
|
|
#include <private/ipmip6_mh.h>
|
|
#include <private/ipmip6_mh_pkt.h>
|
|
#include <private/ipmip6_icmp_pkt.h>
|
|
#include <private/ipmip6_nd_proxy.h>
|
|
#include <private/ipmip6_nd_nud.h>
|
|
#include <private/ipmip6_pkt.h>
|
|
#include <private/ipmip6_ipsec.h>
|
|
#include <private/ipmip6_util.h>
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
static Ipmip6_pkt_tlv_option_t ipmip6_mh_dstopt_options[] =
|
|
{
|
|
{ IPMIP6_DSTOPT_HOME_ADDRESS, 8, 6 },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
IP_PUBLIC IP_CONST Ipmip6_address_t ipmip6_in6addr_linklocal =
|
|
{
|
|
IPMIP6_IN6ADDR_LINKLOCAL,
|
|
64
|
|
};
|
|
|
|
IP_PUBLIC IP_CONST Ipmip6_address_t ipmip6_in6addr_sitelocal =
|
|
{
|
|
IPMIP6_IN6ADDR_SITELOCAL,
|
|
64
|
|
};
|
|
|
|
IP_PUBLIC IP_CONST Ipmip6_address_t ipmip6_in6addr_unspecified =
|
|
{
|
|
IP_IN6ADDR_ANY_INIT,
|
|
128
|
|
};
|
|
|
|
IP_PUBLIC IP_CONST struct Ip_in6_addr ipmip6_in6addr_allmask = IPMIP6_IN6ADDR_ALLSET;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_ancillary
|
|
*===========================================================================
|
|
* Description: Compiles (builds) a packet based on its headers and options.
|
|
* The result is stored in the pkt structure (compile) and will
|
|
* be freed whenever imip6_pkt_free is called, or if
|
|
* ipmip6_pkt_compile is called again.
|
|
* Parameters: pkt - The packet pointer.
|
|
* Returns: IP_TRUE if successful.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmip6_pkt_ancillary(Ipmip6_pktbuf_t *pkt)
|
|
{
|
|
struct Ip_cmsghdr *cmsg = IP_NULL;
|
|
Ip_size_t size;
|
|
|
|
ipmip_buffer_release(&pkt->ancillary);
|
|
|
|
/**/
|
|
if (pkt->sendinfo.rthdr0 != IP_NULL)
|
|
{
|
|
Ipmip6_pkt_ext_hdr_t *hdr;
|
|
Ipmip6_pkt_rthdr0_hdr_t *rth;
|
|
|
|
/* Grab the total size required for the type2 header */
|
|
size = sizeof(struct Ip_in6_addr) +
|
|
sizeof(Ipmip6_pkt_rthdr0_hdr_t) +
|
|
sizeof(Ipmip6_pkt_ext_hdr_t);
|
|
size += ((8 - size) & 7);
|
|
|
|
cmsg = ipmip_buffer_get(&pkt->ancillary, IP_CMSG_SPACE(size));
|
|
|
|
/* We have a routing header type 2 */
|
|
hdr = IP_CMSG_DATA(cmsg);
|
|
rth = (Ipmip6_pkt_rthdr0_hdr_t *) (hdr + 1);
|
|
|
|
/* Copy the option */
|
|
ipcom_memcpy((rth + 1), pkt->sendinfo.rthdr0, sizeof(struct Ip_in6_addr));
|
|
|
|
hdr->next_header = IPMIP6_IPPROTO_RTHEADER;
|
|
hdr->len = 2; /* 16 bytes */
|
|
|
|
/* Set base values */
|
|
rth->rtype = 0;
|
|
rth->segments = 1;
|
|
|
|
/* Create the cmsg header */
|
|
cmsg->cmsg_len = IP_CMSG_LEN(size);
|
|
cmsg->cmsg_level = IP_IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IP_IPV6_RTHDR;
|
|
}
|
|
|
|
/* Do we have a routing header type 2? */
|
|
if (pkt->sendinfo.rthdr2 != IP_NULL)
|
|
{
|
|
/* We have a routing header type 2 */
|
|
Ipmip6_pkt_ext_hdr_t *hdr;
|
|
Ipmip6_pkt_rthdr2_hdr_t *rth;
|
|
|
|
/* Grab the total size required for the type2 header */
|
|
size = sizeof(struct Ip_in6_addr) +
|
|
sizeof(Ipmip6_pkt_rthdr2_hdr_t) +
|
|
sizeof(Ipmip6_pkt_ext_hdr_t);
|
|
/* Calculate any trailing padding */
|
|
size += ((8 - size) & 7);
|
|
|
|
cmsg = ipmip_buffer_get(&pkt->ancillary, IP_CMSG_SPACE(size));
|
|
|
|
/* We have a routing header type 2 */
|
|
hdr = IP_CMSG_DATA(cmsg);
|
|
rth = (Ipmip6_pkt_rthdr2_hdr_t *) (hdr + 1);
|
|
|
|
/* Copy the option */
|
|
ipcom_memcpy((rth + 1), pkt->sendinfo.rthdr2, sizeof(struct Ip_in6_addr));
|
|
|
|
hdr->next_header = IPMIP6_IPPROTO_RTHEADER;
|
|
hdr->len = 2; /* 16 bytes */
|
|
|
|
/* Set base values */
|
|
rth->rtype = 2;
|
|
rth->segments = 1;
|
|
|
|
/* Create the cmsg header */
|
|
cmsg->cmsg_len = IP_CMSG_LEN(size);
|
|
cmsg->cmsg_level = IP_IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IP_IPV6_RTHDR;
|
|
}
|
|
|
|
/* Source address in the packet info structure */
|
|
if (pkt->sendinfo.pktinfo != IP_NULL)
|
|
{
|
|
/* We have a source option */
|
|
struct Ip_in6_pktinfo *info;
|
|
|
|
/* Total size of this option */
|
|
size = sizeof(Ipmip6_pkt_ext_hdr_t) +
|
|
sizeof(struct Ip_in6_pktinfo);
|
|
|
|
/* Calculate any trailing padding */
|
|
size += ((8 - size) & 7);
|
|
|
|
cmsg = ipmip_buffer_get(&pkt->ancillary, IP_CMSG_SPACE(size));
|
|
|
|
/* We have a source option */
|
|
info = (struct Ip_in6_pktinfo *) IP_CMSG_DATA(cmsg);
|
|
|
|
/* Copy the data */
|
|
ipcom_memcpy(info, pkt->sendinfo.pktinfo, sizeof(*info));
|
|
|
|
/* Create the cmsg header */
|
|
cmsg->cmsg_len = IP_CMSG_LEN(size);
|
|
cmsg->cmsg_level = IP_IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IP_IPV6_PKTINFO;
|
|
|
|
/* Nexthop, requires that we've specified nexthop */
|
|
if (pkt->sendinfo.nexthop != IP_NULL)
|
|
{
|
|
/* We have a source option */
|
|
struct Ip_sockaddr_in6 *sin6;
|
|
|
|
/* Size of hoplimit field */
|
|
size = sizeof(struct Ip_sockaddr_in6);
|
|
|
|
cmsg = ipmip_buffer_get(&pkt->ancillary, IP_CMSG_SPACE(size));
|
|
|
|
/* We have a source option */
|
|
sin6 = (struct Ip_sockaddr_in6 *)IP_CMSG_DATA(cmsg);
|
|
|
|
/* Copy the address */
|
|
ipcom_memcpy(&sin6->sin6_addr, pkt->sendinfo.nexthop, sizeof(struct Ip_in6_addr));
|
|
sin6->sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(sin6, sizeof(*sin6));
|
|
|
|
/* Must scope it */
|
|
sin6->sin6_scope_id = pkt->sendinfo.pktinfo->ipi6_ifindex;
|
|
|
|
/* Create the cmsg header */
|
|
cmsg->cmsg_len = IP_CMSG_LEN(size);
|
|
cmsg->cmsg_level = IP_IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IP_IPV6_NEXTHOP;
|
|
}
|
|
}
|
|
|
|
/* Hoplimit */
|
|
if (pkt->sendinfo.hoplimit < 256)
|
|
{
|
|
/* We have a source option */
|
|
Ip_u32 *hoplimit;
|
|
|
|
/* Size of hoplimit field */
|
|
size = sizeof(int);
|
|
|
|
cmsg = ipmip_buffer_get(&pkt->ancillary, IP_CMSG_SPACE(size));
|
|
|
|
/* We have a source option */
|
|
hoplimit = (Ip_u32 *) IP_CMSG_DATA(cmsg);
|
|
|
|
/* Copy the data */
|
|
*hoplimit = pkt->sendinfo.hoplimit;
|
|
|
|
/* Create the cmsg header */
|
|
cmsg->cmsg_len = IP_CMSG_LEN(size);
|
|
cmsg->cmsg_level = IP_IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IP_IPV6_HOPLIMIT;
|
|
}
|
|
|
|
/* Destination options */
|
|
if (IPMIP_BUFFER_SIZE(&pkt->dstopts) != 0)
|
|
{
|
|
/* We have a destination option header */
|
|
Ipmip6_pkt_ext_hdr_t *hdr;
|
|
|
|
/* Calculate any trailing padding */
|
|
size = IPMIP_BUFFER_SIZE(&pkt->dstopts) + sizeof(Ipmip6_pkt_ext_hdr_t);
|
|
|
|
size += ipmip6_pkt_pad(&pkt->dstopts, ((8 - size) & 7));
|
|
|
|
/* size = IPMIP_BUFFER_SIZE(&pkt->dstopts) + ((8 - IPMIP_BUFFER_SIZE(&pkt->dstopts)) & 7); */
|
|
|
|
cmsg = ipmip_buffer_get(&pkt->ancillary, IP_CMSG_SPACE(size));
|
|
|
|
/**/
|
|
hdr = IP_CMSG_DATA(cmsg);
|
|
|
|
/* Destination options */
|
|
hdr->next_header = IP_IPPROTO_DSTOPTS;
|
|
/* 8 bytes increment, skipping the first 8 bytes */
|
|
hdr->len = (Ip_u8) ((size >> 3) - 1);
|
|
|
|
/* Initialize the cmsg header */
|
|
cmsg->cmsg_len = IP_CMSG_LEN(size);
|
|
cmsg->cmsg_level = IP_IPPROTO_IPV6;
|
|
cmsg->cmsg_type = pkt->sendinfo.rthdr0? IP_IPV6_RTHDRDSTOPTS : IP_IPV6_DSTOPTS;
|
|
|
|
ipcom_memcpy((hdr + 1), IPMIP_BUFFER_DATA(&pkt->dstopts), IPMIP_BUFFER_SIZE(&pkt->dstopts));
|
|
}
|
|
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pktbuf_alloc
|
|
*===========================================================================
|
|
* Description: Allocate a packet buffer
|
|
* Parameters:
|
|
* Returns: A newly allocated packet buffer
|
|
*
|
|
*/
|
|
IP_STATIC Ipmip_pktbuf_t *
|
|
ipmip6_pktbuf_alloc(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ip_size_t extra)
|
|
{
|
|
Ipmip6_pktbuf_t *pbuf;
|
|
|
|
pbuf = (Ipmip6_pktbuf_t *)(*ctx->pkt->bufalloc)(&ctx->ipmip, proc, extra);
|
|
if (pbuf)
|
|
{
|
|
/* Init v6 parameters */
|
|
pbuf->sendinfo.hoplimit = 0xFFFFFFFF;
|
|
pbuf->sendinfo.flags = 0;
|
|
pbuf->recvinfo.hoplimit = 0xFFFFFFFF;
|
|
ipmip_buffer_init(&pbuf->ancillary);
|
|
ipmip_buffer_init(&pbuf->dstopts);
|
|
}
|
|
|
|
return &pbuf->ipmip;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pktbuf_free
|
|
*===========================================================================
|
|
* Description: Free a packet buffer
|
|
* Parameters: pbuf - The packet buffer
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip6_pktbuf_free(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6_pktbuf_t *pbuf)
|
|
{
|
|
/* Free any routing header type 2 ancillary options */
|
|
if (pbuf->sendinfo.rthdr0 != IP_NULL)
|
|
ipcom_free(pbuf->sendinfo.rthdr0);
|
|
|
|
/* Free any routing header type 2 ancillary options */
|
|
if (pbuf->sendinfo.rthdr2 != IP_NULL)
|
|
ipcom_free(pbuf->sendinfo.rthdr2);
|
|
|
|
/* Free any routing header type 2 ancillary options */
|
|
if (pbuf->sendinfo.nexthop != IP_NULL)
|
|
ipcom_free(pbuf->sendinfo.nexthop);
|
|
|
|
/* Free any specified source address */
|
|
if (pbuf->sendinfo.pktinfo != IP_NULL)
|
|
ipcom_free(pbuf->sendinfo.pktinfo);
|
|
|
|
/* */
|
|
ipmip_buffer_release(&pbuf->ancillary);
|
|
ipmip_buffer_release(&pbuf->dstopts);
|
|
|
|
/* Invoke chained free */
|
|
(*ctx->pkt->buffree) (&ctx->ipmip, proc, &pbuf->ipmip);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_icmp_recv
|
|
*===========================================================================
|
|
* Description: Receive and process an ICMP message.
|
|
* Parameters: context - The context
|
|
* Returns: IP_TRUE if successful.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip6_pkt_icmp_recv(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
void *salink = IP_NULL;
|
|
struct Ip_cmsghdr *cmsghdr;
|
|
int len;
|
|
struct Ip_sockaddr_in6 src;
|
|
struct Ip_msghdr msg;
|
|
struct Ip_iovec vec;
|
|
|
|
/* Set when the option is located */
|
|
Ipmip6_pkt_icmp6_t *icmp;
|
|
Ipmip6_pktbuf_t *pbuf = IP_NULL;
|
|
|
|
/* Allocate pktbuf */
|
|
pbuf = (Ipmip6_pktbuf_t *)ipmip_pktbuf_alloc(&ctx->ipmip, proc, IPMIP6_PKT_VLEN + IPMIP6_PKT_CLEN);
|
|
|
|
/* Failed to allocate buffer? */
|
|
if (pbuf == IP_NULL)
|
|
return;
|
|
|
|
/* Initialize vector */
|
|
vec.iov_base = IPMIP_BUFFER_DATA(&pbuf->ipmip.packet);
|
|
vec.iov_len = IPMIP6_PKT_VLEN;
|
|
|
|
/* Initialize name */
|
|
msg.msg_name = &src;
|
|
msg.msg_namelen = sizeof(src);
|
|
|
|
/* Initialize io */
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
|
|
/* Initialize control */
|
|
msg.msg_control = ipmip_buffer_offset(&pbuf->ipmip.packet, IPMIP6_PKT_VLEN);
|
|
msg.msg_controllen = IPMIP6_PKT_CLEN;
|
|
|
|
/* Clear flags */
|
|
msg.msg_flags = 0;
|
|
|
|
/* Translate to a pointer type we can index */
|
|
icmp = (Ipmip6_pkt_icmp6_t *) IPMIP_BUFFER_PTR(&pbuf->ipmip.packet);
|
|
|
|
/* Read and process a Mobile Header message */
|
|
if ((len = ipcom_recvmsg(ctx->pkt->fd_icmp_in, &msg, 0)) < 0)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"Failed to receive from ICMPv6 socket = %x",
|
|
ipcom_errno));
|
|
ipmip_util_sock_clear(ctx->pkt->fd_icmp_in);
|
|
goto error_out;
|
|
}
|
|
|
|
/* Sanity, must be atleast the mobility header */
|
|
if ((Ip_size_t)len < sizeof(Ipmip6_pkt_icmp6_t))
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 Received packet was too short"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* We've gotten something */
|
|
if ((msg.msg_flags & (IP_MSG_TRUNC | IP_MSG_CTRUNC)) != 0)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 TRUNC or CTRUNC set, not all data read. Discarding"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Store value */
|
|
ipmip_util_dup(&pbuf->ipmip.srcinfo.src, &src, sizeof(src));
|
|
|
|
/* Move past the read length */
|
|
ipmip_buffer_append(&pbuf->ipmip.packet, IP_NULL, len);
|
|
|
|
/* Did we get any ancillary data? */
|
|
if (msg.msg_controllen > 0)
|
|
{
|
|
/* Loop the ancillary data, see what we find */
|
|
for (cmsghdr = IP_CMSG_FIRSTHDR(&msg); cmsghdr != 0;
|
|
cmsghdr = IP_CMSG_NXTHDR(&msg, cmsghdr))
|
|
{
|
|
/* Correct level? */
|
|
if (cmsghdr->cmsg_level == IP_IPPROTO_IPV6)
|
|
{
|
|
/* Type? */
|
|
switch (cmsghdr->cmsg_type)
|
|
{
|
|
case IP_IPV6_X_UDPENCAP:
|
|
pbuf->recvinfo.encapsulation = (union Ip_sockaddr_union *)IP_CMSG_DATA(cmsghdr);
|
|
break;
|
|
|
|
case IP_IPV6_DSTOPTS:
|
|
/* Got some destination options */
|
|
{
|
|
Ip_socklen_t olen;
|
|
void* optr;
|
|
|
|
/* Locate the desired option */
|
|
if (ipcom_inet6_opt_find(IP_CMSG_DATA(cmsghdr),
|
|
(cmsghdr->cmsg_len - sizeof(*cmsghdr)),
|
|
ipcom_inet6_opt_init(0, (cmsghdr->cmsg_len - sizeof(*cmsghdr))),
|
|
IPMIP6_DSTOPT_HOME_ADDRESS,
|
|
&olen, &optr) != -1)
|
|
{
|
|
if (olen == sizeof(struct Ip_in6_addr))
|
|
{
|
|
/* Need to re-switch */
|
|
ipcom_memcpy(&pbuf->ipmip.srcinfo.src->sin6.sin6_addr, optr, sizeof(struct Ip_in6_addr));
|
|
pbuf->recvinfo.home = optr;
|
|
ipcom_memcpy(pbuf->recvinfo.home, &src.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Wrong size, just ignore */
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IP_IPV6_RECVPKTINFO: /* LKM */
|
|
case IP_IPV6_PKTINFO: /* IPNET */
|
|
ipmip_pktbuf_srcinfo_dst_inet6(&pbuf->ipmip, (struct Ip_in6_pktinfo *)IP_CMSG_DATA(cmsghdr));
|
|
break;
|
|
|
|
case IP_IPV6_RTHDR:
|
|
/* Got some destination options */
|
|
{
|
|
Ipmip6_pkt_ext_hdr_t *ext = IP_CMSG_DATA(cmsghdr);
|
|
Ipmip6_pkt_rthdr2_hdr_t *rthdr = (Ipmip6_pkt_rthdr2_hdr_t *) (ext + 1);
|
|
|
|
if (rthdr->rtype == 2)
|
|
pbuf->recvinfo.rthdr2 = IP_TRUE;
|
|
}
|
|
break;
|
|
case IP_IPV6_HOPLIMIT:
|
|
/* Got some destination options */
|
|
pbuf->recvinfo.hoplimit = IP_GET_32ON8(IP_CMSG_DATA(cmsghdr));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do sanity */
|
|
if (pbuf->ipmip.srcinfo.dst == IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"Did not receive PKTINFO when parsing ICMP packet"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Determine what we've got */
|
|
switch (icmp->icmp6_type)
|
|
{
|
|
case IPMIP6_ICMP6_DST_UNREACH:
|
|
break;
|
|
|
|
case IPMIP6_ND_ROUTER_ADVERT:
|
|
|
|
/* Verify limit */
|
|
if (pbuf->recvinfo.hoplimit != 0xff)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ND Router advert received - Invalid hop limit"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Is it link local? */
|
|
if (!IP_IN6_IS_ADDR_LINK_LOCAL(&src.sin6_addr))
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ND Router advert received - source not link local"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Indicate this packet */
|
|
ipmip6_if_router_radv_recv(ctx, proc, pbuf);
|
|
break;
|
|
|
|
case IPMIP6_ND_NEIGHBOR_SOLICIT:
|
|
if (pbuf->recvinfo.hoplimit != 0xff)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ND Neighbor solicit received - Invalid hop limit"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Indicate this packet */
|
|
ipmip6_nd_proxy_ns_recv(ctx, proc, pbuf);
|
|
break;
|
|
|
|
case IPMIP6_ND_NEIGHBOR_ADVERT:
|
|
if (pbuf->recvinfo.hoplimit != 0xff)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ND Neighbor advert received - Invalid hop limit"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Indicate this packet */
|
|
ipmip6_nd_nud_na_recv(ctx, proc, pbuf);
|
|
break;
|
|
|
|
default:
|
|
/* Dispatch the ICMP messages */
|
|
if (IP_NULL == ctx->pkt->dispatch->icmp || !(*ctx->pkt->dispatch->icmp) (ctx, proc, pbuf))
|
|
ipmip_pktbuf_free(&ctx->ipmip, proc, &pbuf->ipmip);
|
|
break;
|
|
}
|
|
|
|
/* */
|
|
return;
|
|
|
|
error_out:
|
|
ipmip_pktbuf_release(&ctx->ipmip, proc, &pbuf->ipmip);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_mh_recv
|
|
*===========================================================================
|
|
* Description: Receive and process an ICMP message.
|
|
* Parameters: context - The context
|
|
* Returns: IP_TRUE if successful.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip6_pkt_mh_recv(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
void *salink = IP_NULL;
|
|
struct Ip_cmsghdr *cmsghdr;
|
|
int len;
|
|
struct Ip_sockaddr_in6 src;
|
|
struct Ip_msghdr msg;
|
|
struct Ip_iovec vec;
|
|
|
|
/* Set when the option is located */
|
|
Ipmip6_pktbuf_t *pbuf = IP_NULL;
|
|
|
|
/* Allocate pktbuf */
|
|
pbuf = (Ipmip6_pktbuf_t *)ipmip_pktbuf_alloc(&ctx->ipmip, proc, IPMIP6_PKT_VLEN + IPMIP6_PKT_CLEN);
|
|
|
|
/* Failed to allocate buffer? */
|
|
if (pbuf == IP_NULL)
|
|
return;
|
|
|
|
/* Initialize vector */
|
|
vec.iov_base = IPMIP_BUFFER_DATA(&pbuf->ipmip.packet);
|
|
vec.iov_len = IPMIP6_PKT_VLEN;
|
|
|
|
/* Initialize name */
|
|
msg.msg_name = &src;
|
|
msg.msg_namelen = sizeof(src);
|
|
|
|
/* Initialize io */
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
|
|
/* Initialize control */
|
|
msg.msg_control = ipmip_buffer_offset(&pbuf->ipmip.packet, IPMIP6_PKT_VLEN);
|
|
msg.msg_controllen = IPMIP6_PKT_CLEN;
|
|
|
|
/* Clear flags */
|
|
msg.msg_flags = 0;
|
|
|
|
/* Read and process a Mobile Header message */
|
|
if ((len = ipcom_recvmsg(ctx->pkt->fd_mh, &msg, 0)) < 0)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"Failed to receive from MH socket = %x", ipcom_errno));
|
|
ipmip_util_sock_clear(ctx->pkt->fd_mh);
|
|
goto error_out;
|
|
}
|
|
|
|
/* Sanity, must be atleast the mobility header */
|
|
if ((Ip_size_t)len < sizeof(Ipmip6_mobility_header_t))
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"MH Received packet was too short"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* We've gotten something */
|
|
if ((msg.msg_flags & (IP_MSG_TRUNC | IP_MSG_CTRUNC)) != 0)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"MH TRUNC or CTRUNC set, not all data read. Discarding"));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Store value */
|
|
ipmip_util_dup(&pbuf->ipmip.srcinfo.src, &src, sizeof(src));
|
|
|
|
/* Move past the read length */
|
|
ipmip_buffer_append(&pbuf->ipmip.packet, IP_NULL, len);
|
|
|
|
/* Did we get any ancillary data? */
|
|
if (msg.msg_controllen > 0)
|
|
{
|
|
/* Loop the ancillary data, see what we find */
|
|
for (cmsghdr = IP_CMSG_FIRSTHDR(&msg); cmsghdr != 0;
|
|
cmsghdr = IP_CMSG_NXTHDR(&msg, cmsghdr))
|
|
{
|
|
/* Correct level? */
|
|
if (cmsghdr->cmsg_level == IP_IPPROTO_IPV6)
|
|
{
|
|
/* Type? */
|
|
switch (cmsghdr->cmsg_type)
|
|
{
|
|
case IP_IPV6_X_UDPENCAP:
|
|
pbuf->recvinfo.encapsulation = (union Ip_sockaddr_union *)IP_CMSG_DATA(cmsghdr);
|
|
break;
|
|
case IP_IPV6_RECVPKTINFO: /* LKM */
|
|
case IP_IPV6_PKTINFO: /* IPNET */
|
|
ipmip_pktbuf_srcinfo_dst_inet6(&pbuf->ipmip, (struct Ip_in6_pktinfo *)IP_CMSG_DATA(cmsghdr));
|
|
break;
|
|
|
|
case IP_IPV6_DSTOPTS:
|
|
/* Got some destination options */
|
|
{
|
|
Ip_socklen_t olen;
|
|
void* optr;
|
|
|
|
/* Locate the desired option */
|
|
if (ipcom_inet6_opt_find(IP_CMSG_DATA(cmsghdr),
|
|
(cmsghdr->cmsg_len - sizeof(*cmsghdr)),
|
|
ipcom_inet6_opt_init(0, (cmsghdr->cmsg_len - sizeof(*cmsghdr))),
|
|
IPMIP6_DSTOPT_HOME_ADDRESS,
|
|
&olen, &optr) != -1)
|
|
{
|
|
if (olen == sizeof(struct Ip_in6_addr))
|
|
{
|
|
/* Need to re-switch */
|
|
ipcom_memcpy(&pbuf->ipmip.srcinfo.src->sin6.sin6_addr, optr, sizeof(struct Ip_in6_addr));
|
|
pbuf->recvinfo.home = optr;
|
|
ipcom_memcpy(pbuf->recvinfo.home, &src.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Wrong size, just ignore */
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IP_IPV6_RTHDR:
|
|
/* Got some destination options */
|
|
{
|
|
Ipmip6_pkt_ext_hdr_t *ext = IP_CMSG_DATA(cmsghdr);
|
|
Ipmip6_pkt_rthdr2_hdr_t *rthdr = (Ipmip6_pkt_rthdr2_hdr_t *) (ext + 1);
|
|
|
|
if (rthdr->rtype == 2)
|
|
pbuf->recvinfo.rthdr2 = IP_TRUE;
|
|
}
|
|
break;
|
|
case IP_IPV6_HOPLIMIT:
|
|
/* Got some destination options */
|
|
pbuf->recvinfo.hoplimit = IP_GET_32ON8(IP_CMSG_DATA(cmsghdr));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do sanity */
|
|
if (pbuf->ipmip.srcinfo.dst == IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"Did not receive PKTINFO when parsing MH packet"));
|
|
goto error_out;
|
|
}
|
|
else
|
|
{
|
|
Ipmip6_mobility_header_t *hdr = (Ipmip6_mobility_header_t *) IPMIP_BUFFER_PTR(&pbuf->ipmip.packet);
|
|
int mlen = (hdr->mh_len << 3) + 8;
|
|
#if 0
|
|
int mh_min_len = 0;
|
|
#endif
|
|
/* Is the reported length larger than the read length? */
|
|
if (mlen > len)
|
|
{
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"MH Reported length %u is less than read length",
|
|
mlen, len));
|
|
|
|
goto error_out;
|
|
}
|
|
|
|
/* RFC 3775, 9.2. Payload Proto MUST be IPPROTO_NONE */
|
|
if (hdr->mh_nxt != IP_IPPROTO_NONE)
|
|
{
|
|
/* RFC 3775, 9.2 SHOULD send an ICMP Parameter Problem
|
|
* -- Skip for now. */
|
|
|
|
/* RFC 3775, 9.2 MUST discard the packet*/
|
|
goto error_out;
|
|
}
|
|
|
|
#if 0
|
|
/* Verify the size of the packet towards the minimum required size.
|
|
* We know that hdr->mh_type is a valid index, since we've done a
|
|
* sanity already */
|
|
switch (hdr->mh_type)
|
|
{
|
|
case IPMIP6_MH_HOME_TEST_INIT:
|
|
mh_min_len = sizeof(Ipmip6_mh_home_test_init_t);
|
|
break;
|
|
case IPMIP6_MH_CARE_OF_TEST_INIT:
|
|
mh_min_len = sizeof(Ipmip6_mh_care_of_test_init_t);
|
|
break;
|
|
case IPMIP6_MH_CARE_OF_TEST:
|
|
mh_min_len = sizeof(Ipmip6_mh_care_of_test_t);
|
|
break;
|
|
case IPMIP6_MH_BINDING_UPDATE:
|
|
mh_min_len = sizeof(Ipmip6_mh_binding_update_t);
|
|
break;
|
|
case IPMIP6_MH_BINDING_ACKNOWLEDGEMENT:
|
|
mh_min_len = sizeof(Ipmip6_mh_binding_acknowledgement_t);
|
|
break;
|
|
case IPMIP6_MH_BINDING_ERROR:
|
|
mh_min_len = sizeof(Ipmip6_mh_binding_error_t);
|
|
break;
|
|
case IPMIP6_MH_HEARTBEAT_MESSAGE:
|
|
mh_min_len = sizeof(Ipmip6_mh_heartbeat_message_t);
|
|
break;
|
|
default:
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"MH Unknown Mobility Header Type %u", hdr->mh_type));
|
|
|
|
/* Do we have a home address option? */
|
|
if (pbuf->recvinfo.home)
|
|
{
|
|
/* RFC 3775, 9.2, MUST Send BINDING ERROR */
|
|
ipmip6_mh_send_binding_error(ctx,
|
|
proc,
|
|
pbuf->recvinfo.home,
|
|
&pbuf->ipmip.srcinfo.src->sin6.sin6_addr,
|
|
IPMIP6_MH_BE_STATUS_ERROR_UNKNOWN_MH_TYPE);
|
|
}
|
|
|
|
/* RFC 3775, 9.2, MUST Discard */
|
|
goto error_out;
|
|
}
|
|
|
|
if (mh_min_len > (Ip_size_t)mlen)
|
|
{
|
|
/* RFC 3775, 9.2 SHOULD send an ICMP Parameter Problem
|
|
* -- Skip for now. */
|
|
|
|
/* Too little data for this type of mobile header. */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"MH Too little data(%u vs %u) for mobility header type %u",
|
|
mlen, mh_min_len + 8, hdr->mh_type));
|
|
|
|
/* RFC 3775, 9.2 MUST Discard */
|
|
goto error_out;
|
|
}
|
|
#endif
|
|
|
|
ipmip6_context_add_inbound(ctx, IPMIP_BUFFER_SIZE(&pbuf->ipmip.packet), 1);
|
|
|
|
/* Process the received frame */
|
|
if (!(*ctx->pkt->dispatch->mh) (ctx, proc, pbuf))
|
|
{
|
|
if (hdr->mh_type != IPMIP6_MH_BINDING_ERROR)
|
|
{
|
|
ipmip6_mh_send_binding_error(ctx,
|
|
proc,
|
|
pbuf->recvinfo.home,
|
|
&pbuf->ipmip.srcinfo.dst->dst.sin6.sin6_addr,
|
|
&pbuf->ipmip.srcinfo.src->sin6.sin6_addr,
|
|
IPMIP6_MH_BE_STATUS_ERROR_UNKNOWN_MH_TYPE);
|
|
}
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
/* */
|
|
return;
|
|
|
|
error_out:
|
|
ipmip_pktbuf_release(&ctx->ipmip, proc, &pbuf->ipmip);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_loop
|
|
*===========================================================================
|
|
* Description:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip6_pkt_loop(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
void *unused)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(unused);
|
|
|
|
/*********** tick loop ***********/
|
|
for (; proc->quit != IP_TRUE;)
|
|
{
|
|
int maxfd = 0;
|
|
Ip_fd_set rset;
|
|
int ret;
|
|
struct Ip_timeval tv;
|
|
/* Clear it */
|
|
IP_FD_ZERO(&rset);
|
|
|
|
/* Register socket */
|
|
if (ctx->pkt->fd_icmp_in != IP_INVALID_SOCKET)
|
|
{
|
|
maxfd = IP_MAX(maxfd, ctx->pkt->fd_icmp_in);
|
|
IP_FD_SET(ctx->pkt->fd_icmp_in, &rset);
|
|
}
|
|
|
|
if (ctx->pkt->fd_mh != IP_INVALID_SOCKET)
|
|
{
|
|
maxfd = IP_MAX(maxfd, ctx->pkt->fd_mh);
|
|
IP_FD_SET(ctx->pkt->fd_mh, &rset);
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (ctx->ipmip.arp != IP_NULL && ctx->ipmip.arp->fd_arp_in != IP_INVALID_SOCKET)
|
|
{
|
|
maxfd = IP_MAX(maxfd, ctx->ipmip.arp->fd_arp_in);
|
|
IP_FD_SET(ctx->ipmip.arp->fd_arp_in, &rset);
|
|
}
|
|
|
|
if (ctx->ipmip.dhcp != IP_NULL && ctx->ipmip.dhcp->fd_udp != IP_INVALID_SOCKET)
|
|
{
|
|
maxfd = IP_MAX(maxfd, ctx->ipmip.dhcp->fd_udp);
|
|
IP_FD_SET(ctx->ipmip.dhcp->fd_udp, &rset);
|
|
}
|
|
#endif
|
|
|
|
if (ctx->ipmip.dhcp6 != IP_NULL && ctx->ipmip.dhcp6->fd_udp != IP_INVALID_SOCKET)
|
|
{
|
|
maxfd = IP_MAX(maxfd, ctx->ipmip.dhcp6->fd_udp);
|
|
IP_FD_SET(ctx->ipmip.dhcp6->fd_udp, &rset);
|
|
}
|
|
|
|
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 200 * 1000;
|
|
ret = ipcom_socketselect(maxfd + 1, &rset, IP_NULL, IP_NULL, &tv);
|
|
|
|
/* Timeout */
|
|
if (ret == 0)
|
|
continue;
|
|
else if (ret < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"packet context socketselect() failed : %s [%d]",
|
|
ipcom_strerror(ipcom_errno), ipcom_errno));
|
|
}
|
|
else
|
|
{
|
|
if (IP_FD_ISSET(ctx->pkt->fd_icmp_in, &rset))
|
|
ipmip6_pkt_icmp_recv(ctx, proc);
|
|
|
|
if (IP_FD_ISSET(ctx->pkt->fd_mh, &rset))
|
|
ipmip6_pkt_mh_recv(ctx, proc);
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (ctx->ipmip.arp != IP_NULL && ctx->ipmip.arp->fd_arp_in != IP_INVALID_SOCKET && IP_FD_ISSET(ctx->ipmip.arp->fd_arp_in, &rset))
|
|
ipmip_inet_arp_process(&ctx->ipmip, proc);
|
|
|
|
if (ctx->ipmip.dhcp != IP_NULL && ctx->ipmip.dhcp->fd_udp != IP_INVALID_SOCKET && IP_FD_ISSET(ctx->ipmip.dhcp->fd_udp, &rset))
|
|
ipmip_dhcp_process(&ctx->ipmip, proc);
|
|
#endif
|
|
if (ctx->ipmip.dhcp6 != IP_NULL && ctx->ipmip.dhcp6->fd_udp != IP_INVALID_SOCKET && IP_FD_ISSET(ctx->ipmip.dhcp6->fd_udp, &rset))
|
|
ipmip_dhcp6_process(&ctx->ipmip, proc);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_pad
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u8
|
|
ipmip6_pkt_pad(Ipmip_buffer_t *buf,
|
|
Ip_u8 pads)
|
|
{
|
|
if (pads != 0)
|
|
{
|
|
Ip_u8 *padn;
|
|
|
|
padn = ipmip_buffer_get(buf, pads);
|
|
|
|
/* Do the padding */
|
|
if (pads == 1)
|
|
*padn = 0;
|
|
else
|
|
{
|
|
*padn++ = 1;
|
|
*padn = pads - 2;
|
|
}
|
|
}
|
|
|
|
return pads;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_pad8
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_pad8(Ipmip_buffer_t *buf)
|
|
{
|
|
Ip_size_t sz = IPMIP_BUFFER_SIZE(buf);
|
|
|
|
sz = ((8 - sz) & 7);
|
|
ipmip6_pkt_pad(buf, sz);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_tlv_requirement
|
|
*===========================================================================
|
|
* Description: Locate the correct TLV option requirement in the provided
|
|
* option list.
|
|
* Parameters: options - The option list.
|
|
* type - The value of the option to locate.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipmip6_pkt_tlv_option_t *
|
|
ipmip6_pkt_tlv_requirement(Ipmip6_pkt_tlv_option_t *options,
|
|
Ip_u8 type)
|
|
{
|
|
int i;
|
|
Ipmip6_pkt_tlv_option_t *found = IP_NULL;
|
|
|
|
/* Loop til we come to the bitter end. */
|
|
for (i = 0; options[i].type != 0; i++)
|
|
{
|
|
/* Is this the option we're looking for? */
|
|
if (options[i].type == type)
|
|
{
|
|
/* Store the values and return */
|
|
found = &options[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Nothing found */
|
|
return found;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_tlv_alignment
|
|
*===========================================================================
|
|
* Description: Calculate alignment for a given option.
|
|
* Parameters: size - The size.
|
|
* req - The option requirements.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u8
|
|
ipmip6_pkt_tlv_alignment(Ip_u32 size,
|
|
Ipmip6_pkt_tlv_option_t *req)
|
|
{
|
|
Ip_u32 aligned_offset;
|
|
|
|
/* We've located the current size of this frame */
|
|
/* Calculate the new, aligned offset. */
|
|
aligned_offset = size + ((req->alignment - size) & (req->alignment - 1));
|
|
|
|
/* We have a constant. Need to take it into account. */
|
|
if (req->constant != 0)
|
|
{
|
|
/* Calculate the difference between the aligned offset and the
|
|
* current offset */
|
|
Ip_u32 diff = aligned_offset - size;
|
|
|
|
/* If the difference's smaller than the alignment requirement,
|
|
* move to next valid aligned address */
|
|
if (diff < (Ip_u32)(req->alignment - req->constant))
|
|
aligned_offset += req->alignment;
|
|
|
|
/* Reduce the aligned offset by the difference between the alignment
|
|
* and the constant to handle the nested alignment requirements */
|
|
aligned_offset -= (req->alignment - req->constant);
|
|
}
|
|
|
|
/* We've calculated the required aligned offset. The difference between
|
|
* the aligned offset and the current offset, is pads */
|
|
return (Ip_u8) (aligned_offset - size);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_dstopt_opt_add
|
|
*===========================================================================
|
|
* Description: Adds an option to the tail of the dstopt ancillary option list,
|
|
* and calculates the necessary padding based on the alignment
|
|
* requirements.
|
|
* Parameters: pkt - The packet pointer.
|
|
* type - The option type value.
|
|
* len - The length of the option
|
|
* data - The data, or IP_NULL if an empty option is to be
|
|
* added.
|
|
* Returns: IP_TRUE if added, IP_FALSE otherwise.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip6_pkt_dstopt_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
Ip_u8 type,
|
|
Ip_u8 len,
|
|
void* data)
|
|
{
|
|
Ipmip6_pkt_tlv_option_t *req = IP_NULL;
|
|
Ipmip6_pkt_dstopt_hdr_t *dop;
|
|
int pads;
|
|
|
|
/* Did I find the requirements? */
|
|
if ((req = ipmip6_pkt_tlv_requirement(ipmip6_mh_dstopt_options, type)) == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
/* Calculate the number of required pads */
|
|
pads = ipmip6_pkt_tlv_alignment(IPMIP_BUFFER_SIZE(&pkt->dstopts) + sizeof(Ipmip6_pkt_ext_hdr_t), req);
|
|
ipmip6_pkt_pad(&pkt->dstopts, pads);
|
|
|
|
dop = ipmip_buffer_get(&pkt->dstopts, sizeof(*dop));
|
|
|
|
dop->option_type = type;
|
|
dop->option_length = len;
|
|
|
|
ipmip_buffer_append(&pkt->dstopts, data, len);
|
|
|
|
/* All done */
|
|
return IP_TRUE;
|
|
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_nexthop_opt_add
|
|
*===========================================================================
|
|
* Description: Add a nexthop ancillary data option
|
|
* Parameters: pkt - The packet to add the option to.
|
|
* addr - The nexthop
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_nexthop_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
/* Allocate the necessary data */
|
|
if (pkt->sendinfo.nexthop == IP_NULL)
|
|
{
|
|
pkt->sendinfo.nexthop = ipcom_malloc(sizeof(struct Ip_in6_addr));
|
|
}
|
|
|
|
/* */
|
|
if (pkt->sendinfo.nexthop != IP_NULL)
|
|
{
|
|
/* And copy */
|
|
ipcom_memcpy(pkt->sendinfo.nexthop, addr, sizeof(*addr));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_rthdr0_opt_add
|
|
*===========================================================================
|
|
* Description: Add a routing header type 0.
|
|
* Parameters: pkt - The packet to add the routing header to.
|
|
* addr - The address in the routing header.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_rthdr0_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
/* Allocate the necessary data */
|
|
if (pkt->sendinfo.rthdr0 == IP_NULL)
|
|
pkt->sendinfo.rthdr0 = ipcom_malloc(sizeof(struct Ip_in6_addr));
|
|
|
|
if (pkt->sendinfo.rthdr0 != IP_NULL)
|
|
ipcom_memcpy(pkt->sendinfo.rthdr0, addr, sizeof(*addr));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_rthdr2_opt_add
|
|
*===========================================================================
|
|
* Description: Add a routing header type 2.
|
|
* Parameters: pkt - The packet to add the routing header to.
|
|
* addr - The address in the routing header.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_rthdr2_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
/* Allocate the necessary data */
|
|
if (pkt->sendinfo.rthdr2 == IP_NULL)
|
|
pkt->sendinfo.rthdr2 = ipcom_malloc(sizeof(struct Ip_in6_addr));
|
|
|
|
if (pkt->sendinfo.rthdr2 != IP_NULL)
|
|
/* And copy */
|
|
ipcom_memcpy(pkt->sendinfo.rthdr2, addr, sizeof(*addr));
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_pktinfo_src_opt_add
|
|
*===========================================================================
|
|
* Description: Specify what source address is to be used in this particular
|
|
* packet..
|
|
* Parameters: pkt - The packet to add the routing header to.
|
|
* addr - The source address to be used..
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_pktinfo_src_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
/* Allocate */
|
|
if (pkt->sendinfo.pktinfo == IP_NULL)
|
|
{
|
|
pkt->sendinfo.pktinfo = ipcom_calloc(1, sizeof(struct Ip_in6_pktinfo));
|
|
if (pkt->sendinfo.pktinfo == IP_NULL)
|
|
return;
|
|
}
|
|
|
|
/* Copy. */
|
|
ipcom_memcpy(&pkt->sendinfo.pktinfo->ipi6_addr, addr, sizeof(*addr));
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_pktinfo_if_opt_add
|
|
*===========================================================================
|
|
* Description: Specify what interface is to be used in this particular
|
|
* packet.
|
|
* Parameters: pkt - The packet to add the routing header to.
|
|
* ife - The interface
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_pktinfo_if_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
Ipmip6_if_t *ife)
|
|
{
|
|
/* Allocate */
|
|
if (pkt->sendinfo.pktinfo == IP_NULL)
|
|
{
|
|
pkt->sendinfo.pktinfo = ipcom_calloc(1, sizeof(struct Ip_in6_pktinfo));
|
|
|
|
if (pkt->sendinfo.pktinfo == IP_NULL)
|
|
return;
|
|
}
|
|
|
|
/* Copy. */
|
|
pkt->sendinfo.pktinfo->ipi6_ifindex = ife->iff.ifindex;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_pktinfo_prefix_opt_add
|
|
*===========================================================================
|
|
* Description: Adds the ancillary options required to force a specific address
|
|
* and interface to be used.
|
|
* Parameters: pkt - The packet to specify.
|
|
* address - The Care of Address to use.
|
|
* local - Whether or not this is a local packet.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_pktinfo_opt_add(Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *src,
|
|
Ipmip6_if_t *ifp)
|
|
{
|
|
/* Set the source address */
|
|
ipmip6_pkt_pktinfo_src_opt_add(pkt, src);
|
|
|
|
/* Set the interface */
|
|
ipmip6_pkt_pktinfo_if_opt_add(pkt, ifp);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_route_via
|
|
*===========================================================================
|
|
* Description: Force a packet to route via a specific router
|
|
* Parameters: pkt - The packet to force
|
|
* router - The router to force it via
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_route_via(Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *nexthop)
|
|
{
|
|
#if 0
|
|
/* Force the next hop if this isn't a link-local packet */
|
|
if (ipmip6_if_router_default(router) == IP_TRUE)
|
|
#endif
|
|
{
|
|
#ifdef IPMIP6_USE_RTHDR0
|
|
/* Add rthdr0 option */
|
|
ipmip6_pkt_rthdr0_opt_add(pkt, nexthop);
|
|
#else
|
|
/* Add nexthop option */
|
|
ipmip6_pkt_nexthop_opt_add(pkt, nexthop);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_set_hoplimit
|
|
*===========================================================================
|
|
* Description: Set the hoplimit for this particular packet.
|
|
* Parameters: value - Hoplimit. Value between[inclusive] -1 and 255.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_set_hoplimit(Ipmip6_pktbuf_t *pkt,
|
|
Ip_u32 value)
|
|
{
|
|
if (value < 256)
|
|
pkt->sendinfo.hoplimit = value;
|
|
}
|
|
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_set_noroute(Ipmip6_pktbuf_t *pkt)
|
|
{
|
|
pkt->sendinfo.flags |= IP_MSG_DONTROUTE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_send
|
|
*===========================================================================
|
|
* Description: Sends a packet to the desired address.
|
|
* This invokation also frees the packet.
|
|
* Parameters: pkt - The packet to send.
|
|
* addr - The address to send it to.
|
|
* Returns: 0 on success, error code otherwise
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmip6_pkt_send(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ip_fd fd,
|
|
Ipmip6_pktbuf_t *pkt,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
/* Placeholders */
|
|
struct Ip_sockaddr_in6 dst;
|
|
struct Ip_msghdr msg;
|
|
struct Ip_iovec vec;
|
|
int err = 0;
|
|
|
|
/* Compile it if necessary */
|
|
/* Clear it */
|
|
ipcom_memset(&dst, 0, sizeof(dst));
|
|
|
|
/* Set it */
|
|
ipcom_memcpy(&dst.sin6_addr, addr, sizeof(*addr));
|
|
|
|
/* Set size */
|
|
dst.sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(&dst, sizeof(dst));
|
|
|
|
/* Does this address require a scope? */
|
|
if (IPCOM_IN6_IS_ADDR_SCOPED(addr))
|
|
{
|
|
ip_assert(pkt->sendinfo.pktinfo != IP_NULL && pkt->sendinfo.pktinfo->ipi6_ifindex != 0);
|
|
dst.sin6_scope_id = pkt->sendinfo.pktinfo->ipi6_ifindex;
|
|
}
|
|
|
|
/* Finalize this frame */
|
|
if (pkt->finalize != IP_NULL)
|
|
(*pkt->finalize) (ctx, proc, pkt);
|
|
|
|
/* Compile any ancillary options */
|
|
ipmip6_pkt_ancillary(pkt);
|
|
|
|
/* Reset the message */
|
|
ipcom_memset(&msg, 0, sizeof(msg));
|
|
|
|
/* Store the destination */
|
|
msg.msg_name = &dst;
|
|
msg.msg_namelen = sizeof(dst);
|
|
|
|
/* Ancillary options */
|
|
msg.msg_control = IPMIP_BUFFER_SIZE(&pkt->ancillary)? IPMIP_BUFFER_PTR(&pkt->ancillary) : IP_NULL;
|
|
msg.msg_controllen = IPMIP_BUFFER_SIZE(&pkt->ancillary);
|
|
|
|
/* Setup vector */
|
|
vec.iov_base = IPMIP_BUFFER_PTR(&pkt->ipmip.packet);
|
|
vec.iov_len = IPMIP_BUFFER_SIZE(&pkt->ipmip.packet);
|
|
|
|
/* Set up basic data */
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
|
|
|
|
/* All done, send the message */
|
|
if (ipcom_sendmsg(fd, &msg, pkt->sendinfo.flags) < 0)
|
|
{
|
|
err = ipcom_errno;
|
|
|
|
/* Clear the error from the FD */
|
|
ipmip_util_sock_clear(fd);
|
|
}
|
|
else
|
|
{
|
|
/* Do statistics */
|
|
if (fd == ctx->pkt->fd_mh)
|
|
ipmip6_context_add_outbound(ctx, IPMIP_BUFFER_SIZE(&pkt->ipmip.packet), 1);
|
|
}
|
|
|
|
/* Free the packet */
|
|
ipmip_pktbuf_free(&ctx->ipmip, proc, &pkt->ipmip);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_context_create
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip6_pkt_context_create(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6_pkt_context_dispatch_t *dispatch,
|
|
Ip_u16 udp_port,
|
|
Ipmip_pkt_buf_alloc_t bufalloc,
|
|
Ipmip_pkt_buf_free_t buffree)
|
|
{
|
|
int val;
|
|
struct Ip_icmp6_filter filter;
|
|
|
|
ctx->pkt = ipcom_calloc(1, sizeof(*ctx->pkt));
|
|
if (ctx->pkt == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
/**/
|
|
ctx->pkt->dispatch = dispatch;
|
|
|
|
/**/
|
|
ipmip_pktbuf_context_create(&ctx->ipmip,
|
|
proc,
|
|
(Ipmip_pkt_buf_alloc_t)ipmip6_pktbuf_alloc,
|
|
(Ipmip_pkt_buf_free_t)ipmip6_pktbuf_free);
|
|
|
|
ctx->pkt->bufalloc = bufalloc;
|
|
ctx->pkt->buffree = buffree;
|
|
|
|
ctx->pkt->fd_icmp_in = IP_INVALID_SOCKET;
|
|
ctx->pkt->fd_icmp_out = IP_INVALID_SOCKET;
|
|
ctx->pkt->fd_mh = IP_INVALID_SOCKET;
|
|
#ifdef IPMIP_USE_INET
|
|
ctx->pkt->fd_udp_nat = IP_INVALID_SOCKET;
|
|
#endif
|
|
/* Crete the ICMP and MH file descriptors */
|
|
|
|
/* Create the MH socket */
|
|
ctx->pkt->fd_mh = ipcom_socket(IP_AF_INET6, IP_SOCK_RAW, IPMIP6_IPPROTO_MOBILE_HEADER);
|
|
if (ctx->pkt->fd_mh == IP_INVALID_SOCKET)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"Failed to create mobile header socket"));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/* Set the offset into the packet for the checsum */
|
|
val = ip_offsetof(Ipmip6_mobility_header_t, mh_cksum);
|
|
if (ipcom_setsockopt(ctx->pkt->fd_mh, IP_IPPROTO_IPV6, IP_IPV6_CHECKSUM, &val, sizeof(val)) < 0)
|
|
return IP_FALSE;
|
|
|
|
/* I want to receive DSTOPT as ancillary data */
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_mh, IP_IPPROTO_IPV6, IP_IPV6_RECVDSTOPTS, &val, sizeof(val)) < 0)
|
|
return IP_FALSE;
|
|
|
|
/* I want to receive PACKET INFO as ancillary data */
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_mh, IP_IPPROTO_IPV6, IP_IPV6_RECVPKTINFO, &val, sizeof(val)) < 0)
|
|
return IP_FALSE;
|
|
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_mh, IP_IPPROTO_IPV6, IP_IPV6_RECVRTHDR, &val, sizeof(val)) < 0)
|
|
return IP_FALSE;
|
|
|
|
#ifdef IPNET
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_mh, IP_IPPROTO_IPV6, IP_IPV6_X_UDPENCAP, &val, sizeof(val)) < 0)
|
|
return IP_FALSE;
|
|
#endif
|
|
|
|
/* Create the ICMP socket */
|
|
ctx->pkt->fd_icmp_in = ipcom_socket(IP_AF_INET6, IP_SOCK_RAW, IP_IPPROTO_ICMPV6);
|
|
if (ctx->pkt->fd_icmp_in == IP_INVALID_SOCKET)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to create in socket = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/* Shutdown the write channel */
|
|
ipcom_shutdown(ctx->pkt->fd_icmp_in, IP_SHUT_WR);
|
|
|
|
/* Initialize */
|
|
IP_ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
|
|
/* Standard router advertisements */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ND_ROUTER_ADVERT, &filter);
|
|
/* Standard neighbor advertisements */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ND_NEIGHBOR_ADVERT, &filter);
|
|
/* Standard neighbor solicitations */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ND_NEIGHBOR_SOLICIT, &filter);
|
|
/* Parameter problems */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ICMP6_PARAM_PROB, &filter);
|
|
|
|
/* Mobile prefix discovery */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ICMP6_PREFIX_ADVERT, &filter);
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ICMP6_PREFIX_SOLICIT, &filter);
|
|
|
|
/* Home agent discovery */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ICMP6_HA_DISCOVERY_REPLY, &filter);
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ICMP6_HA_DISCOVERY_REQUEST, &filter);
|
|
|
|
/* Correspondent node -- if RO destination isn't there any more */
|
|
IP_ICMP6_FILTER_SETPASS(IPMIP6_ICMP6_DST_UNREACH, &filter);
|
|
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_in,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_RECVPKTINFO,
|
|
&val,
|
|
sizeof(val)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set PKTINFO = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_in,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_RECVHOPLIMIT,
|
|
&val,
|
|
sizeof(val)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set HOPLIMIT = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_in,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_RECVRTHDR,
|
|
&val,
|
|
sizeof(val)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set RTHDR = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_in,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_RECVDSTOPTS,
|
|
&val,
|
|
sizeof(val)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set DSTOPT = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_in,
|
|
IP_IPPROTO_ICMPV6,
|
|
IP_ICMP6_FILTER,
|
|
&filter,
|
|
sizeof(filter)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set ICMP FILTER = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/* I do not want to see anything that I myself send */
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_in,
|
|
IP_IPPROTO_IPV6,
|
|
IP_IPV6_MULTICAST_LOOP,
|
|
&val,
|
|
sizeof(int)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set IP_IPV6_MULTICAST_LOOP = %d", ipcom_errno));
|
|
}
|
|
|
|
|
|
/* Create the ICMP socket */
|
|
ctx->pkt->fd_icmp_out = ipcom_socket(IP_AF_INET6, IP_SOCK_RAW, IP_IPPROTO_ICMPV6);
|
|
if (ctx->pkt->fd_icmp_out == IP_INVALID_SOCKET)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to create out socket = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/* Shutdown the read channel */
|
|
ipcom_shutdown(ctx->pkt->fd_icmp_out, IP_SHUT_RD);
|
|
|
|
/* Initialize */
|
|
IP_ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
/* Filter it all out */
|
|
if (ipcom_setsockopt(ctx->pkt->fd_icmp_out,
|
|
IP_IPPROTO_ICMPV6,
|
|
IP_ICMP6_FILTER,
|
|
&filter,
|
|
sizeof(filter)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"ICMP6 failed to set ICMP FILTER = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
{
|
|
struct Ip_sockaddr_in6 sin6;
|
|
Ip_socklen_t slen;
|
|
|
|
ctx->pkt->fd_udp_nat = ipcom_socket(IP_AF_INET6, IP_SOCK_DGRAM, IP_IPPROTO_UDP);
|
|
if (ctx->pkt->fd_udp_nat == IP_INVALID_SOCKET)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"UDP_NAT_OUT failed to create in socket = %d", ipcom_errno));
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/* Allow NAT-T according to RFC3519 */
|
|
val = IP_UDP_NAT_T_DSMIPV6;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_udp_nat, IP_IPPROTO_UDP, IP_UDP_NAT_T, &val, sizeof(val)))
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"UDP_NAT_OUT failed to set NAT_T = %d", ipcom_errno));
|
|
}
|
|
|
|
/* Allow reusage of address */
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_udp_nat, IP_SOL_SOCKET, IP_SO_REUSEADDR, &val, sizeof(val)))
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"UDP_NAT_OUT failed to set REUSEADDR = %d", ipcom_errno));
|
|
}
|
|
|
|
/* Allow reusage of ports */
|
|
val = 1;
|
|
if (ipcom_setsockopt(ctx->pkt->fd_udp_nat, IP_SOL_SOCKET, IP_SO_REUSEPORT, &val, sizeof(val)))
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"UDP_NAT_OUT failed to set REUSEPORT = %d", ipcom_errno));
|
|
}
|
|
|
|
ipcom_memset(&sin6, 0, sizeof(sin6));
|
|
sin6.sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(&sin6, sizeof(sin6));
|
|
sin6.sin6_port = ip_htons(udp_port);
|
|
|
|
/* */
|
|
if (ipcom_bind(ctx->pkt->fd_udp_nat, (struct Ip_sockaddr *) &sin6, sizeof(sin6)) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"UDP_NAT_OUT failed to set BIND = %d", ipcom_errno));
|
|
}
|
|
|
|
slen = sizeof(ctx->pkt->s_udp_nat);
|
|
if (ipcom_getsockname(ctx->pkt->fd_udp_nat, (struct Ip_sockaddr *) &ctx->pkt->s_udp_nat, &slen) < 0)
|
|
{
|
|
IPMIP6_EVENT_IF(CRIT, proc,
|
|
ipmip_log_event_add(&ctx->ipmip,
|
|
proc,
|
|
"UDP_NAT_OUT failed to do GETSOCKNAME = %d", ipcom_errno));
|
|
}
|
|
}
|
|
|
|
return IP_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_context_destroy
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_context_destroy(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
if (ctx->pkt != IP_NULL)
|
|
{
|
|
/* Close the MH socket */
|
|
if (ctx->pkt->fd_mh != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ctx->pkt->fd_mh);
|
|
|
|
/* Close any open sockets */
|
|
if (ctx->pkt->fd_icmp_out != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ctx->pkt->fd_icmp_out);
|
|
if (ctx->pkt->fd_icmp_in != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ctx->pkt->fd_icmp_in);
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (ctx->pkt->fd_udp_nat != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(ctx->pkt->fd_udp_nat);
|
|
#endif
|
|
ipcom_free(ctx->pkt);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_context_start
|
|
*===========================================================================
|
|
* Description: Start the packet context
|
|
* Parameters: ctx - The context
|
|
* proc - Process context
|
|
* Returns: IP_TRUE on success.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip6_pkt_context_start(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
/* Lock the global mutex */
|
|
if (ctx->pkt != IP_NULL && ctx->pkt->proc == IP_NULL)
|
|
{
|
|
ctx->pkt->proc = ipmip_proc_start(&ctx->ipmip,
|
|
proc,
|
|
"packet",
|
|
(Ipmip_proc_func_t) ipmip6_pkt_loop,
|
|
ctx->pkt);
|
|
}
|
|
|
|
return ctx->pkt != IP_NULL && ctx->pkt->proc != IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6_pkt_context_stop
|
|
*===========================================================================
|
|
* Description: Stops the packet thread.
|
|
* Parameters: ctx - The timeout process
|
|
* proc - The process
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip6_pkt_context_stop(Ipmip6_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
/* Lock the global mutex */
|
|
if (ctx->pkt != IP_NULL && ctx->pkt->proc != IP_NULL)
|
|
{
|
|
ipmip_proc_stop(&ctx->ipmip,
|
|
proc,
|
|
ctx->pkt->proc,
|
|
IP_NULL,
|
|
IP_NULL);
|
|
ctx->pkt->proc = IP_NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|