15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 25d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (c) 1995 Danny Gasparovski 35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Please read the file COPYRIGHT for the 55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * terms and conditions of the copyright. 65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * mbuf's in SLiRP are much simpler than the real mbufs in 105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * FreeBSD. They are fixed size, determined by the MTU, 115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * so that one whole packet can fit. Mbuf's cannot be 125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * chained together. If there's more data than the mbuf 135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * could hold, an external malloced buffer is pointed to 145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * by m_ext (and the data pointers) and M_EXT is set in 155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * the flags 165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include <slirp.h> 195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint mbuf_alloced = 0; 215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct mbuf m_freelist, m_usedlist; 225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define MBUF_THRESH 30 235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint mbuf_max = 0; 245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Find a nice value for msize 275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * XXX if_maxlinkhdr already in mtu 285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + sizeof(struct m_hdr ) + 6) 305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid 325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_init(void) 335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m_freelist.m_next = m_freelist.m_prev = &m_freelist; 355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist; 365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Get an mbuf from the free list, if there are none 405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * malloc one 415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Because fragmentation can occur if we alloc new mbufs and 435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE, 445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * which tells m_free to actually free() it 455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct mbuf * 475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_get(void) 485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner register struct mbuf *m; 505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int flags = 0; 515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_CALL("m_get"); 535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m_freelist.m_next == &m_freelist) { 555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m = (struct mbuf *)malloc(SLIRP_MSIZE); 565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m == NULL) goto end_error; 575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner mbuf_alloced++; 585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (mbuf_alloced > MBUF_THRESH) 595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner flags = M_DOFREE; 605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (mbuf_alloced > mbuf_max) 615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner mbuf_max = mbuf_alloced; 625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } else { 635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m = m_freelist.m_next; 645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner remque(m); 655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Insert it in the used list */ 685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner insque(m,&m_usedlist); 695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_flags = (flags | M_USEDLIST); 705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Initialise it */ 725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_size = SLIRP_MSIZE - sizeof(struct m_hdr); 735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_data = m->m_dat; 745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_len = 0; 755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_nextpkt = NULL; 765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_prevpkt = NULL; 775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerend_error: 785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_ARG("m = %lx", (long )m); 795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return m; 805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid 835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_free(struct mbuf *m) 845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_CALL("m_free"); 875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_ARG("m = %lx", (long )m); 885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if(m) { 905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Remove from m_usedlist */ 915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m->m_flags & M_USEDLIST) 925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner remque(m); 935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* If it's M_EXT, free() it */ 955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m->m_flags & M_EXT) 965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner free(m->m_ext); 975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* 995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Either free() it or put it on the free list 1005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 1015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m->m_flags & M_DOFREE) { 1025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner free(m); 1035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner mbuf_alloced--; 1045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } else if ((m->m_flags & M_FREELIST) == 0) { 1055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner insque(m,&m_freelist); 1065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_flags = M_FREELIST; /* Clobber other flags */ 1075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } /* if(m) */ 1095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 1125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copy data from one mbuf to the end of 1135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * the other.. if result is too big for one mbuf, malloc() 1145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * an M_EXT data segment 1155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 1165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid 1175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_cat(struct mbuf *m, struct mbuf *n) 1185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* 1205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * If there's no room, realloc 1215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 1225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (M_FREEROOM(m) < n->m_len) 1235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m_inc(m,m->m_size+MINCSIZE); 1245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memcpy(m->m_data+m->m_len, n->m_data, n->m_len); 1265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_len += n->m_len; 1275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m_free(n); 1295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* make m size bytes large */ 1335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid 1345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_inc(struct mbuf *m, int size) 1355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int datasize; 1375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* some compiles throw up on gotos. This one we can fake. */ 1395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if(m->m_size>size) return; 1405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m->m_flags & M_EXT) { 1425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner datasize = m->m_data - m->m_ext; 1435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_ext = (char *)realloc(m->m_ext,size); 1445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* if (m->m_ext == NULL) 1455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * return (struct mbuf *)NULL; 1465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 1475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_data = m->m_ext + datasize; 1485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } else { 1495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner char *dat; 1505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner datasize = m->m_data - m->m_dat; 1515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner dat = (char *)malloc(size); 1525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* if (dat == NULL) 1535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * return (struct mbuf *)NULL; 1545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 1555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memcpy(dat, m->m_dat, m->m_size); 1565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_ext = dat; 1585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_data = m->m_ext + datasize; 1595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_flags |= M_EXT; 1605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_size = size; 1635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid 1695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_adj(struct mbuf *m, int len) 1705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m == NULL) 1725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; 1735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (len >= 0) { 1745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Trim from head */ 1755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_data += len; 1765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_len -= len; 1775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } else { 1785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Trim from tail */ 1795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner len = -len; 1805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner m->m_len -= len; 1815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 1865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copy len bytes from m, starting off bytes into n 1875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 1885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint 1895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerm_copy(struct mbuf *n, struct mbuf *m, int off, int len) 1905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (len > M_FREEROOM(n)) 1925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -1; 1935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memcpy((n->m_data + n->m_len), (m->m_data + off), len); 1955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner n->m_len += len; 1965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 1975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 2015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Given a pointer into an mbuf, return the mbuf 2025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * XXX This is a kludge, I should eliminate the need for it 2035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Fortunately, it's not used often 2045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 2055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct mbuf * 2065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerdtom(void *dat) 2075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 2085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner struct mbuf *m; 2095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_CALL("dtom"); 2115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_ARG("dat = %lx", (long )dat); 2125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* bug corrected for M_EXT buffers */ 2145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) { 2155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (m->m_flags & M_EXT) { 2165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) ) 2175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return m; 2185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } else { 2195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) ) 2205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return m; 2215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner DEBUG_ERROR((dfd, "dtom failed")); 2255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return (struct mbuf *)0; 2275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 228