// -*- c-basic-offset: 2; related-file-name: "../include/click/packet.hh" -*- /* * packet.{cc,hh} -- a packet structure. In the Linux kernel, a synonym for * `struct sk_buff' * Eddie Kohler, Robert Morris, Nickolai Zeldovich * * Copyright (c) 1999-2001 Massachusetts Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */ #include #include #include #ifdef CLICK_USERLEVEL #include #endif CLICK_DECLS #ifdef CLICK_LINUXMODULE /* Linux kernel module */ Packet::Packet() { static_assert(sizeof(Anno) <= sizeof(((struct sk_buff *)0)->cb)); panic("Packet constructor"); } Packet::~Packet() { panic("Packet destructor"); } WritablePacket * Packet::make(uint32_t headroom, const unsigned char *data, uint32_t len, uint32_t tailroom) { int want = 1; if (struct sk_buff *skb = skbmgr_allocate_skbs(headroom, len + tailroom, &want)) { assert(want == 1); // packet comes back from skbmgr with headroom reserved __skb_put(skb, len); // leave space for data if (data) memcpy(skb->data, data, len); skb->pkt_type = HOST | PACKET_CLEAN; WritablePacket *q = reinterpret_cast(skb); q->clear_annotations(); return q; } else return 0; } #else /* User-space or BSD kernel module */ inline Packet::Packet() { _use_count = 1; _data_packet = 0; _head = _data = _tail = _end = 0; #if CLICK_USERLEVEL _destructor = 0; #elif CLICK_BSDMODULE _m = 0; #endif clear_annotations(); } Packet::~Packet() { if (_data_packet) _data_packet->kill(); #if CLICK_USERLEVEL else if (_head && _destructor) _destructor(_head, _end - _head); else delete[] _head; #elif CLICK_BSDMODULE else m_freem(_m); #endif _head = _data = 0; } inline WritablePacket * Packet::make(int, int, int) { return static_cast(new Packet(6, 6, 6)); } #ifdef CLICK_USERLEVEL WritablePacket * Packet::make(unsigned char *data, uint32_t len, void (*destruct)(unsigned char *, size_t)) { WritablePacket *p = new WritablePacket; if (p) { p->_head = p->_data = data; p->_tail = p->_end = data + len; p->_destructor = destruct; } return p; } #endif bool Packet::alloc_data(uint32_t headroom, uint32_t len, uint32_t tailroom) { uint32_t n = len + headroom + tailroom; if (n < MIN_BUFFER_LENGTH) { tailroom = MIN_BUFFER_LENGTH - len - headroom; n = MIN_BUFFER_LENGTH; } #if CLICK_USERLEVEL unsigned char *d = new unsigned char[n]; if (!d) return false; _head = d; _data = d + headroom; _tail = _data + len; _end = _head + n; #elif CLICK_BSDMODULE if (n > MCLBYTES) { click_chatter("trying to allocate %d bytes: too many\n", n); return false; } struct mbuf *m; MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) return false; if (n > MHLEN) { MCLGET(m, M_DONTWAIT); if (!(m->m_flags & M_EXT)) { m_freem(m); return false; } } _m = m; _m->m_data += headroom; _m->m_len = len; _m->m_pkthdr.len = len; assimilate_mbuf(); #endif return true; } WritablePacket * Packet::make(uint32_t headroom, const unsigned char *data, uint32_t len, uint32_t tailroom) { WritablePacket *p = new WritablePacket; if (!p) return 0; if (!p->alloc_data(headroom, len, tailroom)) { delete p; return 0; } if (data) memcpy(p->data(), data, len); return p; } #endif /* CLICK_LINUXMODULE */ // // UNIQUEIFICATION // Packet * Packet::clone() { #if CLICK_LINUXMODULE struct sk_buff *nskb = skb_clone(skb(), GFP_ATOMIC); return reinterpret_cast(nskb); #elif CLICK_USERLEVEL || CLICK_BSDMODULE # if CLICK_BSDMODULE struct mbuf *m; if ((m = m_dup(this->_m, M_DONTWAIT)) == NULL) return 0; # endif // timing: .31-.39 normal, .43-.55 two allocs, .55-.58 two memcpys Packet *p = Packet::make(6, 6, 6); // dummy arguments: no initialization if (!p) return 0; memcpy(p, this, sizeof(Packet)); p->_use_count = 1; p->_data_packet = this; # if CLICK_USERLEVEL p->_destructor = 0; # else p->_m = m; # endif // increment our reference count because of _data_packet reference _use_count++; return p; #endif /* CLICK_LINUXMODULE */ } WritablePacket * Packet::expensive_uniqueify(int32_t extra_headroom, int32_t extra_tailroom, bool free_on_failure) { assert(extra_headroom >= (int32_t)(-headroom()) && extra_tailroom >= (int32_t)(-tailroom())); #if CLICK_LINUXMODULE struct sk_buff *nskb = skb(); unsigned char *old_head = nskb->head; uint32_t old_headroom = headroom(), old_length = length(); uint32_t size = buffer_length() + extra_headroom + extra_tailroom; # if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) size = ((size + 15) & ~15); unsigned char *new_data = reinterpret_cast(kmalloc(size + sizeof(atomic_t), GFP_ATOMIC)); # else size = SKB_DATA_ALIGN(size); unsigned char *new_data = reinterpret_cast(kmalloc(size + sizeof(struct skb_shared_info), GFP_ATOMIC)); # endif if (!new_data) { if (free_on_failure) kill(); return 0; } unsigned char *start_copy = old_head + (extra_headroom >= 0 ? 0 : -extra_headroom); unsigned char *end_copy = old_head + buffer_length() + (extra_tailroom >= 0 ? 0 : extra_tailroom); memcpy(new_data + (extra_headroom >= 0 ? extra_headroom : 0), start_copy, end_copy - start_copy); # if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) if (!nskb->cloned || atomic_dec_and_test(skb_datarefp(nskb))) kfree(old_head); # else if (!nskb->cloned || atomic_dec_and_test(&(skb_shinfo(nskb)->dataref))) { assert(!skb_shinfo(nskb)->nr_frags && !skb_shinfo(nskb)->frag_list); kfree(old_head); } # endif nskb->head = new_data; nskb->data = new_data + old_headroom + extra_headroom; nskb->tail = nskb->data + old_length; nskb->end = new_data + size; nskb->len = old_length; # if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) nskb->is_clone = 0; # endif nskb->cloned = 0; # if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) nskb->truesize = size; atomic_set(skb_datarefp(nskb), 1); # else nskb->truesize = size + sizeof(struct sk_buff); atomic_set(&(skb_shinfo(nskb)->dataref), 1); skb_shinfo(nskb)->nr_frags = 0; skb_shinfo(nskb)->frag_list = 0; # endif shift_header_annotations(nskb->head + extra_headroom - old_head); return static_cast(this); #else /* User-level or BSD kernel module */ // If someone else has cloned this packet, then we need to leave its data // pointers around. Make a clone and uniqueify that. if (_use_count > 1) { Packet *p = clone(); WritablePacket *q = (p ? p->expensive_uniqueify(extra_headroom, extra_tailroom, true) : 0); if (q || free_on_failure) kill(); return q; } uint8_t *old_head = _head, *old_end = _end; # if CLICK_BSDMODULE struct mbuf *old_m = _m; # endif if (!alloc_data(headroom() + extra_headroom, length(), tailroom() + extra_tailroom)) { if (free_on_failure) kill(); return 0; } unsigned char *start_copy = old_head + (extra_headroom >= 0 ? 0 : -extra_headroom); unsigned char *end_copy = old_end + (extra_tailroom >= 0 ? 0 : extra_tailroom); memcpy(_head + (extra_headroom >= 0 ? extra_headroom : 0), start_copy, end_copy - start_copy); // free old data if (_data_packet) _data_packet->kill(); # if CLICK_USERLEVEL else if (_destructor) _destructor(old_head, old_end - old_head); else delete[] old_head; _destructor = 0; # elif CLICK_BSDMODULE else m_freem(old_m); # endif _use_count = 1; _data_packet = 0; shift_header_annotations(_head + extra_headroom - old_head); return static_cast(this); #endif /* CLICK_LINUXMODULE */ } #ifdef CLICK_BSDMODULE /* BSD kernel module */ struct mbuf * Packet::steal_m() { struct Packet *p; struct mbuf *m2; p = uniqueify(); m2 = p->m(); /* Clear the mbuf from the packet: otherwise kill will MFREE it */ p->_m = 0; p->kill(); return m2; } #endif /* CLICK_BSDMODULE */ // // EXPENSIVE_PUSH, EXPENSIVE_PUT // /* * Prepend some empty space before a packet. * May kill this packet and return a new one. */ WritablePacket * Packet::expensive_push(uint32_t nbytes) { static int chatter = 0; if (headroom() < nbytes && chatter < 5) { click_chatter("expensive Packet::push; have %d wanted %d", headroom(), nbytes); chatter++; } if (WritablePacket *q = expensive_uniqueify((nbytes + 128) & ~3, 0, true)) { #ifdef CLICK_LINUXMODULE /* Linux kernel module */ __skb_push(q->skb(), nbytes); #else /* User-space and BSD kernel module */ q->_data -= nbytes; # ifdef CLICK_BSDMODULE q->m()->m_data -= nbytes; q->m()->m_len += nbytes; q->m()->m_pkthdr.len += nbytes; # endif #endif return q; } else return 0; } WritablePacket * Packet::expensive_put(uint32_t nbytes) { static int chatter = 0; if (tailroom() < nbytes && chatter < 5) { click_chatter("expensive Packet::put; have %d wanted %d", tailroom(), nbytes); chatter++; } if (WritablePacket *q = expensive_uniqueify(0, nbytes + 128, true)) { #ifdef CLICK_LINUXMODULE /* Linux kernel module */ __skb_put(q->skb(), nbytes); #else /* User-space and BSD kernel module */ q->_tail += nbytes; # ifdef CLICK_BSDMODULE q->m()->m_len += nbytes; q->m()->m_pkthdr.len += nbytes; # endif #endif return q; } else return 0; } Packet * Packet::shift_data(int offset, bool free_on_failure) { if (offset == 0) return this; else if (!shared() && (offset < 0 ? headroom() >= (uint32_t)(-offset) : tailroom() >= (uint32_t)offset)) { WritablePacket *q = static_cast(this); memmove(q->data() + offset, q->data(), q->length()); #if CLICK_LINUXMODULE struct sk_buff *mskb = q->skb(); mskb->data += offset; mskb->tail += offset; #else /* User-space and BSD kernel module */ q->_data += offset; q->_tail += offset; # if CLICK_BSDMODULE q->m()->m_data += offset; # endif #endif shift_header_annotations(offset); return this; } else { int tailroom_offset = (offset < 0 ? -offset : 0); if (offset < 0 && headroom() < (uint32_t)(-offset)) offset = -headroom() + ((uintptr_t)(data() + offset) & 7); else offset += ((uintptr_t)buffer_data() & 7); return expensive_uniqueify(offset, tailroom_offset, free_on_failure); } } CLICK_ENDDECLS