diff options
Diffstat (limited to 'cicn-plugin/cicn/cicn_parser.h')
-rw-r--r-- | cicn-plugin/cicn/cicn_parser.h | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/cicn-plugin/cicn/cicn_parser.h b/cicn-plugin/cicn/cicn_parser.h new file mode 100644 index 00000000..58f1a11e --- /dev/null +++ b/cicn-plugin/cicn/cicn_parser.h @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * cicn_parser.h: Fast-path, vpp-aware ICN packet parser, used in cicn forwarder. + */ + +#ifndef _CICN_PARSER_H_ +#define _CICN_PARSER_H_ 1 + +#if !CICN_VPP_PLUGIN +#error "cicn-internal file included externally" +#endif + +#include <ctype.h> + +#include <vlib/vlib.h> + +#include <cicn/cicn_types.h> +#include <cicn/cicn_std.h> + +#ifndef AOK +# define AOK 0 +#endif + +/* + * Useful macros for working with integers in possibly-unaligned + * buffers and possibly-byteswapped architectures + */ +#define C_GETINT16(n, p) do { \ + register uint8_t *t_p = (uint8_t *)(p); \ + (n) = ((uint16_t)t_p[0] << 8) \ + | ((uint16_t)t_p[1]); \ + } while (0) + +#define C_GETINT32(n, p) do { \ + register uint8_t *t_p = (uint8_t *)(p); \ + (n) = ((uint32_t)t_p[0] << 24) \ + | ((uint32_t)t_p[1] << 16) \ + | ((uint32_t)t_p[2] << 8) \ + | ((uint32_t)t_p[3]); \ + } while (0) + +#define C_GETINT64(n, p) do { \ + register uint8_t *t_p = (uint8_t *)(p); \ + (n) = ((uint64_t)t_p[0] << 56) \ + | ((uint64_t)t_p[1] << 48) \ + | ((uint64_t)t_p[2] << 40) \ + | ((uint64_t)t_p[3] << 32) \ + | ((uint64_t)t_p[4] << 24) \ + | ((uint64_t)t_p[5] << 16) \ + | ((uint64_t)t_p[6] << 8) \ + | ((uint64_t)t_p[7]); \ + } while (0) + +#define C_PUTINT16(p, n) do { \ + register uint16_t t_n = (uint16_t)(n); \ + register uint8_t *t_p = (uint8_t *)(p); \ + t_p[0] = (uint8_t)(t_n >> 8); \ + t_p[1] = (uint8_t)(t_n); \ + } while (0) + +#define C_PUTINT32(p, n) do { \ + register uint32_t t_n = (uint32_t)(n); \ + register uint8_t *t_p = (uint8_t *)(p); \ + t_p[0] = (uint8_t)(t_n >> 24); \ + t_p[1] = (uint8_t)(t_n >> 16); \ + t_p[2] = (uint8_t)(t_n >> 8); \ + t_p[3] = (uint8_t)t_n; \ + } while (0) + +#define C_PUTINT64(p, n) do { \ + register uint64_t t_n = (uint64_t)(n); \ + register uint8_t *t_p = (uint8_t *)(p); \ + t_p[0] = (uint8_t)(t_n >> 56); \ + t_p[1] = (uint8_t)(t_n >> 48); \ + t_p[2] = (uint8_t)(t_n >> 40); \ + t_p[3] = (uint8_t)(t_n >> 32); \ + t_p[4] = (uint8_t)(t_n >> 24); \ + t_p[5] = (uint8_t)(t_n >> 16); \ + t_p[6] = (uint8_t)(t_n >> 8); \ + t_p[7] = (uint8_t)t_n; \ + } while (0) + +/* + * Key type codes for header, header tlvs, body tlvs, and child tlvs + */ + +enum cicn_pkt_type_e +{ + CICN_PKT_TYPE_INTEREST = 0, + CICN_PKT_TYPE_CONTENT = 1, + CICN_PKT_TYPE_NAK = 2, + CICN_PKT_TYPE_CONTROL = 0xA4, + CICN_PKT_TYPE_CONTROL_REQUEST = CICN_PKT_TYPE_CONTROL + 1, + CICN_PKT_TYPE_CONTROL_REPLY +}; + +enum cicn_msg_type_e +{ + CICN_MSG_TYPE_INTEREST = 1, + CICN_MSG_TYPE_CONTENT = 2, + CICN_MSG_TYPE_CONTROL = 0xBEEF, + CICN_MSG_TYPE_ECHO_REQUEST = CICN_MSG_TYPE_CONTROL + 1, + CICN_MSG_TYPE_ECHO_REPLY, + CICN_MSG_TYPE_TRACEROUTE_REQUEST, + CICN_MSG_TYPE_TRACEROUTE_REPLY, +}; + +enum cicn_hdr_tlv_e +{ + CICN_HDR_TLV_INT_LIFETIME = 1, + CICN_HDR_TLV_CACHE_TIME = 2, +}; + +enum cicn_tlv_e +{ + CICN_TLV_NAME = 0, + CICN_TLV_PAYLOAD = 1, + CICN_TLV_PAYLOAD_TYPE = 5, + CICN_TLV_MSG_EXPIRY = 6, +}; + +enum cicn_name_comp_e +{ + CICN_NAME_COMP = 1, + CICN_NAME_COMP_CHUNK = 16 +}; + +enum cicn_msg_err_e +{ + CICN_MSG_ERR_NOROUTE = 1, + CICN_MSG_ERR_HOPLIM = 2, + CICN_MSG_ERR_RESOURCES = 3, + CICN_MSG_ERR_CONGESTION = 6, + CICN_MSG_ERR_MTU = 7 +}; + +/* + * Fixed packet header + */ +typedef struct __attribute__ ((__packed__)) cicn_packet_hdr_s +{ + uint8_t pkt_ver; + uint8_t pkt_type; + uint16_t pkt_len; + uint8_t pkt_hop_limit; + uint8_t pkt_reserved; +#define pkt_nack_code pkt_reserved + uint8_t pkt_flags; + uint8_t pkt_hdr_len; +} cicn_packet_hdr_t; + +typedef struct cicn_pkt_hdr_desc_s +{ + int16_t ph_lifetime_idx; +} cicn_pkt_hdr_desc_t; + +/* Simple min packet len */ +#define CICN_PACKET_MIN_LEN \ + (sizeof(cicn_packet_hdr_t) + /*msg tlv*/ 4 + \ + /*name tlv*/ 4 + /*name comp*/2 + 1) + +/* Protocol versions */ +#define CICN_PROTO_VERSION_1 0x01 +#define CICN_PROTO_VERSION_CURRENT CICN_PROTO_VERSION_1 + +/* The versions we can deal with */ +#define CICN_PROTO_VERSION_MIN CICN_PROTO_VERSION_CURRENT +#define CICN_PROTO_VERSION_MAX CICN_PROTO_VERSION_CURRENT + +/* Default initial hop limit */ +#define CICN_DEFAULT_HOP_LIMIT 128 + +/* Current encoding restricts TLV 'T' and 'L' to two bytes */ +#define CICN_TLV_TYPE_LEN 2 +#define CICN_TLV_LEN_LEN 2 +#define CICN_TLV_HDR_LEN (CICN_TLV_TYPE_LEN + CICN_TLV_LEN_LEN) +#define CICN_TLV_MAX_LEN 0xffff + +#define cicn_parse_tlvtype(p) ((((uint8_t*)(p))[0]<<8) | ((uint8_t*)(p))[1]) +#define cicn_parse_tlvlength(p) ((((uint8_t*)(p))[2]<<8) | ((uint8_t*)(p))[3]) + +static inline uint64_t +cicn_parse_getvlv (uint8_t * p, uint8_t * e) +{ + u64 v; + + /* + * Should check + * if(e <= p || e - p > 8) + * - it's an error. + */ + v = *p++; + while (p < e) + { + v = (v << 8) | *p++; + } + + return v; +} + +/* + * Wrapper to fill in tlv hdr (type and len) for tlv under constructions. + * Byte-swapps if needed for this CPU. + */ +static inline void +cicn_parse_tlv_hdr_build (uint8_t * tlv, uint16_t type, uint16_t len) +{ + C_PUTINT16 (&tlv[0], type); + C_PUTINT16 (&tlv[CICN_TLV_TYPE_LEN], len); +} + +/* + * Wrapper to build tlv given the type, the length, and the pre-constructed + * value + */ +static inline void +cicn_parse_tlv_build (uint8_t * tlv, uint16_t type, uint16_t len, + const uint8_t * v) +{ + cicn_parse_tlv_hdr_build (tlv, type, len); + clib_memcpy (&tlv[CICN_TLV_HDR_LEN], v, len); +} + +/* + * Quickie packet sanity check: check lengths, locate name + */ +static inline int +cicn_parse_pkt (uint8_t * pkt, uint32_t pktlen, uint8_t * type_p, + uint16_t * msg_type_p, uint8_t ** name_p, + uint32_t * namelen_p, cicn_pkt_hdr_desc_t * pkt_hdr_desc) +{ + int ret = EINVAL; + uint8_t *startptr; + const uint8_t *endptr; + uint8_t type; + uint16_t sval; + uint8_t hdr_len; + + if (pkt == NULL || pktlen < CICN_PACKET_MIN_LEN) + { + goto error; + } + + startptr = pkt; + endptr = pkt + pktlen; + + if ((*pkt < CICN_PROTO_VERSION_MIN) || (*pkt > CICN_PROTO_VERSION_MAX)) + { + goto error; + } + + pkt++; + + /* TODO -- validate packet type or make the caller do it? */ + type = *pkt; + if (type_p) + { + *type_p = type; + } + + /* Advance to and check header's packet len */ + pkt++; + + C_GETINT16 (sval, pkt); + if (startptr + sval > endptr) + { + goto error; + } + + /* TODO -- check hop limit here, or let caller do it? */ + + /* Advance past hop limit and reserved bits */ + pkt += 4; + + /* TODO -- any 'flags' to check? */ + + /* Advance to header-len field */ + pkt++; + hdr_len = *pkt; + + /* Check header-len; must be enough room for at least a message tlv and + * a name tlv. + */ + if ((startptr + hdr_len) > (endptr - 4 /*msg */ - 4 /*name */ )) + { + goto error; + } + + /* header options we care about */ + pkt_hdr_desc->ph_lifetime_idx = -1; + uint8_t *hdr_tlv = pkt + 1; + pkt = startptr + hdr_len; + while (hdr_tlv < pkt) + { + uint16_t hdr_tlv_type = cicn_parse_tlvtype (hdr_tlv); + uint16_t hdr_tlv_len = + CICN_TLV_HDR_LEN + cicn_parse_tlvlength (hdr_tlv); + if (hdr_tlv + hdr_tlv_len > pkt) + { + goto error; + } + + switch (hdr_tlv_type) + { + case CICN_HDR_TLV_INT_LIFETIME: + if (type == CICN_PKT_TYPE_INTEREST) + { + pkt_hdr_desc->ph_lifetime_idx = hdr_tlv - startptr; + } + case CICN_HDR_TLV_CACHE_TIME: + if (type == CICN_PKT_TYPE_CONTENT) + { + pkt_hdr_desc->ph_lifetime_idx = hdr_tlv - startptr; + } + break; + default: + break; + } + hdr_tlv += hdr_tlv_len; + } + + /* Capture message type. TODO -- validate/enforce msg types. */ + C_GETINT16 (sval, pkt); + if (msg_type_p) + { + *msg_type_p = sval; + } + + pkt += 2; + + /* Check len of message tlv. TODO -- not checking for other per-msg tlvs */ + C_GETINT16 (sval, pkt); + if (((pkt + sval + CICN_TLV_LEN_LEN) > endptr) || (sval < 4 /*name */ )) + { + goto error; + } + + pkt += 2; + + /* Must find name first in the 'message' */ + C_GETINT16 (sval, pkt); + if (sval != CICN_TLV_NAME) + { + goto error; + } + + /* Capture start of name */ + if (name_p) + { + *name_p = pkt; + } + + pkt += 2; + + /* Validate len of name tlv + * - zero _is_ a validate name len + * - TODO should compare embedded name len with containing message tlv len + */ + C_GETINT16 (sval, pkt); + if ((pkt + sval + CICN_TLV_LEN_LEN) > endptr) + { + goto error; + } + + if (namelen_p) + { + /* Return the whole length from the start of the Name tlv, + * including the T and L octets. + */ + *namelen_p = sval + CICN_TLV_TYPE_LEN + CICN_TLV_LEN_LEN; + } + + /* Looks ok so far... */ + ret = AOK; + + return (ret); + +error: + if (type_p) + { + *type_p = 0; // compiler warning + } + if (msg_type_p) + { + *msg_type_p = 0; // compiler warning + } + return (ret); +} + +/* + * Process optional time-based Hop-by-hop headers. + * Packet already verified for sanity by cicn_parse_pkt(). + * An Interest Lifetime TLV will affect the PIT timeout + * value, or whether the interest should be put in the PIT + * at all (if the value is 0 then no content is expected). + * Caching will use the Recommended Cache Time TLV. + */ +static inline int +cicn_parse_hdr_time_ms (uint8_t * pkt, cicn_pkt_hdr_desc_t * pkt_hdr_desc, + uint16_t type, uint64_t * time_res) +{ + uint8_t *p; + uint16_t len; + uint64_t v; + + if (pkt_hdr_desc->ph_lifetime_idx == -1) + { + return (ENOENT); + } + + p = pkt + pkt_hdr_desc->ph_lifetime_idx; + len = cicn_parse_tlvlength (p); + + switch (type) + { + case CICN_HDR_TLV_INT_LIFETIME: + if (len > 8) + { + return (ENOENT); + } + v = + cicn_parse_getvlv (p + CICN_TLV_HDR_LEN, p + CICN_TLV_HDR_LEN + len); + break; + case CICN_HDR_TLV_CACHE_TIME: + if (len != 8) + { + return (ENOENT); + } + C_GETINT64 (v, p + CICN_TLV_HDR_LEN); + break; + default: + return (ENOENT); + break; + } + + *time_res = v; + return AOK; +} + +/* + * skip over pkt_hdr to msg. + * pkt_hdr must have already been verified by cicn_parse_pkt() + */ +static inline uint8_t * +cicn_parse_pkt2msg (cicn_packet_hdr_t * pkt_hdr) +{ + uint8_t *pkt_hdr_ptr = (uint8_t *) pkt_hdr; + return (&pkt_hdr_ptr[pkt_hdr->pkt_hdr_len]); +} + + +/* + * Utility to convert a string into a series of name-components. We use this + * in cli handling, for example. We write into 'buf', and we return the + * number of octets used, or an error < 0. This only creates name-comps: it's + * up to the caller to create a complete name tlv if that's needed. + * - obuf holds result + * - obuflen is size of result buffer + * - str is name in "/"-separated ascii + * - chunk_name specifies whether the name's last component should be + * chunk name component rather than generic name component. + * + * This is pretty basic right now: + * - the '/' character is the separator + * - binary octets (via escapes) not supported + * - instring component type specification not supported + * - not much validation of the input string. + */ +static inline int +cicn_parse_name_comps_from_str_inline (uint8_t * obuf, int obuflen, + const char *str, int chunk_name) +{ + int ret = -EINVAL; + + int i, used, start; + uint8_t *obufp; + uint8_t *last_comp = NULL; // chunk component, if chunk_name != 0 + + if (obuf == NULL || str == NULL) + { + goto done; + } + + /* Special case empty string, which means a valid name with no components. + */ + if (str[0] == '\000') + { + ret = 0; + goto done; + } + + /* Let's see how many slashes there are, so we can pre-check the + * buffer space we'll need. There is the special-case of a single + * '/', which means a single empty name-component. + */ + used = (str[0] == '/' || str[0] == '\000') ? 0 : 1; + + for (i = 0; str[i] != 0; i++) + { + if (str[i] == '/') + { + used++; + } + } + + /* Estimate safe buf len required */ + if (obuflen < (i + (4 * used))) + { + ret = -ENOSPC; + goto done; + } + + /* Convert to name-comp encoding */ + start = (str[0] == '/') ? 1 : 0; + for (i = start, obufp = obuf;; i++) + { + + if ((str[i] == '/') || ((str[i] == '\000') && (i > start))) + { + + last_comp = obufp; + C_PUTINT16 (obufp, CICN_NAME_COMP); + obufp += CICN_TLV_TYPE_LEN; + C_PUTINT16 (obufp, (i - start)); + obufp += CICN_TLV_LEN_LEN; + + memcpy (obufp, str + start, i - start); + obufp += (i - start); + + start = i + 1; + } + + if (str[i] == 0) + { + ret = obufp - obuf; + break; + } + } + if (chunk_name && (last_comp != NULL)) + { + C_PUTINT16 (last_comp, CICN_NAME_COMP_CHUNK); + } + +done: + + return (ret); +} + +/* + * Utility to convert from tlv-encoded prefix to string (no leading name tlv), + * for cli output e.g. See also cicn_parse_name_to_str() + * + * For resultant buf, return strlen(buf) in *str_len_res + */ +static inline int +cicn_parse_prefix_to_str (char *buf, int bufsize, const uint8_t * prefix, + int pfxlen, int *str_len_res) +{ + int i, str_len, ret = EINVAL; + uint16_t sval; + + str_len = 0; + + if ((buf == NULL) || (bufsize < 1) || (prefix == NULL) || (pfxlen < 0)) + { + goto done; + } + + /* Special-case empty prefix */ + if (pfxlen == 0) + { + *buf = '\0'; + ret = AOK; + goto done; + } + + for (i = 0;; i++) + { + if (i >= pfxlen) + { + break; + } + + /* Must have at least T + L */ + if ((pfxlen - i) < 4) + { + break; + } + + /* Skip 'T' */ + i += 2; + + C_GETINT16 (sval, (prefix + i)); + + /* Must not overrun 'prefix': error */ + if ((i + 2 + sval) > pfxlen) + { + goto done; + } + + /* Advance past 'L' */ + i += 2; + + if (str_len >= bufsize) + { + ret = ENOSPC; + goto done; + } + else + { + buf[str_len++] = '/'; + } + + while (sval > 0) + { + if (prefix[i] == '\\' || !isprint (prefix[i])) + { + int len; + if (prefix[i] == '\\') + { + len = + snprintf (&buf[str_len], bufsize - str_len, "%s", "\\\\"); + } + else + { + len = + snprintf (&buf[str_len], bufsize - str_len, "\\%03o", + prefix[i]); + } + if (len < 0) + { + goto done; + } + str_len += len; + if (str_len >= bufsize) + { + ret = ENOSPC; + goto done; + } + } + else + { + if (str_len >= bufsize) + { + ret = ENOSPC; + goto done; + } + else + { + buf[str_len++] = prefix[i]; + } + } + sval--; + + if (sval > 0) + { + i++; + } + } + + } /* End for... */ + + if (str_len >= bufsize) + { + ret = ENOSPC; // no space for terminal \0, added below + goto done; + } + + ret = AOK; // success + +done: + + if (bufsize <= 0) + { + str_len = 0; + } + else + { + if (str_len >= bufsize) + { + str_len = bufsize - 1; + } + buf[str_len] = '\000'; + } + if (str_len_res) + { + *str_len_res = str_len; + } + return (ret); +} + +/* + * Convert name (including name tlv header) to printable buffer + * For resultant buf, return strlen(buf) in *str_len_res + */ +static inline int +cicn_parse_name_to_str (char *buf, int bufsize, const uint8_t * name, + int namelen, int *str_len_res) +{ + int ret; + uint16_t sval; + + if (namelen < CICN_TLV_HDR_LEN) + { + return (EINVAL); + } + C_GETINT16 (sval, &name[0]); + if (sval != CICN_TLV_NAME) + { + return (EINVAL); + } + C_GETINT16 (sval, &name[CICN_TLV_TYPE_LEN]); + if (sval != namelen - CICN_TLV_HDR_LEN) + { /* Must not overrun name */ + return (EINVAL); + } + ret = + cicn_parse_prefix_to_str (buf, bufsize, name + CICN_TLV_HDR_LEN, + namelen - CICN_TLV_HDR_LEN, str_len_res); + + return (ret); +} + +int +cicn_parse_name_comps_from_str (uint8_t * obuf, int obuflen, const char *str, + cicn_rd_t * cicn_rd); +int cicn_parse_name_from_str (uint8_t * obuf, int obuflen, const char *str, + int is_chunk_name, cicn_rd_t * cicn_rd); + +#endif /* _CICN_PARSER_H_ */ |