diff options
Diffstat (limited to 'isisd/isis_misc.c')
-rw-r--r-- | isisd/isis_misc.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c new file mode 100644 index 0000000..d49ad84 --- /dev/null +++ b/isisd/isis_misc.c @@ -0,0 +1,616 @@ +/* + * IS-IS Rout(e)ing protocol - isis_misc.h + * Miscellanous routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "printfrr.h" +#include "stream.h" +#include "vty.h" +#include "hash.h" +#include "if.h" +#include "command.h" +#include "network.h" + +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_csm.h" +#include "isisd/isisd.h" +#include "isisd/isis_misc.h" + +#include "isisd/isis_lsp.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dynhn.h" + +/* staticly assigned vars for printing purposes */ +struct in_addr new_prefix; +/* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ +/* + place for #0 termination */ +char isonet[51]; +/* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ +char datestring[20]; +char nlpidstring[30]; + +/* + * This converts the isonet to its printable format + */ +const char *isonet_print(const uint8_t *from, int len) +{ + int i = 0; + char tbuf[4]; + isonet[0] = '\0'; + + if (!from) + return "unknown"; + + while (i < len) { + if (i & 1) { + snprintf(tbuf, sizeof(tbuf), "%02x", *(from + i)); + strlcat(isonet, tbuf, sizeof(isonet)); + } else { + if (i == (len - 1)) { /* No dot at the end of address */ + snprintf(tbuf, sizeof(tbuf), "%02x", + *(from + i)); + strlcat(isonet, tbuf, sizeof(isonet)); + } else { + snprintf(tbuf, sizeof(tbuf), "%02x.", + *(from + i)); + strlcat(isonet, tbuf, sizeof(isonet)); + } + } + i++; + } + + return isonet; +} + +/* + * Returns 0 on error, length of buff on ok + * extract dot from the dotted str, and insert all the number in a buff + */ +int dotformat2buff(uint8_t *buff, const char *dotted) +{ + int dotlen, len = 0; + const char *pos = dotted; + uint8_t number[3]; + int nextdotpos = 2; + + number[2] = '\0'; + dotlen = strlen(dotted); + if (dotlen > 50) { + /* this can't be an iso net, its too long */ + return 0; + } + + while ((pos - dotted) < dotlen && len < 20) { + if (*pos == '.') { + /* we expect the . at 2, and than every 5 */ + if ((pos - dotted) != nextdotpos) { + len = 0; + break; + } + nextdotpos += 5; + pos++; + continue; + } + /* we must have at least two chars left here */ + if (dotlen - (pos - dotted) < 2) { + len = 0; + break; + } + + if ((isxdigit((unsigned char)*pos)) && + (isxdigit((unsigned char)*(pos + 1)))) { + memcpy(number, pos, 2); + pos += 2; + } else { + len = 0; + break; + } + + *(buff + len) = (char)strtol((char *)number, NULL, 16); + len++; + } + + return len; +} + +/* + * conversion of XXXX.XXXX.XXXX to memory + */ +int sysid2buff(uint8_t *buff, const char *dotted) +{ + int len = 0; + const char *pos = dotted; + uint8_t number[3]; + + number[2] = '\0'; + // surely not a sysid_string if not 14 length + if (strlen(dotted) != 14) { + return 0; + } + + while (len < ISIS_SYS_ID_LEN) { + if (*pos == '.') { + /* the . is not positioned correctly */ + if (((pos - dotted) != 4) && ((pos - dotted) != 9)) { + len = 0; + break; + } + pos++; + continue; + } + if ((isxdigit((unsigned char)*pos)) && + (isxdigit((unsigned char)*(pos + 1)))) { + memcpy(number, pos, 2); + pos += 2; + } else { + len = 0; + break; + } + + *(buff + len) = (char)strtol((char *)number, NULL, 16); + len++; + } + + return len; +} + +const char *nlpid2str(uint8_t nlpid) +{ + static char buf[4]; + switch (nlpid) { + case NLPID_IP: + return "IPv4"; + case NLPID_IPV6: + return "IPv6"; + case NLPID_SNAP: + return "SNAP"; + case NLPID_CLNP: + return "CLNP"; + case NLPID_ESIS: + return "ES-IS"; + default: + snprintf(buf, sizeof(buf), "%hhu", nlpid); + return buf; + } +} + +/* + * converts the nlpids struct (filled by TLV #129) + * into a string + */ + +char *nlpid2string(struct nlpids *nlpids) +{ + int i; + char tbuf[256]; + nlpidstring[0] = '\0'; + + for (i = 0; i < nlpids->count; i++) { + snprintf(tbuf, sizeof(tbuf), "%s", + nlpid2str(nlpids->nlpids[i])); + strlcat(nlpidstring, tbuf, sizeof(nlpidstring)); + if (nlpids->count - i > 1) + strlcat(nlpidstring, ", ", sizeof(nlpidstring)); + } + + return nlpidstring; +} + +/* + * Returns 0 on error, IS-IS Circuit Type on ok + */ +int string2circuit_t(const char *str) +{ + + if (!str) + return 0; + + if (!strcmp(str, "level-1")) + return IS_LEVEL_1; + + if (!strcmp(str, "level-2-only") || !strcmp(str, "level-2")) + return IS_LEVEL_2; + + if (!strcmp(str, "level-1-2")) + return IS_LEVEL_1_AND_2; + + return 0; +} + +const char *circuit_state2string(int state) +{ + + switch (state) { + case C_STATE_INIT: + return "Init"; + case C_STATE_CONF: + return "Config"; + case C_STATE_UP: + return "Up"; + default: + return "Unknown"; + } + return NULL; +} + +const char *circuit_type2string(int type) +{ + + switch (type) { + case CIRCUIT_T_P2P: + return "p2p"; + case CIRCUIT_T_BROADCAST: + return "lan"; + case CIRCUIT_T_LOOPBACK: + return "loopback"; + default: + return "Unknown"; + } + return NULL; +} + +const char *circuit_t2string(int circuit_t) +{ + switch (circuit_t) { + case IS_LEVEL_1: + return "L1"; + case IS_LEVEL_2: + return "L2"; + case IS_LEVEL_1_AND_2: + return "L1L2"; + default: + return "??"; + } + + return NULL; /* not reached */ +} + +const char *syst2string(int type) +{ + switch (type) { + case ISIS_SYSTYPE_ES: + return "ES"; + case ISIS_SYSTYPE_IS: + return "IS"; + case ISIS_SYSTYPE_L1_IS: + return "1"; + case ISIS_SYSTYPE_L2_IS: + return "2"; + default: + return "??"; + } + + return NULL; /* not reached */ +} + +/* + * Print functions - we print to static vars + */ +const char *snpa_print(const uint8_t *from) +{ + return isis_format_id(from, ISIS_SYS_ID_LEN); +} + +const char *sysid_print(const uint8_t *from) +{ + return isis_format_id(from, ISIS_SYS_ID_LEN); +} + +const char *rawlspid_print(const uint8_t *from) +{ + return isis_format_id(from, 8); +} + +#define FORMAT_ID_SIZE sizeof("0000.0000.0000.00-00") +const char *isis_format_id(const uint8_t *id, size_t len) +{ +#define FORMAT_BUF_COUNT 4 + static char buf_ring[FORMAT_BUF_COUNT][FORMAT_ID_SIZE]; + static size_t cur_buf = 0; + + char *rv; + + cur_buf++; + if (cur_buf >= FORMAT_BUF_COUNT) + cur_buf = 0; + + rv = buf_ring[cur_buf]; + + if (!id) { + snprintf(rv, FORMAT_ID_SIZE, "unknown"); + return rv; + } + + if (len < 6) { + snprintf(rv, FORMAT_ID_SIZE, "Short ID"); + return rv; + } + + snprintf(rv, FORMAT_ID_SIZE, "%02x%02x.%02x%02x.%02x%02x", id[0], id[1], + id[2], id[3], id[4], id[5]); + + if (len > 6) + snprintf(rv + 14, FORMAT_ID_SIZE - 14, ".%02x", id[6]); + if (len > 7) + snprintf(rv + 17, FORMAT_ID_SIZE - 17, "-%02x", id[7]); + + return rv; +} + +const char *time2string(uint32_t time) +{ + uint32_t rest; + char tbuf[32]; + datestring[0] = '\0'; + + if (time == 0) + return "-"; + + if (time / SECS_PER_YEAR) { + snprintf(tbuf, sizeof(tbuf), "%uY", time / SECS_PER_YEAR); + strlcat(datestring, tbuf, sizeof(datestring)); + } + rest = time % SECS_PER_YEAR; + if (rest / SECS_PER_MONTH) { + snprintf(tbuf, sizeof(tbuf), "%uM", rest / SECS_PER_MONTH); + strlcat(datestring, tbuf, sizeof(datestring)); + } + rest = rest % SECS_PER_MONTH; + if (rest / SECS_PER_WEEK) { + snprintf(tbuf, sizeof(tbuf), "%uw", rest / SECS_PER_WEEK); + strlcat(datestring, tbuf, sizeof(datestring)); + } + rest = rest % SECS_PER_WEEK; + if (rest / SECS_PER_DAY) { + snprintf(tbuf, sizeof(tbuf), "%ud", rest / SECS_PER_DAY); + strlcat(datestring, tbuf, sizeof(datestring)); + } + rest = rest % SECS_PER_DAY; + if (rest / SECS_PER_HOUR) { + snprintf(tbuf, sizeof(tbuf), "%uh", rest / SECS_PER_HOUR); + strlcat(datestring, tbuf, sizeof(datestring)); + } + rest = rest % SECS_PER_HOUR; + if (rest / SECS_PER_MINUTE) { + snprintf(tbuf, sizeof(tbuf), "%um", rest / SECS_PER_MINUTE); + strlcat(datestring, tbuf, sizeof(datestring)); + } + rest = rest % SECS_PER_MINUTE; + if (rest) { + snprintf(tbuf, sizeof(tbuf), "%us", rest); + strlcat(datestring, tbuf, sizeof(datestring)); + } + + return datestring; +} + +/* + * routine to decrement a timer by a random + * number + * + * first argument is the timer and the second is + * the jitter + */ +unsigned long isis_jitter(unsigned long timer, unsigned long jitter) +{ + int j, k; + + if (jitter >= 100) + return timer; + + if (timer == 1) + return timer; + /* + * randomizing just the percent value provides + * no good random numbers - hence the spread + * to RANDOM_SPREAD (100000), which is ok as + * most IS-IS timers are no longer than 16 bit + */ + + j = 1 + (int)((RANDOM_SPREAD * frr_weak_random()) / (RAND_MAX + 1.0)); + + k = timer - (timer * (100 - jitter)) / 100; + + timer = timer - (k * j / RANDOM_SPREAD); + + return timer; +} + +struct in_addr newprefix2inaddr(uint8_t *prefix_start, uint8_t prefix_masklen) +{ + memset(&new_prefix, 0, sizeof(new_prefix)); + memcpy(&new_prefix, prefix_start, + (prefix_masklen & 0x3F) + ? ((((prefix_masklen & 0x3F) - 1) >> 3) + 1) + : 0); + return new_prefix; +} + +/* + * Returns the dynamic hostname associated with the passed system ID. + * If no dynamic hostname found then returns formatted system ID. + */ +const char *print_sys_hostname(const uint8_t *sysid) +{ + struct isis_dynhn *dyn; + struct isis *isis = NULL; + struct listnode *node; + + if (!sysid) + return "nullsysid"; + + /* For our system ID return our host name */ + isis = isis_lookup_by_sysid(sysid); + if (isis && !CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + return cmd_hostname_get(); + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + dyn = dynhn_find_by_id(isis, sysid); + if (dyn) + return dyn->hostname; + } + + return sysid_print(sysid); +} + +/* + * This function is a generic utility that logs data of given length. + * Move this to a shared lib so that any protocol can use it. + */ +void zlog_dump_data(void *data, int len) +{ + int i; + unsigned char *p; + unsigned char c; + char bytestr[4]; + char addrstr[10]; + char hexstr[16 * 3 + 5]; + char charstr[16 * 1 + 5]; + + p = data; + memset(bytestr, 0, sizeof(bytestr)); + memset(addrstr, 0, sizeof(addrstr)); + memset(hexstr, 0, sizeof(hexstr)); + memset(charstr, 0, sizeof(charstr)); + + for (i = 1; i <= len; i++) { + c = *p; + if (isalnum(c) == 0) + c = '.'; + + /* store address for this line */ + if ((i % 16) == 1) + snprintf(addrstr, sizeof(addrstr), "%p", p); + + /* store hex str (for left side) */ + snprintf(bytestr, sizeof(bytestr), "%02X ", *p); + strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); + + /* store char str (for right side) */ + snprintf(bytestr, sizeof(bytestr), "%c", c); + strncat(charstr, bytestr, + sizeof(charstr) - strlen(charstr) - 1); + + if ((i % 16) == 0) { + /* line completed */ + zlog_debug("[%8.8s] %-50.50s %s", addrstr, hexstr, + charstr); + hexstr[0] = 0; + charstr[0] = 0; + } else if ((i % 8) == 0) { + /* half line: add whitespaces */ + strncat(hexstr, " ", + sizeof(hexstr) - strlen(hexstr) - 1); + strncat(charstr, " ", + sizeof(charstr) - strlen(charstr) - 1); + } + p++; /* next byte */ + } + + /* print rest of buffer if not empty */ + if (strlen(hexstr) > 0) + zlog_debug("[%8.8s] %-50.50s %s", addrstr, hexstr, charstr); + return; +} + +void log_multiline(int priority, const char *prefix, const char *format, ...) +{ + char shortbuf[256]; + va_list ap; + char *p; + + va_start(ap, format); + p = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); + va_end(ap); + + if (!p) + return; + + char *saveptr = NULL; + for (char *line = strtok_r(p, "\n", &saveptr); line; + line = strtok_r(NULL, "\n", &saveptr)) { + zlog(priority, "%s%s", prefix, line); + } + + if (p != shortbuf) + XFREE(MTYPE_TMP, p); +} + +char *log_uptime(time_t uptime, char *buf, size_t nbuf) +{ + struct tm *tm; + time_t difftime = time(NULL); + difftime -= uptime; + tm = gmtime(&difftime); + + if (difftime < ONE_DAY_SECOND) + snprintf(buf, nbuf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, + tm->tm_sec); + else if (difftime < ONE_WEEK_SECOND) + snprintf(buf, nbuf, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, + tm->tm_min); + else + snprintf(buf, nbuf, "%02dw%dd%02dh", tm->tm_yday / 7, + tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); + + return buf; +} + +void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) +{ + char shortbuf[256]; + va_list ap; + char *p; + + va_start(ap, format); + p = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); + va_end(ap); + + if (!p) + return; + + char *saveptr = NULL; + for (char *line = strtok_r(p, "\n", &saveptr); line; + line = strtok_r(NULL, "\n", &saveptr)) { + vty_out(vty, "%s%s\n", prefix, line); + } + + if (p != shortbuf) + XFREE(MTYPE_TMP, p); +} + +void vty_out_timestr(struct vty *vty, time_t uptime) +{ + time_t difftime = time(NULL); + char buf[MONOTIME_STRLEN]; + + difftime -= uptime; + + frrtime_to_interval(difftime, buf, sizeof(buf)); + + vty_out(vty, "%s ago", buf); +} |