if-linux.c revision e95877ecfa1170d77b1ec1f66752725cdda01b64
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#include "config.h"
50#include "common.h"
51#include "dhcp.h"
52#include "net.h"
53
54/* This netlink stuff is overly compex IMO.
55 * The BSD implementation is much cleaner and a lot less code.
56 * send_netlink handles the actual transmission so we can work out
57 * if there was an error or not. */
58#define BUFFERLEN 256
59static int
60send_netlink(struct nlmsghdr *hdr)
61{
62	int s;
63	pid_t mypid = getpid ();
64	struct sockaddr_nl nl;
65	struct iovec iov;
66	struct msghdr msg;
67	static unsigned int seq;
68	char *buffer = NULL;
69	ssize_t bytes;
70	union
71	{
72		char *buffer;
73		struct nlmsghdr *nlm;
74	} h;
75	int len, l;
76	struct nlmsgerr *err;
77
78	if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
79		return -1;
80
81	memset(&nl, 0, sizeof(nl));
82	nl.nl_family = AF_NETLINK;
83	if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1)
84		goto eexit;
85
86	memset(&iov, 0, sizeof(iov));
87	iov.iov_base = hdr;
88	iov.iov_len = hdr->nlmsg_len;
89
90	memset(&msg, 0, sizeof(msg));
91	msg.msg_name = &nl;
92	msg.msg_namelen = sizeof(nl);
93	msg.msg_iov = &iov;
94	msg.msg_iovlen = 1;
95
96	/* Request a reply */
97	hdr->nlmsg_flags |= NLM_F_ACK;
98	hdr->nlmsg_seq = ++seq;
99
100	if (sendmsg(s, &msg, 0) == -1)
101		goto eexit;
102
103	buffer = xzalloc(sizeof(char) * BUFFERLEN);
104	iov.iov_base = buffer;
105
106	for (;;) {
107		iov.iov_len = BUFFERLEN;
108		bytes = recvmsg(s, &msg, 0);
109
110		if (bytes == -1) {
111			if (errno == EINTR)
112				continue;
113			goto eexit;
114		}
115
116		if (bytes == 0) {
117			errno = ENODATA;
118			goto eexit;
119		}
120
121		if (msg.msg_namelen != sizeof(nl)) {
122			errno = EBADMSG;
123			goto eexit;
124		}
125
126		for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) {
127			len = h.nlm->nlmsg_len;
128			l = len - sizeof(*h.nlm);
129			err = (struct nlmsgerr *)NLMSG_DATA(h.nlm);
130
131			if (l < 0 || len > bytes) {
132				errno = EBADMSG;
133				goto eexit;
134			}
135
136			/* Ensure it's our message */
137			if (nl.nl_pid != 0 ||
138			    (pid_t)h.nlm->nlmsg_pid != mypid ||
139			    h.nlm->nlmsg_seq != seq)
140			{
141				/* Next Message */
142				bytes -= NLMSG_ALIGN(len);
143				h.buffer += NLMSG_ALIGN(len);
144				continue;
145			}
146
147			/* We get an NLMSG_ERROR back with a code of zero for success */
148			if (h.nlm->nlmsg_type != NLMSG_ERROR)
149				continue;
150
151			if ((unsigned)l < sizeof(*err)) {
152				errno = EBADMSG;
153				goto eexit;
154			}
155
156			if (err->error == 0) {
157				close(s);
158				free(buffer);
159				return l;
160			}
161
162			errno = -err->error;
163			goto eexit;
164		}
165	}
166
167eexit:
168	close(s);
169	free(buffer);
170	return -1;
171}
172
173#define NLMSG_TAIL(nmsg) \
174	((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
175
176static int
177add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
178	   const void *data, int alen)
179{
180	int len = RTA_LENGTH(alen);
181	struct rtattr *rta;
182
183	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
184		errno = ENOBUFS;
185		return -1;
186	}
187
188	rta = NLMSG_TAIL(n);
189	rta->rta_type = type;
190	rta->rta_len = len;
191	memcpy(RTA_DATA(rta), data, alen);
192	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
193
194	return 0;
195}
196
197static int
198add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
199{
200	int len = RTA_LENGTH(sizeof(data));
201	struct rtattr *rta;
202
203	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
204		errno = ENOBUFS;
205		return -1;
206	}
207
208	rta = NLMSG_TAIL(n);
209	rta->rta_type = type;
210	rta->rta_len = len;
211	memcpy(RTA_DATA(rta), &data, sizeof(data));
212	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
213
214	return 0;
215}
216
217struct nlma
218{
219	struct nlmsghdr hdr;
220	struct ifaddrmsg ifa;
221	char buffer[64];
222};
223
224struct nlmr
225{
226	struct nlmsghdr hdr;
227	struct rtmsg rt;
228	char buffer[256];
229};
230
231int
232if_address(const char *ifname,
233	   const struct in_addr *address, const struct in_addr *netmask,
234	   const struct in_addr *broadcast, int action)
235{
236	struct nlma *nlm;
237	int retval = 0;
238
239	nlm = xzalloc(sizeof(*nlm));
240	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
241	nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
242	if (action >= 0) {
243		nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
244		nlm->hdr.nlmsg_type = RTM_NEWADDR;
245	} else
246		nlm->hdr.nlmsg_type = RTM_DELADDR;
247	if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
248		free(nlm);
249		errno = ENODEV;
250		return -1;
251	}
252	nlm->ifa.ifa_family = AF_INET;
253	nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
254	/* This creates the aliased interface */
255	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
256		   ifname, strlen(ifname) + 1);
257	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
258		   &address->s_addr, sizeof(address->s_addr));
259	if (action >= 0)
260		add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
261			   &broadcast->s_addr, sizeof(broadcast->s_addr));
262
263	if (send_netlink(&nlm->hdr) == -1)
264		retval = -1;
265	free(nlm);
266	return retval;
267}
268
269int
270if_route(const char *ifname,
271	 const struct in_addr *destination, const struct in_addr *netmask,
272	 const struct in_addr *gateway, int metric, int action)
273{
274	struct nlmr *nlm;
275	unsigned int ifindex;
276	int retval = 0;
277
278
279	if (!(ifindex = if_nametoindex(ifname))) {
280		errno = ENODEV;
281		return -1;
282	}
283
284	nlm = xzalloc(sizeof(*nlm));
285	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
286	nlm->hdr.nlmsg_type = RTM_NEWROUTE;
287	if (action == 0)
288		nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
289	else if (action > 0)
290		/*
291		 * commented out NLM_F_EXCL here and
292		 * below. We sometimes keep one interface up while
293		 * we are configuring the other one, and this flag
294		 * causes route addition to fail.
295		 */
296		nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL*/;
297	else
298		nlm->hdr.nlmsg_type = RTM_DELROUTE;
299	nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
300	nlm->rt.rtm_family = AF_INET;
301	nlm->rt.rtm_table = RT_TABLE_MAIN;
302
303	if (action < 0)
304		nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
305	else {
306		nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/;
307		nlm->rt.rtm_protocol = RTPROT_BOOT;
308		if (gateway->s_addr == INADDR_ANY)
309			nlm->rt.rtm_scope = RT_SCOPE_LINK;
310		else
311			nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
312		nlm->rt.rtm_type = RTN_UNICAST;
313	}
314
315	nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
316	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
317		   &destination->s_addr, sizeof(destination->s_addr));
318	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
319		   &gateway->s_addr, sizeof(gateway->s_addr));
320
321	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
322	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
323
324	if (send_netlink(&nlm->hdr) == -1)
325		retval = -1;
326	free(nlm);
327	return retval;
328}
329