Annotation of embedaddon/bird/sysdep/bsd/krt-sock.c, revision 1.1.1.1
1.1 misho 1: /*
2: * BIRD -- BSD Routing Table Syncing
3: *
4: * (c) 2004 Ondrej Filip <feela@network.cz>
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>
11: #include <ctype.h>
12: #include <fcntl.h>
13: #include <unistd.h>
14: #include <sys/param.h>
15: #include <sys/types.h>
16: #include <sys/socket.h>
17: #include <sys/sysctl.h>
18: #include <sys/ioctl.h>
19: #include <netinet/in.h>
20: #include <net/route.h>
21: #include <net/if.h>
22: #include <net/if_dl.h>
23:
24: #undef LOCAL_DEBUG
25:
26: #include "nest/bird.h"
27: #include "nest/iface.h"
28: #include "nest/route.h"
29: #include "nest/protocol.h"
30: #include "nest/iface.h"
31: #include "lib/timer.h"
32: #include "lib/unix.h"
33: #include "lib/krt.h"
34: #include "lib/string.h"
35: #include "lib/socket.h"
36:
37:
38: /*
39: * There are significant differences in multiple tables support between BSD variants.
40: *
41: * OpenBSD has table_id field for routes in route socket protocol, therefore all
42: * tables could be managed by one kernel socket. FreeBSD lacks such field,
43: * therefore multiple sockets (locked to specific table using SO_SETFIB socket
44: * option) must be used.
45: *
46: * Both FreeBSD and OpenBSD uses separate scans for each table. In OpenBSD,
47: * table_id is specified explicitly as sysctl scan argument, while in FreeBSD it
48: * is handled implicitly by changing default table using setfib() syscall.
49: *
50: * KRT_SHARED_SOCKET - use shared kernel socked instead of one for each krt_proto
51: * KRT_USE_SETFIB_SCAN - use setfib() for sysctl() route scan
52: * KRT_USE_SETFIB_SOCK - use SO_SETFIB socket option for kernel sockets
53: * KRT_USE_SYSCTL_7 - use 7-th arg of sysctl() as table id for route scans
54: * KRT_USE_SYSCTL_NET_FIBS - use net.fibs sysctl() for dynamic max number of fibs
55: */
56:
57: #ifdef __FreeBSD__
58: #define KRT_MAX_TABLES 256
59: #define KRT_USE_SETFIB_SCAN
60: #define KRT_USE_SETFIB_SOCK
61: #define KRT_USE_SYSCTL_NET_FIBS
62: #endif
63:
64: #ifdef __OpenBSD__
65: #define KRT_MAX_TABLES (RT_TABLEID_MAX+1)
66: #define KRT_SHARED_SOCKET
67: #define KRT_USE_SYSCTL_7
68: #endif
69:
70: #ifndef KRT_MAX_TABLES
71: #define KRT_MAX_TABLES 1
72: #endif
73:
74:
75:
76: /* Dynamic max number of tables */
77:
78: int krt_max_tables;
79:
80: #ifdef KRT_USE_SYSCTL_NET_FIBS
81:
82: static int
83: krt_get_max_tables(void)
84: {
85: int fibs;
86: size_t fibs_len = sizeof(fibs);
87:
88: if (sysctlbyname("net.fibs", &fibs, &fibs_len, NULL, 0) < 0)
89: {
90: log(L_WARN "KRT: unable to get max number of fib tables: %m");
91: return 1;
92: }
93:
94: return MIN(fibs, KRT_MAX_TABLES);
95: }
96:
97: #else
98:
99: static int
100: krt_get_max_tables(void)
101: {
102: return KRT_MAX_TABLES;
103: }
104:
105: #endif /* KRT_USE_SYSCTL_NET_FIBS */
106:
107:
108: /* setfib() syscall for FreeBSD scans */
109:
110: #ifdef KRT_USE_SETFIB_SCAN
111:
112: /*
113: static int krt_default_fib;
114:
115: static int
116: krt_get_active_fib(void)
117: {
118: int fib;
119: size_t fib_len = sizeof(fib);
120:
121: if (sysctlbyname("net.my_fibnum", &fib, &fib_len, NULL, 0) < 0)
122: {
123: log(L_WARN "KRT: unable to get active fib number: %m");
124: return 0;
125: }
126:
127: return fib;
128: }
129: */
130:
131: extern int setfib(int fib);
132:
133: #endif /* KRT_USE_SETFIB_SCAN */
134:
135:
136: /* table_id -> krt_proto map */
137:
138: #ifdef KRT_SHARED_SOCKET
139: static struct krt_proto *krt_table_map[KRT_MAX_TABLES];
140: #endif
141:
142:
143: /* Route socket message processing */
144:
145: int
146: krt_capable(rte *e)
147: {
148: rta *a = e->attrs;
149:
150: return
151: a->cast == RTC_UNICAST &&
152: (a->dest == RTD_ROUTER
153: || a->dest == RTD_DEVICE
154: #ifdef RTF_REJECT
155: || a->dest == RTD_UNREACHABLE
156: #endif
157: #ifdef RTF_BLACKHOLE
158: || a->dest == RTD_BLACKHOLE
159: #endif
160: );
161: }
162:
163: #ifndef RTAX_MAX
164: #define RTAX_MAX 8
165: #endif
166:
167: struct ks_msg
168: {
169: struct rt_msghdr rtm;
170: struct sockaddr_storage buf[RTAX_MAX];
171: };
172:
173: #define ROUNDUP(a) \
174: ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
175:
176: #define NEXTADDR(w, u) \
177: if (msg.rtm.rtm_addrs & (w)) {\
178: l = ROUNDUP(((struct sockaddr *)&(u))->sa_len);\
179: memmove(body, &(u), l); body += l;}
180:
181: #define GETADDR(p, F) \
182: bzero(p, sizeof(*p));\
183: if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\
184: uint l = ROUNDUP(((struct sockaddr *)body)->sa_len);\
185: memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\
186: body += l;}
187:
188: static int
189: krt_send_route(struct krt_proto *p, int cmd, rte *e)
190: {
191: net *net = e->net;
192: rta *a = e->attrs;
193: static int msg_seq;
194: struct iface *j, *i = a->iface;
195: int l;
196: struct ks_msg msg;
197: char *body = (char *)msg.buf;
198: sockaddr gate, mask, dst;
199: ip_addr gw;
200:
201: DBG("krt-sock: send %I/%d via %I\n", net->n.prefix, net->n.pxlen, a->gw);
202:
203: bzero(&msg,sizeof (struct rt_msghdr));
204: msg.rtm.rtm_version = RTM_VERSION;
205: msg.rtm.rtm_type = cmd;
206: msg.rtm.rtm_seq = msg_seq++;
207: msg.rtm.rtm_addrs = RTA_DST;
208: msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1;
209:
210: if (net->n.pxlen == MAX_PREFIX_LENGTH)
211: msg.rtm.rtm_flags |= RTF_HOST;
212: else
213: msg.rtm.rtm_addrs |= RTA_NETMASK;
214:
215: #ifdef KRT_SHARED_SOCKET
216: msg.rtm.rtm_tableid = KRT_CF->sys.table_id;
217: #endif
218:
219: #ifdef RTF_REJECT
220: if(a->dest == RTD_UNREACHABLE)
221: msg.rtm.rtm_flags |= RTF_REJECT;
222: #endif
223: #ifdef RTF_BLACKHOLE
224: if(a->dest == RTD_BLACKHOLE)
225: msg.rtm.rtm_flags |= RTF_BLACKHOLE;
226: #endif
227:
228: /* This is really very nasty, but I'm not able
229: * to add "(reject|blackhole)" route without
230: * gateway set
231: */
232: if(!i)
233: {
234: i = HEAD(iface_list);
235:
236: WALK_LIST(j, iface_list)
237: {
238: if (j->flags & IF_LOOPBACK)
239: {
240: i = j;
241: break;
242: }
243: }
244: }
245:
246: gw = a->gw;
247:
248: #ifdef IPV6
249: /* Embed interface ID to link-local address */
250: if (ipa_is_link_local(gw))
251: _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff);
252: #endif
253:
254: sockaddr_fill(&dst, BIRD_AF, net->n.prefix, NULL, 0);
255: sockaddr_fill(&mask, BIRD_AF, ipa_mkmask(net->n.pxlen), NULL, 0);
256: sockaddr_fill(&gate, BIRD_AF, gw, NULL, 0);
257:
258: switch (a->dest)
259: {
260: case RTD_ROUTER:
261: msg.rtm.rtm_flags |= RTF_GATEWAY;
262: msg.rtm.rtm_addrs |= RTA_GATEWAY;
263: break;
264:
265: #ifdef RTF_REJECT
266: case RTD_UNREACHABLE:
267: #endif
268: #ifdef RTF_BLACKHOLE
269: case RTD_BLACKHOLE:
270: #endif
271: case RTD_DEVICE:
272: if(i)
273: {
274: #ifdef RTF_CLONING
275: if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */
276: msg.rtm.rtm_flags |= RTF_CLONING;
277: #endif
278:
279: if(!i->addr) {
280: log(L_ERR "KRT: interface %s has no IP addess", i->name);
281: return -1;
282: }
283:
284: sockaddr_fill(&gate, BIRD_AF, i->addr->ip, NULL, 0);
285: msg.rtm.rtm_addrs |= RTA_GATEWAY;
286: }
287: break;
288: default:
289: bug("krt-sock: unknown flags, but not filtered");
290: }
291:
292: msg.rtm.rtm_index = i->index;
293:
294: NEXTADDR(RTA_DST, dst);
295: NEXTADDR(RTA_GATEWAY, gate);
296: NEXTADDR(RTA_NETMASK, mask);
297:
298: l = body - (char *)&msg;
299: msg.rtm.rtm_msglen = l;
300:
301: if ((l = write(p->sys.sk->fd, (char *)&msg, l)) < 0) {
302: log(L_ERR "KRT: Error sending route %I/%d to kernel: %m", net->n.prefix, net->n.pxlen);
303: return -1;
304: }
305:
306: return 0;
307: }
308:
309: void
310: krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old,
311: struct ea_list *eattrs UNUSED)
312: {
313: int err = 0;
314:
315: if (old)
316: krt_send_route(p, RTM_DELETE, old);
317:
318: if (new)
319: err = krt_send_route(p, RTM_ADD, new);
320:
321: if (err < 0)
322: n->n.flags |= KRF_SYNC_ERROR;
323: else
324: n->n.flags &= ~KRF_SYNC_ERROR;
325: }
326:
327: #define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)
328:
329: static void
330: krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
331: {
332: /* p is NULL iff KRT_SHARED_SOCKET and !scan */
333:
334: rte *e;
335: net *net;
336: sockaddr dst, gate, mask;
337: ip_addr idst, igate, imask;
338: void *body = (char *)msg->buf;
339: int new = (msg->rtm.rtm_type != RTM_DELETE);
340: char *errmsg = "KRT: Invalid route received";
341: int flags = msg->rtm.rtm_flags;
342: int addrs = msg->rtm.rtm_addrs;
343: int src;
344: byte src2;
345:
346: if (!(flags & RTF_UP) && scan)
347: SKIP("not up in scan\n");
348:
349: if (!(flags & RTF_DONE) && !scan)
350: SKIP("not done in async\n");
351:
352: if (flags & RTF_LLINFO)
353: SKIP("link-local\n");
354:
355: #ifdef KRT_SHARED_SOCKET
356: if (!scan)
357: {
358: int table_id = msg->rtm.rtm_tableid;
359: p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id] : NULL;
360:
361: if (!p)
362: SKIP("unknown table id %d\n", table_id);
363: }
364: #endif
365:
366: GETADDR(&dst, RTA_DST);
367: GETADDR(&gate, RTA_GATEWAY);
368: GETADDR(&mask, RTA_NETMASK);
369:
370: if (dst.sa.sa_family != BIRD_AF)
371: SKIP("invalid DST");
372:
373: idst = ipa_from_sa(&dst);
374: imask = ipa_from_sa(&mask);
375: igate = (gate.sa.sa_family == BIRD_AF) ? ipa_from_sa(&gate) : IPA_NONE;
376:
377: /* We do not test family for RTA_NETMASK, because BSD sends us
378: some strange values, but interpreting them as IPv4/IPv6 works */
379:
380:
381: int c = ipa_classify_net(idst);
382: if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
383: SKIP("strange class/scope\n");
384:
385: int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_masklen(imask);
386: if (pxlen < 0)
387: { log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; }
388:
389: if ((flags & RTF_GATEWAY) && ipa_zero(igate))
390: { log(L_ERR "%s (%I/%d) - missing gateway", errmsg, idst, pxlen); return; }
391:
392: u32 self_mask = RTF_PROTO1;
393: u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY;
394:
395: src2 = (flags & RTF_STATIC) ? 1 : 0;
396: src2 |= (flags & RTF_PROTO1) ? 2 : 0;
397:
398: #ifdef RTF_PROTO2
399: alien_mask |= RTF_PROTO2;
400: src2 |= (flags & RTF_PROTO2) ? 4 : 0;
401: #endif
402:
403: #ifdef RTF_PROTO3
404: alien_mask |= RTF_PROTO3;
405: src2 |= (flags & RTF_PROTO3) ? 8 : 0;
406: #endif
407:
408: #ifdef RTF_REJECT
409: alien_mask |= RTF_REJECT;
410: #endif
411:
412: #ifdef RTF_BLACKHOLE
413: alien_mask |= RTF_BLACKHOLE;
414: #endif
415:
416: if (flags & (RTF_DYNAMIC | RTF_MODIFIED))
417: src = KRT_SRC_REDIRECT;
418: else if (flags & self_mask)
419: {
420: if (!scan)
421: SKIP("echo\n");
422: src = KRT_SRC_BIRD;
423: }
424: else if (flags & alien_mask)
425: src = KRT_SRC_ALIEN;
426: else
427: src = KRT_SRC_KERNEL;
428:
429: net = net_get(p->p.table, idst, pxlen);
430:
431: rta a = {
432: .src = p->p.main_source,
433: .source = RTS_INHERIT,
434: .scope = SCOPE_UNIVERSE,
435: .cast = RTC_UNICAST
436: };
437:
438: /* reject/blackhole routes have also set RTF_GATEWAY,
439: we wil check them first. */
440:
441: #ifdef RTF_REJECT
442: if(flags & RTF_REJECT) {
443: a.dest = RTD_UNREACHABLE;
444: goto done;
445: }
446: #endif
447:
448: #ifdef RTF_BLACKHOLE
449: if(flags & RTF_BLACKHOLE) {
450: a.dest = RTD_BLACKHOLE;
451: goto done;
452: }
453: #endif
454:
455: a.iface = if_find_by_index(msg->rtm.rtm_index);
456: if (!a.iface)
457: {
458: log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u",
459: net->n.prefix, net->n.pxlen, msg->rtm.rtm_index);
460: return;
461: }
462:
463: if (flags & RTF_GATEWAY)
464: {
465: neighbor *ng;
466: a.dest = RTD_ROUTER;
467: a.gw = igate;
468:
469: #ifdef IPV6
470: /* Clean up embedded interface ID returned in link-local address */
471: if (ipa_is_link_local(a.gw))
472: _I0(a.gw) = 0xfe800000;
473: #endif
474:
475: ng = neigh_find2(&p->p, &a.gw, a.iface, 0);
476: if (!ng || (ng->scope == SCOPE_HOST))
477: {
478: /* Ignore routes with next-hop 127.0.0.1, host routes with such
479: next-hop appear on OpenBSD for address aliases. */
480: if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST))
481: return;
482:
483: log(L_ERR "KRT: Received route %I/%d with strange next-hop %I",
484: net->n.prefix, net->n.pxlen, a.gw);
485: return;
486: }
487: }
488: else
489: a.dest = RTD_DEVICE;
490:
491: done:
492: e = rte_get_temp(&a);
493: e->net = net;
494: e->u.krt.src = src;
495: e->u.krt.proto = src2;
496: e->u.krt.seen = 0;
497: e->u.krt.best = 0;
498: e->u.krt.metric = 0;
499:
500: if (scan)
501: krt_got_route(p, e);
502: else
503: krt_got_route_async(p, e, new);
504: }
505:
506: static void
507: krt_read_ifannounce(struct ks_msg *msg)
508: {
509: struct if_announcemsghdr *ifam = (struct if_announcemsghdr *)&msg->rtm;
510:
511: if (ifam->ifan_what == IFAN_ARRIVAL)
512: {
513: /* Not enough info to create the iface, so we just trigger iface scan */
514: kif_request_scan();
515: }
516: else if (ifam->ifan_what == IFAN_DEPARTURE)
517: {
518: struct iface *iface = if_find_by_index(ifam->ifan_index);
519:
520: /* Interface is destroyed */
521: if (!iface)
522: {
523: DBG("KRT: unknown interface (%s, #%d) going down. Ignoring\n", ifam->ifan_name, ifam->ifan_index);
524: return;
525: }
526:
527: if_delete(iface);
528: }
529:
530: DBG("KRT: IFANNOUNCE what: %d index %d name %s\n", ifam->ifan_what, ifam->ifan_index, ifam->ifan_name);
531: }
532:
533: static void
534: krt_read_ifinfo(struct ks_msg *msg, int scan)
535: {
536: struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm;
537: void *body = (void *)(ifm + 1);
538: struct sockaddr_dl *dl = NULL;
539: uint i;
540: struct iface *iface = NULL, f = {};
541: int fl = ifm->ifm_flags;
542: int nlen = 0;
543:
544: for (i = 1; i<=RTA_IFP; i <<= 1)
545: {
546: if (i & ifm->ifm_addrs)
547: {
548: if (i == RTA_IFP)
549: {
550: dl = (struct sockaddr_dl *)body;
551: break;
552: }
553: body += ROUNDUP(((struct sockaddr *)&(body))->sa_len);
554: }
555: }
556:
557: if (dl && (dl->sdl_family != AF_LINK))
558: {
559: log(L_WARN "Ignoring strange IFINFO");
560: return;
561: }
562:
563: if (dl)
564: nlen = MIN(sizeof(f.name)-1, dl->sdl_nlen);
565:
566: /* Note that asynchronous IFINFO messages do not contain iface
567: name, so we have to found an existing iface by iface index */
568:
569: iface = if_find_by_index(ifm->ifm_index);
570: if (!iface)
571: {
572: /* New interface */
573: if (!dl)
574: return; /* No interface name, ignoring */
575:
576: memcpy(f.name, dl->sdl_data, nlen);
577: DBG("New interface '%s' found\n", f.name);
578: }
579: else if (dl && memcmp(iface->name, dl->sdl_data, nlen))
580: {
581: /* Interface renamed */
582: if_delete(iface);
583: memcpy(f.name, dl->sdl_data, nlen);
584: }
585: else
586: {
587: /* Old interface */
588: memcpy(f.name, iface->name, sizeof(f.name));
589: }
590:
591: f.index = ifm->ifm_index;
592: f.mtu = ifm->ifm_data.ifi_mtu;
593:
594: if (fl & IFF_UP)
595: f.flags |= IF_ADMIN_UP;
596: if (ifm->ifm_data.ifi_link_state != LINK_STATE_DOWN)
597: f.flags |= IF_LINK_UP; /* up or unknown */
598: if (fl & IFF_LOOPBACK) /* Loopback */
599: f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE;
600: else if (fl & IFF_POINTOPOINT) /* PtP */
601: f.flags |= IF_MULTICAST;
602: else if (fl & IFF_BROADCAST) /* Broadcast */
603: f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST;
604: else
605: f.flags |= IF_MULTIACCESS; /* NBMA */
606:
607: iface = if_update(&f);
608:
609: if (!scan)
610: if_end_partial_update(iface);
611: }
612:
613: static void
614: krt_read_addr(struct ks_msg *msg, int scan)
615: {
616: struct ifa_msghdr *ifam = (struct ifa_msghdr *)&msg->rtm;
617: void *body = (void *)(ifam + 1);
618: sockaddr addr, mask, brd;
619: struct iface *iface = NULL;
620: struct ifa ifa;
621: struct sockaddr null;
622: ip_addr iaddr, imask, ibrd;
623: int addrs = ifam->ifam_addrs;
624: int scope, masklen = -1;
625: int new = (ifam->ifam_type == RTM_NEWADDR);
626:
627: /* Strange messages with zero (invalid) ifindex appear on OpenBSD */
628: if (ifam->ifam_index == 0)
629: return;
630:
631: if(!(iface = if_find_by_index(ifam->ifam_index)))
632: {
633: log(L_ERR "KIF: Received address message for unknown interface %d", ifam->ifam_index);
634: return;
635: }
636:
637: GETADDR (&null, RTA_DST);
638: GETADDR (&null, RTA_GATEWAY);
639: GETADDR (&mask, RTA_NETMASK);
640: GETADDR (&null, RTA_GENMASK);
641: GETADDR (&null, RTA_IFP);
642: GETADDR (&addr, RTA_IFA);
643: GETADDR (&null, RTA_AUTHOR);
644: GETADDR (&brd, RTA_BRD);
645:
646: /* Some other family address */
647: if (addr.sa.sa_family != BIRD_AF)
648: return;
649:
650: iaddr = ipa_from_sa(&addr);
651: imask = ipa_from_sa(&mask);
652: ibrd = ipa_from_sa(&brd);
653:
654:
655: if ((masklen = ipa_masklen(imask)) < 0)
656: {
657: log(L_ERR "KIF: Invalid masklen %I for %s", imask, iface->name);
658: return;
659: }
660:
661: #ifdef IPV6
662: /* Clean up embedded interface ID returned in link-local address */
663:
664: if (ipa_is_link_local(iaddr))
665: _I0(iaddr) = 0xfe800000;
666:
667: if (ipa_is_link_local(ibrd))
668: _I0(ibrd) = 0xfe800000;
669: #endif
670:
671:
672: bzero(&ifa, sizeof(ifa));
673: ifa.iface = iface;
674: ifa.ip = iaddr;
675: ifa.pxlen = masklen;
676:
677: scope = ipa_classify(ifa.ip);
678: if (scope < 0)
679: {
680: log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, iface->name);
681: return;
682: }
683: ifa.scope = scope & IADDR_SCOPE_MASK;
684:
685: if (masklen < BITS_PER_IP_ADDRESS)
686: {
687: ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen));
688:
689: if (masklen == (BITS_PER_IP_ADDRESS - 1))
690: ifa.opposite = ipa_opposite_m1(ifa.ip);
691:
692: #ifndef IPV6
693: if (masklen == (BITS_PER_IP_ADDRESS - 2))
694: ifa.opposite = ipa_opposite_m2(ifa.ip);
695: #endif
696:
697: if (iface->flags & IF_BROADCAST)
698: ifa.brd = ibrd;
699:
700: if (!(iface->flags & IF_MULTIACCESS))
701: ifa.opposite = ibrd;
702: }
703: else if (!(iface->flags & IF_MULTIACCESS) && ipa_nonzero(ibrd))
704: {
705: ifa.prefix = ifa.opposite = ibrd;
706: ifa.flags |= IA_PEER;
707: }
708: else
709: {
710: ifa.prefix = ifa.ip;
711: ifa.flags |= IA_HOST;
712: }
713:
714: if (new)
715: ifa_update(&ifa);
716: else
717: ifa_delete(&ifa);
718:
719: if (!scan)
720: if_end_partial_update(iface);
721: }
722:
723: static void
724: krt_read_msg(struct proto *p, struct ks_msg *msg, int scan)
725: {
726: /* p is NULL iff KRT_SHARED_SOCKET and !scan */
727:
728: switch (msg->rtm.rtm_type)
729: {
730: case RTM_GET:
731: if(!scan) return;
732: case RTM_ADD:
733: case RTM_DELETE:
734: case RTM_CHANGE:
735: krt_read_route(msg, (struct krt_proto *)p, scan);
736: break;
737: case RTM_IFANNOUNCE:
738: krt_read_ifannounce(msg);
739: break;
740: case RTM_IFINFO:
741: krt_read_ifinfo(msg, scan);
742: break;
743: case RTM_NEWADDR:
744: case RTM_DELADDR:
745: krt_read_addr(msg, scan);
746: break;
747: default:
748: break;
749: }
750: }
751:
752:
753: /* Sysctl based scans */
754:
755: static byte *krt_buffer;
756: static size_t krt_buflen, krt_bufmin;
757: static struct proto *krt_buffer_owner;
758:
759: static byte *
760: krt_buffer_update(struct proto *p, size_t *needed)
761: {
762: size_t req = *needed;
763:
764: if ((req > krt_buflen) ||
765: ((p == krt_buffer_owner) && (req < krt_bufmin)))
766: {
767: /* min buflen is 32 kB, step is 8 kB, or 128 kB if > 1 MB */
768: size_t step = (req < 0x100000) ? 0x2000 : 0x20000;
769: krt_buflen = (req < 0x6000) ? 0x8000 : (req + step);
770: krt_bufmin = (req < 0x8000) ? 0 : (req - 2*step);
771:
772: if (krt_buffer)
773: mb_free(krt_buffer);
774: krt_buffer = mb_alloc(krt_pool, krt_buflen);
775: krt_buffer_owner = p;
776: }
777:
778: *needed = krt_buflen;
779: return krt_buffer;
780: }
781:
782: static void
783: krt_buffer_release(struct proto *p)
784: {
785: if (p == krt_buffer_owner)
786: {
787: mb_free(krt_buffer);
788: krt_buffer = NULL;
789: krt_buflen = 0;
790: krt_buffer_owner = 0;
791: }
792: }
793:
794: static void
795: krt_sysctl_scan(struct proto *p, int cmd, int table_id)
796: {
797: byte *buf, *next;
798: int mib[7], mcnt;
799: size_t needed;
800: struct ks_msg *m;
801: int retries = 3;
802: int rv;
803:
804: mib[0] = CTL_NET;
805: mib[1] = PF_ROUTE;
806: mib[2] = 0;
807: mib[3] = BIRD_AF;
808: mib[4] = cmd;
809: mib[5] = 0;
810: mcnt = 6;
811:
812: #ifdef KRT_USE_SYSCTL_7
813: if (table_id >= 0)
814: {
815: mib[6] = table_id;
816: mcnt = 7;
817: }
818: #endif
819:
820: #ifdef KRT_USE_SETFIB_SCAN
821: if (table_id > 0)
822: if (setfib(table_id) < 0)
823: {
824: log(L_ERR "KRT: setfib(%d) failed: %m", table_id);
825: return;
826: }
827: #endif
828:
829: try:
830: rv = sysctl(mib, mcnt, NULL, &needed, NULL, 0);
831: if (rv < 0)
832: {
833: /* OpenBSD returns EINVAL for not yet used tables */
834: if ((errno == EINVAL) && (table_id > 0))
835: goto exit;
836:
837: log(L_ERR "KRT: Route scan estimate failed: %m");
838: goto exit;
839: }
840:
841: /* The table is empty */
842: if (needed == 0)
843: goto exit;
844:
845: buf = krt_buffer_update(p, &needed);
846:
847: rv = sysctl(mib, mcnt, buf, &needed, NULL, 0);
848: if (rv < 0)
849: {
850: /* The buffer size changed since last sysctl ('needed' is not changed) */
851: if ((errno == ENOMEM) && retries--)
852: goto try;
853:
854: log(L_ERR "KRT: Route scan failed: %m");
855: goto exit;
856: }
857:
858: #ifdef KRT_USE_SETFIB_SCAN
859: if (table_id > 0)
860: if (setfib(0) < 0)
861: die("KRT: setfib(%d) failed: %m", 0);
862: #endif
863:
864: /* Process received messages */
865: for (next = buf; next < (buf + needed); next += m->rtm.rtm_msglen)
866: {
867: m = (struct ks_msg *)next;
868: krt_read_msg(p, m, 1);
869: }
870:
871: return;
872:
873: exit:
874: krt_buffer_release(p);
875:
876: #ifdef KRT_USE_SETFIB_SCAN
877: if (table_id > 0)
878: if (setfib(0) < 0)
879: die("KRT: setfib(%d) failed: %m", 0);
880: #endif
881: }
882:
883: void
884: krt_do_scan(struct krt_proto *p)
885: {
886: krt_sysctl_scan(&p->p, NET_RT_DUMP, KRT_CF->sys.table_id);
887: }
888:
889: void
890: kif_do_scan(struct kif_proto *p)
891: {
892: if_start_update();
893: krt_sysctl_scan(&p->p, NET_RT_IFLIST, -1);
894: if_end_update();
895: }
896:
897:
898: /* Kernel sockets */
899:
900: static int
901: krt_sock_hook(sock *sk, uint size UNUSED)
902: {
903: struct ks_msg msg;
904: int l = read(sk->fd, (char *)&msg, sizeof(msg));
905:
906: if (l <= 0)
907: log(L_ERR "krt-sock: read failed");
908: else
909: krt_read_msg((struct proto *) sk->data, &msg, 0);
910:
911: return 0;
912: }
913:
914: static void
915: krt_sock_err_hook(sock *sk, int e UNUSED)
916: {
917: krt_sock_hook(sk, 0);
918: }
919:
920: static sock *
921: krt_sock_open(pool *pool, void *data, int table_id UNUSED)
922: {
923: sock *sk;
924: int fd;
925:
926: fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
927: if (fd < 0)
928: die("Cannot open kernel socket for routes");
929:
930: #ifdef KRT_USE_SETFIB_SOCK
931: if (table_id > 0)
932: {
933: if (setsockopt(fd, SOL_SOCKET, SO_SETFIB, &table_id, sizeof(table_id)) < 0)
934: die("Cannot set FIB %d for kernel socket: %m", table_id);
935: }
936: #endif
937:
938: sk = sk_new(pool);
939: sk->type = SK_MAGIC;
940: sk->rx_hook = krt_sock_hook;
941: sk->err_hook = krt_sock_err_hook;
942: sk->fd = fd;
943: sk->data = data;
944:
945: if (sk_open(sk) < 0)
946: bug("krt-sock: sk_open failed");
947:
948: return sk;
949: }
950:
951:
952: #ifdef KRT_SHARED_SOCKET
953:
954: static sock *krt_sock;
955: static int krt_sock_count;
956:
957:
958: static void
959: krt_sock_open_shared(void)
960: {
961: if (!krt_sock_count)
962: krt_sock = krt_sock_open(krt_pool, NULL, -1);
963:
964: krt_sock_count++;
965: }
966:
967: static void
968: krt_sock_close_shared(void)
969: {
970: krt_sock_count--;
971:
972: if (!krt_sock_count)
973: {
974: rfree(krt_sock);
975: krt_sock = NULL;
976: }
977: }
978:
979: int
980: krt_sys_start(struct krt_proto *p)
981: {
982: krt_table_map[KRT_CF->sys.table_id] = p;
983:
984: krt_sock_open_shared();
985: p->sys.sk = krt_sock;
986:
987: return 1;
988: }
989:
990: void
991: krt_sys_shutdown(struct krt_proto *p)
992: {
993: krt_sock_close_shared();
994: p->sys.sk = NULL;
995:
996: krt_table_map[KRT_CF->sys.table_id] = NULL;
997:
998: krt_buffer_release(&p->p);
999: }
1000:
1001: #else
1002:
1003: int
1004: krt_sys_start(struct krt_proto *p)
1005: {
1006: p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id);
1007: return 1;
1008: }
1009:
1010: void
1011: krt_sys_shutdown(struct krt_proto *p)
1012: {
1013: rfree(p->sys.sk);
1014: p->sys.sk = NULL;
1015:
1016: krt_buffer_release(&p->p);
1017: }
1018:
1019: #endif /* KRT_SHARED_SOCKET */
1020:
1021:
1022: /* KRT configuration callbacks */
1023:
1024: static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32];
1025:
1026: int
1027: krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o)
1028: {
1029: return n->sys.table_id == o->sys.table_id;
1030: }
1031:
1032: void
1033: krt_sys_preconfig(struct config *c UNUSED)
1034: {
1035: krt_max_tables = krt_get_max_tables();
1036: bzero(&krt_table_cf, sizeof(krt_table_cf));
1037: }
1038:
1039: void
1040: krt_sys_postconfig(struct krt_config *x)
1041: {
1042: u32 *tbl = krt_table_cf;
1043: int id = x->sys.table_id;
1044:
1045: if (tbl[id/32] & (1 << (id%32)))
1046: cf_error("Multiple kernel syncers defined for table #%d", id);
1047:
1048: tbl[id/32] |= (1 << (id%32));
1049: }
1050:
1051: void krt_sys_init_config(struct krt_config *c)
1052: {
1053: c->sys.table_id = 0; /* Default table */
1054: }
1055:
1056: void krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
1057: {
1058: d->sys.table_id = s->sys.table_id;
1059: }
1060:
1061:
1062: /* KIF misc code */
1063:
1064: void
1065: kif_sys_start(struct kif_proto *p UNUSED)
1066: {
1067: }
1068:
1069: void
1070: kif_sys_shutdown(struct kif_proto *p)
1071: {
1072: krt_buffer_release(&p->p);
1073: }
1074:
1075:
1076: struct ifa *
1077: kif_get_primary_ip(struct iface *i UNUSED6)
1078: {
1079: #ifndef IPV6
1080: static int fd = -1;
1081:
1082: if (fd < 0)
1083: fd = socket(AF_INET, SOCK_DGRAM, 0);
1084:
1085: struct ifreq ifr;
1086: memset(&ifr, 0, sizeof(ifr));
1087: strncpy(ifr.ifr_name, i->name, IFNAMSIZ);
1088:
1089: int rv = ioctl(fd, SIOCGIFADDR, (char *) &ifr);
1090: if (rv < 0)
1091: return NULL;
1092:
1093: ip_addr addr;
1094: struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
1095: memcpy(&addr, &sin->sin_addr.s_addr, sizeof(ip_addr));
1096: ipa_ntoh(addr);
1097:
1098: struct ifa *a;
1099: WALK_LIST(a, i->addrs)
1100: {
1101: if (ipa_equal(a->ip, addr))
1102: return a;
1103: }
1104: #endif
1105:
1106: return NULL;
1107: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>