1 /*******************************************************************
2  *
3  * OpenLLDP TX Statemachine
4  *
5  * See LICENSE file for more info.
6  *
7  * File: lldp_sm_tx.c
8  *
9  * Authors: Terry Simons (terry.simons@gmail.com)
10  *
11  *******************************************************************/
12 
13 #ifndef WIN32
14 #include <stdint.h>
15 #include <strings.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #else  // WIN32
20 #include <Winsock2.h>
21 #include "stdintwin.h"
22 #endif // WIN32
23 #include <string.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #ifdef __LINUX__
27 #include <malloc.h>
28 #endif /* __LINUX__ */
29 #include <stdlib.h>
30 #include "tx_sm.h"
31 #include "lldp_port.h"
32 #include "lldp_debug.h"
33 #include "tlv/tlv.h"
34 #include "tlv/tlv_common.h"
35 #include "platform/framehandler.h"
36 
37 #define TRUE 1
38 #define FALSE 0
39 
40 extern struct lci_s lci;
41 
42 /* This function tests out of order TLVs to the upstream device */
mibConstrInfoLLDPDU_test_out_of_order()43 void mibConstrInfoLLDPDU_test_out_of_order() {
44 }
45 
46 /* This function tests invalid TLV creation and validation */
mibConstrInfoLLDPDU_test_invalid_tlv()47 void mibConstrInfoLLDPDU_test_invalid_tlv() {
48 }
49 
mibConstrInfoLLDPDU(struct lldp_port * lldp_port)50 void mibConstrInfoLLDPDU(struct lldp_port *lldp_port)
51 {
52     // This code will only work for Linux!
53     struct eth_hdr tx_hdr;
54     struct lldp_tlv_list *tlv_list = NULL;
55 	struct lldp_flat_tlv *tlv      = NULL;
56 	struct lldp_tlv_list *tmp      = 0;
57 	uint32_t frame_offset          = 0;
58 
59     // Update our port information so it's current. ;)
60     // This should be replaced with OS-specific events if possible...
61     refreshInterfaceData(lldp_port);
62 
63     /* As per section 10.3.1, verify the destination and ethertype */
64     tx_hdr.dst[0] = 0x01;
65     tx_hdr.dst[1] = 0x80;
66     tx_hdr.dst[2] = 0xc2;
67     tx_hdr.dst[3] = 0x00;
68     tx_hdr.dst[4] = 0x00;
69     tx_hdr.dst[5] = 0x0e;
70 
71 #ifndef WIN32
72 #warning Write a test that sends a frame with a source & dest of 0180c2000e...
73 #endif // WIN32
74     tx_hdr.src[0] = lldp_port->source_mac[0];
75     tx_hdr.src[1] = lldp_port->source_mac[1];
76     tx_hdr.src[2] = lldp_port->source_mac[2];
77     tx_hdr.src[3] = lldp_port->source_mac[3];
78     tx_hdr.src[4] = lldp_port->source_mac[4];
79     tx_hdr.src[5] = lldp_port->source_mac[5];
80 
81     tx_hdr.ethertype = htons(0x88cc);
82 
83     // How far into our frame should we offset our copies?
84     frame_offset = 0;
85 
86 	if(lldp_port->tx.frame != NULL)
87 	{
88 		memcpy(&lldp_port->tx.frame[frame_offset], &tx_hdr, sizeof(tx_hdr));
89 	}
90 
91     frame_offset += sizeof(struct eth_hdr);
92 
93     // This TLV *MUST* be first.
94     add_tlv(create_chassis_id_tlv(lldp_port), &tlv_list);
95 
96     // This TLV *MUST* be second.
97     add_tlv(create_port_id_tlv(lldp_port), &tlv_list);
98 
99     // This TLV *MUST* be third.
100     add_tlv(create_ttl_tlv(lldp_port), &tlv_list);
101 
102     add_tlv(create_port_description_tlv(lldp_port), &tlv_list);
103 
104     add_tlv(create_system_name_tlv(lldp_port), &tlv_list);
105 
106     add_tlv(create_system_description_tlv(lldp_port), &tlv_list);
107 
108     add_tlv(create_system_capabilities_tlv(lldp_port), &tlv_list);
109 
110     add_tlv(create_management_address_tlv(lldp_port), &tlv_list);
111 
112     //add LLDP-MED location identification
113     if (lci.location_data_format != -1)
114       add_tlv (create_lldpmed_location_identification_tlv (lldp_port),
115 	       &tlv_list);
116 
117     // This TLV *MUST* be last.
118     add_tlv(create_end_of_lldpdu_tlv(lldp_port), &tlv_list);
119 
120     tmp = tlv_list;
121 
122     while(tmp != NULL) {
123         tlv = flatten_tlv(tmp->tlv);
124 
125 		if(lldp_port->tx.frame != NULL)
126 		{
127 			memcpy(&lldp_port->tx.frame[frame_offset], tlv->tlv, tlv->size);
128 		}
129 
130         frame_offset += tlv->size;
131 
132         destroy_flattened_tlv(&tlv);
133 
134         tmp = tmp->next;
135     }
136 
137     destroy_tlv_list(&tlv_list);
138 
139     // Pad to 64 bytes
140     if(frame_offset < 64) {
141         lldp_port->tx.sendsize = 64;
142     } else {
143         lldp_port->tx.sendsize = frame_offset;
144     }
145 
146 }
147 
mibConstrShutdownLLDPDU(struct lldp_port * lldp_port)148 void mibConstrShutdownLLDPDU(struct lldp_port *lldp_port)
149 {
150     struct lldp_tlv *end_of_lldpdu_tlv = create_end_of_lldpdu_tlv(lldp_port);
151 
152 	debug_printf(DEBUG_NORMAL, "Would send shutdown!");
153 
154     if(validate_end_of_lldpdu_tlv(end_of_lldpdu_tlv))
155     {
156         // We only want the first 3 bytes...
157         // The type/length pair are bytes 1 & 2 (7 and 9 bits respectively)
158         // The 3rd octect should contain a 0.
159         memcpy(&lldp_port->tx.frame[0], end_of_lldpdu_tlv, 3);
160     }
161     else
162     {
163         debug_printf(DEBUG_NORMAL, "[ERROR] TLV End of LLDPDU validation faliure in %s() at line: %d!\n", __FUNCTION__, __LINE__);
164     }
165 
166     free(end_of_lldpdu_tlv);
167 }
168 
txFrame(struct lldp_port * lldp_port)169 uint8_t txFrame(struct lldp_port *lldp_port)
170 {
171     debug_printf(DEBUG_INT, "Sending LLDPDU on [%s]!\n", lldp_port->if_name);
172 
173     // Call the platform specific frame transmission code.
174     // lldp_port contains all of the necessary information to do platform-specific
175     // transmits.
176     lldp_write(lldp_port);
177 
178     if(lldp_port->tx.frame != NULL)
179       {
180 	// Should only need to memset 0x0 to sendsize, because we shouldn't have filled it more than that. ;)
181 	memset(&lldp_port->tx.frame[0], 0x0, lldp_port->tx.sendsize);
182       }
183     // Update the statsFramesOutTotal counter...
184     lldp_port->tx.statistics.statsFramesOutTotal++;
185 
186     return 0;
187 }
188 
txInitializeLLDP(struct lldp_port * lldp_port)189 uint8_t txInitializeLLDP(struct lldp_port *lldp_port)
190 {
191     /* As per IEEE 802.1AB section 10.1.1 */
192     lldp_port->tx.somethingChangedLocal = 0;
193 
194     /* Defined in 10.5.2.1 */
195     lldp_port->tx.statistics.statsFramesOutTotal = 0;
196 
197     lldp_port->tx.timers.reinitDelay   = 2;  // Recommended minimum by 802.1AB 10.5.3.3
198     lldp_port->tx.timers.msgTxHold     = 4;  // Recommended minimum by 802.1AB 10.5.3.3
199     lldp_port->tx.timers.msgTxInterval = 30; // Recommended minimum by 802.1AB 10.5.3.3
200     lldp_port->tx.timers.txDelay       = 2;  // Recommended minimum by 802.1AB 10.5.3.3
201 
202     // Unsure what to set these to...
203     lldp_port->tx.timers.txShutdownWhile = 0;
204 
205     /* Collect all of the system specific information here */
206     return 0;
207 }
208 
txChangeToState(struct lldp_port * lldp_port,uint8_t state)209 void txChangeToState(struct lldp_port *lldp_port, uint8_t state) {
210     debug_printf(DEBUG_STATE, "%s -> %s\n", txStateFromID(lldp_port->tx.state), txStateFromID(state));
211 
212     switch(state) {
213         case TX_LLDP_INITIALIZE: {
214                                      if((lldp_port->tx.state != TX_SHUTDOWN_FRAME) && lldp_port->portEnabled) {
215                                          debug_printf(DEBUG_STATE, "[ERROR] Illegal Transition: [%s] %s -> %s\n", lldp_port->if_name, txStateFromID(lldp_port->tx.state), txStateFromID(state));
216                                      }
217                                  }break;
218         case TX_IDLE: {
219                           if(!(lldp_port->tx.state == TX_LLDP_INITIALIZE ||
220                                       lldp_port->tx.state == TX_INFO_FRAME)) {
221                               debug_printf(DEBUG_STATE, "[ERROR] Illegal Transition: [%s] %s -> %s\n", lldp_port->if_name, txStateFromID(lldp_port->tx.state), txStateFromID(state));
222                           }
223 
224                           lldp_port->tx.txTTL = min(65535, (lldp_port->tx.timers.msgTxInterval * lldp_port->tx.timers.msgTxHold));
225                           lldp_port->tx.timers.txTTR = lldp_port->tx.timers.msgTxInterval;
226                           lldp_port->tx.somethingChangedLocal = FALSE;
227                           lldp_port->tx.timers.txDelayWhile = lldp_port->tx.timers.txDelay;
228                       }break;
229         case TX_SHUTDOWN_FRAME:
230         case TX_INFO_FRAME: {
231                                 if(lldp_port->tx.state != TX_IDLE) {
232                                     debug_printf(DEBUG_STATE, "[ERROR] Illegal Transition: [%s] %s -> %s\n", lldp_port->if_name, txStateFromID(lldp_port->tx.state), txStateFromID(state));
233                                 }
234                             }break;
235         default:
236                             debug_printf(DEBUG_STATE, "[ERROR] Illegal Transition: [%s] %s -> %s\n", lldp_port->if_name, txStateFromID(lldp_port->tx.state), txStateFromID(state));
237     };
238 
239     lldp_port->tx.state = state;
240 }
241 
txStateFromID(uint8_t state)242 char *txStateFromID(uint8_t state) {
243     switch(state) {
244         case TX_LLDP_INITIALIZE:
245             return "TX_LLDP_INITIALIZE";
246         case TX_IDLE:
247             return "TX_IDLE";
248         case TX_SHUTDOWN_FRAME:
249             return "TX_SHUTDOWN_FRAME";
250         case TX_INFO_FRAME:
251             return "TX_INFO_FRAME";
252     };
253 
254     debug_printf(DEBUG_NORMAL, "[ERROR] Unknown TX State: '%d'\n", state);
255     return "Unknown";
256 }
257 
txGlobalStatemachineRun(struct lldp_port * lldp_port)258 void txGlobalStatemachineRun(struct lldp_port *lldp_port) {
259     /* Sit in TX_LLDP_INITIALIZE until the next initialization */
260     if(lldp_port->portEnabled == FALSE) {
261         lldp_port->portEnabled = TRUE;
262 
263 #ifndef WIN32
264 #warning ChEAT
265 #endif // WIN32
266         txChangeToState(lldp_port, TX_LLDP_INITIALIZE);
267     }
268 
269     switch(lldp_port->tx.state) {
270         case TX_LLDP_INITIALIZE: {
271                                      if((lldp_port->adminStatus == enabledRxTx) || (lldp_port->adminStatus == enabledTxOnly)) {
272                                          txChangeToState(lldp_port, TX_IDLE);
273                                      }
274                                  }break;
275         case TX_IDLE: {
276                           // It's time to send a shutdown frame...
277                           if((lldp_port->adminStatus == disabled) || (lldp_port->adminStatus == enabledRxOnly)) {
278                               txChangeToState(lldp_port, TX_SHUTDOWN_FRAME);
279                               break;
280                           }
281 
282                           // It's time to send a frame...
283                           if((lldp_port->tx.timers.txDelayWhile == 0) && ((lldp_port->tx.timers.txTTR == 0) || (lldp_port->tx.somethingChangedLocal))) {
284                               txChangeToState(lldp_port, TX_INFO_FRAME);
285                           }
286                       }break;
287         case TX_SHUTDOWN_FRAME: {
288                                     if(lldp_port->tx.timers.txShutdownWhile == 0)
289                                         txChangeToState(lldp_port, TX_LLDP_INITIALIZE);
290                                 }break;
291         case TX_INFO_FRAME: {
292                                 txChangeToState(lldp_port, TX_IDLE);
293                             }break;
294         default:
295                             debug_printf(DEBUG_NORMAL, "[ERROR] The TX State Machine is broken!\n");
296     };
297 }
298 
299 #ifndef WIN32
min(uint16_t value1,uint16_t value2)300 uint16_t min(uint16_t value1, uint16_t value2)
301 {
302     if(value1 < value2)
303     {
304         return value1;
305     }
306 
307     return value2;
308 }
309 #endif // WIN32
310 
txStatemachineRun(struct lldp_port * lldp_port)311 void txStatemachineRun(struct lldp_port *lldp_port)
312 {
313     debug_printf(DEBUG_STATE, "%s -> %s\n", lldp_port->if_name, txStateFromID(lldp_port->tx.state));
314 
315     txGlobalStatemachineRun(lldp_port);
316 
317     switch(lldp_port->tx.state)
318     {
319         case TX_LLDP_INITIALIZE:
320             {
321                 tx_do_tx_lldp_initialize(lldp_port);
322             }break;
323         case TX_IDLE:
324             {
325                 tx_do_tx_idle(lldp_port);
326             }break;
327         case TX_SHUTDOWN_FRAME:
328             {
329                 tx_do_tx_shutdown_frame(lldp_port);
330             }break;
331         case TX_INFO_FRAME:
332             {
333                 tx_do_tx_info_frame(lldp_port);
334             }break;
335         default:
336             debug_printf(DEBUG_NORMAL, "[ERROR] The TX State Machine is broken!\n");
337     };
338 
339     tx_do_update_timers(lldp_port);
340 }
341 
tx_decrement_timer(uint16_t * timer)342 void tx_decrement_timer(uint16_t *timer) {
343   if((*timer) > 0)
344     (*timer)--;
345 }
346 
tx_do_update_timers(struct lldp_port * lldp_port)347 void tx_do_update_timers(struct lldp_port *lldp_port) {
348 
349     tx_decrement_timer(&lldp_port->tx.timers.txShutdownWhile);
350     tx_decrement_timer(&lldp_port->tx.timers.txDelayWhile);
351     tx_decrement_timer(&lldp_port->tx.timers.txTTR);
352 
353     tx_display_timers(lldp_port);
354 }
355 
tx_display_timers(struct lldp_port * lldp_port)356 void tx_display_timers(struct lldp_port *lldp_port) {
357 
358   debug_printf(DEBUG_STATE, "[IP] (%s) IP: %d.%d.%d.%d\n", lldp_port->if_name, lldp_port->source_ipaddr[0], lldp_port->source_ipaddr[1], lldp_port->source_ipaddr[2], lldp_port->source_ipaddr[3]);
359 
360     debug_printf(DEBUG_STATE, "[TIMER] (%s) txTTL: %d\n", lldp_port->if_name, lldp_port->tx.txTTL);
361     debug_printf(DEBUG_STATE, "[TIMER] (%s) txTTR: %d\n", lldp_port->if_name, lldp_port->tx.timers.txTTR);
362     debug_printf(DEBUG_STATE, "[TIMER] (%s) txDelayWhile: %d\n", lldp_port->if_name, lldp_port->tx.timers.txDelayWhile);
363     debug_printf(DEBUG_STATE, "[TIMER] (%s) txShutdownWhile: %d\n", lldp_port->if_name, lldp_port->tx.timers.txShutdownWhile);
364 }
365 
tx_do_tx_lldp_initialize(struct lldp_port * lldp_port)366 void tx_do_tx_lldp_initialize(struct lldp_port *lldp_port) {
367     /* As per 802.1AB 10.5.4.3 */
368     txInitializeLLDP(lldp_port);
369 }
370 
tx_do_tx_idle(struct lldp_port * lldp_port)371 void tx_do_tx_idle(struct lldp_port *lldp_port) {
372     /* As per 802.1AB 10.5.4.3 */
373     /* I think these belong in the change to state block...
374        lldp_port->tx.txTTL = min(65535, (lldp_port->tx.timers.msgTxInterval * lldp_port->tx.timers.msgTxHold));
375        lldp_port->tx.timers.txTTR = lldp_port->tx.timers.msgTxInterval;
376        lldp_port->tx.somethingChangedLocal = FALSE;
377        lldp_port->tx.timers.txDelayWhile = lldp_port->tx.timers.txDelay;
378        */
379 }
380 
tx_do_tx_shutdown_frame(struct lldp_port * lldp_port)381 void tx_do_tx_shutdown_frame(struct lldp_port *lldp_port) {
382     /* As per 802.1AB 10.5.4.3 */
383     mibConstrShutdownLLDPDU(lldp_port);
384     txFrame(lldp_port);
385     lldp_port->tx.timers.txShutdownWhile = lldp_port->tx.timers.reinitDelay;
386 }
387 
tx_do_tx_info_frame(struct lldp_port * lldp_port)388 void tx_do_tx_info_frame(struct lldp_port *lldp_port) {
389     /* As per 802.1AB 10.5.4.3 */
390     mibConstrInfoLLDPDU(lldp_port);
391     txFrame(lldp_port);
392 }
393