if-linux.c revision 0545fad98723550607287a86bfee3807c7d26e91
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <asm/types.h> /* Needed for 2.4 kernels */
29
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/ioctl.h>
33#include <sys/param.h>
34
35#include <linux/netlink.h>
36#include <linux/rtnetlink.h>
37
38/* Support older kernels */
39#ifndef IFLA_WIRELESS
40# define IFLA_WIRELESS (IFLA_MASTER + 1)
41#endif
42
43#include <errno.h>
44#include <ctype.h>
45#include <stddef.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include "config.h"
52#include "common.h"
53#include "configure.h"
54#include "dhcp.h"
55#include "net.h"
56
57static int sock_fd;
58static struct sockaddr_nl sock_nl;
59
60int
61if_init(struct interface *iface)
62{
63	char path[PATH_MAX];
64	FILE *fp;
65	int n;
66
67	/* We enable promote_secondaries so that we can do this
68	 * add 192.168.1.2/24
69	 * add 192.168.1.3/24
70	 * del 192.168.1.2/24
71	 * and the subnet mask moves onto 192.168.1.3/24
72	 * This matches the behaviour of BSD which makes coding dhcpcd
73	 * a little easier as there's just one behaviour. */
74	snprintf(path, sizeof(path),
75	    "/proc/sys/net/ipv4/conf/%s/promote_secondaries",
76	    iface->name);
77
78	fp = fopen(path, "w");
79	if (fp == NULL)
80		return errno == ENOENT ? 0 : -1;
81	n = fprintf(fp, "1");
82	fclose(fp);
83	return n == -1 ? -1 : 0;
84}
85
86int
87if_conf(struct interface *iface)
88{
89	char path[PATH_MAX], buf[1];
90	FILE *fp;
91
92	/* Some qeth setups require the use of the broadcast flag. */
93	snprintf(path, sizeof(path),
94	    "/sys/class/net/%s/device/layer2",
95	    iface->name);
96
97	fp = fopen(path, "r");
98	if (fp == NULL)
99		return errno == ENOENT ? 0 : -1;
100	if (fgets(buf, sizeof(buf), fp) != NULL && buf[0] == '0')
101		iface->state->options->options |= DHCPCD_BROADCAST;
102	fclose(fp);
103	return 0;
104}
105
106static int
107_open_link_socket(struct sockaddr_nl *nl)
108{
109	int fd;
110
111	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
112		return -1;
113	nl->nl_family = AF_NETLINK;
114	if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1)
115		return -1;
116	set_cloexec(fd);
117	return fd;
118}
119
120int
121init_sockets(void)
122{
123	if ((socket_afnet = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
124		return -1;
125	set_cloexec(socket_afnet);
126	sock_fd = _open_link_socket(&sock_nl);
127	set_cloexec(sock_fd);
128	return sock_fd;
129}
130
131int
132open_link_socket(void)
133{
134	struct sockaddr_nl snl;
135
136	memset(&snl, 0, sizeof(snl));
137	snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
138	return _open_link_socket(&snl);
139}
140
141static int
142get_netlink(int fd, int flags,
143    int (*callback)(struct nlmsghdr *))
144{
145	char *buf = NULL, *nbuf;
146	ssize_t buflen = 0, bytes;
147	struct nlmsghdr *nlm;
148	int r = -1;
149
150	for (;;) {
151		bytes = recv(fd, NULL, 0,
152		    flags | MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
153		if (bytes == -1) {
154			if (errno == EAGAIN) {
155				r = 0;
156				goto eexit;
157			}
158			if (errno == EINTR)
159				continue;
160			goto eexit;
161		} else if (bytes == buflen) {
162			/* Support kernels older than 2.6.22 */
163			if (bytes == 0)
164				bytes = 512;
165			else
166				bytes *= 2;
167		}
168		if (buflen < bytes) {
169			/* Alloc 1 more so we work with older kernels */
170			buflen = bytes + 1;
171			nbuf = realloc(buf, buflen);
172			if (nbuf == NULL)
173				goto eexit;
174			buf = nbuf;
175		}
176		bytes = recv(fd, buf, buflen, flags);
177		if (bytes == -1) {
178			if (errno == EAGAIN) {
179				r = 0;
180				goto eexit;
181			}
182			if (errno == EINTR)
183				continue;
184			goto eexit;
185		}
186		for (nlm = (struct nlmsghdr *)buf;
187		     NLMSG_OK(nlm, (size_t)bytes);
188		     nlm = NLMSG_NEXT(nlm, bytes))
189		{
190			r = callback(nlm);
191			if (r != 0)
192				goto eexit;
193		}
194	}
195
196eexit:
197	free(buf);
198	return r;
199}
200
201static int
202err_netlink(struct nlmsghdr *nlm)
203{
204	struct nlmsgerr *err;
205	int l;
206
207	if (nlm->nlmsg_type != NLMSG_ERROR)
208		return 0;
209	l = nlm->nlmsg_len - sizeof(*nlm);
210	if ((size_t)l < sizeof(*err)) {
211		errno = EBADMSG;
212		return -1;
213	}
214	err = (struct nlmsgerr *)NLMSG_DATA(nlm);
215	if (err->error == 0)
216		return l;
217	errno = -err->error;
218	return -1;
219}
220
221static int
222link_route(struct nlmsghdr *nlm)
223{
224	int len, idx, metric;
225	struct rtattr *rta;
226	struct rtmsg *rtm;
227	struct rt rt;
228	char ifn[IF_NAMESIZE + 1];
229
230	if (nlm->nlmsg_type != RTM_DELROUTE)
231		return 0;
232
233	len = nlm->nlmsg_len - sizeof(*nlm);
234	if ((size_t)len < sizeof(*rtm)) {
235		errno = EBADMSG;
236		return -1;
237	}
238	rtm = NLMSG_DATA(nlm);
239	if (rtm->rtm_type != RTN_UNICAST ||
240	    rtm->rtm_table != RT_TABLE_MAIN ||
241	    rtm->rtm_family != AF_INET ||
242	    nlm->nlmsg_pid == (uint32_t)getpid())
243		return 1;
244	rta = (struct rtattr *) ((char *)rtm + NLMSG_ALIGN(sizeof(*rtm)));
245	len = NLMSG_PAYLOAD(nlm, sizeof(*rtm));
246	rt.iface = NULL;
247	rt.dest.s_addr = INADDR_ANY;
248	rt.net.s_addr = INADDR_ANY;
249	rt.gate.s_addr = INADDR_ANY;
250	rt.next = NULL;
251	metric = 0;
252	while (RTA_OK(rta, len)) {
253		switch (rta->rta_type) {
254		case RTA_DST:
255			memcpy(&rt.dest.s_addr, RTA_DATA(rta),
256			    sizeof(rt.dest.s_addr));
257			break;
258		case RTA_GATEWAY:
259			memcpy(&rt.gate.s_addr, RTA_DATA(rta),
260			    sizeof(rt.gate.s_addr));
261			break;
262		case RTA_OIF:
263			idx = *(int *)RTA_DATA(rta);
264			if (if_indextoname(idx, ifn))
265				rt.iface = find_interface(ifn);
266			break;
267		case RTA_PRIORITY:
268			metric = *(int *)RTA_DATA(rta);
269			break;
270		}
271		rta = RTA_NEXT(rta, len);
272	}
273	if (rt.iface != NULL) {
274		if (metric == rt.iface->metric) {
275			inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
276			route_deleted(&rt);
277		}
278	}
279	return 1;
280}
281
282static int
283link_addr(struct nlmsghdr *nlm)
284{
285	int len;
286	struct rtattr *rta;
287	struct ifaddrmsg *ifa;
288	struct in_addr addr, net, dest;
289	char ifn[IF_NAMESIZE + 1];
290	struct interface *iface;
291
292	if (nlm->nlmsg_type != RTM_DELADDR && nlm->nlmsg_type != RTM_NEWADDR)
293		return 0;
294
295	len = nlm->nlmsg_len - sizeof(*nlm);
296	if ((size_t)len < sizeof(*ifa)) {
297		errno = EBADMSG;
298		return -1;
299	}
300	if (nlm->nlmsg_pid == (uint32_t)getpid())
301		return 1;
302	ifa = NLMSG_DATA(nlm);
303	if (if_indextoname(ifa->ifa_index, ifn) == NULL)
304		return -1;
305	iface = find_interface(ifn);
306	if (iface == NULL)
307		return 1;
308	rta = (struct rtattr *) IFA_RTA(ifa);
309	len = NLMSG_PAYLOAD(nlm, sizeof(*ifa));
310	addr.s_addr = dest.s_addr = INADDR_ANY;
311	dest.s_addr = INADDR_ANY;
312	inet_cidrtoaddr(ifa->ifa_prefixlen, &net);
313	while (RTA_OK(rta, len)) {
314		switch (rta->rta_type) {
315		case IFA_ADDRESS:
316			if (iface->flags & IFF_POINTOPOINT) {
317				memcpy(&dest.s_addr, RTA_DATA(rta),
318				    sizeof(addr.s_addr));
319			}
320			break;
321		case IFA_LOCAL:
322			memcpy(&addr.s_addr, RTA_DATA(rta),
323			    sizeof(addr.s_addr));
324			break;
325		}
326		rta = RTA_NEXT(rta, len);
327	}
328	handle_ifa(nlm->nlmsg_type, ifn, &addr, &net, &dest);
329	return 1;
330}
331
332static int
333link_netlink(struct nlmsghdr *nlm)
334{
335	int len;
336	struct rtattr *rta;
337	struct ifinfomsg *ifi;
338	char ifn[IF_NAMESIZE + 1];
339
340	len = link_route(nlm);
341	if (len != 0)
342		return len;
343	len = link_addr(nlm);
344	if (len != 0)
345		return len;
346
347	if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
348		return 0;
349	len = nlm->nlmsg_len - sizeof(*nlm);
350	if ((size_t)len < sizeof(*ifi)) {
351		errno = EBADMSG;
352		return -1;
353	}
354	ifi = NLMSG_DATA(nlm);
355	if (ifi->ifi_flags & IFF_LOOPBACK)
356		return 1;
357	rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));
358	len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
359	*ifn = '\0';
360	while (RTA_OK(rta, len)) {
361		switch (rta->rta_type) {
362		case IFLA_WIRELESS:
363			/* Ignore wireless messages */
364			if (nlm->nlmsg_type == RTM_NEWLINK &&
365			    ifi->ifi_change == 0)
366				return 1;
367			break;
368		case IFLA_IFNAME:
369			strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
370			break;
371		}
372		rta = RTA_NEXT(rta, len);
373	}
374	if (nlm->nlmsg_type == RTM_NEWLINK)
375		len = ifi->ifi_change == ~0U ? 1 : 0;
376	else
377		len = -1;
378	handle_interface(len, ifn);
379	return 1;
380}
381
382int
383manage_link(int fd)
384{
385	return get_netlink(fd, MSG_DONTWAIT, &link_netlink);
386}
387
388static int
389send_netlink(struct nlmsghdr *hdr)
390{
391	int r;
392	struct iovec iov;
393	struct msghdr msg;
394	static unsigned int seq;
395
396	memset(&iov, 0, sizeof(iov));
397	iov.iov_base = hdr;
398	iov.iov_len = hdr->nlmsg_len;
399	memset(&msg, 0, sizeof(msg));
400	msg.msg_name = &sock_nl;
401	msg.msg_namelen = sizeof(sock_nl);
402	msg.msg_iov = &iov;
403	msg.msg_iovlen = 1;
404	/* Request a reply */
405	hdr->nlmsg_flags |= NLM_F_ACK;
406	hdr->nlmsg_seq = ++seq;
407
408	if (sendmsg(sock_fd, &msg, 0) != -1)
409		r = get_netlink(sock_fd, 0, &err_netlink);
410	else
411		r = -1;
412	return r;
413}
414
415#define NLMSG_TAIL(nmsg)						\
416	((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
417
418static int
419add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
420    const void *data, int alen)
421{
422	int len = RTA_LENGTH(alen);
423	struct rtattr *rta;
424
425	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
426		errno = ENOBUFS;
427		return -1;
428	}
429
430	rta = NLMSG_TAIL(n);
431	rta->rta_type = type;
432	rta->rta_len = len;
433	memcpy(RTA_DATA(rta), data, alen);
434	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
435
436	return 0;
437}
438
439static int
440add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
441{
442	int len = RTA_LENGTH(sizeof(data));
443	struct rtattr *rta;
444
445	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
446		errno = ENOBUFS;
447		return -1;
448	}
449
450	rta = NLMSG_TAIL(n);
451	rta->rta_type = type;
452	rta->rta_len = len;
453	memcpy(RTA_DATA(rta), &data, sizeof(data));
454	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
455
456	return 0;
457}
458
459struct nlma
460{
461	struct nlmsghdr hdr;
462	struct ifaddrmsg ifa;
463	char buffer[64];
464};
465
466struct nlmr
467{
468	struct nlmsghdr hdr;
469	struct rtmsg rt;
470	char buffer[256];
471};
472
473int
474if_address(const struct interface *iface,
475    const struct in_addr *address, const struct in_addr *netmask,
476    const struct in_addr *broadcast, int action)
477{
478	struct nlma *nlm;
479	int retval = 0;
480
481	nlm = xzalloc(sizeof(*nlm));
482	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
483	nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
484	if (action >= 0) {
485		nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
486		nlm->hdr.nlmsg_type = RTM_NEWADDR;
487	} else
488		nlm->hdr.nlmsg_type = RTM_DELADDR;
489	if (!(nlm->ifa.ifa_index = if_nametoindex(iface->name))) {
490		free(nlm);
491		errno = ENODEV;
492		return -1;
493	}
494	nlm->ifa.ifa_family = AF_INET;
495	nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
496	/* This creates the aliased interface */
497	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
498	    iface->name, strlen(iface->name) + 1);
499	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
500	    &address->s_addr, sizeof(address->s_addr));
501	if (action >= 0 && broadcast)
502		add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
503		    &broadcast->s_addr, sizeof(broadcast->s_addr));
504
505	if (send_netlink(&nlm->hdr) == -1)
506		retval = -1;
507	free(nlm);
508	return retval;
509}
510
511int
512if_route(const struct interface *iface,
513    const struct in_addr *destination, const struct in_addr *netmask,
514    const struct in_addr *gateway, int metric, int action)
515{
516	struct nlmr *nlm;
517	unsigned int ifindex;
518	int retval = 0;
519
520	if (!(ifindex = if_nametoindex(iface->name))) {
521		errno = ENODEV;
522		return -1;
523	}
524
525	nlm = xzalloc(sizeof(*nlm));
526	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
527	nlm->hdr.nlmsg_type = RTM_NEWROUTE;
528	if (action == 0)
529		nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
530	else if (action == 1)
531		nlm->hdr.nlmsg_flags = NLM_F_CREATE /*| NLM_F_EXCL*/;
532	else
533		nlm->hdr.nlmsg_type = RTM_DELROUTE;
534	nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
535	nlm->rt.rtm_family = AF_INET;
536	nlm->rt.rtm_table = RT_TABLE_MAIN;
537
538	if (action == -1 || action == -2)
539		nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
540	else {
541		nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/;
542		/* We only change route metrics for kernel routes */
543		if (destination->s_addr ==
544		    (iface->addr.s_addr & iface->net.s_addr) &&
545		    netmask->s_addr == iface->net.s_addr)
546			nlm->rt.rtm_protocol = RTPROT_KERNEL;
547		else
548			nlm->rt.rtm_protocol = RTPROT_BOOT;
549		if (gateway->s_addr == INADDR_ANY ||
550		    (gateway->s_addr == destination->s_addr &&
551			netmask->s_addr == INADDR_BROADCAST))
552			nlm->rt.rtm_scope = RT_SCOPE_LINK;
553		else
554			nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
555		nlm->rt.rtm_type = RTN_UNICAST;
556	}
557
558	nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
559	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
560	    &destination->s_addr, sizeof(destination->s_addr));
561	if (nlm->rt.rtm_protocol == RTPROT_KERNEL) {
562		add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC,
563		    &iface->addr.s_addr, sizeof(iface->addr.s_addr));
564	}
565	/* If destination == gateway then don't add the gateway */
566	if (destination->s_addr != gateway->s_addr ||
567	    netmask->s_addr != INADDR_BROADCAST)
568		add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
569		    &gateway->s_addr, sizeof(gateway->s_addr));
570
571	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
572	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
573
574	if (send_netlink(&nlm->hdr) == -1)
575		retval = -1;
576	free(nlm);
577	return retval;
578}
579