]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * IS-IS Rout(e)ing protocol - isis_misc.h | |
3 | * Miscellanous routines | |
4 | * | |
5 | * Copyright (C) 2001,2002 Sampo Saaristo | |
6 | * Tampere University of Technology | |
7 | * Institute of Communications Engineering | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public Licenseas published by the Free | |
11 | * Software Foundation; either version 2 of the License, or (at your option) | |
12 | * any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful,but WITHOUT | |
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | * more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; see the file COPYING; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | */ | |
23 | ||
24 | #include <zebra.h> | |
25 | ||
26 | #include "stream.h" | |
27 | #include "vty.h" | |
28 | #include "hash.h" | |
29 | #include "if.h" | |
30 | #include "command.h" | |
31 | #include "log_int.h" | |
32 | ||
33 | #include "isisd/dict.h" | |
34 | #include "isisd/isis_constants.h" | |
35 | #include "isisd/isis_common.h" | |
36 | #include "isisd/isis_flags.h" | |
37 | #include "isisd/isis_circuit.h" | |
38 | #include "isisd/isis_csm.h" | |
39 | #include "isisd/isisd.h" | |
40 | #include "isisd/isis_misc.h" | |
41 | ||
42 | #include "isisd/isis_lsp.h" | |
43 | #include "isisd/isis_constants.h" | |
44 | #include "isisd/isis_adjacency.h" | |
45 | #include "isisd/isis_dynhn.h" | |
46 | ||
47 | /* staticly assigned vars for printing purposes */ | |
48 | struct in_addr new_prefix; | |
49 | /* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ | |
50 | /* + place for #0 termination */ | |
51 | char isonet[51]; | |
52 | /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ | |
53 | char datestring[20]; | |
54 | char nlpidstring[30]; | |
55 | ||
56 | /* | |
57 | * This converts the isonet to its printable format | |
58 | */ | |
59 | const char *isonet_print(const uint8_t *from, int len) | |
60 | { | |
61 | int i = 0; | |
62 | char *pos = isonet; | |
63 | ||
64 | if (!from) | |
65 | return "unknown"; | |
66 | ||
67 | while (i < len) { | |
68 | if (i & 1) { | |
69 | sprintf(pos, "%02x", *(from + i)); | |
70 | pos += 2; | |
71 | } else { | |
72 | if (i == (len - 1)) { /* No dot at the end of address */ | |
73 | sprintf(pos, "%02x", *(from + i)); | |
74 | pos += 2; | |
75 | } else { | |
76 | sprintf(pos, "%02x.", *(from + i)); | |
77 | pos += 3; | |
78 | } | |
79 | } | |
80 | i++; | |
81 | } | |
82 | *(pos) = '\0'; | |
83 | return isonet; | |
84 | } | |
85 | ||
86 | /* | |
87 | * Returns 0 on error, length of buff on ok | |
88 | * extract dot from the dotted str, and insert all the number in a buff | |
89 | */ | |
90 | int dotformat2buff(uint8_t *buff, const char *dotted) | |
91 | { | |
92 | int dotlen, len = 0; | |
93 | const char *pos = dotted; | |
94 | uint8_t number[3]; | |
95 | int nextdotpos = 2; | |
96 | ||
97 | number[2] = '\0'; | |
98 | dotlen = strlen(dotted); | |
99 | if (dotlen > 50) { | |
100 | /* this can't be an iso net, its too long */ | |
101 | return 0; | |
102 | } | |
103 | ||
104 | while ((pos - dotted) < dotlen && len < 20) { | |
105 | if (*pos == '.') { | |
106 | /* we expect the . at 2, and than every 5 */ | |
107 | if ((pos - dotted) != nextdotpos) { | |
108 | len = 0; | |
109 | break; | |
110 | } | |
111 | nextdotpos += 5; | |
112 | pos++; | |
113 | continue; | |
114 | } | |
115 | /* we must have at least two chars left here */ | |
116 | if (dotlen - (pos - dotted) < 2) { | |
117 | len = 0; | |
118 | break; | |
119 | } | |
120 | ||
121 | if ((isxdigit((int)*pos)) && (isxdigit((int)*(pos + 1)))) { | |
122 | memcpy(number, pos, 2); | |
123 | pos += 2; | |
124 | } else { | |
125 | len = 0; | |
126 | break; | |
127 | } | |
128 | ||
129 | *(buff + len) = (char)strtol((char *)number, NULL, 16); | |
130 | len++; | |
131 | } | |
132 | ||
133 | return len; | |
134 | } | |
135 | ||
136 | /* | |
137 | * conversion of XXXX.XXXX.XXXX to memory | |
138 | */ | |
139 | int sysid2buff(uint8_t *buff, const char *dotted) | |
140 | { | |
141 | int len = 0; | |
142 | const char *pos = dotted; | |
143 | uint8_t number[3]; | |
144 | ||
145 | number[2] = '\0'; | |
146 | // surely not a sysid_string if not 14 length | |
147 | if (strlen(dotted) != 14) { | |
148 | return 0; | |
149 | } | |
150 | ||
151 | while (len < ISIS_SYS_ID_LEN) { | |
152 | if (*pos == '.') { | |
153 | /* the . is not positioned correctly */ | |
154 | if (((pos - dotted) != 4) && ((pos - dotted) != 9)) { | |
155 | len = 0; | |
156 | break; | |
157 | } | |
158 | pos++; | |
159 | continue; | |
160 | } | |
161 | if ((isxdigit((int)*pos)) && (isxdigit((int)*(pos + 1)))) { | |
162 | memcpy(number, pos, 2); | |
163 | pos += 2; | |
164 | } else { | |
165 | len = 0; | |
166 | break; | |
167 | } | |
168 | ||
169 | *(buff + len) = (char)strtol((char *)number, NULL, 16); | |
170 | len++; | |
171 | } | |
172 | ||
173 | return len; | |
174 | } | |
175 | ||
176 | const char *nlpid2str(uint8_t nlpid) | |
177 | { | |
178 | static char buf[4]; | |
179 | switch (nlpid) { | |
180 | case NLPID_IP: | |
181 | return "IPv4"; | |
182 | case NLPID_IPV6: | |
183 | return "IPv6"; | |
184 | case NLPID_SNAP: | |
185 | return "SNAP"; | |
186 | case NLPID_CLNP: | |
187 | return "CLNP"; | |
188 | case NLPID_ESIS: | |
189 | return "ES-IS"; | |
190 | default: | |
191 | snprintf(buf, sizeof(buf), "%" PRIu8, nlpid); | |
192 | return buf; | |
193 | } | |
194 | } | |
195 | ||
196 | /* | |
197 | * converts the nlpids struct (filled by TLV #129) | |
198 | * into a string | |
199 | */ | |
200 | ||
201 | char *nlpid2string(struct nlpids *nlpids) | |
202 | { | |
203 | char *pos = nlpidstring; | |
204 | int i; | |
205 | ||
206 | for (i = 0; i < nlpids->count; i++) { | |
207 | pos += sprintf(pos, "%s", nlpid2str(nlpids->nlpids[i])); | |
208 | if (nlpids->count - i > 1) | |
209 | pos += sprintf(pos, ", "); | |
210 | } | |
211 | ||
212 | *(pos) = '\0'; | |
213 | ||
214 | return nlpidstring; | |
215 | } | |
216 | ||
217 | /* | |
218 | * Returns 0 on error, IS-IS Circuit Type on ok | |
219 | */ | |
220 | int string2circuit_t(const char *str) | |
221 | { | |
222 | ||
223 | if (!str) | |
224 | return 0; | |
225 | ||
226 | if (!strcmp(str, "level-1")) | |
227 | return IS_LEVEL_1; | |
228 | ||
229 | if (!strcmp(str, "level-2-only") || !strcmp(str, "level-2")) | |
230 | return IS_LEVEL_2; | |
231 | ||
232 | if (!strcmp(str, "level-1-2")) | |
233 | return IS_LEVEL_1_AND_2; | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | const char *circuit_state2string(int state) | |
239 | { | |
240 | ||
241 | switch (state) { | |
242 | case C_STATE_INIT: | |
243 | return "Init"; | |
244 | case C_STATE_CONF: | |
245 | return "Config"; | |
246 | case C_STATE_UP: | |
247 | return "Up"; | |
248 | default: | |
249 | return "Unknown"; | |
250 | } | |
251 | return NULL; | |
252 | } | |
253 | ||
254 | const char *circuit_type2string(int type) | |
255 | { | |
256 | ||
257 | switch (type) { | |
258 | case CIRCUIT_T_P2P: | |
259 | return "p2p"; | |
260 | case CIRCUIT_T_BROADCAST: | |
261 | return "lan"; | |
262 | case CIRCUIT_T_LOOPBACK: | |
263 | return "loopback"; | |
264 | default: | |
265 | return "Unknown"; | |
266 | } | |
267 | return NULL; | |
268 | } | |
269 | ||
270 | const char *circuit_t2string(int circuit_t) | |
271 | { | |
272 | switch (circuit_t) { | |
273 | case IS_LEVEL_1: | |
274 | return "L1"; | |
275 | case IS_LEVEL_2: | |
276 | return "L2"; | |
277 | case IS_LEVEL_1_AND_2: | |
278 | return "L1L2"; | |
279 | default: | |
280 | return "??"; | |
281 | } | |
282 | ||
283 | return NULL; /* not reached */ | |
284 | } | |
285 | ||
286 | const char *syst2string(int type) | |
287 | { | |
288 | switch (type) { | |
289 | case ISIS_SYSTYPE_ES: | |
290 | return "ES"; | |
291 | case ISIS_SYSTYPE_IS: | |
292 | return "IS"; | |
293 | case ISIS_SYSTYPE_L1_IS: | |
294 | return "1"; | |
295 | case ISIS_SYSTYPE_L2_IS: | |
296 | return "2"; | |
297 | default: | |
298 | return "??"; | |
299 | } | |
300 | ||
301 | return NULL; /* not reached */ | |
302 | } | |
303 | ||
304 | /* | |
305 | * Print functions - we print to static vars | |
306 | */ | |
307 | const char *snpa_print(const uint8_t *from) | |
308 | { | |
309 | return isis_format_id(from, ISIS_SYS_ID_LEN); | |
310 | } | |
311 | ||
312 | const char *sysid_print(const uint8_t *from) | |
313 | { | |
314 | return isis_format_id(from, ISIS_SYS_ID_LEN); | |
315 | } | |
316 | ||
317 | const char *rawlspid_print(const uint8_t *from) | |
318 | { | |
319 | return isis_format_id(from, 8); | |
320 | } | |
321 | ||
322 | #define FORMAT_ID_SIZE sizeof("0000.0000.0000.00-00") | |
323 | const char *isis_format_id(const uint8_t *id, size_t len) | |
324 | { | |
325 | #define FORMAT_BUF_COUNT 4 | |
326 | static char buf_ring[FORMAT_BUF_COUNT][FORMAT_ID_SIZE]; | |
327 | static size_t cur_buf = 0; | |
328 | ||
329 | char *rv; | |
330 | ||
331 | cur_buf++; | |
332 | if (cur_buf >= FORMAT_BUF_COUNT) | |
333 | cur_buf = 0; | |
334 | ||
335 | rv = buf_ring[cur_buf]; | |
336 | ||
337 | if (!id) { | |
338 | snprintf(rv, FORMAT_ID_SIZE, "unknown"); | |
339 | return rv; | |
340 | } | |
341 | ||
342 | if (len < 6) { | |
343 | snprintf(rv, FORMAT_ID_SIZE, "Short ID"); | |
344 | return rv; | |
345 | } | |
346 | ||
347 | snprintf(rv, FORMAT_ID_SIZE, "%02x%02x.%02x%02x.%02x%02x", id[0], id[1], | |
348 | id[2], id[3], id[4], id[5]); | |
349 | ||
350 | if (len > 6) | |
351 | snprintf(rv + 14, FORMAT_ID_SIZE - 14, ".%02x", id[6]); | |
352 | if (len > 7) | |
353 | snprintf(rv + 17, FORMAT_ID_SIZE - 17, "-%02x", id[7]); | |
354 | ||
355 | return rv; | |
356 | } | |
357 | ||
358 | const char *time2string(uint32_t time) | |
359 | { | |
360 | char *pos = datestring; | |
361 | uint32_t rest; | |
362 | ||
363 | if (time == 0) | |
364 | return "-"; | |
365 | ||
366 | if (time / SECS_PER_YEAR) | |
367 | pos += sprintf(pos, "%uY", time / SECS_PER_YEAR); | |
368 | rest = time % SECS_PER_YEAR; | |
369 | if (rest / SECS_PER_MONTH) | |
370 | pos += sprintf(pos, "%uM", rest / SECS_PER_MONTH); | |
371 | rest = rest % SECS_PER_MONTH; | |
372 | if (rest / SECS_PER_WEEK) | |
373 | pos += sprintf(pos, "%uw", rest / SECS_PER_WEEK); | |
374 | rest = rest % SECS_PER_WEEK; | |
375 | if (rest / SECS_PER_DAY) | |
376 | pos += sprintf(pos, "%ud", rest / SECS_PER_DAY); | |
377 | rest = rest % SECS_PER_DAY; | |
378 | if (rest / SECS_PER_HOUR) | |
379 | pos += sprintf(pos, "%uh", rest / SECS_PER_HOUR); | |
380 | rest = rest % SECS_PER_HOUR; | |
381 | if (rest / SECS_PER_MINUTE) | |
382 | pos += sprintf(pos, "%um", rest / SECS_PER_MINUTE); | |
383 | rest = rest % SECS_PER_MINUTE; | |
384 | if (rest) | |
385 | pos += sprintf(pos, "%us", rest); | |
386 | ||
387 | *(pos) = 0; | |
388 | ||
389 | return datestring; | |
390 | } | |
391 | ||
392 | /* | |
393 | * routine to decrement a timer by a random | |
394 | * number | |
395 | * | |
396 | * first argument is the timer and the second is | |
397 | * the jitter | |
398 | */ | |
399 | unsigned long isis_jitter(unsigned long timer, unsigned long jitter) | |
400 | { | |
401 | int j, k; | |
402 | ||
403 | if (jitter >= 100) | |
404 | return timer; | |
405 | ||
406 | if (timer == 1) | |
407 | return timer; | |
408 | /* | |
409 | * randomizing just the percent value provides | |
410 | * no good random numbers - hence the spread | |
411 | * to RANDOM_SPREAD (100000), which is ok as | |
412 | * most IS-IS timers are no longer than 16 bit | |
413 | */ | |
414 | ||
415 | j = 1 + (int)((RANDOM_SPREAD * random()) / (RAND_MAX + 1.0)); | |
416 | ||
417 | k = timer - (timer * (100 - jitter)) / 100; | |
418 | ||
419 | timer = timer - (k * j / RANDOM_SPREAD); | |
420 | ||
421 | return timer; | |
422 | } | |
423 | ||
424 | struct in_addr newprefix2inaddr(uint8_t *prefix_start, uint8_t prefix_masklen) | |
425 | { | |
426 | memset(&new_prefix, 0, sizeof(new_prefix)); | |
427 | memcpy(&new_prefix, prefix_start, | |
428 | (prefix_masklen & 0x3F) | |
429 | ? ((((prefix_masklen & 0x3F) - 1) >> 3) + 1) | |
430 | : 0); | |
431 | return new_prefix; | |
432 | } | |
433 | ||
434 | /* | |
435 | * Returns the dynamic hostname associated with the passed system ID. | |
436 | * If no dynamic hostname found then returns formatted system ID. | |
437 | */ | |
438 | const char *print_sys_hostname(const uint8_t *sysid) | |
439 | { | |
440 | struct isis_dynhn *dyn; | |
441 | ||
442 | if (!sysid) | |
443 | return "nullsysid"; | |
444 | ||
445 | /* For our system ID return our host name */ | |
446 | if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0) | |
447 | return cmd_hostname_get(); | |
448 | ||
449 | dyn = dynhn_find_by_id(sysid); | |
450 | if (dyn) | |
451 | return dyn->hostname; | |
452 | ||
453 | return sysid_print(sysid); | |
454 | } | |
455 | ||
456 | /* | |
457 | * This function is a generic utility that logs data of given length. | |
458 | * Move this to a shared lib so that any protocol can use it. | |
459 | */ | |
460 | void zlog_dump_data(void *data, int len) | |
461 | { | |
462 | int i; | |
463 | unsigned char *p; | |
464 | unsigned char c; | |
465 | char bytestr[4]; | |
466 | char addrstr[10]; | |
467 | char hexstr[16 * 3 + 5]; | |
468 | char charstr[16 * 1 + 5]; | |
469 | ||
470 | p = data; | |
471 | memset(bytestr, 0, sizeof(bytestr)); | |
472 | memset(addrstr, 0, sizeof(addrstr)); | |
473 | memset(hexstr, 0, sizeof(hexstr)); | |
474 | memset(charstr, 0, sizeof(charstr)); | |
475 | ||
476 | for (i = 1; i <= len; i++) { | |
477 | c = *p; | |
478 | if (isalnum(c) == 0) | |
479 | c = '.'; | |
480 | ||
481 | /* store address for this line */ | |
482 | if ((i % 16) == 1) | |
483 | snprintf(addrstr, sizeof(addrstr), "%p", p); | |
484 | ||
485 | /* store hex str (for left side) */ | |
486 | snprintf(bytestr, sizeof(bytestr), "%02X ", *p); | |
487 | strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); | |
488 | ||
489 | /* store char str (for right side) */ | |
490 | snprintf(bytestr, sizeof(bytestr), "%c", c); | |
491 | strncat(charstr, bytestr, | |
492 | sizeof(charstr) - strlen(charstr) - 1); | |
493 | ||
494 | if ((i % 16) == 0) { | |
495 | /* line completed */ | |
496 | zlog_debug("[%8.8s] %-50.50s %s", addrstr, hexstr, | |
497 | charstr); | |
498 | hexstr[0] = 0; | |
499 | charstr[0] = 0; | |
500 | } else if ((i % 8) == 0) { | |
501 | /* half line: add whitespaces */ | |
502 | strncat(hexstr, " ", | |
503 | sizeof(hexstr) - strlen(hexstr) - 1); | |
504 | strncat(charstr, " ", | |
505 | sizeof(charstr) - strlen(charstr) - 1); | |
506 | } | |
507 | p++; /* next byte */ | |
508 | } | |
509 | ||
510 | /* print rest of buffer if not empty */ | |
511 | if (strlen(hexstr) > 0) | |
512 | zlog_debug("[%8.8s] %-50.50s %s", addrstr, hexstr, charstr); | |
513 | return; | |
514 | } | |
515 | ||
516 | static char *qasprintf(const char *format, va_list ap) | |
517 | { | |
518 | va_list aq; | |
519 | va_copy(aq, ap); | |
520 | ||
521 | int size = 0; | |
522 | char *p = NULL; | |
523 | ||
524 | size = vsnprintf(p, size, format, ap); | |
525 | ||
526 | if (size < 0) { | |
527 | va_end(aq); | |
528 | return NULL; | |
529 | } | |
530 | ||
531 | size++; | |
532 | p = XMALLOC(MTYPE_TMP, size); | |
533 | ||
534 | size = vsnprintf(p, size, format, aq); | |
535 | va_end(aq); | |
536 | ||
537 | if (size < 0) { | |
538 | XFREE(MTYPE_TMP, p); | |
539 | return NULL; | |
540 | } | |
541 | ||
542 | return p; | |
543 | } | |
544 | ||
545 | void log_multiline(int priority, const char *prefix, const char *format, ...) | |
546 | { | |
547 | va_list ap; | |
548 | char *p; | |
549 | ||
550 | va_start(ap, format); | |
551 | p = qasprintf(format, ap); | |
552 | va_end(ap); | |
553 | ||
554 | if (!p) | |
555 | return; | |
556 | ||
557 | char *saveptr = NULL; | |
558 | for (char *line = strtok_r(p, "\n", &saveptr); line; | |
559 | line = strtok_r(NULL, "\n", &saveptr)) { | |
560 | zlog(priority, "%s%s", prefix, line); | |
561 | } | |
562 | ||
563 | XFREE(MTYPE_TMP, p); | |
564 | } | |
565 | ||
566 | void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) | |
567 | { | |
568 | va_list ap; | |
569 | char *p; | |
570 | ||
571 | va_start(ap, format); | |
572 | p = qasprintf(format, ap); | |
573 | va_end(ap); | |
574 | ||
575 | if (!p) | |
576 | return; | |
577 | ||
578 | char *saveptr = NULL; | |
579 | for (char *line = strtok_r(p, "\n", &saveptr); line; | |
580 | line = strtok_r(NULL, "\n", &saveptr)) { | |
581 | vty_out(vty, "%s%s\n", prefix, line); | |
582 | } | |
583 | ||
584 | XFREE(MTYPE_TMP, p); | |
585 | } | |
586 | ||
587 | void vty_out_timestr(struct vty *vty, time_t uptime) | |
588 | { | |
589 | struct tm *tm; | |
590 | time_t difftime = time(NULL); | |
591 | difftime -= uptime; | |
592 | tm = gmtime(&difftime); | |
593 | ||
594 | if (difftime < ONE_DAY_SECOND) | |
595 | vty_out(vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, | |
596 | tm->tm_sec); | |
597 | else if (difftime < ONE_WEEK_SECOND) | |
598 | vty_out(vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, | |
599 | tm->tm_min); | |
600 | else | |
601 | vty_out(vty, "%02dw%dd%02dh", tm->tm_yday / 7, | |
602 | tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); | |
603 | vty_out(vty, " ago"); | |
604 | } |