| /* |
| * Copyright (c) 1995 Danny Gasparovski |
| * |
| * Please read the file COPYRIGHT for the |
| * terms and conditions of the copyright. |
| */ |
| |
| /* |
| * mbuf's in SLiRP are much simpler than the real mbufs in |
| * FreeBSD. They are fixed size, determined by the MTU, |
| * so that one whole packet can fit. Mbuf's cannot be |
| * chained together. If there's more data than the mbuf |
| * could hold, an external malloced buffer is pointed to |
| * by m_ext (and the data pointers) and M_EXT is set in |
| * the flags |
| */ |
| |
| #include <slirp.h> |
| |
| int mbuf_alloced = 0; |
| struct mbuf m_freelist, m_usedlist; |
| #define MBUF_THRESH 30 |
| int mbuf_max = 0; |
| |
| /* |
| * Find a nice value for msize |
| * XXX if_maxlinkhdr already in mtu |
| */ |
| #define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + sizeof(struct m_hdr) + 6) |
| |
| void m_init() |
| { |
| m_freelist.m_next = m_freelist.m_prev = &m_freelist; |
| m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist; |
| } |
| |
| /* |
| * Get an mbuf from the free list, if there are none |
| * malloc one |
| * |
| * Because fragmentation can occur if we alloc new mbufs and |
| * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE, |
| * which tells m_free to actually free() it |
| */ |
| struct mbuf *m_get() |
| { |
| register struct mbuf *m; |
| int flags = 0; |
| |
| DEBUG_CALL("m_get"); |
| |
| if (m_freelist.m_next == &m_freelist) { |
| m = (struct mbuf *)malloc(SLIRP_MSIZE); |
| if (m == NULL) |
| goto end_error; |
| mbuf_alloced++; |
| if (mbuf_alloced > MBUF_THRESH) |
| flags = M_DOFREE; |
| if (mbuf_alloced > mbuf_max) |
| mbuf_max = mbuf_alloced; |
| } else { |
| m = m_freelist.m_next; |
| remque(m); |
| } |
| |
| /* Insert it in the used list */ |
| insque(m, &m_usedlist); |
| m->m_flags = (flags | M_USEDLIST); |
| |
| /* Initialise it */ |
| m->m_size = SLIRP_MSIZE - sizeof(struct m_hdr); |
| m->m_data = m->m_dat; |
| m->m_len = 0; |
| m->m_nextpkt = 0; |
| m->m_prevpkt = 0; |
| end_error: |
| DEBUG_ARG("m = %lx", (long)m); |
| return m; |
| } |
| |
| void m_free(m) struct mbuf *m; |
| { |
| DEBUG_CALL("m_free"); |
| DEBUG_ARG("m = %lx", (long)m); |
| |
| if (m) { |
| /* Remove from m_usedlist */ |
| if (m->m_flags & M_USEDLIST) |
| remque(m); |
| |
| /* If it's M_EXT, free() it */ |
| if (m->m_flags & M_EXT) |
| free(m->m_ext); |
| |
| /* |
| * Either free() it or put it on the free list |
| */ |
| if (m->m_flags & M_DOFREE) { |
| free(m); |
| mbuf_alloced--; |
| } else if ((m->m_flags & M_FREELIST) == 0) { |
| insque(m, &m_freelist); |
| m->m_flags = M_FREELIST; /* Clobber other flags */ |
| } |
| } /* if(m) */ |
| } |
| |
| /* |
| * Copy data from one mbuf to the end of |
| * the other.. if result is too big for one mbuf, malloc() |
| * an M_EXT data segment |
| */ |
| void m_cat(m, n) register struct mbuf *m, *n; |
| { |
| /* |
| * If there's no room, realloc |
| */ |
| if (M_FREEROOM(m) < n->m_len) |
| m_inc(m, m->m_size + MINCSIZE); |
| |
| memcpy(m->m_data + m->m_len, n->m_data, n->m_len); |
| m->m_len += n->m_len; |
| |
| m_free(n); |
| } |
| |
| |
| /* make m size bytes large */ |
| void m_inc(m, size) struct mbuf *m; |
| int size; |
| { |
| int datasize; |
| |
| /* some compiles throw up on gotos. This one we can fake. */ |
| if (m->m_size > size) |
| return; |
| |
| if (m->m_flags & M_EXT) { |
| datasize = m->m_data - m->m_ext; |
| m->m_ext = (char *)realloc(m->m_ext, size); |
| /* if (m->m_ext == NULL) |
| * return (struct mbuf *)NULL; |
| */ |
| m->m_data = m->m_ext + datasize; |
| } else { |
| char *dat; |
| datasize = m->m_data - m->m_dat; |
| dat = (char *)malloc(size); |
| /* if (dat == NULL) |
| * return (struct mbuf *)NULL; |
| */ |
| memcpy(dat, m->m_dat, m->m_size); |
| |
| m->m_ext = dat; |
| m->m_data = m->m_ext + datasize; |
| m->m_flags |= M_EXT; |
| } |
| |
| m->m_size = size; |
| } |
| |
| |
| void m_adj(m, len) struct mbuf *m; |
| int len; |
| { |
| if (m == NULL) |
| return; |
| if (len >= 0) { |
| /* Trim from head */ |
| m->m_data += len; |
| m->m_len -= len; |
| } else { |
| /* Trim from tail */ |
| len = -len; |
| m->m_len -= len; |
| } |
| } |
| |
| |
| /* |
| * Copy len bytes from m, starting off bytes into n |
| */ |
| int m_copy(n, m, off, len) struct mbuf *n, *m; |
| int off, len; |
| { |
| if (len > M_FREEROOM(n)) |
| return -1; |
| |
| memcpy((n->m_data + n->m_len), (m->m_data + off), len); |
| n->m_len += len; |
| return 0; |
| } |
| |
| |
| /* |
| * Given a pointer into an mbuf, return the mbuf |
| * XXX This is a kludge, I should eliminate the need for it |
| * Fortunately, it's not used often |
| */ |
| struct mbuf *dtom(dat) void *dat; |
| { |
| struct mbuf *m; |
| |
| DEBUG_CALL("dtom"); |
| DEBUG_ARG("dat = %lx", (long)dat); |
| |
| /* bug corrected for M_EXT buffers */ |
| for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) { |
| if (m->m_flags & M_EXT) { |
| if ((char *)dat >= m->m_ext && (char *)dat < (m->m_ext + m->m_size)) |
| return m; |
| } else { |
| if ((char *)dat >= m->m_dat && (char *)dat < (m->m_dat + m->m_size)) |
| return m; |
| } |
| } |
| |
| DEBUG_ERROR((dfd, "dtom failed")); |
| |
| return (struct mbuf *)0; |
| } |