178189Sbrian/*-
278189Sbrian * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
378189Sbrian *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
478189Sbrian *                           Internet Initiative Japan, Inc (IIJ)
578189Sbrian * All rights reserved.
66059Samurai *
778189Sbrian * Redistribution and use in source and binary forms, with or without
878189Sbrian * modification, are permitted provided that the following conditions
978189Sbrian * are met:
1078189Sbrian * 1. Redistributions of source code must retain the above copyright
1178189Sbrian *    notice, this list of conditions and the following disclaimer.
1278189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1378189Sbrian *    notice, this list of conditions and the following disclaimer in the
1478189Sbrian *    documentation and/or other materials provided with the distribution.
156059Samurai *
1678189Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1778189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1878189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1978189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2078189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2178189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2278189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2378189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2478189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2578189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2678189Sbrian * SUCH DAMAGE.
276059Samurai *
2850479Speter * $FreeBSD$
296059Samurai */
3078189Sbrian
3136285Sbrian#include <sys/types.h>
3230715Sbrian
33102500Sbrian#include <stdarg.h>
3430715Sbrian#include <stdio.h>
3530715Sbrian#include <stdlib.h>
3630715Sbrian#include <string.h>
3736285Sbrian#include <sysexits.h>
3836285Sbrian#include <termios.h>
3930715Sbrian
4037009Sbrian#include "defs.h"
4131343Sbrian#include "command.h"
4230715Sbrian#include "mbuf.h"
4330715Sbrian#include "log.h"
4436285Sbrian#include "descriptor.h"
4536285Sbrian#include "prompt.h"
4636285Sbrian#include "main.h"
476059Samurai
4854913Sbrian#define BUCKET_CHUNK	20
4954913Sbrian#define BUCKET_HASH	256
5054913Sbrian
5154913Sbrianstruct mbucket;
5254913Sbrian
5354913Sbrianstruct mfree {
5454913Sbrian  struct mbucket *next;
5554913Sbrian  size_t count;
5654913Sbrian};
5754913Sbrian
5854913Sbrianstatic struct mbucket {
5954913Sbrian  union {
6054913Sbrian    struct mbuf m;
6154913Sbrian    struct mfree f;
6254913Sbrian  } u;
6354913Sbrian} *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH];
6454913Sbrian
6554913Sbrian#define M_BINDEX(sz)	(((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH)
6654913Sbrian#define M_BUCKET(sz)	(bucket + M_BINDEX(sz))
6754913Sbrian#define M_ROUNDUP(sz)	((M_BINDEX(sz) + 1) * BUCKET_HASH)
6854913Sbrian
6932663Sbrianstatic struct memmap {
706059Samurai  struct mbuf *queue;
7154912Sbrian  size_t fragments;
7254912Sbrian  size_t octets;
7347695Sbrian} MemMap[MB_MAX + 1];
746059Samurai
7546686Sbrianstatic unsigned long long mbuf_Mallocs, mbuf_Frees;
766059Samurai
77134789Sbriansize_t
7854912Sbrianm_length(struct mbuf *bp)
796059Samurai{
80134789Sbrian  size_t len;
816059Samurai
8254912Sbrian  for (len = 0; bp; bp = bp->m_next)
8354912Sbrian    len += bp->m_len;
8446828Sbrian  return len;
856059Samurai}
866059Samurai
8767911Sbrianstatic const char *
8867680Sbrianmbuftype(int type)
8967680Sbrian{
9098243Sbrian  static const char * const mbufdesc[MB_MAX] = {
9181634Sbrian    "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out",
9281634Sbrian    "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out",
9381634Sbrian    "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out",
9481634Sbrian    "proto in", "proto out", "acf in", "acf out", "sync in", "sync out",
9581634Sbrian    "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out",
9681634Sbrian    "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out",
9781634Sbrian    "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out"
9867680Sbrian  };
9967680Sbrian
10067680Sbrian  return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type];
10167680Sbrian}
10267680Sbrian
1036059Samuraistruct mbuf *
10454912Sbrianm_get(size_t m_len, int type)
1056059Samurai{
10654913Sbrian  struct mbucket **mb;
1076059Samurai  struct mbuf *bp;
10854913Sbrian  size_t size;
1096059Samurai
11047695Sbrian  if (type > MB_MAX) {
11136285Sbrian    log_Printf(LogERROR, "Bad mbuf type %d\n", type);
11247695Sbrian    type = MB_UNKNOWN;
11347695Sbrian  }
11498243Sbrian
11554913Sbrian  if (m_len > M_MAXLEN || m_len == 0) {
11667680Sbrian    log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n",
11767680Sbrian               (u_long)m_len, mbuftype(type));
11836285Sbrian    AbortProgram(EX_OSERR);
11925630Sbrian  }
12054913Sbrian
12154913Sbrian  mb = M_BUCKET(m_len);
12254913Sbrian  size = M_ROUNDUP(m_len);
12354913Sbrian
12454913Sbrian  if (*mb) {
12554913Sbrian    /* We've got some free blocks of the right size */
12654913Sbrian    bp = &(*mb)->u.m;
12754913Sbrian    if (--(*mb)->u.f.count == 0)
12854913Sbrian      *mb = (*mb)->u.f.next;
12954913Sbrian    else {
13054913Sbrian      ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count;
13154913Sbrian      *mb = (struct mbucket *)((char *)*mb + size);
13254913Sbrian      (*mb)->u.f.next = NULL;
13354913Sbrian    }
13454913Sbrian  } else {
13554913Sbrian    /*
13654913Sbrian     * Allocate another chunk of mbufs, use the first and put the rest on
13754913Sbrian     * the free list
13854913Sbrian     */
13954913Sbrian    *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size);
14054913Sbrian    if (*mb == NULL) {
14158042Sbrian      log_Printf(LogALERT, "Failed to allocate memory (%lu)\n",
14258042Sbrian                 (unsigned long)BUCKET_CHUNK * size);
14354913Sbrian      AbortProgram(EX_OSERR);
14454913Sbrian    }
14554913Sbrian    bp = &(*mb)->u.m;
14654913Sbrian    *mb = (struct mbucket *)((char *)*mb + size);
14754913Sbrian    (*mb)->u.f.count = BUCKET_CHUNK - 1;
14854913Sbrian    (*mb)->u.f.next = NULL;
14954913Sbrian  }
15054913Sbrian
15146686Sbrian  mbuf_Mallocs++;
15254913Sbrian
15330715Sbrian  memset(bp, '\0', sizeof(struct mbuf));
15454913Sbrian  bp->m_size = size - sizeof *bp;
15554913Sbrian  bp->m_len = m_len;
15654913Sbrian  bp->m_type = type;
15754913Sbrian
15838471Sbrian  MemMap[type].fragments++;
15954913Sbrian  MemMap[type].octets += bp->m_size;
16054913Sbrian
16145103Sbrian  return bp;
1626059Samurai}
1636059Samurai
1646059Samuraistruct mbuf *
16554912Sbrianm_free(struct mbuf *bp)
1666059Samurai{
16754913Sbrian  struct mbucket **mb, *f;
1686059Samurai  struct mbuf *nbp;
1696059Samurai
17054913Sbrian  if ((f = (struct mbucket *)bp) != NULL) {
17154912Sbrian    MemMap[bp->m_type].fragments--;
17254912Sbrian    MemMap[bp->m_type].octets -= bp->m_size;
17354913Sbrian
17454913Sbrian    nbp = bp->m_next;
17554913Sbrian    mb = M_BUCKET(bp->m_size);
17654913Sbrian    f->u.f.next = *mb;
17754913Sbrian    f->u.f.count = 1;
17854913Sbrian    *mb = f;
17954913Sbrian
18046686Sbrian    mbuf_Frees++;
18145103Sbrian    bp = nbp;
1826059Samurai  }
18345103Sbrian
18445103Sbrian  return bp;
1856059Samurai}
1866059Samurai
1876059Samuraivoid
18854912Sbrianm_freem(struct mbuf *bp)
1896059Samurai{
1906059Samurai  while (bp)
19154912Sbrian    bp = m_free(bp);
1926059Samurai}
1936059Samurai
1946059Samuraistruct mbuf *
19546686Sbrianmbuf_Read(struct mbuf *bp, void *v, size_t len)
1966059Samurai{
1976059Samurai  int nb;
19846686Sbrian  u_char *ptr = v;
1996059Samurai
2006059Samurai  while (bp && len > 0) {
20154912Sbrian    if (len > bp->m_len)
20254912Sbrian      nb = bp->m_len;
2036059Samurai    else
2046059Samurai      nb = len;
20546686Sbrian    if (nb) {
20646686Sbrian      memcpy(ptr, MBUF_CTOP(bp), nb);
20746686Sbrian      ptr += nb;
20854912Sbrian      bp->m_len -= nb;
20946686Sbrian      len -= nb;
21054912Sbrian      bp->m_offset += nb;
21146686Sbrian    }
21254912Sbrian    if (bp->m_len == 0)
21354912Sbrian      bp = m_free(bp);
2146059Samurai  }
21546686Sbrian
21654912Sbrian  while (bp && bp->m_len == 0)
21754912Sbrian    bp = m_free(bp);
21846686Sbrian
21946686Sbrian  return bp;
2206059Samurai}
2218857Srgrimes
22246686Sbriansize_t
22346686Sbrianmbuf_View(struct mbuf *bp, void *v, size_t len)
22446686Sbrian{
22546686Sbrian  size_t nb, l = len;
22646686Sbrian  u_char *ptr = v;
22746686Sbrian
22846686Sbrian  while (bp && l > 0) {
22954912Sbrian    if (l > bp->m_len)
23054912Sbrian      nb = bp->m_len;
23146686Sbrian    else
23246686Sbrian      nb = l;
23346686Sbrian    memcpy(ptr, MBUF_CTOP(bp), nb);
23446686Sbrian    ptr += nb;
23546686Sbrian    l -= nb;
23654912Sbrian    bp = bp->m_next;
23746686Sbrian  }
23846686Sbrian
23946686Sbrian  return len - l;
24046686Sbrian}
24146686Sbrian
24246686Sbrianstruct mbuf *
243134789Sbrianm_prepend(struct mbuf *bp, const void *ptr, size_t len, u_short extra)
24446686Sbrian{
24546686Sbrian  struct mbuf *head;
24646686Sbrian
24754912Sbrian  if (bp && bp->m_offset) {
24854912Sbrian    if (bp->m_offset >= len) {
24954912Sbrian      bp->m_offset -= len;
25054912Sbrian      bp->m_len += len;
251128338Sbrian      if (ptr)
252128338Sbrian        memcpy(MBUF_CTOP(bp), ptr, len);
25346686Sbrian      return bp;
25446686Sbrian    }
25554912Sbrian    len -= bp->m_offset;
256128338Sbrian    if (ptr)
257128338Sbrian      memcpy(bp + 1, (const char *)ptr + len, bp->m_offset);
25854912Sbrian    bp->m_len += bp->m_offset;
25954912Sbrian    bp->m_offset = 0;
26046686Sbrian  }
26146686Sbrian
26254912Sbrian  head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN);
26354912Sbrian  head->m_offset = extra;
26454912Sbrian  head->m_len -= extra;
26555353Sbrian  if (ptr)
26655353Sbrian    memcpy(MBUF_CTOP(head), ptr, len);
26754912Sbrian  head->m_next = bp;
26846686Sbrian
26946686Sbrian  return head;
27046686Sbrian}
27146686Sbrian
27246686Sbrianstruct mbuf *
27354912Sbrianm_adj(struct mbuf *bp, ssize_t n)
27446686Sbrian{
27554912Sbrian  if (n > 0) {
27654912Sbrian    while (bp) {
277134789Sbrian      if ((size_t)n < bp->m_len) {
27854912Sbrian        bp->m_len = n;
27954912Sbrian        bp->m_offset += n;
28054912Sbrian        return bp;
28154912Sbrian      }
28254912Sbrian      n -= bp->m_len;
28354912Sbrian      bp = m_free(bp);
28454912Sbrian    }
28554912Sbrian  } else {
28654912Sbrian    if ((n = m_length(bp) + n) <= 0) {
28754912Sbrian      m_freem(bp);
28854912Sbrian      return NULL;
28954912Sbrian    }
29054912Sbrian    for (; bp; bp = bp->m_next, n -= bp->m_len)
291134789Sbrian      if ((size_t)n < bp->m_len) {
29254912Sbrian        bp->m_len = n;
29354912Sbrian        m_freem(bp->m_next);
29454912Sbrian        bp->m_next = NULL;
29554912Sbrian        break;
29654912Sbrian      }
29746686Sbrian  }
29846686Sbrian
29946686Sbrian  return bp;
30046686Sbrian}
30146686Sbrian
3026059Samuraivoid
30354912Sbrianmbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len)
3046059Samurai{
305134789Sbrian  size_t plen;
3066059Samurai  int nb;
3076059Samurai
30854912Sbrian  plen = m_length(bp);
30954912Sbrian  if (plen < m_len)
31054912Sbrian    m_len = plen;
3116059Samurai
31254912Sbrian  while (m_len > 0) {
31354912Sbrian    nb = (m_len < bp->m_len) ? m_len : bp->m_len;
31430715Sbrian    memcpy(MBUF_CTOP(bp), ptr, nb);
31554912Sbrian    m_len -= bp->m_len;
31654912Sbrian    bp = bp->m_next;
3176059Samurai  }
3186059Samurai}
3196059Samurai
3206059Samuraiint
32136285Sbrianmbuf_Show(struct cmdargs const *arg)
3226059Samurai{
3236059Samurai  int i;
3246059Samurai
32538471Sbrian  prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
32647695Sbrian  for (i = 0; i < MB_MAX; i += 2)
32754912Sbrian    prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t"
32854912Sbrian                  "%10.10s: %04lu (%06lu)\n",
32967680Sbrian	          mbuftype(i), (u_long)MemMap[i].fragments,
33067680Sbrian                  (u_long)MemMap[i].octets, mbuftype(i+1),
33154912Sbrian                  (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets);
33226516Sbrian
33336285Sbrian  if (i == MB_MAX)
33454912Sbrian    prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n",
33567680Sbrian                  mbuftype(i), (u_long)MemMap[i].fragments,
33654912Sbrian                  (u_long)MemMap[i].octets);
33726516Sbrian
33849582Sbrian  prompt_Printf(arg->prompt, "Mallocs: %llu,   Frees: %llu\n",
33946686Sbrian                mbuf_Mallocs, mbuf_Frees);
34046686Sbrian
34126516Sbrian  return 0;
3426059Samurai}
3436059Samurai
34436285Sbrianstruct mbuf *
34554912Sbrianm_dequeue(struct mqueue *q)
34636285Sbrian{
34736285Sbrian  struct mbuf *bp;
34898243Sbrian
34954912Sbrian  log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len);
35036285Sbrian  bp = q->top;
35136285Sbrian  if (bp) {
35254912Sbrian    q->top = q->top->m_nextpkt;
35354912Sbrian    q->len--;
35436285Sbrian    if (q->top == NULL) {
35536285Sbrian      q->last = q->top;
35654912Sbrian      if (q->len)
35754912Sbrian	log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n",
35854912Sbrian                   (u_long)q->len);
35936285Sbrian    }
36054912Sbrian    bp->m_nextpkt = NULL;
36136285Sbrian  }
36236285Sbrian
36336285Sbrian  return bp;
36436285Sbrian}
36536285Sbrian
36636285Sbrianvoid
36754912Sbrianm_enqueue(struct mqueue *queue, struct mbuf *bp)
36836285Sbrian{
36946828Sbrian  if (bp != NULL) {
37046828Sbrian    if (queue->last) {
37154912Sbrian      queue->last->m_nextpkt = bp;
37246828Sbrian      queue->last = bp;
37346828Sbrian    } else
37446828Sbrian      queue->last = queue->top = bp;
37554912Sbrian    queue->len++;
37658042Sbrian    log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len);
37746828Sbrian  }
37836285Sbrian}
37945103Sbrian
38045103Sbrianstruct mbuf *
38154912Sbrianm_pullup(struct mbuf *bp)
38245103Sbrian{
38345103Sbrian  /* Put it all in one contigous (aligned) mbuf */
38445103Sbrian
38546828Sbrian  if (bp != NULL) {
38654912Sbrian    if (bp->m_next != NULL) {
38746828Sbrian      struct mbuf *nbp;
38846828Sbrian      u_char *cp;
38945103Sbrian
39054912Sbrian      nbp = m_get(m_length(bp), bp->m_type);
39145103Sbrian
39254912Sbrian      for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) {
39354912Sbrian        memcpy(cp, MBUF_CTOP(bp), bp->m_len);
39454912Sbrian        cp += bp->m_len;
39546828Sbrian      }
39646828Sbrian      bp = nbp;
39745103Sbrian    }
39845103Sbrian#ifndef __i386__	/* Do any other archs not care about alignment ? */
39954912Sbrian    else if ((bp->m_offset & (sizeof(long) - 1)) != 0) {
40054912Sbrian      bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len);
40154912Sbrian      bp->m_offset = 0;
40246828Sbrian    }
40346828Sbrian#endif
40445103Sbrian  }
40545103Sbrian
40645103Sbrian  return bp;
40745103Sbrian}
40847695Sbrian
40947695Sbrianvoid
41054912Sbrianm_settype(struct mbuf *bp, int type)
41147695Sbrian{
41254912Sbrian  for (; bp; bp = bp->m_next)
41354912Sbrian    if (type != bp->m_type) {
41454912Sbrian      MemMap[bp->m_type].fragments--;
41554912Sbrian      MemMap[bp->m_type].octets -= bp->m_size;
41654912Sbrian      bp->m_type = type;
41747695Sbrian      MemMap[type].fragments++;
41854912Sbrian      MemMap[type].octets += bp->m_size;
41947695Sbrian    }
42047695Sbrian}
42155353Sbrian
42255353Sbrianstruct mbuf *
42360135Sbrianm_append(struct mbuf *bp, const void *v, size_t sz)
42455353Sbrian{
42560135Sbrian  struct mbuf *m = bp;
42660135Sbrian
42755353Sbrian  if (m) {
42855353Sbrian    while (m->m_next)
42955353Sbrian      m = m->m_next;
430128338Sbrian    if (m->m_size - m->m_len >= sz) {
431128338Sbrian      if (v)
432128338Sbrian        memcpy((char *)(m + 1) + m->m_len, v, sz);
43355353Sbrian      m->m_len += sz;
434128338Sbrian    } else
43555353Sbrian      m->m_next = m_prepend(NULL, v, sz, 0);
43655353Sbrian  } else
43760135Sbrian    bp = m_prepend(NULL, v, sz, 0);
43855353Sbrian
43960135Sbrian  return bp;
44055353Sbrian}
441