gems-kernel/source/THIRDPARTY/xnu/bsd/net/pf_pbuf.c
2024-06-03 11:29:39 -05:00

441 lines
10 KiB
C

/*
* Copyright (c) 2016-2022 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <sys/cdefs.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mcache.h>
#include <kern/kern_types.h>
#include <net/pf_pbuf.h>
#include <net/pfvar.h>
#include <netinet/in.h>
void
pbuf_init_mbuf(pbuf_t *pbuf, struct mbuf *m, struct ifnet *ifp)
{
VERIFY((m->m_flags & M_PKTHDR) != 0);
pbuf->pb_type = PBUF_TYPE_MBUF;
pbuf->pb_mbuf = m;
pbuf->pb_ifp = ifp;
pbuf->pb_next = NULL;
pbuf_sync(pbuf);
}
void
pbuf_init_memory(pbuf_t *pbuf, const struct pbuf_memory *mp, struct ifnet *ifp)
{
pbuf->pb_type = PBUF_TYPE_MEMORY;
pbuf->pb_memory = *mp;
pbuf->pb_ifp = ifp;
pbuf->pb_next = NULL;
pbuf_sync(pbuf);
}
void
pbuf_destroy(pbuf_t *pbuf)
{
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
if (pbuf->pb_mbuf) {
m_freem(pbuf->pb_mbuf);
pbuf->pb_mbuf = NULL;
}
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
VERIFY(pbuf->pb_memory.pm_buffer != NULL);
(void) (pbuf->pb_memory.pm_action)(&pbuf->pb_memory,
PBUF_ACTION_DESTROY);
} else {
VERIFY(pbuf->pb_type == PBUF_TYPE_ZOMBIE);
}
memset(pbuf, 0, sizeof(*pbuf));
}
void
pbuf_sync(pbuf_t *pbuf)
{
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
struct mbuf *m = pbuf->pb_mbuf;
VERIFY(m != NULL);
VERIFY(m->m_flags & M_PKTHDR);
pbuf->pb_data = mtod(m, void *);
pbuf->pb_packet_len = m->m_pkthdr.len;
pbuf->pb_contig_len = m->m_len;
pbuf->pb_csum_flags = &m->m_pkthdr.csum_flags;
pbuf->pb_csum_data = &m->m_pkthdr.csum_data;
pbuf->pb_proto = &m->m_pkthdr.pkt_proto;
pbuf->pb_flowsrc = &m->m_pkthdr.pkt_flowsrc;
pbuf->pb_flowid = &m->m_pkthdr.pkt_flowid;
pbuf->pb_flow_gencnt = &m->m_pkthdr.comp_gencnt;
pbuf->pb_flags = &m->m_pkthdr.pkt_flags;
pbuf->pb_pftag = m_pftag(m);
pbuf->pb_pf_fragtag = pf_find_fragment_tag(m);
ASSERT((pbuf->pb_pf_fragtag == NULL) ||
(pbuf->pb_pftag->pftag_flags & PF_TAG_REASSEMBLED));
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
struct pbuf_memory *nm = &pbuf->pb_memory;
VERIFY(nm->pm_buffer != NULL);
VERIFY(nm->pm_buffer_len != 0);
VERIFY(nm->pm_len != 0);
VERIFY(nm->pm_len <= nm->pm_buffer_len);
VERIFY(nm->pm_offset < nm->pm_len);
pbuf->pb_data = &nm->pm_buffer[nm->pm_offset];
pbuf->pb_packet_len = nm->pm_len;
pbuf->pb_contig_len = nm->pm_len;
pbuf->pb_csum_flags = &nm->pm_csum_flags;
pbuf->pb_csum_data = &nm->pm_csum_data;
pbuf->pb_proto = &nm->pm_proto;
pbuf->pb_flowsrc = &nm->pm_flowsrc;
pbuf->pb_flowid = &nm->pm_flowid;
pbuf->pb_flow_gencnt = &nm->pm_flow_gencnt;
pbuf->pb_flags = &nm->pm_flags;
pbuf->pb_pftag = &nm->pm_pftag;
pbuf->pb_pf_fragtag = &nm->pm_pf_fragtag;
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
}
struct mbuf *
pbuf_to_mbuf(pbuf_t *pbuf, boolean_t release_ptr)
{
struct mbuf *m = NULL;
pbuf_sync(pbuf);
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
m = pbuf->pb_mbuf;
if (release_ptr) {
pbuf->pb_mbuf = NULL;
}
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
boolean_t fragtag = FALSE;
if (pbuf->pb_packet_len > (u_int)MHLEN) {
if (pbuf->pb_packet_len > (u_int)MCLBYTES) {
printf("%s: packet too big for cluster (%u)\n",
__func__, pbuf->pb_packet_len);
return NULL;
}
m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
} else {
m = m_gethdr(M_DONTWAIT, MT_DATA);
}
if (m == NULL) {
goto done;
}
m_copyback(m, 0, pbuf->pb_packet_len, pbuf->pb_data);
m->m_pkthdr.csum_flags = *pbuf->pb_csum_flags;
m->m_pkthdr.csum_data = *pbuf->pb_csum_data;
m->m_pkthdr.pkt_proto = *pbuf->pb_proto;
m->m_pkthdr.pkt_flowsrc = *pbuf->pb_flowsrc;
m->m_pkthdr.pkt_flowid = *pbuf->pb_flowid;
m->m_pkthdr.comp_gencnt = *pbuf->pb_flow_gencnt;
m->m_pkthdr.pkt_flags = *pbuf->pb_flags;
if (pbuf->pb_pftag != NULL) {
struct pf_mtag *pftag = m_pftag(m);
ASSERT(pftag != NULL);
*pftag = *pbuf->pb_pftag;
fragtag =
((pftag->pftag_flags & PF_TAG_REASSEMBLED) != 0);
}
if (fragtag && pbuf->pb_pf_fragtag != NULL) {
if (pf_copy_fragment_tag(m, pbuf->pb_pf_fragtag,
M_NOWAIT) == NULL) {
m_freem(m);
m = NULL;
goto done;
}
}
}
done:
if (release_ptr) {
pbuf_destroy(pbuf);
}
return m;
}
struct mbuf *
pbuf_clone_to_mbuf(pbuf_t *pbuf)
{
struct mbuf *m = NULL;
pbuf_sync(pbuf);
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
m = m_copy(pbuf->pb_mbuf, 0, M_COPYALL);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
m = pbuf_to_mbuf(pbuf, FALSE);
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
return m;
}
void *
pbuf_ensure_writable(pbuf_t *pbuf, size_t len)
{
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
struct mbuf *m = pbuf->pb_mbuf;
if (m_makewritable(&pbuf->pb_mbuf, 0, len, M_DONTWAIT)) {
return NULL;
}
if (pbuf->pb_mbuf == NULL) {
pbuf_destroy(pbuf);
return NULL;
}
if (m != pbuf->pb_mbuf) {
pbuf_sync(pbuf);
}
} else if (pbuf->pb_type != PBUF_TYPE_MEMORY) {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
return pbuf->pb_data;
}
void *
pbuf_resize_segment(pbuf_t *pbuf, int off, int olen, int nlen)
{
void *rv = NULL;
VERIFY(off >= 0);
VERIFY((u_int)off <= pbuf->pb_packet_len);
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
struct mbuf *m, *n;
VERIFY(pbuf->pb_mbuf != NULL);
m = pbuf->pb_mbuf;
if (off > 0) {
/* Split the mbuf chain at the specified boundary */
if ((n = m_split(m, off, M_DONTWAIT)) == NULL) {
return NULL;
}
} else {
n = m;
}
/* Trim old length */
m_adj(n, olen);
/* Prepend new length */
if (M_PREPEND(n, nlen, M_DONTWAIT, 0) == NULL) {
/* mbuf is freed by M_PREPEND in this case */
pbuf->pb_mbuf = NULL;
pbuf_destroy(pbuf);
return NULL;
}
rv = mtod(n, void *);
if (off > 0) {
/* Merge the two chains */
int mlen;
mlen = n->m_pkthdr.len;
m_cat(m, n);
m->m_pkthdr.len += mlen;
} else {
/* The new mbuf becomes the packet header */
pbuf->pb_mbuf = n;
}
pbuf_sync(pbuf);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
struct pbuf_memory *nm = &pbuf->pb_memory;
u_int true_offset, move_len;
int delta_len;
uint8_t *psrc, *pdst;
delta_len = nlen - olen;
VERIFY(nm->pm_offset + (nm->pm_len + delta_len) <=
nm->pm_buffer_len);
true_offset = (u_int)off + nm->pm_offset;
rv = &nm->pm_buffer[true_offset];
psrc = &nm->pm_buffer[true_offset + (u_int)olen];
pdst = &nm->pm_buffer[true_offset + (u_int)nlen];
move_len = pbuf->pb_packet_len - ((u_int)off + olen);
memmove(pdst, psrc, move_len);
nm->pm_len += delta_len;
VERIFY((nm->pm_len + nm->pm_offset) <= nm->pm_buffer_len);
pbuf_sync(pbuf);
} else {
panic("pbuf_csum_flags_get: bad pb_type: %d", pbuf->pb_type);
}
return rv;
}
void *
pbuf_contig_segment(pbuf_t *pbuf, int off, int len)
{
void *rv = NULL;
VERIFY(off >= 0);
VERIFY(len >= 0);
VERIFY((u_int)(off + len) <= pbuf->pb_packet_len);
/*
* Note: If this fails, then the pbuf is destroyed. This is a
* side-effect of m_pulldown().
*
* PF expects this behaviour so it's not a real problem.
*/
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
struct mbuf *n;
int moff;
n = m_pulldown(pbuf->pb_mbuf, off, len, &moff);
if (n == NULL) {
/* mbuf is freed by m_pulldown() in this case */
pbuf->pb_mbuf = NULL;
pbuf_destroy(pbuf);
return NULL;
}
pbuf_sync(pbuf);
rv = (void *)(mtod(n, uint8_t *) + moff);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
/*
* This always succeeds since memory pbufs are fully contig.
*/
rv = (void *)(uintptr_t)(((uint8_t *)pbuf->pb_data)[off]);
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
return rv;
}
void
pbuf_copy_back(pbuf_t *pbuf, int off, int len, void *src)
{
VERIFY(off >= 0);
VERIFY(len >= 0);
VERIFY((u_int)(off + len) <= pbuf->pb_packet_len);
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
m_copyback(pbuf->pb_mbuf, off, len, src);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
if (len) {
memcpy(&((uint8_t *)pbuf->pb_data)[off], src, len);
}
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
}
void
pbuf_copy_data(pbuf_t *pbuf, int off, int len, void *dst)
{
VERIFY(off >= 0);
VERIFY(len >= 0);
VERIFY((u_int)(off + len) <= pbuf->pb_packet_len);
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
m_copydata(pbuf->pb_mbuf, off, len, dst);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
if (len) {
memcpy(dst, &((uint8_t *)pbuf->pb_data)[off], len);
}
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
}
uint16_t
pbuf_inet_cksum(const pbuf_t *pbuf, uint32_t nxt, uint32_t off, uint32_t len)
{
uint16_t sum = 0;
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
sum = inet_cksum(pbuf->pb_mbuf, nxt, off, len);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
sum = inet_cksum_buffer(pbuf->pb_data, nxt, off, len);
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
return sum;
}
uint16_t
pbuf_inet6_cksum(const pbuf_t *pbuf, uint32_t nxt, uint32_t off, uint32_t len)
{
uint16_t sum = 0;
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
sum = inet6_cksum(pbuf->pb_mbuf, nxt, off, len);
} else if (pbuf->pb_type == PBUF_TYPE_MEMORY) {
sum = inet6_cksum_buffer(pbuf->pb_data, nxt, off, len);
} else {
panic("%s: bad pb_type: %d", __func__, pbuf->pb_type);
}
return sum;
}
mbuf_svc_class_t
pbuf_get_service_class(const pbuf_t *pbuf)
{
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
return m_get_service_class(pbuf->pb_mbuf);
}
VERIFY(pbuf->pb_type == PBUF_TYPE_MEMORY);
return MBUF_SC_BE;
}
void *
pbuf_get_packet_buffer_address(const pbuf_t *pbuf)
{
VERIFY(pbuf != NULL);
if (pbuf->pb_type == PBUF_TYPE_MBUF) {
return pbuf->pb_mbuf;
} else {
return pbuf->pb_memory.pm_buffer;
}
}