/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus 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, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef included_vec_h #define included_vec_h #include /* word, etc */ #include /* clib_mem_free */ #include /* memcpy, memmove */ #include /** \file CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers". Many CLIB data structures (e.g. hash, heap, pool) are vectors with various different headers. The memory layout looks like this: ~~~~~~~~ user header (aligned to uword boundary) vector length: number of elements user's pointer-> vector element #0 vector element #1 ... ~~~~~~~~ The user pointer contains the address of vector element # 0. Null pointer vectors are valid and mean a zero length vector. You can reset the length of an allocated vector to zero via the vec_reset_length(v) macro, or by setting the vector length field to zero (e.g. _vec_len (v) = 0). Vec_reset_length(v) preferred: it understands Null pointers. Typically, the header is not present. Headers allow for other data structures to be built atop CLIB vectors. Users may specify the alignment for first data element of a vector via the vec_*_aligned macros. Vector elements can be any C type e.g. (int, double, struct bar). This is also true for data types built atop vectors (e.g. heap, pool, etc.). Many macros have \_a variants supporting alignment of vector elements and \_h variants supporting non-zero-length vector headers. The \_ha variants support both. Additionally cacheline alignment within a vector element structure can be specified using the CLIB_CACHE_LINE_ALIGN_MARK() macro. Standard programming error: memorize a pointer to the ith element of a vector then expand it. Vectors expand by 3/2, so such code may appear to work for a period of time. Memorize vector indices which are invariant. */ /** \brief Low-level resize allocation function, usually not called directly @param v pointer to a vector @param length_increment length increment in elements @param data_bytes requested size in bytes @param header_bytes header size in bytes (may be zero) @param data_align alignment (may be zero) @return v_prime pointer to resized vector, may or may not equal v */ void *vec_resize_allocate_memory (void *v, word length_increment, uword data_bytes, uword header_bytes, uword data_align); /** \brief Low-level vector resize function, usually not called directly @param v pointer to a vector @param length_increment length increment in elements @param data_bytes requested size in bytes @param header_bytes header size in bytes (may be zero) @param data_align alignment (may be zero) @return v_prime pointer to resized vector, may or may not equal v */ #define _vec_resize(V,L,DB,HB,A) \ _vec_resize_inline(V,L,DB,HB,clib_max((__alignof__((V)[0])),(A))) always_inline void * _vec_resize_inline (void *v, word length_increment, uword data_bytes, uword header_bytes, uword data_align) { vec_header_t *vh = _vec_find (v); uword new_data_bytes, aligned_header_bytes; aligned_header_bytes = vec_header_bytes (header_bytes); new_data_bytes = data_bytes + aligned_header_bytes; if (PREDICT_TRUE (v != 0)) { void *p = v - aligned_header_bytes; /* Vector header must start heap object. */ ASSERT (clib_mem_is_heap_object (p)); /* Typically we'll not need to resize. */ if (new_data_bytes <= clib_mem_size (p)) { vh->len += length_increment; return v; } } /* Slow path: call helper function. */ return vec_resize_allocate_memory (v, length_increment, data_bytes, header_bytes, clib_max (sizeof (vec_header_t), data_align)); } /** \brief Determine if vector will resize with next allocation @param v pointer to a vector @param length_increment length increment in elements @param data_bytes requested size in bytes @param header_bytes header size in bytes (may be zero) @param data_align alignment (may be zero) @return 1 if vector will resize 0 otherwise */ always_inline int _vec_resize_will_expand (void *v, word length_increment, uword data_bytes, uword header_bytes, uword data_align) { uword new_data_bytes, aligned_header_bytes; aligned_header_bytes = vec_header_bytes (header_bytes); new_data_bytes = data_bytes + aligned_header_bytes; if (PREDICT_TRUE (v != 0)) { void *p = v - aligned_header_bytes; /* Vector header must start heap object. */ ASSERT (clib_mem_is_heap_object (p)); /* Typically we'll not need to resize. */ if (new_data_bytes <= clib_mem_size (p)) return 0; } return 1; } /** \brief Predicate function, says whether the supplied vector is a clib heap object (general version). @param v pointer to a vector @param header_bytes vector header size in bytes (may be zero) @return 0 or 1 */ uword clib_mem_is_vec_h (void *v, uword header_bytes); /** \brief Predicate function, says whether the supplied vector is a clib heap object @param v pointer to a vector @return 0 or 1 */ always_inline uword clib_mem_is_vec (void *v) { return clib_mem_is_vec_h (v, 0); } /* Local variable naming macro (prevents collisions with other macro naming). */ #define _v(var) _vec_##var /** \brief Resize a vector (general version). Add N elements to end of given vector V, return pointer to start of vector. Vector will have room for H header bytes and will have user's data aligned at alignment A (rounded to next power of 2). @param V pointer to a vector @param N number of elements to add @param H header size in bytes (may be zero) @param A alignment (may be zero) @return V (value-result macro parameter) */ #define vec_resize_ha(V,N,H,A) \ do { \ word _v(n) = (N); \ word _v(l) = vec_len (V); \ V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A)); \ } while (0) /** \brief Resize a vector (no header, unspecified alignment) Add N elements to end of given vector V, return pointer to start of vector. Vector will have room for H header bytes and will have user's data aligned at alignment A (rounded to next power of 2). @param V pointer to a vector @param N number of elements to add @return V (value-result macro parameter) */ #define vec_resize(V,N) vec_resize_ha(V,N,0,0) /** \brief Resize a vector (no header, alignment specified). Add N elements to end of given vector V, return pointer to start of vector. Vector will have room for H header bytes and will have user's data aligned at alignment A (rounded to next power of 2). @param V pointer to a vector @param N number of elements to add @param A alignment (may be zero) @return V (value-result macro parameter) */ #define vec_resize_aligned(V,N,A) vec_resize_ha(V,N,0,A) /** \brief Allocate space for N more elements @param V pointer to a vector @param N number of elements to add @param H header size in bytes (may be zero) @param A alignment (may be zero) @return V (value-result macro parameter) */ #define vec_alloc_ha(V,N,H,A) \ do { \ uword _v(l) = vec_len (V); \ vec_resize_ha (V, N, H, A); \ _vec_len (V) = _v(l); \ } while (0) /** \brief Allocate space for N more elements (no header, unspecified alignment) @param V pointer to a vector @param N number of elements to add @return V (value-result macro parameter) */ #define vec_alloc(V,N) vec_alloc_ha(V,N,0,0) /** \brief Allocate space for N more elements (no header, given alignment) @param V pointer to a vector @param N number of elements to add @param A alignment (may be zero) @return V (value-result macro parameter) */ #define vec_alloc_aligned(V,N,A) vec_alloc_ha(V,N,0,A) /** \brief Create new vector of given type and length (general version). @param T type of elements in new vector @param N number of elements to add @param H header size in bytes (may be zero) @param A alignment (may be zero) @return V new vector */ #define vec_new_ha(T,N,H,A) \ ({ \ word _v(n) = (N); \ _vec_resize ((T *) 0, _v(n), _v(n) * sizeof (T), (H), (A)); \ }) /** \brief Create new vector of given type and length (unspecified alignment, no header). @param T type of elements in new vector @param N number of elements to add @return V new vector */ #define vec_new(T,N) vec_new_ha(T,N,0,0) /** \brief Create new vector of given type and length (alignment specified, no header). @param T type of elements in new vector @param N number of elements to add @param A alignment (may be zero) @return V new vector */ #define vec_new_aligned(T,N,A) vec_new_ha(T,N,0,A) /** \brief Free vector's memory (general version) @param V pointer to a vector @param H size of header in bytes @return V (value-result parameter, V=0) */ #define vec_free_h(V,H) \ do { \ if (V) \ { \ clib_mem_free (vec_header ((V), (H))); \ V = 0; \ } \ } while (0) /** \brief Free vector's memory (no header). @param V pointer to a vector @return V (value-result parameter, V=0) */ #define vec_free(V) vec_free_h(V,0) /**\brief Free vector user header (syntactic sugar) @param h vector header @void */ #define vec_free_header(h) clib_mem_free (h) /** \brief Return copy of vector (general version). @param V pointer to a vector @param H size of header in bytes @param A alignment (may be zero) @return Vdup copy of vector */ #define vec_dup_ha(V,H,A) \ ({ \ __typeof__ ((V)[0]) * _v(v) = 0; \ uword _v(l) = vec_len (V); \ if (_v(l) > 0) \ { \ vec_resize_ha (_v(v), _v(l), (H), (A)); \ clib_memcpy_fast (_v(v
/*
 * Copyright (c) 2011-2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file
 * @brief LLDP packet generation implementation
 */
