]> git.ipfire.org Git - thirdparty/bird.git/blame - sysdep/linux/netlink.c
Some consts for function arguments
[thirdparty/bird.git] / sysdep / linux / netlink.c
CommitLineData
95616c82
OZ
1/*
2 * BIRD -- Linux Netlink Interface
3 *
4 * (c) 1999--2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9#include <stdio.h>
f83ce94d 10#include <unistd.h>
95616c82
OZ
11#include <fcntl.h>
12#include <sys/socket.h>
13#include <sys/uio.h>
14#include <errno.h>
15
16#undef LOCAL_DEBUG
17
18#include "nest/bird.h"
19#include "nest/route.h"
20#include "nest/protocol.h"
21#include "nest/iface.h"
22#include "lib/alloca.h"
23#include "lib/timer.h"
24#include "lib/unix.h"
25#include "lib/krt.h"
26#include "lib/socket.h"
27#include "lib/string.h"
9ddbfbdd 28#include "lib/hash.h"
95616c82
OZ
29#include "conf/conf.h"
30
31#include <asm/types.h>
32#include <linux/if.h>
33#include <linux/netlink.h>
34#include <linux/rtnetlink.h>
35
9ddbfbdd 36
95616c82
OZ
37#ifndef MSG_TRUNC /* Hack: Several versions of glibc miss this one :( */
38#define MSG_TRUNC 0x20
39#endif
40
41#ifndef IFF_LOWER_UP
42#define IFF_LOWER_UP 0x10000
43#endif
44
9ddbfbdd
JMM
45#ifndef RTA_TABLE
46#define RTA_TABLE 15
47#endif
48
49
95616c82
OZ
50/*
51 * Synchronous Netlink interface
52 */
53
54struct nl_sock
55{
56 int fd;
57 u32 seq;
58 byte *rx_buffer; /* Receive buffer */
59 struct nlmsghdr *last_hdr; /* Recently received packet */
ae80a2de 60 uint last_size;
95616c82
OZ
61};
62
63#define NL_RX_SIZE 8192
64
65static struct nl_sock nl_scan = {.fd = -1}; /* Netlink socket for synchronous scan */
66static struct nl_sock nl_req = {.fd = -1}; /* Netlink socket for requests */
67
68static void
69nl_open_sock(struct nl_sock *nl)
70{
71 if (nl->fd < 0)
72 {
73 nl->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
74 if (nl->fd < 0)
75 die("Unable to open rtnetlink socket: %m");
76 nl->seq = now;
77 nl->rx_buffer = xmalloc(NL_RX_SIZE);
78 nl->last_hdr = NULL;
79 nl->last_size = 0;
80 }
81}
82
83static void
84nl_open(void)
85{
86 nl_open_sock(&nl_scan);
87 nl_open_sock(&nl_req);
88}
89
90static void
91nl_send(struct nl_sock *nl, struct nlmsghdr *nh)
92{
93 struct sockaddr_nl sa;
94
95 memset(&sa, 0, sizeof(sa));
96 sa.nl_family = AF_NETLINK;
97 nh->nlmsg_pid = 0;
98 nh->nlmsg_seq = ++(nl->seq);
99 if (sendto(nl->fd, nh, nh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0)
100 die("rtnetlink sendto: %m");
101 nl->last_hdr = NULL;
102}
103
104static void
86c3eea0 105nl_request_dump(int af, int cmd)
95616c82
OZ
106{
107 struct {
108 struct nlmsghdr nh;
109 struct rtgenmsg g;
641172c6
OZ
110 } req = {
111 .nh.nlmsg_type = cmd,
112 .nh.nlmsg_len = sizeof(req),
113 .nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
114 .g.rtgen_family = af
115 };
95616c82
OZ
116 nl_send(&nl_scan, &req.nh);
117}
118
119static struct nlmsghdr *
120nl_get_reply(struct nl_sock *nl)
121{
122 for(;;)
123 {
124 if (!nl->last_hdr)
125 {
126 struct iovec iov = { nl->rx_buffer, NL_RX_SIZE };
127 struct sockaddr_nl sa;
128 struct msghdr m = { (struct sockaddr *) &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
129 int x = recvmsg(nl->fd, &m, 0);
130 if (x < 0)
131 die("nl_get_reply: %m");
132 if (sa.nl_pid) /* It isn't from the kernel */
133 {
134 DBG("Non-kernel packet\n");
135 continue;
136 }
137 nl->last_size = x;
138 nl->last_hdr = (void *) nl->rx_buffer;
139 if (m.msg_flags & MSG_TRUNC)
140 bug("nl_get_reply: got truncated reply which should be impossible");
141 }
142 if (NLMSG_OK(nl->last_hdr, nl->last_size))
143 {
144 struct nlmsghdr *h = nl->last_hdr;
145 nl->last_hdr = NLMSG_NEXT(h, nl->last_size);
146 if (h->nlmsg_seq != nl->seq)
147 {
148 log(L_WARN "nl_get_reply: Ignoring out of sequence netlink packet (%x != %x)",
149 h->nlmsg_seq, nl->seq);
150 continue;
151 }
152 return h;
153 }
154 if (nl->last_size)
155 log(L_WARN "nl_get_reply: Found packet remnant of size %d", nl->last_size);
156 nl->last_hdr = NULL;
157 }
158}
159
1123e707 160static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS;
95616c82
OZ
161
162static int
163nl_error(struct nlmsghdr *h)
164{
165 struct nlmsgerr *e;
166 int ec;
167
168 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
169 {
170 log(L_WARN "Netlink: Truncated error message received");
171 return ENOBUFS;
172 }
173 e = (struct nlmsgerr *) NLMSG_DATA(h);
174 ec = -e->error;
175 if (ec)
176 log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec));
177 return ec;
178}
179
180static struct nlmsghdr *
181nl_get_scan(void)
182{
183 struct nlmsghdr *h = nl_get_reply(&nl_scan);
184
185 if (h->nlmsg_type == NLMSG_DONE)
186 return NULL;
187 if (h->nlmsg_type == NLMSG_ERROR)
188 {
189 nl_error(h);
190 return NULL;
191 }
192 return h;
193}
194
195static int
196nl_exchange(struct nlmsghdr *pkt)
197{
198 struct nlmsghdr *h;
199
200 nl_send(&nl_req, pkt);
201 for(;;)
202 {
203 h = nl_get_reply(&nl_req);
204 if (h->nlmsg_type == NLMSG_ERROR)
205 break;
206 log(L_WARN "nl_exchange: Unexpected reply received");
207 }
208 return nl_error(h) ? -1 : 0;
209}
210
211/*
212 * Netlink attributes
213 */
214
215static int nl_attr_len;
216
217static void *
218nl_checkin(struct nlmsghdr *h, int lsize)
219{
220 nl_attr_len = h->nlmsg_len - NLMSG_LENGTH(lsize);
221 if (nl_attr_len < 0)
222 {
223 log(L_ERR "nl_checkin: underrun by %d bytes", -nl_attr_len);
224 return NULL;
225 }
226 return NLMSG_DATA(h);
227}
228
229static int
230nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize)
231{
232 int max = ksize / sizeof(struct rtattr *);
233 bzero(k, ksize);
234 while (RTA_OK(a, nl_attr_len))
235 {
236 if (a->rta_type < max)
237 k[a->rta_type] = a;
238 a = RTA_NEXT(a, nl_attr_len);
239 }
240 if (nl_attr_len)
241 {
242 log(L_ERR "nl_parse_attrs: remnant of size %d", nl_attr_len);
243 return 0;
244 }
245 else
246 return 1;
247}
248
fce764f9 249static inline u32 rta_get_u32(struct rtattr *a)
acb04cfd
OZ
250{ return *(u32 *) RTA_DATA(a); }
251
252static inline ip4_addr rta_get_ip4(struct rtattr *a)
253{ return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); }
254
255static inline ip6_addr rta_get_ip6(struct rtattr *a)
256{ return ip6_ntoh(*(ip6_addr *) RTA_DATA(a)); }
257
258
9fdf9d29
OZ
259struct rtattr *
260nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
95616c82 261{
9fdf9d29
OZ
262 uint pos = NLMSG_ALIGN(h->nlmsg_len);
263 uint len = RTA_LENGTH(dlen);
95616c82
OZ
264
265 if (pos + len > bufsize)
266 bug("nl_add_attr: packet buffer overflow");
9fdf9d29
OZ
267
268 struct rtattr *a = (struct rtattr *)((char *)h + pos);
95616c82
OZ
269 a->rta_type = code;
270 a->rta_len = len;
271 h->nlmsg_len = pos + len;
9fdf9d29
OZ
272
273 if (dlen > 0)
274 memcpy(RTA_DATA(a), data, dlen);
275
276 return a;
95616c82
OZ
277}
278
279static inline void
280nl_add_attr_u32(struct nlmsghdr *h, unsigned bufsize, int code, u32 data)
281{
282 nl_add_attr(h, bufsize, code, &data, 4);
283}
284
285static inline void
286nl_add_attr_ipa(struct nlmsghdr *h, unsigned bufsize, int code, ip_addr ipa)
287{
288 ipa_hton(ipa);
289 nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa));
290}
291
9fdf9d29
OZ
292static inline struct rtattr *
293nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
294{
295 return nl_add_attr(h, bufsize, code, NULL, 0);
296}
95616c82
OZ
297
298static inline void
9fdf9d29 299nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
95616c82 300{
9fdf9d29 301 a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a;
95616c82
OZ
302}
303
9fdf9d29
OZ
304static inline struct rtnexthop *
305nl_open_nexthop(struct nlmsghdr *h, uint bufsize)
306{
307 uint pos = NLMSG_ALIGN(h->nlmsg_len);
308 uint len = RTNH_LENGTH(0);
309
310 if (pos + len > bufsize)
311 bug("nl_open_nexthop: packet buffer overflow");
312
313 h->nlmsg_len = pos + len;
314
315 return (void *)h + pos;
316}
317
318static inline void
319nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
320{
321 nh->rtnh_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)nh;
322}
95616c82
OZ
323
324static void
325nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh)
326{
9fdf9d29
OZ
327 struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
328
95616c82 329 for (; nh; nh = nh->next)
9fdf9d29
OZ
330 {
331 struct rtnexthop *rtnh = nl_open_nexthop(h, bufsize);
95616c82 332
9fdf9d29
OZ
333 rtnh->rtnh_flags = 0;
334 rtnh->rtnh_hops = nh->weight;
335 rtnh->rtnh_ifindex = nh->iface->index;
95616c82 336
38e835de 337 nl_add_attr_ipa(h, bufsize, RTA_GATEWAY, nh->gw);
95616c82 338
9fdf9d29
OZ
339 nl_close_nexthop(h, rtnh);
340 }
341
342 nl_close_attr(h, a);
343}
95616c82
OZ
344
345static struct mpnh *
346nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
347{
348 /* Temporary buffer for multicast nexthops */
349 static struct mpnh *nh_buffer;
350 static int nh_buf_size; /* in number of structures */
351 static int nh_buf_used;
352
353 struct rtattr *a[RTA_CACHEINFO+1];
354 struct rtnexthop *nh = RTA_DATA(ra);
355 struct mpnh *rv, *first, **last;
356 int len = RTA_PAYLOAD(ra);
357
358 first = NULL;
359 last = &first;
360 nh_buf_used = 0;
361
362 while (len)
363 {
364 /* Use RTNH_OK(nh,len) ?? */
365 if ((len < sizeof(*nh)) || (len < nh->rtnh_len))
366 return NULL;
367
368 if (nh_buf_used == nh_buf_size)
369 {
370 nh_buf_size = nh_buf_size ? (nh_buf_size * 2) : 4;
371 nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct mpnh));
372 }
373 *last = rv = nh_buffer + nh_buf_used++;
374 rv->next = NULL;
375 last = &(rv->next);
376
377 rv->weight = nh->rtnh_hops;
378 rv->iface = if_find_by_index(nh->rtnh_ifindex);
379 if (!rv->iface)
380 return NULL;
381
382 /* Nonexistent RTNH_PAYLOAD ?? */
383 nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
384 nl_parse_attrs(RTNH_DATA(nh), a, sizeof(a));
385 if (a[RTA_GATEWAY])
386 {
387 if (RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))
388 return NULL;
389
390 memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr));
391 ipa_ntoh(rv->gw);
392
393 neighbor *ng = neigh_find2(&p->p, &rv->gw, rv->iface,
394 (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
395 if (!ng || (ng->scope == SCOPE_HOST))
396 return NULL;
397 }
398 else
399 return NULL;
400
401 len -= NLMSG_ALIGN(nh->rtnh_len);
402 nh = RTNH_NEXT(nh);
403 }
404
405 return first;
406}
407
9fdf9d29
OZ
408static void
409nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max)
410{
411 struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS);
412 int t;
413
414 for (t = 1; t < max; t++)
415 if (metrics[0] & (1 << t))
416 nl_add_attr_u32(h, bufsize, t, metrics[t]);
417
418 nl_close_attr(h, a);
419}
420
421static int
422nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max)
423{
424 struct rtattr *a = RTA_DATA(hdr);
425 int len = RTA_PAYLOAD(hdr);
426
427 metrics[0] = 0;
428 for (; RTA_OK(a, len); a = RTA_NEXT(a, len))
429 {
430 if (a->rta_type == RTA_UNSPEC)
431 continue;
432
433 if (a->rta_type >= max)
434 continue;
435
436 if (RTA_PAYLOAD(a) != 4)
437 return -1;
438
439 metrics[0] |= 1 << a->rta_type;
acb04cfd 440 metrics[a->rta_type] = rta_get_u32(a);
9fdf9d29
OZ
441 }
442
443 if (len > 0)
444 return -1;
445
446 return 0;
447}
448
95616c82
OZ
449
450/*
451 * Scanning of interfaces
452 */
453
454static void
455nl_parse_link(struct nlmsghdr *h, int scan)
456{
457 struct ifinfomsg *i;
458 struct rtattr *a[IFLA_WIRELESS+1];
459 int new = h->nlmsg_type == RTM_NEWLINK;
460 struct iface f = {};
461 struct iface *ifi;
462 char *name;
463 u32 mtu;
ae80a2de 464 uint fl;
95616c82
OZ
465
466 if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), a, sizeof(a)))
467 return;
468 if (!a[IFLA_IFNAME] || RTA_PAYLOAD(a[IFLA_IFNAME]) < 2 ||
469 !a[IFLA_MTU] || RTA_PAYLOAD(a[IFLA_MTU]) != 4)
470 {
471 if (scan || !a[IFLA_WIRELESS])
472 log(L_ERR "nl_parse_link: Malformed message received");
473 return;
474 }
475 name = RTA_DATA(a[IFLA_IFNAME]);
acb04cfd 476 mtu = rta_get_u32(a[IFLA_MTU]);
95616c82
OZ
477
478 ifi = if_find_by_index(i->ifi_index);
479 if (!new)
480 {
481 DBG("KIF: IF%d(%s) goes down\n", i->ifi_index, name);
482 if (!ifi)
483 return;
484
485 if_delete(ifi);
486 }
487 else
488 {
489 DBG("KIF: IF%d(%s) goes up (mtu=%d,flg=%x)\n", i->ifi_index, name, mtu, i->ifi_flags);
490 if (ifi && strncmp(ifi->name, name, sizeof(ifi->name)-1))
491 if_delete(ifi);
492
493 strncpy(f.name, name, sizeof(f.name)-1);
494 f.index = i->ifi_index;
495 f.mtu = mtu;
496
497 fl = i->ifi_flags;
498 if (fl & IFF_UP)
499 f.flags |= IF_ADMIN_UP;
500 if (fl & IFF_LOWER_UP)
501 f.flags |= IF_LINK_UP;
502 if (fl & IFF_LOOPBACK) /* Loopback */
503 f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE;
504 else if (fl & IFF_POINTOPOINT) /* PtP */
505 f.flags |= IF_MULTICAST;
506 else if (fl & IFF_BROADCAST) /* Broadcast */
507 f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST;
508 else
509 f.flags |= IF_MULTIACCESS; /* NBMA */
3216eb03 510
16a3254c
OZ
511 if (fl & IFF_MULTICAST)
512 f.flags |= IF_MULTICAST;
513
3216eb03
OZ
514 ifi = if_update(&f);
515
516 if (!scan)
517 if_end_partial_update(ifi);
95616c82
OZ
518 }
519}
520
521static void
3216eb03 522nl_parse_addr(struct nlmsghdr *h, int scan)
95616c82
OZ
523{
524 struct ifaddrmsg *i;
525 struct rtattr *a[IFA_ANYCAST+1];
526 int new = h->nlmsg_type == RTM_NEWADDR;
527 struct ifa ifa;
528 struct iface *ifi;
529 int scope;
530
531 if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFA_RTA(i), a, sizeof(a)))
532 return;
533 if (i->ifa_family != BIRD_AF)
534 return;
535 if (!a[IFA_ADDRESS] || RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip_addr)
536#ifdef IPV6
537 || a[IFA_LOCAL] && RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)
538#else
539 || !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)
540 || (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr))
541#endif
542 )
543 {
544 log(L_ERR "nl_parse_addr: Malformed message received");
545 return;
546 }
547
548 ifi = if_find_by_index(i->ifa_index);
549 if (!ifi)
550 {
551 log(L_ERR "KIF: Received address message for unknown interface %d", i->ifa_index);
552 return;
553 }
554
555 bzero(&ifa, sizeof(ifa));
556 ifa.iface = ifi;
557 if (i->ifa_flags & IFA_F_SECONDARY)
558 ifa.flags |= IA_SECONDARY;
559
560 /* IFA_LOCAL can be unset for IPv6 interfaces */
561 memcpy(&ifa.ip, RTA_DATA(a[IFA_LOCAL] ? : a[IFA_ADDRESS]), sizeof(ifa.ip));
562 ipa_ntoh(ifa.ip);
563 ifa.pxlen = i->ifa_prefixlen;
564 if (i->ifa_prefixlen > BITS_PER_IP_ADDRESS)
565 {
566 log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen);
567 new = 0;
568 }
569 if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS)
570 {
571 ip_addr addr;
572 memcpy(&addr, RTA_DATA(a[IFA_ADDRESS]), sizeof(addr));
573 ipa_ntoh(addr);
574 ifa.prefix = ifa.brd = addr;
575
576 /* It is either a host address or a peer address */
577 if (ipa_equal(ifa.ip, addr))
578 ifa.flags |= IA_HOST;
579 else
580 {
581 ifa.flags |= IA_PEER;
582 ifa.opposite = addr;
583 }
584 }
585 else
586 {
587 ip_addr netmask = ipa_mkmask(ifa.pxlen);
588 ifa.prefix = ipa_and(ifa.ip, netmask);
589 ifa.brd = ipa_or(ifa.ip, ipa_not(netmask));
590 if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 1)
591 ifa.opposite = ipa_opposite_m1(ifa.ip);
592
593#ifndef IPV6
594 if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 2)
595 ifa.opposite = ipa_opposite_m2(ifa.ip);
596
597 if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST])
598 {
599 ip_addr xbrd;
600 memcpy(&xbrd, RTA_DATA(a[IFA_BROADCAST]), sizeof(xbrd));
601 ipa_ntoh(xbrd);
602 if (ipa_equal(xbrd, ifa.prefix) || ipa_equal(xbrd, ifa.brd))
603 ifa.brd = xbrd;
604 else if (ifi->flags & IF_TMP_DOWN) /* Complain only during the first scan */
605 log(L_ERR "KIF: Invalid broadcast address %I for %s", xbrd, ifi->name);
606 }
607#endif
608 }
609
610 scope = ipa_classify(ifa.ip);
611 if (scope < 0)
612 {
613 log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, ifi->name);
614 return;
615 }
616 ifa.scope = scope & IADDR_SCOPE_MASK;
617
618 DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %I/%d, brd %I, opp %I\n",
619 ifi->index, ifi->name,
620 new ? "added" : "removed",
621 ifa.ip, ifa.flags, ifa.prefix, ifa.pxlen, ifa.brd, ifa.opposite);
3216eb03 622
95616c82
OZ
623 if (new)
624 ifa_update(&ifa);
625 else
626 ifa_delete(&ifa);
3216eb03
OZ
627
628 if (!scan)
629 if_end_partial_update(ifi);
95616c82
OZ
630}
631
632void
633kif_do_scan(struct kif_proto *p UNUSED)
634{
635 struct nlmsghdr *h;
636
637 if_start_update();
638
86c3eea0 639 nl_request_dump(AF_UNSPEC, RTM_GETLINK);
95616c82
OZ
640 while (h = nl_get_scan())
641 if (h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)
642 nl_parse_link(h, 1);
643 else
644 log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
645
86c3eea0 646 nl_request_dump(BIRD_AF, RTM_GETADDR);
95616c82
OZ
647 while (h = nl_get_scan())
648 if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
3216eb03 649 nl_parse_addr(h, 1);
95616c82
OZ
650 else
651 log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
652
653 if_end_update();
654}
655
656/*
657 * Routes
658 */
659
9ddbfbdd
JMM
660static inline u32
661krt_table_id(struct krt_proto *p)
662{
663 return KRT_CF->sys.table_id;
664}
665
666static HASH(struct krt_proto) nl_table_map;
667
668#define RTH_FN(k) u32_hash(k)
669#define RTH_EQ(k1,k2) k1 == k2
670#define RTH_KEY(p) krt_table_id(p)
671#define RTH_NEXT(p) p->sys.hash_next
672
673#define RTH_REHASH rth_rehash
674#define RTH_PARAMS /8, *2, 2, 2, 6, 20
675
676HASH_DEFINE_REHASH_FN(RTH, struct krt_proto)
95616c82
OZ
677
678int
679krt_capable(rte *e)
680{
681 rta *a = e->attrs;
682
683 if (a->cast != RTC_UNICAST)
684 return 0;
685
686 switch (a->dest)
687 {
688 case RTD_ROUTER:
689 case RTD_DEVICE:
690 if (a->iface == NULL)
691 return 0;
692 case RTD_BLACKHOLE:
693 case RTD_UNREACHABLE:
694 case RTD_PROHIBIT:
695 case RTD_MULTIPATH:
696 break;
697 default:
698 return 0;
699 }
700 return 1;
701}
702
703static inline int
704nh_bufsize(struct mpnh *nh)
705{
706 int rv = 0;
707 for (; nh != NULL; nh = nh->next)
9fdf9d29 708 rv += RTNH_LENGTH(RTA_LENGTH(sizeof(ip_addr)));
95616c82
OZ
709 return rv;
710}
711
712static int
713nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
714{
715 eattr *ea;
716 net *net = e->net;
717 rta *a = e->attrs;
718 struct {
719 struct nlmsghdr h;
720 struct rtmsg r;
9fdf9d29 721 char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)];
95616c82
OZ
722 } r;
723
724 DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new);
725
726 bzero(&r.h, sizeof(r.h));
727 bzero(&r.r, sizeof(r.r));
728 r.h.nlmsg_type = new ? RTM_NEWROUTE : RTM_DELROUTE;
729 r.h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
730 r.h.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (new ? NLM_F_CREATE|NLM_F_EXCL : 0);
731
732 r.r.rtm_family = BIRD_AF;
733 r.r.rtm_dst_len = net->n.pxlen;
95616c82
OZ
734 r.r.rtm_protocol = RTPROT_BIRD;
735 r.r.rtm_scope = RT_SCOPE_UNIVERSE;
736 nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
737
9ddbfbdd
JMM
738 if (krt_table_id(p) < 256)
739 r.r.rtm_table = krt_table_id(p);
740 else
741 nl_add_attr_u32(&r.h, sizeof(r), RTA_TABLE, krt_table_id(p));
742
78a2cc28
OZ
743 /* For route delete, we do not specify route attributes */
744 if (!new)
745 return nl_exchange(&r.h);
746
747
95616c82 748 if (ea = ea_find(eattrs, EA_KRT_METRIC))
9fdf9d29 749 nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data);
95616c82
OZ
750
751 if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
752 nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
753
754 if (ea = ea_find(eattrs, EA_KRT_REALM))
755 nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
756
9fdf9d29
OZ
757
758 u32 metrics[KRT_METRICS_MAX];
759 metrics[0] = 0;
760
761 struct ea_walk_state ews = { .eattrs = eattrs };
762 while (ea = ea_walk(&ews, EA_KRT_METRICS, KRT_METRICS_MAX))
763 {
764 int id = ea->id - EA_KRT_METRICS;
765 metrics[0] |= 1 << id;
766 metrics[id] = ea->u.data;
767 }
768
769 if (metrics[0])
770 nl_add_metrics(&r.h, sizeof(r), metrics, KRT_METRICS_MAX);
771
772
95616c82
OZ
773 /* a->iface != NULL checked in krt_capable() for router and device routes */
774
775 switch (a->dest)
776 {
777 case RTD_ROUTER:
778 r.r.rtm_type = RTN_UNICAST;
779 nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, a->iface->index);
780 nl_add_attr_ipa(&r.h, sizeof(r), RTA_GATEWAY, a->gw);
781 break;
782 case RTD_DEVICE:
783 r.r.rtm_type = RTN_UNICAST;
784 nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, a->iface->index);
785 break;
786 case RTD_BLACKHOLE:
787 r.r.rtm_type = RTN_BLACKHOLE;
788 break;
789 case RTD_UNREACHABLE:
790 r.r.rtm_type = RTN_UNREACHABLE;
791 break;
792 case RTD_PROHIBIT:
793 r.r.rtm_type = RTN_PROHIBIT;
794 break;
795 case RTD_MULTIPATH:
796 r.r.rtm_type = RTN_UNICAST;
797 nl_add_multipath(&r.h, sizeof(r), a->nexthops);
798 break;
799 default:
800 bug("krt_capable inconsistent with nl_send_route");
801 }
802
803 return nl_exchange(&r.h);
804}
805
806void
807krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs)
808{
809 int err = 0;
810
811 /*
812 * NULL for eattr of the old route is a little hack, but we don't
813 * get proper eattrs for old in rt_notify() anyway. NULL means no
814 * extended route attributes and therefore matches if the kernel
815 * route has any of them.
816 */
817
818 if (old)
819 nl_send_route(p, old, NULL, 0);
820
821 if (new)
822 err = nl_send_route(p, new, eattrs, 1);
823
824 if (err < 0)
825 n->n.flags |= KRF_SYNC_ERROR;
826 else
827 n->n.flags &= ~KRF_SYNC_ERROR;
828}
829
830
831#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)
832
833static void
834nl_parse_route(struct nlmsghdr *h, int scan)
835{
836 struct krt_proto *p;
837 struct rtmsg *i;
9ddbfbdd 838 struct rtattr *a[RTA_TABLE+1];
95616c82
OZ
839 int new = h->nlmsg_type == RTM_NEWROUTE;
840
841 ip_addr dst = IPA_NONE;
842 u32 oif = ~0;
9ddbfbdd 843 u32 table;
95616c82
OZ
844 int src;
845
846 if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a)))
847 return;
848 if (i->rtm_family != BIRD_AF)
849 return;
850 if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) ||
851#ifdef IPV6
852 (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) ||
853#endif
854 (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) ||
9ddbfbdd 855 (a[RTA_TABLE] && RTA_PAYLOAD(a[RTA_TABLE]) != 4) ||
95616c82
OZ
856 (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) ||
857 (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
858 (a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) ||
c06de722 859 (a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_FLOW]) != 4))
95616c82
OZ
860 {
861 log(L_ERR "KRT: Malformed message received");
862 return;
863 }
864
865 if (a[RTA_DST])
866 {
867 memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst));
868 ipa_ntoh(dst);
869 }
870
871 if (a[RTA_OIF])
acb04cfd 872 oif = rta_get_u32(a[RTA_OIF]);
95616c82 873
9ddbfbdd
JMM
874 if (a[RTA_TABLE])
875 table = rta_get_u32(a[RTA_TABLE]);
876 else
877 table = i->rtm_table;
878
879 p = HASH_FIND(nl_table_map, RTH, table); /* Do we know this table? */
880 DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, table, i->rtm_protocol, p ? p->p.name : "(none)");
95616c82 881 if (!p)
9ddbfbdd 882 SKIP("unknown table %d\n", table);
95616c82
OZ
883
884
885#ifdef IPV6
886 if (a[RTA_IIF])
887 SKIP("IIF set\n");
888#else
889 if (i->rtm_tos != 0) /* We don't support TOS */
890 SKIP("TOS %02x\n", i->rtm_tos);
891#endif
892
893 if (scan && !new)
894 SKIP("RTM_DELROUTE in scan\n");
895
896 int c = ipa_classify_net(dst);
897 if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
898 SKIP("strange class/scope\n");
899
900 // ignore rtm_scope, it is not a real scope
901 // if (i->rtm_scope != RT_SCOPE_UNIVERSE)
902 // SKIP("scope %u\n", i->rtm_scope);
903
904 switch (i->rtm_protocol)
905 {
906 case RTPROT_UNSPEC:
907 SKIP("proto unspec\n");
908
909 case RTPROT_REDIRECT:
910 src = KRT_SRC_REDIRECT;
911 break;
912
913 case RTPROT_KERNEL:
914 src = KRT_SRC_KERNEL;
915 return;
916
917 case RTPROT_BIRD:
918 if (!scan)
919 SKIP("echo\n");
920 src = KRT_SRC_BIRD;
921 break;
922
923 case RTPROT_BOOT:
924 default:
925 src = KRT_SRC_ALIEN;
926 }
927
928 net *net = net_get(p->p.table, dst, i->rtm_dst_len);
929
930 rta ra = {
094d2bdb 931 .src= p->p.main_source,
95616c82
OZ
932 .source = RTS_INHERIT,
933 .scope = SCOPE_UNIVERSE,
934 .cast = RTC_UNICAST
935 };
936
937 switch (i->rtm_type)
938 {
939 case RTN_UNICAST:
940
941 if (a[RTA_MULTIPATH])
942 {
943 ra.dest = RTD_MULTIPATH;
944 ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]);
945 if (!ra.nexthops)
946 {
947 log(L_ERR "KRT: Received strange multipath route %I/%d",
948 net->n.prefix, net->n.pxlen);
949 return;
950 }
9fdf9d29 951
95616c82
OZ
952 break;
953 }
954
955 ra.iface = if_find_by_index(oif);
956 if (!ra.iface)
957 {
958 log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u",
959 net->n.prefix, net->n.pxlen, oif);
960 return;
961 }
962
963 if (a[RTA_GATEWAY])
964 {
965 neighbor *ng;
966 ra.dest = RTD_ROUTER;
967 memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw));
968 ipa_ntoh(ra.gw);
969
9810d055 970#ifdef IPV6
95616c82
OZ
971 /* Silently skip strange 6to4 routes */
972 if (ipa_in_net(ra.gw, IPA_NONE, 96))
973 return;
9810d055 974#endif
95616c82
OZ
975
976 ng = neigh_find2(&p->p, &ra.gw, ra.iface,
977 (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
978 if (!ng || (ng->scope == SCOPE_HOST))
979 {
980 log(L_ERR "KRT: Received route %I/%d with strange next-hop %I",
981 net->n.prefix, net->n.pxlen, ra.gw);
982 return;
983 }
984 }
985 else
986 {
987 ra.dest = RTD_DEVICE;
95616c82
OZ
988 }
989
990 break;
991 case RTN_BLACKHOLE:
992 ra.dest = RTD_BLACKHOLE;
993 break;
994 case RTN_UNREACHABLE:
995 ra.dest = RTD_UNREACHABLE;
996 break;
997 case RTN_PROHIBIT:
998 ra.dest = RTD_PROHIBIT;
999 break;
1000 /* FIXME: What about RTN_THROW? */
1001 default:
1002 SKIP("type %d\n", i->rtm_type);
1003 return;
1004 }
1005
1006 rte *e = rte_get_temp(&ra);
1007 e->net = net;
1008 e->u.krt.src = src;
1009 e->u.krt.proto = i->rtm_protocol;
1010 e->u.krt.type = i->rtm_type;
acb04cfd 1011 e->u.krt.metric = 0;
95616c82
OZ
1012
1013 if (a[RTA_PRIORITY])
acb04cfd 1014 e->u.krt.metric = rta_get_u32(a[RTA_PRIORITY]);
95616c82
OZ
1015
1016 if (a[RTA_PREFSRC])
1017 {
1018 ip_addr ps;
1019 memcpy(&ps, RTA_DATA(a[RTA_PREFSRC]), sizeof(ps));
1020 ipa_ntoh(ps);
1021
1022 ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr));
1023 ea->next = ra.eattrs;
1024 ra.eattrs = ea;
1025 ea->flags = EALF_SORTED;
1026 ea->count = 1;
1027 ea->attrs[0].id = EA_KRT_PREFSRC;
1028 ea->attrs[0].flags = 0;
1029 ea->attrs[0].type = EAF_TYPE_IP_ADDRESS;
1030 ea->attrs[0].u.ptr = alloca(sizeof(struct adata) + sizeof(ps));
1031 ea->attrs[0].u.ptr->length = sizeof(ps);
1032 memcpy(ea->attrs[0].u.ptr->data, &ps, sizeof(ps));
1033 }
1034
1035 if (a[RTA_FLOW])
1036 {
1037 ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr));
1038 ea->next = ra.eattrs;
1039 ra.eattrs = ea;
1040 ea->flags = EALF_SORTED;
1041 ea->count = 1;
1042 ea->attrs[0].id = EA_KRT_REALM;
1043 ea->attrs[0].flags = 0;
1044 ea->attrs[0].type = EAF_TYPE_INT;
acb04cfd 1045 ea->attrs[0].u.data = rta_get_u32(a[RTA_FLOW]);
95616c82
OZ
1046 }
1047
9fdf9d29
OZ
1048 if (a[RTA_METRICS])
1049 {
1050 u32 metrics[KRT_METRICS_MAX];
1051 ea_list *ea = alloca(sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr));
1052 int t, n = 0;
1053
1054 if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
1055 {
1056 log(L_ERR "KRT: Received route %I/%d with strange RTA_METRICS attribute",
1057 net->n.prefix, net->n.pxlen);
1058 return;
1059 }
1060
1061 for (t = 1; t < KRT_METRICS_MAX; t++)
1062 if (metrics[0] & (1 << t))
1063 {
1064 ea->attrs[n].id = EA_CODE(EAP_KRT, KRT_METRICS_OFFSET + t);
1065 ea->attrs[n].flags = 0;
1066 ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
1067 ea->attrs[n].u.data = metrics[t];
1068 n++;
1069 }
1070
1071 if (n > 0)
1072 {
1073 ea->next = ra.eattrs;
1074 ea->flags = EALF_SORTED;
1075 ea->count = n;
1076 ra.eattrs = ea;
1077 }
1078 }
1079
95616c82
OZ
1080 if (scan)
1081 krt_got_route(p, e);
1082 else
1083 krt_got_route_async(p, e, new);
1084}
1085
1086void
1087krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */
1088{
1089 struct nlmsghdr *h;
1090
86c3eea0 1091 nl_request_dump(BIRD_AF, RTM_GETROUTE);
95616c82
OZ
1092 while (h = nl_get_scan())
1093 if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
1094 nl_parse_route(h, 1);
1095 else
1096 log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
1097}
1098
1099/*
1100 * Asynchronous Netlink interface
1101 */
1102
1103static sock *nl_async_sk; /* BIRD socket for asynchronous notifications */
1104static byte *nl_async_rx_buffer; /* Receive buffer */
1105
1106static void
1107nl_async_msg(struct nlmsghdr *h)
1108{
1109 switch (h->nlmsg_type)
1110 {
1111 case RTM_NEWROUTE:
1112 case RTM_DELROUTE:
1113 DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
1114 nl_parse_route(h, 0);
1115 break;
1116 case RTM_NEWLINK:
1117 case RTM_DELLINK:
1118 DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type);
1e4891e4
OZ
1119 if (kif_proto)
1120 nl_parse_link(h, 0);
95616c82
OZ
1121 break;
1122 case RTM_NEWADDR:
1123 case RTM_DELADDR:
1124 DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type);
1e4891e4
OZ
1125 if (kif_proto)
1126 nl_parse_addr(h, 0);
95616c82
OZ
1127 break;
1128 default:
1129 DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type);
1130 }
1131}
1132
1133static int
1134nl_async_hook(sock *sk, int size UNUSED)
1135{
1136 struct iovec iov = { nl_async_rx_buffer, NL_RX_SIZE };
1137 struct sockaddr_nl sa;
1138 struct msghdr m = { (struct sockaddr *) &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
1139 struct nlmsghdr *h;
1140 int x;
ae80a2de 1141 uint len;
95616c82
OZ
1142
1143 x = recvmsg(sk->fd, &m, 0);
1144 if (x < 0)
1145 {
1146 if (errno == ENOBUFS)
1147 {
1148 /*
1149 * Netlink reports some packets have been thrown away.
1150 * One day we might react to it by asking for route table
1151 * scan in near future.
1152 */
1153 return 1; /* More data are likely to be ready */
1154 }
1155 else if (errno != EWOULDBLOCK)
1156 log(L_ERR "Netlink recvmsg: %m");
1157 return 0;
1158 }
1159 if (sa.nl_pid) /* It isn't from the kernel */
1160 {
1161 DBG("Non-kernel packet\n");
1162 return 1;
1163 }
1164 h = (void *) nl_async_rx_buffer;
1165 len = x;
1166 if (m.msg_flags & MSG_TRUNC)
1167 {
1168 log(L_WARN "Netlink got truncated asynchronous message");
1169 return 1;
1170 }
1171 while (NLMSG_OK(h, len))
1172 {
1173 nl_async_msg(h);
1174 h = NLMSG_NEXT(h, len);
1175 }
1176 if (len)
1177 log(L_WARN "nl_async_hook: Found packet remnant of size %d", len);
1178 return 1;
1179}
1180
1181static void
1182nl_open_async(void)
1183{
1184 sock *sk;
1185 struct sockaddr_nl sa;
1186 int fd;
95616c82 1187
f83ce94d 1188 if (nl_async_sk)
95616c82 1189 return;
95616c82
OZ
1190
1191 DBG("KRT: Opening async netlink socket\n");
1192
1193 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1194 if (fd < 0)
1195 {
1196 log(L_ERR "Unable to open asynchronous rtnetlink socket: %m");
1197 return;
1198 }
1199
1200 bzero(&sa, sizeof(sa));
1201 sa.nl_family = AF_NETLINK;
1202#ifdef IPV6
1203 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
1204#else
1205 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
1206#endif
1207 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
1208 {
1209 log(L_ERR "Unable to bind asynchronous rtnetlink socket: %m");
f83ce94d 1210 close(fd);
95616c82
OZ
1211 return;
1212 }
1213
f83ce94d
OZ
1214 nl_async_rx_buffer = xmalloc(NL_RX_SIZE);
1215
95616c82
OZ
1216 sk = nl_async_sk = sk_new(krt_pool);
1217 sk->type = SK_MAGIC;
1218 sk->rx_hook = nl_async_hook;
1219 sk->fd = fd;
05476c4d 1220 if (sk_open(sk) < 0)
95616c82 1221 bug("Netlink: sk_open failed");
95616c82
OZ
1222}
1223
9ddbfbdd 1224
95616c82
OZ
1225/*
1226 * Interface to the UNIX krt module
1227 */
1228
95616c82 1229void
9ddbfbdd
JMM
1230krt_sys_io_init(void)
1231{
1232 HASH_INIT(nl_table_map, krt_pool, 6);
1233}
1234
1235int
c6964c30 1236krt_sys_start(struct krt_proto *p)
95616c82 1237{
9ddbfbdd
JMM
1238 struct krt_proto *old = HASH_FIND(nl_table_map, RTH, krt_table_id(p));
1239
1240 if (old)
1241 {
1242 log(L_ERR "%s: Kernel table %u already registered by %s",
1243 p->p.name, krt_table_id(p), old->p.name);
1244 return 0;
1245 }
1246
1247 HASH_INSERT2(nl_table_map, RTH, krt_pool, p);
c6964c30
OZ
1248
1249 nl_open();
1250 nl_open_async();
9ddbfbdd
JMM
1251
1252 return 1;
95616c82
OZ
1253}
1254
1255void
9ddbfbdd 1256krt_sys_shutdown(struct krt_proto *p)
95616c82 1257{
9ddbfbdd 1258 HASH_REMOVE2(nl_table_map, RTH, krt_pool, p);
95616c82
OZ
1259}
1260
1261int
1262krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o)
1263{
1264 return n->sys.table_id == o->sys.table_id;
1265}
1266
95616c82
OZ
1267void
1268krt_sys_init_config(struct krt_config *cf)
1269{
1270 cf->sys.table_id = RT_TABLE_MAIN;
1271}
1272
1273void
1274krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
1275{
1276 d->sys.table_id = s->sys.table_id;
1277}
1278
9fdf9d29
OZ
1279static const char *krt_metrics_names[KRT_METRICS_MAX] = {
1280 NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss",
1281 "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack"
1282};
1283
1284static const char *krt_features_names[KRT_FEATURES_MAX] = {
1285 "ecn", NULL, NULL, "allfrag"
1286};
1287
1288int
1289krt_sys_get_attr(eattr *a, byte *buf, int buflen UNUSED)
1290{
1291 switch (a->id)
1292 {
1293 case EA_KRT_PREFSRC:
1294 bsprintf(buf, "prefsrc");
1295 return GA_NAME;
1296
1297 case EA_KRT_REALM:
1298 bsprintf(buf, "realm");
1299 return GA_NAME;
1300
1301 case EA_KRT_LOCK:
1302 buf += bsprintf(buf, "lock:");
1303 ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);
1304 return GA_FULL;
1305
1306 case EA_KRT_FEATURES:
1307 buf += bsprintf(buf, "features:");
1308 ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX);
1309 return GA_FULL;
1310
1311 default:;
1312 int id = (int)EA_ID(a->id) - KRT_METRICS_OFFSET;
1313 if (id > 0 && id < KRT_METRICS_MAX)
1314 {
1315 bsprintf(buf, "%s", krt_metrics_names[id]);
1316 return GA_NAME;
1317 }
1318
1319 return GA_UNKNOWN;
1320 }
1321}
1322
95616c82
OZ
1323
1324
1325void
1326kif_sys_start(struct kif_proto *p UNUSED)
1327{
1328 nl_open();
1329 nl_open_async();
1330}
1331
1332void
1333kif_sys_shutdown(struct kif_proto *p UNUSED)
1334{
1335}