Merge series "ASoC: Fix dependency issues of SND_SOC" from Wei Li <liwei391@huawei...
[linux-2.6-microblaze.git] / tools / testing / selftests / net / mptcp / pm_nl_ctl.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <errno.h>
4 #include <error.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9
10 #include <sys/socket.h>
11 #include <sys/types.h>
12
13 #include <arpa/inet.h>
14 #include <net/if.h>
15
16 #include <linux/rtnetlink.h>
17 #include <linux/genetlink.h>
18
19 #include "linux/mptcp.h"
20
21 #ifndef MPTCP_PM_NAME
22 #define MPTCP_PM_NAME           "mptcp_pm"
23 #endif
24
25 static void syntax(char *argv[])
26 {
27         fprintf(stderr, "%s add|get|del|flush|dump|accept [<args>]\n", argv[0]);
28         fprintf(stderr, "\tadd [flags signal|subflow|backup] [id <nr>] [dev <name>] <ip>\n");
29         fprintf(stderr, "\tdel <id>\n");
30         fprintf(stderr, "\tget <id>\n");
31         fprintf(stderr, "\tflush\n");
32         fprintf(stderr, "\tdump\n");
33         fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n");
34         exit(0);
35 }
36
37 static int init_genl_req(char *data, int family, int cmd, int version)
38 {
39         struct nlmsghdr *nh = (void *)data;
40         struct genlmsghdr *gh;
41         int off = 0;
42
43         nh->nlmsg_type = family;
44         nh->nlmsg_flags = NLM_F_REQUEST;
45         nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
46         off += NLMSG_ALIGN(sizeof(*nh));
47
48         gh = (void *)(data + off);
49         gh->cmd = cmd;
50         gh->version = version;
51         off += NLMSG_ALIGN(sizeof(*gh));
52         return off;
53 }
54
55 static void nl_error(struct nlmsghdr *nh)
56 {
57         struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh);
58         int len = nh->nlmsg_len - sizeof(*nh);
59         uint32_t off;
60
61         if (len < sizeof(struct nlmsgerr))
62                 error(1, 0, "netlink error message truncated %d min %ld", len,
63                       sizeof(struct nlmsgerr));
64
65         if (!err->error) {
66                 /* check messages from kernel */
67                 struct rtattr *attrs = (struct rtattr *)NLMSG_DATA(nh);
68
69                 while (RTA_OK(attrs, len)) {
70                         if (attrs->rta_type == NLMSGERR_ATTR_MSG)
71                                 fprintf(stderr, "netlink ext ack msg: %s\n",
72                                         (char *)RTA_DATA(attrs));
73                         if (attrs->rta_type == NLMSGERR_ATTR_OFFS) {
74                                 memcpy(&off, RTA_DATA(attrs), 4);
75                                 fprintf(stderr, "netlink err off %d\n",
76                                         (int)off);
77                         }
78                         attrs = RTA_NEXT(attrs, len);
79                 }
80         } else {
81                 fprintf(stderr, "netlink error %d", err->error);
82         }
83 }
84
85 /* do a netlink command and, if max > 0, fetch the reply  */
86 static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max)
87 {
88         struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
89         socklen_t addr_len;
90         void *data = nh;
91         int rem, ret;
92         int err = 0;
93
94         nh->nlmsg_len = len;
95         ret = sendto(fd, data, len, 0, (void *)&nladdr, sizeof(nladdr));
96         if (ret != len)
97                 error(1, errno, "send netlink: %uB != %uB\n", ret, len);
98         if (max == 0)
99                 return 0;
100
101         addr_len = sizeof(nladdr);
102         rem = ret = recvfrom(fd, data, max, 0, (void *)&nladdr, &addr_len);
103         if (ret < 0)
104                 error(1, errno, "recv netlink: %uB\n", ret);
105
106         /* Beware: the NLMSG_NEXT macro updates the 'rem' argument */
107         for (; NLMSG_OK(nh, rem); nh = NLMSG_NEXT(nh, rem)) {
108                 if (nh->nlmsg_type == NLMSG_ERROR) {
109                         nl_error(nh);
110                         err = 1;
111                 }
112         }
113         if (err)
114                 error(1, 0, "bailing out due to netlink error[s]");
115         return ret;
116 }
117
118 static int genl_parse_getfamily(struct nlmsghdr *nlh)
119 {
120         struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
121         int len = nlh->nlmsg_len;
122         struct rtattr *attrs;
123
124         if (nlh->nlmsg_type != GENL_ID_CTRL)
125                 error(1, errno, "Not a controller message, len=%d type=0x%x\n",
126                       nlh->nlmsg_len, nlh->nlmsg_type);
127
128         len -= NLMSG_LENGTH(GENL_HDRLEN);
129
130         if (len < 0)
131                 error(1, errno, "wrong controller message len %d\n", len);
132
133         if (ghdr->cmd != CTRL_CMD_NEWFAMILY)
134                 error(1, errno, "Unknown controller command %d\n", ghdr->cmd);
135
136         attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
137         while (RTA_OK(attrs, len)) {
138                 if (attrs->rta_type == CTRL_ATTR_FAMILY_ID)
139                         return *(__u16 *)RTA_DATA(attrs);
140                 attrs = RTA_NEXT(attrs, len);
141         }
142
143         error(1, errno, "can't find CTRL_ATTR_FAMILY_ID attr");
144         return -1;
145 }
146
147 static int resolve_mptcp_pm_netlink(int fd)
148 {
149         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
150                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
151                   1024];
152         struct nlmsghdr *nh;
153         struct rtattr *rta;
154         int namelen;
155         int off = 0;
156
157         memset(data, 0, sizeof(data));
158         nh = (void *)data;
159         off = init_genl_req(data, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 0);
160
161         rta = (void *)(data + off);
162         namelen = strlen(MPTCP_PM_NAME) + 1;
163         rta->rta_type = CTRL_ATTR_FAMILY_NAME;
164         rta->rta_len = RTA_LENGTH(namelen);
165         memcpy(RTA_DATA(rta), MPTCP_PM_NAME, namelen);
166         off += NLMSG_ALIGN(rta->rta_len);
167
168         do_nl_req(fd, nh, off, sizeof(data));
169         return genl_parse_getfamily((void *)data);
170 }
171
172 int add_addr(int fd, int pm_family, int argc, char *argv[])
173 {
174         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
175                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
176                   1024];
177         struct rtattr *rta, *nest;
178         struct nlmsghdr *nh;
179         u_int16_t family;
180         u_int32_t flags;
181         int nest_start;
182         u_int8_t id;
183         int off = 0;
184         int arg;
185
186         memset(data, 0, sizeof(data));
187         nh = (void *)data;
188         off = init_genl_req(data, pm_family, MPTCP_PM_CMD_ADD_ADDR,
189                             MPTCP_PM_VER);
190
191         if (argc < 3)
192                 syntax(argv);
193
194         nest_start = off;
195         nest = (void *)(data + off);
196         nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
197         nest->rta_len = RTA_LENGTH(0);
198         off += NLMSG_ALIGN(nest->rta_len);
199
200         /* addr data */
201         rta = (void *)(data + off);
202         if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) {
203                 family = AF_INET;
204                 rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
205                 rta->rta_len = RTA_LENGTH(4);
206         } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) {
207                 family = AF_INET6;
208                 rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
209                 rta->rta_len = RTA_LENGTH(16);
210         } else
211                 error(1, errno, "can't parse ip %s", argv[2]);
212         off += NLMSG_ALIGN(rta->rta_len);
213
214         /* family */
215         rta = (void *)(data + off);
216         rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
217         rta->rta_len = RTA_LENGTH(2);
218         memcpy(RTA_DATA(rta), &family, 2);
219         off += NLMSG_ALIGN(rta->rta_len);
220
221         for (arg = 3; arg < argc; arg++) {
222                 if (!strcmp(argv[arg], "flags")) {
223                         char *tok, *str;
224
225                         /* flags */
226                         flags = 0;
227                         if (++arg >= argc)
228                                 error(1, 0, " missing flags value");
229
230                         /* do not support flag list yet */
231                         for (str = argv[arg]; (tok = strtok(str, ","));
232                              str = NULL) {
233                                 if (!strcmp(tok, "subflow"))
234                                         flags |= MPTCP_PM_ADDR_FLAG_SUBFLOW;
235                                 else if (!strcmp(tok, "signal"))
236                                         flags |= MPTCP_PM_ADDR_FLAG_SIGNAL;
237                                 else if (!strcmp(tok, "backup"))
238                                         flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
239                                 else
240                                         error(1, errno,
241                                               "unknown flag %s", argv[arg]);
242                         }
243
244                         rta = (void *)(data + off);
245                         rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS;
246                         rta->rta_len = RTA_LENGTH(4);
247                         memcpy(RTA_DATA(rta), &flags, 4);
248                         off += NLMSG_ALIGN(rta->rta_len);
249                 } else if (!strcmp(argv[arg], "id")) {
250                         if (++arg >= argc)
251                                 error(1, 0, " missing id value");
252
253                         id = atoi(argv[arg]);
254                         rta = (void *)(data + off);
255                         rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
256                         rta->rta_len = RTA_LENGTH(1);
257                         memcpy(RTA_DATA(rta), &id, 1);
258                         off += NLMSG_ALIGN(rta->rta_len);
259                 } else if (!strcmp(argv[arg], "dev")) {
260                         int32_t ifindex;
261
262                         if (++arg >= argc)
263                                 error(1, 0, " missing dev name");
264
265                         ifindex = if_nametoindex(argv[arg]);
266                         if (!ifindex)
267                                 error(1, errno, "unknown device %s", argv[arg]);
268
269                         rta = (void *)(data + off);
270                         rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX;
271                         rta->rta_len = RTA_LENGTH(4);
272                         memcpy(RTA_DATA(rta), &ifindex, 4);
273                         off += NLMSG_ALIGN(rta->rta_len);
274                 } else
275                         error(1, 0, "unknown keyword %s", argv[arg]);
276         }
277         nest->rta_len = off - nest_start;
278
279         do_nl_req(fd, nh, off, 0);
280         return 0;
281 }
282
283 int del_addr(int fd, int pm_family, int argc, char *argv[])
284 {
285         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
286                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
287                   1024];
288         struct rtattr *rta, *nest;
289         struct nlmsghdr *nh;
290         int nest_start;
291         u_int8_t id;
292         int off = 0;
293
294         memset(data, 0, sizeof(data));
295         nh = (void *)data;
296         off = init_genl_req(data, pm_family, MPTCP_PM_CMD_DEL_ADDR,
297                             MPTCP_PM_VER);
298
299         /* the only argument is the address id */
300         if (argc != 3)
301                 syntax(argv);
302
303         id = atoi(argv[2]);
304
305         nest_start = off;
306         nest = (void *)(data + off);
307         nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
308         nest->rta_len =  RTA_LENGTH(0);
309         off += NLMSG_ALIGN(nest->rta_len);
310
311         /* build a dummy addr with only the ID set */
312         rta = (void *)(data + off);
313         rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
314         rta->rta_len = RTA_LENGTH(1);
315         memcpy(RTA_DATA(rta), &id, 1);
316         off += NLMSG_ALIGN(rta->rta_len);
317         nest->rta_len = off - nest_start;
318
319         do_nl_req(fd, nh, off, 0);
320         return 0;
321 }
322
323 static void print_addr(struct rtattr *attrs, int len)
324 {
325         uint16_t family = 0;
326         char str[1024];
327         uint32_t flags;
328         uint8_t id;
329
330         while (RTA_OK(attrs, len)) {
331                 if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FAMILY)
332                         memcpy(&family, RTA_DATA(attrs), 2);
333                 if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR4) {
334                         if (family != AF_INET)
335                                 error(1, errno, "wrong IP (v4) for family %d",
336                                       family);
337                         inet_ntop(AF_INET, RTA_DATA(attrs), str, sizeof(str));
338                         printf("%s", str);
339                 }
340                 if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR6) {
341                         if (family != AF_INET6)
342                                 error(1, errno, "wrong IP (v6) for family %d",
343                                       family);
344                         inet_ntop(AF_INET6, RTA_DATA(attrs), str, sizeof(str));
345                         printf("%s", str);
346                 }
347                 if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ID) {
348                         memcpy(&id, RTA_DATA(attrs), 1);
349                         printf("id %d ", id);
350                 }
351                 if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FLAGS) {
352                         memcpy(&flags, RTA_DATA(attrs), 4);
353
354                         printf("flags ");
355                         if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL) {
356                                 printf("signal");
357                                 flags &= ~MPTCP_PM_ADDR_FLAG_SIGNAL;
358                                 if (flags)
359                                         printf(",");
360                         }
361
362                         if (flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) {
363                                 printf("subflow");
364                                 flags &= ~MPTCP_PM_ADDR_FLAG_SUBFLOW;
365                                 if (flags)
366                                         printf(",");
367                         }
368
369                         if (flags & MPTCP_PM_ADDR_FLAG_BACKUP) {
370                                 printf("backup");
371                                 flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP;
372                                 if (flags)
373                                         printf(",");
374                         }
375
376                         /* bump unknown flags, if any */
377                         if (flags)
378                                 printf("0x%x", flags);
379                         printf(" ");
380                 }
381                 if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_IF_IDX) {
382                         char name[IF_NAMESIZE], *ret;
383                         int32_t ifindex;
384
385                         memcpy(&ifindex, RTA_DATA(attrs), 4);
386                         ret = if_indextoname(ifindex, name);
387                         if (ret)
388                                 printf("dev %s ", ret);
389                         else
390                                 printf("dev unknown/%d", ifindex);
391                 }
392
393                 attrs = RTA_NEXT(attrs, len);
394         }
395         printf("\n");
396 }
397
398 static void print_addrs(struct nlmsghdr *nh, int pm_family, int total_len)
399 {
400         struct rtattr *attrs;
401
402         for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) {
403                 int len = nh->nlmsg_len;
404
405                 if (nh->nlmsg_type == NLMSG_DONE)
406                         break;
407                 if (nh->nlmsg_type == NLMSG_ERROR)
408                         nl_error(nh);
409                 if (nh->nlmsg_type != pm_family)
410                         continue;
411
412                 len -= NLMSG_LENGTH(GENL_HDRLEN);
413                 attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) +
414                                            GENL_HDRLEN);
415                 while (RTA_OK(attrs, len)) {
416                         if (attrs->rta_type ==
417                             (MPTCP_PM_ATTR_ADDR | NLA_F_NESTED))
418                                 print_addr((void *)RTA_DATA(attrs),
419                                            attrs->rta_len);
420                         attrs = RTA_NEXT(attrs, len);
421                 }
422         }
423 }
424
425 int get_addr(int fd, int pm_family, int argc, char *argv[])
426 {
427         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
428                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
429                   1024];
430         struct rtattr *rta, *nest;
431         struct nlmsghdr *nh;
432         int nest_start;
433         u_int8_t id;
434         int off = 0;
435
436         memset(data, 0, sizeof(data));
437         nh = (void *)data;
438         off = init_genl_req(data, pm_family, MPTCP_PM_CMD_GET_ADDR,
439                             MPTCP_PM_VER);
440
441         /* the only argument is the address id */
442         if (argc != 3)
443                 syntax(argv);
444
445         id = atoi(argv[2]);
446
447         nest_start = off;
448         nest = (void *)(data + off);
449         nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
450         nest->rta_len =  RTA_LENGTH(0);
451         off += NLMSG_ALIGN(nest->rta_len);
452
453         /* build a dummy addr with only the ID set */
454         rta = (void *)(data + off);
455         rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
456         rta->rta_len = RTA_LENGTH(1);
457         memcpy(RTA_DATA(rta), &id, 1);
458         off += NLMSG_ALIGN(rta->rta_len);
459         nest->rta_len = off - nest_start;
460
461         print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data)));
462         return 0;
463 }
464
465 int dump_addrs(int fd, int pm_family, int argc, char *argv[])
466 {
467         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
468                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
469                   1024];
470         pid_t pid = getpid();
471         struct nlmsghdr *nh;
472         int off = 0;
473
474         memset(data, 0, sizeof(data));
475         nh = (void *)data;
476         off = init_genl_req(data, pm_family, MPTCP_PM_CMD_GET_ADDR,
477                             MPTCP_PM_VER);
478         nh->nlmsg_flags |= NLM_F_DUMP;
479         nh->nlmsg_seq = 1;
480         nh->nlmsg_pid = pid;
481         nh->nlmsg_len = off;
482
483         print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data)));
484         return 0;
485 }
486
487 int flush_addrs(int fd, int pm_family, int argc, char *argv[])
488 {
489         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
490                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
491                   1024];
492         struct nlmsghdr *nh;
493         int off = 0;
494
495         memset(data, 0, sizeof(data));
496         nh = (void *)data;
497         off = init_genl_req(data, pm_family, MPTCP_PM_CMD_FLUSH_ADDRS,
498                             MPTCP_PM_VER);
499
500         do_nl_req(fd, nh, off, 0);
501         return 0;
502 }
503
504 static void print_limits(struct nlmsghdr *nh, int pm_family, int total_len)
505 {
506         struct rtattr *attrs;
507         uint32_t max;
508
509         for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) {
510                 int len = nh->nlmsg_len;
511
512                 if (nh->nlmsg_type == NLMSG_DONE)
513                         break;
514                 if (nh->nlmsg_type == NLMSG_ERROR)
515                         nl_error(nh);
516                 if (nh->nlmsg_type != pm_family)
517                         continue;
518
519                 len -= NLMSG_LENGTH(GENL_HDRLEN);
520                 attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) +
521                                            GENL_HDRLEN);
522                 while (RTA_OK(attrs, len)) {
523                         int type = attrs->rta_type;
524
525                         if (type != MPTCP_PM_ATTR_RCV_ADD_ADDRS &&
526                             type != MPTCP_PM_ATTR_SUBFLOWS)
527                                 goto next;
528
529                         memcpy(&max, RTA_DATA(attrs), 4);
530                         printf("%s %u\n", type == MPTCP_PM_ATTR_SUBFLOWS ?
531                                           "subflows" : "accept", max);
532
533 next:
534                         attrs = RTA_NEXT(attrs, len);
535                 }
536         }
537 }
538
539 int get_set_limits(int fd, int pm_family, int argc, char *argv[])
540 {
541         char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
542                   NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
543                   1024];
544         uint32_t rcv_addr = 0, subflows = 0;
545         int cmd, len = sizeof(data);
546         struct nlmsghdr *nh;
547         int off = 0;
548
549         /* limit */
550         if (argc == 4) {
551                 rcv_addr = atoi(argv[2]);
552                 subflows = atoi(argv[3]);
553                 cmd = MPTCP_PM_CMD_SET_LIMITS;
554         } else {
555                 cmd = MPTCP_PM_CMD_GET_LIMITS;
556         }
557
558         memset(data, 0, sizeof(data));
559         nh = (void *)data;
560         off = init_genl_req(data, pm_family, cmd, MPTCP_PM_VER);
561
562         /* limit */
563         if (cmd == MPTCP_PM_CMD_SET_LIMITS) {
564                 struct rtattr *rta = (void *)(data + off);
565
566                 rta->rta_type = MPTCP_PM_ATTR_RCV_ADD_ADDRS;
567                 rta->rta_len = RTA_LENGTH(4);
568                 memcpy(RTA_DATA(rta), &rcv_addr, 4);
569                 off += NLMSG_ALIGN(rta->rta_len);
570
571                 rta = (void *)(data + off);
572                 rta->rta_type = MPTCP_PM_ATTR_SUBFLOWS;
573                 rta->rta_len = RTA_LENGTH(4);
574                 memcpy(RTA_DATA(rta), &subflows, 4);
575                 off += NLMSG_ALIGN(rta->rta_len);
576
577                 /* do not expect a reply */
578                 len = 0;
579         }
580
581         len = do_nl_req(fd, nh, off, len);
582         if (cmd == MPTCP_PM_CMD_GET_LIMITS)
583                 print_limits(nh, pm_family, len);
584         return 0;
585 }
586
587 int main(int argc, char *argv[])
588 {
589         int fd, pm_family;
590
591         if (argc < 2)
592                 syntax(argv);
593
594         fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
595         if (fd == -1)
596                 error(1, errno, "socket netlink");
597
598         pm_family = resolve_mptcp_pm_netlink(fd);
599
600         if (!strcmp(argv[1], "add"))
601                 return add_addr(fd, pm_family, argc, argv);
602         else if (!strcmp(argv[1], "del"))
603                 return del_addr(fd, pm_family, argc, argv);
604         else if (!strcmp(argv[1], "flush"))
605                 return flush_addrs(fd, pm_family, argc, argv);
606         else if (!strcmp(argv[1], "get"))
607                 return get_addr(fd, pm_family, argc, argv);
608         else if (!strcmp(argv[1], "dump"))
609                 return dump_addrs(fd, pm_family, argc, argv);
610         else if (!strcmp(argv[1], "limits"))
611                 return get_set_limits(fd, pm_family, argc, argv);
612
613         fprintf(stderr, "unknown sub-command: %s", argv[1]);
614         syntax(argv);
615         return 0;
616 }