#include <vnet/lldp/lldp_node.h>

static void
lldp_add_chassis_id (const vnet_hw_interface_t * hw, u8 ** t0p)
{
  lldp_chassis_id_tlv_t *t = (lldp_chassis_id_tlv_t *) * t0p;

  lldp_tlv_set_code ((lldp_tlv_t *) t, LLDP_TLV_NAME (chassis_id));
  t->subtype = LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr);

  const size_t addr_len = 6;
  clib_memcpy (&t->id, hw->hw_address, addr_len);
  const size_t len =
    STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) + addr_len;
  lldp_tlv_set_length ((lldp_tlv_t *) t, len);
  *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
}

static void
lldp_add_port_id (const vnet_hw_interface_t * hw, u8 ** t0p)
{
  lldp_port_id_tlv_t *t = (lldp_port_id_tlv_t *) * t0p;

  lldp_tlv_set_code ((lldp_tlv_t *) t, LLDP_TLV_NAME (port_id));
  t->subtype = LLDP_PORT_ID_SUBTYPE_NAME (intf_name);

  const size_t name_len = vec_len (hw->name);
  clib_memcpy (&t->id, hw->name, name_len);
  const size_t len = STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype) + name_len;
  lldp_tlv_set_length ((lldp_tlv_t *) t, len);
  *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
}

static void
lldp_add_ttl (const lldp_main_t * lm, u8 ** t0p, int shutdown)
{
  lldp_ttl_tlv_t *t = (lldp_ttl_tlv_t *) * t0p;
  lldp_tlv_set_code ((lldp_tlv_t *) t, LLDP_TLV_NAME (ttl));
  if (shutdown)
    {
      t->ttl = 0;
    }
  else
    {
      if ((size_t) lm->msg_tx_interval * lm->msg_tx_hold + 1 > (1 << 16) - 1)
	{
	  t->ttl = htons ((1 << 16) - 1);
	}
      else
	{
	  t->ttl = htons (lm->msg_tx_hold * lm->msg_tx_interval + 1);
	}
    }
  const size_t len = STRUCT_SIZE_OF (lldp_ttl_tlv_t, ttl);
  lldp_tlv_set_length ((lldp_tlv_t *) t, len);
  *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
}

static void
lldp_add_sys_name (const lldp_main_t * lm, u8 ** t0p)
{
  const size_t len = vec_len (lm->sys_name);
  if (len)
    {
      lldp_tlv_t *t = (lldp_tlv_t *) * t0p;
      lldp_tlv_set_code (t, LLDP_TLV_NAME (sys_name));
      lldp_tlv_set_length (t, len);
      clib_memcpy (t->v, lm->sys_name, len);
      *t0p += STRUCT_SIZE_OF (lldp_tlv_t<