/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_udp.c,v $ $Revision: 1.87.10.8 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_udp.c,v $ * $Author: svc-cmnet $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* User datagram protocol transport layer */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include "ipnet.h" #include "ipnet_dst_cache.h" #include "ipnet_h.h" #include "ipnet_usr_sock.h" #ifdef IPCOM_USE_INET6 #include "ipnet_icmp6.h" #endif #ifdef IPNET_USE_TUNNEL #include "ipnet_tunnel.h" #endif #ifdef IPCOM_WV_INSTRUMENTATION #include #endif #ifdef IPCOM_USE_FORWARDER #include #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipnet_sock_udp_ops_struct { Ipnet_sock_inet_ops inet; Ipnet_sock_init_f network_init; Ipnet_sock_connect_f network_connect; Ipnet_sock_bind_f network_bind; } Ipnet_sock_udp_ops; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_sock_udp_init *=========================================================================== * Description: The UDP transport layer initialization. * Parameters: sock - The socket that is being created. * user_context - unused by UDP. * Returns: 0 = success, <0 = error code * */ IP_STATIC int ipnet_sock_udp_init(Ipnet_socket *sock, Ip_bool user_context) { Ipnet_sock_udp_ops *ops; IPCOM_UNUSED_ARG(user_context); if (sock->proto == 0) /* * Default to UDP in case no protocol was specified. */ sock->proto = IP_IPPROTO_UDP; sock->chk_offset = 6; #ifdef IPCOM_USE_INET if (sock->proto == IP_IPPROTO_UDPLITE || ipnet->conf.inet.udp_checksum) #endif IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM); ops = (Ipnet_sock_udp_ops *) ipnet_sock_get_ops(sock); if (ops->network_init != IP_NULL) return ops->network_init(sock, user_context); return 0; } /* *=========================================================================== * ipnet_sock_udp_bind *=========================================================================== * Description: The UDP network layer bind. * Parameters: sock - The socket to bind. * name - The socket name. * namelen - The length of the name variable. * check_reuse - IP_TRUE if the port should be checked for reuse. * Returns: 0 = success, <0 = error code * */ IP_STATIC int ipnet_sock_udp_bind(struct Ipnet_socket_struct *sock, IP_CONST struct Ip_sockaddr *name, Ip_socklen_t namelen, Ip_bool check_reuse) { int ret; Ipnet_sock_udp_ops *ops; ops = (Ipnet_sock_udp_ops *) ipnet_sock_get_ops(sock); ret = ops->network_bind(sock, name, namelen, check_reuse); if (ret >= 0) ret = ipnet_sock_bind_to_port(sock, name, check_reuse); return ret; } /* *=========================================================================== * ipnet_sock_udp_connect *=========================================================================== * Description: The UDP transport layer connect. * Parameters: sock - The socket to connect. * to - The address to connect to. * tolen - The size of the 'to' buffer. * Returns: 0 = success, <0 = error code * */ IP_STATIC int ipnet_sock_udp_connect(Ipnet_socket *sock, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen) { Ipnet_sock_udp_ops *ops; int ret; ops = (Ipnet_sock_udp_ops *) ipnet_sock_get_ops(sock); ret = ops->network_connect(sock, to, tolen); if (ret < 0) return ret; if (ret == 1) /* Socket was unconnected */ return 0; if (sock->dport == 0) /* Invalid port */ return -IP_ERRNO_EINVAL; IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_CONNECTED); return 0; } /* *=========================================================================== * ipnet_sock_udp_tx *=========================================================================== * Description: Adds a UDP header to the application data. * Parameters: sock - The socket to use when sending. * msg - The send parameters. * pkt - Packet that contains the UDP payload. * Returns: ==0 = success, <0 = error code. * */ IP_STATIC int ipnet_sock_udp_tx(Ipnet_socket *sock, IP_CONST struct Ip_msghdr *msg, Ipcom_pkt *pkt) { Ipnet_sock_udp_ops *ops; Ip_u16 to_port_n; Ipnet_pkt_udp *udp; int pkt_len; IPCOM_WV_MARKER_1 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_VERBOSE, 1, 1, IPCOM_WV_NETDEVENT_START, ipnet_sock_udp_tx, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_output++); ip_assert(sock->ipcom.type == IP_SOCK_DGRAM); ip_assert(sock->proto == IP_IPPROTO_UDP); if (msg->msg_name == IP_NULL) to_port_n = ip_htons(sock->dport); else /* * Ok for both IPv4 and IPv6 since the offset for "sin_port" * is the same as "sin6_port" */ to_port_n = ((struct Ip_sockaddr_in*) msg->msg_name)->sin_port; /* * Fill in UDP header. */ udp = ipcom_pkt_push_front(pkt, IPNET_UDP_HDR_SIZE); pkt_len = ipcom_pkt_get_length(pkt); if (IP_UNLIKELY(pkt_len > 0xffff)) { /* * The UDP payload length field is 16-bit. */ ipcom_pkt_free(pkt); return -IP_ERRNO_EMSGSIZE; } udp->sport = ip_htons(sock->sport); udp->dport = to_port_n; udp->sum = 0; udp->ulen = (Ip_u16) ip_htons(pkt_len); #ifdef IPCOM_FORWARDER_NAE if (IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_TL_CHECKSUM)) { /* * Update the checksum with the UDP and payload */ pkt->chk = ipcom_in_checksum_update(udp, pkt_len); } #else /* * Update the checksum with the UDP header */ pkt->chk += ipcom_in_checksum_update(udp, IPNET_UDP_HDR_SIZE); #endif /* !IPCOM_FORWARDER_NAE */ IPCOM_MIB2(udpOutDatagrams++); IPCOM_MIB2_U64_INC(udpHCOutDatagrams); /* * Call the network layer send handler */ ops = (Ipnet_sock_udp_ops *) ipnet_sock_get_ops(sock); return ops->inet.network_send(sock, msg, pkt); } /* *=========================================================================== * ipnet_sock_udplite_tx *=========================================================================== * Description: Adds a UDP-lite header to the application data. * Parameters: sock - The socket to use when sending. * msg - The send parameters. * pkt - Packet that contains the UDP payload. * Returns: ==0 = success, <0 = error code. * */ IP_STATIC int ipnet_sock_udplite_tx(Ipnet_socket *sock, IP_CONST struct Ip_msghdr *msg, Ipcom_pkt *pkt) { Ipnet_sock_udp_ops *ops; Ip_u16 to_port_n; Ipnet_pkt_udplite *udplite; Ipcom_pkt *next_part; int total_len = 0; int data_len; int pkt_len; IPCOM_WV_MARKER_1 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_VERBOSE, 1, 1, IPCOM_WV_NETDEVENT_START, ipnet_sock_udplite_tx, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); ip_assert(sock->ipcom.type == IP_SOCK_DGRAM); ip_assert(sock->proto == IP_IPPROTO_UDPLITE); if (msg->msg_name == IP_NULL) to_port_n = ip_htons(sock->dport); else /* * Ok for both IPv4 and IPv6 since the offset for "sin_port" * is the same as "sin6_port" */ to_port_n = ((struct Ip_sockaddr_in*) msg->msg_name)->sin_port; /* * Fill in UDP-Lite header. */ udplite = ipcom_pkt_push_front(pkt, IPNET_UDPLITE_HDR_SIZE); udplite->sport = ip_htons(sock->sport); udplite->dport = to_port_n; udplite->sum_cov = ip_htons(sock->udplite_sum_cov); udplite->sum = 0; /* * Calculate the checksum on the covered part UDP-Lite header. */ next_part = pkt; total_len = 0; data_len = sock->udplite_sum_cov ? (int)sock->udplite_sum_cov : ipcom_pkt_get_length(pkt); pkt->chk = 0; while (next_part != IP_NULL && total_len < data_len) { pkt_len = next_part->end - next_part->start; pkt_len = IP_MIN(pkt_len, data_len - total_len); pkt->chk += ipcom_in_checksum_update(&next_part->data[next_part->start], pkt_len); next_part = next_part->next_part; total_len += pkt_len; } #ifdef IPCOM_USE_MIB2_FIXME IPCOM_MIB2(udpliteOutDatagrams++); if (sock->udplite_sum_cov != 0 && sock->udplite_sum_cov < ipcom_pkt_get_length(pkt)) IPCOM_MIB2(udpliteOutPartialCov++); #endif /* * Call the network layer send handler */ ops = (Ipnet_sock_udp_ops *) ipnet_sock_get_ops(sock); return ops->inet.network_send(sock, msg, pkt); } /* *=========================================================================== * ipnet_udp_nat_traversal *=========================================================================== * Description: Check if this packet should be processed by the NAT * traversal functionality. * Parameters: sock - The socket * pkt - The packet in question. * Returns: IP_FALSE; give to UDP socket. * IP_TRUE; Do NAT_T processing. * */ IP_STATIC Ip_bool ipnet_udp_encapsulation(Ipnet_socket *sock, Ipcom_pkt *pkt, int *nat_t) { /* 'pkt' is unused in some build configurations */ IPCOM_UNUSED_ARG(pkt); /* Did we have any NAT-T's? */ if (IP_UNLIKELY(sock->udp_encapsulation != IP_UDP_NAT_T_DISABLED)) { #if defined(IPNET_USE_TUNNEL) && defined(IPCOM_USE_INET) /* NAT-T processing? */ if (sock->ipcom.domain == IP_AF_INET && sock->udp_encapsulation == IP_UDP_NAT_T_RFC3519) { Ip_u8 *data; Ip_u8 nproto; /* Sanity; There MUST be atleast 4 bytes available */ if (ipcom_pkt_get_length(pkt) < 4) { /* Give to UDP socket */ return IP_FALSE; } /* Grab the start */ data = ipcom_pkt_get_data(pkt, 0); /* * NAT-T according to RFC3519 * IP HEADER * UDP HEADER * 4 byte NAT-T header; * TYPE(1 byte) := 4 * NH(1 byte) := 4, 47 or 55 * RESERVED(2 bytes) := 0 */ /* Must be 4 */ if (data[0] != 4) { /* Give to UDP socket */ return IP_FALSE; } /* *Verify that the tunnel protocol is correct */ nproto = data[1]; if (nproto == IP_IPPROTO_IPIP || nproto == IP_IPPROTO_GRE || nproto == IP_IPPROTO_MOBILE) { /* * Correct UDP RFC3519 encap; do process */ *nat_t = IP_UDP_NAT_T_RFC3519; return IP_TRUE; } /* Feed to socket */ return IP_FALSE; } #endif /* IPNET_USE_TUNNEL */ #if defined(IPIPSEC2) && defined(IPCOM_USE_INET) if (sock->ipcom.domain == IP_AF_INET && sock->udp_encapsulation == IP_UDP_NAT_T_RFC3948) { if ((pkt->end - pkt->start == 1) && (pkt->data[pkt->start] == 0xff)) { /* NAT-keepalive Packet; Give to socket */ return IP_FALSE; } if ((pkt->end - pkt->start > 4) && (IP_GET_32ON16(&pkt->data[pkt->start]) == 0)) { /* Non-ESP marker set; do give to socket - this is an IKE packet */ return IP_FALSE; } *nat_t = IP_UDP_NAT_T_RFC3948; return IP_TRUE; } #endif #ifdef IPNET_USE_TUNNEL /* NAT-T processing? */ if (sock->udp_encapsulation == IP_UDP_NAT_T_DSMIPV6) { /* Correct UDP RFC3519 encap; do process */ *nat_t = IP_UDP_NAT_T_DSMIPV6; return IP_TRUE; } #endif } return IP_FALSE; } /* *=========================================================================== * ipnet_udp_port_unreachable *=========================================================================== * Description: The destination UDP port did not have any listeners. * Parameters: dst - destination cache entry used by the UDP datagram * pkt - packet where pkt->start is the offset to the UDP * header. * Returns: * */ IP_STATIC void ipnet_udp_port_unreachable(Ipnet_dst_cache *dst, Ipcom_pkt *pkt) { #if defined(IPCOM_FORWARDER_NAE) IPCOM_UNUSED_ARG(dst); IPCOM_LOG2(DEBUG2, "ipnet_udp_port_unreachable() :: datagram (%s to %s) passed to master os", ipcom_inet_ntop(IP_AF_INET, &dst->flow_spec.from, ipnet->log_buf, sizeof(ipnet->log_buf)), ipcom_inet_ntop(IP_AF_INET, &dst->flow_spec.to, ipnet->log_buf + IP_INET_ADDRSTRLEN, sizeof(ipnet->log_buf)- IP_INET_ADDRSTRLEN)); ipcom_pkt_to_master_os(pkt); #else /* !defined(IPCOM_FORWARDER_NAE) */ int domain = ipnet_flow_spec_domain(&dst->flow_spec); #ifdef IPCOM_USE_INET if (domain == IP_AF_INET) ipnet_icmp4_send_port_unreachable(pkt); #endif #ifdef IPCOM_USE_INET6 if (domain == IP_AF_INET6) ipnet_icmp6_send_port_unreachable(pkt); #endif #endif /* !defined(IPCOM_FORWARDER_NAE) */ } /* *=========================================================================== * ipnet_udp_sock_rx *=========================================================================== * Description: Tries to find a matching socket and queue the packet * on that socket. * Parameters: dst - flow followed by this packet * pkt - UDP or UDP-Lite packet * socklookup - network domain specific lookup function * src_port - UDP source port * dst_port - UDP destination port * ip_proto - IP_IPPROTO_UDP or IP_IPPROTO_UDPLITE * Returns: * */ IP_STATIC void ipnet_udp_sock_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt, Ipnet_sock_lookup_f socklookup, Ip_u16 src_port, Ip_u16 dst_port, Ip_bool ip_proto) { int ret, nat_t; Ipnet_socket *sock; Ipcom_socket_eventcb event_cb; IPCOM_WV_DECLARE_VARS; if (dst->to_type != IPNET_ADDR_TYPE_UNICAST) { /* * Multicast/broadcast packets must be delivered to all * matching socket(s). ipnet_raw_input() has the required * logic for doing that. */ IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO, 1, 7, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV, ipnet_udp_sock_rx, IPCOM_WV_NETD_INFO_RECEIVE, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_input_multicast++); ret = ipnet_raw_input(pkt, IP_TRUE, ip_proto, &dst->flow_spec.from, src_port, &dst->flow_spec.to, dst_port, socklookup); if (ret < 0) IPCOM_MIB2(udpInErrors++); else { IPCOM_MIB2(udpInDatagrams++); IPCOM_MIB2_U64_INC(udpHCInDatagrams); } return; } sock = socklookup(dst->flow_spec.vr, ip_proto, &dst->flow_spec.to, dst_port, &dst->flow_spec.from, src_port); #if IPCOM_VR_MAX > 1 if (IP_UNLIKELY(sock == IP_NULL)) /* * No matching socket, try the wildcard VR group */ sock = socklookup(IPCOM_VR_ANY, ip_proto, &dst->flow_spec.to, dst_port, &dst->flow_spec.from, src_port); #endif if (IP_UNLIKELY(sock == IP_NULL)) { IPCOM_WV_SET_VARS(13, IPCOM_WV_NETD_NOPORT); IPNET_STATS(udp_input_sock_nomatch++); IPCOM_MIB2(udpNoPorts++); IPNET_STATS(udp_input_econnrefused++); ipnet_udp_port_unreachable(dst, pkt); goto errout; } #if defined(IPCOM_FORWARDER_NAE) if (!ipcom_nae_rate_limit_process(pkt)) { ipcom_pkt_free(pkt); goto errout; } #endif /* * Matching socket found. */ IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO, 1, 8, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV, ipnet_udp_sock_rx, IPCOM_WV_NETD_INFO_RECEIVE, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_input_sock_match++); pkt->fd = sock->ipcom.fd; if (IP_UNLIKELY(ip_proto == IP_IPPROTO_UDP && ipnet_udp_encapsulation(sock, pkt, &nat_t))) { /* * NAT traversal packet. Do not queue this on the socket. */ ipcom_pkt_set_info(pkt, IPNET_PKT_INFO_ENCAP_UDP, IPNET_UDP_HDR_SIZE, &pkt->data[pkt->tlstart]); pkt->start -= IPNET_UDP_HDR_SIZE; switch (nat_t) { #ifdef IPIPSEC2 case IP_UDP_NAT_T_RFC3948: ipnet_ipsec_rx(dst, pkt, IP_IPPROTO_UDP); break; #endif /* IPIPSEC2 */ #if defined(IPNET_USE_TUNNEL) && defined(IPCOM_USE_INET) case IP_UDP_NAT_T_RFC3519: ipnet_tunnel_ip4_rfc3519_rx(dst, pkt); break; #endif #ifdef IPNET_USE_TUNNEL case IP_UDP_NAT_T_DSMIPV6: ipnet_tunnel_dsmipv6_input(dst, pkt); break; #endif default: goto errout; } return; } /* * Add the packet to the receive queue of the matching socket. */ ret = ipnet_queue_received_packet(pkt, sock); if (ret < 0) { IPCOM_WV_SET_VARS(9, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_input_queue_pkt_err++); IPCOM_MIB2(udpInErrors++); goto errout; } IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO, 1, 10, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV, ipnet_udp_sock_rx, IPCOM_WV_NETD_INFO_RECEIVE, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_input_queue_pkt_ok++); event_cb = sock->ipcom.event_cb; if (IP_NULL != event_cb) event_cb(&sock->ipcom, pkt, IP_SOEVENT_CB_INPKT); IPCOM_MIB2(udpInDatagrams++); IPCOM_MIB2_U64_INC(udpHCInDatagrams); return; errout: IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING, 1, IPCOM_WV_VAR1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV, ipnet_udp_sock_rx, IPCOM_WV_VAR2, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); #ifndef IPCOM_FORWARDER_NAE ipcom_pkt_free(pkt); #endif } /* *=========================================================================== * ipnet_udp_rx *=========================================================================== * Description: Reception handler for packet to UDP sockets. * Parameters: dst - destination cache entry matching the packet. * pkt - The received packet, must be freed by the called if * this function fails. * socklookup - A pointer to a function that can return the socket * that is associated with this packet. * Returns: * */ IP_STATIC void ipnet_udp_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt, Ipnet_sock_lookup_f socklookup) { Ipnet_pkt_udp *udp_hdr; int ulen_h; Ip_u16 src_port; Ip_u16 dst_port; int len; IPCOM_WV_DECLARE_VARS; IPCOM_WV_MARKER_1 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_VERBOSE, 1, 3, IPCOM_WV_NETDEVENT_START, ipnet_udp_rx, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_input++); IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_UDP_INPUT); pkt->tlstart = pkt->start; len = ipcom_pkt_get_length(pkt); if (IP_UNLIKELY(len < IPNET_UDP_HDR_SIZE)) { IPCOM_WV_SET_VARS(4, IPCOM_WV_NETD_BADHLEN); IPNET_STATS(udp_input_hdr_trunc++); IPCOM_MIB2(udpInErrors++); goto errout; } udp_hdr = ipcom_pkt_pop_front(pkt, IPNET_UDP_HDR_SIZE); ulen_h = ip_ntohs(udp_hdr->ulen); src_port = ip_ntohs(udp_hdr->sport); dst_port = ip_ntohs(udp_hdr->dport); if (IP_UNLIKELY(len < ulen_h || ulen_h < IPNET_UDP_HDR_SIZE)) { IPCOM_WV_SET_VARS(5, IPCOM_WV_NETD_BADLEN); IPNET_STATS(udp_input_hdr_trunc++); IPCOM_MIB2(udpInErrors++); goto errout; } if (len > ulen_h) { ipcom_pkt_trim_tail(pkt, len - ulen_h); } if (udp_hdr->sum) { Ip_u16 chksum; /* * Verify the UDP checksum */ #ifdef IPCOM_USE_HW_CHECKSUM_RX if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_HW_CHECKSUM)) chksum = 0; else if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_TL_CHECKSUM)) chksum = ipcom_in_checksum_finish(pkt->chk); else #endif /* IPCOM_USE_HW_CHECKSUM_RX */ { pkt->chk += ipcom_in_checksum_update(udp_hdr, IPNET_UDP_HDR_SIZE); pkt->chk += ipcom_in_checksum_update_pkt(pkt, 0); chksum = ipcom_in_checksum_finish(pkt->chk); } if (IP_UNLIKELY(chksum != 0)) { /* * Wrong checksum */ IPCOM_WV_SET_VARS(6, IPCOM_WV_NETD_BADSUM); IPNET_STATS(udp_input_badchksum++); IPCOM_MIB2(udpInErrors++); goto errout; } } /* * else: UDP checksum disabled. */ ipnet_udp_sock_rx(dst, pkt, socklookup, src_port, dst_port, IP_IPPROTO_UDP); return; errout: IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING, 1, IPCOM_WV_VAR1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV, ipnet_udp_rx, IPCOM_WV_VAR2, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); ipcom_pkt_free(pkt); } /* *=========================================================================== * ipnet_udplite_rx *=========================================================================== * Description: Reception handler for packet to UDP-Lite sockets. * Parameters: dst - destination cache entry matching the packet. * pkt - The received packet, must be freed by the called if * this function fails. * socklookup - A pointer to a function that can return the socket * that is associated with this packet. * Returns: * */ IP_STATIC void ipnet_udplite_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt, Ipnet_sock_lookup_f socklookup) { Ipnet_pkt_udplite *udplite_hdr; Ip_u16 src_port; Ip_u16 dst_port; int sum_cov; int len; Ip_u16 chksum; IPCOM_WV_DECLARE_VARS; IPCOM_WV_MARKER_1 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_VERBOSE, 1, 3, IPCOM_WV_NETDEVENT_START, ipnet_udplite_rx, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); IPNET_STATS(udp_input++); IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_UDP_INPUT); len = ipcom_pkt_get_length(pkt); if (IP_UNLIKELY(len < IPNET_UDP_HDR_SIZE)) { IPCOM_WV_SET_VARS(4, IPCOM_WV_NETD_BADHLEN); goto errout; } udplite_hdr = ipcom_pkt_pop_front(pkt, IPNET_UDPLITE_HDR_SIZE); sum_cov = ip_ntohs(udplite_hdr->sum_cov); src_port = ip_ntohs(udplite_hdr->sport); dst_port = ip_ntohs(udplite_hdr->dport); if (IP_UNLIKELY((sum_cov > 0 && sum_cov < 8) || sum_cov > len)) { /* * The part converted by the checksum must be all octets * (sum_cov == 0) or something equal to or larger than the * size of the header (8 octets). */ IPCOM_WV_SET_VARS(5, IPCOM_WV_NETD_BADLEN); goto errout; } if (sum_cov == 0) sum_cov = len; /* * Verify the UDP-Lite checksum */ if (pkt->next_part == IP_NULL) { pkt->chk += ipcom_in_checksum_update(udplite_hdr, (Ip_size_t) sum_cov); } else /* udp checksum for scattered packet */ { Ipcom_pkt *next_part = pkt; int part_len = 0; int total_len = 0; pkt->chk += ipcom_in_checksum_update(udplite_hdr, IPNET_UDPLITE_HDR_SIZE); total_len += IPNET_UDPLITE_HDR_SIZE; while ((next_part != IP_NULL) && (total_len < sum_cov)) { part_len = next_part->end - next_part->start; if (total_len + part_len > sum_cov) { part_len = sum_cov - total_len; } pkt->chk += ipcom_in_checksum_update(next_part->data+next_part->start, (Ip_size_t)part_len); total_len += part_len; next_part = next_part->next_part; } } chksum = ipcom_in_checksum_finish(pkt->chk); if (IP_UNLIKELY(chksum != 0)) { /* * Wrong checksum */ IPCOM_WV_SET_VARS(6, IPCOM_WV_NETD_BADSUM); IPNET_STATS(udp_input_badchksum++); IPCOM_MIB2(udpInErrors++); goto errout; } ipnet_udp_sock_rx(dst, pkt, socklookup, src_port, dst_port, IP_IPPROTO_UDPLITE); return; errout: IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING, 1, IPCOM_WV_VAR1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV, ipnet_udplite_rx, IPCOM_WV_VAR2, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4); ipcom_pkt_free(pkt); } #ifdef IPCOM_USE_INET /* *=========================================================================== * ipnet_udp_ip4_rx *=========================================================================== * Description: Input handler for IPv4 UDP datagrams. Calculates the * pseudo header checkum and calls the generic input handler * Parameters: dst - destination cache entry used by the UDP datagram * pkt - UDP datagram * Returns: * */ IP_STATIC void ipnet_udp_ip4_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt) { #ifdef IPCOM_USE_HW_CHECKSUM_RX if (IP_UNLIKELY(IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_HW_CHECKSUM))) #endif pkt->chk += ipnet_ip4_pseudo_header_checksum_update(&dst->flow_spec.from.in, &dst->flow_spec.to.in, IP_IPPROTO_UDP, (Ip_u16)ipcom_pkt_get_length(pkt)); ipnet_udp_rx(dst, pkt, (Ipnet_sock_lookup_f) ipnet_sock_ip4_lookup); } /* *=========================================================================== * ipnet_udplite_ip4_rx *=========================================================================== * Description: Input handler for IPv4 UDP-Lite datagrams. Calculates the * pseudo header checkum and calls the generic input handler * Parameters: dst - destination cache entry used by the UDP-Lite datagram * pkt - UDP-Lite datagram * Returns: * */ IP_STATIC void ipnet_udplite_ip4_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt) { /* * RFC3828, chapter 3.2 * The UDP-Lite pseudo header is always calculated as if the whole * packet was covered by the checksum. */ pkt->chk += ipnet_ip4_pseudo_header_checksum_update(&dst->flow_spec.from.in, &dst->flow_spec.to.in, IP_IPPROTO_UDPLITE, (Ip_u16)ipcom_pkt_get_length(pkt)); ipnet_udplite_rx(dst, pkt, (Ipnet_sock_lookup_f) ipnet_sock_ip4_lookup); } #endif #ifdef IPCOM_USE_INET6 /* *=========================================================================== * ipnet_udp_ip6_rx *=========================================================================== * Description: Input handler for IPv6 UDP datagrams. Calculates the * pseudo header checkum and calls the generic input handler * Parameters: dst - destination cache entry used by the UDP datagram * pkt - UDP datagram * Returns: * */ IP_STATIC void ipnet_udp_ip6_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt) { pkt->chk += ipnet_ip6_pseudo_header_checksum_update(&dst->flow_spec.from.in6, &dst->flow_spec.to.in6, IP_IPPROTO_UDP, ipcom_pkt_get_length(pkt)); ipnet_udp_rx(dst, pkt, (Ipnet_sock_lookup_f) ipnet_sock_ip6_lookup); } /* *=========================================================================== * ipnet_udplite_ip6_rx *=========================================================================== * Description: Input handler for IPv6 UDP-Lite datagrams. Calculates the * pseudo header checkum and calls the generic input handler * Parameters: dst - destination cache entry used by the UDP-Lite datagram * pkt - UDP-Lite datagram * socklookup - domain specific socket lookup function. * Returns: * */ IP_STATIC void ipnet_udplite_ip6_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt) { /* * RFC3828, chapter 3.2 * The UDP-Lite pseudo header is always calculated as if the whole * packet was covered by the checksum. */ pkt->chk += ipnet_ip6_pseudo_header_checksum_update(&dst->flow_spec.from.in6, &dst->flow_spec.to.in6, IP_IPPROTO_UDPLITE, ipcom_pkt_get_length(pkt)); ipnet_udplite_rx(dst, pkt, (Ipnet_sock_lookup_f) ipnet_sock_ip6_lookup); } #endif /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_sock_udp_register *=========================================================================== * Description: Registers the UDP protocol for IPv4 and/or IPv6. * Parameters: * Returns: 0 = success, <0 = error code. * */ IP_GLOBAL int ipnet_sock_udp_register(void) { Ipnet_sock_udp_ops *ops; int domains[] = { #ifdef IPCOM_USE_INET IP_AF_INET, #endif #ifdef IPCOM_USE_INET6 IP_AF_INET6, #endif -1 }; int protos[] = { 0, IP_IPPROTO_UDP, IP_IPPROTO_UDPLITE, -1 }; int *proto; int *domain; int ret; for (proto = protos; *proto >= 0; proto++) { for (domain = domains; *domain >= 0; domain++) { ops = ipcom_calloc(1, sizeof(*ops)); if (ops == IP_NULL) { IPCOM_LOG0(CRIT, "Could not register the UDP protocol, out of memory"); IP_PANIC(); return -IP_ERRNO_ENOMEM; } switch (*domain) { #ifdef IPCOM_USE_INET case IP_AF_INET: ipnet_sock_ip4_get_ops(&ops->inet); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: ipnet_sock_ip6_get_ops(&ops->inet); break; #endif default: break; } ops->network_bind = ops->inet.network_bind; ops->inet.network_bind = ipnet_sock_udp_bind; ops->network_init = ops->inet.sock.init; ops->network_connect = ops->inet.sock.connect; ops->inet.sock.init = ipnet_sock_udp_init; ops->inet.sock.connect = ipnet_sock_udp_connect; ops->inet.sock.type = IP_SOCK_DGRAM; ops->inet.sock.proto = *proto; ops->inet.sock.allow_async_send = IP_TRUE; ops->inet.sock.usr_recv = ipnet_usr_sock_recvmsg; if (*proto == IP_IPPROTO_UDPLITE) ops->inet.sock.send = ipnet_sock_udplite_tx; else ops->inet.sock.send = ipnet_sock_udp_tx; ops->inet.sock.hdr_space += IPNET_UDP_HDR_SIZE; ret = ipnet_sock_register_ops(&ops->inet.sock); if (ret < 0) { ipcom_free (ops); return ret; } } } /* * Register an IP protocol handler for UDP into the network layer(s) */ #ifdef IPCOM_USE_INET ipnet_ip4_reg_transport_layer(IP_IPPROTO_UDP, ipnet_udp_ip4_rx); ipnet_ip4_reg_transport_layer(IP_IPPROTO_UDPLITE, ipnet_udplite_ip4_rx); #endif #ifdef IPCOM_USE_INET6 ipnet_ip6_reg_transport_layer(IP_IPPROTO_UDP, ipnet_udp_ip6_rx); ipnet_ip6_reg_transport_layer(IP_IPPROTO_UDPLITE, ipnet_udplite_ip6_rx); #endif return 0; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */