1/* SPDX-License-Identifier: BSD-3-Clause */
2/*
3 * Copyright (c) 1995 Danny Gasparovski
4 */
5
6/*
7 * mbuf's in SLiRP are much simpler than the real mbufs in
8 * FreeBSD. They are fixed size, determined by the MTU,
9 * so that one whole packet can fit. Mbuf's cannot be
10 * chained together. If there's more data than the mbuf
11 * could hold, an external g_malloced buffer is pointed to
12 * by m_ext (and the data pointers) and M_EXT is set in
13 * the flags
14 */
15
16#include "slirp.h"
17
18#define MBUF_THRESH 30
19
20/*
21 * Find a nice value for msize
22 */
23#define SLIRP_MSIZE \
24 (offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + IF_MTU)
25
26void m_init(Slirp *slirp)
27{
28 slirp->m_freelist.qh_link = slirp->m_freelist.qh_rlink = &slirp->m_freelist;
29 slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist;
30}
31
32void m_cleanup(Slirp *slirp)
33{
34 struct mbuf *m, *next;
35
36 m = (struct mbuf *)slirp->m_usedlist.qh_link;
37 while ((struct quehead *)m != &slirp->m_usedlist) {
38 next = m->m_next;
39 if (m->m_flags & M_EXT) {
40 g_free(m->m_ext);
41 }
42 g_free(m);
43 m = next;
44 }
45 m = (struct mbuf *)slirp->m_freelist.qh_link;
46 while ((struct quehead *)m != &slirp->m_freelist) {
47 next = m->m_next;
48 g_free(m);
49 m = next;
50 }
51}
52
53/*
54 * Get an mbuf from the free list, if there are none
55 * allocate one
56 *
57 * Because fragmentation can occur if we alloc new mbufs and
58 * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
59 * which tells m_free to actually g_free() it
60 */
61struct mbuf *m_get(Slirp *slirp)
62{
63 register struct mbuf *m;
64 int flags = 0;
65
66 DEBUG_CALL("m_get");
67
68 if (slirp->m_freelist.qh_link == &slirp->m_freelist) {
69 m = g_malloc(SLIRP_MSIZE);
70 slirp->mbuf_alloced++;
71 if (slirp->mbuf_alloced > MBUF_THRESH)
72 flags = M_DOFREE;
73 m->slirp = slirp;
74 } else {
75 m = (struct mbuf *)slirp->m_freelist.qh_link;
76 remque(m);
77 }
78
79 /* Insert it in the used list */
80 insque(m, &slirp->m_usedlist);
81 m->m_flags = (flags | M_USEDLIST);
82
83 /* Initialise it */
84 m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
85 m->m_data = m->m_dat;
86 m->m_len = 0;
87 m->m_nextpkt = NULL;
88 m->m_prevpkt = NULL;
89 m->resolution_requested = false;
90 m->expiration_date = (uint64_t)-1;
91 DEBUG_ARG("m = %p", m);
92 return m;
93}
94
95void m_free(struct mbuf *m)
96{
97 DEBUG_CALL("m_free");
98 DEBUG_ARG("m = %p", m);
99
100 if (m) {
101 /* Remove from m_usedlist */
102 if (m->m_flags & M_USEDLIST)
103 remque(m);
104
105 /* If it's M_EXT, free() it */
106 if (m->m_flags & M_EXT) {
107 g_free(m->m_ext);
108 }
109 /*
110 * Either free() it or put it on the free list
111 */
112 if (m->m_flags & M_DOFREE) {
113 m->slirp->mbuf_alloced--;
114 g_free(m);
115 } else if ((m->m_flags & M_FREELIST) == 0) {
116 insque(m, &m->slirp->m_freelist);
117 m->m_flags = M_FREELIST; /* Clobber other flags */
118 }
119 } /* if(m) */
120}
121
122/*
123 * Copy data from one mbuf to the end of
124 * the other.. if result is too big for one mbuf, allocate
125 * an M_EXT data segment
126 */
127void m_cat(struct mbuf *m, struct mbuf *n)
128{
129 /*
130 * If there's no room, realloc
131 */
132 if (M_FREEROOM(m) < n->m_len)
133 m_inc(m, m->m_len + n->m_len);
134
135 memcpy(m->m_data + m->m_len, n->m_data, n->m_len);
136 m->m_len += n->m_len;
137
138 m_free(n);
139}
140
141
142/* make m 'size' bytes large from m_data */
143void m_inc(struct mbuf *m, int size)
144{
145 int gapsize;
146
147 /* some compilers throw up on gotos. This one we can fake. */
148 if (M_ROOM(m) > size) {
149 return;
150 }
151
152 if (m->m_flags & M_EXT) {
153 gapsize = m->m_data - m->m_ext;
154 m->m_ext = g_realloc(m->m_ext, size + gapsize);
155 } else {
156 gapsize = m->m_data - m->m_dat;
157 m->m_ext = g_malloc(size + gapsize);
158 memcpy(m->m_ext, m->m_dat, m->m_size);
159 m->m_flags |= M_EXT;
160 }
161
162 m->m_data = m->m_ext + gapsize;
163 m->m_size = size + gapsize;
164}
165
166
167void m_adj(struct mbuf *m, int len)
168{
169 if (m == NULL)
170 return;
171 if (len >= 0) {
172 /* Trim from head */
173 m->m_data += len;
174 m->m_len -= len;
175 } else {
176 /* Trim from tail */
177 len = -len;
178 m->m_len -= len;
179 }
180}
181
182
183/*
184 * Copy len bytes from m, starting off bytes into n
185 */
186int m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
187{
188 if (len > M_FREEROOM(n))
189 return -1;
190
191 memcpy((n->m_data + n->m_len), (m->m_data + off), len);
192 n->m_len += len;
193 return 0;
194}
195
196
197/*
198 * Given a pointer into an mbuf, return the mbuf
199 * XXX This is a kludge, I should eliminate the need for it
200 * Fortunately, it's not used often
201 */
202struct mbuf *dtom(Slirp *slirp, void *dat)
203{
204 struct mbuf *m;
205
206 DEBUG_CALL("dtom");
207 DEBUG_ARG("dat = %p", dat);
208
209 /* bug corrected for M_EXT buffers */
210 for (m = (struct mbuf *)slirp->m_usedlist.qh_link;
211 (struct quehead *)m != &slirp->m_usedlist; m = m->m_next) {
212 if (m->m_flags & M_EXT) {
213 if ((char *)dat >= m->m_ext && (char *)dat < (m->m_ext + m->m_size))
214 return m;
215 } else {
216 if ((char *)dat >= m->m_dat && (char *)dat < (m->m_dat + m->m_size))
217 return m;
218 }
219 }
220
221 DEBUG_ERROR("dtom failed");
222
223 return (struct mbuf *)0;
224}
225