1 /* $KAME: dhcp6_ctl.c,v 1.4 2004/09/07 05:03:03 jinmei 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/param.h>
32 #include <sys/types.h>
33 #include <sys/queue.h>
34 #include <sys/socket.h>
35 #if TIME_WITH_SYS_TIME
36 # include <sys/time.h>
40 # include <sys/time.h>
46 #include <netinet/in.h>
62 #include <dhcp6_ctl.h>
64 TAILQ_HEAD(dhcp6_commandqueue
, dhcp6_commandctx
);
66 static struct dhcp6_commandqueue commandqueue_head
;
67 static int max_commands
;
68 static int commands
= 0;
70 struct dhcp6_commandctx
{
71 TAILQ_ENTRY(dhcp6_commandctx
) link
;
73 int s
; /* communication socket */
74 char inputbuf
[1024]; /* input buffer */
77 int (*callback
) __P((char *, ssize_t
));
81 dhcp6_ctl_init(addr
, port
, max
, sockp
)
85 struct addrinfo hints
, *res
= NULL
;
90 memset(&hints
, 0, sizeof(hints
));
91 hints
.ai_family
= AF_INET6
;
92 hints
.ai_socktype
= SOCK_STREAM
;
93 hints
.ai_protocol
= IPPROTO_TCP
;
94 error
= getaddrinfo(addr
, port
, &hints
, &res
);
96 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
100 ctlsock
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
102 dprintf(LOG_ERR
, FNAME
, "socket(control sock): %s",
107 if (setsockopt(ctlsock
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
))
109 dprintf(LOG_ERR
, FNAME
,
110 "setsockopt(control sock, SO_REUSEADDR: %s",
114 if (bind(ctlsock
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
115 dprintf(LOG_ERR
, FNAME
, "bind(control sock): %s",
120 if (listen(ctlsock
, 1)) {
121 dprintf(LOG_ERR
, FNAME
, "listen(control sock): %s",
126 TAILQ_INIT(&commandqueue_head
);
129 dprintf(LOG_ERR
, FNAME
,
130 "invalid maximum number of commands (%d)", max_commands
);
148 dhcp6_ctl_authinit(keyfile
, keyinfop
, digestlenp
)
150 struct keyinfo
**keyinfop
;
154 struct keyinfo
*ctlkey
= NULL
;
155 char line
[1024], secret
[1024];
158 /* Currently, we only support HMAC-MD5 for authentication. */
159 *digestlenp
= MD5_DIGESTLENGTH
;
161 if ((fp
= fopen(keyfile
, "r")) == NULL
) {
162 dprintf(LOG_ERR
, FNAME
, "failed to open %s: %s", keyfile
,
166 if (fgets(line
, sizeof(line
), fp
) == NULL
&& ferror(fp
)) {
167 dprintf(LOG_ERR
, FNAME
, "failed to read key file: %s",
171 if ((secretlen
= base64_decodestring(line
, secret
, sizeof(secret
)))
173 dprintf(LOG_ERR
, FNAME
, "failed to decode base64 string");
176 if ((ctlkey
= malloc(sizeof(*ctlkey
))) == NULL
) {
177 dprintf(LOG_WARNING
, FNAME
, "failed to allocate control key");
180 memset(ctlkey
, 0, sizeof(*ctlkey
));
181 if ((ctlkey
->secret
= malloc(secretlen
)) == NULL
) {
182 dprintf(LOG_WARNING
, FNAME
, "failed to allocate secret key");
185 ctlkey
->secretlen
= (size_t)secretlen
;
186 memcpy(ctlkey
->secret
, secret
, secretlen
);
196 if (ctlkey
!= NULL
&& ctlkey
->secret
!= NULL
)
197 free(ctlkey
->secret
);
205 dhcp6_ctl_acceptcommand(sl
, callback
)
207 int (*callback
) __P((char *, ssize_t
));
210 struct sockaddr_storage from_ss
;
211 struct sockaddr
*from
= (struct sockaddr
*)&from_ss
;
213 struct dhcp6_commandctx
*ctx
, *new;
215 fromlen
= sizeof(from_ss
);
216 if ((s
= accept(sl
, from
, &fromlen
)) < 0) {
217 dprintf(LOG_WARNING
, FNAME
,
218 "failed to accept control connection: %s",
223 dprintf(LOG_DEBUG
, FNAME
, "accept control connection from %s",
226 if (max_commands
<= 0) {
227 dprintf(LOG_ERR
, FNAME
, "command queue is not initialized");
232 new = malloc(sizeof(*new));
234 dprintf(LOG_WARNING
, FNAME
,
235 "failed to allocate new command context");
239 /* if the command queue is full, purge the oldest one */
240 if (commands
== max_commands
) {
241 ctx
= TAILQ_FIRST(&commandqueue_head
);
243 dprintf(LOG_INFO
, FNAME
, "command queue is full. "
244 "drop the oldest one (fd=%d)", ctx
->s
);
246 TAILQ_REMOVE(&commandqueue_head
, ctx
, link
);
247 dhcp6_ctl_closecommand(ctx
);
250 /* insert the next context to the queue */
251 memset(new, 0, sizeof(*new));
253 new->callback
= callback
;
254 new->input_len
= sizeof(struct dhcp6ctl
);
255 TAILQ_INSERT_TAIL(&commandqueue_head
, new, link
);
267 dhcp6_ctl_closecommand(ctx
)
268 struct dhcp6_commandctx
*ctx
;
274 dprintf(LOG_ERR
, FNAME
, "assumption error: "
275 "command queue is empty?");
284 dhcp6_ctl_readcommand(read_fds
)
287 struct dhcp6_commandctx
*ctx
, *ctx_next
;
289 int cc
, resid
, result
;
290 struct dhcp6ctl
*ctlhead
;
292 for (ctx
= TAILQ_FIRST(&commandqueue_head
); ctx
!= NULL
;
294 ctx_next
= TAILQ_NEXT(ctx
, link
);
296 if (FD_ISSET(ctx
->s
, read_fds
)) {
297 cp
= ctx
->inputbuf
+ ctx
->input_filled
;
298 resid
= ctx
->input_len
- ctx
->input_filled
;
300 cc
= read(ctx
->s
, cp
, resid
);
302 dprintf(LOG_WARNING
, FNAME
, "read failed: %s",
307 dprintf(LOG_INFO
, FNAME
,
308 "control channel was reset by peer");
312 ctx
->input_filled
+= cc
;
313 if (ctx
->input_filled
< ctx
->input_len
)
314 continue; /* we need more data */
315 else if (ctx
->input_filled
== sizeof(*ctlhead
)) {
316 ctlhead
= (struct dhcp6ctl
*)ctx
->inputbuf
;
317 ctx
->input_len
+= ntohs(ctlhead
->len
);
320 if (ctx
->input_filled
== ctx
->input_len
) {
321 /* we're done. execute the command. */
322 result
= (ctx
->callback
)(ctx
->inputbuf
,
326 case DHCP6CTL_R_DONE
:
327 case DHCP6CTL_R_FAILURE
:
332 } else if (ctx
->input_len
> sizeof(ctx
->inputbuf
)) {
333 dprintf(LOG_INFO
, FNAME
,
334 "too large command (%d bytes)",
342 TAILQ_REMOVE(&commandqueue_head
, ctx
, link
);
343 dhcp6_ctl_closecommand(ctx
);
351 dhcp6_ctl_setreadfds(read_fds
, maxfdp
)
356 struct dhcp6_commandctx
*ctx
;
358 for (ctx
= TAILQ_FIRST(&commandqueue_head
); ctx
!= NULL
;
359 ctx
= TAILQ_NEXT(ctx
, link
)) {
360 FD_SET(ctx
->s
, read_fds
);