/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipmip6_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include "ipmip6_h.h" #include #include #include #include #include #include #include #include #include #include /* **************************************************************************** * 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 **************************************************************************** */