]> git.ipfire.org Git - thirdparty/bird.git/blame - sysdep/linux/netlink.c
Static: Protocol rework wrt. struct nexthop changes; MPLS label support
[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
a8caff32 9#include <alloca.h>
95616c82 10#include <stdio.h>
f83ce94d 11#include <unistd.h>
95616c82
OZ
12#include <fcntl.h>
13#include <sys/socket.h>
14#include <sys/uio.h>
15#include <errno.h>
16
17#undef LOCAL_DEBUG
18
19#include "nest/bird.h"
20#include "nest/route.h"
21#include "nest/protocol.h"
22#include "nest/iface.h"
4e276a89 23#include "lib/alloca.h"
7152e5ef
JMM
24#include "sysdep/unix/timer.h"
25#include "sysdep/unix/unix.h"
26#include "sysdep/unix/krt.h"
95616c82
OZ
27#include "lib/socket.h"
28#include "lib/string.h"
9ddbfbdd 29#include "lib/hash.h"
95616c82
OZ
30#include "conf/conf.h"
31
32#include <asm/types.h>
33#include <linux/if.h>
34#include <linux/netlink.h>
35#include <linux/rtnetlink.h>
36
9ddbfbdd 37
95616c82
OZ
38#ifndef MSG_TRUNC /* Hack: Several versions of glibc miss this one :( */
39#define MSG_TRUNC 0x20
40#endif
41
a08a81c6
OZ
42#ifndef IFA_FLAGS
43#define IFA_FLAGS 8
44#endif
45
95616c82
OZ
46#ifndef IFF_LOWER_UP
47#define IFF_LOWER_UP 0x10000
48#endif
49
9ddbfbdd
JMM
50#ifndef RTA_TABLE
51#define RTA_TABLE 15
52#endif
53
54
cc5b93f7 55#define krt_ecmp6(p) ((p)->af == AF_INET6)
2feaa693
OZ
56
57/*
58 * Structure nl_parse_state keeps state of received route processing. Ideally,
59 * we could just independently parse received Netlink messages and immediately
60 * propagate received routes to the rest of BIRD, but Linux kernel represents
61 * and announces IPv6 ECMP routes not as one route with multiple next hops (like
62 * RTA_MULTIPATH in IPv4 ECMP), but as a set of routes with the same prefix.
63 *
64 * Therefore, BIRD keeps currently processed route in nl_parse_state structure
65 * and postpones its propagation until we expect it to be final; i.e., when
66 * non-matching route is received or when the scan ends. When another matching
67 * route is received, it is merged with the already processed route to form an
68 * ECMP route. Note that merging is done only for IPv6 (merge == 1), but the
69 * postponing is done in both cases (for simplicity). All IPv4 routes are just
70 * considered non-matching.
71 *
72 * This is ignored for asynchronous notifications (every notification is handled
73 * as a separate route). It is not an issue for our routes, as we ignore such
74 * notifications anyways. But importing alien IPv6 ECMP routes does not work
75 * properly.
76 */
77
78struct nl_parse_state
79{
80 struct linpool *pool;
81 int scan;
82 int merge;
83
84 net *net;
85 rta *attrs;
86 struct krt_proto *proto;
87 s8 new;
88 s8 krt_src;
89 u8 krt_type;
90 u8 krt_proto;
91 u32 krt_metric;
92};
93
95616c82
OZ
94/*
95 * Synchronous Netlink interface
96 */
97
98struct nl_sock
99{
100 int fd;
101 u32 seq;
102 byte *rx_buffer; /* Receive buffer */
103 struct nlmsghdr *last_hdr; /* Recently received packet */
ae80a2de 104 uint last_size;
95616c82
OZ
105};
106
107#define NL_RX_SIZE 8192
108
2feaa693
OZ
109#define NL_OP_DELETE 0
110#define NL_OP_ADD (NLM_F_CREATE|NLM_F_EXCL)
111#define NL_OP_REPLACE (NLM_F_CREATE|NLM_F_REPLACE)
112#define NL_OP_APPEND (NLM_F_CREATE|NLM_F_APPEND)
113
114static linpool *nl_linpool;
115
95616c82
OZ
116static struct nl_sock nl_scan = {.fd = -1}; /* Netlink socket for synchronous scan */
117static struct nl_sock nl_req = {.fd = -1}; /* Netlink socket for requests */
118
119static void
120nl_open_sock(struct nl_sock *nl)
121{
122 if (nl->fd < 0)
123 {
124 nl->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
125 if (nl->fd < 0)
126 die("Unable to open rtnetlink socket: %m");
127 nl->seq = now;
128 nl->rx_buffer = xmalloc(NL_RX_SIZE);
129 nl->last_hdr = NULL;
130 nl->last_size = 0;
131 }
132}
133
134static void
135nl_open(void)
136{
137 nl_open_sock(&nl_scan);
138 nl_open_sock(&nl_req);
139}
140
141static void
142nl_send(struct nl_sock *nl, struct nlmsghdr *nh)
143{
144 struct sockaddr_nl sa;
145
146 memset(&sa, 0, sizeof(sa));
147 sa.nl_family = AF_NETLINK;
148 nh->nlmsg_pid = 0;
149 nh->nlmsg_seq = ++(nl->seq);
150 if (sendto(nl->fd, nh, nh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0)
151 die("rtnetlink sendto: %m");
152 nl->last_hdr = NULL;
153}
154
155static void
86c3eea0 156nl_request_dump(int af, int cmd)
95616c82
OZ
157{
158 struct {
159 struct nlmsghdr nh;
160 struct rtgenmsg g;
641172c6
OZ
161 } req = {
162 .nh.nlmsg_type = cmd,
163 .nh.nlmsg_len = sizeof(req),
164 .nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
165 .g.rtgen_family = af
166 };
95616c82
OZ
167 nl_send(&nl_scan, &req.nh);
168}
169
170static struct nlmsghdr *
171nl_get_reply(struct nl_sock *nl)
172{
173 for(;;)
174 {
175 if (!nl->last_hdr)
176 {
177 struct iovec iov = { nl->rx_buffer, NL_RX_SIZE };
178 struct sockaddr_nl sa;
31e9e101
ST
179 struct msghdr m = {
180 .msg_name = &sa,
181 .msg_namelen = sizeof(sa),
182 .msg_iov = &iov,
183 .msg_iovlen = 1,
184 };
95616c82
OZ
185 int x = recvmsg(nl->fd, &m, 0);
186 if (x < 0)
187 die("nl_get_reply: %m");
188 if (sa.nl_pid) /* It isn't from the kernel */
189 {
190 DBG("Non-kernel packet\n");
191 continue;
192 }
193 nl->last_size = x;
194 nl->last_hdr = (void *) nl->rx_buffer;
195 if (m.msg_flags & MSG_TRUNC)
196 bug("nl_get_reply: got truncated reply which should be impossible");
197 }
198 if (NLMSG_OK(nl->last_hdr, nl->last_size))
199 {
200 struct nlmsghdr *h = nl->last_hdr;
201 nl->last_hdr = NLMSG_NEXT(h, nl->last_size);
202 if (h->nlmsg_seq != nl->seq)
203 {
204 log(L_WARN "nl_get_reply: Ignoring out of sequence netlink packet (%x != %x)",
205 h->nlmsg_seq, nl->seq);
206 continue;
207 }
208 return h;
209 }
210 if (nl->last_size)
211 log(L_WARN "nl_get_reply: Found packet remnant of size %d", nl->last_size);
212 nl->last_hdr = NULL;
213 }
214}
215
1123e707 216static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS;
95616c82
OZ
217
218static int
2feaa693 219nl_error(struct nlmsghdr *h, int ignore_esrch)
95616c82
OZ
220{
221 struct nlmsgerr *e;
222 int ec;
223
224 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
225 {
226 log(L_WARN "Netlink: Truncated error message received");
227 return ENOBUFS;
228 }
229 e = (struct nlmsgerr *) NLMSG_DATA(h);
230 ec = -e->error;
2feaa693 231 if (ec && !(ignore_esrch && (ec == ESRCH)))
95616c82
OZ
232 log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec));
233 return ec;
234}
235
236static struct nlmsghdr *
237nl_get_scan(void)
238{
239 struct nlmsghdr *h = nl_get_reply(&nl_scan);
240
241 if (h->nlmsg_type == NLMSG_DONE)
242 return NULL;
243 if (h->nlmsg_type == NLMSG_ERROR)
244 {
2feaa693 245 nl_error(h, 0);
95616c82
OZ
246 return NULL;
247 }
248 return h;
249}
250
251static int
2feaa693 252nl_exchange(struct nlmsghdr *pkt, int ignore_esrch)
95616c82
OZ
253{
254 struct nlmsghdr *h;
255
256 nl_send(&nl_req, pkt);
257 for(;;)
258 {
259 h = nl_get_reply(&nl_req);
260 if (h->nlmsg_type == NLMSG_ERROR)
261 break;
262 log(L_WARN "nl_exchange: Unexpected reply received");
263 }
2feaa693 264 return nl_error(h, ignore_esrch) ? -1 : 0;
95616c82
OZ
265}
266
267/*
268 * Netlink attributes
269 */
270
271static int nl_attr_len;
272
273static void *
274nl_checkin(struct nlmsghdr *h, int lsize)
275{
276 nl_attr_len = h->nlmsg_len - NLMSG_LENGTH(lsize);
277 if (nl_attr_len < 0)
278 {
279 log(L_ERR "nl_checkin: underrun by %d bytes", -nl_attr_len);
280 return NULL;
281 }
282 return NLMSG_DATA(h);
283}
284
ad276157
JMM
285struct nl_want_attrs {
286 u8 defined:1;
287 u8 checksize:1;
288 u8 size;
289};
290
291
292#define BIRD_IFLA_MAX (IFLA_WIRELESS+1)
293
294static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = {
295 [IFLA_IFNAME] = { 1, 0, 0 },
296 [IFLA_MTU] = { 1, 1, sizeof(u32) },
297 [IFLA_WIRELESS] = { 1, 0, 0 },
298};
299
29a64162 300
e37d2e3e 301#define BIRD_IFA_MAX (IFA_FLAGS+1)
ad276157 302
ad276157
JMM
303static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = {
304 [IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) },
305 [IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) },
306 [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) },
307};
29a64162 308
ad276157
JMM
309static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
310 [IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) },
311 [IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) },
e37d2e3e 312 [IFA_FLAGS] = { 1, 1, sizeof(u32) },
ad276157 313};
29a64162 314
ad276157 315
ad276157
JMM
316#define BIRD_RTA_MAX (RTA_TABLE+1)
317
4e276a89 318static struct nl_want_attrs nexthop_attr_want4[BIRD_RTA_MAX] = {
ad276157
JMM
319 [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) },
320};
321
ad276157
JMM
322static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
323 [RTA_DST] = { 1, 1, sizeof(ip4_addr) },
324 [RTA_OIF] = { 1, 1, sizeof(u32) },
325 [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) },
326 [RTA_PRIORITY] = { 1, 1, sizeof(u32) },
327 [RTA_PREFSRC] = { 1, 1, sizeof(ip4_addr) },
328 [RTA_METRICS] = { 1, 0, 0 },
329 [RTA_MULTIPATH] = { 1, 0, 0 },
330 [RTA_FLOW] = { 1, 1, sizeof(u32) },
331 [RTA_TABLE] = { 1, 1, sizeof(u32) },
332};
29a64162 333
ad276157
JMM
334static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
335 [RTA_DST] = { 1, 1, sizeof(ip6_addr) },
336 [RTA_IIF] = { 1, 1, sizeof(u32) },
337 [RTA_OIF] = { 1, 1, sizeof(u32) },
338 [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) },
339 [RTA_PRIORITY] = { 1, 1, sizeof(u32) },
340 [RTA_PREFSRC] = { 1, 1, sizeof(ip6_addr) },
341 [RTA_METRICS] = { 1, 0, 0 },
342 [RTA_FLOW] = { 1, 1, sizeof(u32) },
343 [RTA_TABLE] = { 1, 1, sizeof(u32) },
344};
ad276157
JMM
345
346
95616c82 347static int
ad276157 348nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize)
95616c82
OZ
349{
350 int max = ksize / sizeof(struct rtattr *);
351 bzero(k, ksize);
ad276157
JMM
352
353 for ( ; RTA_OK(a, nl_attr_len); a = RTA_NEXT(a, nl_attr_len))
95616c82 354 {
ad276157
JMM
355 if ((a->rta_type >= max) || !want[a->rta_type].defined)
356 continue;
357
358 if (want[a->rta_type].checksize && (RTA_PAYLOAD(a) != want[a->rta_type].size))
359 {
9b136840 360 log(L_ERR "nl_parse_attrs: Malformed attribute received");
ad276157
JMM
361 return 0;
362 }
363
364 k[a->rta_type] = a;
95616c82 365 }
ad276157 366
95616c82
OZ
367 if (nl_attr_len)
368 {
369 log(L_ERR "nl_parse_attrs: remnant of size %d", nl_attr_len);
370 return 0;
371 }
ad276157
JMM
372
373 return 1;
95616c82
OZ
374}
375
fce764f9 376static inline u32 rta_get_u32(struct rtattr *a)
acb04cfd
OZ
377{ return *(u32 *) RTA_DATA(a); }
378
379static inline ip4_addr rta_get_ip4(struct rtattr *a)
380{ return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); }
381
382static inline ip6_addr rta_get_ip6(struct rtattr *a)
383{ return ip6_ntoh(*(ip6_addr *) RTA_DATA(a)); }
384
9b136840
JMM
385static inline ip_addr rta_get_ipa(struct rtattr *a)
386{
387 if (RTA_PAYLOAD(a) == sizeof(ip4_addr))
388 return ipa_from_ip4(rta_get_ip4(a));
389 else
390 return ipa_from_ip6(rta_get_ip6(a));
391}
acb04cfd 392
9fdf9d29
OZ
393struct rtattr *
394nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
95616c82 395{
9fdf9d29
OZ
396 uint pos = NLMSG_ALIGN(h->nlmsg_len);
397 uint len = RTA_LENGTH(dlen);
95616c82
OZ
398
399 if (pos + len > bufsize)
400 bug("nl_add_attr: packet buffer overflow");
9fdf9d29
OZ
401
402 struct rtattr *a = (struct rtattr *)((char *)h + pos);
95616c82
OZ
403 a->rta_type = code;
404 a->rta_len = len;
405 h->nlmsg_len = pos + len;
9fdf9d29
OZ
406
407 if (dlen > 0)
408 memcpy(RTA_DATA(a), data, dlen);
409
410 return a;
95616c82
OZ
411}
412
413static inline void
29a64162 414nl_add_attr_u32(struct nlmsghdr *h, uint bufsize, int code, u32 data)
95616c82
OZ
415{
416 nl_add_attr(h, bufsize, code, &data, 4);
417}
418
419static inline void
29a64162 420nl_add_attr_ip4(struct nlmsghdr *h, uint bufsize, int code, ip4_addr ip4)
95616c82 421{
29a64162
OZ
422 ip4 = ip4_hton(ip4);
423 nl_add_attr(h, bufsize, code, &ip4, sizeof(ip4));
424}
425
426static inline void
427nl_add_attr_ip6(struct nlmsghdr *h, uint bufsize, int code, ip6_addr ip6)
428{
429 ip6 = ip6_hton(ip6);
430 nl_add_attr(h, bufsize, code, &ip6, sizeof(ip6));
431}
432
433static inline void
434nl_add_attr_ipa(struct nlmsghdr *h, uint bufsize, int code, ip_addr ipa)
435{
436 if (ipa_is_ip4(ipa))
437 nl_add_attr_ip4(h, bufsize, code, ipa_to_ip4(ipa));
9b136840 438 else
29a64162 439 nl_add_attr_ip6(h, bufsize, code, ipa_to_ip6(ipa));
95616c82
OZ
440}
441
9fdf9d29
OZ
442static inline struct rtattr *
443nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
444{
445 return nl_add_attr(h, bufsize, code, NULL, 0);
446}
95616c82
OZ
447
448static inline void
9fdf9d29 449nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
95616c82 450{
9fdf9d29 451 a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a;
95616c82
OZ
452}
453
9fdf9d29
OZ
454static inline struct rtnexthop *
455nl_open_nexthop(struct nlmsghdr *h, uint bufsize)
456{
457 uint pos = NLMSG_ALIGN(h->nlmsg_len);
458 uint len = RTNH_LENGTH(0);
459
460 if (pos + len > bufsize)
461 bug("nl_open_nexthop: packet buffer overflow");
462
463 h->nlmsg_len = pos + len;
464
465 return (void *)h + pos;
466}
467
468static inline void
469nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
470{
471 nh->rtnh_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)nh;
472}
95616c82
OZ
473
474static void
4e276a89 475nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh)
95616c82 476{
9fdf9d29
OZ
477 struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
478
95616c82 479 for (; nh; nh = nh->next)
9fdf9d29
OZ
480 {
481 struct rtnexthop *rtnh = nl_open_nexthop(h, bufsize);
95616c82 482
9fdf9d29
OZ
483 rtnh->rtnh_flags = 0;
484 rtnh->rtnh_hops = nh->weight;
485 rtnh->rtnh_ifindex = nh->iface->index;
95616c82 486
29a64162 487 nl_add_attr_ipa(h, bufsize, RTA_GATEWAY, nh->gw);
95616c82 488
9fdf9d29
OZ
489 nl_close_nexthop(h, rtnh);
490 }
491
492 nl_close_attr(h, a);
493}
95616c82 494
4e276a89 495static struct nexthop *
95616c82
OZ
496nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
497{
498 /* Temporary buffer for multicast nexthops */
4e276a89 499 static struct nexthop *nh_buffer;
95616c82
OZ
500 static int nh_buf_size; /* in number of structures */
501 static int nh_buf_used;
502
ad276157 503 struct rtattr *a[BIRD_RTA_MAX];
95616c82 504 struct rtnexthop *nh = RTA_DATA(ra);
4e276a89 505 struct nexthop *rv, *first, **last;
3e236955 506 unsigned len = RTA_PAYLOAD(ra);
95616c82
OZ
507
508 first = NULL;
509 last = &first;
510 nh_buf_used = 0;
511
512 while (len)
513 {
514 /* Use RTNH_OK(nh,len) ?? */
515 if ((len < sizeof(*nh)) || (len < nh->rtnh_len))
516 return NULL;
517
518 if (nh_buf_used == nh_buf_size)
519 {
520 nh_buf_size = nh_buf_size ? (nh_buf_size * 2) : 4;
4e276a89 521 nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct nexthop));
95616c82
OZ
522 }
523 *last = rv = nh_buffer + nh_buf_used++;
524 rv->next = NULL;
525 last = &(rv->next);
526
527 rv->weight = nh->rtnh_hops;
528 rv->iface = if_find_by_index(nh->rtnh_ifindex);
529 if (!rv->iface)
530 return NULL;
531
532 /* Nonexistent RTNH_PAYLOAD ?? */
533 nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
4e276a89 534 nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a));
95616c82
OZ
535 if (a[RTA_GATEWAY])
536 {
23c212e7 537 rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
95616c82 538
23c212e7
OZ
539 neighbor *nbr;
540 nbr = neigh_find2(&p->p, &rv->gw, rv->iface,
541 (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
542 if (!nbr || (nbr->scope == SCOPE_HOST))
95616c82
OZ
543 return NULL;
544 }
545 else
546 return NULL;
547
548 len -= NLMSG_ALIGN(nh->rtnh_len);
549 nh = RTNH_NEXT(nh);
550 }
551
552 return first;
553}
554
9fdf9d29
OZ
555static void
556nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max)
557{
558 struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS);
559 int t;
560
561 for (t = 1; t < max; t++)
562 if (metrics[0] & (1 << t))
563 nl_add_attr_u32(h, bufsize, t, metrics[t]);
564
565 nl_close_attr(h, a);
566}
567
568static int
569nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max)
570{
571 struct rtattr *a = RTA_DATA(hdr);
572 int len = RTA_PAYLOAD(hdr);
573
574 metrics[0] = 0;
575 for (; RTA_OK(a, len); a = RTA_NEXT(a, len))
576 {
577 if (a->rta_type == RTA_UNSPEC)
578 continue;
579
580 if (a->rta_type >= max)
581 continue;
582
583 if (RTA_PAYLOAD(a) != 4)
584 return -1;
585
586 metrics[0] |= 1 << a->rta_type;
acb04cfd 587 metrics[a->rta_type] = rta_get_u32(a);
9fdf9d29
OZ
588 }
589
590 if (len > 0)
591 return -1;
592
593 return 0;
594}
595
95616c82
OZ
596
597/*
598 * Scanning of interfaces
599 */
600
601static void
602nl_parse_link(struct nlmsghdr *h, int scan)
603{
604 struct ifinfomsg *i;
ad276157 605 struct rtattr *a[BIRD_IFLA_MAX];
95616c82
OZ
606 int new = h->nlmsg_type == RTM_NEWLINK;
607 struct iface f = {};
608 struct iface *ifi;
609 char *name;
610 u32 mtu;
ae80a2de 611 uint fl;
95616c82 612
ad276157 613 if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), ifla_attr_want, a, sizeof(a)))
95616c82 614 return;
ad276157 615 if (!a[IFLA_IFNAME] || (RTA_PAYLOAD(a[IFLA_IFNAME]) < 2) || !a[IFLA_MTU])
95616c82 616 {
ad276157
JMM
617 /*
618 * IFLA_IFNAME and IFLA_MTU are required, in fact, but there may also come
619 * a message with IFLA_WIRELESS set, where (e.g.) no IFLA_IFNAME exists.
620 * We simply ignore all such messages with IFLA_WIRELESS without notice.
621 */
622
623 if (a[IFLA_WIRELESS])
624 return;
625
626 log(L_ERR "KIF: Malformed message received");
95616c82
OZ
627 return;
628 }
ad276157 629
95616c82 630 name = RTA_DATA(a[IFLA_IFNAME]);
acb04cfd 631 mtu = rta_get_u32(a[IFLA_MTU]);
95616c82
OZ
632
633 ifi = if_find_by_index(i->ifi_index);
634 if (!new)
635 {
636 DBG("KIF: IF%d(%s) goes down\n", i->ifi_index, name);
637 if (!ifi)
638 return;
639
640 if_delete(ifi);
641 }
642 else
643 {
644 DBG("KIF: IF%d(%s) goes up (mtu=%d,flg=%x)\n", i->ifi_index, name, mtu, i->ifi_flags);
645 if (ifi && strncmp(ifi->name, name, sizeof(ifi->name)-1))
646 if_delete(ifi);
647
648 strncpy(f.name, name, sizeof(f.name)-1);
649 f.index = i->ifi_index;
650 f.mtu = mtu;
651
652 fl = i->ifi_flags;
653 if (fl & IFF_UP)
654 f.flags |= IF_ADMIN_UP;
655 if (fl & IFF_LOWER_UP)
656 f.flags |= IF_LINK_UP;
657 if (fl & IFF_LOOPBACK) /* Loopback */
658 f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE;
659 else if (fl & IFF_POINTOPOINT) /* PtP */
660 f.flags |= IF_MULTICAST;
661 else if (fl & IFF_BROADCAST) /* Broadcast */
662 f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST;
663 else
664 f.flags |= IF_MULTIACCESS; /* NBMA */
3216eb03 665
16a3254c
OZ
666 if (fl & IFF_MULTICAST)
667 f.flags |= IF_MULTICAST;
668
3216eb03
OZ
669 ifi = if_update(&f);
670
671 if (!scan)
672 if_end_partial_update(ifi);
95616c82
OZ
673 }
674}
675
676static void
9b136840 677nl_parse_addr4(struct ifaddrmsg *i, int scan, int new)
95616c82 678{
ad276157 679 struct rtattr *a[BIRD_IFA_MAX];
95616c82 680 struct iface *ifi;
e37d2e3e 681 u32 ifa_flags;
95616c82
OZ
682 int scope;
683
9b136840 684 if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a)))
95616c82 685 return;
ad276157 686
9b136840 687 if (!a[IFA_LOCAL])
ad276157 688 {
9b136840
JMM
689 log(L_ERR "KIF: Malformed message received (missing IFA_LOCAL)");
690 return;
ad276157 691 }
ad276157 692 if (!a[IFA_ADDRESS])
95616c82 693 {
ad276157 694 log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)");
95616c82
OZ
695 return;
696 }
697
698 ifi = if_find_by_index(i->ifa_index);
699 if (!ifi)
700 {
701 log(L_ERR "KIF: Received address message for unknown interface %d", i->ifa_index);
702 return;
703 }
704
e37d2e3e
OZ
705 if (a[IFA_FLAGS])
706 ifa_flags = rta_get_u32(a[IFA_FLAGS]);
707 else
708 ifa_flags = i->ifa_flags;
709
9b136840 710 struct ifa ifa;
95616c82
OZ
711 bzero(&ifa, sizeof(ifa));
712 ifa.iface = ifi;
cc5b93f7 713 if (ifa_flags & IFA_F_SECONDARY)
95616c82
OZ
714 ifa.flags |= IA_SECONDARY;
715
9b136840
JMM
716 ifa.ip = rta_get_ipa(a[IFA_LOCAL]);
717
d7661fbe 718 if (i->ifa_prefixlen > IP4_MAX_PREFIX_LENGTH)
95616c82
OZ
719 {
720 log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen);
721 new = 0;
722 }
d7661fbe 723 if (i->ifa_prefixlen == IP4_MAX_PREFIX_LENGTH)
95616c82 724 {
9b136840
JMM
725 ifa.brd = rta_get_ipa(a[IFA_ADDRESS]);
726 net_fill_ip4(&ifa.prefix, rta_get_ip4(a[IFA_ADDRESS]), i->ifa_prefixlen);
95616c82
OZ
727
728 /* It is either a host address or a peer address */
9b136840 729 if (ipa_equal(ifa.ip, ifa.brd))
95616c82
OZ
730 ifa.flags |= IA_HOST;
731 else
732 {
733 ifa.flags |= IA_PEER;
9b136840 734 ifa.opposite = ifa.brd;
95616c82
OZ
735 }
736 }
737 else
738 {
9b136840
JMM
739 net_fill_ip4(&ifa.prefix, ipa_to_ip4(ifa.ip), i->ifa_prefixlen);
740 net_normalize(&ifa.prefix);
741
d7661fbe 742 if (i->ifa_prefixlen == IP4_MAX_PREFIX_LENGTH - 1)
95616c82
OZ
743 ifa.opposite = ipa_opposite_m1(ifa.ip);
744
d7661fbe 745 if (i->ifa_prefixlen == IP4_MAX_PREFIX_LENGTH - 2)
95616c82
OZ
746 ifa.opposite = ipa_opposite_m2(ifa.ip);
747
748 if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST])
749 {
9b136840
JMM
750 ip4_addr xbrd = rta_get_ip4(a[IFA_BROADCAST]);
751 ip4_addr ybrd = ip4_or(ipa_to_ip4(ifa.ip), ip4_not(ip4_mkmask(i->ifa_prefixlen)));
752
753 if (ip4_equal(xbrd, net4_prefix(&ifa.prefix)) || ip4_equal(xbrd, ybrd))
754 ifa.brd = ipa_from_ip4(xbrd);
95616c82 755 else if (ifi->flags & IF_TMP_DOWN) /* Complain only during the first scan */
9b136840 756 {
e691d16a 757 log(L_ERR "KIF: Invalid broadcast address %I4 for %s", xbrd, ifi->name);
9b136840
JMM
758 ifa.brd = ipa_from_ip4(ybrd);
759 }
760 }
761 }
762
763 scope = ipa_classify(ifa.ip);
764 if (scope < 0)
765 {
766 log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, ifi->name);
767 return;
768 }
769 ifa.scope = scope & IADDR_SCOPE_MASK;
770
771 DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %N, brd %I, opp %I\n",
772 ifi->index, ifi->name,
773 new ? "added" : "removed",
774 ifa.ip, ifa.flags, ifa.prefix, ifa.brd, ifa.opposite);
775
776 if (new)
777 ifa_update(&ifa);
778 else
779 ifa_delete(&ifa);
780
781 if (!scan)
782 if_end_partial_update(ifi);
783}
784
785static void
786nl_parse_addr6(struct ifaddrmsg *i, int scan, int new)
787{
788 struct rtattr *a[BIRD_IFA_MAX];
789 struct iface *ifi;
cc5b93f7 790 u32 ifa_flags;
9b136840
JMM
791 int scope;
792
793 if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a)))
794 return;
795
796 if (!a[IFA_ADDRESS])
797 {
798 log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)");
799 return;
800 }
801
802 ifi = if_find_by_index(i->ifa_index);
803 if (!ifi)
804 {
805 log(L_ERR "KIF: Received address message for unknown interface %d", i->ifa_index);
806 return;
807 }
808
cc5b93f7
OZ
809 if (a[IFA_FLAGS])
810 ifa_flags = rta_get_u32(a[IFA_FLAGS]);
811 else
812 ifa_flags = i->ifa_flags;
813
9b136840
JMM
814 struct ifa ifa;
815 bzero(&ifa, sizeof(ifa));
816 ifa.iface = ifi;
e37d2e3e 817 if (ifa_flags & IFA_F_SECONDARY)
9b136840
JMM
818 ifa.flags |= IA_SECONDARY;
819
e37d2e3e
OZ
820 /* Ignore tentative addresses silently */
821 if (ifa_flags & IFA_F_TENTATIVE)
822 return;
9b136840 823
95616c82 824 /* IFA_LOCAL can be unset for IPv6 interfaces */
9b136840
JMM
825 ifa.ip = rta_get_ipa(a[IFA_LOCAL] ? : a[IFA_ADDRESS]);
826
d7661fbe 827 if (i->ifa_prefixlen > IP6_MAX_PREFIX_LENGTH)
9b136840
JMM
828 {
829 log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen);
830 new = 0;
831 }
d7661fbe 832 if (i->ifa_prefixlen == IP6_MAX_PREFIX_LENGTH)
9b136840
JMM
833 {
834 ifa.brd = rta_get_ipa(a[IFA_ADDRESS]);
835 net_fill_ip6(&ifa.prefix, rta_get_ip6(a[IFA_ADDRESS]), i->ifa_prefixlen);
836
837 /* It is either a host address or a peer address */
838 if (ipa_equal(ifa.ip, ifa.brd))
839 ifa.flags |= IA_HOST;
840 else
841 {
842 ifa.flags |= IA_PEER;
843 ifa.opposite = ifa.brd;
95616c82 844 }
9b136840
JMM
845 }
846 else
847 {
848 net_fill_ip6(&ifa.prefix, ipa_to_ip6(ifa.ip), i->ifa_prefixlen);
849 net_normalize(&ifa.prefix);
850
d7661fbe 851 if (i->ifa_prefixlen == IP6_MAX_PREFIX_LENGTH - 1)
9b136840 852 ifa.opposite = ipa_opposite_m1(ifa.ip);
95616c82
OZ
853 }
854
855 scope = ipa_classify(ifa.ip);
856 if (scope < 0)
857 {
858 log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, ifi->name);
859 return;
860 }
861 ifa.scope = scope & IADDR_SCOPE_MASK;
862
9b136840 863 DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %N, brd %I, opp %I\n",
95616c82
OZ
864 ifi->index, ifi->name,
865 new ? "added" : "removed",
9b136840 866 ifa.ip, ifa.flags, ifa.prefix, ifa.brd, ifa.opposite);
3216eb03 867
95616c82
OZ
868 if (new)
869 ifa_update(&ifa);
870 else
871 ifa_delete(&ifa);
3216eb03
OZ
872
873 if (!scan)
874 if_end_partial_update(ifi);
95616c82
OZ
875}
876
9b136840
JMM
877static void
878nl_parse_addr(struct nlmsghdr *h, int scan)
879{
880 struct ifaddrmsg *i;
881
882 if (!(i = nl_checkin(h, sizeof(*i))))
883 return;
884
885 int new = (h->nlmsg_type == RTM_NEWADDR);
886
887 switch (i->ifa_family)
888 {
9b136840
JMM
889 case AF_INET:
890 return nl_parse_addr4(i, scan, new);
29a64162 891
9b136840
JMM
892 case AF_INET6:
893 return nl_parse_addr6(i, scan, new);
9b136840
JMM
894 }
895}
896
95616c82
OZ
897void
898kif_do_scan(struct kif_proto *p UNUSED)
899{
900 struct nlmsghdr *h;
901
902 if_start_update();
903
86c3eea0 904 nl_request_dump(AF_UNSPEC, RTM_GETLINK);
95616c82
OZ
905 while (h = nl_get_scan())
906 if (h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)
907 nl_parse_link(h, 1);
908 else
909 log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
29a64162 910
d7661fbe 911 nl_request_dump(AF_INET, RTM_GETADDR);
95616c82
OZ
912 while (h = nl_get_scan())
913 if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
3216eb03 914 nl_parse_addr(h, 1);
95616c82
OZ
915 else
916 log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
29a64162 917
d7661fbe
JMM
918 nl_request_dump(AF_INET6, RTM_GETADDR);
919 while (h = nl_get_scan())
920 if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
921 nl_parse_addr(h, 1);
922 else
923 log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
29a64162 924
95616c82
OZ
925 if_end_update();
926}
927
928/*
929 * Routes
930 */
931
9ddbfbdd
JMM
932static inline u32
933krt_table_id(struct krt_proto *p)
934{
935 return KRT_CF->sys.table_id;
936}
937
938static HASH(struct krt_proto) nl_table_map;
939
29a64162
OZ
940#define RTH_KEY(p) p->af, krt_table_id(p)
941#define RTH_NEXT(p) p->sys.hash_next
942#define RTH_EQ(a1,i1,a2,i2) a1 == a2 && i1 == i2
943#define RTH_FN(a,i) a ^ u32_hash(i)
9ddbfbdd
JMM
944
945#define RTH_REHASH rth_rehash
946#define RTH_PARAMS /8, *2, 2, 2, 6, 20
947
948HASH_DEFINE_REHASH_FN(RTH, struct krt_proto)
95616c82
OZ
949
950int
951krt_capable(rte *e)
952{
953 rta *a = e->attrs;
954
95616c82
OZ
955 switch (a->dest)
956 {
4e276a89
JMM
957 case RTD_UNICAST:
958 for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
959 if (nh->iface)
960 return 1;
961 return 0;
95616c82
OZ
962 case RTD_BLACKHOLE:
963 case RTD_UNREACHABLE:
964 case RTD_PROHIBIT:
95616c82
OZ
965 break;
966 default:
967 return 0;
968 }
969 return 1;
970}
971
972static inline int
4e276a89 973nh_bufsize(struct nexthop *nh)
95616c82
OZ
974{
975 int rv = 0;
976 for (; nh != NULL; nh = nh->next)
9fdf9d29 977 rv += RTNH_LENGTH(RTA_LENGTH(sizeof(ip_addr)));
95616c82
OZ
978 return rv;
979}
980
981static int
4e276a89 982nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int dest, struct nexthop *nh)
95616c82
OZ
983{
984 eattr *ea;
985 net *net = e->net;
986 rta *a = e->attrs;
4e276a89 987 int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(&(a->nh));
4adcb9df 988 u32 priority = 0;
a8caff32 989
95616c82
OZ
990 struct {
991 struct nlmsghdr h;
992 struct rtmsg r;
a8caff32
JMM
993 char buf[0];
994 } *r;
995
996 int rsize = sizeof(*r) + bufsize;
997 r = alloca(rsize);
95616c82 998
cc5b93f7 999 DBG("nl_send_route(%N,op=%x)\n", net->n.addr, op);
95616c82 1000
a8caff32
JMM
1001 bzero(&r->h, sizeof(r->h));
1002 bzero(&r->r, sizeof(r->r));
cc5b93f7 1003 r->h.nlmsg_type = op ? RTM_NEWROUTE : RTM_DELROUTE;
a8caff32 1004 r->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
cc5b93f7 1005 r->h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK;
95616c82 1006
a8caff32
JMM
1007 r->r.rtm_family = p->af;
1008 r->r.rtm_dst_len = net_pxlen(net->n.addr);
1009 r->r.rtm_protocol = RTPROT_BIRD;
1010 r->r.rtm_scope = RT_SCOPE_UNIVERSE;
1011 nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
95616c82 1012
2feaa693
OZ
1013 /*
1014 * Strange behavior for RTM_DELROUTE:
1015 * 1) rtm_family is ignored in IPv6, works for IPv4
1016 * 2) not setting RTA_PRIORITY is different from setting default value (on IPv6)
1017 * 3) not setting RTA_PRIORITY is equivalent to setting 0, which is wildcard
1018 */
1019
9ddbfbdd 1020 if (krt_table_id(p) < 256)
a8caff32 1021 r->r.rtm_table = krt_table_id(p);
9ddbfbdd 1022 else
a8caff32 1023 nl_add_attr_u32(&r->h, rsize, RTA_TABLE, krt_table_id(p));
9ddbfbdd 1024
4adcb9df
OZ
1025 if (a->source == RTS_DUMMY)
1026 priority = e->u.krt.metric;
1027 else if (KRT_CF->sys.metric)
1028 priority = KRT_CF->sys.metric;
1029 else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC)))
1030 priority = ea->u.data;
78a2cc28 1031
4adcb9df 1032 if (priority)
cc5b93f7 1033 nl_add_attr_u32(&r->h, sizeof(r), RTA_PRIORITY, priority);
78a2cc28 1034
2feaa693
OZ
1035 /* For route delete, we do not specify remaining route attributes */
1036 if (op == NL_OP_DELETE)
1037 goto dest;
78a2cc28 1038
6e75d0d2
OZ
1039 /* Default scope is LINK for device routes, UNIVERSE otherwise */
1040 if (ea = ea_find(eattrs, EA_KRT_SCOPE))
cc5b93f7 1041 r->r.rtm_scope = ea->u.data;
6e75d0d2 1042 else
4e276a89 1043 r->r.rtm_scope = (dest == RTD_UNICAST && ipa_zero(nh->gw)) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
95616c82
OZ
1044
1045 if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
a8caff32 1046 nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
95616c82
OZ
1047
1048 if (ea = ea_find(eattrs, EA_KRT_REALM))
a8caff32 1049 nl_add_attr_u32(&r->h, rsize, RTA_FLOW, ea->u.data);
95616c82 1050
9fdf9d29
OZ
1051
1052 u32 metrics[KRT_METRICS_MAX];
1053 metrics[0] = 0;
1054
1055 struct ea_walk_state ews = { .eattrs = eattrs };
1056 while (ea = ea_walk(&ews, EA_KRT_METRICS, KRT_METRICS_MAX))
1057 {
1058 int id = ea->id - EA_KRT_METRICS;
1059 metrics[0] |= 1 << id;
1060 metrics[id] = ea->u.data;
1061 }
1062
1063 if (metrics[0])
a8caff32 1064 nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX);
9fdf9d29
OZ
1065
1066
2feaa693 1067dest:
95616c82 1068 /* a->iface != NULL checked in krt_capable() for router and device routes */
2feaa693 1069 switch (dest)
95616c82 1070 {
4e276a89 1071 case RTD_UNICAST:
a8caff32 1072 r->r.rtm_type = RTN_UNICAST;
4e276a89
JMM
1073 if (nh->next && !krt_ecmp6(p))
1074 nl_add_multipath(&r->h, rsize, nh);
1075 else
1076 {
1077 nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
1078
1079 if (ipa_nonzero(nh->gw))
1080 nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, nh->gw);
1081 }
95616c82
OZ
1082 break;
1083 case RTD_BLACKHOLE:
a8caff32 1084 r->r.rtm_type = RTN_BLACKHOLE;
95616c82
OZ
1085 break;
1086 case RTD_UNREACHABLE:
a8caff32 1087 r->r.rtm_type = RTN_UNREACHABLE;
95616c82
OZ
1088 break;
1089 case RTD_PROHIBIT:
a8caff32 1090 r->r.rtm_type = RTN_PROHIBIT;
95616c82 1091 break;
2feaa693
OZ
1092 case RTD_NONE:
1093 break;
95616c82
OZ
1094 default:
1095 bug("krt_capable inconsistent with nl_send_route");
1096 }
1097
2feaa693 1098 /* Ignore missing for DELETE */
cc5b93f7 1099 return nl_exchange(&r->h, (op == NL_OP_DELETE));
2feaa693
OZ
1100}
1101
1102static inline int
1103nl_add_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
1104{
1105 rta *a = e->attrs;
1106 int err = 0;
1107
4e276a89 1108 if (krt_ecmp6(p) && a->nh.next)
2feaa693 1109 {
4e276a89 1110 struct nexthop *nh = &(a->nh);
2feaa693 1111
4e276a89 1112 err = nl_send_route(p, e, eattrs, NL_OP_ADD, RTD_UNICAST, nh);
2feaa693
OZ
1113 if (err < 0)
1114 return err;
1115
1116 for (nh = nh->next; nh; nh = nh->next)
4e276a89 1117 err += nl_send_route(p, e, eattrs, NL_OP_APPEND, RTD_UNICAST, nh);
2feaa693
OZ
1118
1119 return err;
1120 }
1121
4e276a89 1122 return nl_send_route(p, e, eattrs, NL_OP_ADD, a->dest, &(a->nh));
2feaa693
OZ
1123}
1124
1125static inline int
1126nl_delete_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
1127{
1128 int err = 0;
1129
1130 /* For IPv6, we just repeatedly request DELETE until we get error */
1131 do
4e276a89 1132 err = nl_send_route(p, e, eattrs, NL_OP_DELETE, RTD_NONE, NULL);
2feaa693
OZ
1133 while (krt_ecmp6(p) && !err);
1134
1135 return err;
95616c82
OZ
1136}
1137
1138void
1139krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs)
1140{
1141 int err = 0;
1142
1143 /*
2feaa693
OZ
1144 * We could use NL_OP_REPLACE, but route replace on Linux has some problems:
1145 *
1146 * 1) Does not check for matching rtm_protocol
1147 * 2) Has broken semantics for IPv6 ECMP
1148 * 3) Crashes some kernel version when used for IPv6 ECMP
1149 *
1150 * So we use NL_OP_DELETE and then NL_OP_ADD. We also do not trust the old
1151 * route value, so we do not try to optimize IPv6 ECMP reconfigurations.
95616c82
OZ
1152 */
1153
1154 if (old)
2feaa693 1155 nl_delete_rte(p, old, eattrs);
95616c82
OZ
1156
1157 if (new)
2feaa693 1158 err = nl_add_rte(p, new, eattrs);
95616c82
OZ
1159
1160 if (err < 0)
1161 n->n.flags |= KRF_SYNC_ERROR;
1162 else
1163 n->n.flags &= ~KRF_SYNC_ERROR;
1164}
1165
1166
4e276a89
JMM
1167static inline struct nexthop *
1168nl_alloc_nexthop(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight)
2feaa693 1169{
4e276a89 1170 struct nexthop *nh = lp_alloc(s->pool, sizeof(struct nexthop));
2feaa693
OZ
1171
1172 nh->gw = gw;
1173 nh->iface = iface;
1174 nh->next = NULL;
1175 nh->weight = weight;
1176
1177 return nh;
1178}
1179
1180static int
1181nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type)
1182{
1183 /* Route merging must be active */
1184 if (!s->merge)
1185 return 0;
1186
1187 /* Saved and new route must have same network, proto/table, and priority */
1188 if ((s->net != net) || (s->proto != p) || (s->krt_metric != priority))
1189 return 0;
1190
1191 /* Both must be regular unicast routes */
1192 if ((s->krt_type != RTN_UNICAST) || (krt_type != RTN_UNICAST))
1193 return 0;
1194
1195 return 1;
1196}
1197
1198static void
1199nl_announce_route(struct nl_parse_state *s)
1200{
1201 rte *e = rte_get_temp(s->attrs);
1202 e->net = s->net;
1203 e->u.krt.src = s->krt_src;
1204 e->u.krt.proto = s->krt_proto;
1205 e->u.krt.seen = 0;
1206 e->u.krt.best = 0;
1207 e->u.krt.metric = s->krt_metric;
1208
1209 if (s->scan)
1210 krt_got_route(s->proto, e);
1211 else
1212 krt_got_route_async(s->proto, e, s->new);
1213
1214 s->net = NULL;
1215 s->attrs = NULL;
1216 s->proto = NULL;
1217 lp_flush(s->pool);
1218}
1219
1220static inline void
1221nl_parse_begin(struct nl_parse_state *s, int scan, int merge)
1222{
1223 memset(s, 0, sizeof (struct nl_parse_state));
1224 s->pool = nl_linpool;
1225 s->scan = scan;
1226 s->merge = merge;
1227}
1228
1229static inline void
1230nl_parse_end(struct nl_parse_state *s)
1231{
1232 if (s->net)
1233 nl_announce_route(s);
1234}
1235
1236
95616c82
OZ
1237#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)
1238
1239static void
2feaa693 1240nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
95616c82
OZ
1241{
1242 struct krt_proto *p;
1243 struct rtmsg *i;
ad276157 1244 struct rtattr *a[BIRD_RTA_MAX];
95616c82
OZ
1245 int new = h->nlmsg_type == RTM_NEWROUTE;
1246
29a64162 1247 net_addr dst;
95616c82 1248 u32 oif = ~0;
29a64162 1249 u32 table_id;
2feaa693 1250 u32 priority = 0;
6e75d0d2 1251 u32 def_scope = RT_SCOPE_UNIVERSE;
95616c82
OZ
1252 int src;
1253
ad276157 1254 if (!(i = nl_checkin(h, sizeof(*i))))
95616c82 1255 return;
ad276157
JMM
1256
1257 switch (i->rtm_family)
95616c82 1258 {
29a64162
OZ
1259 case AF_INET:
1260 if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want4, a, sizeof(a)))
1261 return;
1262
1263 if (a[RTA_DST])
1264 net_fill_ip4(&dst, rta_get_ip4(a[RTA_DST]), i->rtm_dst_len);
1265 else
1266 net_fill_ip4(&dst, IP4_NONE, 0);
1267 break;
1268
cc5b93f7
OZ
1269 case AF_INET6:
1270 if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a)))
1271 return;
29a64162
OZ
1272
1273 if (a[RTA_DST])
1274 net_fill_ip6(&dst, rta_get_ip6(a[RTA_DST]), i->rtm_dst_len);
1275 else
1276 net_fill_ip6(&dst, IP6_NONE, 0);
1277 break;
1278
1279 default:
1280 return;
95616c82
OZ
1281 }
1282
95616c82 1283 if (a[RTA_OIF])
acb04cfd 1284 oif = rta_get_u32(a[RTA_OIF]);
95616c82 1285
9ddbfbdd 1286 if (a[RTA_TABLE])
29a64162 1287 table_id = rta_get_u32(a[RTA_TABLE]);
9ddbfbdd 1288 else
29a64162 1289 table_id = i->rtm_table;
9ddbfbdd 1290
29a64162
OZ
1291 /* Do we know this table? */
1292 p = HASH_FIND(nl_table_map, RTH, i->rtm_family, table_id);
95616c82 1293 if (!p)
9ddbfbdd 1294 SKIP("unknown table %d\n", table);
95616c82 1295
95616c82
OZ
1296 if (a[RTA_IIF])
1297 SKIP("IIF set\n");
29a64162 1298
95616c82
OZ
1299 if (i->rtm_tos != 0) /* We don't support TOS */
1300 SKIP("TOS %02x\n", i->rtm_tos);
95616c82 1301
2feaa693 1302 if (s->scan && !new)
95616c82
OZ
1303 SKIP("RTM_DELROUTE in scan\n");
1304
2feaa693
OZ
1305 if (a[RTA_PRIORITY])
1306 priority = rta_get_u32(a[RTA_PRIORITY]);
1307
9b136840 1308 int c = net_classify(&dst);
95616c82
OZ
1309 if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
1310 SKIP("strange class/scope\n");
1311
95616c82
OZ
1312 switch (i->rtm_protocol)
1313 {
1314 case RTPROT_UNSPEC:
1315 SKIP("proto unspec\n");
1316
1317 case RTPROT_REDIRECT:
1318 src = KRT_SRC_REDIRECT;
1319 break;
1320
1321 case RTPROT_KERNEL:
1322 src = KRT_SRC_KERNEL;
1323 return;
1324
1325 case RTPROT_BIRD:
2feaa693 1326 if (!s->scan)
95616c82
OZ
1327 SKIP("echo\n");
1328 src = KRT_SRC_BIRD;
1329 break;
1330
1331 case RTPROT_BOOT:
1332 default:
1333 src = KRT_SRC_ALIEN;
1334 }
1335
f4a60a9b 1336 net *net = net_get(p->p.main_channel->table, &dst);
95616c82 1337
2feaa693
OZ
1338 if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
1339 nl_announce_route(s);
1340
4e276a89 1341 rta *ra = lp_allocz(s->pool, sizeof(rta)); // TODO: fix alloc
2feaa693
OZ
1342 ra->src = p->p.main_source;
1343 ra->source = RTS_INHERIT;
1344 ra->scope = SCOPE_UNIVERSE;
95616c82
OZ
1345
1346 switch (i->rtm_type)
1347 {
1348 case RTN_UNICAST:
1349
ad276157 1350 if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
95616c82 1351 {
4e276a89
JMM
1352 struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH]);
1353 if (!nh)
95616c82 1354 {
fe9f1a6d 1355 log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
95616c82
OZ
1356 return;
1357 }
9fdf9d29 1358
4e276a89 1359 nexthop_link(ra, nh);
95616c82
OZ
1360 break;
1361 }
1362
4e276a89
JMM
1363 ra->nh.iface = if_find_by_index(oif);
1364 if (!ra->nh.iface)
95616c82 1365 {
fe9f1a6d 1366 log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
95616c82
OZ
1367 return;
1368 }
1369
1370 if (a[RTA_GATEWAY])
1371 {
4e276a89 1372 ra->nh.gw = rta_get_ipa(a[RTA_GATEWAY]);
95616c82
OZ
1373
1374 /* Silently skip strange 6to4 routes */
0bf95f99 1375 const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96);
4e276a89 1376 if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit))
95616c82
OZ
1377 return;
1378
23c212e7 1379 neighbor *nbr;
4e276a89 1380 nbr = neigh_find2(&p->p, &ra->nh.gw, ra->nh.iface,
23c212e7
OZ
1381 (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
1382 if (!nbr || (nbr->scope == SCOPE_HOST))
95616c82 1383 {
4e276a89
JMM
1384 log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr,
1385 ra->nh.gw);
95616c82
OZ
1386 return;
1387 }
1388 }
95616c82
OZ
1389
1390 break;
1391 case RTN_BLACKHOLE:
2feaa693 1392 ra->dest = RTD_BLACKHOLE;
95616c82
OZ
1393 break;
1394 case RTN_UNREACHABLE:
2feaa693 1395 ra->dest = RTD_UNREACHABLE;
95616c82
OZ
1396 break;
1397 case RTN_PROHIBIT:
2feaa693 1398 ra->dest = RTD_PROHIBIT;
95616c82
OZ
1399 break;
1400 /* FIXME: What about RTN_THROW? */
1401 default:
1402 SKIP("type %d\n", i->rtm_type);
1403 return;
1404 }
1405
6e75d0d2
OZ
1406 if (i->rtm_scope != def_scope)
1407 {
1408 ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
1409 ea->next = ra->eattrs;
1410 ra->eattrs = ea;
1411 ea->flags = EALF_SORTED;
1412 ea->count = 1;
1413 ea->attrs[0].id = EA_KRT_SCOPE;
1414 ea->attrs[0].flags = 0;
1415 ea->attrs[0].type = EAF_TYPE_INT;
1416 ea->attrs[0].u.data = i->rtm_scope;
1417 }
95616c82
OZ
1418
1419 if (a[RTA_PREFSRC])
1420 {
9b136840 1421 ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
95616c82 1422
2feaa693
OZ
1423 ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
1424 ea->next = ra->eattrs;
1425 ra->eattrs = ea;
95616c82
OZ
1426 ea->flags = EALF_SORTED;
1427 ea->count = 1;
1428 ea->attrs[0].id = EA_KRT_PREFSRC;
1429 ea->attrs[0].flags = 0;
1430 ea->attrs[0].type = EAF_TYPE_IP_ADDRESS;
2feaa693 1431 ea->attrs[0].u.ptr = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
95616c82
OZ
1432 ea->attrs[0].u.ptr->length = sizeof(ps);
1433 memcpy(ea->attrs[0].u.ptr->data, &ps, sizeof(ps));
1434 }
1435
1436 if (a[RTA_FLOW])
1437 {
2feaa693
OZ
1438 ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
1439 ea->next = ra->eattrs;
1440 ra->eattrs = ea;
95616c82
OZ
1441 ea->flags = EALF_SORTED;
1442 ea->count = 1;
1443 ea->attrs[0].id = EA_KRT_REALM;
1444 ea->attrs[0].flags = 0;
1445 ea->attrs[0].type = EAF_TYPE_INT;
acb04cfd 1446 ea->attrs[0].u.data = rta_get_u32(a[RTA_FLOW]);
95616c82
OZ
1447 }
1448
9fdf9d29
OZ
1449 if (a[RTA_METRICS])
1450 {
1451 u32 metrics[KRT_METRICS_MAX];
2feaa693 1452 ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr));
9fdf9d29
OZ
1453 int t, n = 0;
1454
1455 if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
1456 {
fe9f1a6d 1457 log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net->n.addr);
9fdf9d29
OZ
1458 return;
1459 }
1460
1461 for (t = 1; t < KRT_METRICS_MAX; t++)
1462 if (metrics[0] & (1 << t))
1463 {
1464 ea->attrs[n].id = EA_CODE(EAP_KRT, KRT_METRICS_OFFSET + t);
1465 ea->attrs[n].flags = 0;
1466 ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
1467 ea->attrs[n].u.data = metrics[t];
1468 n++;
1469 }
1470
1471 if (n > 0)
1472 {
2feaa693 1473 ea->next = ra->eattrs;
9fdf9d29
OZ
1474 ea->flags = EALF_SORTED;
1475 ea->count = n;
2feaa693 1476 ra->eattrs = ea;
9fdf9d29
OZ
1477 }
1478 }
1479
2feaa693
OZ
1480 /*
1481 * Ideally, now we would send the received route to the rest of kernel code.
1482 * But IPv6 ECMP routes are sent as a sequence of routes, so we postpone it
1483 * and merge next hops until the end of the sequence.
1484 */
1485
1486 if (!s->net)
1487 {
1488 /* Store the new route */
1489 s->net = net;
1490 s->attrs = ra;
1491 s->proto = p;
1492 s->new = new;
1493 s->krt_src = src;
1494 s->krt_type = i->rtm_type;
1495 s->krt_proto = i->rtm_protocol;
1496 s->krt_metric = priority;
1497 }
95616c82 1498 else
2feaa693
OZ
1499 {
1500 /* Merge next hops with the stored route */
1501 rta *a = s->attrs;
1502
4e276a89 1503 nexthop_insert(&a->nh, &ra->nh);
2feaa693 1504 }
95616c82
OZ
1505}
1506
1507void
1508krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */
1509{
1510 struct nlmsghdr *h;
2feaa693 1511 struct nl_parse_state s;
95616c82 1512
cc5b93f7 1513 nl_parse_begin(&s, 1, 0);
d7661fbe 1514 nl_request_dump(AF_INET, RTM_GETROUTE);
95616c82
OZ
1515 while (h = nl_get_scan())
1516 if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
2feaa693 1517 nl_parse_route(&s, h);
95616c82
OZ
1518 else
1519 log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
cc5b93f7 1520 nl_parse_end(&s);
29a64162 1521
cc5b93f7 1522 nl_parse_begin(&s, 1, 1);
d7661fbe
JMM
1523 nl_request_dump(AF_INET6, RTM_GETROUTE);
1524 while (h = nl_get_scan())
1525 if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
cc5b93f7 1526 nl_parse_route(&s, h);
d7661fbe
JMM
1527 else
1528 log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
2feaa693 1529 nl_parse_end(&s);
95616c82
OZ
1530}
1531
1532/*
1533 * Asynchronous Netlink interface
1534 */
1535
1536static sock *nl_async_sk; /* BIRD socket for asynchronous notifications */
1537static byte *nl_async_rx_buffer; /* Receive buffer */
1538
1539static void
1540nl_async_msg(struct nlmsghdr *h)
1541{
2feaa693
OZ
1542 struct nl_parse_state s;
1543
95616c82
OZ
1544 switch (h->nlmsg_type)
1545 {
1546 case RTM_NEWROUTE:
1547 case RTM_DELROUTE:
1548 DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
2feaa693
OZ
1549 nl_parse_begin(&s, 0, 0);
1550 nl_parse_route(&s, h);
1551 nl_parse_end(&s);
95616c82
OZ
1552 break;
1553 case RTM_NEWLINK:
1554 case RTM_DELLINK:
1555 DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type);
1e4891e4
OZ
1556 if (kif_proto)
1557 nl_parse_link(h, 0);
95616c82
OZ
1558 break;
1559 case RTM_NEWADDR:
1560 case RTM_DELADDR:
1561 DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type);
1e4891e4
OZ
1562 if (kif_proto)
1563 nl_parse_addr(h, 0);
95616c82
OZ
1564 break;
1565 default:
1566 DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type);
1567 }
1568}
1569
1570static int
3e236955 1571nl_async_hook(sock *sk, uint size UNUSED)
95616c82
OZ
1572{
1573 struct iovec iov = { nl_async_rx_buffer, NL_RX_SIZE };
1574 struct sockaddr_nl sa;
31e9e101
ST
1575 struct msghdr m = {
1576 .msg_name = &sa,
1577 .msg_namelen = sizeof(sa),
1578 .msg_iov = &iov,
1579 .msg_iovlen = 1,
1580 };
95616c82
OZ
1581 struct nlmsghdr *h;
1582 int x;
ae80a2de 1583 uint len;
95616c82
OZ
1584
1585 x = recvmsg(sk->fd, &m, 0);
1586 if (x < 0)
1587 {
1588 if (errno == ENOBUFS)
1589 {
1590 /*
1591 * Netlink reports some packets have been thrown away.
1592 * One day we might react to it by asking for route table
1593 * scan in near future.
1594 */
1595 return 1; /* More data are likely to be ready */
1596 }
1597 else if (errno != EWOULDBLOCK)
1598 log(L_ERR "Netlink recvmsg: %m");
1599 return 0;
1600 }
1601 if (sa.nl_pid) /* It isn't from the kernel */
1602 {
1603 DBG("Non-kernel packet\n");
1604 return 1;
1605 }
1606 h = (void *) nl_async_rx_buffer;
1607 len = x;
1608 if (m.msg_flags & MSG_TRUNC)
1609 {
1610 log(L_WARN "Netlink got truncated asynchronous message");
1611 return 1;
1612 }
1613 while (NLMSG_OK(h, len))
1614 {
1615 nl_async_msg(h);
1616 h = NLMSG_NEXT(h, len);
1617 }
1618 if (len)
1619 log(L_WARN "nl_async_hook: Found packet remnant of size %d", len);
1620 return 1;
1621}
1622
ccd2a3ed
JMM
1623static void
1624nl_async_err_hook(sock *sk, int e UNUSED)
1625{
1626 nl_async_hook(sk, 0);
1627}
1628
95616c82
OZ
1629static void
1630nl_open_async(void)
1631{
1632 sock *sk;
1633 struct sockaddr_nl sa;
1634 int fd;
95616c82 1635
f83ce94d 1636 if (nl_async_sk)
95616c82 1637 return;
95616c82
OZ
1638
1639 DBG("KRT: Opening async netlink socket\n");
1640
1641 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1642 if (fd < 0)
1643 {
1644 log(L_ERR "Unable to open asynchronous rtnetlink socket: %m");
1645 return;
1646 }
1647
1648 bzero(&sa, sizeof(sa));
1649 sa.nl_family = AF_NETLINK;
29a64162
OZ
1650 sa.nl_groups = RTMGRP_LINK |
1651 RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
1652 RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
1653
95616c82
OZ
1654 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
1655 {
1656 log(L_ERR "Unable to bind asynchronous rtnetlink socket: %m");
f83ce94d 1657 close(fd);
95616c82
OZ
1658 return;
1659 }
1660
f83ce94d
OZ
1661 nl_async_rx_buffer = xmalloc(NL_RX_SIZE);
1662
95616c82
OZ
1663 sk = nl_async_sk = sk_new(krt_pool);
1664 sk->type = SK_MAGIC;
1665 sk->rx_hook = nl_async_hook;
ccd2a3ed 1666 sk->err_hook = nl_async_err_hook;
95616c82 1667 sk->fd = fd;
05476c4d 1668 if (sk_open(sk) < 0)
95616c82 1669 bug("Netlink: sk_open failed");
95616c82
OZ
1670}
1671
9ddbfbdd 1672
95616c82
OZ
1673/*
1674 * Interface to the UNIX krt module
1675 */
1676
95616c82 1677void
9ddbfbdd
JMM
1678krt_sys_io_init(void)
1679{
2feaa693 1680 nl_linpool = lp_new(krt_pool, 4080);
9ddbfbdd
JMM
1681 HASH_INIT(nl_table_map, krt_pool, 6);
1682}
1683
1684int
c6964c30 1685krt_sys_start(struct krt_proto *p)
95616c82 1686{
29a64162 1687 struct krt_proto *old = HASH_FIND(nl_table_map, RTH, p->af, krt_table_id(p));
9ddbfbdd
JMM
1688
1689 if (old)
1690 {
1691 log(L_ERR "%s: Kernel table %u already registered by %s",
1692 p->p.name, krt_table_id(p), old->p.name);
1693 return 0;
1694 }
1695
1696 HASH_INSERT2(nl_table_map, RTH, krt_pool, p);
c6964c30
OZ
1697
1698 nl_open();
1699 nl_open_async();
9ddbfbdd
JMM
1700
1701 return 1;
95616c82
OZ
1702}
1703
1704void
9ddbfbdd 1705krt_sys_shutdown(struct krt_proto *p)
95616c82 1706{
9ddbfbdd 1707 HASH_REMOVE2(nl_table_map, RTH, krt_pool, p);
95616c82
OZ
1708}
1709
1710int
1711krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o)
1712{
4adcb9df 1713 return (n->sys.table_id == o->sys.table_id) && (n->sys.metric == o->sys.metric);
95616c82
OZ
1714}
1715
95616c82
OZ
1716void
1717krt_sys_init_config(struct krt_config *cf)
1718{
1719 cf->sys.table_id = RT_TABLE_MAIN;
4adcb9df 1720 cf->sys.metric = 0;
95616c82
OZ
1721}
1722
1723void
1724krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
1725{
1726 d->sys.table_id = s->sys.table_id;
4adcb9df 1727 d->sys.metric = s->sys.metric;
95616c82
OZ
1728}
1729
9fdf9d29
OZ
1730static const char *krt_metrics_names[KRT_METRICS_MAX] = {
1731 NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss",
1732 "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack"
1733};
1734
1735static const char *krt_features_names[KRT_FEATURES_MAX] = {
1736 "ecn", NULL, NULL, "allfrag"
1737};
1738
1739int
1740krt_sys_get_attr(eattr *a, byte *buf, int buflen UNUSED)
1741{
1742 switch (a->id)
1743 {
1744 case EA_KRT_PREFSRC:
1745 bsprintf(buf, "prefsrc");
1746 return GA_NAME;
1747
1748 case EA_KRT_REALM:
1749 bsprintf(buf, "realm");
1750 return GA_NAME;
1751
6e75d0d2
OZ
1752 case EA_KRT_SCOPE:
1753 bsprintf(buf, "scope");
1754 return GA_NAME;
1755
9fdf9d29
OZ
1756 case EA_KRT_LOCK:
1757 buf += bsprintf(buf, "lock:");
1758 ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);
1759 return GA_FULL;
1760
1761 case EA_KRT_FEATURES:
1762 buf += bsprintf(buf, "features:");
1763 ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX);
1764 return GA_FULL;
1765
1766 default:;
1767 int id = (int)EA_ID(a->id) - KRT_METRICS_OFFSET;
1768 if (id > 0 && id < KRT_METRICS_MAX)
1769 {
1770 bsprintf(buf, "%s", krt_metrics_names[id]);
1771 return GA_NAME;
1772 }
1773
1774 return GA_UNKNOWN;
1775 }
1776}
1777
95616c82
OZ
1778
1779
1780void
1781kif_sys_start(struct kif_proto *p UNUSED)
1782{
1783 nl_open();
1784 nl_open_async();
1785}
1786
1787void
1788kif_sys_shutdown(struct kif_proto *p UNUSED)
1789{
1790}