]>
Commit | Line | Data |
---|---|---|
1 | /* $OpenBSD$ */ | |
2 | ||
3 | /* | |
4 | * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> | |
5 | * | |
6 | * Permission to use, copy, modify, and distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | */ | |
18 | ||
19 | #include <zebra.h> | |
20 | #include <sys/un.h> | |
21 | ||
22 | #include "ldpd.h" | |
23 | #include "ldpe.h" | |
24 | #include "log.h" | |
25 | #include "control.h" | |
26 | ||
27 | #define CONTROL_BACKLOG 5 | |
28 | ||
29 | static int control_accept(struct thread *); | |
30 | static struct ctl_conn *control_connbyfd(int); | |
31 | static struct ctl_conn *control_connbypid(pid_t); | |
32 | static void control_close(int); | |
33 | static int control_dispatch_imsg(struct thread *); | |
34 | ||
35 | struct ctl_conns ctl_conns; | |
36 | ||
37 | static int control_fd; | |
38 | ||
39 | int | |
40 | control_init(void) | |
41 | { | |
42 | struct sockaddr_un s_un; | |
43 | int fd; | |
44 | mode_t old_umask; | |
45 | ||
46 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { | |
47 | log_warn("%s: socket", __func__); | |
48 | return (-1); | |
49 | } | |
50 | sock_set_nonblock(fd); | |
51 | ||
52 | memset(&s_un, 0, sizeof(s_un)); | |
53 | s_un.sun_family = AF_UNIX; | |
54 | strlcpy(s_un.sun_path, ctl_sock_path, sizeof(s_un.sun_path)); | |
55 | ||
56 | if (unlink(ctl_sock_path) == -1) | |
57 | if (errno != ENOENT) { | |
58 | log_warn("%s: unlink %s", __func__, ctl_sock_path); | |
59 | close(fd); | |
60 | return (-1); | |
61 | } | |
62 | ||
63 | old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); | |
64 | if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { | |
65 | log_warn("%s: bind: %s", __func__, ctl_sock_path); | |
66 | close(fd); | |
67 | umask(old_umask); | |
68 | return (-1); | |
69 | } | |
70 | umask(old_umask); | |
71 | ||
72 | if (chmod(ctl_sock_path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { | |
73 | log_warn("%s: chmod", __func__); | |
74 | close(fd); | |
75 | (void)unlink(ctl_sock_path); | |
76 | return (-1); | |
77 | } | |
78 | ||
79 | control_fd = fd; | |
80 | ||
81 | return (0); | |
82 | } | |
83 | ||
84 | int | |
85 | control_listen(void) | |
86 | { | |
87 | if (listen(control_fd, CONTROL_BACKLOG) == -1) { | |
88 | log_warn("%s: listen", __func__); | |
89 | return (-1); | |
90 | } | |
91 | ||
92 | return (accept_add(control_fd, control_accept, NULL)); | |
93 | } | |
94 | ||
95 | void | |
96 | control_cleanup(void) | |
97 | { | |
98 | accept_del(control_fd); | |
99 | close(control_fd); | |
100 | unlink(ctl_sock_path); | |
101 | } | |
102 | ||
103 | /* ARGSUSED */ | |
104 | static int | |
105 | control_accept(struct thread *thread) | |
106 | { | |
107 | int connfd; | |
108 | socklen_t len; | |
109 | struct sockaddr_un s_un; | |
110 | struct ctl_conn *c; | |
111 | ||
112 | len = sizeof(s_un); | |
113 | if ((connfd = accept(THREAD_FD(thread), (struct sockaddr *)&s_un, | |
114 | &len)) == -1) { | |
115 | /* | |
116 | * Pause accept if we are out of file descriptors, or | |
117 | * libevent will haunt us here too. | |
118 | */ | |
119 | if (errno == ENFILE || errno == EMFILE) | |
120 | accept_pause(); | |
121 | else if (errno != EWOULDBLOCK && errno != EINTR && | |
122 | errno != ECONNABORTED) | |
123 | log_warn("%s: accept", __func__); | |
124 | return (0); | |
125 | } | |
126 | sock_set_nonblock(connfd); | |
127 | ||
128 | if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { | |
129 | log_warn(__func__); | |
130 | close(connfd); | |
131 | return (0); | |
132 | } | |
133 | ||
134 | imsg_init(&c->iev.ibuf, connfd); | |
135 | c->iev.handler_read = control_dispatch_imsg; | |
136 | c->iev.ev_read = thread_add_read(master, c->iev.handler_read, | |
137 | &c->iev, c->iev.ibuf.fd); | |
138 | c->iev.handler_write = ldp_write_handler; | |
139 | c->iev.ev_write = NULL; | |
140 | ||
141 | TAILQ_INSERT_TAIL(&ctl_conns, c, entry); | |
142 | ||
143 | return (0); | |
144 | } | |
145 | ||
146 | static struct ctl_conn * | |
147 | control_connbyfd(int fd) | |
148 | { | |
149 | struct ctl_conn *c; | |
150 | ||
151 | for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd; | |
152 | c = TAILQ_NEXT(c, entry)) | |
153 | ; /* nothing */ | |
154 | ||
155 | return (c); | |
156 | } | |
157 | ||
158 | static struct ctl_conn * | |
159 | control_connbypid(pid_t pid) | |
160 | { | |
161 | struct ctl_conn *c; | |
162 | ||
163 | for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.pid != pid; | |
164 | c = TAILQ_NEXT(c, entry)) | |
165 | ; /* nothing */ | |
166 | ||
167 | return (c); | |
168 | } | |
169 | ||
170 | static void | |
171 | control_close(int fd) | |
172 | { | |
173 | struct ctl_conn *c; | |
174 | ||
175 | if ((c = control_connbyfd(fd)) == NULL) { | |
176 | log_warnx("%s: fd %d: not found", __func__, fd); | |
177 | return; | |
178 | } | |
179 | ||
180 | msgbuf_clear(&c->iev.ibuf.w); | |
181 | TAILQ_REMOVE(&ctl_conns, c, entry); | |
182 | ||
183 | THREAD_READ_OFF(c->iev.ev_read); | |
184 | THREAD_WRITE_OFF(c->iev.ev_write); | |
185 | close(c->iev.ibuf.fd); | |
186 | accept_unpause(); | |
187 | free(c); | |
188 | } | |
189 | ||
190 | /* ARGSUSED */ | |
191 | static int | |
192 | control_dispatch_imsg(struct thread *thread) | |
193 | { | |
194 | int fd = THREAD_FD(thread); | |
195 | struct ctl_conn *c; | |
196 | struct imsg imsg; | |
197 | ssize_t n; | |
198 | unsigned int ifidx; | |
199 | ||
200 | if ((c = control_connbyfd(fd)) == NULL) { | |
201 | log_warnx("%s: fd %d: not found", __func__, fd); | |
202 | return (0); | |
203 | } | |
204 | ||
205 | c->iev.ev_read = NULL; | |
206 | ||
207 | if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || | |
208 | n == 0) { | |
209 | control_close(fd); | |
210 | return (0); | |
211 | } | |
212 | ||
213 | for (;;) { | |
214 | if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { | |
215 | control_close(fd); | |
216 | return (0); | |
217 | } | |
218 | ||
219 | if (n == 0) | |
220 | break; | |
221 | ||
222 | switch (imsg.hdr.type) { | |
223 | case IMSG_CTL_FIB_COUPLE: | |
224 | case IMSG_CTL_FIB_DECOUPLE: | |
225 | case IMSG_CTL_RELOAD: | |
226 | case IMSG_CTL_KROUTE: | |
227 | case IMSG_CTL_KROUTE_ADDR: | |
228 | case IMSG_CTL_IFINFO: | |
229 | /* ignore */ | |
230 | break; | |
231 | case IMSG_CTL_SHOW_INTERFACE: | |
232 | if (imsg.hdr.len == IMSG_HEADER_SIZE + | |
233 | sizeof(ifidx)) { | |
234 | memcpy(&ifidx, imsg.data, sizeof(ifidx)); | |
235 | ldpe_iface_ctl(c, ifidx); | |
236 | imsg_compose_event(&c->iev, IMSG_CTL_END, 0, | |
237 | 0, -1, NULL, 0); | |
238 | } | |
239 | break; | |
240 | case IMSG_CTL_SHOW_DISCOVERY: | |
241 | ldpe_adj_ctl(c); | |
242 | break; | |
243 | case IMSG_CTL_SHOW_LIB: | |
244 | case IMSG_CTL_SHOW_L2VPN_PW: | |
245 | case IMSG_CTL_SHOW_L2VPN_BINDING: | |
246 | c->iev.ibuf.pid = imsg.hdr.pid; | |
247 | ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid, | |
248 | imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); | |
249 | break; | |
250 | case IMSG_CTL_SHOW_NBR: | |
251 | ldpe_nbr_ctl(c); | |
252 | break; | |
253 | case IMSG_CTL_CLEAR_NBR: | |
254 | if (imsg.hdr.len != IMSG_HEADER_SIZE + | |
255 | sizeof(struct ctl_nbr)) | |
256 | break; | |
257 | ||
258 | nbr_clear_ctl(imsg.data); | |
259 | break; | |
260 | case IMSG_CTL_LOG_VERBOSE: | |
261 | /* ignore */ | |
262 | break; | |
263 | default: | |
264 | log_debug("%s: error handling imsg %d", __func__, | |
265 | imsg.hdr.type); | |
266 | break; | |
267 | } | |
268 | imsg_free(&imsg); | |
269 | } | |
270 | ||
271 | imsg_event_add(&c->iev); | |
272 | ||
273 | return (0); | |
274 | } | |
275 | ||
276 | int | |
277 | control_imsg_relay(struct imsg *imsg) | |
278 | { | |
279 | struct ctl_conn *c; | |
280 | ||
281 | if ((c = control_connbypid(imsg->hdr.pid)) == NULL) | |
282 | return (0); | |
283 | ||
284 | return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, | |
285 | -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); | |
286 | } |