/*
- * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <net/necp.h>
#endif /* NECP */
+#if FLOW_DIVERT
+#include <netinet/flow_divert.h>
+#endif /* FLOW_DIVERT */
+
#define DBG_LAYER_IN_BEG NETDBG_CODE(DBG_NETUDP, 0)
#define DBG_LAYER_IN_END NETDBG_CODE(DBG_NETUDP, 2)
#define DBG_LAYER_OUT_BEG NETDBG_CODE(DBG_NETUDP, 1)
static int udp_bind(struct socket *, struct sockaddr *, struct proc *);
static int udp_connect(struct socket *, struct sockaddr *, struct proc *);
static int udp_connectx(struct socket *, struct sockaddr_list **,
- struct sockaddr_list **, struct proc *, uint32_t, associd_t, connid_t *,
- uint32_t, void *, uint32_t);
+ struct sockaddr_list **, struct proc *, uint32_t, sae_associd_t,
+ sae_connid_t *, uint32_t, void *, uint32_t, struct uio *, user_ssize_t *);
static int udp_detach(struct socket *);
static int udp_disconnect(struct socket *);
-static int udp_disconnectx(struct socket *, associd_t, connid_t);
+static int udp_disconnectx(struct socket *, sae_associd_t, sae_connid_t);
static int udp_send(struct socket *, int, struct mbuf *, struct sockaddr *,
struct mbuf *, struct proc *);
static void udp_append(struct inpcb *, struct ip *, struct mbuf *, int,
.pru_sockaddr = in_getsockaddr,
.pru_sosend = sosend,
.pru_soreceive = soreceive,
+ .pru_soreceive_list = soreceive_list,
};
void
#if NECP
skipit = 0;
- if (!necp_socket_is_allowed_to_send_recv_v4(inp, uh->uh_dport, uh->uh_sport, &ip->ip_dst, &ip->ip_src, ifp, NULL)) {
+ if (!necp_socket_is_allowed_to_send_recv_v4(inp,
+ uh->uh_dport, uh->uh_sport, &ip->ip_dst,
+ &ip->ip_src, ifp, NULL, NULL)) {
/* do not inject data to pcb */
skipit = 1;
}
goto bad;
}
#if NECP
- if (!necp_socket_is_allowed_to_send_recv_v4(inp, uh->uh_dport, uh->uh_sport, &ip->ip_dst, &ip->ip_src, ifp, NULL)) {
+ if (!necp_socket_is_allowed_to_send_recv_v4(inp, uh->uh_dport,
+ uh->uh_sport, &ip->ip_dst, &ip->ip_src, ifp, NULL, NULL)) {
udp_unlock(inp->inp_socket, 1, 0);
IF_UDP_STATINC(ifp, badipsec);
goto bad;
else
inp->inp_flags &= ~INP_UDP_NOCKSUM;
break;
+ case UDP_KEEPALIVE_OFFLOAD:
+ {
+ struct udp_keepalive_offload ka;
+ /*
+ * If the socket is not connected, the stack will
+ * not know the destination address to put in the
+ * keepalive datagram. Return an error now instead
+ * of failing later.
+ */
+ if (!(so->so_state & SS_ISCONNECTED)) {
+ error = EINVAL;
+ break;
+ }
+ if (sopt->sopt_valsize != sizeof(ka)) {
+ error = EINVAL;
+ break;
+ }
+ if ((error = sooptcopyin(sopt, &ka, sizeof(ka),
+ sizeof(ka))) != 0)
+ break;
+
+ /* application should specify the type */
+ if (ka.ka_type == 0)
+ return (EINVAL);
+
+ if (ka.ka_interval == 0) {
+ /*
+ * if interval is 0, disable the offload
+ * mechanism
+ */
+ if (inp->inp_keepalive_data != NULL)
+ FREE(inp->inp_keepalive_data,
+ M_TEMP);
+ inp->inp_keepalive_data = NULL;
+ inp->inp_keepalive_datalen = 0;
+ inp->inp_keepalive_interval = 0;
+ inp->inp_keepalive_type = 0;
+ inp->inp_flags2 &= ~INP2_KEEPALIVE_OFFLOAD;
+ } else {
+ if (inp->inp_keepalive_data != NULL) {
+ FREE(inp->inp_keepalive_data,
+ M_TEMP);
+ inp->inp_keepalive_data = NULL;
+ }
+ inp->inp_keepalive_datalen = min(
+ ka.ka_data_len,
+ UDP_KEEPALIVE_OFFLOAD_DATA_SIZE);
+ if (inp->inp_keepalive_datalen > 0) {
+ MALLOC(inp->inp_keepalive_data,
+ u_int8_t *,
+ inp->inp_keepalive_datalen,
+ M_TEMP, M_WAITOK);
+ if (inp->inp_keepalive_data == NULL) {
+ inp->inp_keepalive_datalen = 0;
+ error = ENOMEM;
+ break;
+ }
+ bcopy(ka.ka_data,
+ inp->inp_keepalive_data,
+ inp->inp_keepalive_datalen);
+ } else {
+ inp->inp_keepalive_datalen = 0;
+ }
+ inp->inp_keepalive_interval =
+ min(UDP_KEEPALIVE_INTERVAL_MAX_SECONDS,
+ ka.ka_interval);
+ inp->inp_keepalive_type = ka.ka_type;
+ inp->inp_flags2 |= INP2_KEEPALIVE_OFFLOAD;
+ }
+ break;
+ }
case SO_FLUSH:
if ((error = sooptcopyin(sopt, &optval, sizeof (optval),
sizeof (optval))) != 0)
* Calculate data length and get a mbuf
* for UDP and IP headers.
*/
- M_PREPEND(m, sizeof (struct udpiphdr), M_DONTWAIT);
+ M_PREPEND(m, sizeof (struct udpiphdr), M_DONTWAIT, 1);
if (m == 0) {
error = ENOBUFS;
goto abort;
KERNEL_DEBUG(DBG_LAYER_OUT_END, ui->ui_dport, ui->ui_sport,
ui->ui_src.s_addr, ui->ui_dst.s_addr, ui->ui_ulen);
-
+
#if NECP
{
necp_kernel_policy_id policy_id;
- if (!necp_socket_is_allowed_to_send_recv_v4(inp, lport, fport, &laddr, &faddr, NULL, &policy_id)) {
+ u_int32_t route_rule_id;
+ if (!necp_socket_is_allowed_to_send_recv_v4(inp, lport, fport,
+ &laddr, &faddr, NULL, &policy_id, &route_rule_id)) {
error = EHOSTUNREACH;
goto abort;
}
- necp_mark_packet_from_socket(m, inp, policy_id);
+ necp_mark_packet_from_socket(m, inp, policy_id, route_rule_id);
}
#endif /* NECP */
-
+
#if IPSEC
if (inp->inp_sp != NULL && ipsec_setsocket(m, inp->inp_socket) != 0) {
error = ENOBUFS;
* If the destination route is unicast, update outifp with
* that of the route interface used by IP.
*/
- if (rt != NULL && (outifp = rt->rt_ifp) != inp->inp_last_outifp)
- inp->inp_last_outifp = outifp; /* no reference needed */
+ if (rt != NULL &&
+ (outifp = rt->rt_ifp) != inp->inp_last_outifp) {
+ inp->inp_last_outifp = outifp; /* no reference needed */
+
+ so->so_pktheadroom = P2ROUNDUP(
+ sizeof(struct udphdr) +
+ sizeof(struct ip) +
+ ifnet_hdrlen(outifp) +
+ ifnet_packetpreamblelen(outifp),
+ sizeof(u_int32_t));
+ }
} else {
ROUTE_RELEASE(&inp->inp_route);
}
return (EAFNOSUPPORT);
inp = sotoinpcb(so);
- if (inp == NULL
-#if NECP
- || (necp_socket_should_use_flow_divert(inp))
-#endif /* NECP */
- )
- return (inp == NULL ? EINVAL : EPROTOTYPE);
+ if (inp == NULL)
+ return (EINVAL);
error = in_pcbbind(inp, nam, p);
return (error);
}
int error;
inp = sotoinpcb(so);
- if (inp == NULL
-#if NECP
- || (necp_socket_should_use_flow_divert(inp))
-#endif /* NECP */
- )
- return (inp == NULL ? EINVAL : EPROTOTYPE);
+ if (inp == NULL)
+ return (EINVAL);
if (inp->inp_faddr.s_addr != INADDR_ANY)
return (EISCONN);
+
+#if NECP
+#if FLOW_DIVERT
+ if (necp_socket_should_use_flow_divert(inp)) {
+ uint32_t fd_ctl_unit =
+ necp_socket_get_flow_divert_control_unit(inp);
+ if (fd_ctl_unit > 0) {
+ error = flow_divert_pcb_init(so, fd_ctl_unit);
+ if (error == 0) {
+ error = flow_divert_connect_out(so, nam, p);
+ }
+ } else {
+ error = ENETDOWN;
+ }
+ return (error);
+ }
+#endif /* FLOW_DIVERT */
+#endif /* NECP */
+
error = in_pcbconnect(inp, nam, p, IFSCOPE_NONE, NULL);
if (error == 0) {
soisconnected(so);
int
udp_connectx_common(struct socket *so, int af,
struct sockaddr_list **src_sl, struct sockaddr_list **dst_sl,
- struct proc *p, uint32_t ifscope, associd_t aid, connid_t *pcid,
- uint32_t flags, void *arg, uint32_t arglen)
+ struct proc *p, uint32_t ifscope, sae_associd_t aid, sae_connid_t *pcid,
+ uint32_t flags, void *arg, uint32_t arglen,
+ struct uio *uio, user_ssize_t *bytes_written)
{
#pragma unused(aid, flags, arg, arglen)
struct sockaddr_entry *src_se = NULL, *dst_se = NULL;
struct inpcb *inp = sotoinpcb(so);
int error;
+ user_ssize_t datalen = 0;
if (inp == NULL)
return (EINVAL);
VERIFY(src_se == NULL || src_se->se_addr->sa_family == af);
#if NECP
- inp_update_necp_policy(inp, src_se ? src_se->se_addr : NULL, dst_se ? dst_se->se_addr : NULL, ifscope);
+ inp_update_necp_policy(inp, src_se ? src_se->se_addr : NULL,
+ dst_se ? dst_se->se_addr : NULL, ifscope);
#endif /* NECP */
/* bind socket to the specified interface, if requested */
/* NOTREACHED */
}
+ if (error != 0)
+ return (error);
+
+ /*
+ * If there is data, copy it. DATA_IDEMPOTENT is ignored.
+ * CONNECT_RESUME_ON_READ_WRITE is ignored.
+ */
+ if (uio != NULL) {
+ socket_unlock(so, 0);
+
+ VERIFY(bytes_written != NULL);
+
+ datalen = uio_resid(uio);
+ error = so->so_proto->pr_usrreqs->pru_sosend(so, NULL,
+ (uio_t)uio, NULL, NULL, 0);
+ socket_lock(so, 0);
+
+ /* If error returned is EMSGSIZE, for example, disconnect */
+ if (error == 0 || error == EWOULDBLOCK)
+ *bytes_written = datalen - uio_resid(uio);
+ else
+ (void)so->so_proto->pr_usrreqs->pru_disconnectx(so,
+ SAE_ASSOCID_ANY, SAE_CONNID_ANY);
+ /*
+ * mask the EWOULDBLOCK error so that the caller
+ * knows that atleast the connect was successful.
+ */
+ if (error == EWOULDBLOCK)
+ error = 0;
+ }
+
if (error == 0 && pcid != NULL)
- *pcid = 1; /* there is only 1 connection for a UDP */
+ *pcid = 1; /* there is only 1 connection for UDP */
return (error);
}
static int
udp_connectx(struct socket *so, struct sockaddr_list **src_sl,
struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
- associd_t aid, connid_t *pcid, uint32_t flags, void *arg,
- uint32_t arglen)
+ sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
+ uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written)
{
return (udp_connectx_common(so, AF_INET, src_sl, dst_sl,
- p, ifscope, aid, pcid, flags, arg, arglen));
+ p, ifscope, aid, pcid, flags, arg, arglen, uio, bytes_written));
}
static int
}
static int
-udp_disconnectx(struct socket *so, associd_t aid, connid_t cid)
+udp_disconnectx(struct socket *so, sae_associd_t aid, sae_connid_t cid)
{
#pragma unused(cid)
- if (aid != ASSOCID_ANY && aid != ASSOCID_ALL)
+ if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL)
return (EINVAL);
return (udp_disconnect(so));
udp_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr *addr, struct mbuf *control, struct proc *p)
{
+#ifndef FLOW_DIVERT
#pragma unused(flags)
+#endif /* !(FLOW_DIVERT) */
struct inpcb *inp;
inp = sotoinpcb(so);
- if (inp == NULL
-#if NECP
- || (necp_socket_should_use_flow_divert(inp))
-#endif /* NECP */
- ) {
+ if (inp == NULL) {
if (m != NULL)
m_freem(m);
if (control != NULL)
m_freem(control);
- return (inp == NULL ? EINVAL : EPROTOTYPE);
+ return (EINVAL);
}
+#if NECP
+#if FLOW_DIVERT
+ if (necp_socket_should_use_flow_divert(inp)) {
+ /* Implicit connect */
+ return (flow_divert_implicit_data_out(so, flags, m, addr, control, p));
+ }
+#endif /* FLOW_DIVERT */
+#endif /* NECP */
+
return (udp_output(inp, m, addr, control, p));
}
return (0);
}
+
+extern void
+udp_fill_keepalive_offload_frames(ifnet_t ifp,
+ struct ifnet_keepalive_offload_frame *frames_array,
+ u_int32_t frames_array_count, size_t frame_data_offset,
+ u_int32_t *used_frames_count);
+
+void
+udp_fill_keepalive_offload_frames(ifnet_t ifp,
+ struct ifnet_keepalive_offload_frame *frames_array,
+ u_int32_t frames_array_count, size_t frame_data_offset,
+ u_int32_t *used_frames_count)
+{
+ struct inpcb *inp;
+ inp_gen_t gencnt;
+ u_int32_t frame_index = *used_frames_count;
+
+ if (ifp == NULL || frames_array == NULL ||
+ frames_array_count == 0 ||
+ frame_index >= frames_array_count ||
+ frame_data_offset >= IFNET_KEEPALIVE_OFFLOAD_FRAME_DATA_SIZE)
+ return;
+
+ lck_rw_lock_shared(udbinfo.ipi_lock);
+ gencnt = udbinfo.ipi_gencnt;
+ LIST_FOREACH(inp, udbinfo.ipi_listhead, inp_list) {
+ struct socket *so;
+ u_int8_t *data;
+ struct ifnet_keepalive_offload_frame *frame;
+ struct mbuf *m = NULL;
+
+ if (frame_index >= frames_array_count)
+ break;
+
+ if (inp->inp_gencnt > gencnt ||
+ inp->inp_state == INPCB_STATE_DEAD)
+ continue;
+
+ if ((so = inp->inp_socket) == NULL ||
+ (so->so_state & SS_DEFUNCT))
+ continue;
+ /*
+ * check for keepalive offload flag without socket
+ * lock to avoid a deadlock
+ */
+ if (!(inp->inp_flags2 & INP2_KEEPALIVE_OFFLOAD)) {
+ continue;
+ }
+
+ udp_lock(so, 1, 0);
+ if (!(inp->inp_vflag & (INP_IPV4 | INP_IPV6))) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if ((inp->inp_vflag & INP_IPV4) &&
+ (inp->inp_laddr.s_addr == INADDR_ANY ||
+ inp->inp_faddr.s_addr == INADDR_ANY)) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if ((inp->inp_vflag & INP_IPV6) &&
+ (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) ||
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if (inp->inp_lport == 0 || inp->inp_fport == 0) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if (inp->inp_last_outifp == NULL ||
+ inp->inp_last_outifp->if_index != ifp->if_index) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if ((inp->inp_vflag & INP_IPV4)) {
+ if ((frame_data_offset + sizeof(struct udpiphdr) +
+ inp->inp_keepalive_datalen) >
+ IFNET_KEEPALIVE_OFFLOAD_FRAME_DATA_SIZE) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if ((sizeof(struct udpiphdr) +
+ inp->inp_keepalive_datalen) > _MHLEN) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ } else {
+ if ((frame_data_offset + sizeof(struct ip6_hdr) +
+ sizeof(struct udphdr) +
+ inp->inp_keepalive_datalen) >
+ IFNET_KEEPALIVE_OFFLOAD_FRAME_DATA_SIZE) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ if ((sizeof(struct ip6_hdr) + sizeof(struct udphdr) +
+ inp->inp_keepalive_datalen) > _MHLEN) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ }
+ MGETHDR(m, M_WAIT, MT_HEADER);
+ if (m == NULL) {
+ udp_unlock(so, 1, 0);
+ continue;
+ }
+ /*
+ * This inp has all the information that is needed to
+ * generate an offload frame.
+ */
+ if (inp->inp_vflag & INP_IPV4) {
+ struct ip *ip;
+ struct udphdr *udp;
+
+ frame = &frames_array[frame_index];
+ frame->length = frame_data_offset +
+ sizeof(struct udpiphdr) +
+ inp->inp_keepalive_datalen;
+ frame->ether_type =
+ IFNET_KEEPALIVE_OFFLOAD_FRAME_ETHERTYPE_IPV4;
+ frame->interval = inp->inp_keepalive_interval;
+ switch (inp->inp_keepalive_type) {
+ case UDP_KEEPALIVE_OFFLOAD_TYPE_AIRPLAY:
+ frame->type =
+ IFNET_KEEPALIVE_OFFLOAD_FRAME_AIRPLAY;
+ break;
+ default:
+ break;
+ }
+ data = mtod(m, u_int8_t *);
+ bzero(data, sizeof(struct udpiphdr));
+ ip = (__typeof__(ip))(void *)data;
+ udp = (__typeof__(udp))(void *) (data +
+ sizeof(struct ip));
+ m->m_len = sizeof(struct udpiphdr);
+ data = data + sizeof(struct udpiphdr);
+ if (inp->inp_keepalive_datalen > 0 &&
+ inp->inp_keepalive_data != NULL) {
+ bcopy(inp->inp_keepalive_data, data,
+ inp->inp_keepalive_datalen);
+ m->m_len += inp->inp_keepalive_datalen;
+ }
+ m->m_pkthdr.len = m->m_len;
+
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = (sizeof(struct ip) >> 2);
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_len = htons(sizeof(struct udpiphdr) +
+ (u_short)inp->inp_keepalive_datalen);
+ ip->ip_ttl = inp->inp_ip_ttl;
+ ip->ip_tos = inp->inp_ip_tos;
+ ip->ip_src = inp->inp_laddr;
+ ip->ip_dst = inp->inp_faddr;
+ ip->ip_sum = in_cksum_hdr_opt(ip);
+
+ udp->uh_sport = inp->inp_lport;
+ udp->uh_dport = inp->inp_fport;
+ udp->uh_ulen = htons(sizeof(struct udphdr) +
+ (u_short)inp->inp_keepalive_datalen);
+
+ if (!(inp->inp_flags & INP_UDP_NOCKSUM)) {
+ udp->uh_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr,
+ htons(sizeof(struct udphdr) +
+ (u_short)inp->inp_keepalive_datalen +
+ IPPROTO_UDP));
+ m->m_pkthdr.csum_flags = CSUM_UDP;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr,
+ uh_sum);
+ }
+ m->m_pkthdr.pkt_proto = IPPROTO_UDP;
+ in_delayed_cksum(m);
+ bcopy(m->m_data, frame->data + frame_data_offset,
+ m->m_len);
+ } else {
+ struct ip6_hdr *ip6;
+ struct udphdr *udp6;
+
+ VERIFY(inp->inp_vflag & INP_IPV6);
+ frame = &frames_array[frame_index];
+ frame->length = frame_data_offset +
+ sizeof(struct ip6_hdr) +
+ sizeof(struct udphdr) +
+ inp->inp_keepalive_datalen;
+ frame->ether_type =
+ IFNET_KEEPALIVE_OFFLOAD_FRAME_ETHERTYPE_IPV6;
+ frame->interval = inp->inp_keepalive_interval;
+ switch (inp->inp_keepalive_type) {
+ case UDP_KEEPALIVE_OFFLOAD_TYPE_AIRPLAY:
+ frame->type =
+ IFNET_KEEPALIVE_OFFLOAD_FRAME_AIRPLAY;
+ break;
+ default:
+ break;
+ }
+ data = mtod(m, u_int8_t *);
+ bzero(data, sizeof(struct ip6_hdr) + sizeof(struct udphdr));
+ ip6 = (__typeof__(ip6))(void *)data;
+ udp6 = (__typeof__(udp6))(void *)(data +
+ sizeof(struct ip6_hdr));
+ m->m_len = sizeof(struct ip6_hdr) +
+ sizeof(struct udphdr);
+ data = data + (sizeof(struct ip6_hdr) +
+ sizeof(struct udphdr));
+ if (inp->inp_keepalive_datalen > 0 &&
+ inp->inp_keepalive_data != NULL) {
+ bcopy(inp->inp_keepalive_data, data,
+ inp->inp_keepalive_datalen);
+ m->m_len += inp->inp_keepalive_datalen;
+ }
+ m->m_pkthdr.len = m->m_len;
+ ip6->ip6_flow = inp->inp_flow & IPV6_FLOWINFO_MASK;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ ip6->ip6_nxt = IPPROTO_UDP;
+ ip6->ip6_hlim = ip6_defhlim;
+ ip6->ip6_plen = htons(sizeof(struct udphdr) +
+ (u_short)inp->inp_keepalive_datalen);
+ ip6->ip6_src = inp->in6p_laddr;
+ if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
+ ip6->ip6_src.s6_addr16[1] = 0;
+
+ ip6->ip6_dst = inp->in6p_faddr;
+ if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] = 0;
+
+ udp6->uh_sport = inp->in6p_lport;
+ udp6->uh_dport = inp->in6p_fport;
+ udp6->uh_ulen = htons(sizeof(struct udphdr) +
+ (u_short)inp->inp_keepalive_datalen);
+ if (!(inp->inp_flags & INP_UDP_NOCKSUM)) {
+ udp6->uh_sum = in6_pseudo(&ip6->ip6_src,
+ &ip6->ip6_dst,
+ htonl(sizeof(struct udphdr) +
+ (u_short)inp->inp_keepalive_datalen +
+ IPPROTO_UDP));
+ m->m_pkthdr.csum_flags = CSUM_UDPIPV6;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr,
+ uh_sum);
+ }
+ m->m_pkthdr.pkt_proto = IPPROTO_UDP;
+ in6_delayed_cksum(m);
+ bcopy(m->m_data, frame->data + frame_data_offset,
+ m->m_len);
+ }
+ if (m != NULL) {
+ m_freem(m);
+ m = NULL;
+ }
+ frame_index++;
+ udp_unlock(so, 1, 0);
+ }
+ lck_rw_done(udbinfo.ipi_lock);
+ *used_frames_count = frame_index;
+}