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: stable/11/usr.sbin/ppp/mbuf.c 330449 2018-03-05 07:26:05Z eadler $
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
53struct mbucket;
54
55struct mfree {
56  struct mbucket *next;
57  size_t count;
58};
59
60static 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
71static struct memmap {
72  struct mbuf *queue;
73  size_t fragments;
74  size_t octets;
75} MemMap[MB_MAX + 1];
76
77static unsigned long long mbuf_Mallocs, mbuf_Frees;
78
79size_t
80m_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
89static const char *
90mbuftype(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
105struct mbuf *
106m_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
166struct mbuf *
167m_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
189void
190m_freem(struct mbuf *bp)
191{
192  while (bp)
193    bp = m_free(bp);
194}
195
196struct mbuf *
197mbuf_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
224size_t
225mbuf_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
244struct mbuf *
245m_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
274struct mbuf *
275m_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
304void
305mbuf_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
322int
323mbuf_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
346struct mbuf *
347m_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
368void
369m_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
382struct mbuf *
383m_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
411void
412m_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
424struct mbuf *
425m_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