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

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
****************************************************************************
*/