1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2014 Cong Wang <[email protected]>
4 */
5
6 #include "nl-default.h"
7
8 #include <linux/pkt_sched.h>
9
10 #include <netlink/cli/utils.h>
11 #include <netlink/cli/tc.h>
12 #include <netlink/route/qdisc/hfsc.h>
13
print_qdisc_usage(void)14 static void print_qdisc_usage(void)
15 {
16 printf(
17 "Usage: nl-qdisc-add [...] hfsc [OPTIONS]...\n"
18 "\n"
19 "OPTIONS\n"
20 " --help Show this help text.\n"
21 " --default=ID Default class for unclassified traffic.\n"
22 "\n"
23 "EXAMPLE"
24 " # Create hfsc root qdisc 1: and direct unclassified traffic to class 1:10\n"
25 " nl-qdisc-add --dev=eth1 --parent=root --handle=1: hfsc --default=10\n");
26 }
27
hfsc_parse_qdisc_argv(struct rtnl_tc * tc,int argc,char ** argv)28 static void hfsc_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv)
29 {
30 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
31
32 for (;;) {
33 int c, optidx = 0;
34 enum {
35 ARG_DEFAULT = 257,
36 };
37 static struct option long_opts[] = {
38 { "help", 0, 0, 'h' },
39 { "default", 1, 0, ARG_DEFAULT },
40 { 0, 0, 0, 0 }
41 };
42
43 c = getopt_long(argc, argv, "hv", long_opts, &optidx);
44 if (c == -1)
45 break;
46
47 switch (c) {
48 case 'h':
49 print_qdisc_usage();
50 return;
51
52 case ARG_DEFAULT:
53 rtnl_qdisc_hfsc_set_defcls(qdisc, nl_cli_parse_u32(optarg));
54 break;
55 }
56 }
57 }
58
print_class_usage(void)59 static void print_class_usage(void)
60 {
61 printf(
62 "Usage: nl-class-add [...] hfsc [OPTIONS]...\n"
63 "\n"
64 "OPTIONS\n"
65 " --help Show this help text.\n"
66 " --ls=SC Link-sharing service curve\n"
67 " --rt=SC Real-time service curve\n"
68 " --sc=SC Specifiy both of the above\n"
69 " --ul=SC Upper limit\n"
70 " where SC := [ [ m1 bits ] d usec ] m2 bits\n"
71 "\n"
72 "EXAMPLE"
73 " # Attach class 1:1 to hfsc qdisc 1: and use rt and ls curve\n"
74 " nl-class-add --dev=eth1 --parent=1: --classid=1:1 hfsc --sc=m1:250,d:8,m2:100\n");
75 }
76
77 static int
hfsc_get_sc(char * optarg,struct tc_service_curve * sc)78 hfsc_get_sc(char *optarg, struct tc_service_curve *sc)
79 {
80 unsigned int m1 = 0, d = 0, m2 = 0;
81 char *tmp = strdup(optarg);
82 char *p, *endptr;
83 char *pp = tmp;
84
85 if (!tmp)
86 return -ENOMEM;
87
88 p = strstr(pp, "m1:");
89 if (p) {
90 char *q;
91 p += 3;
92 if (*p == 0)
93 goto err;
94 q = strchr(p, ',');
95 if (!q)
96 goto err;
97 *q = 0;
98 m1 = strtoul(p, &endptr, 10);
99 if (endptr == p)
100 goto err;
101 pp = q + 1;
102 }
103
104 p = strstr(pp, "d:");
105 if (p) {
106 char *q;
107 p += 2;
108 if (*p == 0)
109 goto err;
110 q = strchr(p, ',');
111 if (!q)
112 goto err;
113 *q = 0;
114 d = strtoul(p, &endptr, 10);
115 if (endptr == p)
116 goto err;
117 pp = q + 1;
118 }
119
120 p = strstr(pp, "m2:");
121 if (p) {
122 p += 3;
123 if (*p == 0)
124 goto err;
125 m2 = strtoul(p, &endptr, 10);
126 if (endptr == p)
127 goto err;
128 } else
129 goto err;
130
131 free(tmp);
132 sc->m1 = m1;
133 sc->d = d;
134 sc->m2 = m2;
135 return 0;
136
137 err:
138 free(tmp);
139 return -EINVAL;
140 }
141
hfsc_parse_class_argv(struct rtnl_tc * tc,int argc,char ** argv)142 static void hfsc_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv)
143 {
144 struct rtnl_class *class = (struct rtnl_class *) tc;
145 int arg_ok = 0, ret = -EINVAL;
146
147 for (;;) {
148 int c, optidx = 0;
149 enum {
150 ARG_RT = 257,
151 ARG_LS = 258,
152 ARG_SC,
153 ARG_UL,
154 };
155 static struct option long_opts[] = {
156 { "help", 0, 0, 'h' },
157 { "rt", 1, 0, ARG_RT },
158 { "ls", 1, 0, ARG_LS },
159 { "sc", 1, 0, ARG_SC },
160 { "ul", 1, 0, ARG_UL },
161 { 0, 0, 0, 0 }
162 };
163 struct tc_service_curve tsc;
164
165 c = getopt_long(argc, argv, "h", long_opts, &optidx);
166 if (c == -1)
167 break;
168
169 switch (c) {
170 case 'h':
171 print_class_usage();
172 return;
173
174 case ARG_RT:
175 ret = hfsc_get_sc(optarg, &tsc);
176 if (ret < 0) {
177 nl_cli_fatal(ret, "Unable to parse sc "
178 "\"%s\": Invalid format.", optarg);
179 }
180
181 rtnl_class_hfsc_set_rsc(class, &tsc);
182 arg_ok++;
183 break;
184
185 case ARG_LS:
186 ret = hfsc_get_sc(optarg, &tsc);
187 if (ret < 0) {
188 nl_cli_fatal(ret, "Unable to parse sc "
189 "\"%s\": Invalid format.", optarg);
190 }
191
192 rtnl_class_hfsc_set_fsc(class, &tsc);
193 arg_ok++;
194 break;
195
196 case ARG_SC:
197 ret = hfsc_get_sc(optarg, &tsc);
198 if (ret < 0) {
199 nl_cli_fatal(ret, "Unable to parse sc "
200 "\"%s\": Invalid format.", optarg);
201 }
202
203 rtnl_class_hfsc_set_rsc(class, &tsc);
204 rtnl_class_hfsc_set_fsc(class, &tsc);
205 arg_ok++;
206 break;
207
208 case ARG_UL:
209 ret = hfsc_get_sc(optarg, &tsc);
210 if (ret < 0) {
211 nl_cli_fatal(ret, "Unable to parse sc "
212 "\"%s\": Invalid format.", optarg);
213 }
214
215 rtnl_class_hfsc_set_usc(class, &tsc);
216 arg_ok++;
217 break;
218 }
219 }
220
221 if (!arg_ok)
222 nl_cli_fatal(ret, "Invalid arguments");
223 }
224
225 static struct nl_cli_tc_module hfsc_qdisc_module =
226 {
227 .tm_name = "hfsc",
228 .tm_type = RTNL_TC_TYPE_QDISC,
229 .tm_parse_argv = hfsc_parse_qdisc_argv,
230 };
231
232 static struct nl_cli_tc_module hfsc_class_module =
233 {
234 .tm_name = "hfsc",
235 .tm_type = RTNL_TC_TYPE_CLASS,
236 .tm_parse_argv = hfsc_parse_class_argv,
237 };
238
hfsc_init(void)239 static void _nl_init hfsc_init(void)
240 {
241 nl_cli_tc_register(&hfsc_qdisc_module);
242 nl_cli_tc_register(&hfsc_class_module);
243 }
244
hfsc_exit(void)245 static void _nl_exit hfsc_exit(void)
246 {
247 nl_cli_tc_unregister(&hfsc_class_module);
248 nl_cli_tc_unregister(&hfsc_qdisc_module);
249 }
250