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