xref: /freebsd/usr.sbin/ppp/mbuf.c (revision f6a3b357e9be4c6423c85eff9a847163a0d307c8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
5  *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
6  *                           Internet Initiative Japan, Inc (IIJ)
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 #include <sys/types.h>
34 
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sysexits.h>
40 #include <termios.h>
41 
42 #include "defs.h"
43 #include "command.h"
44 #include "mbuf.h"
45 #include "log.h"
46 #include "descriptor.h"
47 #include "prompt.h"
48 #include "main.h"
49 
50 #define BUCKET_CHUNK	20
51 #define BUCKET_HASH	256
52 
53 struct mbucket;
54 
55 struct mfree {
56   struct mbucket *next;
57   size_t count;
58 };
59 
60 static struct mbucket {
61   union {
62     struct mbuf m;
63     struct mfree f;
64   } u;
65 } *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH];
66 
67 #define M_BINDEX(sz)	(((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH)
68 #define M_BUCKET(sz)	(bucket + M_BINDEX(sz))
69 #define M_ROUNDUP(sz)	((M_BINDEX(sz) + 1) * BUCKET_HASH)
70 
71 static struct memmap {
72   struct mbuf *queue;
73   size_t fragments;
74   size_t octets;
75 } MemMap[MB_MAX + 1];
76 
77 static unsigned long long mbuf_Mallocs, mbuf_Frees;
78 
79 size_t
80 m_length(struct mbuf *bp)
81 {
82   size_t len;
83 
84   for (len = 0; bp; bp = bp->m_next)
85     len += bp->m_len;
86   return len;
87 }
88 
89 static const char *
90 mbuftype(int type)
91 {
92   static const char * const mbufdesc[MB_MAX] = {
93     "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out",
94     "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out",
95     "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out",
96     "proto in", "proto out", "acf in", "acf out", "sync in", "sync out",
97     "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out",
98     "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out",
99     "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out"
100   };
101 
102   return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type];
103 }
104 
105 struct mbuf *
106 m_get(size_t m_len, int type)
107 {
108   struct mbucket **mb;
109   struct mbuf *bp;
110   size_t size;
111 
112   if (type > MB_MAX) {
113     log_Printf(LogERROR, "Bad mbuf type %d\n", type);
114     type = MB_UNKNOWN;
115   }
116 
117   if (m_len > M_MAXLEN || m_len == 0) {
118     log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n",
119                (u_long)m_len, mbuftype(type));
120     AbortProgram(EX_OSERR);
121   }
122 
123   mb = M_BUCKET(m_len);
124   size = M_ROUNDUP(m_len);
125 
126   if (*mb) {
127     /* We've got some free blocks of the right size */
128     bp = &(*mb)->u.m;
129     if (--(*mb)->u.f.count == 0)
130       *mb = (*mb)->u.f.next;
131     else {
132       ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count;
133       *mb = (struct mbucket *)((char *)*mb + size);
134       (*mb)->u.f.next = NULL;
135     }
136   } else {
137     /*
138      * Allocate another chunk of mbufs, use the first and put the rest on
139      * the free list
140      */
141     *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size);
142     if (*mb == NULL) {
143       log_Printf(LogALERT, "Failed to allocate memory (%lu)\n",
144                  (unsigned long)BUCKET_CHUNK * size);
145       AbortProgram(EX_OSERR);
146     }
147     bp = &(*mb)->u.m;
148     *mb = (struct mbucket *)((char *)*mb + size);
149     (*mb)->u.f.count = BUCKET_CHUNK - 1;
150     (*mb)->u.f.next = NULL;
151   }
152 
153   mbuf_Mallocs++;
154 
155   memset(bp, '\0', sizeof(struct mbuf));
156   bp->m_size = size - sizeof *bp;
157   bp->m_len = m_len;
158   bp->m_type = type;
159 
160   MemMap[type].fragments++;
161   MemMap[type].octets += bp->m_size;
162 
163   return bp;
164 }
165 
166 struct mbuf *
167 m_free(struct mbuf *bp)
168 {
169   struct mbucket **mb, *f;
170   struct mbuf *nbp;
171 
172   if ((f = (struct mbucket *)bp) != NULL) {
173     MemMap[bp->m_type].fragments--;
174     MemMap[bp->m_type].octets -= bp->m_size;
175 
176     nbp = bp->m_next;
177     mb = M_BUCKET(bp->m_size);
178     f->u.f.next = *mb;
179     f->u.f.count = 1;
180     *mb = f;
181 
182     mbuf_Frees++;
183     bp = nbp;
184   }
185 
186   return bp;
187 }
188 
189 void
190 m_freem(struct mbuf *bp)
191 {
192   while (bp)
193     bp = m_free(bp);
194 }
195 
196 struct mbuf *
197 mbuf_Read(struct mbuf *bp, void *v, size_t len)
198 {
199   int nb;
200   u_char *ptr = v;
201 
202   while (bp && len > 0) {
203     if (len > bp->m_len)
204       nb = bp->m_len;
205     else
206       nb = len;
207     if (nb) {
208       memcpy(ptr, MBUF_CTOP(bp), nb);
209       ptr += nb;
210       bp->m_len -= nb;
211       len -= nb;
212       bp->m_offset += nb;
213     }
214     if (bp->m_len == 0)
215       bp = m_free(bp);
216   }
217 
218   while (bp && bp->m_len == 0)
219     bp = m_free(bp);
220 
221   return bp;
222 }
223 
224 size_t
225 mbuf_View(struct mbuf *bp, void *v, size_t len)
226 {
227   size_t nb, l = len;
228   u_char *ptr = v;
229 
230   while (bp && l > 0) {
231     if (l > bp->m_len)
232       nb = bp->m_len;
233     else
234       nb = l;
235     memcpy(ptr, MBUF_CTOP(bp), nb);
236     ptr += nb;
237     l -= nb;
238     bp = bp->m_next;
239   }
240 
241   return len - l;
242 }
243 
244 struct mbuf *
245 m_prepend(struct mbuf *bp, const void *ptr, size_t len, u_short extra)
246 {
247   struct mbuf *head;
248 
249   if (bp && bp->m_offset) {
250     if (bp->m_offset >= len) {
251       bp->m_offset -= len;
252       bp->m_len += len;
253       if (ptr)
254         memcpy(MBUF_CTOP(bp), ptr, len);
255       return bp;
256     }
257     len -= bp->m_offset;
258     if (ptr)
259       memcpy(bp + 1, (const char *)ptr + len, bp->m_offset);
260     bp->m_len += bp->m_offset;
261     bp->m_offset = 0;
262   }
263 
264   head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN);
265   head->m_offset = extra;
266   head->m_len -= extra;
267   if (ptr)
268     memcpy(MBUF_CTOP(head), ptr, len);
269   head->m_next = bp;
270 
271   return head;
272 }
273 
274 struct mbuf *
275 m_adj(struct mbuf *bp, ssize_t n)
276 {
277   if (n > 0) {
278     while (bp) {
279       if ((size_t)n < bp->m_len) {
280         bp->m_len = n;
281         bp->m_offset += n;
282         return bp;
283       }
284       n -= bp->m_len;
285       bp = m_free(bp);
286     }
287   } else {
288     if ((n = m_length(bp) + n) <= 0) {
289       m_freem(bp);
290       return NULL;
291     }
292     for (; bp; bp = bp->m_next, n -= bp->m_len)
293       if ((size_t)n < bp->m_len) {
294         bp->m_len = n;
295         m_freem(bp->m_next);
296         bp->m_next = NULL;
297         break;
298       }
299   }
300 
301   return bp;
302 }
303 
304 void
305 mbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len)
306 {
307   size_t plen;
308   int nb;
309 
310   plen = m_length(bp);
311   if (plen < m_len)
312     m_len = plen;
313 
314   while (m_len > 0) {
315     nb = (m_len < bp->m_len) ? m_len : bp->m_len;
316     memcpy(MBUF_CTOP(bp), ptr, nb);
317     m_len -= bp->m_len;
318     bp = bp->m_next;
319   }
320 }
321 
322 int
323 mbuf_Show(struct cmdargs const *arg)
324 {
325   int i;
326 
327   prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
328   for (i = 0; i < MB_MAX; i += 2)
329     prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t"
330                   "%10.10s: %04lu (%06lu)\n",
331 	          mbuftype(i), (u_long)MemMap[i].fragments,
332                   (u_long)MemMap[i].octets, mbuftype(i+1),
333                   (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets);
334 
335   if (i == MB_MAX)
336     prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n",
337                   mbuftype(i), (u_long)MemMap[i].fragments,
338                   (u_long)MemMap[i].octets);
339 
340   prompt_Printf(arg->prompt, "Mallocs: %llu,   Frees: %llu\n",
341                 mbuf_Mallocs, mbuf_Frees);
342 
343   return 0;
344 }
345 
346 struct mbuf *
347 m_dequeue(struct mqueue *q)
348 {
349   struct mbuf *bp;
350 
351   log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len);
352   bp = q->top;
353   if (bp) {
354     q->top = q->top->m_nextpkt;
355     q->len--;
356     if (q->top == NULL) {
357       q->last = q->top;
358       if (q->len)
359 	log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n",
360                    (u_long)q->len);
361     }
362     bp->m_nextpkt = NULL;
363   }
364 
365   return bp;
366 }
367 
368 void
369 m_enqueue(struct mqueue *queue, struct mbuf *bp)
370 {
371   if (bp != NULL) {
372     if (queue->last) {
373       queue->last->m_nextpkt = bp;
374       queue->last = bp;
375     } else
376       queue->last = queue->top = bp;
377     queue->len++;
378     log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len);
379   }
380 }
381 
382 struct mbuf *
383 m_pullup(struct mbuf *bp)
384 {
385   /* Put it all in one contigous (aligned) mbuf */
386 
387   if (bp != NULL) {
388     if (bp->m_next != NULL) {
389       struct mbuf *nbp;
390       u_char *cp;
391 
392       nbp = m_get(m_length(bp), bp->m_type);
393 
394       for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) {
395         memcpy(cp, MBUF_CTOP(bp), bp->m_len);
396         cp += bp->m_len;
397       }
398       bp = nbp;
399     }
400 #ifndef __i386__	/* Do any other archs not care about alignment ? */
401     else if ((bp->m_offset & (sizeof(long) - 1)) != 0) {
402       bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len);
403       bp->m_offset = 0;
404     }
405 #endif
406   }
407 
408   return bp;
409 }
410 
411 void
412 m_settype(struct mbuf *bp, int type)
413 {
414   for (; bp; bp = bp->m_next)
415     if (type != bp->m_type) {
416       MemMap[bp->m_type].fragments--;
417       MemMap[bp->m_type].octets -= bp->m_size;
418       bp->m_type = type;
419       MemMap[type].fragments++;
420       MemMap[type].octets += bp->m_size;
421     }
422 }
423 
424 struct mbuf *
425 m_append(struct mbuf *bp, const void *v, size_t sz)
426 {
427   struct mbuf *m = bp;
428 
429   if (m) {
430     while (m->m_next)
431       m = m->m_next;
432     if (m->m_size - m->m_len >= sz) {
433       if (v)
434         memcpy((char *)(m + 1) + m->m_len, v, sz);
435       m->m_len += sz;
436     } else
437       m->m_next = m_prepend(NULL, v, sz, 0);
438   } else
439     bp = m_prepend(NULL, v, sz, 0);
440 
441   return bp;
442 }
443