]>
Commit | Line | Data |
---|---|---|
b5d9ee5c MM |
1 | /* |
2 | * BIRD Internet Routing Daemon -- Unix I/O | |
3 | * | |
b4b3b39e | 4 | * (c) 1998--1999 Martin Mares <mj@ucw.cz> |
b5d9ee5c MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
abae6e9c | 11 | #include <string.h> |
b5d9ee5c MM |
12 | #include <sys/time.h> |
13 | #include <sys/types.h> | |
14 | #include <sys/socket.h> | |
15 | #include <sys/fcntl.h> | |
16 | #include <unistd.h> | |
17 | #include <errno.h> | |
18 | ||
1b50a1e4 MM |
19 | #ifndef HAVE_STRUCT_IP_MREQN |
20 | #include <net/if.h> | |
21 | #endif | |
22 | ||
b5d9ee5c MM |
23 | #include "nest/bird.h" |
24 | #include "lib/lists.h" | |
25 | #include "lib/resource.h" | |
26 | #include "lib/timer.h" | |
27 | #include "lib/socket.h" | |
e8f73195 | 28 | #include "lib/event.h" |
b5d9ee5c MM |
29 | #include "nest/iface.h" |
30 | ||
31 | #include "lib/unix.h" | |
32 | ||
33 | /* | |
34 | * Timers | |
35 | */ | |
36 | ||
37 | #define NEAR_TIMER_LIMIT 4 | |
38 | ||
39 | #ifdef TIME_T_IS_64BIT | |
40 | #define TIME_INFINITY 0x7fffffffffffffff | |
41 | #else | |
42 | #ifdef TIME_T_IS_SIGNED | |
43 | #define TIME_INFINITY 0x7fffffff | |
44 | #else | |
45 | #define TIME_INFINITY 0xffffffff | |
46 | #endif | |
47 | #endif | |
48 | ||
49 | static list near_timers, far_timers; | |
50 | static bird_clock_t first_far_timer = TIME_INFINITY; | |
51 | ||
52 | bird_clock_t now; | |
53 | ||
54 | static void | |
55 | tm_free(resource *r) | |
56 | { | |
57 | timer *t = (timer *) r; | |
58 | ||
59 | tm_stop(t); | |
60 | } | |
61 | ||
62 | static void | |
63 | tm_dump(resource *r) | |
64 | { | |
65 | timer *t = (timer *) r; | |
66 | ||
e8f73195 | 67 | debug("(code %p, data %p, ", t->hook, t->data); |
af847acc MM |
68 | if (t->randomize) |
69 | debug("rand %d, ", t->randomize); | |
70 | if (t->recurrent) | |
71 | debug("recur %d, ", t->recurrent); | |
b5d9ee5c MM |
72 | if (t->expires) |
73 | debug("expires in %d sec)\n", t->expires - now); | |
74 | else | |
75 | debug("inactive)\n"); | |
76 | } | |
77 | ||
78 | static struct resclass tm_class = { | |
79 | "Timer", | |
80 | sizeof(timer), | |
81 | tm_free, | |
82 | tm_dump | |
83 | }; | |
84 | ||
85 | timer * | |
86 | tm_new(pool *p) | |
87 | { | |
88 | timer *t = ralloc(p, &tm_class); | |
89 | t->hook = NULL; | |
90 | t->data = NULL; | |
91 | t->randomize = 0; | |
92 | t->expires = 0; | |
93 | return t; | |
94 | } | |
95 | ||
96 | static inline void | |
97 | tm_insert_near(timer *t) | |
98 | { | |
99 | node *n = HEAD(near_timers); | |
100 | ||
101 | while (n->next && (SKIP_BACK(timer, n, n)->expires < t->expires)) | |
102 | n = n->next; | |
103 | insert_node(&t->n, n->prev); | |
104 | } | |
105 | ||
106 | void | |
107 | tm_start(timer *t, unsigned after) | |
108 | { | |
109 | bird_clock_t when; | |
110 | ||
111 | if (t->randomize) | |
af847acc | 112 | after += random() % (t->randomize + 1); |
b5d9ee5c MM |
113 | when = now + after; |
114 | if (t->expires == when) | |
115 | return; | |
116 | if (t->expires) | |
117 | rem_node(&t->n); | |
118 | t->expires = when; | |
119 | if (after <= NEAR_TIMER_LIMIT) | |
120 | tm_insert_near(t); | |
121 | else | |
122 | { | |
123 | if (!first_far_timer || first_far_timer > when) | |
124 | first_far_timer = when; | |
125 | add_tail(&far_timers, &t->n); | |
126 | } | |
127 | } | |
128 | ||
129 | void | |
130 | tm_stop(timer *t) | |
131 | { | |
132 | if (t->expires) | |
133 | { | |
134 | rem_node(&t->n); | |
135 | t->expires = 0; | |
136 | } | |
137 | } | |
138 | ||
139 | static void | |
140 | tm_dump_them(char *name, list *l) | |
141 | { | |
142 | node *n; | |
143 | timer *t; | |
144 | ||
145 | debug("%s timers:\n", name); | |
146 | WALK_LIST(n, *l) | |
147 | { | |
148 | t = SKIP_BACK(timer, n, n); | |
149 | debug("%p ", t); | |
150 | tm_dump(&t->r); | |
151 | } | |
152 | debug("\n"); | |
153 | } | |
154 | ||
155 | void | |
156 | tm_dump_all(void) | |
157 | { | |
158 | tm_dump_them("Near", &near_timers); | |
159 | tm_dump_them("Far", &far_timers); | |
160 | } | |
161 | ||
162 | static inline time_t | |
163 | tm_first_shot(void) | |
164 | { | |
165 | time_t x = first_far_timer; | |
166 | ||
167 | if (!EMPTY_LIST(near_timers)) | |
168 | { | |
169 | timer *t = SKIP_BACK(timer, n, HEAD(near_timers)); | |
170 | if (t->expires < x) | |
171 | x = t->expires; | |
172 | } | |
173 | return x; | |
174 | } | |
175 | ||
176 | static void | |
177 | tm_shot(void) | |
178 | { | |
179 | timer *t; | |
180 | node *n, *m; | |
181 | ||
182 | if (first_far_timer <= now) | |
183 | { | |
28a9a189 | 184 | bird_clock_t limit = now + NEAR_TIMER_LIMIT; |
b5d9ee5c MM |
185 | first_far_timer = TIME_INFINITY; |
186 | n = HEAD(far_timers); | |
187 | while (m = n->next) | |
188 | { | |
189 | t = SKIP_BACK(timer, n, n); | |
190 | if (t->expires <= limit) | |
191 | { | |
192 | rem_node(n); | |
193 | tm_insert_near(t); | |
194 | } | |
195 | else if (t->expires < first_far_timer) | |
196 | first_far_timer = t->expires; | |
197 | n = m; | |
198 | } | |
199 | } | |
200 | while ((n = HEAD(near_timers)) -> next) | |
201 | { | |
af847acc | 202 | int delay; |
b5d9ee5c MM |
203 | t = SKIP_BACK(timer, n, n); |
204 | if (t->expires > now) | |
205 | break; | |
206 | rem_node(n); | |
af847acc | 207 | delay = t->expires - now; |
b5d9ee5c | 208 | t->expires = 0; |
af847acc MM |
209 | if (t->recurrent) |
210 | { | |
211 | int i = t->recurrent - delay; | |
212 | if (i < 0) | |
213 | i = 0; | |
214 | tm_start(t, i); | |
215 | } | |
b5d9ee5c MM |
216 | t->hook(t); |
217 | } | |
218 | } | |
219 | ||
220 | /* | |
221 | * Sockets | |
222 | */ | |
223 | ||
abae6e9c MM |
224 | #ifndef SOL_IP |
225 | #define SOL_IP IPPROTO_IP | |
226 | #endif | |
227 | ||
b5d9ee5c MM |
228 | static list sock_list; |
229 | ||
230 | static void | |
231 | sk_free(resource *r) | |
232 | { | |
233 | sock *s = (sock *) r; | |
234 | ||
235 | if (s->fd >= 0) | |
236 | rem_node(&s->n); | |
237 | } | |
238 | ||
239 | static void | |
240 | sk_dump(resource *r) | |
241 | { | |
242 | sock *s = (sock *) r; | |
b4b3b39e | 243 | static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", "UDP/MC", "IP", "IP/MC", "MAGIC" }; |
b5d9ee5c MM |
244 | |
245 | debug("(%s, ud=%p, sa=%08x, sp=%d, da=%08x, dp=%d, tos=%d, ttl=%d, if=%s)\n", | |
246 | sk_type_names[s->type], | |
247 | s->data, | |
248 | s->saddr, | |
249 | s->sport, | |
250 | s->daddr, | |
251 | s->dport, | |
252 | s->tos, | |
253 | s->ttl, | |
254 | s->iface ? s->iface->name : "none"); | |
255 | } | |
256 | ||
257 | static struct resclass sk_class = { | |
258 | "Socket", | |
259 | sizeof(sock), | |
260 | sk_free, | |
261 | sk_dump | |
262 | }; | |
263 | ||
264 | sock * | |
265 | sk_new(pool *p) | |
266 | { | |
267 | sock *s = ralloc(p, &sk_class); | |
268 | s->pool = p; | |
269 | s->data = NULL; | |
270 | s->saddr = s->daddr = IPA_NONE; | |
271 | s->sport = s->dport = 0; | |
272 | s->tos = s->ttl = -1; | |
273 | s->iface = NULL; | |
274 | s->rbuf = NULL; | |
275 | s->rx_hook = NULL; | |
276 | s->rbsize = 0; | |
277 | s->tbuf = NULL; | |
278 | s->tx_hook = NULL; | |
279 | s->tbsize = 0; | |
280 | s->err_hook = NULL; | |
281 | s->fd = -1; | |
282 | return s; | |
283 | } | |
284 | ||
285 | #define ERR(x) do { err = x; goto bad; } while(0) | |
286 | ||
287 | static inline void | |
288 | set_inaddr(struct in_addr *ia, ip_addr a) | |
289 | { | |
290 | a = ipa_hton(a); | |
291 | memcpy(&ia->s_addr, &a, sizeof(a)); | |
292 | } | |
293 | ||
4cf45766 | 294 | void |
b5d9ee5c MM |
295 | fill_in_sockaddr(struct sockaddr_in *sa, ip_addr a, unsigned port) |
296 | { | |
297 | sa->sin_family = AF_INET; | |
298 | sa->sin_port = htons(port); | |
299 | set_inaddr(&sa->sin_addr, a); | |
300 | } | |
301 | ||
af847acc | 302 | void |
b5d9ee5c MM |
303 | get_sockaddr(struct sockaddr_in *sa, ip_addr *a, unsigned *port) |
304 | { | |
af847acc | 305 | if (sa->sin_family != AF_INET) |
08c69a77 | 306 | bug("get_sockaddr called for wrong address family"); |
af847acc MM |
307 | if (port) |
308 | *port = ntohs(sa->sin_port); | |
b5d9ee5c MM |
309 | memcpy(a, &sa->sin_addr.s_addr, sizeof(*a)); |
310 | *a = ipa_ntoh(*a); | |
311 | } | |
312 | ||
313 | static char * | |
314 | sk_setup(sock *s) | |
315 | { | |
316 | int fd = s->fd; | |
317 | int one = 1; | |
318 | char *err; | |
319 | ||
320 | if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) | |
321 | ERR("fcntl(O_NONBLOCK)"); | |
322 | if ((s->tos >= 0) && setsockopt(fd, SOL_IP, IP_TOS, &s->tos, sizeof(s->tos)) < 0) | |
323 | ERR("IP_TOS"); | |
324 | if (s->ttl >= 0) | |
325 | { | |
326 | if (setsockopt(fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0) | |
327 | ERR("IP_TTL"); | |
328 | if (setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0) | |
329 | ERR("SO_DONTROUTE"); | |
330 | } | |
331 | #ifdef IP_PMTUDISC | |
b4b3b39e | 332 | if (s->type != SK_TCP_PASSIVE && s->type != SK_TCP_ACTIVE && s->type != SK_MAGIC) |
b5d9ee5c MM |
333 | { |
334 | int dont = IP_PMTUDISC_DONT; | |
335 | if (setsockopt(fd, SOL_IP, IP_PMTUDISC, &dont, sizeof(dont)) < 0) | |
336 | ERR("IP_PMTUDISC"); | |
337 | } | |
338 | #endif | |
339 | /* FIXME: Set send/receive buffers? */ | |
340 | /* FIXME: Set keepalive for TCP connections? */ | |
341 | err = NULL; | |
342 | bad: | |
343 | return err; | |
344 | } | |
345 | ||
346 | static void | |
347 | sk_alloc_bufs(sock *s) | |
348 | { | |
349 | if (!s->rbuf && s->rbsize) | |
350 | s->rbuf = mb_alloc(s->pool, s->rbsize); | |
351 | s->rpos = s->rbuf; | |
352 | if (!s->tbuf && s->tbsize) | |
353 | s->tbuf = mb_alloc(s->pool, s->tbsize); | |
354 | s->tpos = s->ttx = s->tbuf; | |
355 | } | |
356 | ||
357 | void | |
358 | sk_tcp_connected(sock *s) | |
359 | { | |
360 | s->rx_hook(s, 0); | |
361 | s->type = SK_TCP; | |
362 | sk_alloc_bufs(s); | |
363 | } | |
364 | ||
365 | int | |
366 | sk_open(sock *s) | |
367 | { | |
368 | int fd, e; | |
369 | struct sockaddr_in sa; | |
370 | int zero = 0; | |
371 | int one = 1; | |
372 | int type = s->type; | |
373 | int has_src = ipa_nonzero(s->saddr) || s->sport; | |
374 | int has_dest = ipa_nonzero(s->daddr); | |
375 | char *err; | |
376 | ||
377 | switch (type) | |
378 | { | |
379 | case SK_TCP_ACTIVE: | |
380 | case SK_TCP_PASSIVE: | |
381 | fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | |
382 | break; | |
383 | case SK_UDP: | |
384 | case SK_UDP_MC: | |
385 | fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
386 | break; | |
387 | case SK_IP: | |
388 | case SK_IP_MC: | |
389 | fd = socket(PF_INET, SOCK_RAW, s->dport); | |
390 | break; | |
b4b3b39e MM |
391 | case SK_MAGIC: |
392 | fd = s->fd; | |
393 | break; | |
b5d9ee5c | 394 | default: |
b4b3b39e | 395 | bug("sk_open() called for invalid sock type %d", type); |
b5d9ee5c MM |
396 | } |
397 | if (fd < 0) | |
398 | die("sk_open: socket: %m"); | |
399 | s->fd = fd; | |
400 | ||
401 | if (err = sk_setup(s)) | |
402 | goto bad; | |
403 | switch (type) | |
404 | { | |
405 | case SK_UDP: | |
406 | case SK_IP: | |
407 | if (s->iface) /* It's a broadcast socket */ | |
408 | if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) | |
409 | ERR("SO_BROADCAST"); | |
410 | break; | |
411 | case SK_UDP_MC: | |
412 | case SK_IP_MC: | |
413 | { | |
61fb537c | 414 | #ifdef HAVE_STRUCT_IP_MREQN |
b5d9ee5c | 415 | struct ip_mreqn mreq; |
af847acc | 416 | #define mreq_add mreq |
9a158361 | 417 | ASSERT(s->iface && s->iface->addr); |
b5d9ee5c | 418 | mreq.imr_ifindex = s->iface->index; |
9a158361 | 419 | set_inaddr(&mreq.imr_address, s->iface->addr->ip); |
1b50a1e4 MM |
420 | #else |
421 | struct in_addr mreq; | |
422 | struct ip_mreq mreq_add; | |
9a158361 MM |
423 | ASSERT(s->iface && s->iface->addr); |
424 | set_inaddr(&mreq, s->iface->addr->ip); | |
36154beb MM |
425 | #ifdef SO_BINDTODEVICE |
426 | { | |
427 | struct ifreq ifr; | |
428 | strcpy(ifr.ifr_name, s->iface->name); | |
429 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) | |
430 | ERR("SO_BINDTODEVICE"); | |
4c5e5e3a | 431 | mreq_add.imr_interface.s_addr = INADDR_ANY; |
36154beb MM |
432 | } |
433 | #else | |
434 | #error Multicasts not supported on PtP devices /* FIXME: Solve it somehow? */ | |
c7208da0 | 435 | mreq_add.imr_interface = mreq; |
bd7f1081 | 436 | #endif |
b5d9ee5c | 437 | #endif |
af847acc | 438 | set_inaddr(&mreq_add.imr_multiaddr, s->daddr); |
b5d9ee5c MM |
439 | if (has_dest) |
440 | { | |
441 | if ( | |
442 | #ifdef IP_DEFAULT_MULTICAST_TTL | |
443 | s->ttl != IP_DEFAULT_MULTICAST_TTL && | |
444 | #endif | |
445 | setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0) | |
446 | ERR("IP_MULTICAST_TTL"); | |
447 | if ( | |
448 | #ifdef IP_DEFAULT_MULTICAST_LOOP | |
449 | IP_DEFAULT_MULTICAST_LOOP && | |
450 | #endif | |
451 | setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) | |
452 | ERR("IP_MULTICAST_LOOP"); | |
5a99ade4 | 453 | /* This defines where should we send _outgoing_ multicasts */ |
b5d9ee5c MM |
454 | if (setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) |
455 | ERR("IP_MULTICAST_IF"); | |
456 | } | |
5a99ade4 | 457 | /* And this one sets interface for _receiving_ multicasts from */ |
af847acc | 458 | if (has_src && setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq_add, sizeof(mreq_add)) < 0) |
b5d9ee5c MM |
459 | ERR("IP_ADD_MEMBERSHIP"); |
460 | break; | |
461 | } | |
462 | } | |
463 | if (has_src) | |
464 | { | |
465 | int port; | |
466 | ||
467 | if (type == SK_IP || type == SK_IP_MC) | |
468 | port = 0; | |
469 | else | |
470 | { | |
471 | port = s->sport; | |
472 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) | |
473 | ERR("SO_REUSEADDR"); | |
474 | } | |
475 | fill_in_sockaddr(&sa, s->saddr, port); | |
476 | if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) | |
477 | ERR("bind"); | |
478 | } | |
479 | fill_in_sockaddr(&sa, s->daddr, s->dport); | |
480 | switch (type) | |
481 | { | |
482 | case SK_TCP_ACTIVE: | |
483 | if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0) | |
484 | sk_tcp_connected(s); | |
485 | else if (errno != EINTR && errno != EAGAIN) | |
486 | ERR("connect"); | |
487 | break; | |
488 | case SK_TCP_PASSIVE: | |
489 | if (listen(fd, 8)) | |
490 | ERR("listen"); | |
491 | break; | |
492 | } | |
493 | ||
494 | sk_alloc_bufs(s); | |
495 | add_tail(&sock_list, &s->n); | |
496 | return 0; | |
497 | ||
498 | bad: | |
499 | log(L_ERR "sk_open: %s: %m", err); | |
500 | close(fd); | |
501 | s->fd = -1; | |
502 | return -1; | |
503 | } | |
504 | ||
505 | static int | |
506 | sk_maybe_write(sock *s) | |
507 | { | |
508 | int e; | |
509 | ||
510 | switch (s->type) | |
511 | { | |
512 | case SK_TCP: | |
b4b3b39e | 513 | case SK_MAGIC: |
b5d9ee5c MM |
514 | while (s->ttx != s->tpos) |
515 | { | |
516 | e = write(s->fd, s->ttx, s->tpos - s->ttx); | |
517 | if (e < 0) | |
518 | { | |
519 | if (errno != EINTR && errno != EAGAIN) | |
520 | { | |
521 | log(L_ERR "write: %m"); | |
522 | s->err_hook(s, errno); | |
523 | return -1; | |
524 | } | |
525 | return 0; | |
526 | } | |
527 | s->ttx += e; | |
528 | } | |
529 | s->ttx = s->tpos = s->tbuf; | |
530 | return 1; | |
531 | case SK_UDP: | |
532 | case SK_UDP_MC: | |
533 | case SK_IP: | |
534 | case SK_IP_MC: | |
535 | { | |
536 | struct sockaddr_in sa; | |
537 | ||
538 | if (s->tbuf == s->tpos) | |
539 | return 1; | |
540 | fill_in_sockaddr(&sa, s->faddr, s->fport); | |
541 | e = sendto(s->fd, s->tbuf, s->tpos - s->tbuf, 0, (struct sockaddr *) &sa, sizeof(sa)); | |
542 | if (e < 0) | |
543 | { | |
544 | if (errno != EINTR && errno != EAGAIN) | |
545 | { | |
546 | log(L_ERR "sendto: %m"); | |
547 | s->err_hook(s, errno); | |
548 | return -1; | |
549 | } | |
550 | return 0; | |
551 | } | |
552 | s->tpos = s->tbuf; | |
553 | return 1; | |
554 | } | |
555 | default: | |
08c69a77 | 556 | bug("sk_maybe_write: unknown socket type %d", s->type); |
b5d9ee5c MM |
557 | } |
558 | } | |
559 | ||
560 | int | |
561 | sk_send(sock *s, unsigned len) | |
562 | { | |
563 | s->faddr = s->daddr; | |
564 | s->fport = s->dport; | |
565 | s->ttx = s->tbuf; | |
566 | s->tpos = s->tbuf + len; | |
567 | return sk_maybe_write(s); | |
568 | } | |
569 | ||
570 | int | |
571 | sk_send_to(sock *s, unsigned len, ip_addr addr, unsigned port) | |
572 | { | |
573 | s->faddr = addr; | |
574 | s->fport = port; | |
575 | s->ttx = s->tbuf; | |
576 | s->tpos = s->tbuf + len; | |
577 | return sk_maybe_write(s); | |
578 | } | |
579 | ||
580 | static int | |
581 | sk_read(sock *s) | |
582 | { | |
583 | switch (s->type) | |
584 | { | |
585 | case SK_TCP_ACTIVE: | |
586 | { | |
587 | struct sockaddr_in sa; | |
588 | fill_in_sockaddr(&sa, s->daddr, s->dport); | |
589 | if (connect(s->fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0) | |
590 | sk_tcp_connected(s); | |
591 | else if (errno != EINTR && errno != EAGAIN) | |
592 | { | |
593 | log(L_ERR "connect: %m"); | |
594 | s->err_hook(s, errno); | |
595 | } | |
596 | return 0; | |
597 | } | |
598 | case SK_TCP_PASSIVE: | |
599 | { | |
600 | struct sockaddr_in sa; | |
601 | int al = sizeof(sa); | |
602 | int fd = accept(s->fd, (struct sockaddr *) &sa, &al); | |
603 | if (fd >= 0) | |
604 | { | |
605 | sock *t = sk_new(s->pool); | |
606 | char *err; | |
607 | t->type = SK_TCP; | |
608 | t->fd = fd; | |
609 | add_tail(&sock_list, &t->n); | |
610 | s->rx_hook(t, 0); | |
611 | if (err = sk_setup(t)) | |
612 | { | |
613 | log(L_ERR "Incoming connection: %s: %m", err); | |
614 | s->err_hook(s, errno); | |
615 | return 0; | |
616 | } | |
617 | sk_alloc_bufs(t); | |
618 | return 1; | |
619 | } | |
620 | else if (errno != EINTR && errno != EAGAIN) | |
621 | { | |
622 | log(L_ERR "accept: %m"); | |
623 | s->err_hook(s, errno); | |
624 | } | |
625 | return 0; | |
626 | } | |
627 | case SK_TCP: | |
628 | { | |
629 | int c = read(s->fd, s->rpos, s->rbuf + s->rbsize - s->rpos); | |
630 | ||
631 | if (c < 0) | |
632 | { | |
633 | if (errno != EINTR && errno != EAGAIN) | |
634 | { | |
635 | log(L_ERR "read: %m"); | |
636 | s->err_hook(s, errno); | |
637 | } | |
638 | } | |
639 | else if (!c) | |
640 | s->err_hook(s, 0); | |
641 | else | |
642 | { | |
643 | s->rpos += c; | |
644 | if (s->rx_hook(s, s->rpos - s->rbuf)) | |
645 | s->rpos = s->rbuf; | |
646 | return 1; | |
647 | } | |
648 | return 0; | |
649 | } | |
b4b3b39e MM |
650 | case SK_MAGIC: |
651 | return s->rx_hook(s, 0); | |
b5d9ee5c MM |
652 | default: |
653 | { | |
654 | struct sockaddr_in sa; | |
655 | int al = sizeof(sa); | |
656 | int e = recvfrom(s->fd, s->rbuf, s->rbsize, 0, (struct sockaddr *) &sa, &al); | |
657 | ||
658 | if (e < 0) | |
659 | { | |
660 | if (errno != EINTR && errno != EAGAIN) | |
661 | { | |
662 | log(L_ERR "recvfrom: %m"); | |
663 | s->err_hook(s, errno); | |
664 | } | |
665 | return 0; | |
666 | } | |
667 | s->rpos = s->rbuf + e; | |
668 | get_sockaddr(&sa, &s->faddr, &s->fport); | |
669 | s->rx_hook(s, e); | |
670 | return 1; | |
671 | } | |
672 | } | |
673 | } | |
674 | ||
675 | static void | |
676 | sk_write(sock *s) | |
677 | { | |
678 | while (s->ttx != s->tbuf && sk_maybe_write(s) > 0) | |
679 | s->tx_hook(s); | |
680 | } | |
681 | ||
682 | void | |
683 | sk_dump_all(void) | |
684 | { | |
685 | node *n; | |
686 | sock *s; | |
687 | ||
688 | debug("Open sockets:\n"); | |
689 | WALK_LIST(n, sock_list) | |
690 | { | |
691 | s = SKIP_BACK(sock, n, n); | |
692 | debug("%p ", s); | |
693 | sk_dump(&s->r); | |
694 | } | |
695 | debug("\n"); | |
696 | } | |
697 | ||
698 | #undef ERR | |
699 | ||
700 | /* | |
701 | * Main I/O Loop | |
702 | */ | |
703 | ||
4c9dd1e4 MM |
704 | volatile int async_config_flag; /* Asynchronous reconfiguration/dump scheduled */ |
705 | volatile int async_dump_flag; | |
706 | ||
b5d9ee5c MM |
707 | void |
708 | io_init(void) | |
709 | { | |
710 | init_list(&near_timers); | |
711 | init_list(&far_timers); | |
712 | init_list(&sock_list); | |
e8f73195 | 713 | init_list(&global_event_list); |
7e5f5ffd | 714 | krt_io_init(); |
b5d9ee5c MM |
715 | now = time(NULL); |
716 | } | |
717 | ||
718 | void | |
719 | io_loop(void) | |
720 | { | |
721 | fd_set rd, wr; | |
722 | struct timeval timo; | |
723 | time_t tout; | |
724 | int hi; | |
725 | sock *s; | |
726 | node *n; | |
727 | ||
728 | /* FIXME: Use poll() if available */ | |
729 | ||
730 | FD_ZERO(&rd); | |
731 | FD_ZERO(&wr); | |
732 | for(;;) | |
733 | { | |
e8f73195 | 734 | ev_run_list(&global_event_list); |
b5d9ee5c MM |
735 | now = time(NULL); |
736 | tout = tm_first_shot(); | |
737 | if (tout <= now) | |
738 | { | |
739 | tm_shot(); | |
740 | continue; | |
741 | } | |
742 | else | |
5331da6a MM |
743 | { |
744 | timo.tv_sec = tout - now; | |
745 | timo.tv_usec = 0; | |
746 | } | |
b5d9ee5c MM |
747 | |
748 | hi = 0; | |
749 | WALK_LIST(n, sock_list) | |
750 | { | |
751 | s = SKIP_BACK(sock, n, n); | |
752 | if (s->rx_hook) | |
753 | { | |
754 | FD_SET(s->fd, &rd); | |
755 | if (s->fd > hi) | |
756 | hi = s->fd; | |
757 | } | |
758 | if (s->tx_hook && s->ttx != s->tpos) | |
759 | { | |
760 | FD_SET(s->fd, &wr); | |
761 | if (s->fd > hi) | |
762 | hi = s->fd; | |
763 | } | |
764 | } | |
765 | ||
4c9dd1e4 MM |
766 | /* |
767 | * Yes, this is racy. But even if the signal comes before this test | |
768 | * and entering select(), it gets caught on the next timer tick. | |
769 | */ | |
770 | ||
771 | if (async_config_flag) | |
772 | { | |
773 | async_config(); | |
774 | async_config_flag = 0; | |
f4aabcee | 775 | continue; |
4c9dd1e4 MM |
776 | } |
777 | if (async_dump_flag) | |
778 | { | |
779 | async_dump(); | |
780 | async_dump_flag = 0; | |
f4aabcee MM |
781 | continue; |
782 | } | |
783 | if (async_shutdown_flag) | |
784 | { | |
785 | async_shutdown(); | |
786 | async_shutdown_flag = 0; | |
787 | continue; | |
4c9dd1e4 MM |
788 | } |
789 | ||
790 | /* And finally enter select() to find active sockets */ | |
791 | ||
b5d9ee5c MM |
792 | hi = select(hi+1, &rd, &wr, NULL, &timo); |
793 | if (hi < 0) | |
794 | { | |
795 | if (errno == EINTR || errno == EAGAIN) | |
796 | continue; | |
797 | die("select: %m"); | |
798 | } | |
799 | if (hi) | |
800 | { | |
801 | WALK_LIST(n, sock_list) | |
802 | { | |
803 | s = SKIP_BACK(sock, n, n); | |
804 | if (FD_ISSET(s->fd, &rd)) | |
805 | { | |
806 | FD_CLR(s->fd, &rd); | |
807 | while (sk_read(s)) | |
808 | ; | |
809 | } | |
810 | if (FD_ISSET(s->fd, &wr)) | |
811 | { | |
812 | FD_CLR(s->fd, &wr); | |
813 | sk_write(s); | |
814 | } | |
815 | } | |
816 | } | |
817 | } | |
818 | } |