]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* Kernel routing table updates using netlink over GNU/Linux system. |
2 | * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro | |
3 | * | |
4 | * This file is part of GNU Zebra. | |
5 | * | |
6 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * GNU Zebra is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GNU Zebra; see the file COPYING. If not, write to the Free | |
18 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
19 | * 02111-1307, USA. | |
20 | */ | |
21 | ||
22 | #include <zebra.h> | |
23 | ||
24 | /* Hack for GNU libc version 2. */ | |
25 | #ifndef MSG_TRUNC | |
26 | #define MSG_TRUNC 0x20 | |
27 | #endif /* MSG_TRUNC */ | |
28 | ||
29 | #include "linklist.h" | |
30 | #include "if.h" | |
31 | #include "log.h" | |
32 | #include "prefix.h" | |
33 | #include "connected.h" | |
34 | #include "table.h" | |
35 | #include "rib.h" | |
e04ab74d | 36 | #include "thread.h" |
edd7c245 | 37 | #include "privs.h" |
718e3744 | 38 | |
39 | #include "zebra/zserv.h" | |
40 | #include "zebra/redistribute.h" | |
41 | #include "zebra/interface.h" | |
42 | #include "zebra/debug.h" | |
43 | ||
44 | /* Socket interface to kernel */ | |
45 | struct nlsock | |
46 | { | |
47 | int sock; | |
48 | int seq; | |
49 | struct sockaddr_nl snl; | |
50 | char *name; | |
51 | } netlink = { -1, 0, {0}, "netlink-listen" }, /* kernel messages */ | |
52 | netlink_cmd = { -1, 0, {0}, "netlink-cmd" }, /* command channel */ | |
53 | netlink_addr = {-1, 0, {0}, "netlink-addr" }; /* address channel */ | |
54 | ||
55 | struct message nlmsg_str[] = | |
56 | { | |
57 | {RTM_NEWROUTE, "RTM_NEWROUTE"}, | |
58 | {RTM_DELROUTE, "RTM_DELROUTE"}, | |
59 | {RTM_GETROUTE, "RTM_GETROUTE"}, | |
60 | {RTM_NEWLINK, "RTM_NEWLINK"}, | |
61 | {RTM_DELLINK, "RTM_DELLINK"}, | |
62 | {RTM_GETLINK, "RTM_GETLINK"}, | |
63 | {RTM_NEWADDR, "RTM_NEWADDR"}, | |
64 | {RTM_DELADDR, "RTM_DELADDR"}, | |
65 | {RTM_GETADDR, "RTM_GETADDR"}, | |
66 | {0, NULL} | |
67 | }; | |
68 | ||
69 | extern int rtm_table_default; | |
70 | ||
edd7c245 | 71 | extern struct zebra_privs_t zserv_privs; |
72 | ||
718e3744 | 73 | /* Make socket for Linux netlink interface. */ |
74 | static int | |
75 | netlink_socket (struct nlsock *nl, unsigned long groups) | |
76 | { | |
77 | int ret; | |
78 | struct sockaddr_nl snl; | |
79 | int sock; | |
80 | int namelen; | |
81 | ||
82 | sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
83 | if (sock < 0) | |
84 | { | |
85 | zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name, | |
86 | strerror (errno)); | |
87 | return -1; | |
88 | } | |
89 | ||
90 | ret = fcntl (sock, F_SETFL, O_NONBLOCK); | |
91 | if (ret < 0) | |
92 | { | |
93 | zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", nl->name, | |
94 | strerror (errno)); | |
95 | close (sock); | |
96 | return -1; | |
97 | } | |
98 | ||
99 | memset (&snl, 0, sizeof snl); | |
100 | snl.nl_family = AF_NETLINK; | |
101 | snl.nl_groups = groups; | |
102 | ||
103 | /* Bind the socket to the netlink structure for anything. */ | |
edd7c245 | 104 | if ( zserv_privs.change(ZPRIVS_RAISE) ) |
105 | { | |
106 | zlog (NULL, LOG_ERR, "Can't raise privileges"); | |
107 | return -1; | |
108 | } | |
109 | ||
718e3744 | 110 | ret = bind (sock, (struct sockaddr *) &snl, sizeof snl); |
111 | if (ret < 0) | |
112 | { | |
edd7c245 | 113 | if ( zserv_privs.change(ZPRIVS_LOWER) ) |
114 | zlog (NULL, LOG_ERR, "Can't lower privileges"); | |
718e3744 | 115 | zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s", |
116 | nl->name, snl.nl_groups, strerror (errno)); | |
117 | close (sock); | |
118 | return -1; | |
119 | } | |
edd7c245 | 120 | |
121 | if ( zserv_privs.change(ZPRIVS_LOWER) ) | |
122 | zlog (NULL, LOG_ERR, "Can't lower privileges"); | |
718e3744 | 123 | |
124 | /* multiple netlink sockets will have different nl_pid */ | |
125 | namelen = sizeof snl; | |
126 | ret = getsockname (sock, (struct sockaddr *) &snl, &namelen); | |
127 | if (ret < 0 || namelen != sizeof snl) | |
128 | { | |
129 | zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name, | |
130 | strerror (errno)); | |
131 | close (sock); | |
132 | return -1; | |
133 | } | |
134 | ||
135 | nl->snl = snl; | |
136 | nl->sock = sock; | |
137 | return ret; | |
138 | } | |
139 | ||
5f37d86f | 140 | int set_netlink_blocking(struct nlsock *nl, int *flags) |
141 | { | |
142 | ||
143 | /* Change socket flags for blocking I/O. */ | |
144 | if((*flags = fcntl(nl->sock, F_GETFL, 0)) < 0) | |
145 | { | |
146 | zlog (NULL, LOG_ERR, "%s:%i F_GETFL error: %s", | |
147 | __FUNCTION__, __LINE__, strerror (errno)); | |
148 | return -1; | |
149 | } | |
150 | *flags &= ~O_NONBLOCK; | |
151 | if(fcntl(nl->sock, F_SETFL, *flags) < 0) | |
152 | { | |
153 | zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s", | |
154 | __FUNCTION__, __LINE__, strerror (errno)); | |
155 | return -1; | |
156 | } | |
157 | return 0; | |
158 | } | |
159 | ||
160 | int set_netlink_nonblocking(struct nlsock *nl, int *flags) | |
161 | { | |
162 | /* Restore socket flags for nonblocking I/O */ | |
163 | *flags |= O_NONBLOCK; | |
164 | if(fcntl(nl->sock, F_SETFL, *flags) < 0) | |
165 | { | |
166 | zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s", | |
167 | __FUNCTION__, __LINE__, strerror (errno)); | |
168 | return -1; | |
169 | } | |
170 | return 0; | |
171 | } | |
172 | ||
718e3744 | 173 | /* Get type specified information from netlink. */ |
174 | static int | |
175 | netlink_request (int family, int type, struct nlsock *nl) | |
176 | { | |
177 | int ret; | |
178 | struct sockaddr_nl snl; | |
179 | ||
180 | struct | |
181 | { | |
182 | struct nlmsghdr nlh; | |
183 | struct rtgenmsg g; | |
184 | } req; | |
185 | ||
186 | ||
187 | /* Check netlink socket. */ | |
188 | if (nl->sock < 0) | |
189 | { | |
190 | zlog (NULL, LOG_ERR, "%s socket isn't active.", nl->name); | |
191 | return -1; | |
192 | } | |
193 | ||
194 | memset (&snl, 0, sizeof snl); | |
195 | snl.nl_family = AF_NETLINK; | |
196 | ||
197 | req.nlh.nlmsg_len = sizeof req; | |
198 | req.nlh.nlmsg_type = type; | |
199 | req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; | |
200 | req.nlh.nlmsg_pid = 0; | |
201 | req.nlh.nlmsg_seq = ++nl->seq; | |
202 | req.g.rtgen_family = family; | |
edd7c245 | 203 | |
204 | /* linux appears to check capabilities on every message | |
205 | * have to raise caps for every message sent | |
206 | */ | |
207 | if ( zserv_privs.change(ZPRIVS_RAISE) ) | |
208 | { | |
209 | zlog (NULL, LOG_ERR, "Can't raise privileges"); | |
210 | return -1; | |
211 | } | |
718e3744 | 212 | |
213 | ret = sendto (nl->sock, (void*) &req, sizeof req, 0, | |
214 | (struct sockaddr*) &snl, sizeof snl); | |
edd7c245 | 215 | |
216 | if ( zserv_privs.change(ZPRIVS_LOWER) ) | |
217 | zlog (NULL, LOG_ERR, "Can't lower privileges"); | |
218 | ||
718e3744 | 219 | if (ret < 0) |
edd7c245 | 220 | { |
718e3744 | 221 | zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name, strerror (errno)); |
222 | return -1; | |
223 | } | |
edd7c245 | 224 | |
718e3744 | 225 | return 0; |
226 | } | |
227 | ||
228 | /* Receive message from netlink interface and pass those information | |
229 | to the given function. */ | |
230 | static int | |
231 | netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), | |
232 | struct nlsock *nl) | |
233 | { | |
234 | int status; | |
235 | int ret = 0; | |
236 | int error; | |
237 | ||
238 | while (1) | |
239 | { | |
240 | char buf[4096]; | |
241 | struct iovec iov = { buf, sizeof buf }; | |
242 | struct sockaddr_nl snl; | |
243 | struct msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0}; | |
244 | struct nlmsghdr *h; | |
245 | ||
edd7c245 | 246 | if ( zserv_privs.change(ZPRIVS_RAISE) ) |
247 | zlog (NULL, LOG_ERR, "Can't raise privileges"); | |
248 | ||
718e3744 | 249 | status = recvmsg (nl->sock, &msg, 0); |
edd7c245 | 250 | |
251 | if ( zserv_privs.change(ZPRIVS_LOWER) ) | |
252 | zlog (NULL, LOG_ERR, "Can't lower privileges"); | |
718e3744 | 253 | |
254 | if (status < 0) | |
255 | { | |
256 | if (errno == EINTR) | |
257 | continue; | |
258 | if (errno == EWOULDBLOCK || errno == EAGAIN) | |
259 | break; | |
260 | zlog (NULL, LOG_ERR, "%s recvmsg overrun", nl->name); | |
261 | continue; | |
262 | } | |
263 | ||
264 | if (status == 0) | |
265 | { | |
266 | zlog (NULL, LOG_ERR, "%s EOF", nl->name); | |
267 | return -1; | |
268 | } | |
269 | ||
270 | if (msg.msg_namelen != sizeof snl) | |
271 | { | |
272 | zlog (NULL, LOG_ERR, "%s sender address length error: length %d", | |
273 | nl->name, msg.msg_namelen); | |
274 | return -1; | |
275 | } | |
276 | ||
277 | for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, status); | |
278 | h = NLMSG_NEXT (h, status)) | |
279 | { | |
280 | /* Finish of reading. */ | |
281 | if (h->nlmsg_type == NLMSG_DONE) | |
282 | return ret; | |
283 | ||
284 | /* Error handling. */ | |
285 | if (h->nlmsg_type == NLMSG_ERROR) | |
286 | { | |
287 | struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h); | |
288 | ||
289 | /* If the error field is zero, then this is an ACK */ | |
290 | if (err->error == 0) | |
291 | { | |
292 | if (IS_ZEBRA_DEBUG_KERNEL) | |
293 | { | |
294 | zlog_info("%s: %s ACK: type=%s(%u), seq=%u, pid=%d", | |
295 | __FUNCTION__, nl->name, | |
296 | lookup (nlmsg_str, err->msg.nlmsg_type), | |
297 | err->msg.nlmsg_type, err->msg.nlmsg_seq, | |
298 | err->msg.nlmsg_pid); | |
299 | } | |
300 | ||
301 | /* return if not a multipart message, otherwise continue */ | |
302 | if(!(h->nlmsg_flags & NLM_F_MULTI)) | |
303 | { | |
304 | return 0; | |
305 | } | |
306 | continue; | |
307 | } | |
308 | ||
309 | if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) | |
310 | { | |
311 | zlog (NULL, LOG_ERR, "%s error: message truncated", | |
312 | nl->name); | |
313 | return -1; | |
314 | } | |
d753e9ee | 315 | |
316 | /* Deal with Error Noise - MAG*/ | |
317 | { | |
318 | int loglvl = LOG_ERR; | |
319 | int errnum = err->error; | |
320 | int msg_type = err->msg.nlmsg_type; | |
321 | ||
322 | if (nl == &netlink_cmd | |
323 | && (-errnum == ENODEV || -errnum == ESRCH) | |
324 | && (msg_type == RTM_NEWROUTE | |
325 | || msg_type == RTM_DELROUTE)) | |
326 | loglvl = LOG_DEBUG; | |
327 | ||
328 | zlog (NULL, loglvl, "%s error: %s, type=%s(%u), " | |
329 | "seq=%u, pid=%d", | |
330 | nl->name, strerror (-errnum), | |
331 | lookup (nlmsg_str, msg_type), | |
332 | msg_type, err->msg.nlmsg_seq, | |
718e3744 | 333 | err->msg.nlmsg_pid); |
d753e9ee | 334 | } |
718e3744 | 335 | /* |
336 | ret = -1; | |
337 | continue; | |
338 | */ | |
339 | return -1; | |
340 | } | |
341 | ||
342 | /* OK we got netlink message. */ | |
343 | if (IS_ZEBRA_DEBUG_KERNEL) | |
344 | zlog_info ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%d", | |
345 | nl->name, | |
346 | lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, | |
347 | h->nlmsg_seq, h->nlmsg_pid); | |
348 | ||
349 | /* skip unsolicited messages originating from command socket */ | |
350 | if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid) | |
351 | { | |
352 | if (IS_ZEBRA_DEBUG_KERNEL) | |
353 | zlog_info ("netlink_parse_info: %s packet comes from %s", | |
354 | nl->name, netlink_cmd.name); | |
355 | continue; | |
356 | } | |
357 | ||
358 | error = (*filter) (&snl, h); | |
359 | if (error < 0) | |
360 | { | |
361 | zlog (NULL, LOG_ERR, "%s filter function error", nl->name); | |
362 | ret = error; | |
363 | } | |
364 | } | |
365 | ||
366 | /* After error care. */ | |
367 | if (msg.msg_flags & MSG_TRUNC) | |
368 | { | |
369 | zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name); | |
370 | continue; | |
371 | } | |
372 | if (status) | |
373 | { | |
374 | zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name, | |
375 | status); | |
376 | return -1; | |
377 | } | |
378 | } | |
379 | return ret; | |
380 | } | |
381 | ||
382 | /* Utility function for parse rtattr. */ | |
383 | static void | |
384 | netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta, int len) | |
385 | { | |
386 | while (RTA_OK(rta, len)) | |
387 | { | |
388 | if (rta->rta_type <= max) | |
389 | tb[rta->rta_type] = rta; | |
390 | rta = RTA_NEXT(rta,len); | |
391 | } | |
392 | } | |
393 | ||
394 | /* Called from interface_lookup_netlink(). This function is only used | |
395 | during bootstrap. */ | |
396 | int | |
397 | netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
398 | { | |
399 | int len; | |
400 | struct ifinfomsg *ifi; | |
401 | struct rtattr *tb[IFLA_MAX + 1]; | |
402 | struct interface *ifp; | |
403 | char *name; | |
404 | int i; | |
405 | ||
406 | ifi = NLMSG_DATA (h); | |
407 | ||
408 | if (h->nlmsg_type != RTM_NEWLINK) | |
409 | return 0; | |
410 | ||
411 | len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg)); | |
412 | if (len < 0) | |
413 | return -1; | |
414 | ||
415 | /* Looking up interface name. */ | |
416 | memset (tb, 0, sizeof tb); | |
417 | netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len); | |
418 | if (tb[IFLA_IFNAME] == NULL) | |
419 | return -1; | |
420 | name = (char *)RTA_DATA(tb[IFLA_IFNAME]); | |
421 | ||
422 | /* Add interface. */ | |
423 | ifp = if_get_by_name (name); | |
424 | ||
425 | ifp->ifindex = ifi->ifi_index; | |
426 | ifp->flags = ifi->ifi_flags & 0x0000fffff; | |
427 | ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]); | |
428 | ifp->metric = 1; | |
429 | ||
430 | /* Hardware type and address. */ | |
431 | ifp->hw_type = ifi->ifi_type; | |
432 | ||
433 | if (tb[IFLA_ADDRESS]) | |
434 | { | |
435 | int hw_addr_len; | |
436 | ||
437 | hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); | |
438 | ||
439 | if (hw_addr_len > INTERFACE_HWADDR_MAX) | |
440 | zlog_warn ("Hardware address is too large: %d", hw_addr_len); | |
441 | else | |
442 | { | |
443 | ifp->hw_addr_len = hw_addr_len; | |
444 | memcpy (ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len); | |
445 | ||
446 | for (i = 0; i < hw_addr_len; i++) | |
447 | if (ifp->hw_addr[i] != 0) | |
448 | break; | |
449 | ||
450 | if (i == hw_addr_len) | |
451 | ifp->hw_addr_len = 0; | |
452 | else | |
453 | ifp->hw_addr_len = hw_addr_len; | |
454 | } | |
455 | } | |
456 | ||
457 | if_add_update (ifp); | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | /* Lookup interface IPv4/IPv6 address. */ | |
463 | int | |
464 | netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
465 | { | |
466 | int len; | |
467 | struct ifaddrmsg *ifa; | |
468 | struct rtattr *tb [IFA_MAX + 1]; | |
469 | struct interface *ifp; | |
470 | void *addr = NULL; | |
471 | void *broad = NULL; | |
472 | u_char flags = 0; | |
473 | char *label = NULL; | |
00df0c1e | 474 | int peeronly = 0; |
718e3744 | 475 | |
476 | ifa = NLMSG_DATA (h); | |
477 | ||
478 | if (ifa->ifa_family != AF_INET | |
479 | #ifdef HAVE_IPV6 | |
480 | && ifa->ifa_family != AF_INET6 | |
481 | #endif /* HAVE_IPV6 */ | |
482 | ) | |
483 | return 0; | |
484 | ||
485 | if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR) | |
486 | return 0; | |
487 | ||
488 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifaddrmsg)); | |
489 | if (len < 0) | |
490 | return -1; | |
491 | ||
492 | memset (tb, 0, sizeof tb); | |
493 | netlink_parse_rtattr (tb, IFA_MAX, IFA_RTA (ifa), len); | |
494 | ||
495 | ifp = if_lookup_by_index (ifa->ifa_index); | |
496 | if (ifp == NULL) | |
497 | { | |
498 | zlog_err ("netlink_interface_addr can't find interface by index %d", | |
499 | ifa->ifa_index); | |
500 | return -1; | |
501 | } | |
502 | ||
00df0c1e | 503 | if (IS_ZEBRA_DEBUG_KERNEL) /* remove this line to see initial ifcfg */ |
718e3744 | 504 | { |
00df0c1e | 505 | char buf[BUFSIZ]; |
506 | zlog_info ("netlink_interface_addr %s %s/%d:", | |
507 | lookup (nlmsg_str, h->nlmsg_type), | |
508 | ifp->name, ifa->ifa_prefixlen); | |
718e3744 | 509 | if (tb[IFA_LOCAL]) |
00df0c1e | 510 | zlog_info (" IFA_LOCAL %s", inet_ntop (ifa->ifa_family, |
511 | RTA_DATA (tb[IFA_LOCAL]), buf, BUFSIZ)); | |
718e3744 | 512 | if (tb[IFA_ADDRESS]) |
00df0c1e | 513 | zlog_info (" IFA_ADDRESS %s", inet_ntop (ifa->ifa_family, |
514 | RTA_DATA (tb[IFA_ADDRESS]), buf, BUFSIZ)); | |
718e3744 | 515 | if (tb[IFA_BROADCAST]) |
00df0c1e | 516 | zlog_info (" IFA_BROADCAST %s", inet_ntop (ifa->ifa_family, |
517 | RTA_DATA (tb[IFA_BROADCAST]), buf, BUFSIZ)); | |
518 | if (tb[IFA_LABEL] && strcmp (ifp->name, RTA_DATA (tb[IFA_LABEL]))) | |
519 | zlog_info (" IFA_LABEL %s", RTA_DATA (tb[IFA_LABEL])); | |
718e3744 | 520 | } |
521 | ||
00df0c1e | 522 | /* peer or broadcast network? */ |
523 | if (ifa->ifa_family == AF_INET) | |
524 | peeronly = if_is_pointopoint (ifp) || | |
525 | ifa->ifa_prefixlen >= IPV4_MAX_PREFIXLEN - 1; | |
526 | #ifdef HAVE_IPV6 | |
8fdcfdec | 527 | if (ifa->ifa_family == AF_INET6) { |
00df0c1e | 528 | peeronly = if_is_pointopoint (ifp) || |
529 | ifa->ifa_prefixlen >= IPV6_MAX_PREFIXLEN - 1; | |
8fdcfdec | 530 | } |
00df0c1e | 531 | #endif /* HAVE_IPV6*/ |
8fdcfdec | 532 | if (!(tb[IFA_LOCAL] && tb[IFA_ADDRESS])) { |
533 | /* FIXME: IPv6 Appears to have only IFA_ADDRESS */ | |
534 | peeronly=0; | |
535 | } | |
00df0c1e | 536 | |
537 | /* network. prefixlen applies to IFA_ADDRESS rather than IFA_LOCAL */ | |
538 | if (tb[IFA_ADDRESS] && !peeronly) | |
539 | addr = RTA_DATA (tb[IFA_ADDRESS]); | |
540 | else if (tb[IFA_LOCAL]) | |
541 | addr = RTA_DATA (tb[IFA_LOCAL]); | |
542 | else | |
543 | addr = NULL; | |
544 | ||
545 | /* broadcast/peer */ | |
546 | if (tb[IFA_BROADCAST]) | |
547 | broad = RTA_DATA (tb[IFA_BROADCAST]); | |
548 | else if (tb[IFA_ADDRESS] && peeronly) | |
549 | broad = RTA_DATA (tb[IFA_ADDRESS]); /* peer address specified */ | |
550 | else | |
551 | broad = NULL; | |
552 | ||
718e3744 | 553 | /* Flags. */ |
554 | if (ifa->ifa_flags & IFA_F_SECONDARY) | |
555 | SET_FLAG (flags, ZEBRA_IFA_SECONDARY); | |
556 | ||
557 | /* Label */ | |
558 | if (tb[IFA_LABEL]) | |
559 | label = (char *) RTA_DATA (tb[IFA_LABEL]); | |
560 | ||
561 | if (ifp && label && strcmp (ifp->name, label) == 0) | |
562 | label = NULL; | |
563 | ||
564 | /* Register interface address to the interface. */ | |
565 | if (ifa->ifa_family == AF_INET) | |
566 | { | |
567 | if (h->nlmsg_type == RTM_NEWADDR) | |
568 | connected_add_ipv4 (ifp, flags, | |
569 | (struct in_addr *) addr, ifa->ifa_prefixlen, | |
570 | (struct in_addr *) broad, label); | |
571 | else | |
572 | connected_delete_ipv4 (ifp, flags, | |
573 | (struct in_addr *) addr, ifa->ifa_prefixlen, | |
574 | (struct in_addr *) broad, label); | |
575 | } | |
576 | #ifdef HAVE_IPV6 | |
577 | if (ifa->ifa_family == AF_INET6) | |
578 | { | |
579 | if (h->nlmsg_type == RTM_NEWADDR) | |
580 | connected_add_ipv6 (ifp, | |
581 | (struct in6_addr *) addr, ifa->ifa_prefixlen, | |
582 | (struct in6_addr *) broad); | |
583 | else | |
584 | connected_delete_ipv6 (ifp, | |
585 | (struct in6_addr *) addr, ifa->ifa_prefixlen, | |
586 | (struct in6_addr *) broad); | |
587 | } | |
588 | #endif /* HAVE_IPV6*/ | |
589 | ||
590 | return 0; | |
591 | } | |
592 | ||
593 | /* Looking up routing table by netlink interface. */ | |
594 | int | |
595 | netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
596 | { | |
597 | int len; | |
598 | struct rtmsg *rtm; | |
599 | struct rtattr *tb [RTA_MAX + 1]; | |
600 | u_char flags = 0; | |
601 | ||
602 | char anyaddr[16] = {0}; | |
603 | ||
604 | int index; | |
605 | int table; | |
606 | void *dest; | |
607 | void *gate; | |
608 | ||
609 | rtm = NLMSG_DATA (h); | |
610 | ||
611 | if (h->nlmsg_type != RTM_NEWROUTE) | |
612 | return 0; | |
613 | if (rtm->rtm_type != RTN_UNICAST) | |
614 | return 0; | |
615 | ||
616 | table = rtm->rtm_table; | |
617 | #if 0 /* we weed them out later in rib_weed_tables () */ | |
618 | if (table != RT_TABLE_MAIN && table != rtm_table_default) | |
619 | return 0; | |
620 | #endif | |
621 | ||
622 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct rtmsg)); | |
623 | if (len < 0) | |
624 | return -1; | |
625 | ||
626 | memset (tb, 0, sizeof tb); | |
627 | netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len); | |
628 | ||
629 | if (rtm->rtm_flags & RTM_F_CLONED) | |
630 | return 0; | |
631 | if (rtm->rtm_protocol == RTPROT_REDIRECT) | |
632 | return 0; | |
633 | if (rtm->rtm_protocol == RTPROT_KERNEL) | |
634 | return 0; | |
635 | ||
636 | if (rtm->rtm_src_len != 0) | |
637 | return 0; | |
638 | ||
639 | /* Route which inserted by Zebra. */ | |
640 | if (rtm->rtm_protocol == RTPROT_ZEBRA) | |
641 | flags |= ZEBRA_FLAG_SELFROUTE; | |
642 | ||
643 | index = 0; | |
644 | dest = NULL; | |
645 | gate = NULL; | |
646 | ||
647 | if (tb[RTA_OIF]) | |
648 | index = *(int *) RTA_DATA (tb[RTA_OIF]); | |
649 | ||
650 | if (tb[RTA_DST]) | |
651 | dest = RTA_DATA (tb[RTA_DST]); | |
652 | else | |
653 | dest = anyaddr; | |
654 | ||
655 | /* Multipath treatment is needed. */ | |
656 | if (tb[RTA_GATEWAY]) | |
657 | gate = RTA_DATA (tb[RTA_GATEWAY]); | |
658 | ||
659 | if (rtm->rtm_family == AF_INET) | |
660 | { | |
661 | struct prefix_ipv4 p; | |
662 | p.family = AF_INET; | |
663 | memcpy (&p.prefix, dest, 4); | |
664 | p.prefixlen = rtm->rtm_dst_len; | |
665 | ||
666 | rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table, 0, 0); | |
667 | } | |
668 | #ifdef HAVE_IPV6 | |
669 | if (rtm->rtm_family == AF_INET6) | |
670 | { | |
671 | struct prefix_ipv6 p; | |
672 | p.family = AF_INET6; | |
673 | memcpy (&p.prefix, dest, 16); | |
674 | p.prefixlen = rtm->rtm_dst_len; | |
675 | ||
676 | rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table); | |
677 | } | |
678 | #endif /* HAVE_IPV6 */ | |
679 | ||
680 | return 0; | |
681 | } | |
682 | ||
683 | struct message rtproto_str [] = | |
684 | { | |
685 | {RTPROT_REDIRECT, "redirect"}, | |
686 | {RTPROT_KERNEL, "kernel"}, | |
687 | {RTPROT_BOOT, "boot"}, | |
688 | {RTPROT_STATIC, "static"}, | |
689 | {RTPROT_GATED, "GateD"}, | |
690 | {RTPROT_RA, "router advertisement"}, | |
691 | {RTPROT_MRT, "MRT"}, | |
692 | {RTPROT_ZEBRA, "Zebra"}, | |
693 | #ifdef RTPROT_BIRD | |
694 | {RTPROT_BIRD, "BIRD"}, | |
695 | #endif /* RTPROT_BIRD */ | |
696 | {0, NULL} | |
697 | }; | |
698 | ||
699 | /* Routing information change from the kernel. */ | |
700 | int | |
701 | netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
702 | { | |
703 | int len; | |
704 | struct rtmsg *rtm; | |
705 | struct rtattr *tb [RTA_MAX + 1]; | |
706 | ||
707 | char anyaddr[16] = {0}; | |
708 | ||
709 | int index; | |
710 | int table; | |
711 | void *dest; | |
712 | void *gate; | |
713 | ||
714 | rtm = NLMSG_DATA (h); | |
715 | ||
716 | if (! (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) | |
717 | { | |
718 | /* If this is not route add/delete message print warning. */ | |
719 | zlog_warn ("Kernel message: %d\n", h->nlmsg_type); | |
720 | return 0; | |
721 | } | |
722 | ||
723 | /* Connected route. */ | |
724 | if (IS_ZEBRA_DEBUG_KERNEL) | |
725 | zlog_info ("%s %s %s proto %s", | |
726 | h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE", | |
727 | rtm->rtm_family == AF_INET ? "ipv4" : "ipv6", | |
728 | rtm->rtm_type == RTN_UNICAST ? "unicast" : "multicast", | |
729 | lookup (rtproto_str, rtm->rtm_protocol)); | |
730 | ||
731 | if (rtm->rtm_type != RTN_UNICAST) | |
732 | { | |
733 | return 0; | |
734 | } | |
735 | ||
736 | table = rtm->rtm_table; | |
737 | if (table != RT_TABLE_MAIN && table != rtm_table_default) | |
738 | { | |
739 | return 0; | |
740 | } | |
741 | ||
742 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct rtmsg)); | |
743 | if (len < 0) | |
744 | return -1; | |
745 | ||
746 | memset (tb, 0, sizeof tb); | |
747 | netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len); | |
748 | ||
749 | if (rtm->rtm_flags & RTM_F_CLONED) | |
750 | return 0; | |
751 | if (rtm->rtm_protocol == RTPROT_REDIRECT) | |
752 | return 0; | |
753 | if (rtm->rtm_protocol == RTPROT_KERNEL) | |
754 | return 0; | |
755 | ||
756 | if (rtm->rtm_protocol == RTPROT_ZEBRA && h->nlmsg_type == RTM_NEWROUTE) | |
757 | return 0; | |
758 | ||
759 | if (rtm->rtm_src_len != 0) | |
760 | { | |
761 | zlog_warn ("netlink_route_change(): no src len"); | |
762 | return 0; | |
763 | } | |
764 | ||
765 | index = 0; | |
766 | dest = NULL; | |
767 | gate = NULL; | |
768 | ||
769 | if (tb[RTA_OIF]) | |
770 | index = *(int *) RTA_DATA (tb[RTA_OIF]); | |
771 | ||
772 | if (tb[RTA_DST]) | |
773 | dest = RTA_DATA (tb[RTA_DST]); | |
774 | else | |
775 | dest = anyaddr; | |
776 | ||
777 | if (tb[RTA_GATEWAY]) | |
778 | gate = RTA_DATA (tb[RTA_GATEWAY]); | |
779 | ||
780 | if (rtm->rtm_family == AF_INET) | |
781 | { | |
782 | struct prefix_ipv4 p; | |
783 | p.family = AF_INET; | |
784 | memcpy (&p.prefix, dest, 4); | |
785 | p.prefixlen = rtm->rtm_dst_len; | |
786 | ||
787 | if (IS_ZEBRA_DEBUG_KERNEL) | |
788 | { | |
789 | if (h->nlmsg_type == RTM_NEWROUTE) | |
790 | zlog_info ("RTM_NEWROUTE %s/%d", | |
791 | inet_ntoa (p.prefix), p.prefixlen); | |
792 | else | |
793 | zlog_info ("RTM_DELROUTE %s/%d", | |
794 | inet_ntoa (p.prefix), p.prefixlen); | |
795 | } | |
796 | ||
797 | if (h->nlmsg_type == RTM_NEWROUTE) | |
798 | rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, 0, 0); | |
799 | else | |
800 | rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table); | |
801 | } | |
802 | ||
803 | #ifdef HAVE_IPV6 | |
804 | if (rtm->rtm_family == AF_INET6) | |
805 | { | |
806 | struct prefix_ipv6 p; | |
807 | char buf[BUFSIZ]; | |
808 | ||
809 | p.family = AF_INET6; | |
810 | memcpy (&p.prefix, dest, 16); | |
811 | p.prefixlen = rtm->rtm_dst_len; | |
812 | ||
813 | if (IS_ZEBRA_DEBUG_KERNEL) | |
814 | { | |
815 | if (h->nlmsg_type == RTM_NEWROUTE) | |
816 | zlog_info ("RTM_NEWROUTE %s/%d", | |
817 | inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ), | |
818 | p.prefixlen); | |
819 | else | |
820 | zlog_info ("RTM_DELROUTE %s/%d", | |
821 | inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ), | |
822 | p.prefixlen); | |
823 | } | |
824 | ||
825 | if (h->nlmsg_type == RTM_NEWROUTE) | |
826 | rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0); | |
827 | else | |
828 | rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0); | |
829 | } | |
830 | #endif /* HAVE_IPV6 */ | |
831 | ||
832 | return 0; | |
833 | } | |
834 | ||
835 | int | |
836 | netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
837 | { | |
838 | int len; | |
839 | struct ifinfomsg *ifi; | |
840 | struct rtattr *tb [IFLA_MAX + 1]; | |
841 | struct interface *ifp; | |
842 | char *name; | |
843 | ||
844 | ifi = NLMSG_DATA (h); | |
845 | ||
846 | if (! (h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) | |
847 | { | |
848 | /* If this is not link add/delete message so print warning. */ | |
849 | zlog_warn ("netlink_link_change: wrong kernel message %d\n", | |
850 | h->nlmsg_type); | |
851 | return 0; | |
852 | } | |
853 | ||
854 | len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg)); | |
855 | if (len < 0) | |
856 | return -1; | |
857 | ||
858 | /* Looking up interface name. */ | |
859 | memset (tb, 0, sizeof tb); | |
860 | netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len); | |
861 | if (tb[IFLA_IFNAME] == NULL) | |
862 | return -1; | |
863 | name = (char *)RTA_DATA(tb[IFLA_IFNAME]); | |
864 | ||
865 | /* Add interface. */ | |
866 | if (h->nlmsg_type == RTM_NEWLINK) | |
867 | { | |
868 | ifp = if_lookup_by_name (name); | |
869 | ||
870 | if (ifp == NULL || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) | |
871 | { | |
872 | if (ifp == NULL) | |
873 | ifp = if_get_by_name (name); | |
874 | ||
875 | ifp->ifindex = ifi->ifi_index; | |
876 | ifp->flags = ifi->ifi_flags & 0x0000fffff; | |
877 | ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]); | |
878 | ifp->metric = 1; | |
879 | ||
880 | /* If new link is added. */ | |
881 | if_add_update(ifp); | |
882 | } | |
883 | else | |
884 | { | |
885 | /* Interface status change. */ | |
886 | ifp->ifindex = ifi->ifi_index; | |
887 | ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]); | |
888 | ifp->metric = 1; | |
889 | ||
2e3b2e47 | 890 | if (if_is_operative (ifp)) |
718e3744 | 891 | { |
892 | ifp->flags = ifi->ifi_flags & 0x0000fffff; | |
2e3b2e47 | 893 | if (! if_is_operative (ifp)) |
718e3744 | 894 | if_down (ifp); |
895 | } | |
896 | else | |
897 | { | |
898 | ifp->flags = ifi->ifi_flags & 0x0000fffff; | |
2e3b2e47 | 899 | if (if_is_operative (ifp)) |
718e3744 | 900 | if_up (ifp); |
901 | } | |
902 | } | |
903 | } | |
904 | else | |
905 | { | |
906 | /* RTM_DELLINK. */ | |
907 | ifp = if_lookup_by_name (name); | |
908 | ||
909 | if (ifp == NULL) | |
910 | { | |
911 | zlog (NULL, LOG_WARNING, "interface %s is deleted but can't find", | |
912 | name); | |
913 | return 0; | |
914 | } | |
915 | ||
916 | if_delete_update (ifp); | |
917 | } | |
918 | ||
919 | return 0; | |
920 | } | |
921 | ||
922 | int | |
923 | netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
924 | { | |
925 | switch (h->nlmsg_type) | |
926 | { | |
927 | case RTM_NEWROUTE: | |
928 | return netlink_route_change (snl, h); | |
929 | break; | |
930 | case RTM_DELROUTE: | |
931 | return netlink_route_change (snl, h); | |
932 | break; | |
933 | case RTM_NEWLINK: | |
934 | return netlink_link_change (snl, h); | |
935 | break; | |
936 | case RTM_DELLINK: | |
937 | return netlink_link_change (snl, h); | |
938 | break; | |
939 | case RTM_NEWADDR: | |
940 | return netlink_interface_addr (snl, h); | |
941 | break; | |
942 | case RTM_DELADDR: | |
943 | return netlink_interface_addr (snl, h); | |
944 | break; | |
945 | default: | |
946 | zlog_warn ("Unknown netlink nlmsg_type %d\n", h->nlmsg_type); | |
947 | break; | |
948 | } | |
949 | return 0; | |
950 | } | |
951 | ||
952 | /* Interface lookup by netlink socket. */ | |
953 | int | |
954 | interface_lookup_netlink () | |
955 | { | |
956 | int ret; | |
5f37d86f | 957 | int flags; |
958 | int snb_ret; | |
959 | ||
960 | /* | |
961 | * Change netlink socket flags to blocking to ensure we get | |
962 | * a reply via nelink_parse_info | |
963 | */ | |
964 | snb_ret = set_netlink_blocking(&netlink_cmd, &flags); | |
965 | if(snb_ret < 0) | |
966 | zlog (NULL, LOG_WARNING, | |
967 | "%s:%i Warning: Could not set netlink socket to blocking.", | |
968 | __FUNCTION__, __LINE__); | |
969 | ||
718e3744 | 970 | /* Get interface information. */ |
971 | ret = netlink_request (AF_PACKET, RTM_GETLINK, &netlink_cmd); | |
972 | if (ret < 0) | |
973 | return ret; | |
974 | ret = netlink_parse_info (netlink_interface, &netlink_cmd); | |
975 | if (ret < 0) | |
976 | return ret; | |
977 | ||
978 | /* Get IPv4 address of the interfaces. */ | |
979 | ret = netlink_request (AF_INET, RTM_GETADDR, &netlink_cmd); | |
980 | if (ret < 0) | |
981 | return ret; | |
982 | ret = netlink_parse_info (netlink_interface_addr, &netlink_cmd); | |
983 | if (ret < 0) | |
984 | return ret; | |
985 | ||
986 | #ifdef HAVE_IPV6 | |
987 | /* Get IPv6 address of the interfaces. */ | |
988 | ret = netlink_request (AF_INET6, RTM_GETADDR, &netlink_cmd); | |
989 | if (ret < 0) | |
990 | return ret; | |
991 | ret = netlink_parse_info (netlink_interface_addr, &netlink_cmd); | |
992 | if (ret < 0) | |
993 | return ret; | |
994 | #endif /* HAVE_IPV6 */ | |
995 | ||
5f37d86f | 996 | /* restore socket flags */ |
997 | if(snb_ret == 0) | |
998 | set_netlink_nonblocking(&netlink_cmd, &flags); | |
718e3744 | 999 | return 0; |
1000 | } | |
1001 | ||
1002 | /* Routing table read function using netlink interface. Only called | |
1003 | bootstrap time. */ | |
1004 | int | |
1005 | netlink_route_read () | |
1006 | { | |
1007 | int ret; | |
5f37d86f | 1008 | int flags; |
1009 | int snb_ret; | |
1010 | ||
1011 | /* | |
1012 | * Change netlink socket flags to blocking to ensure we get | |
1013 | * a reply via nelink_parse_info | |
1014 | */ | |
1015 | snb_ret = set_netlink_blocking(&netlink_cmd, &flags); | |
1016 | if(snb_ret < 0) | |
1017 | zlog (NULL, LOG_WARNING, | |
1018 | "%s:%i Warning: Could not set netlink socket to blocking.", | |
1019 | __FUNCTION__, __LINE__); | |
1020 | ||
718e3744 | 1021 | /* Get IPv4 routing table. */ |
1022 | ret = netlink_request (AF_INET, RTM_GETROUTE, &netlink_cmd); | |
1023 | if (ret < 0) | |
1024 | return ret; | |
1025 | ret = netlink_parse_info (netlink_routing_table, &netlink_cmd); | |
1026 | if (ret < 0) | |
1027 | return ret; | |
1028 | ||
1029 | #ifdef HAVE_IPV6 | |
1030 | /* Get IPv6 routing table. */ | |
1031 | ret = netlink_request (AF_INET6, RTM_GETROUTE, &netlink_cmd); | |
1032 | if (ret < 0) | |
1033 | return ret; | |
1034 | ret = netlink_parse_info (netlink_routing_table, &netlink_cmd); | |
1035 | if (ret < 0) | |
1036 | return ret; | |
1037 | #endif /* HAVE_IPV6 */ | |
1038 | ||
5f37d86f | 1039 | /* restore flags */ |
1040 | if(snb_ret == 0) | |
1041 | set_netlink_nonblocking(&netlink_cmd, &flags); | |
718e3744 | 1042 | return 0; |
1043 | } | |
1044 | ||
1045 | /* Utility function comes from iproute2. | |
1046 | Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */ | |
1047 | int | |
1048 | addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) | |
1049 | { | |
1050 | int len; | |
1051 | struct rtattr *rta; | |
1052 | ||
1053 | len = RTA_LENGTH(alen); | |
1054 | ||
1055 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) | |
1056 | return -1; | |
1057 | ||
1058 | rta = (struct rtattr*) (((char*)n) + NLMSG_ALIGN (n->nlmsg_len)); | |
1059 | rta->rta_type = type; | |
1060 | rta->rta_len = len; | |
1061 | memcpy (RTA_DATA(rta), data, alen); | |
1062 | n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; | |
1063 | ||
1064 | return 0; | |
1065 | } | |
1066 | ||
1067 | int | |
1068 | rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) | |
1069 | { | |
1070 | int len; | |
1071 | struct rtattr *subrta; | |
1072 | ||
1073 | len = RTA_LENGTH(alen); | |
1074 | ||
1075 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) | |
1076 | return -1; | |
1077 | ||
1078 | subrta = (struct rtattr*) (((char*)rta) + RTA_ALIGN (rta->rta_len)); | |
1079 | subrta->rta_type = type; | |
1080 | subrta->rta_len = len; | |
1081 | memcpy (RTA_DATA(subrta), data, alen); | |
1082 | rta->rta_len = NLMSG_ALIGN (rta->rta_len) + len; | |
1083 | ||
1084 | return 0; | |
1085 | } | |
1086 | ||
1087 | /* Utility function comes from iproute2. | |
1088 | Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */ | |
1089 | int | |
1090 | addattr32 (struct nlmsghdr *n, int maxlen, int type, int data) | |
1091 | { | |
1092 | int len; | |
1093 | struct rtattr *rta; | |
1094 | ||
1095 | len = RTA_LENGTH(4); | |
1096 | ||
1097 | if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) | |
1098 | return -1; | |
1099 | ||
1100 | rta = (struct rtattr*) (((char*)n) + NLMSG_ALIGN (n->nlmsg_len)); | |
1101 | rta->rta_type = type; | |
1102 | rta->rta_len = len; | |
1103 | memcpy (RTA_DATA(rta), &data, 4); | |
1104 | n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; | |
1105 | ||
1106 | return 0; | |
1107 | } | |
1108 | ||
1109 | static int | |
1110 | netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h) | |
1111 | { | |
1112 | zlog_warn ("netlink_talk: ignoring message type 0x%04x", h->nlmsg_type); | |
1113 | return 0; | |
1114 | } | |
1115 | ||
1116 | /* sendmsg() to netlink socket then recvmsg(). */ | |
1117 | int | |
1118 | netlink_talk (struct nlmsghdr *n, struct nlsock *nl) | |
1119 | { | |
1120 | int status; | |
1121 | struct sockaddr_nl snl; | |
1122 | struct iovec iov = { (void*) n, n->nlmsg_len }; | |
1123 | struct msghdr msg = {(void*) &snl, sizeof snl, &iov, 1, NULL, 0, 0}; | |
1124 | int flags = 0; | |
5f37d86f | 1125 | int snb_ret; |
718e3744 | 1126 | |
1127 | memset (&snl, 0, sizeof snl); | |
1128 | snl.nl_family = AF_NETLINK; | |
1129 | ||
1130 | n->nlmsg_seq = ++netlink_cmd.seq; | |
1131 | ||
1132 | /* Request an acknowledgement by setting NLM_F_ACK */ | |
1133 | n->nlmsg_flags |= NLM_F_ACK; | |
1134 | ||
1135 | if (IS_ZEBRA_DEBUG_KERNEL) | |
1136 | zlog_info ("netlink_talk: %s type %s(%u), seq=%u", netlink_cmd.name, | |
1137 | lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, | |
1138 | n->nlmsg_seq); | |
1139 | ||
1140 | /* Send message to netlink interface. */ | |
edd7c245 | 1141 | if ( zserv_privs.change(ZPRIVS_RAISE) ) |
1142 | zlog (NULL, LOG_ERR, "Can't raise privileges"); | |
718e3744 | 1143 | status = sendmsg (nl->sock, &msg, 0); |
edd7c245 | 1144 | if ( zserv_privs.change(ZPRIVS_LOWER) ) |
1145 | zlog (NULL, LOG_ERR, "Can't lower privileges"); | |
1146 | ||
718e3744 | 1147 | if (status < 0) |
1148 | { | |
1149 | zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s", | |
1150 | strerror (errno)); | |
1151 | return -1; | |
1152 | } | |
1153 | ||
1154 | /* | |
1155 | * Change socket flags for blocking I/O. | |
1156 | * This ensures we wait for a reply in netlink_parse_info(). | |
1157 | */ | |
5f37d86f | 1158 | snb_ret = set_netlink_blocking(nl, &flags); |
1159 | if(snb_ret < 0) | |
1160 | zlog (NULL, LOG_WARNING, | |
1161 | "%s:%i Warning: Could not set netlink socket to blocking.", | |
1162 | __FUNCTION__, __LINE__); | |
718e3744 | 1163 | |
1164 | /* | |
1165 | * Get reply from netlink socket. | |
1166 | * The reply should either be an acknowlegement or an error. | |
1167 | */ | |
1168 | status = netlink_parse_info (netlink_talk_filter, nl); | |
1169 | ||
1170 | /* Restore socket flags for nonblocking I/O */ | |
5f37d86f | 1171 | if(snb_ret == 0) |
1172 | set_netlink_nonblocking(nl, &flags); | |
718e3744 | 1173 | |
1174 | return status; | |
1175 | } | |
1176 | ||
1177 | /* Routing table change via netlink interface. */ | |
1178 | int | |
1179 | netlink_route (int cmd, int family, void *dest, int length, void *gate, | |
1180 | int index, int zebra_flags, int table) | |
1181 | { | |
1182 | int ret; | |
1183 | int bytelen; | |
1184 | struct sockaddr_nl snl; | |
1185 | int discard; | |
1186 | ||
1187 | struct | |
1188 | { | |
1189 | struct nlmsghdr n; | |
1190 | struct rtmsg r; | |
1191 | char buf[1024]; | |
1192 | } req; | |
1193 | ||
1194 | memset (&req, 0, sizeof req); | |
1195 | ||
1196 | bytelen = (family == AF_INET ? 4 : 16); | |
1197 | ||
1198 | req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); | |
1199 | req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; | |
1200 | req.n.nlmsg_type = cmd; | |
1201 | req.r.rtm_family = family; | |
1202 | req.r.rtm_table = table; | |
1203 | req.r.rtm_dst_len = length; | |
1204 | ||
81dfcaa2 | 1205 | if ((zebra_flags & ZEBRA_FLAG_BLACKHOLE) |
1206 | || (zebra_flags & ZEBRA_FLAG_REJECT)) | |
718e3744 | 1207 | discard = 1; |
1208 | else | |
1209 | discard = 0; | |
1210 | ||
1211 | if (cmd == RTM_NEWROUTE) | |
1212 | { | |
1213 | req.r.rtm_protocol = RTPROT_ZEBRA; | |
1214 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | |
1215 | ||
595db7f1 | 1216 | if (discard) |
1217 | { | |
1218 | if (zebra_flags & ZEBRA_FLAG_BLACKHOLE) | |
1219 | req.r.rtm_type = RTN_BLACKHOLE; | |
1220 | else if (zebra_flags & ZEBRA_FLAG_REJECT) | |
1221 | req.r.rtm_type = RTN_UNREACHABLE; | |
1222 | else assert(RTN_BLACKHOLE != RTN_UNREACHABLE); /* false */ | |
1223 | } | |
1224 | else | |
1225 | req.r.rtm_type = RTN_UNICAST; | |
718e3744 | 1226 | } |
1227 | ||
1228 | if (dest) | |
1229 | addattr_l (&req.n, sizeof req, RTA_DST, dest, bytelen); | |
1230 | ||
1231 | if (! discard) | |
1232 | { | |
1233 | if (gate) | |
1234 | addattr_l (&req.n, sizeof req, RTA_GATEWAY, gate, bytelen); | |
1235 | if (index > 0) | |
1236 | addattr32 (&req.n, sizeof req, RTA_OIF, index); | |
1237 | } | |
1238 | ||
1239 | /* Destination netlink address. */ | |
1240 | memset (&snl, 0, sizeof snl); | |
1241 | snl.nl_family = AF_NETLINK; | |
1242 | ||
1243 | /* Talk to netlink socket. */ | |
1244 | ret = netlink_talk (&req.n, &netlink); | |
1245 | if (ret < 0) | |
1246 | return -1; | |
1247 | ||
1248 | return 0; | |
1249 | } | |
1250 | ||
1251 | /* Routing table change via netlink interface. */ | |
1252 | int | |
1253 | netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, | |
1254 | int family) | |
1255 | { | |
1256 | int bytelen; | |
1257 | struct sockaddr_nl snl; | |
1258 | struct nexthop *nexthop = NULL; | |
1259 | int nexthop_num = 0; | |
1260 | struct nlsock *nl; | |
1261 | int discard; | |
1262 | ||
1263 | struct | |
1264 | { | |
1265 | struct nlmsghdr n; | |
1266 | struct rtmsg r; | |
1267 | char buf[1024]; | |
1268 | } req; | |
1269 | ||
1270 | memset (&req, 0, sizeof req); | |
1271 | ||
1272 | bytelen = (family == AF_INET ? 4 : 16); | |
1273 | ||
1274 | req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); | |
1275 | req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; | |
1276 | req.n.nlmsg_type = cmd; | |
1277 | req.r.rtm_family = family; | |
1278 | req.r.rtm_table = rib->table; | |
1279 | req.r.rtm_dst_len = p->prefixlen; | |
1280 | ||
13766da4 | 1281 | #ifdef RTM_F_EQUALIZE |
1282 | req.r.rtm_flags |= RTM_F_EQUALIZE; | |
1283 | #endif /* RTM_F_EQUALIZE */ | |
1284 | ||
81dfcaa2 | 1285 | if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) |
1286 | || (rib->flags & ZEBRA_FLAG_REJECT)) | |
718e3744 | 1287 | discard = 1; |
1288 | else | |
1289 | discard = 0; | |
1290 | ||
1291 | if (cmd == RTM_NEWROUTE) | |
1292 | { | |
1293 | req.r.rtm_protocol = RTPROT_ZEBRA; | |
1294 | req.r.rtm_scope = RT_SCOPE_UNIVERSE; | |
1295 | ||
595db7f1 | 1296 | if (discard) |
1297 | { | |
1298 | if (rib->flags & ZEBRA_FLAG_BLACKHOLE) | |
1299 | req.r.rtm_type = RTN_BLACKHOLE; | |
1300 | else if (rib->flags & ZEBRA_FLAG_REJECT) | |
1301 | req.r.rtm_type = RTN_UNREACHABLE; | |
1302 | else assert(RTN_BLACKHOLE != RTN_UNREACHABLE); /* false */ | |
1303 | } | |
1304 | else | |
1305 | req.r.rtm_type = RTN_UNICAST; | |
718e3744 | 1306 | } |
1307 | ||
1308 | addattr_l (&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); | |
1309 | ||
1310 | /* Metric. */ | |
1311 | addattr32 (&req.n, sizeof req, RTA_PRIORITY, rib->metric); | |
1312 | ||
1313 | if (discard) | |
1314 | { | |
1315 | if (cmd == RTM_NEWROUTE) | |
1316 | for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) | |
1317 | SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); | |
1318 | goto skip; | |
1319 | } | |
1320 | ||
1321 | /* Multipath case. */ | |
1322 | if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1) | |
1323 | { | |
1324 | for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) | |
1325 | { | |
1326 | if ((cmd == RTM_NEWROUTE | |
1327 | && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) | |
1328 | || (cmd == RTM_DELROUTE | |
1329 | && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) | |
1330 | { | |
1331 | if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
1332 | { | |
1333 | if (nexthop->rtype == NEXTHOP_TYPE_IPV4 | |
1334 | || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) | |
1335 | addattr_l (&req.n, sizeof req, RTA_GATEWAY, | |
1336 | &nexthop->rgate.ipv4, bytelen); | |
1337 | #ifdef HAVE_IPV6 | |
1338 | if (nexthop->rtype == NEXTHOP_TYPE_IPV6 | |
1339 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX | |
1340 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) | |
1341 | addattr_l (&req.n, sizeof req, RTA_GATEWAY, | |
1342 | &nexthop->rgate.ipv6, bytelen); | |
1343 | #endif /* HAVE_IPV6 */ | |
1344 | if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX | |
1345 | || nexthop->rtype == NEXTHOP_TYPE_IFNAME | |
1346 | || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX | |
1347 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX | |
1348 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) | |
1349 | addattr32 (&req.n, sizeof req, RTA_OIF, | |
1350 | nexthop->rifindex); | |
1351 | } | |
1352 | else | |
1353 | { | |
1354 | if (nexthop->type == NEXTHOP_TYPE_IPV4 | |
1355 | || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) | |
1356 | addattr_l (&req.n, sizeof req, RTA_GATEWAY, | |
1357 | &nexthop->gate.ipv4, bytelen); | |
1358 | #ifdef HAVE_IPV6 | |
1359 | if (nexthop->type == NEXTHOP_TYPE_IPV6 | |
1360 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME | |
1361 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1362 | addattr_l (&req.n, sizeof req, RTA_GATEWAY, | |
1363 | &nexthop->gate.ipv6, bytelen); | |
1364 | #endif /* HAVE_IPV6 */ | |
1365 | if (nexthop->type == NEXTHOP_TYPE_IFINDEX | |
1366 | || nexthop->type == NEXTHOP_TYPE_IFNAME | |
1367 | || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX | |
1368 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX | |
1369 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) | |
1370 | addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex); | |
1371 | } | |
1372 | ||
1373 | if (cmd == RTM_NEWROUTE) | |
1374 | SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); | |
1375 | ||
1376 | nexthop_num++; | |
1377 | break; | |
1378 | } | |
1379 | } | |
1380 | } | |
1381 | else | |
1382 | { | |
1383 | char buf[1024]; | |
1384 | struct rtattr *rta = (void *) buf; | |
1385 | struct rtnexthop *rtnh; | |
1386 | ||
1387 | rta->rta_type = RTA_MULTIPATH; | |
1388 | rta->rta_len = RTA_LENGTH(0); | |
1389 | rtnh = RTA_DATA(rta); | |
1390 | ||
1391 | nexthop_num = 0; | |
1392 | for (nexthop = rib->nexthop; | |
1393 | nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM); | |
1394 | nexthop = nexthop->next) | |
1395 | { | |
1396 | if ((cmd == RTM_NEWROUTE | |
1397 | && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) | |
1398 | || (cmd == RTM_DELROUTE | |
1399 | && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) | |
1400 | { | |
1401 | nexthop_num++; | |
1402 | ||
1403 | rtnh->rtnh_len = sizeof (*rtnh); | |
1404 | rtnh->rtnh_flags = 0; | |
1405 | rtnh->rtnh_hops = 0; | |
1406 | rta->rta_len += rtnh->rtnh_len; | |
1407 | ||
1408 | if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
1409 | { | |
1410 | if (nexthop->rtype == NEXTHOP_TYPE_IPV4 | |
1411 | || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) | |
1412 | { | |
1413 | rta_addattr_l (rta, 4096, RTA_GATEWAY, | |
1414 | &nexthop->rgate.ipv4, bytelen); | |
1415 | rtnh->rtnh_len += sizeof (struct rtattr) + 4; | |
1416 | } | |
1417 | #ifdef HAVE_IPV6 | |
1418 | if (nexthop->rtype == NEXTHOP_TYPE_IPV6 | |
1419 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME | |
1420 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1421 | rta_addattr_l (rta, 4096, RTA_GATEWAY, | |
1422 | &nexthop->rgate.ipv6, bytelen); | |
1423 | #endif /* HAVE_IPV6 */ | |
1424 | /* ifindex */ | |
1425 | if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX | |
1426 | || nexthop->rtype == NEXTHOP_TYPE_IFNAME | |
1427 | || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX | |
1428 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX | |
1429 | || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) | |
1430 | rtnh->rtnh_ifindex = nexthop->rifindex; | |
1431 | else | |
1432 | rtnh->rtnh_ifindex = 0; | |
1433 | } | |
1434 | else | |
1435 | { | |
1436 | if (nexthop->type == NEXTHOP_TYPE_IPV4 | |
1437 | || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) | |
1438 | { | |
1439 | rta_addattr_l (rta, 4096, RTA_GATEWAY, | |
1440 | &nexthop->gate.ipv4, bytelen); | |
1441 | rtnh->rtnh_len += sizeof (struct rtattr) + 4; | |
1442 | } | |
1443 | #ifdef HAVE_IPV6 | |
1444 | if (nexthop->type == NEXTHOP_TYPE_IPV6 | |
1445 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME | |
1446 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1447 | rta_addattr_l (rta, 4096, RTA_GATEWAY, | |
1448 | &nexthop->gate.ipv6, bytelen); | |
1449 | #endif /* HAVE_IPV6 */ | |
1450 | /* ifindex */ | |
1451 | if (nexthop->type == NEXTHOP_TYPE_IFINDEX | |
1452 | || nexthop->type == NEXTHOP_TYPE_IFNAME | |
1453 | || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX | |
1454 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME | |
1455 | || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1456 | rtnh->rtnh_ifindex = nexthop->ifindex; | |
1457 | else | |
1458 | rtnh->rtnh_ifindex = 0; | |
1459 | } | |
1460 | rtnh = RTNH_NEXT(rtnh); | |
1461 | ||
1462 | if (cmd == RTM_NEWROUTE) | |
1463 | SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); | |
1464 | } | |
1465 | } | |
1466 | ||
1467 | if (rta->rta_len > RTA_LENGTH (0)) | |
1468 | addattr_l (&req.n, 1024, RTA_MULTIPATH, RTA_DATA(rta), | |
1469 | RTA_PAYLOAD(rta)); | |
1470 | } | |
1471 | ||
1472 | /* If there is no useful nexthop then return. */ | |
1473 | if (nexthop_num == 0) | |
1474 | { | |
1475 | if (IS_ZEBRA_DEBUG_KERNEL) | |
1476 | zlog_info ("netlink_route_multipath(): No useful nexthop."); | |
1477 | return 0; | |
1478 | } | |
1479 | ||
1480 | skip: | |
1481 | ||
1482 | /* Destination netlink address. */ | |
1483 | memset (&snl, 0, sizeof snl); | |
1484 | snl.nl_family = AF_NETLINK; | |
1485 | ||
1486 | if (family == AF_INET) | |
1487 | nl = &netlink_cmd; | |
1488 | else | |
1489 | nl = &netlink; | |
1490 | ||
1491 | /* Talk to netlink socket. */ | |
1492 | return netlink_talk (&req.n, nl); | |
1493 | } | |
1494 | ||
1495 | int | |
1496 | kernel_add_ipv4 (struct prefix *p, struct rib *rib) | |
1497 | { | |
1498 | return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET); | |
1499 | } | |
1500 | ||
1501 | int | |
1502 | kernel_delete_ipv4 (struct prefix *p, struct rib *rib) | |
1503 | { | |
1504 | return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET); | |
1505 | } | |
1506 | ||
1507 | #ifdef HAVE_IPV6 | |
1508 | int | |
1509 | kernel_add_ipv6 (struct prefix *p, struct rib *rib) | |
1510 | { | |
1511 | return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET6); | |
1512 | } | |
1513 | ||
1514 | int | |
1515 | kernel_delete_ipv6 (struct prefix *p, struct rib *rib) | |
1516 | { | |
1517 | return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET6); | |
1518 | } | |
1519 | ||
1520 | /* Delete IPv6 route from the kernel. */ | |
1521 | int | |
1522 | kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, | |
1523 | int index, int flags, int table) | |
1524 | { | |
1525 | return netlink_route (RTM_DELROUTE, AF_INET6, &dest->prefix, dest->prefixlen, | |
1526 | gate, index, flags, table); | |
1527 | } | |
1528 | #endif /* HAVE_IPV6 */ | |
1529 | \f | |
1530 | /* Interface address modification. */ | |
1531 | int | |
1532 | netlink_address (int cmd, int family, struct interface *ifp, | |
1533 | struct connected *ifc) | |
1534 | { | |
1535 | int bytelen; | |
1536 | struct prefix *p; | |
1537 | ||
1538 | struct | |
1539 | { | |
1540 | struct nlmsghdr n; | |
1541 | struct ifaddrmsg ifa; | |
1542 | char buf[1024]; | |
1543 | } req; | |
1544 | ||
1545 | p = ifc->address; | |
1546 | memset (&req, 0, sizeof req); | |
1547 | ||
1548 | bytelen = (family == AF_INET ? 4 : 16); | |
1549 | ||
1550 | req.n.nlmsg_len = NLMSG_LENGTH (sizeof(struct ifaddrmsg)); | |
1551 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
1552 | req.n.nlmsg_type = cmd; | |
1553 | req.ifa.ifa_family = family; | |
1554 | ||
1555 | req.ifa.ifa_index = ifp->ifindex; | |
1556 | req.ifa.ifa_prefixlen = p->prefixlen; | |
1557 | ||
1558 | addattr_l (&req.n, sizeof req, IFA_LOCAL, &p->u.prefix, bytelen); | |
1559 | ||
1560 | if (family == AF_INET && cmd == RTM_NEWADDR) | |
1561 | { | |
1562 | if (if_is_broadcast (ifp) && ifc->destination) | |
1563 | { | |
1564 | p = ifc->destination; | |
1565 | addattr_l(&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix, bytelen); | |
1566 | } | |
1567 | } | |
1568 | ||
1569 | if (CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY)) | |
1570 | SET_FLAG (req.ifa.ifa_flags, IFA_F_SECONDARY); | |
1571 | ||
1572 | if (ifc->label) | |
1573 | addattr_l (&req.n, sizeof req, IFA_LABEL, ifc->label, | |
1574 | strlen (ifc->label) + 1); | |
1575 | ||
1576 | return netlink_talk (&req.n, &netlink_cmd); | |
1577 | } | |
1578 | ||
1579 | int | |
1580 | kernel_address_add_ipv4 (struct interface *ifp, struct connected *ifc) | |
1581 | { | |
1582 | return netlink_address (RTM_NEWADDR, AF_INET, ifp, ifc); | |
1583 | } | |
1584 | ||
1585 | int | |
1586 | kernel_address_delete_ipv4 (struct interface *ifp, struct connected *ifc) | |
1587 | { | |
1588 | return netlink_address (RTM_DELADDR, AF_INET, ifp, ifc); | |
1589 | } | |
1590 | ||
718e3744 | 1591 | |
1592 | extern struct thread_master *master; | |
1593 | ||
1594 | /* Kernel route reflection. */ | |
1595 | int | |
1596 | kernel_read (struct thread *thread) | |
1597 | { | |
1598 | int ret; | |
1599 | int sock; | |
1600 | ||
1601 | sock = THREAD_FD (thread); | |
1602 | ret = netlink_parse_info (netlink_information_fetch, &netlink); | |
1603 | thread_add_read (master, kernel_read, NULL, netlink.sock); | |
1604 | ||
1605 | return 0; | |
1606 | } | |
1607 | ||
1608 | /* Exported interface function. This function simply calls | |
1609 | netlink_socket (). */ | |
1610 | void | |
1611 | kernel_init () | |
1612 | { | |
1613 | unsigned long groups; | |
1614 | ||
1615 | groups = RTMGRP_LINK|RTMGRP_IPV4_ROUTE|RTMGRP_IPV4_IFADDR; | |
1616 | #ifdef HAVE_IPV6 | |
1617 | groups |= RTMGRP_IPV6_ROUTE|RTMGRP_IPV6_IFADDR; | |
1618 | #endif /* HAVE_IPV6 */ | |
1619 | netlink_socket (&netlink, groups); | |
1620 | netlink_socket (&netlink_cmd, 0); | |
1621 | ||
1622 | /* Register kernel socket. */ | |
1623 | if (netlink.sock > 0) | |
1624 | thread_add_read (master, kernel_read, NULL, netlink.sock); | |
1625 | } |