1 /* $KAME: dhcp6_ctlclient.c,v 1.5 2005/01/12 06:06:11 suz Exp $ */
4 * Copyright (C) 2004 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #if TIME_WITH_SYS_TIME
34 # include <sys/time.h>
38 # include <sys/time.h>
44 #include <netinet/in.h>
57 #define MD5_DIGESTLENGTH 16
58 #define DEFAULT_SERVER_KEYFILE SYSCONFDIR "/dhcp6sctlkey"
59 #define DEFAULT_CLIENT_KEYFILE SYSCONFDIR "/dhcp6cctlkey"
64 static enum { CTLCLIENT
, CTLSERVER
} ctltype
= CTLCLIENT
;
66 static inline int put16
__P((char **, int *, u_int16_t
));
67 static inline int put32
__P((char **, int *, u_int32_t
));
68 static inline int putval
__P((char **, int *, void *, size_t));
70 static int setup_auth
__P((char *, struct keyinfo
*, int *));
71 static int make_command
__P((int, char **, char **, size_t *,
72 struct keyinfo
*, int));
73 static int make_remove_command
__P((int, char **, char **, int *));
74 static int make_start_command
__P((int, char **, char **, int *));
75 static int make_stop_command
__P((int, char **, char **, int *));
76 static int make_binding_object
__P((int, char **, char **, int *));
77 static int make_interface_object
__P((int, char **, char **, int *));
78 static int make_ia_object
__P((int, char **, char **, int *));
79 static int parse_duid
__P((char *, int *, char **, int *));
80 static void usage
__P((void));
87 int cc
, ch
, s
, error
, passed
;
88 int Cflag
= 0, Sflag
= 0;
91 struct addrinfo hints
, *res0
, *res
;
96 while ((ch
= getopt(argc
, argv
, "CSa:k:p:")) != -1) {
100 errx(1, "-C and -S are exclusive");
106 errx(1, "-C and -S are exclusive");
132 ctladdr
= DEFAULT_CLIENT_CONTROL_ADDR
;
134 ctlport
= DEFAULT_CLIENT_CONTROL_PORT
;
136 keyfile
= DEFAULT_CLIENT_KEYFILE
;
140 ctladdr
= DEFAULT_SERVER_CONTROL_ADDR
;
142 ctlport
= DEFAULT_SERVER_CONTROL_PORT
;
144 keyfile
= DEFAULT_SERVER_KEYFILE
;
148 memset(&key
, 0, sizeof(key
));
150 if (setup_auth(keyfile
, &key
, &digestlen
) != 0)
151 errx(1, "failed to setup message authentication");
153 if ((passed
= make_command(argc
, argv
, &cbuf
, &clen
,
154 &key
, digestlen
)) < 0) {
155 errx(1, "failed to make command buffer");
160 warnx("redundant command argument after \"%s\"", argv
[0]);
162 memset(&hints
, 0, sizeof(hints
));
163 hints
.ai_family
= AF_INET6
;
164 hints
.ai_socktype
= SOCK_STREAM
;
165 hints
.ai_protocol
= IPPROTO_TCP
;
166 error
= getaddrinfo(ctladdr
, ctlport
, &hints
, &res0
);
168 errx(1, "getaddrinfo failed: %s", gai_strerror(error
));
171 for (res
= res0
; res
!= NULL
; res
= res
->ai_next
) {
172 s
= socket(res
->ai_family
, res
->ai_socktype
,
178 if (connect(s
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
187 warnx("failed to connect to the %s",
188 ctltype
== CTLCLIENT
? "client" : "server");
192 cc
= write(s
, cbuf
, clen
);
194 err(1, "write command");
196 errx(1, "failed to send complete command");
205 setup_auth(keyfile
, key
, digestlenp
)
211 char line
[1024], secret
[1024];
216 /* Currently, we only support HMAC-MD5 for authentication. */
217 *digestlenp
= MD5_DIGESTLENGTH
;
219 if ((fp
= fopen(keyfile
, "r")) == NULL
) {
220 warn("fopen: %s", keyfile
);
223 if (fgets(line
, sizeof(line
), fp
) == NULL
&& ferror(fp
)) {
224 warn("fgets failed");
227 if ((secretlen
= base64_decodestring(line
, secret
, sizeof(secret
)))
229 warnx("failed to decode base64 string");
232 if ((key
->secret
= malloc(secretlen
)) == NULL
) {
233 warn("setup_auth: malloc failed");
236 key
->secretlen
= (size_t)secretlen
;
237 memcpy(key
->secret
, secret
, secretlen
);
246 if (key
->secret
!= NULL
)
252 put16(bpp
, lenp
, val
)
260 if (len
< sizeof(val
))
264 memcpy(bp
, &val
, sizeof(val
));
275 put32(bpp
, lenp
, val
)
283 if (len
< sizeof(val
))
287 memcpy(bp
, &val
, sizeof(val
));
298 putval(bpp
, lenp
, val
, valsize
)
310 memcpy(bp
, val
, valsize
);
321 make_command(argc
, argv
, bufp
, lenp
, key
, authlen
)
329 char commandbuf
[4096]; /* XXX: ad-hoc value */
330 char *bp
, *buf
, *mac
;
332 int argc_passed
= 0, passed
;
336 warnx("command is too short");
340 bp
= commandbuf
+ sizeof(ctl
) + authlen
;
341 if (bp
>= commandbuf
+ sizeof(commandbuf
)) {
342 warnx("make_command: local buffer is too short");
345 buflen
= sizeof(commandbuf
) - sizeof(ctl
);
347 memset(&ctl
, 0, sizeof(ctl
));
348 ctl
.version
= htons(DHCP6CTL_VERSION
);
350 if (strcmp(argv
[0], "reload") == 0)
351 ctl
.command
= htons(DHCP6CTL_COMMAND_RELOAD
);
352 else if (strcmp(argv
[0], "remove") == 0) {
353 if (ctltype
!= CTLSERVER
) {
354 warnx("remove command is only for server");
357 if ((passed
= make_remove_command(argc
- 1, argv
+ 1,
358 &bp
, &buflen
)) < 0) {
361 argc_passed
+= passed
;
362 ctl
.command
= htons(DHCP6CTL_COMMAND_REMOVE
);
363 } else if (strcmp(argv
[0], "start") == 0) {
364 if ((passed
= make_start_command(argc
- 1, argv
+ 1,
365 &bp
, &buflen
)) < 0) {
368 argc_passed
+= passed
;
369 ctl
.command
= htons(DHCP6CTL_COMMAND_START
);
370 } else if (strcmp(argv
[0], "stop") == 0) {
371 if ((passed
= make_stop_command(argc
- 1, argv
+ 1,
372 &bp
, &buflen
)) < 0) {
375 argc_passed
+= passed
;
376 ctl
.command
= htons(DHCP6CTL_COMMAND_STOP
);
378 warnx("unknown command: %s", argv
[0]);
382 len
= bp
- commandbuf
;
383 ctl
.len
= htons(len
- sizeof(ctl
));
385 if ((now
= time(NULL
)) < 0) {
386 warn("failed to get current time");
389 ctl
.timestamp
= htonl((u_int32_t
)now
);
391 memcpy(commandbuf
, &ctl
, sizeof(ctl
));
393 mac
= commandbuf
+ sizeof(ctl
);
394 memset(mac
, 0, authlen
);
395 if (dhcp6_calc_mac(commandbuf
, len
, DHCP6CTL_AUTHPROTO_UNDEF
,
396 DHCP6CTL_AUTHALG_HMACMD5
, sizeof(ctl
), key
) != 0) {
397 warnx("failed to calculate MAC");
401 if ((buf
= malloc(len
)) == NULL
) {
402 warn("memory allocation failed");
405 memcpy(buf
, commandbuf
, len
);
412 return (argc_passed
);
416 make_remove_command(argc
, argv
, bpp
, lenp
)
420 int argc_passed
= 0, passed
;
423 warnx("make_remove_command: command is too short");
427 if (strcmp(argv
[0], "binding") == 0) {
428 if (put32(bpp
, lenp
, DHCP6CTL_BINDING
))
430 if ((passed
= make_binding_object(argc
- 1, argv
+ 1,
434 argc_passed
+= passed
;
436 warnx("remove target not supported: %s", argv
[0]);
441 return (argc_passed
);
444 warnx("make_remove_command failed");
449 make_start_command(argc
, argv
, bpp
, lenp
)
453 int argc_passed
= 0, passed
;
456 warnx("make_remove_command: command is too short");
460 if (ctltype
!= CTLCLIENT
) {
461 warnx("client-only command is specified for a server");
465 if (strcmp(argv
[0], "interface") == 0) {
466 if (put32(bpp
, lenp
, DHCP6CTL_INTERFACE
))
468 if ((passed
= make_interface_object(argc
- 1, argv
+ 1,
472 argc_passed
+= passed
;
474 warnx("start target not supported: %s", argv
[0]);
479 return (argc_passed
);
482 warnx("make_start_command failed");
487 make_stop_command(argc
, argv
, bpp
, lenp
)
491 int argc_passed
= 0, passed
;
496 if (ctltype
!= CTLCLIENT
) {
497 warnx("client-only command is specified for a server");
501 if (strcmp(argv
[0], "interface") == 0) {
502 if (put32(bpp
, lenp
, DHCP6CTL_INTERFACE
))
504 if ((passed
= make_interface_object(argc
- 1, argv
+ 1,
508 argc_passed
+= passed
;
510 warnx("stop target not supported: %s", argv
[0]);
515 return (argc_passed
);
518 warnx("make_stop_command failed");
523 make_interface_object(argc
, argv
, bpp
, lenp
)
531 warnx("make_interface_object: interface not specified");
536 iflen
= strlen(argv
[0]) + 1;
537 if (put32(bpp
, lenp
, (u_int32_t
)iflen
))
539 if (putval(bpp
, lenp
, argv
[0], strlen(argv
[0]) + 1))
542 return (argc_passed
);
545 warnx("make_interface_object: failed");
550 make_binding_object(argc
, argv
, bpp
, lenp
)
554 int argc_passed
= 0, passed
;
557 /* or allow this as "all"? */
558 warnx("make_binding_object: command is too short");
562 if (strcmp(argv
[0], "IA") == 0) {
563 if (put32(bpp
, lenp
, DHCP6CTL_BINDING_IA
))
565 if ((passed
= make_ia_object(argc
- 1, argv
+ 1,
569 argc_passed
+= passed
;
571 warn("unknown binding type: %s", argv
[0]);
576 return (argc_passed
);
579 warnx("make_binding_object: failed");
584 make_ia_object(argc
, argv
, bpp
, lenp
)
588 struct dhcp6ctl_iaspec iaspec
;
589 int duidlen
, dummylen
= 0;
595 * Right now, we require all three parameters of
596 * <IA type, IAID, DUID>. This should be more flexible in
599 warnx("command is too short for an IA spec");
604 memset(&iaspec
, 0, sizeof(iaspec
));
606 if (strcmp(argv
[0], "IA_PD") == 0)
607 iaspec
.type
= htonl(DHCP6CTL_IA_PD
);
608 else if (strcmp(argv
[0], "IA_NA") == 0)
609 iaspec
.type
= htonl(DHCP6CTL_IA_NA
);
611 warnx("IA type not supported: %s", argv
[0]);
615 iaspec
.id
= htonl((u_int32_t
)strtol(argv
[1], NULL
, 10));
617 if (parse_duid(argv
[2], &duidlen
, &dummy
, &dummylen
))
619 iaspec
.duidlen
= htonl(duidlen
);
621 if (putval(bpp
, lenp
, &iaspec
, sizeof(iaspec
)))
624 if (parse_duid(argv
[2], &duidlen
, bpp
, lenp
))
627 return (argc_passed
);
630 warnx("make_ia_object: failed");
635 parse_duid(str
, lenp
, bufp
, buflenp
)
643 int duidlen
, slen
, buflen
;
646 /* calculate DUID len */
654 duidlen
+= (slen
/ 3);
656 warn("too long DUID (%d bytes)", duidlen
);
665 if (buflen
< duidlen
)
668 for (cp
= str
, bp
= buf
; *cp
!= '\0';) {
669 if (bp
- buf
>= buflen
)
672 if (sscanf(cp
, "%02x", &x
) != 1)
698 fprintf(stderr
, "usage: dhcp6ctl [-C|-S] [-a ctladdr] [-k keyfile] "
699 "[-p ctlport] command...\n");