]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/netlink.c
Increase event buffer
[thirdparty/lldpd.git] / src / daemon / netlink.c
CommitLineData
e12c2365
VB
1/* -*- mode: c; c-file-style: "openbsd" -*- */
2/*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
adbb6e54
VB
18/* Grabbing interfaces information with netlink only. */
19
e12c2365
VB
20#include "lldpd.h"
21
22#include <stdio.h>
23#include <unistd.h>
24#include <sys/types.h>
25#include <sys/socket.h>
adbb6e54 26#include <net/if_arp.h>
e12c2365
VB
27#include <linux/netlink.h>
28#include <linux/rtnetlink.h>
29
30#define NETLINK_BUFFER 4096
31
32struct netlink_req {
33 struct nlmsghdr hdr;
34 struct rtgenmsg gen;
35};
36
37/**
38 * Connect to netlink.
39 *
40 * Open a Netlink socket and connect to it.
41 *
42 * @param protocol Which protocol to use (eg NETLINK_ROUTE).
0484f180 43 * @param groups Which groups we want to subscribe to
e12c2365
VB
44 * @return The opened socket or -1 on error.
45 */
46static int
0484f180 47netlink_connect(int protocol, unsigned groups)
e12c2365 48{
281a5cd4 49 int s;
e12c2365
VB
50 struct sockaddr_nl local = {
51 .nl_family = AF_NETLINK,
52 .nl_pid = getpid(),
0484f180 53 .nl_groups = groups
e12c2365
VB
54 };
55
56 /* Open Netlink socket */
57 log_debug("netlink", "opening netlink socket");
58 s = socket(AF_NETLINK, SOCK_RAW, protocol);
59 if (s == -1) {
60 log_warn("netlink", "unable to open netlink socket");
61 return -1;
62 }
0484f180 63 if (groups && bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
e12c2365
VB
64 log_warn("netlink", "unable to bind netlink socket");
65 close(s);
66 return -1;
67 }
68 return s;
69}
70
71/**
72 * Send a netlink message.
73 *
74 * The type of the message can be chosen as well the route family. The
75 * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
76 *
77 * @param s the netlink socket
78 * @param type the request type (eg RTM_GETLINK)
79 * @param family the rt family (eg AF_PACKET)
80 * @return 0 on success, -1 otherwise
81 */
82static int
83netlink_send(int s, int type, int family)
84{
85 struct netlink_req req = {
86 .hdr = {
87 .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
de461f15 88 .nlmsg_type = type,
e12c2365
VB
89 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
90 .nlmsg_seq = 1,
91 .nlmsg_pid = getpid() },
de461f15 92 .gen = { .rtgen_family = family }
e12c2365
VB
93 };
94 struct iovec iov = {
95 .iov_base = &req,
96 .iov_len = req.hdr.nlmsg_len
97 };
98 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
99 struct msghdr rtnl_msg = {
100 .msg_iov = &iov,
101 .msg_iovlen = 1,
102 .msg_name = &peer,
103 .msg_namelen = sizeof(struct sockaddr_nl)
104 };
105
106 /* Send netlink message. This is synchronous but we are guaranteed
107 * to not block. */
108 log_debug("netlink", "sending netlink message");
109 if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) {
110 log_warn("netlink", "unable to send netlink message");
111 return -1;
112 }
113
114 return 0;
115}
116
e12c2365
VB
117/**
118 * Parse a `link` netlink message.
119 *
120 * @param msg message to be parsed
121 * @param iff where to put the result
122 * return 0 if the interface is worth it, -1 otherwise
123 */
124static int
125netlink_parse_link(struct nlmsghdr *msg,
adbb6e54 126 struct interfaces_device *iff)
e12c2365 127{
2fafbd30 128 struct ifinfomsg *ifi;
e12c2365
VB
129 struct rtattr *attribute;
130 int len;
2fafbd30 131 ifi = NLMSG_DATA(msg);
e12c2365
VB
132 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
133
c258a053 134 if (!((ifi->ifi_flags & IFF_UP) && (ifi->ifi_flags & IFF_RUNNING))) {
adbb6e54
VB
135 log_debug("netlink", "skip down interface at index %d",
136 ifi->ifi_index);
137 return -1;
138 }
139 if (ifi->ifi_type != ARPHRD_ETHER) {
140 log_debug("netlink", "skip non Ethernet interface at index %d",
141 ifi->ifi_index);
142 return -1;
143 }
144
2fafbd30 145 iff->index = ifi->ifi_index;
2fafbd30 146 iff->flags = ifi->ifi_flags;
adbb6e54
VB
147 iff->lower_idx = -1;
148 iff->upper_idx = -1;
e12c2365 149
2fafbd30 150 for (attribute = IFLA_RTA(ifi);
e12c2365
VB
151 RTA_OK(attribute, len);
152 attribute = RTA_NEXT(attribute, len)) {
153 switch(attribute->rta_type) {
154 case IFLA_IFNAME:
155 /* Interface name */
156 iff->name = strdup(RTA_DATA(attribute));
157 break;
158 case IFLA_IFALIAS:
159 /* Interface alias */
160 iff->alias = strdup(RTA_DATA(attribute));
161 break;
162 case IFLA_ADDRESS:
163 /* Interface MAC address */
164 iff->address = malloc(RTA_PAYLOAD(attribute));
165 if (iff->address)
166 memcpy(iff->address, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
167 break;
168 case IFLA_LINK:
adbb6e54
VB
169 /* Index of "lower" interface */
170 iff->lower_idx = *(int*)RTA_DATA(attribute);
e12c2365
VB
171 break;
172 case IFLA_MASTER:
173 /* Index of master interface */
adbb6e54 174 iff->upper_idx = *(int*)RTA_DATA(attribute);
e12c2365
VB
175 break;
176 case IFLA_TXQLEN:
177 /* Transmit queue length */
178 iff->txqueue = *(int*)RTA_DATA(attribute);
179 break;
180 case IFLA_MTU:
181 /* Maximum Transmission Unit */
182 iff->mtu = *(int*)RTA_DATA(attribute);
183 break;
184 default:
adbb6e54 185 log_debug("netlink", "unhandled link attribute type %d for iface %s",
e12c2365
VB
186 attribute->rta_type, iff->name ? iff->name : "(unknown)");
187 break;
188 }
189 }
190 if (!iff->name || !iff->address) {
191 log_info("netlink", "interface %d does not have a name or an address, skip",
192 iff->index);
193 return -1;
194 }
195 return 0;
196}
197
198/**
199 * Parse a `address` netlink message.
200 *
201 * @param msg message to be parsed
202 * @param ifa where to put the result
203 * return 0 if the address is worth it, -1 otherwise
204 */
205static int
206netlink_parse_address(struct nlmsghdr *msg,
adbb6e54 207 struct interfaces_address *ifa)
e12c2365
VB
208{
209 struct ifaddrmsg *ifi;
210 struct rtattr *attribute;
211 int len;
212 ifi = NLMSG_DATA(msg);
213 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
214
215 ifa->index = ifi->ifa_index;
216 ifa->flags = ifi->ifa_flags;
217 switch (ifi->ifa_family) {
218 case AF_INET:
219 case AF_INET6: break;
220 default:
221 log_debug("netlink", "got a non IP address on if %d (family: %d)",
222 ifa->index, ifi->ifa_family);
223 return -1;
224 }
225
226 for (attribute = IFA_RTA(ifi);
227 RTA_OK(attribute, len);
228 attribute = RTA_NEXT(attribute, len)) {
229 switch(attribute->rta_type) {
230 case IFA_ADDRESS:
231 /* Address */
232 if (ifi->ifa_family == AF_INET) {
233 struct sockaddr_in ip = { .sin_family = AF_INET };
234 memcpy(&ip.sin_addr, RTA_DATA(attribute),
235 sizeof(struct in_addr));
236 memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in));
237 } else {
238 struct sockaddr_in6 ip6 = { .sin6_family = AF_INET6 };
239 memcpy(&ip6.sin6_addr, RTA_DATA(attribute),
240 sizeof(struct in6_addr));
241 memcpy(&ifa->address, &ip6, sizeof(struct sockaddr_in6));
242 }
243 break;
244 default:
adbb6e54 245 log_debug("netlink", "unhandled address attribute type %d for iface %d",
e12c2365
VB
246 attribute->rta_type, ifa->index);
247 break;
248 }
249 }
250 if (ifa->address.ss_family == AF_UNSPEC) {
251 log_debug("netlink", "no IP for interface %d",
252 ifa->index);
253 return -1;
254 }
255 return 0;
256}
257
258/**
259 * Receive netlink answer from the kernel.
260 *
261 * @param s the netlink socket
262 * @param ifs list to store interface list or NULL if we don't
263 * @param ifas list to store address list or NULL if we don't
264 * @return 0 on success, -1 on error
265 */
266static int
267netlink_recv(int s,
adbb6e54
VB
268 struct interfaces_device_list *ifs,
269 struct interfaces_address_list *ifas)
e12c2365 270{
2fafbd30 271 char reply[NETLINK_BUFFER] __attribute__ ((aligned));
e12c2365
VB
272 int end = 0;
273
adbb6e54
VB
274 struct interfaces_device *iff;
275 struct interfaces_address *ifa;
e12c2365
VB
276
277 while (!end) {
278 int len;
279 struct nlmsghdr *msg;
280 struct iovec iov = {
281 .iov_base = reply,
282 .iov_len = NETLINK_BUFFER
283 };
284 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
285 struct msghdr rtnl_reply = {
286 .msg_iov = &iov,
287 .msg_iovlen = 1,
288 .msg_name = &peer,
289 .msg_namelen = sizeof(struct sockaddr_nl)
290 };
291
292 len = recvmsg(s, &rtnl_reply, 0);
293 if (len == -1) {
294 log_warnx("netlink", "unable to receive netlink answer");
295 return -1;
296 }
297 if (!len) return 0;
2fafbd30 298 for (msg = (struct nlmsghdr*)(void*)reply;
e12c2365
VB
299 NLMSG_OK(msg, len);
300 msg = NLMSG_NEXT(msg, len)) {
301 switch (msg->nlmsg_type) {
302 case NLMSG_DONE:
303 log_debug("netlink", "received end of dump message");
304 end = 1;
305 break;
306 case RTM_NEWLINK:
307 if (!ifs) break;
308 log_debug("netlink", "received link information");
adbb6e54 309 iff = calloc(1, sizeof(struct interfaces_device));
e12c2365
VB
310 if (iff == NULL) {
311 log_warn("netlink", "not enough memory for another interface, give what we have");
312 return 0;
313 }
314 if (netlink_parse_link(msg, iff) == 0)
315 TAILQ_INSERT_TAIL(ifs, iff, next);
316 else
adbb6e54 317 interfaces_free_device(iff);
e12c2365
VB
318 break;
319 case RTM_NEWADDR:
320 if (!ifas) break;
321 log_debug("netlink", "received address information");
adbb6e54 322 ifa = calloc(1, sizeof(struct interfaces_address));
e12c2365
VB
323 if (ifa == NULL) {
324 log_warn("netlink", "not enough memory for another address, give what we have");
325 return 0;
326 }
327 if (netlink_parse_address(msg, ifa) == 0)
328 TAILQ_INSERT_TAIL(ifas, ifa, next);
329 else
adbb6e54 330 interfaces_free_address(ifa);
e12c2365
VB
331 break;
332 default:
333 log_debug("netlink",
334 "received unhandled message type %d (len: %d)",
335 msg->nlmsg_type, msg->nlmsg_len);
336 }
337 }
338 }
339 return 0;
340}
341
342/**
343 * Receive the list of interfaces.
344 *
345 * @return a list of interfaces.
346 */
adbb6e54 347struct interfaces_device_list*
e12c2365
VB
348netlink_get_interfaces()
349{
350 int s;
adbb6e54
VB
351 struct interfaces_device_list *ifs;
352 struct interfaces_device *iface1, *iface2;
e12c2365 353
0484f180 354 if ((s = netlink_connect(NETLINK_ROUTE, 0)) == -1)
e12c2365
VB
355 return NULL;
356 if (netlink_send(s, RTM_GETLINK, AF_PACKET) == -1) {
357 close(s);
358 return NULL;
359 }
360
361 log_debug("netlink", "get the list of available interfaces");
adbb6e54 362 ifs = malloc(sizeof(struct interfaces_device_list));
e12c2365
VB
363 if (ifs == NULL) {
364 log_warn("netlink", "not enough memory for interface list");
3ee3f25d 365 close(s);
e12c2365
VB
366 return NULL;
367 }
368 TAILQ_INIT(ifs);
369 netlink_recv(s, ifs, NULL);
370
adbb6e54
VB
371 /* Fill out lower/upper */
372 TAILQ_FOREACH(iface1, ifs, next) {
373 if (iface1->upper_idx != -1 && iface1->upper_idx != iface1->index)
374 TAILQ_FOREACH(iface2, ifs, next) {
375 if (iface1->upper_idx == iface2->index) {
376 iface1->upper = iface2;
377 break;
378 }
379 }
380 if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index)
381 TAILQ_FOREACH(iface2, ifs, next) {
382 if (iface1->lower_idx == iface2->index) {
383 iface1->lower = iface2;
384 break;
385 }
386 }
387 }
388
e12c2365
VB
389 close(s);
390 return ifs;
391}
392
393/**
394 * Receive the list of addresses.
395 *
396 * @return a list of addresses.
397 */
adbb6e54 398struct interfaces_address_list*
e12c2365
VB
399netlink_get_addresses()
400{
401 int s;
adbb6e54 402 struct interfaces_address_list *ifaddrs;
e12c2365 403
0484f180 404 if ((s = netlink_connect(NETLINK_ROUTE, 0)) == -1)
e12c2365
VB
405 return NULL;
406 if (netlink_send(s, RTM_GETADDR, AF_UNSPEC) == -1) {
407 close(s);
408 return NULL;
409 }
410
411 log_debug("netlink", "get the list of available addresses");
adbb6e54 412 ifaddrs = malloc(sizeof(struct interfaces_address_list));
e12c2365
VB
413 if (ifaddrs == NULL) {
414 log_warn("netlink", "not enough memory for address list");
3ee3f25d 415 close(s);
e12c2365
VB
416 return NULL;
417 }
418 TAILQ_INIT(ifaddrs);
419 netlink_recv(s, NULL, ifaddrs);
420
421 close(s);
422 return ifaddrs;
423}
0484f180
VB
424
425/**
426 * Subscribe to link changes.
427 *
428 * @return The socket we should listen to for changes.
429 */
430int
431netlink_subscribe_changes()
432{
433 log_debug("netlink", "listening on interface changes");
434 return netlink_connect(NETLINK_ROUTE, RTMGRP_LINK);
435}