lacp: passive mode support [VPP-1551]
[vpp.git] / src / plugins / lacp / input.c
1 /*
2  * Copyright (c) 2017 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 #define _GNU_SOURCE
17 #include <vnet/bonding/node.h>
18 #include <lacp/node.h>
19
20 static int
21 lacp_packet_scan (vlib_main_t * vm, slave_if_t * sif)
22 {
23   lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
24
25   if (lacpdu->subtype != LACP_SUBTYPE)
26     return LACP_ERROR_UNSUPPORTED;
27
28   /*
29    * According to the spec, no checking on the version number and tlv types.
30    * But we may check the tlv lengths.
31    */
32   if ((lacpdu->actor.tlv_length != sizeof (lacp_actor_partner_t)) ||
33       (lacpdu->partner.tlv_length != sizeof (lacp_actor_partner_t)) ||
34       (lacpdu->collector.tlv_length != sizeof (lacp_collector_t)) ||
35       (lacpdu->terminator.tlv_length != 0))
36     return (LACP_ERROR_BAD_TLV);
37
38   lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
39                          LACP_RX_EVENT_PDU_RECEIVED, &sif->rx_state);
40
41   return LACP_ERROR_NONE;
42 }
43
44 static void
45 marker_fill_pdu (marker_pdu_t * marker, slave_if_t * sif)
46 {
47   marker_pdu_t *pkt = (marker_pdu_t *) sif->last_marker_pkt;
48
49   marker->marker_info = pkt->marker_info;
50   marker->marker_info.tlv_type = MARKER_RESPONSE_INFORMATION;
51 }
52
53 void
54 marker_fill_request_pdu (marker_pdu_t * marker, slave_if_t * sif)
55 {
56   marker->marker_info.tlv_type = MARKER_INFORMATION;
57   marker->marker_info.requester_port = sif->actor.port_number;
58   clib_memcpy (marker->marker_info.requester_system, sif->actor.system, 6);
59   marker->marker_info.requester_transaction_id = sif->marker_tx_id;
60   sif->marker_tx_id++;
61 }
62
63 static void
64 send_ethernet_marker_response_pdu (slave_if_t * sif)
65 {
66   lacp_main_t *lm = &lacp_main;
67   u32 *to_next;
68   ethernet_marker_pdu_t *h0;
69   vnet_hw_interface_t *hw;
70   u32 bi0;
71   vlib_buffer_t *b0;
72   vlib_frame_t *f;
73   vlib_main_t *vm = lm->vlib_main;
74   vnet_main_t *vnm = lm->vnet_main;
75
76   /*
77    * see lacp_periodic_init() to understand what's already painted
78    * into the buffer by the packet template mechanism
79    */
80   h0 = vlib_packet_template_get_packet
81     (vm, &lm->marker_packet_templates[sif->packet_template_index], &bi0);
82
83   if (!h0)
84     return;
85
86   /* Add the interface's ethernet source address */
87   hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
88
89   clib_memcpy (h0->ethernet.src_address, hw->hw_address,
90                vec_len (hw->hw_address));
91
92   marker_fill_pdu (&h0->marker, sif);
93
94   /* Set the outbound packet length */
95   b0 = vlib_get_buffer (vm, bi0);
96   b0->current_length = sizeof (ethernet_marker_pdu_t);
97   b0->current_data = 0;
98   b0->total_length_not_including_first_buffer = 0;
99
100   /* And the outbound interface */
101   vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
102
103   /* And output the packet on the correct interface */
104   f = vlib_get_frame_to_node (vm, hw->output_node_index);
105
106   to_next = vlib_frame_vector_args (f);
107   to_next[0] = bi0;
108   f->n_vectors = 1;
109
110   vlib_put_frame_to_node (vm, hw->output_node_index, f);
111   sif->last_marker_pdu_sent_time = vlib_time_now (lm->vlib_main);
112   sif->marker_pdu_sent++;
113 }
114
115 static int
116 handle_marker_protocol (vlib_main_t * vm, slave_if_t * sif)
117 {
118   marker_pdu_t *marker = (marker_pdu_t *) sif->last_marker_pkt;
119
120   /*
121    * According to the spec, no checking on the version number and tlv types.
122    * But we may check the tlv lengths.
123    */
124   if ((marker->marker_info.tlv_length != sizeof (marker_information_t)) ||
125       (marker->terminator.tlv_length != 0))
126     return (LACP_ERROR_BAD_TLV);
127
128   send_ethernet_marker_response_pdu (sif);
129
130   return LACP_ERROR_NONE;
131 }
132
133 /*
134  * lacp input routine
135  */
136 lacp_error_t
137 lacp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
138 {
139   lacp_main_t *lm = &lacp_main;
140   slave_if_t *sif;
141   uword nbytes;
142   lacp_error_t e;
143   marker_pdu_t *marker;
144   uword last_packet_signature;
145   bond_if_t *bif;
146
147   sif =
148     bond_get_slave_by_sw_if_index (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
149   if ((sif == 0) || (sif->mode != BOND_MODE_LACP))
150     {
151       return LACP_ERROR_DISABLED;
152     }
153
154   /* Handle marker protocol */
155   marker = (marker_pdu_t *) (b0->data + b0->current_data);
156   if (marker->subtype == MARKER_SUBTYPE)
157     {
158       sif->last_marker_pdu_recd_time = vlib_time_now (lm->vlib_main);
159       if (sif->last_marker_pkt)
160         _vec_len (sif->last_marker_pkt) = 0;
161       vec_validate (sif->last_marker_pkt,
162                     vlib_buffer_length_in_chain (vm, b0) - 1);
163       nbytes = vlib_buffer_contents (vm, bi0, sif->last_marker_pkt);
164       ASSERT (nbytes <= vec_len (sif->last_marker_pkt));
165       if (nbytes < sizeof (lacp_pdu_t))
166         {
167           sif->marker_bad_pdu_received++;
168           return LACP_ERROR_TOO_SMALL;
169         }
170       e = handle_marker_protocol (vm, sif);
171       sif->marker_pdu_received++;
172       return e;
173     }
174
175   /*
176    * typical clib idiom. Don't repeatedly allocate and free
177    * the per-neighbor rx buffer. Reset its apparent length to zero
178    * and reuse it.
179    */
180   if (sif->last_rx_pkt)
181     _vec_len (sif->last_rx_pkt) = 0;
182
183   /*
184    * Make sure the per-neighbor rx buffer is big enough to hold
185    * the data we're about to copy
186    */
187   vec_validate (sif->last_rx_pkt, vlib_buffer_length_in_chain (vm, b0) - 1);
188
189   /*
190    * Coalesce / copy the buffer chain into the per-neighbor
191    * rx buffer
192    */
193   nbytes = vlib_buffer_contents (vm, bi0, sif->last_rx_pkt);
194   ASSERT (nbytes <= vec_len (sif->last_rx_pkt));
195
196   sif->last_lacpdu_recd_time = vlib_time_now (lm->vlib_main);
197   if (nbytes < sizeof (lacp_pdu_t))
198     {
199       sif->bad_pdu_received++;
200       return LACP_ERROR_TOO_SMALL;
201     }
202
203   last_packet_signature =
204     hash_memory (sif->last_rx_pkt, vec_len (sif->last_rx_pkt), 0xd00b);
205
206   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
207   if (sif->last_packet_signature_valid &&
208       (sif->last_packet_signature == last_packet_signature) &&
209       hash_get (bif->active_slave_by_sw_if_index, sif->sw_if_index))
210     {
211       lacp_start_current_while_timer (lm->vlib_main, sif,
212                                       sif->ttl_in_seconds);
213       e = LACP_ERROR_CACHE_HIT;
214     }
215   else
216     {
217       /* Actually scan the packet */
218       e = lacp_packet_scan (vm, sif);
219       sif->last_packet_signature_valid = 1;
220       sif->last_packet_signature = last_packet_signature;
221     }
222   sif->pdu_received++;
223
224   if (sif->last_rx_pkt)
225     _vec_len (sif->last_rx_pkt) = 0;
226
227   return e;
228 }
229
230 /*
231  * setup neighbor hash table
232  */
233 static clib_error_t *
234 lacp_init (vlib_main_t * vm)
235 {
236   clib_error_t *error;
237
238   if ((error = vlib_call_init_function (vm, lacp_periodic_init)))
239     return error;
240
241   return 0;
242 }
243
244 VLIB_INIT_FUNCTION (lacp_init);
245
246 /*
247  * packet trace format function, very similar to
248  * lacp_packet_scan except that we call the per TLV format
249  * functions instead of the per TLV processing functions
250  */
251 u8 *
252 lacp_input_format_trace (u8 * s, va_list * args)
253 {
254   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
255   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
256   lacp_input_trace_t *t = va_arg (*args, lacp_input_trace_t *);
257   lacp_pdu_t *lacpdu = &t->pkt.lacpdu;
258   marker_pdu_t *marker = &t->pkt.marker;
259   int i, len;
260   u8 *p;
261   lacp_state_struct *state_entry;
262
263   s = format (s, "%U:\n", format_vnet_sw_if_index_name, vnet_get_main (),
264               t->sw_if_index);
265   s = format (s, "Length: %d\n", t->len);
266   if (t->len >= sizeof (lacp_pdu_t))
267     {
268       switch (lacpdu->subtype)
269         {
270         case MARKER_SUBTYPE:
271           if (marker->version_number == MARKER_PROTOCOL_VERSION)
272             s = format (s, "  Markerv1\n");
273           else
274             s = format (s, "  Subtype %u, Version %u\n", marker->subtype,
275                         marker->version_number);
276           s = format (s, "  Marker Information TLV: type %u\n",
277                       marker->marker_info.tlv_type);
278           s = format (s, "  Marker Information TLV: length %u\n",
279                       marker->marker_info.tlv_length);
280           s = format (s, "  Requester port: %u\n",
281                       marker->marker_info.requester_port);
282           s = format (s, "  Requester system: %U\n", format_ethernet_address,
283                       marker->marker_info.requester_system);
284           s = format (s, "  Requester transaction ID: %u\n",
285                       marker->marker_info.requester_transaction_id);
286           break;
287
288         case LACP_SUBTYPE:
289           if (lacpdu->version_number == LACP_ACTOR_LACP_VERSION)
290             s = format (s, "  LACPv1\n");
291           else
292             s = format (s, "  Subtype %u, Version %u\n", lacpdu->subtype,
293                         lacpdu->version_number);
294           s = format (s, "  Actor Information TLV: length %u\n",
295                       lacpdu->actor.tlv_length);
296           s = format (s, "    System %U\n", format_ethernet_address,
297                       lacpdu->actor.port_info.system);
298           s = format (s, "    System priority %u\n",
299                       ntohs (lacpdu->actor.port_info.system_priority));
300           s = format (s, "    Key %u\n", ntohs (lacpdu->actor.port_info.key));
301           s = format (s, "    Port priority %u\n",
302                       ntohs (lacpdu->actor.port_info.port_priority));
303           s = format (s, "    Port number %u\n",
304                       ntohs (lacpdu->actor.port_info.port_number));
305           s = format (s, "    State 0x%x\n", lacpdu->actor.port_info.state);
306           state_entry = (lacp_state_struct *) & lacp_state_array;
307           while (state_entry->str)
308             {
309               if (lacpdu->actor.port_info.state & (1 << state_entry->bit))
310                 s = format (s, "      %s (%d)\n", state_entry->str,
311                             state_entry->bit);
312               state_entry++;
313             }
314
315           s = format (s, "  Partner Information TLV: length %u\n",
316                       lacpdu->partner.tlv_length);
317           s = format (s, "    System %U\n", format_ethernet_address,
318                       lacpdu->partner.port_info.system);
319           s = format (s, "    System priority %u\n",
320                       ntohs (lacpdu->partner.port_info.system_priority));
321           s =
322             format (s, "    Key %u\n", ntohs (lacpdu->partner.port_info.key));
323           s =
324             format (s, "    Port priority %u\n",
325                     ntohs (lacpdu->partner.port_info.port_priority));
326           s =
327             format (s, "    Port number %u\n",
328                     ntohs (lacpdu->partner.port_info.port_number));
329           s = format (s, "    State 0x%x\n", lacpdu->partner.port_info.state);
330           state_entry = (lacp_state_struct *) & lacp_state_array;
331           while (state_entry->str)
332             {
333               if (lacpdu->partner.port_info.state & (1 << state_entry->bit))
334                 s = format (s, "      %s (%d)\n", state_entry->str,
335                             state_entry->bit);
336               state_entry++;
337             }
338           break;
339
340         default:
341           break;
342         }
343     }
344
345   if (t->len > sizeof (lacp_pdu_t))
346     len = sizeof (lacp_pdu_t);
347   else
348     len = t->len;
349   p = (u8 *) lacpdu;
350   for (i = 0; i < len; i++)
351     {
352       if ((i % 16) == 0)
353         {
354           if (i)
355             s = format (s, "\n");
356           s = format (s, "  0x%04x: ", i);
357         }
358       if ((i % 2) == 0)
359         s = format (s, " ");
360       s = format (s, "%02x", p[i]);
361     }
362
363   return s;
364 }
365
366 /*
367  * fd.io coding-style-patch-verification: ON
368  *
369  * Local Variables:
370  * eval: (c-set-style "gnu")
371  * End:
372  */