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

1177 lines
37 KiB
C

/*
******************************************************************************
* 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 <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 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 <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_inet.h>
#include <ipcom_syslog.h>
#include <ipcom_type.h>
#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 <ipcom_windview.h>
#endif
#ifdef IPCOM_USE_FORWARDER
#include <ipcom_forwarder.h>
#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 <tunnel endpoint - tunnel endpoint>
* 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
****************************************************************************
*/