/** * Append the result of a template engine into an autobuffer. * Each usage of a key will be replaced with the corresponding * value. * @param autobuf pointer to autobuf object * @param file code file where this function was called (supplied by macro) * @param line line number in file where this function was called (supplied by macro) * @param format format string (as supplied to abuf_template_init() * @param values array of values (same number as keys) * @param table pointer to index table initialized by abuf_template_init() * @param indexCount length of index table as returned by abuf_template_init() * @return -1 if an out-of-memory error happened, 0 otherwise */ int abuf_templatef (struct autobuf *autobuf, const char *format, char **values, size_t *table, size_t indexCount) { size_t i, last = 0; if (autobuf == NULL) return 0; for (i=0; i<indexCount; i+=3) { /* copy prefix text */ if (last < table[i+1]) { if (abuf_memcpy(autobuf, &format[last], table[i+1] - last) < 0) { return -1; } } if (abuf_puts(autobuf, values[table[i]]) < 0) { return -1; } last = table[i+2]; } if (last < strlen(format)) { if (abuf_puts(autobuf, &format[last]) < 0) { return -1; } } return 0; }
/** * Print start of message * @param context rfc5444 tlvblock reader context * @return see rfc5444_result enum */ enum rfc5444_result _cb_print_msg_start(struct rfc5444_reader_tlvblock_context *context) { struct rfc5444_print_session *session; struct netaddr_str buf; assert(context->type == RFC5444_CONTEXT_MESSAGE); session = container_of(context->consumer, struct rfc5444_print_session, _msg); abuf_puts(session->output, "\t| ,-------------------\n"); abuf_puts(session->output, "\t| | MESSAGE\n"); abuf_puts(session->output, "\t| |-------------------\n"); abuf_appendf(session->output, "\t| | * Message type: %u\n", context->msg_type); abuf_appendf(session->output, "\t| | * Message flags: 0x%02x\n", context->msg_flags); abuf_appendf(session->output, "\t| | * Address length: %u\n", context->addr_len); if (context->has_origaddr) { abuf_appendf(session->output, "\t| | * Originator address: %s\n", netaddr_to_string(&buf, &context->orig_addr)); } if (context->has_hoplimit) { abuf_appendf(session->output, "\t| | * Hop limit: %u\n", context->hoplimit); } if (context->has_hopcount) { abuf_appendf(session->output, "\t| | * Hop count: %u\n", context->hopcount); } if (context->has_seqno) { abuf_appendf(session->output, "\t| | * Message seq number: %u\n", context->seqno); } return RFC5444_OKAY; }
/** * Append the result of a template engine into an autobuffer. * Each usage of a key will be replaced with the corresponding * value. * @param out pointer to autobuf object * @param format format string (as supplied to abuf_template_init() * @param storage pointer to template storage object, which will be filled by * this function * @return -1 if an out-of-memory error happened, 0 otherwise */ int abuf_add_template(struct autobuf *out, const char *format, struct abuf_template_storage *storage) { struct abuf_template_storage_entry *entry; size_t i, last = 0; if (out == NULL) return 0; for (i=0; i<storage->count; i++) { entry = &storage->indices[i]; /* copy prefix text */ if (last < entry->start) { if (abuf_memcpy(out, &format[last], entry->start - last) < 0) { return -1; } } if (entry->data->value) { if (abuf_puts(out, entry->data->value) < 0) { return -1; } } last = entry->end; } if (last < strlen(format)) { if (abuf_puts(out, &format[last]) < 0) { return -1; } } return 0; }
/** * Prints a string to an autobuffer, using JSON escape rules * @param out pointer to output buffer * @param txt string to print * @param delimiter true if string must be enclosed in quotation marks * @return -1 if an error happened, 0 otherwise */ static int _json_printvalue(struct autobuf *out, const char *txt, bool delimiter) { const char *ptr; bool unprintable; if (delimiter) { if (abuf_puts(out, "\"") < 0) { return -1; } } else if (*txt == 0) { abuf_puts(out, "0"); } ptr = txt; while (*ptr) { unprintable = !str_char_is_printable(*ptr); if (unprintable || *ptr == '\\' || *ptr == '\"') { if (ptr != txt) { if (abuf_memcpy(out, txt, ptr - txt) < 0) { return -1; } } if (unprintable) { if (abuf_appendf(out, "\\u00%02x", (unsigned char)(*ptr++)) < 0) { return -1; } } else { if (abuf_appendf(out, "\\%c", *ptr++) < 0) { return -1; } } txt = ptr; } else { ptr++; } } if (abuf_puts(out, txt) < 0) { return -1; } if (delimiter) { if (abuf_puts(out, "\"") < 0) { return -1; } } return 0; }
void cfg_help_choice(struct autobuf *out, bool preamble, const char **choices, size_t choice_count) { size_t i; if (preamble) { cfg_append_printable_line(out, _PREFIX "Parameter must be on of the following list:"); } abuf_puts(out, " "); for (i=0; i < choice_count; i++) { abuf_appendf(out, "%s'%s'", i==0 ? "" : ", ", choices[i]); } abuf_puts(out, "\n"); }
void cfg_help_netaddr(struct autobuf *out, bool preamble, bool prefix, const int8_t *af_types, size_t af_types_count) { int8_t type; bool first; size_t i; if (preamble) { abuf_puts(out, _PREFIX "Parameter must be an address of the following type: "); } first = true; for (i=0; i<af_types_count; i++) { type = af_types[i]; if (type == -1) { continue; } if (first) { first = false; } else { abuf_puts(out, ", "); } switch (type) { case AF_INET: abuf_puts(out, "IPv4"); break; case AF_INET6: abuf_puts(out, "IPv6"); break; case AF_MAC48: abuf_puts(out, "MAC48"); break; case AF_EUI64: abuf_puts(out, "EUI64"); break; default: abuf_puts(out, "Unspec (-)"); break; } } if (prefix) { abuf_puts(out, "\n" _PREFIX " (the address can have an optional prefix)"); } abuf_puts(out, "\n"); }
/** * Converts a key/value list for the template engine into * JSON compatible output. * @param out output buffer * @param prefix string prefix for all lines * @param data array of template data * @param data_count number of template data entries * @return -1 if an error happened, 0 otherwise */ int abuf_add_json(struct autobuf *out, const char *prefix, struct abuf_template_data *data, size_t data_count) { bool first; size_t i; if (abuf_appendf(out, "%s{\n", prefix) < 0) { return -1; } first = true; for (i=0; i<data_count; i++) { if (data[i].value == NULL) { continue; } if (!first) { if (abuf_puts(out, ",\n") < 0) { return -1; } } else { first = false; } if (abuf_appendf(out, "%s \"%s\" : ", prefix, data[i].key) < 0) { return -1; } if (_json_printvalue(out, data[i].value, data[i].string)) { return -1; } } if (!first) { if (abuf_puts(out, "\n") < 0) { return -1; } } if (abuf_appendf(out, "%s}\n", prefix) < 0) { return -1; } return 0; }
/** * Clear output buffer and print start of packet * @param context rfc5444 tlvblock reader context * @return see rfc5444_result enum */ static enum rfc5444_result _cb_print_pkt_start(struct rfc5444_reader_tlvblock_context *context) { struct rfc5444_print_session *session; assert(context->type == RFC5444_CONTEXT_PACKET); session = container_of(context->consumer, struct rfc5444_print_session, _pkt); abuf_puts(session->output, "\t,------------------\n"); abuf_puts(session->output, "\t| PACKET\n"); abuf_puts(session->output, "\t|------------------\n"); abuf_appendf(session->output, "\t| * Packet version: %u\n", context->pkt_version); abuf_appendf(session->output, "\t| * Packet flags: 0x%x\n", context->pkt_flags); if (context->has_pktseqno) { abuf_appendf(session->output, "\t| * Packet seq number: %u\n", context->pkt_seqno); } return RFC5444_OKAY; }
void cfg_help_acl(struct autobuf *out, bool preamble, bool prefix, const int8_t *af_types, size_t af_types_count) { if (preamble) { abuf_puts(out, _PREFIX "Parameter is an apache2 style access control list made from a list of network addresses of the following types:\n"); } cfg_help_netaddr(out, false, prefix, af_types, af_types_count); abuf_puts(out, _PREFIX " Each of the addresses/prefixes can start with a" " '+' to add them to the whitelist and '-' to add it to the blacklist" " (default is the whitelist).\n" _PREFIX " In addition to this there are four keywords to configure the ACL:\n" _PREFIX " - '" ACL_FIRST_ACCEPT "' to parse the whitelist first\n" _PREFIX " - '" ACL_FIRST_REJECT "' to parse the blacklist first\n" _PREFIX " - '" ACL_DEFAULT_ACCEPT "' to accept input if it doesn't match either list\n" _PREFIX " - '" ACL_DEFAULT_REJECT "' to not accept it if it doesn't match either list\n" _PREFIX " (default mode is '" ACL_FIRST_ACCEPT "' and '" ACL_DEFAULT_REJECT "')\n"); }
/** * Print end of address * @param context rfc5444 tlvblock reader context * @param dropped unused * @return see rfc5444_result enum */ enum rfc5444_result _cb_print_addr_end(struct rfc5444_reader_tlvblock_context *context, bool dropped __attribute__((unused))) { struct rfc5444_print_session *session; assert(context->type == RFC5444_CONTEXT_ADDRESS); session = container_of(context->consumer, struct rfc5444_print_session, _addr); abuf_puts(session->output, "\t| | `-------------------\n"); return RFC5444_OKAY; }
/** * Print address tlv * @param tlv tlvblock entry * @param context rfc5444 tlvblock reader context * @return see rfc5444_result enum */ enum rfc5444_result _cb_print_addr_tlv(struct rfc5444_reader_tlvblock_entry *tlv, struct rfc5444_reader_tlvblock_context *context) { struct rfc5444_print_session *session; assert(context->type == RFC5444_CONTEXT_ADDRESS); session = container_of(context->consumer, struct rfc5444_print_session, _addr); abuf_puts(session->output, "\t| | | | - TLV\n"); abuf_appendf(session->output, "\t| | | | Flags = 0x%02x\n", tlv->flags); abuf_appendf(session->output, "\t| | | | Type = %u", tlv->type); if (tlv->type_ext != 0) { abuf_appendf(session->output, "; Type ext. = %u", tlv->type_ext); } abuf_puts(session->output, "\n"); if (tlv->length > 0) { abuf_appendf(session->output, "\t| | | | Value length: %u\n", tlv->length); abuf_hexdump(session->output, "\t| | | | ", tlv->single_value, tlv->length); } return RFC5444_OKAY; }
/** * Print end of packet and call print callback if necessary * @param context rfc5444 tlvblock reader context * @param dropped unused * @return see rfc5444_result enum */ enum rfc5444_result _cb_print_pkt_end(struct rfc5444_reader_tlvblock_context *context, bool dropped __attribute__((unused))) { struct rfc5444_print_session *session; session = container_of(context->consumer, struct rfc5444_print_session, _pkt); abuf_puts(session->output, "\t`------------------\n"); if (session->print_packet) { session->print_packet(session); } return RFC5444_OKAY; }
/** * Print start of address * @param context rfc5444 tlvblock reader context * @return see rfc5444_result enum */ enum rfc5444_result _cb_print_addr_start(struct rfc5444_reader_tlvblock_context *context) { struct rfc5444_print_session *session; struct netaddr_str buf; assert(context->type == RFC5444_CONTEXT_ADDRESS); session = container_of(context->consumer, struct rfc5444_print_session, _addr); abuf_puts(session->output, "\t| | ,-------------------\n"); abuf_appendf(session->output, "\t| | | Address: %s\n", netaddr_to_string(&buf, &context->addr)); return RFC5444_OKAY; }
/** * Print a hexdump of a buffer to an autobuf and prepends a prefix string * to each line. * @param out output buffer * @param prefix string to prepend to each line * @param buffer buffer to be hexdumped * @param length length of buffer in bytes */ void abuf_hexdump(struct autobuf *out, const char *prefix, void *buffer, size_t length) { uint8_t *buf; size_t j, l; buf = buffer; for (j = 0; j < length; j += 32) { abuf_appendf(out, "%s%04zx:", prefix, j); l = length - j; if (l > 32) { l = 32; } _print_hexline(out, &buf[j], l); abuf_puts(out, "\n"); } }
int rfc5444_print_raw(struct autobuf *out, void *buffer, size_t length) { static uint8_t ZEROTAIL[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint8_t *ptr, *end; size_t idx, idx2, prefix_idx, i; uint8_t flags, head_len, tail_len, *head, *tail, num_addr; uint16_t msg_size, addr_length, mid_len; ptr = buffer; idx = 0; end = &ptr[length]; if (idx + 1 > length) { return -1; } abuf_puts(out, "\t,------------------\n"); abuf_puts(out, "\t| PACKET\n"); abuf_puts(out, "\t|------------------\n"); flags = ptr[0]; abuf_appendf(out, "\t| Packet version: %u\n", flags >> 4); abuf_appendf(out, "\t| Packet flags: 0x%02x\n", flags & 0x0f); idx++; if (flags & RFC5444_PKT_FLAG_SEQNO) { if (idx + 2 > length) { return -1; } abuf_appendf(out, "\t| Packet seq number: %u\n", (ptr[0] << 8) | ptr[1]); idx += 2; } if (flags & RFC5444_PKT_FLAG_TLV) { if (idx + 2 > length) { return -1; } if (_print_raw_tlvblock(out, "\t| | ", ptr, &idx, length)) { return -1; } } while (idx < length) { idx2 = idx; /* print messages */ if (idx2 + 4 > length) { return -1; } abuf_puts(out, "\t| ,-------------------\n"); abuf_puts(out, "\t| | MESSAGE\n"); abuf_puts(out, "\t| |-------------------\n"); abuf_appendf(out, "\t| | Message type: %u\n", ptr[idx]); flags = ptr[idx2 + 1]; abuf_appendf(out, "\t| | Message flags: 0x%02x\n", flags >> 4); addr_length = (flags & 15) + 1; abuf_appendf(out, "\t| | Address length: %u\n", addr_length); msg_size = (ptr[idx2 + 2] << 8) + ptr[idx + 3]; abuf_appendf(out, "\t| | Size: %u\n", msg_size); idx2 += 4; if (flags & RFC5444_MSG_FLAG_ORIGINATOR) { if (idx2 + addr_length > idx + msg_size) { return -1; } abuf_appendf(out, "\t| | Originator address: "); _print_hex(out, &ptr[idx2], addr_length, end); idx2 += addr_length; } if (flags & RFC5444_MSG_FLAG_HOPLIMIT) { if (idx2 + 1 > idx + msg_size) { return -1; } abuf_appendf(out, "\t| | Hop limit: %u\n", ptr[idx2]); idx2++; } if (flags & RFC5444_MSG_FLAG_HOPCOUNT) { if (idx2 + 1 > idx + msg_size) { return -1; } abuf_appendf(out, "\t| | Hop count: %u\n", ptr[idx2]); idx2++; } if (flags & RFC5444_MSG_FLAG_SEQNO) { if (idx2 + 2 > idx + msg_size) { return -1; } abuf_appendf(out, "\t| | Sequence Number: %u\n", (ptr[idx2] << 8) | ptr[idx2 + 1]); idx2 += 2; } if (_print_raw_tlvblock(out, "\t| | ", ptr, &idx2, msg_size)) { return -1; } while (idx2 < idx + msg_size) { /* print address blocks */ if (idx2 + 2 > idx + msg_size) { return -1; } abuf_puts(out, "\t| | ,-------------------\n"); abuf_puts(out, "\t| | | ADDRESS-BLOCK\n"); abuf_puts(out, "\t| | |-------------------\n"); num_addr = ptr[idx2]; abuf_appendf(out, "\t| | | Num-Addr: %u\n", num_addr); flags = ptr[idx2 + 1]; abuf_appendf(out, "\t| | | Flags: 0x%02x\n", flags); idx2 += 2; head_len = tail_len = 0; head = NULL; tail = NULL; if (flags & RFC5444_ADDR_FLAG_HEAD) { if (idx2 + 1 > idx + msg_size) { return -1; } head_len = ptr[idx2]; idx2++; if (idx2 + head_len > idx + msg_size) { return -1; } head = &ptr[idx2]; abuf_appendf(out, "\t| | | Head: "); _print_hex(out, head, head_len, end); abuf_puts(out, "\n"); idx2 += head_len; } if (flags & RFC5444_ADDR_FLAG_FULLTAIL) { if (idx2 + 1 > idx + msg_size) { return -1; } tail_len = ptr[idx2]; idx2++; if (idx2 + tail_len > idx + msg_size) { return -1; } tail = &ptr[idx2]; abuf_appendf(out, "\t| | | Tail: "); _print_hex(out, tail, tail_len, end); abuf_puts(out, "\n"); idx2 += tail_len; } if (flags & RFC5444_ADDR_FLAG_ZEROTAIL) { if (idx2 + 1 > idx + msg_size) { return -1; } tail_len = ptr[idx2]; tail = ZEROTAIL; abuf_appendf(out, "\t| | | ZeroTail: "); _print_hex(out, tail, tail_len, end); abuf_puts(out, "\n"); idx2++; } if (head_len + tail_len >= addr_length) { return -1; } mid_len = (addr_length - head_len - tail_len) * num_addr; if (idx2 + mid_len > idx + msg_size) { return -1; } prefix_idx = idx + mid_len; if (flags & RFC5444_ADDR_FLAG_SINGLEPLEN) { if (prefix_idx + 1 > idx + msg_size) { return -1; } } else if (flags & RFC5444_ADDR_FLAG_MULTIPLEN) { if (prefix_idx + num_addr > idx + msg_size) { return -1; } } else { prefix_idx = 0; } for (i = 0; i < num_addr; i++) { abuf_puts(out, "\t| | | ,-------------------\n"); abuf_puts(out, "\t| | | | Address\n"); abuf_puts(out, "\t| | | |-------------------\n"); abuf_appendf(out, "\t| | | | Address: "); if (head_len) { _print_hex(out, head, head_len, end); abuf_puts(out, " | "); } _print_hex(out, &ptr[idx2], addr_length - head_len - tail_len, end); idx2 += addr_length - head_len - tail_len; if (tail_len) { abuf_puts(out, " | "); _print_hex(out, tail, tail_len, end); } if (prefix_idx) { abuf_appendf(out, " / %u", ptr[prefix_idx]); } if (flags & RFC5444_ADDR_FLAG_MULTIPLEN) { prefix_idx++; } abuf_puts(out, "\n"); } if (_print_raw_tlvblock(out, "\t| | | ", ptr, &idx2, msg_size)) { return -1; } } if (idx + msg_size != idx2) { return -1; } idx = idx2; } return 0; }
static int _print_raw_tlvblock(struct autobuf *out, const char *prefix, uint8_t *blockptr, size_t *idx, size_t length) { char valueprefix[128]; uint16_t blocklength, tlv_len, tlv_singlelength; uint8_t *tlvptr; size_t idx2; uint8_t tlv_flags, startidx, endidx; if (2 > length) { return -1; } abuf_appendf(out, "%s,-------------------\n", prefix); abuf_appendf(out, "%s| TLV BLOCK\n", prefix); abuf_appendf(out, "%s|-------------------\n", prefix); blocklength = (blockptr[*idx] << 8) | blockptr[*idx + 1]; abuf_appendf(out, "%s| * TLV Block Size: %u\n", prefix, blocklength); if (blocklength + 2u > length) { return -1; } *idx += 2; tlvptr = &blockptr[*idx]; for (idx2 = 0; idx2 < blocklength;) { if (idx2 + 2 > blocklength) { return -1; } abuf_appendf(out, "%s| ,-------------------\n", prefix); abuf_appendf(out, "%s| | TLV\n", prefix); abuf_appendf(out, "%s| |-------------------\n", prefix); abuf_appendf(out, "%s| | type: %u\n", prefix, tlvptr[idx2]); idx2++; tlv_flags = tlvptr[idx2]; abuf_appendf(out, "%s| | flags: 0x%02x\n", prefix, tlv_flags); idx2++; if (tlv_flags & RFC5444_TLV_FLAG_TYPEEXT) { if (idx2 + 1 > blocklength) { return -1; } abuf_appendf(out, "%s| | ext-type: %u\n", prefix, tlvptr[idx2]); idx2++; } startidx = 0; endidx = 0; if (tlv_flags & (RFC5444_TLV_FLAG_SINGLE_IDX | RFC5444_TLV_FLAG_MULTI_IDX)) { if (idx2 + 1 > blocklength) { return -1; } startidx = tlvptr[idx2]; endidx = startidx; abuf_appendf(out, "%s| | index-start: %u\n", prefix, startidx); idx2++; } if (tlv_flags & (RFC5444_TLV_FLAG_MULTI_IDX)) { if (idx2 + 1 > blocklength) { return -1; } endidx = tlvptr[idx2]; if (endidx < startidx) { return -1; } abuf_appendf(out, "%s| | index-end: %u\n", prefix, endidx); idx2++; } tlv_len = 0; if (tlv_flags & (RFC5444_TLV_FLAG_EXTVALUE)) { if (idx2 + 1 > blocklength) { return -1; } tlv_len = tlvptr[idx2] << 8; idx2++; } if (tlv_flags & (RFC5444_TLV_FLAG_VALUE)) { if (idx2 + 1 > blocklength) { return -1; } tlv_len |= tlvptr[idx2]; idx2++; } if (tlv_flags & (RFC5444_TLV_FLAG_EXTVALUE | RFC5444_TLV_FLAG_VALUE)) { abuf_appendf(out, "%s| | length: %u\n", prefix, tlv_len); } if (idx2 + tlv_len > blocklength) { return -1; } if (tlv_flags & RFC5444_TLV_FLAG_MULTIVALUE) { if (tlv_len % (endidx - startidx + 1)) { return -1; } tlv_singlelength = tlv_len / (endidx - startidx + 1); } else { tlv_singlelength = tlv_len; endidx = startidx; } snprintf(valueprefix, sizeof(valueprefix), "%s| | ", prefix); for (; startidx <= endidx; startidx++) { if (idx2 + tlv_singlelength > blocklength) { return -1; } abuf_hexdump(out, valueprefix, &tlvptr[idx2], tlv_singlelength); idx2 += tlv_singlelength; abuf_puts(out, "\n"); } } if (blocklength != idx2) { return -1; } *idx += blocklength; return 0; }