![]() |
|
|||
0001 // SPDX-License-Identifier: GPL-2.0+ 0002 /* 0003 * SSH message parser. 0004 * 0005 * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com> 0006 */ 0007 0008 #include <asm/unaligned.h> 0009 #include <linux/compiler.h> 0010 #include <linux/device.h> 0011 #include <linux/types.h> 0012 0013 #include <linux/surface_aggregator/serial_hub.h> 0014 #include "ssh_parser.h" 0015 0016 /** 0017 * sshp_validate_crc() - Validate a CRC in raw message data. 0018 * @src: The span of data over which the CRC should be computed. 0019 * @crc: The pointer to the expected u16 CRC value. 0020 * 0021 * Computes the CRC of the provided data span (@src), compares it to the CRC 0022 * stored at the given address (@crc), and returns the result of this 0023 * comparison, i.e. %true if equal. This function is intended to run on raw 0024 * input/message data. 0025 * 0026 * Return: Returns %true if the computed CRC matches the stored CRC, %false 0027 * otherwise. 0028 */ 0029 static bool sshp_validate_crc(const struct ssam_span *src, const u8 *crc) 0030 { 0031 u16 actual = ssh_crc(src->ptr, src->len); 0032 u16 expected = get_unaligned_le16(crc); 0033 0034 return actual == expected; 0035 } 0036 0037 /** 0038 * sshp_starts_with_syn() - Check if the given data starts with SSH SYN bytes. 0039 * @src: The data span to check the start of. 0040 */ 0041 static bool sshp_starts_with_syn(const struct ssam_span *src) 0042 { 0043 return src->len >= 2 && get_unaligned_le16(src->ptr) == SSH_MSG_SYN; 0044 } 0045 0046 /** 0047 * sshp_find_syn() - Find SSH SYN bytes in the given data span. 0048 * @src: The data span to search in. 0049 * @rem: The span (output) indicating the remaining data, starting with SSH 0050 * SYN bytes, if found. 0051 * 0052 * Search for SSH SYN bytes in the given source span. If found, set the @rem 0053 * span to the remaining data, starting with the first SYN bytes and capped by 0054 * the source span length, and return %true. This function does not copy any 0055 * data, but rather only sets pointers to the respective start addresses and 0056 * length values. 0057 * 0058 * If no SSH SYN bytes could be found, set the @rem span to the zero-length 0059 * span at the end of the source span and return %false. 0060 * 0061 * If partial SSH SYN bytes could be found at the end of the source span, set 0062 * the @rem span to cover these partial SYN bytes, capped by the end of the 0063 * source span, and return %false. This function should then be re-run once 0064 * more data is available. 0065 * 0066 * Return: Returns %true if a complete SSH SYN sequence could be found, 0067 * %false otherwise. 0068 */ 0069 bool sshp_find_syn(const struct ssam_span *src, struct ssam_span *rem) 0070 { 0071 size_t i; 0072 0073 for (i = 0; i < src->len - 1; i++) { 0074 if (likely(get_unaligned_le16(src->ptr + i) == SSH_MSG_SYN)) { 0075 rem->ptr = src->ptr + i; 0076 rem->len = src->len - i; 0077 return true; 0078 } 0079 } 0080 0081 if (unlikely(src->ptr[src->len - 1] == (SSH_MSG_SYN & 0xff))) { 0082 rem->ptr = src->ptr + src->len - 1; 0083 rem->len = 1; 0084 return false; 0085 } 0086 0087 rem->ptr = src->ptr + src->len; 0088 rem->len = 0; 0089 return false; 0090 } 0091 0092 /** 0093 * sshp_parse_frame() - Parse SSH frame. 0094 * @dev: The device used for logging. 0095 * @source: The source to parse from. 0096 * @frame: The parsed frame (output). 0097 * @payload: The parsed payload (output). 0098 * @maxlen: The maximum supported message length. 0099 * 0100 * Parses and validates a SSH frame, including its payload, from the given 0101 * source. Sets the provided @frame pointer to the start of the frame and 0102 * writes the limits of the frame payload to the provided @payload span 0103 * pointer. 0104 * 0105 * This function does not copy any data, but rather only validates the message 0106 * data and sets pointers (and length values) to indicate the respective parts. 0107 * 0108 * If no complete SSH frame could be found, the frame pointer will be set to 0109 * the %NULL pointer and the payload span will be set to the null span (start 0110 * pointer %NULL, size zero). 0111 * 0112 * Return: Returns zero on success or if the frame is incomplete, %-ENOMSG if 0113 * the start of the message is invalid, %-EBADMSG if any (frame-header or 0114 * payload) CRC is invalid, or %-EMSGSIZE if the SSH message is bigger than 0115 * the maximum message length specified in the @maxlen parameter. 0116 */ 0117 int sshp_parse_frame(const struct device *dev, const struct ssam_span *source, 0118 struct ssh_frame **frame, struct ssam_span *payload, 0119 size_t maxlen) 0120 { 0121 struct ssam_span sf; 0122 struct ssam_span sp; 0123 0124 /* Initialize output. */ 0125 *frame = NULL; 0126 payload->ptr = NULL; 0127 payload->len = 0; 0128 0129 if (!sshp_starts_with_syn(source)) { 0130 dev_warn(dev, "rx: parser: invalid start of frame\n"); 0131 return -ENOMSG; 0132 } 0133 0134 /* Check for minimum packet length. */ 0135 if (unlikely(source->len < SSH_MESSAGE_LENGTH(0))) { 0136 dev_dbg(dev, "rx: parser: not enough data for frame\n"); 0137 return 0; 0138 } 0139 0140 /* Pin down frame. */ 0141 sf.ptr = source->ptr + sizeof(u16); 0142 sf.len = sizeof(struct ssh_frame); 0143 0144 /* Validate frame CRC. */ 0145 if (unlikely(!sshp_validate_crc(&sf, sf.ptr + sf.len))) { 0146 dev_warn(dev, "rx: parser: invalid frame CRC\n"); 0147 return -EBADMSG; 0148 } 0149 0150 /* Ensure packet does not exceed maximum length. */ 0151 sp.len = get_unaligned_le16(&((struct ssh_frame *)sf.ptr)->len); 0152 if (unlikely(SSH_MESSAGE_LENGTH(sp.len) > maxlen)) { 0153 dev_warn(dev, "rx: parser: frame too large: %llu bytes\n", 0154 SSH_MESSAGE_LENGTH(sp.len)); 0155 return -EMSGSIZE; 0156 } 0157 0158 /* Pin down payload. */ 0159 sp.ptr = sf.ptr + sf.len + sizeof(u16); 0160 0161 /* Check for frame + payload length. */ 0162 if (source->len < SSH_MESSAGE_LENGTH(sp.len)) { 0163 dev_dbg(dev, "rx: parser: not enough data for payload\n"); 0164 return 0; 0165 } 0166 0167 /* Validate payload CRC. */ 0168 if (unlikely(!sshp_validate_crc(&sp, sp.ptr + sp.len))) { 0169 dev_warn(dev, "rx: parser: invalid payload CRC\n"); 0170 return -EBADMSG; 0171 } 0172 0173 *frame = (struct ssh_frame *)sf.ptr; 0174 *payload = sp; 0175 0176 dev_dbg(dev, "rx: parser: valid frame found (type: %#04x, len: %u)\n", 0177 (*frame)->type, (*frame)->len); 0178 0179 return 0; 0180 } 0181 0182 /** 0183 * sshp_parse_command() - Parse SSH command frame payload. 0184 * @dev: The device used for logging. 0185 * @source: The source to parse from. 0186 * @command: The parsed command (output). 0187 * @command_data: The parsed command data/payload (output). 0188 * 0189 * Parses and validates a SSH command frame payload. Sets the @command pointer 0190 * to the command header and the @command_data span to the command data (i.e. 0191 * payload of the command). This will result in a zero-length span if the 0192 * command does not have any associated data/payload. This function does not 0193 * check the frame-payload-type field, which should be checked by the caller 0194 * before calling this function. 0195 * 0196 * The @source parameter should be the complete frame payload, e.g. returned 0197 * by the sshp_parse_frame() command. 0198 * 0199 * This function does not copy any data, but rather only validates the frame 0200 * payload data and sets pointers (and length values) to indicate the 0201 * respective parts. 0202 * 0203 * Return: Returns zero on success or %-ENOMSG if @source does not represent a 0204 * valid command-type frame payload, i.e. is too short. 0205 */ 0206 int sshp_parse_command(const struct device *dev, const struct ssam_span *source, 0207 struct ssh_command **command, 0208 struct ssam_span *command_data) 0209 { 0210 /* Check for minimum length. */ 0211 if (unlikely(source->len < sizeof(struct ssh_command))) { 0212 *command = NULL; 0213 command_data->ptr = NULL; 0214 command_data->len = 0; 0215 0216 dev_err(dev, "rx: parser: command payload is too short\n"); 0217 return -ENOMSG; 0218 } 0219 0220 *command = (struct ssh_command *)source->ptr; 0221 command_data->ptr = source->ptr + sizeof(struct ssh_command); 0222 command_data->len = source->len - sizeof(struct ssh_command); 0223 0224 dev_dbg(dev, "rx: parser: valid command found (tc: %#04x, cid: %#04x)\n", 0225 (*command)->tc, (*command)->cid); 0226 0227 return 0; 0228 }
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.1.0 LXR engine. The LXR team |
![]() ![]() |