xref: /freebsd/usr.sbin/ppp/mbuf.c (revision 51a9219f5780e61e1437d25220bf8750d9df7f8b)
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 <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sysexits.h>
38 #include <termios.h>
39 
40 #include "defs.h"
41 #include "command.h"
42 #include "mbuf.h"
43 #include "log.h"
44 #include "descriptor.h"
45 #include "prompt.h"
46 #include "main.h"
47 
48 #define BUCKET_CHUNK	20
49 #define BUCKET_HASH	256
50 
51 struct mbucket;
52 
53 struct mfree {
54   struct mbucket *next;
55   size_t count;
56 };
57 
58 static struct mbucket {
59   union {
60     struct mbuf m;
61     struct mfree f;
62   } u;
63 } *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH];
64 
65 #define M_BINDEX(sz)	(((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH)
66 #define M_BUCKET(sz)	(bucket + M_BINDEX(sz))
67 #define M_ROUNDUP(sz)	((M_BINDEX(sz) + 1) * BUCKET_HASH)
68 
69 static struct memmap {
70   struct mbuf *queue;
71   size_t fragments;
72   size_t octets;
73 } MemMap[MB_MAX + 1];
74 
75 static unsigned long long mbuf_Mallocs, mbuf_Frees;
76 
77 int
78 m_length(struct mbuf *bp)
79 {
80   int len;
81 
82   for (len = 0; bp; bp = bp->m_next)
83     len += bp->m_len;
84   return len;
85 }
86 
87 static const char *
88 mbuftype(int type)
89 {
90   static const char * const mbufdesc[MB_MAX] = {
91     "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out",
92     "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out",
93     "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out",
94     "proto in", "proto out", "acf in", "acf out", "sync in", "sync out",
95     "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out",
96     "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out",
97     "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out"
98   };
99 
100   return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type];
101 }
102 
103 struct mbuf *
104 m_get(size_t m_len, int type)
105 {
106   struct mbucket **mb;
107   struct mbuf *bp;
108   size_t size;
109 
110   if (type > MB_MAX) {
111     log_Printf(LogERROR, "Bad mbuf type %d\n", type);
112     type = MB_UNKNOWN;
113   }
114 
115   if (m_len > M_MAXLEN || m_len == 0) {
116     log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n",
117                (u_long)m_len, mbuftype(type));
118     AbortProgram(EX_OSERR);
119   }
120 
121   mb = M_BUCKET(m_len);
122   size = M_ROUNDUP(m_len);
123 
124   if (*mb) {
125     /* We've got some free blocks of the right size */
126     bp = &(*mb)->u.m;
127     if (--(*mb)->u.f.count == 0)
128       *mb = (*mb)->u.f.next;
129     else {
130       ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count;
131       *mb = (struct mbucket *)((char *)*mb + size);
132       (*mb)->u.f.next = NULL;
133     }
134   } else {
135     /*
136      * Allocate another chunk of mbufs, use the first and put the rest on
137      * the free list
138      */
139     *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size);
140     if (*mb == NULL) {
141       log_Printf(LogALERT, "Failed to allocate memory (%lu)\n",
142                  (unsigned long)BUCKET_CHUNK * size);
143       AbortProgram(EX_OSERR);
144     }
145     bp = &(*mb)->u.m;
146     *mb = (struct mbucket *)((char *)*mb + size);
147     (*mb)->u.f.count = BUCKET_CHUNK - 1;
148     (*mb)->u.f.next = NULL;
149   }
150 
151   mbuf_Mallocs++;
152 
153   memset(bp, '\0', sizeof(struct mbuf));
154   bp->m_size = size - sizeof *bp;
155   bp->m_len = m_len;
156   bp->m_type = type;
157 
158   MemMap[type].fragments++;
159   MemMap[type].octets += bp->m_size;
160 
161   return bp;
162 }
163 
164 struct mbuf *
165 m_free(struct mbuf *bp)
166 {
167   struct mbucket **mb, *f;
168   struct mbuf *nbp;
169 
170   if ((f = (struct mbucket *)bp) != NULL) {
171     MemMap[bp->m_type].fragments--;
172     MemMap[bp->m_type].octets -= bp->m_size;
173 
174     nbp = bp->m_next;
175     mb = M_BUCKET(bp->m_size);
176     f->u.f.next = *mb;
177     f->u.f.count = 1;
178     *mb = f;
179 
180     mbuf_Frees++;
181     bp = nbp;
182   }
183 
184   return bp;
185 }
186 
187 void
188 m_freem(struct mbuf *bp)
189 {
190   while (bp)
191     bp = m_free(bp);
192 }
193 
194 struct mbuf *
195 mbuf_Read(struct mbuf *bp, void *v, size_t len)
196 {
197   int nb;
198   u_char *ptr = v;
199 
200   while (bp && len > 0) {
201     if (len > bp->m_len)
202       nb = bp->m_len;
203     else
204       nb = len;
205     if (nb) {
206       memcpy(ptr, MBUF_CTOP(bp), nb);
207       ptr += nb;
208       bp->m_len -= nb;
209       len -= nb;
210       bp->m_offset += nb;
211     }
212     if (bp->m_len == 0)
213       bp = m_free(bp);
214   }
215 
216   while (bp && bp->m_len == 0)
217     bp = m_free(bp);
218 
219   return bp;
220 }
221 
222 size_t
223 mbuf_View(struct mbuf *bp, void *v, size_t len)
224 {
225   size_t nb, l = len;
226   u_char *ptr = v;
227 
228   while (bp && l > 0) {
229     if (l > bp->m_len)
230       nb = bp->m_len;
231     else
232       nb = l;
233     memcpy(ptr, MBUF_CTOP(bp), nb);
234     ptr += nb;
235     l -= nb;
236     bp = bp->m_next;
237   }
238 
239   return len - l;
240 }
241 
242 struct mbuf *
243 m_prepend(struct mbuf *bp, const void *ptr, size_t len, size_t extra)
244 {
245   struct mbuf *head;
246 
247   if (bp && bp->m_offset) {
248     if (bp->m_offset >= len) {
249       bp->m_offset -= len;
250       bp->m_len += len;
251       memcpy(MBUF_CTOP(bp), ptr, len);
252       return bp;
253     }
254     len -= bp->m_offset;
255     memcpy(bp + 1, (const char *)ptr + len, bp->m_offset);
256     bp->m_len += bp->m_offset;
257     bp->m_offset = 0;
258   }
259 
260   head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN);
261   head->m_offset = extra;
262   head->m_len -= extra;
263   if (ptr)
264     memcpy(MBUF_CTOP(head), ptr, len);
265   head->m_next = bp;
266 
267   return head;
268 }
269 
270 struct mbuf *
271 m_adj(struct mbuf *bp, ssize_t n)
272 {
273   if (n > 0) {
274     while (bp) {
275       if (n < bp->m_len) {
276         bp->m_len = n;
277         bp->m_offset += n;
278         return bp;
279       }
280       n -= bp->m_len;
281       bp = m_free(bp);
282     }
283   } else {
284     if ((n = m_length(bp) + n) <= 0) {
285       m_freem(bp);
286       return NULL;
287     }
288     for (; bp; bp = bp->m_next, n -= bp->m_len)
289       if (n < bp->m_len) {
290         bp->m_len = n;
291         m_freem(bp->m_next);
292         bp->m_next = NULL;
293         break;
294       }
295   }
296 
297   return bp;
298 }
299 
300 void
301 mbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len)
302 {
303   int plen;
304   int nb;
305 
306   plen = m_length(bp);
307   if (plen < m_len)
308     m_len = plen;
309 
310   while (m_len > 0) {
311     nb = (m_len < bp->m_len) ? m_len : bp->m_len;
312     memcpy(MBUF_CTOP(bp), ptr, nb);
313     m_len -= bp->m_len;
314     bp = bp->m_next;
315   }
316 }
317 
318 int
319 mbuf_Show(struct cmdargs const *arg)
320 {
321   int i;
322 
323   prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
324   for (i = 0; i < MB_MAX; i += 2)
325     prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t"
326                   "%10.10s: %04lu (%06lu)\n",
327 	          mbuftype(i), (u_long)MemMap[i].fragments,
328                   (u_long)MemMap[i].octets, mbuftype(i+1),
329                   (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets);
330 
331   if (i == MB_MAX)
332     prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n",
333                   mbuftype(i), (u_long)MemMap[i].fragments,
334                   (u_long)MemMap[i].octets);
335 
336   prompt_Printf(arg->prompt, "Mallocs: %llu,   Frees: %llu\n",
337                 mbuf_Mallocs, mbuf_Frees);
338 
339   return 0;
340 }
341 
342 struct mbuf *
343 m_dequeue(struct mqueue *q)
344 {
345   struct mbuf *bp;
346 
347   log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len);
348   bp = q->top;
349   if (bp) {
350     q->top = q->top->m_nextpkt;
351     q->len--;
352     if (q->top == NULL) {
353       q->last = q->top;
354       if (q->len)
355 	log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n",
356                    (u_long)q->len);
357     }
358     bp->m_nextpkt = NULL;
359   }
360 
361   return bp;
362 }
363 
364 void
365 m_enqueue(struct mqueue *queue, struct mbuf *bp)
366 {
367   if (bp != NULL) {
368     if (queue->last) {
369       queue->last->m_nextpkt = bp;
370       queue->last = bp;
371     } else
372       queue->last = queue->top = bp;
373     queue->len++;
374     log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len);
375   }
376 }
377 
378 struct mbuf *
379 m_pullup(struct mbuf *bp)
380 {
381   /* Put it all in one contigous (aligned) mbuf */
382 
383   if (bp != NULL) {
384     if (bp->m_next != NULL) {
385       struct mbuf *nbp;
386       u_char *cp;
387 
388       nbp = m_get(m_length(bp), bp->m_type);
389 
390       for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) {
391         memcpy(cp, MBUF_CTOP(bp), bp->m_len);
392         cp += bp->m_len;
393       }
394       bp = nbp;
395     }
396 #ifndef __i386__	/* Do any other archs not care about alignment ? */
397     else if ((bp->m_offset & (sizeof(long) - 1)) != 0) {
398       bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len);
399       bp->m_offset = 0;
400     }
401 #endif
402   }
403 
404   return bp;
405 }
406 
407 void
408 m_settype(struct mbuf *bp, int type)
409 {
410   for (; bp; bp = bp->m_next)
411     if (type != bp->m_type) {
412       MemMap[bp->m_type].fragments--;
413       MemMap[bp->m_type].octets -= bp->m_size;
414       bp->m_type = type;
415       MemMap[type].fragments++;
416       MemMap[type].octets += bp->m_size;
417     }
418 }
419 
420 struct mbuf *
421 m_append(struct mbuf *bp, const void *v, size_t sz)
422 {
423   struct mbuf *m = bp;
424 
425   if (m) {
426     while (m->m_next)
427       m = m->m_next;
428     if (m->m_size - m->m_len > sz)
429       m->m_len += sz;
430     else
431       m->m_next = m_prepend(NULL, v, sz, 0);
432   } else
433     bp = m_prepend(NULL, v, sz, 0);
434 
435   return bp;
436 }
437