if-linux.c revision f7c5421560640d23fc10803b9d59a9ff1d83e467
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright 2006-2008 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/stat.h>
33#include <sys/ioctl.h>
34#include <sys/param.h>
35
36#include <arpa/inet.h>
37#include <linux/netlink.h>
38#include <linux/rtnetlink.h>
39#include <netinet/ether.h>
40#include <netpacket/packet.h>
41
42#include <errno.h>
43#include <stddef.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49/* Support older kernels */
50#ifndef IFLA_WIRELESS
51# define IFLA_WIRELSSS (IFLFA_MASTER + 1)
52#endif
53
54#include "config.h"
55#include "common.h"
56#include "dhcp.h"
57#include "net.h"
58
59#define BUFFERLEN 256
60
61int
62open_link_socket(struct interface *iface)
63{
64	int fd;
65	struct sockaddr_nl nl;
66
67	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
68		return -1;
69	memset(&nl, 0, sizeof(nl));
70	nl.nl_family = AF_NETLINK;
71	nl.nl_groups = RTMGRP_LINK;
72	if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
73		return -1;
74	set_cloexec(fd);
75	if (iface->link_fd != -1)
76		close(iface->link_fd);
77	iface->link_fd = fd;
78	return 0;
79}
80
81static int
82get_netlink(int fd, int flags,
83	    int (*callback)(struct nlmsghdr *, const char *),
84	    const char *ifname)
85{
86	char *buffer = NULL;
87	ssize_t bytes;
88	struct nlmsghdr *nlm;
89	int r = -1;
90
91	buffer = xzalloc(sizeof(char) * BUFFERLEN);
92	for (;;) {
93		bytes = recv(fd, buffer, BUFFERLEN, flags);
94		if (bytes == -1) {
95			if (errno == EAGAIN) {
96				r = 0;
97				goto eexit;
98			}
99			if (errno == EINTR)
100				continue;
101			goto eexit;
102		}
103		for (nlm = (struct nlmsghdr *)buffer;
104		     NLMSG_OK(nlm, (size_t)bytes);
105		     nlm = NLMSG_NEXT(nlm, bytes))
106		{
107			r = callback(nlm, ifname);
108			if (r != 0)
109				goto eexit;
110		}
111	}
112
113eexit:
114	free(buffer);
115	return r;
116}
117
118static int
119err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
120{
121	struct nlmsgerr *err;
122	int l;
123
124	if (nlm->nlmsg_type != NLMSG_ERROR)
125		return 0;
126	l = nlm->nlmsg_len - sizeof(*nlm);
127	if ((size_t)l < sizeof(*err)) {
128		errno = EBADMSG;
129		return -1;
130	}
131	err = (struct nlmsgerr *)NLMSG_DATA(nlm);
132	if (err->error == 0)
133		return l;
134	errno = -err->error;
135	return -1;
136}
137
138static int
139link_netlink(struct nlmsghdr *nlm, const char *ifname)
140{
141	int len;
142	struct rtattr *rta;
143	struct ifinfomsg *ifi;
144	char ifn[IF_NAMESIZE + 1];
145
146	if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
147		return 0;
148	len = nlm->nlmsg_len - sizeof(*nlm);
149	if ((size_t)len < sizeof(*ifi)) {
150		errno = EBADMSG;
151		return -1;
152	}
153	ifi = NLMSG_DATA(nlm);
154	if (ifi->ifi_flags & IFF_LOOPBACK)
155		return 0;
156	rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));
157	len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
158	*ifn = '\0';
159	while (RTA_OK(rta, len)) {
160		switch (rta->rta_type) {
161		case IFLA_WIRELESS:
162			/* Ignore wireless messages */
163			if (nlm->nlmsg_type == RTM_NEWLINK &&
164			    ifi->ifi_change  == 0)
165				return 0;
166			break;
167		case IFLA_IFNAME:
168			strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
169			break;
170		}
171		rta = RTA_NEXT(rta, len);
172	}
173
174	if (strncmp(ifname, ifn, sizeof(ifn)) == 0)
175		return 1;
176	return 0;
177}
178
179int
180link_changed(struct interface *iface)
181{
182	return get_netlink(iface->link_fd, MSG_DONTWAIT,
183			   &link_netlink, iface->name);
184}
185
186static int
187send_netlink(struct nlmsghdr *hdr)
188{
189	int fd, r;
190	struct sockaddr_nl nl;
191	struct iovec iov;
192	struct msghdr msg;
193	static unsigned int seq;
194
195	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
196		return -1;
197	memset(&nl, 0, sizeof(nl));
198	nl.nl_family = AF_NETLINK;
199	if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
200		close(fd);
201		return -1;
202	}
203	memset(&iov, 0, sizeof(iov));
204	iov.iov_base = hdr;
205	iov.iov_len = hdr->nlmsg_len;
206	memset(&msg, 0, sizeof(msg));
207	msg.msg_name = &nl;
208	msg.msg_namelen = sizeof(nl);
209	msg.msg_iov = &iov;
210	msg.msg_iovlen = 1;
211	/* Request a reply */
212	hdr->nlmsg_flags |= NLM_F_ACK;
213	hdr->nlmsg_seq = ++seq;
214
215	if (sendmsg(fd, &msg, 0) != -1)
216		r = get_netlink(fd, 0, &err_netlink, NULL);
217	else
218		r = -1;
219	close(fd);
220	return r;
221}
222
223#define NLMSG_TAIL(nmsg) \
224	((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
225
226static int
227add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
228	   const void *data, int alen)
229{
230	int len = RTA_LENGTH(alen);
231	struct rtattr *rta;
232
233	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
234		errno = ENOBUFS;
235		return -1;
236	}
237
238	rta = NLMSG_TAIL(n);
239	rta->rta_type = type;
240	rta->rta_len = len;
241	memcpy(RTA_DATA(rta), data, alen);
242	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
243
244	return 0;
245}
246
247static int
248add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
249{
250	int len = RTA_LENGTH(sizeof(data));
251	struct rtattr *rta;
252
253	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
254		errno = ENOBUFS;
255		return -1;
256	}
257
258	rta = NLMSG_TAIL(n);
259	rta->rta_type = type;
260	rta->rta_len = len;
261	memcpy(RTA_DATA(rta), &data, sizeof(data));
262	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
263
264	return 0;
265}
266
267struct nlma
268{
269	struct nlmsghdr hdr;
270	struct ifaddrmsg ifa;
271	char buffer[64];
272};
273
274struct nlmr
275{
276	struct nlmsghdr hdr;
277	struct rtmsg rt;
278	char buffer[256];
279};
280
281int
282if_address(const char *ifname,
283	   const struct in_addr *address, const struct in_addr *netmask,
284	   const struct in_addr *broadcast, int action)
285{
286	struct nlma *nlm;
287	int retval = 0;
288
289	nlm = xzalloc(sizeof(*nlm));
290	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
291	nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
292	if (action >= 0) {
293		nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
294		nlm->hdr.nlmsg_type = RTM_NEWADDR;
295	} else
296		nlm->hdr.nlmsg_type = RTM_DELADDR;
297	if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
298		free(nlm);
299		errno = ENODEV;
300		return -1;
301	}
302	nlm->ifa.ifa_family = AF_INET;
303	nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
304	/* This creates the aliased interface */
305	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
306		   ifname, strlen(ifname) + 1);
307	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
308		   &address->s_addr, sizeof(address->s_addr));
309	if (action >= 0)
310		add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
311			   &broadcast->s_addr, sizeof(broadcast->s_addr));
312
313	if (send_netlink(&nlm->hdr) == -1)
314		retval = -1;
315	free(nlm);
316	return retval;
317}
318
319int
320if_route(const char *ifname,
321	 const struct in_addr *destination, const struct in_addr *netmask,
322	 const struct in_addr *gateway, int metric, int action)
323{
324	struct nlmr *nlm;
325	unsigned int ifindex;
326	int retval = 0;
327
328
329	if (!(ifindex = if_nametoindex(ifname))) {
330		errno = ENODEV;
331		return -1;
332	}
333
334	nlm = xzalloc(sizeof(*nlm));
335	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
336	nlm->hdr.nlmsg_type = RTM_NEWROUTE;
337	if (action == 0)
338            nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
339	else if (action > 0)
340		/*
341		 * ers@google:
342		 * commented out NLM_F_EXCL here and below. We
343		 * sometimes keep one interface up while we are
344		 * configuring the other one, and this flag
345		 * causes route addition to fail.
346		 */
347		nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL */;
348	else
349		nlm->hdr.nlmsg_type = RTM_DELROUTE;
350	nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
351	nlm->rt.rtm_family = AF_INET;
352	nlm->rt.rtm_table = RT_TABLE_MAIN;
353
354	if (action < 0)
355		nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
356	else {
357		nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/;
358		nlm->rt.rtm_protocol = RTPROT_BOOT;
359		if (gateway->s_addr == INADDR_ANY)
360			nlm->rt.rtm_scope = RT_SCOPE_LINK;
361		else
362			nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
363		nlm->rt.rtm_type = RTN_UNICAST;
364	}
365
366	nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
367	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
368		   &destination->s_addr, sizeof(destination->s_addr));
369	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
370		   &gateway->s_addr, sizeof(gateway->s_addr));
371
372	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
373	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
374
375	if (send_netlink(&nlm->hdr) == -1)
376		retval = -1;
377	free(nlm);
378	return retval;
379}
380