Annotation of embedaddon/mtr/packet/command.c, revision 1.1
1.1 ! misho 1: /*
! 2: mtr -- a network diagnostic tool
! 3: Copyright (C) 2016 Matt Kimball
! 4:
! 5: This program is free software; you can redistribute it and/or modify
! 6: it under the terms of the GNU General Public License version 2 as
! 7: published by the Free Software Foundation.
! 8:
! 9: This program is distributed in the hope that it will be useful,
! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of
! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 12: GNU General Public License for more details.
! 13:
! 14: You should have received a copy of the GNU General Public License
! 15: along with this program; if not, write to the Free Software
! 16: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
! 17: */
! 18:
! 19: #include "command.h"
! 20:
! 21: #include <assert.h>
! 22: #include <errno.h>
! 23: #include <stdbool.h>
! 24: #include <stdio.h>
! 25: #include <stdlib.h>
! 26: #include <string.h>
! 27: #include <strings.h>
! 28:
! 29: #include "cmdparse.h"
! 30: #include "platform.h"
! 31: #include "config.h"
! 32:
! 33: /*
! 34: Find a parameter with a particular name in a command_t structure.
! 35: If no such parameter exists, return NULL.
! 36: */
! 37: static
! 38: const char *find_parameter(
! 39: const struct command_t *command,
! 40: const char *name_request)
! 41: {
! 42: const char *name;
! 43: const char *value;
! 44: int i;
! 45:
! 46: for (i = 0; i < command->argument_count; i++) {
! 47: name = command->argument_name[i];
! 48: value = command->argument_value[i];
! 49:
! 50: if (!strcmp(name, name_request)) {
! 51: return value;
! 52: }
! 53: }
! 54:
! 55: return NULL;
! 56: }
! 57:
! 58: /* Returns a feature support string for a particular probe protocol */
! 59: static
! 60: const char *check_protocol_support(
! 61: struct net_state_t *net_state,
! 62: int protocol)
! 63: {
! 64: if (is_protocol_supported(net_state, protocol)) {
! 65: return "ok";
! 66: } else {
! 67: return "no";
! 68: }
! 69: }
! 70:
! 71: /* Return a feature support string for an IP protocol version */
! 72: static
! 73: const char *check_ip_version_support(
! 74: struct net_state_t *net_state,
! 75: int ip_version)
! 76: {
! 77: if (is_ip_version_supported(net_state, ip_version)) {
! 78: return "ok";
! 79: } else {
! 80: return "no";
! 81: }
! 82: }
! 83:
! 84: /* Given a feature name, return a string for the check-support reply */
! 85: static
! 86: const char *check_support(
! 87: const char *feature,
! 88: struct net_state_t *net_state)
! 89: {
! 90: if (!strcmp(feature, "version")) {
! 91: return PACKAGE_VERSION;
! 92: }
! 93:
! 94: if (!strcmp(feature, "ip-4")) {
! 95: return check_ip_version_support(net_state, 4);
! 96: }
! 97:
! 98: if (!strcmp(feature, "ip-6")) {
! 99: return check_ip_version_support(net_state, 6);
! 100: }
! 101:
! 102: if (!strcmp(feature, "send-probe")) {
! 103: return "ok";
! 104: }
! 105:
! 106: if (!strcmp(feature, "icmp")) {
! 107: return check_protocol_support(net_state, IPPROTO_ICMP);
! 108: }
! 109:
! 110: if (!strcmp(feature, "udp")) {
! 111: return check_protocol_support(net_state, IPPROTO_UDP);
! 112: }
! 113:
! 114: if (!strcmp(feature, "tcp")) {
! 115: return check_protocol_support(net_state, IPPROTO_TCP);
! 116: }
! 117: #ifdef IPPROTO_SCTP
! 118: if (!strcmp(feature, "sctp")) {
! 119: return check_protocol_support(net_state, IPPROTO_SCTP);
! 120: }
! 121: #endif
! 122:
! 123: #ifdef SO_MARK
! 124: if (!strcmp(feature, "mark")) {
! 125: return "ok";
! 126: }
! 127: #endif
! 128:
! 129: return "no";
! 130: }
! 131:
! 132: /* Handle a check-support request by checking for a particular feature */
! 133: static
! 134: void check_support_command(
! 135: const struct command_t *command,
! 136: struct net_state_t *net_state)
! 137: {
! 138: const char *feature;
! 139: const char *support;
! 140:
! 141: feature = find_parameter(command, "feature");
! 142: if (feature == NULL) {
! 143: printf("%d invalid-argument\n", command->token);
! 144: return;
! 145: }
! 146:
! 147: support = check_support(feature, net_state);
! 148: printf("%d feature-support support %s\n", command->token, support);
! 149: }
! 150:
! 151: /*
! 152: If a named send_probe argument is recognized, fill in the probe paramters
! 153: structure with the argument value.
! 154: */
! 155: static
! 156: bool decode_probe_argument(
! 157: struct probe_param_t *param,
! 158: const char *name,
! 159: const char *value)
! 160: {
! 161: char *endstr = NULL;
! 162:
! 163: /* Pass IPv4 addresses as string values */
! 164: if (!strcmp(name, "ip-4")) {
! 165: param->ip_version = 4;
! 166: param->remote_address = value;
! 167: }
! 168:
! 169: /* IPv6 address */
! 170: if (!strcmp(name, "ip-6")) {
! 171: param->ip_version = 6;
! 172: param->remote_address = value;
! 173: }
! 174:
! 175: /* IPv4 address to send from */
! 176: if (!strcmp(name, "local-ip-4")) {
! 177: param->local_address = value;
! 178: }
! 179:
! 180: /* IPv6 address to send from */
! 181: if (!strcmp(name, "local-ip-6")) {
! 182: param->local_address = value;
! 183: }
! 184:
! 185: /* Protocol for the probe */
! 186: if (!strcmp(name, "protocol")) {
! 187: if (!strcmp(value, "icmp")) {
! 188: param->protocol = IPPROTO_ICMP;
! 189: } else if (!strcmp(value, "udp")) {
! 190: param->protocol = IPPROTO_UDP;
! 191: } else if (!strcmp(value, "tcp")) {
! 192: param->protocol = IPPROTO_TCP;
! 193: #ifdef IPPROTO_SCTP
! 194: } else if (!strcmp(value, "sctp")) {
! 195: param->protocol = IPPROTO_SCTP;
! 196: #endif
! 197: } else {
! 198: return false;
! 199: }
! 200: }
! 201:
! 202: /* Destination port for the probe */
! 203: if (!strcmp(name, "port")) {
! 204: param->dest_port = strtol(value, &endstr, 10);
! 205: if (*endstr != 0) {
! 206: return false;
! 207: }
! 208: }
! 209:
! 210: /* The local port to send UDP probes from */
! 211: if (!strcmp(name, "local-port")) {
! 212: param->local_port = strtol(value, &endstr, 10);
! 213: if (*endstr != 0) {
! 214: return false;
! 215: }
! 216:
! 217: /*
! 218: Don't allow using a local port which requires
! 219: privileged binding.
! 220: */
! 221: if (param->local_port < 1024) {
! 222: param->local_port = 0;
! 223: return false;
! 224: }
! 225: }
! 226:
! 227: /* The "type of service" field for the IP header */
! 228: if (!strcmp(name, "tos")) {
! 229: param->type_of_service = strtol(value, &endstr, 10);
! 230: if (*endstr != 0) {
! 231: return false;
! 232: }
! 233: }
! 234:
! 235: /* The Linux packet mark for mark-based routing */
! 236: if (!strcmp(name, "mark")) {
! 237: param->routing_mark = strtol(value, &endstr, 10);
! 238: if (*endstr != 0) {
! 239: return false;
! 240: }
! 241: }
! 242:
! 243: /* The size of the packet (including headers) */
! 244: if (!strcmp(name, "size")) {
! 245: param->packet_size = strtol(value, &endstr, 10);
! 246: if (*endstr != 0) {
! 247: return false;
! 248: }
! 249: }
! 250:
! 251: /* The packet's bytes will be filled with this value */
! 252: if (!strcmp(name, "bit-pattern")) {
! 253: param->bit_pattern = strtol(value, &endstr, 10);
! 254: if (*endstr != 0) {
! 255: return false;
! 256: }
! 257: }
! 258:
! 259: /* Time-to-live values */
! 260: if (!strcmp(name, "ttl")) {
! 261: param->ttl = strtol(value, &endstr, 10);
! 262: if (*endstr != 0) {
! 263: return false;
! 264: }
! 265: }
! 266:
! 267: /* Number of seconds to wait for a reply */
! 268: if (!strcmp(name, "timeout")) {
! 269: param->timeout = strtol(value, &endstr, 10);
! 270: if (*endstr != 0) {
! 271: return false;
! 272: }
! 273: }
! 274:
! 275: return true;
! 276: }
! 277:
! 278: /*
! 279: Check the probe parameters against currently supported features
! 280: and report and error if there is a problem.
! 281:
! 282: Return true if everything is okay, false otherwise.
! 283: */
! 284: static
! 285: bool validate_probe_parameters(
! 286: struct net_state_t *net_state,
! 287: struct probe_param_t *param)
! 288: {
! 289: if (!is_ip_version_supported(net_state, param->ip_version)) {
! 290: printf("%d invalid-argument reason ip-version-not-supported\n",
! 291: param->command_token);
! 292:
! 293: return false;
! 294: }
! 295:
! 296: if (!is_protocol_supported(net_state, param->protocol)) {
! 297: printf("%d invalid-argument reason protocol-not-supported\n",
! 298: param->command_token);
! 299:
! 300: return false;
! 301: }
! 302:
! 303: return true;
! 304: }
! 305:
! 306: /* Handle "send-probe" commands */
! 307: static
! 308: void send_probe_command(
! 309: const struct command_t *command,
! 310: struct net_state_t *net_state)
! 311: {
! 312: struct probe_param_t param;
! 313: int i;
! 314: char *name;
! 315: char *value;
! 316:
! 317: /* We will prepare a probe_param_t for send_probe. */
! 318: memset(¶m, 0, sizeof(struct probe_param_t));
! 319: param.command_token = command->token;
! 320: param.protocol = IPPROTO_ICMP;
! 321: param.ttl = 255;
! 322: param.packet_size = 64;
! 323: param.timeout = 10;
! 324:
! 325: for (i = 0; i < command->argument_count; i++) {
! 326: name = command->argument_name[i];
! 327: value = command->argument_value[i];
! 328:
! 329: if (!decode_probe_argument(¶m, name, value)) {
! 330: printf("%d invalid-argument\n", command->token);
! 331: return;
! 332: }
! 333: }
! 334:
! 335: if (!validate_probe_parameters(net_state, ¶m)) {
! 336: return;
! 337: }
! 338:
! 339: /* Send the probe using a platform specific mechanism */
! 340: send_probe(net_state, ¶m);
! 341: }
! 342:
! 343: /*
! 344: Given a parsed command, dispatch to the handler for specific
! 345: command requests.
! 346: */
! 347: static
! 348: void dispatch_command(
! 349: const struct command_t *command,
! 350: struct net_state_t *net_state)
! 351: {
! 352: if (!strcmp(command->command_name, "check-support")) {
! 353: check_support_command(command, net_state);
! 354: } else if (!strcmp(command->command_name, "send-probe")) {
! 355: send_probe_command(command, net_state);
! 356: } else {
! 357: /* For unrecognized commands, respond with an error */
! 358: printf("%d unknown-command\n", command->token);
! 359: }
! 360: }
! 361:
! 362: /*
! 363: With newly read data in our command buffer, dispatch all completed
! 364: command requests.
! 365: */
! 366: void dispatch_buffer_commands(
! 367: struct command_buffer_t *buffer,
! 368: struct net_state_t *net_state)
! 369: {
! 370: struct command_t command;
! 371: char *end_of_command;
! 372: char full_command[COMMAND_BUFFER_SIZE];
! 373: int command_length;
! 374: int remaining_count;
! 375:
! 376: while (true) {
! 377: assert(buffer->incoming_read_position < COMMAND_BUFFER_SIZE);
! 378:
! 379: /* Terminate the buffer string */
! 380: buffer->incoming_buffer[buffer->incoming_read_position] = 0;
! 381:
! 382: /* Find the next newline, which terminates command requests */
! 383: end_of_command = index(buffer->incoming_buffer, '\n');
! 384: if (end_of_command == NULL) {
! 385: /*
! 386: No newlines found, so any data we've read so far is
! 387: not yet complete.
! 388: */
! 389: break;
! 390: }
! 391:
! 392: command_length = end_of_command - buffer->incoming_buffer;
! 393: remaining_count =
! 394: buffer->incoming_read_position - command_length - 1;
! 395:
! 396: /* Copy the completed command */
! 397: memmove(full_command, buffer->incoming_buffer, command_length);
! 398: full_command[command_length] = 0;
! 399:
! 400: /*
! 401: Free the space used by the completed command by advancing the
! 402: remaining requests within the buffer.
! 403: */
! 404: memmove(buffer->incoming_buffer, end_of_command + 1,
! 405: remaining_count);
! 406: buffer->incoming_read_position -= command_length + 1;
! 407:
! 408: if (parse_command(&command, full_command)) {
! 409: /* If the command fails to parse, respond with an error */
! 410: printf("0 command-parse-error\n");
! 411: } else {
! 412: dispatch_command(&command, net_state);
! 413: }
! 414: }
! 415:
! 416: if (buffer->incoming_read_position >= COMMAND_BUFFER_SIZE - 1) {
! 417: /*
! 418: If we've filled the buffer without a complete command, the
! 419: only thing we can do is discard what we've read and hope that
! 420: new data is better formatted.
! 421: */
! 422: printf("0 command-buffer-overflow\n");
! 423: buffer->incoming_read_position = 0;
! 424: }
! 425: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>