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