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