+} /* }}} void usage_exit */
+
+__attribute__((noreturn))
+static void usage_qos_exit (const char *arg, int status) /* {{{ */
+{
+ if (arg != 0)
+ fprintf (stderr, "Invalid QoS argument: \"%s\"\n\n", arg);
+
+ fprintf (stderr, "Valid QoS arguments (option \"-Q\") are:\n"
+ "\n"
+ " Differentiated Services (IPv4 and IPv6, RFC 2474)\n"
+ "\n"
+ " be Best Effort (BE, default PHB).\n"
+ " ef Expedited Forwarding (EF) PHB group (RFC 3246).\n"
+ " (low delay, low loss, low jitter)\n"
+ " va Voice Admit (VA) DSCP (RFC 5865).\n"
+ " (capacity-admitted traffic)\n"
+ " af[1-4][1-3] Assured Forwarding (AF) PHB group (RFC 2597).\n"
+ " For example: \"af12\" (class 1, precedence 2)\n"
+ " cs[0-7] Class Selector (CS) PHB group (RFC 2474).\n"
+ " For example: \"cs1\" (priority traffic)\n"
+ "\n"
+ " Type of Service (IPv4, RFC 1349, obsolete)\n"
+ "\n"
+ " lowdelay (%#04x) minimize delay\n"
+ " throughput (%#04x) maximize throughput\n"
+ " reliability (%#04x) maximize reliability\n"
+ " mincost (%#04x) minimize monetary cost\n"
+ "\n"
+ " Specify manually\n"
+ "\n"
+ " 0x00 - 0xff Hexadecimal numeric specification.\n"
+ " 0 - 255 Decimal numeric specification.\n"
+ "\n",
+ (unsigned int) IPTOS_LOWDELAY,
+ (unsigned int) IPTOS_THROUGHPUT,
+ (unsigned int) IPTOS_RELIABILITY,
+ (unsigned int) IPTOS_MINCOST);
+
+ exit (status);
+} /* }}} void usage_qos_exit */
+
+static int set_opt_send_qos (const char *opt) /* {{{ */
+{
+ if (opt == NULL)
+ return (EINVAL);
+
+ if (strcasecmp ("help", opt) == 0)
+ usage_qos_exit (/* arg = */ NULL, /* status = */ EXIT_SUCCESS);
+ /* DiffServ (RFC 2474): */
+ /* - Best effort (BE) */
+ else if (strcasecmp ("be", opt) == 0)
+ opt_send_qos = 0;
+ /* - Expedited Forwarding (EF, RFC 3246) */
+ else if (strcasecmp ("ef", opt) == 0)
+ opt_send_qos = 0xB8; /* == 0x2E << 2 */
+ /* - Voice Admit (VA, RFC 5865) */
+ else if (strcasecmp ("va", opt) == 0)
+ opt_send_qos = 0xB0; /* == 0x2D << 2 */
+ /* - Assured Forwarding (AF, RFC 2597) */
+ else if ((strncasecmp ("af", opt, strlen ("af")) == 0)
+ && (strlen (opt) == 4))
+ {
+ uint8_t dscp;
+ uint8_t class = 0;
+ uint8_t prec = 0;
+
+ /* There are four classes, AF1x, AF2x, AF3x, and AF4x. */
+ if (opt[2] == '1')
+ class = 1;
+ else if (opt[2] == '2')
+ class = 2;
+ else if (opt[2] == '3')
+ class = 3;
+ else if (opt[2] == '4')
+ class = 4;
+ else
+ usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
+
+ /* In each class, there are three precedences, AFx1, AFx2, and AFx3 */
+ if (opt[3] == '1')
+ prec = 1;
+ else if (opt[3] == '2')
+ prec = 2;
+ else if (opt[3] == '3')
+ prec = 3;
+ else
+ usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
+
+ dscp = (8 * class) + (2 * prec);
+ /* The lower two bits are used for Explicit Congestion Notification (ECN) */
+ opt_send_qos = dscp << 2;
+ }
+ /* - Class Selector (CS) */
+ else if ((strncasecmp ("cs", opt, strlen ("cs")) == 0)
+ && (strlen (opt) == 3))
+ {
+ uint8_t class;
+
+ if ((opt[2] < '0') || (opt[2] > '7'))
+ usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
+
+ /* Not exactly legal by the C standard, but I don't know of any
+ * system not supporting this hack. */
+ class = ((uint8_t) opt[2]) - ((uint8_t) '0');
+ opt_send_qos = class << 5;
+ }
+ /* Type of Service (RFC 1349) */
+ else if (strcasecmp ("lowdelay", opt) == 0)
+ opt_send_qos = IPTOS_LOWDELAY;
+ else if (strcasecmp ("throughput", opt) == 0)
+ opt_send_qos = IPTOS_THROUGHPUT;
+ else if (strcasecmp ("reliability", opt) == 0)
+ opt_send_qos = IPTOS_RELIABILITY;
+ else if (strcasecmp ("mincost", opt) == 0)
+ opt_send_qos = IPTOS_MINCOST;
+ /* Numeric value */
+ else
+ {
+ unsigned long value;
+ char *endptr;
+
+ errno = 0;
+ endptr = NULL;
+ value = strtoul (opt, &endptr, /* base = */ 0);
+ if ((errno != 0) || (endptr == opt)
+ || (endptr == NULL) || (*endptr != 0)
+ || (value > 0xff))
+ usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
+
+ opt_send_qos = (uint8_t) value;
+ }