1aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger/*
27518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger * q_hfsc.c	HFSC.
3aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *
4aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *		This program is free software; you can redistribute it and/or
5aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *		modify it under the terms of the GNU General Public License
6aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *		as published by the Free Software Foundation; either version
7aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *		2 of the License, or (at your option) any later version.
8aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *
97518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger * Authors:	Patrick McHardy, <kaber@trash.net>
10aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger *
11aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger */
12aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
13aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <stdio.h>
14aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <stdlib.h>
15aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <unistd.h>
16aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <syslog.h>
17aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <fcntl.h>
18aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <sys/socket.h>
19aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <netinet/in.h>
20aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <arpa/inet.h>
21aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include <string.h>
227518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger#include <math.h>
23aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
24aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include "utils.h"
25aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger#include "tc_util.h"
26aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
277518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int hfsc_get_sc(int *, char ***, struct tc_service_curve *);
28aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
297518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
307518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic void
317518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerexplain_qdisc(void)
327518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
337518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(stderr,
347518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"Usage: ... hfsc [ default CLASSID ]\n"
357518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
367518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" default: default class for unclassified packets\n"
377518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	);
387518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
397518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
407518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic void
417518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerexplain_class(void)
427518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
437518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(stderr,
44b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger		"Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n"
457518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
4641f6004139e3dd8c8ab0d62ad0142f92b22e8c4aMichal Soltys		"SC := [ [ m1 BPS ] d SEC ] m2 BPS\n"
477518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
487518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" m1 : slope of first segment\n"
497518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" d  : x-coordinate of intersection\n"
507518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" m2 : slope of second segment\n"
517518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
527518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"Alternative format:\n"
537518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
547518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"SC := [ [ umax BYTE ] dmax SEC ] rate BPS\n"
557518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
567518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" umax : maximum unit of work\n"
577518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" dmax : maximum delay\n"
587518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		" rate : rate\n"
597518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		"\n"
6041f6004139e3dd8c8ab0d62ad0142f92b22e8c4aMichal Soltys		"Remarks:\n"
6141f6004139e3dd8c8ab0d62ad0142f92b22e8c4aMichal Soltys		" - at least one of 'rt', 'ls' or 'sc' must be specified\n"
6241f6004139e3dd8c8ab0d62ad0142f92b22e8c4aMichal Soltys		" - 'ul' can only be specified with 'ls' or 'sc'\n"
6341f6004139e3dd8c8ab0d62ad0142f92b22e8c4aMichal Soltys		"\n"
647518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	);
657518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
667518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
677518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic void
687518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerexplain1(char *arg)
697518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
707518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(stderr, "HFSC: Illegal \"%s\"\n", arg);
717518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
727518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
737518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
747518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
757518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
767518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct tc_hfsc_qopt qopt;
777518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
787518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	memset(&qopt, 0, sizeof(qopt));
797518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
807518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	while (argc > 0) {
817518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (matches(*argv, "default") == 0) {
827518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			NEXT_ARG();
837518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			if (qopt.defcls != 0) {
847518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				fprintf(stderr, "HFSC: Double \"default\"\n");
857518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				return -1;
867518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			}
877518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			if (get_u16(&qopt.defcls, *argv, 16) < 0) {
887518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				explain1("default");
897518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				return -1;
907518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			}
917518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		} else if (matches(*argv, "help") == 0) {
927518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain_qdisc();
937518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
947518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		} else {
957518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv);
967518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain_qdisc();
977518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
987518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
997518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		argc--, argv++;
1007518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
1017518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1027518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	addattr_l(n, 1024, TCA_OPTIONS, &qopt, sizeof(qopt));
1037518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
1047518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
1057518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1067518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
1077518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
108aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger{
1097518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct tc_hfsc_qopt *qopt;
1107518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1117518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (opt == NULL)
1127518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return 0;
1137518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (RTA_PAYLOAD(opt) < sizeof(*qopt))
1147518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
1157518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	qopt = RTA_DATA(opt);
1167518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1177518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (qopt->defcls != 0)
1187518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		fprintf(f, "default %x ", qopt->defcls);
1197518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1207518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
121aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger}
122aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
1237518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
1247518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
125aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger{
1267518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct tc_hfsc_stats *st;
1277518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1287518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (xstats == NULL)
1297518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return 0;
1307518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (RTA_PAYLOAD(xstats) < sizeof(*st))
1317518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
1327518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	st = RTA_DATA(xstats);
1337518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1347518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(f, " period %u ", st->period);
1357518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (st->work != 0)
136b906243b62c832f24473de3ead7d7feef0e75e4bnet[shemminger]!shemminger		fprintf(f, "work %llu bytes ", (unsigned long long) st->work);
1377518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (st->rtwork != 0)
138b906243b62c832f24473de3ead7d7feef0e75e4bnet[shemminger]!shemminger		fprintf(f, "rtwork %llu bytes ", (unsigned long long) st->rtwork);
1397518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(f, "level %u ", st->level);
1407518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(f, "\n");
1417518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1427518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
1437518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
1447518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1457518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
1467518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
1477518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger                     struct nlmsghdr *n)
1487518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
1497518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct tc_service_curve rsc, fsc, usc;
1507518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	int rsc_ok, fsc_ok, usc_ok;
1517518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct rtattr *tail;
1527518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1537518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	memset(&rsc, 0, sizeof(rsc));
1547518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	memset(&fsc, 0, sizeof(fsc));
1557518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	memset(&usc, 0, sizeof(usc));
1567518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	rsc_ok = fsc_ok = usc_ok = 0;
1577518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
1587518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	while (argc > 0) {
1597518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (matches(*argv, "rt") == 0) {
1607518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			NEXT_ARG();
1617518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
1627518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				explain1("rt");
1637518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				return -1;
1647518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			}
1657518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			rsc_ok = 1;
1667518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		} else if (matches(*argv, "ls") == 0) {
1677518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			NEXT_ARG();
1687518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			if (hfsc_get_sc(&argc, &argv, &fsc) < 0) {
1697518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				explain1("ls");
1707518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				return -1;
1717518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			}
1727518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fsc_ok = 1;
173b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger		} else if (matches(*argv, "sc") == 0) {
174b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			NEXT_ARG();
175b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
176b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger				explain1("sc");
177b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger				return -1;
178b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			}
179b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			memcpy(&fsc, &rsc, sizeof(fsc));
180b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			rsc_ok = 1;
181b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			fsc_ok = 1;
1827518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		} else if (matches(*argv, "ul") == 0) {
1837518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			NEXT_ARG();
1847518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			if (hfsc_get_sc(&argc, &argv, &usc) < 0) {
1857518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				explain1("ul");
1867518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger				return -1;
1877518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			}
1887518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			usc_ok = 1;
1897518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		} else if (matches(*argv, "help") == 0) {
1907518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain_class();
1917518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
1927518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		} else {
1937518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv);
1947518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain_class();
1957518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
1967518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
1977518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		argc--, argv++;
1987518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
1997518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2007518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (!(rsc_ok || fsc_ok || usc_ok)) {
2017518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		fprintf(stderr, "HFSC: no parameters given\n");
2027518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		explain_class();
2037518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
2047518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
2057518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (usc_ok && !fsc_ok) {
2067518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		fprintf(stderr, "HFSC: Upper-limit Service Curve without "
2077518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		                "Link-Share Service Curve\n");
2087518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		explain_class();
2097518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
2107518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
2117518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2124a86fe19bc7f125d3eb40366277f87da4cda78ddn);	tail = NLMSG_TAIL(n);
2137518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2147518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
2157518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (rsc_ok)
2167518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		addattr_l(n, 1024, TCA_HFSC_RSC, &rsc, sizeof(rsc));
2177518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (fsc_ok)
2187518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		addattr_l(n, 1024, TCA_HFSC_FSC, &fsc, sizeof(fsc));
2197518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (usc_ok)
2207518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		addattr_l(n, 1024, TCA_HFSC_USC, &usc, sizeof(usc));
2217518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2224a86fe19bc7f125d3eb40366277f87da4cda78ddvoid *) NLMSG_TAIL(n) - (void *) tail;	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
2237518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
224aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger}
225aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
2267518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic void
2277518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc)
228aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger{
2297518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	SPRINT_BUF(b1);
2307518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2317518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(f, "%s ", name);
2327518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1));
2338f34caafbdbc8f87d228d577872591e138caadb1Patrick McHardy	fprintf(f, "d %s ", sprint_time(tc_core_ktime2time(sc->d), b1));
2347518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1));
235aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger}
236aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
2377518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
2387518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
2397518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
2407518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct rtattr *tb[TCA_HFSC_MAX+1];
2417518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
2427518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2437518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (opt == NULL)
2447518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return 0;
2457518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
246ac2fc2df5e117f6bf20d823f4bf1d9390ffbedf4tb, TCA_HFSC_MAX, opt);	parse_rtattr_nested(tb, TCA_HFSC_MAX, opt);
2477518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2487518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (tb[TCA_HFSC_RSC]) {
2497518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (RTA_PAYLOAD(tb[TCA_HFSC_RSC]) < sizeof(*rsc))
2507518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fprintf(stderr, "HFSC: truncated realtime option\n");
2517518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		else
2527518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			rsc = RTA_DATA(tb[TCA_HFSC_RSC]);
2537518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
2547518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (tb[TCA_HFSC_FSC]) {
2557518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (RTA_PAYLOAD(tb[TCA_HFSC_FSC]) < sizeof(*fsc))
2567518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fprintf(stderr, "HFSC: truncated linkshare option\n");
2577518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		else
2587518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fsc = RTA_DATA(tb[TCA_HFSC_FSC]);
2597518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
2607518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (tb[TCA_HFSC_USC]) {
2617518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (RTA_PAYLOAD(tb[TCA_HFSC_USC]) < sizeof(*usc))
2627518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			fprintf(stderr, "HFSC: truncated upperlimit option\n");
2637518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		else
2647518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			usc = RTA_DATA(tb[TCA_HFSC_USC]);
2657518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
2667518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
267ae665a522bd46bea44c5ea84c89c8b1731954170Stephen Hemminger
268b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger	if (rsc != NULL && fsc != NULL &&
269b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger	    memcmp(rsc, fsc, sizeof(*rsc)) == 0)
270b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger		hfsc_print_sc(f, "sc", rsc);
271b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger	else {
272b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger		if (rsc != NULL)
273b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			hfsc_print_sc(f, "rt", rsc);
274b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger		if (fsc != NULL)
275b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger			hfsc_print_sc(f, "ls", fsc);
276b7de67dad5255cb9942b0fbed9f1fda95ff916e5net[shemminger]!shemminger	}
2777518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (usc != NULL)
2787518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		hfsc_print_sc(f, "ul", usc);
2797518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2807518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
2817518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
282ae665a522bd46bea44c5ea84c89c8b1731954170Stephen Hemminger
28395812b56a5a66e7e9a21744cfe8bc0bb9791ea98net[shemminger]!kaberstruct qdisc_util hfsc_qdisc_util = {
284f2f99e2eefdbd9cb6a750b19a7b3036db351b983osdl.net!shemminger	.id		= "hfsc",
285f2f99e2eefdbd9cb6a750b19a7b3036db351b983osdl.net!shemminger	.parse_qopt	= hfsc_parse_opt,
286f2f99e2eefdbd9cb6a750b19a7b3036db351b983osdl.net!shemminger	.print_qopt	= hfsc_print_opt,
287f2f99e2eefdbd9cb6a750b19a7b3036db351b983osdl.net!shemminger	.print_xstats	= hfsc_print_xstats,
288f2f99e2eefdbd9cb6a750b19a7b3036db351b983osdl.net!shemminger	.parse_copt	= hfsc_parse_class_opt,
289f2f99e2eefdbd9cb6a750b19a7b3036db351b983osdl.net!shemminger	.print_copt	= hfsc_print_class_opt,
290aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger};
291aba5acdfdb347d2c21fc67d613d83d4430ca3937osdl.org!shemminger
2927518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
2937518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc)
2947518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
2957518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	char **argv = *argvp;
2967518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	int argc = *argcp;
2977518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	unsigned int m1 = 0, d = 0, m2 = 0;
2987518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
2997518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (matches(*argv, "m1") == 0) {
3007518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3017518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (get_rate(&m1, *argv) < 0) {
3027518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain1("m1");
3037518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
3047518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
3057518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3067518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
3077518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3087518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (matches(*argv, "d") == 0) {
3097518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3108f34caafbdbc8f87d228d577872591e138caadb1Patrick McHardy		if (get_time(&d, *argv) < 0) {
3117518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain1("d");
3127518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
3137518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
3147518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3157518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
3167518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3177518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (matches(*argv, "m2") == 0) {
3187518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3197518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (get_rate(&m2, *argv) < 0) {
3207518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain1("m2");
3217518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
3227518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
3237518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	} else
3247518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
3257518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3267518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	sc->m1 = m1;
327f0bda7e5a52d880a85af698a041afa7dc379e76ePatrick McHardy	sc->d  = tc_core_time2ktime(d);
3287518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	sc->m2 = m2;
3297518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3307518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	*argvp = argv;
3317518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	*argcp = argc;
3327518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
3337518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
3347518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3357518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
3367518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc)
3377518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
3387518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	char **argv = *argvp;
3397518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	int argc = *argcp;
3407518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	unsigned int umax = 0, dmax = 0, rate = 0;
3417518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3427518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (matches(*argv, "umax") == 0) {
3437518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3447518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (get_size(&umax, *argv) < 0) {
3457518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain1("umax");
3467518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
3477518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
3487518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3497518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
3507518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3517518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (matches(*argv, "dmax") == 0) {
3527518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3538f34caafbdbc8f87d228d577872591e138caadb1Patrick McHardy		if (get_time(&dmax, *argv) < 0) {
3547518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain1("dmax");
3557518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
3567518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
3577518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3587518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
3597518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3607518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (matches(*argv, "rate") == 0) {
3617518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		NEXT_ARG();
3627518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		if (get_rate(&rate, *argv) < 0) {
3637518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			explain1("rate");
3647518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger			return -1;
3657518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		}
3667518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	} else
3677518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
3687518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3697518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (umax != 0 && dmax == 0) {
3707518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		fprintf(stderr, "HFSC: umax given but dmax is zero.\n");
3717518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
3727518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
3737518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
374f0bda7e5a52d880a85af698a041afa7dc379e76ePatrick McHardy	if (dmax != 0 && ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax) > rate) {
3757518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		/*
3767518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		 * concave curve, slope of first segment is umax/dmax,
3777518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		 * intersection is at dmax
3787518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		 */
379f0bda7e5a52d880a85af698a041afa7dc379e76ePatrick McHardy		sc->m1 = ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax); /* in bps */
380f0bda7e5a52d880a85af698a041afa7dc379e76ePatrick McHardy		sc->d  = tc_core_time2ktime(dmax);
3817518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		sc->m2 = rate;
3827518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	} else {
3837518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		/*
3847518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		 * convex curve, slope of first segment is 0, intersection
3857518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		 * is at dmax - umax / rate
3867518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		 */
3877518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		sc->m1 = 0;
388f0bda7e5a52d880a85af698a041afa7dc379e76ePatrick McHardy		sc->d  = tc_core_time2ktime(ceil(dmax - umax * TIME_UNITS_PER_SEC / rate));
3897518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		sc->m2 = rate;
3907518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
3917518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3927518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	*argvp = argv;
3937518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	*argcp = argc;
3947518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
3957518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
3967518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
3977518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerstatic int
3987518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemmingerhfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc)
3997518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger{
4007518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (hfsc_get_sc1(argcp, argvp, sc) < 0 &&
4017518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	    hfsc_get_sc2(argcp, argvp, sc) < 0)
4027518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
4037518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
4047518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	if (sc->m1 == 0 && sc->m2 == 0) {
4057518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		fprintf(stderr, "HFSC: Service Curve has two zero slopes\n");
4067518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger		return -1;
4077518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	}
4087518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger
4097518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger	return 0;
4107518df00e72235d3d1f9e355033cb6f147b32435net[shemminger]!shemminger}
411