8698ec976ac6dbe23dbdd8e3af35dd2e9e227156
[vpp.git] / src / vnet / lldp / lldp_output.c
1 /*
2  * Copyright (c) 2011-2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief LLDP packet generation implementation
18  */
19 #include <vnet/lldp/lldp_node.h>
20
21 static void
22 lldp_add_chassis_id (const vnet_hw_interface_t * hw, u8 ** t0p)
23 {
24   lldp_chassis_id_tlv_t *t = (lldp_chassis_id_tlv_t *) * t0p;
25
26   lldp_tlv_set_code ((lldp_tlv_t *) t, LLDP_TLV_NAME (chassis_id));
27   t->subtype = LLDP_CHASS_ID_SUBTYPE_NAME (mac_addr);
28
29   const size_t addr_len = 6;
30   clib_memcpy (&t->id, hw->hw_address, addr_len);
31   const size_t len =
32     STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) + addr_len;
33   lldp_tlv_set_length ((lldp_tlv_t *) t, len);
34   *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
35 }
36
37 static void
38 lldp_add_port_id (const vnet_hw_interface_t * hw, u8 ** t0p)
39 {
40   lldp_port_id_tlv_t *t = (lldp_port_id_tlv_t *) * t0p;
41
42   lldp_tlv_set_code ((lldp_tlv_t *) t, LLDP_TLV_NAME (port_id));
43   t->subtype = LLDP_PORT_ID_SUBTYPE_NAME (intf_name);
44
45   const size_t name_len = vec_len (hw->name);
46   clib_memcpy (&t->id, hw->name, name_len);
47   const size_t len = STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype) + name_len;
48   lldp_tlv_set_length ((lldp_tlv_t *) t, len);
49   *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
50 }
51
52 static void
53 lldp_add_ttl (const lldp_main_t * lm, u8 ** t0p, int shutdown)
54 {
55   lldp_ttl_tlv_t *t = (lldp_ttl_tlv_t *) * t0p;
56   lldp_tlv_set_code ((lldp_tlv_t *) t, LLDP_TLV_NAME (ttl));
57   if (shutdown)
58     {
59       t->ttl = 0;
60     }
61   else
62     {
63       if ((size_t) lm->msg_tx_interval * lm->msg_tx_hold + 1 > (1 << 16) - 1)
64         {
65           t->ttl = htons ((1 << 16) - 1);
66         }
67       else
68         {
69           t->ttl = htons (lm->msg_tx_hold * lm->msg_tx_interval + 1);
70         }
71     }
72   const size_t len = STRUCT_SIZE_OF (lldp_ttl_tlv_t, ttl);
73   lldp_tlv_set_length ((lldp_tlv_t *) t, len);
74   *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
75 }
76
77 static void
78 lldp_add_port_desc (const lldp_main_t * lm, lldp_intf_t * n, u8 ** t0p)
79 {
80   const size_t len = vec_len (n->port_desc);
81   if (len)
82     {
83       lldp_tlv_t *t = (lldp_tlv_t *) * t0p;
84       lldp_tlv_set_code (t, LLDP_TLV_NAME (port_desc));
85       lldp_tlv_set_length (t, len);
86       clib_memcpy (t->v, n->port_desc, len);
87       *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
88     }
89 }
90
91 static void
92 lldp_add_sys_name (const lldp_main_t * lm, u8 ** t0p)
93 {
94   const size_t len = vec_len (lm->sys_name);
95   if (len)
96     {
97       lldp_tlv_t *t = (lldp_tlv_t *) * t0p;
98       lldp_tlv_set_code (t, LLDP_TLV_NAME (sys_name));
99       lldp_tlv_set_length (t, len);
100       clib_memcpy (t->v, lm->sys_name, len);
101       *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head) + len;
102     }
103 }
104
105 static void
106 lldp_add_pdu_end (u8 ** t0p)
107 {
108   lldp_tlv_t *t = (lldp_tlv_t *) * t0p;
109   lldp_tlv_set_code (t, LLDP_TLV_NAME (pdu_end));
110   lldp_tlv_set_length (t, 0);
111   *t0p += STRUCT_SIZE_OF (lldp_tlv_t, head);
112 }
113
114 static void
115 lldp_add_tlvs (lldp_main_t * lm, vnet_hw_interface_t * hw, u8 ** t0p,
116                int shutdown, lldp_intf_t * n)
117 {
118   lldp_add_chassis_id (hw, t0p);
119   lldp_add_port_id (hw, t0p);
120   lldp_add_ttl (lm, t0p, shutdown);
121   lldp_add_port_desc (lm, n, t0p);
122   lldp_add_sys_name (lm, t0p);
123   lldp_add_pdu_end (t0p);
124 }
125
126 /*
127  * send a lldp pkt on an ethernet interface
128  */
129 void
130 lldp_send_ethernet (lldp_main_t * lm, lldp_intf_t * n, int shutdown)
131 {
132   u32 *to_next;
133   ethernet_header_t *h0;
134   vnet_hw_interface_t *hw;
135   u32 bi0;
136   vlib_buffer_t *b0;
137   u8 *t0;
138   vlib_frame_t *f;
139   vlib_main_t *vm = lm->vlib_main;
140   vnet_main_t *vnm = lm->vnet_main;
141
142   /*
143    * see lldp_template_init() to understand what's already painted
144    * into the buffer by the packet template mechanism
145    */
146   h0 = vlib_packet_template_get_packet (vm, &lm->packet_template, &bi0);
147
148   if (!h0)
149     return;
150
151   /* Add the interface's ethernet source address */
152   hw = vnet_get_hw_interface (vnm, n->hw_if_index);
153
154   clib_memcpy (h0->src_address, hw->hw_address, vec_len (hw->hw_address));
155
156   u8 *data = ((u8 *) h0) + sizeof (*h0);
157   t0 = data;
158
159   /* add TLVs */
160   lldp_add_tlvs (lm, hw, &t0, shutdown, n);
161
162   /* Set the outbound packet length */
163   b0 = vlib_get_buffer (vm, bi0);
164   b0->current_length = sizeof (*h0) + t0 - data;
165
166   /* And the outbound interface */
167   vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
168
169   /* And output the packet on the correct interface */
170   f = vlib_get_frame_to_node (vm, hw->output_node_index);
171   to_next = vlib_frame_vector_args (f);
172   to_next[0] = bi0;
173   f->n_vectors = 1;
174
175   vlib_put_frame_to_node (vm, hw->output_node_index, f);
176   n->last_sent = vlib_time_now (vm);
177 }
178
179 void
180 lldp_delete_intf (lldp_main_t * lm, lldp_intf_t * n)
181 {
182   if (n)
183     {
184       lldp_unschedule_intf (lm, n);
185       hash_unset (lm->intf_by_hw_if_index, n->hw_if_index);
186       vec_free (n->chassis_id);
187       vec_free (n->port_id);
188       vec_free (n->port_desc);
189       pool_put (lm->intfs, n);
190     }
191 }
192
193 static clib_error_t *
194 lldp_template_init (vlib_main_t * vm)
195 {
196   lldp_main_t *lm = &lldp_main;
197
198   /* Create the ethernet lldp packet template */
199   {
200     ethernet_header_t h;
201
202     memset (&h, 0, sizeof (h));
203
204     /*
205      * Send to 01:80:C2:00:00:0E - propagation constrained to a single
206      * physical link - stopped by all type of bridge
207      */
208     h.dst_address[0] = 0x01;
209     h.dst_address[1] = 0x80;
210     h.dst_address[2] = 0xC2;
211     /* h.dst_address[3] = 0x00; (memset) */
212     /* h.dst_address[4] = 0x00; (memset) */
213     h.dst_address[5] = 0x0E;
214
215     /* leave src address blank (fill in at send time) */
216
217     h.type = htons (ETHERNET_TYPE_802_1_LLDP);
218
219     vlib_packet_template_init (vm, &lm->packet_template,
220                                /* data */ &h, sizeof (h),
221                                /* alloc chunk size */ 8, "lldp-ethernet");
222   }
223
224   return 0;
225 }
226
227 VLIB_INIT_FUNCTION (lldp_template_init);
228
229 /*
230  * fd.io coding-style-patch-verification: ON
231  *
232  * Local Variables:
233  * eval: (c-set-style "gnu")
234  * End:
235  */