interface.c revision 1.22
1214152Sed/*	$OpenBSD: interface.c,v 1.22 2015/03/21 18:32:01 renato Exp $ */
2214152Sed
3214152Sed/*
4214152Sed * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5214152Sed * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
6214152Sed *
7214152Sed * Permission to use, copy, modify, and distribute this software for any
8214152Sed * purpose with or without fee is hereby granted, provided that the above
9214152Sed * copyright notice and this permission notice appear in all copies.
10214152Sed *
11214152Sed * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12214152Sed * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13214152Sed * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14214152Sed * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15214152Sed * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16214152Sed * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17214152Sed * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18214152Sed */
19214152Sed
20214152Sed#include <sys/types.h>
21214152Sed#include <sys/ioctl.h>
22214152Sed#include <sys/time.h>
23214152Sed#include <sys/socket.h>
24214152Sed#include <netinet/in.h>
25214152Sed#include <arpa/inet.h>
26214152Sed#include <net/if.h>
27214152Sed#include <net/if_types.h>
28214152Sed#include <fcntl.h>
29214152Sed#include <ctype.h>
30214152Sed#include <err.h>
31214152Sed#include <stdio.h>
32214152Sed#include <stdlib.h>
33214152Sed#include <unistd.h>
34214152Sed#include <string.h>
35214152Sed#include <event.h>
36214152Sed
37214152Sed#include "ldpd.h"
38214152Sed#include "ldp.h"
39214152Sed#include "log.h"
40214152Sed#include "ldpe.h"
41214152Sed
42214152Sedextern struct ldpd_conf        *leconf;
43214152Sed
44214152Sedvoid		 if_hello_timer(int, short, void *);
45214152Sedvoid		 if_start_hello_timer(struct iface *);
46214152Sedvoid		 if_stop_hello_timer(struct iface *);
47214152Sed
48214152Sedstruct iface *
49214152Sedif_new(struct kif *kif)
50214152Sed{
51214152Sed	struct iface		*iface;
52214152Sed
53214152Sed	if ((iface = calloc(1, sizeof(*iface))) == NULL)
54214152Sed		err(1, "if_new: calloc");
55214152Sed
56214152Sed	iface->state = IF_STA_DOWN;
57214152Sed
58214152Sed	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
59214152Sed
60	/* get type */
61	if (kif->flags & IFF_POINTOPOINT)
62		iface->type = IF_TYPE_POINTOPOINT;
63	if (kif->flags & IFF_BROADCAST &&
64	    kif->flags & IFF_MULTICAST)
65		iface->type = IF_TYPE_BROADCAST;
66
67	/* get index and flags */
68	iface->ifindex = kif->ifindex;
69	iface->flags = kif->flags;
70	iface->linkstate = kif->link_state;
71	iface->media_type = kif->media_type;
72
73	return (iface);
74}
75
76void
77if_del(struct iface *iface)
78{
79	struct if_addr		*if_addr;
80
81	if (iface->state == IF_STA_ACTIVE)
82		if_reset(iface);
83
84	log_debug("if_del: interface %s", iface->name);
85
86	while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL)
87		LIST_REMOVE(if_addr, iface_entry);
88
89	free(iface);
90}
91
92void
93if_init(struct ldpd_conf *xconf, struct iface *iface)
94{
95	/* set event handlers for interface */
96	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
97
98	iface->discovery_fd = xconf->ldp_discovery_socket;
99}
100
101struct iface *
102if_lookup(u_short ifindex)
103{
104	struct iface *iface;
105
106	LIST_FOREACH(iface, &leconf->iface_list, entry)
107		if (iface->ifindex == ifindex)
108			return (iface);
109
110	return (NULL);
111}
112
113/* timers */
114/* ARGSUSED */
115void
116if_hello_timer(int fd, short event, void *arg)
117{
118	struct iface *iface = arg;
119	struct timeval tv;
120
121	send_hello(HELLO_LINK, iface, NULL);
122
123	/* reschedule hello_timer */
124	timerclear(&tv);
125	tv.tv_sec = iface->hello_interval;
126	if (evtimer_add(&iface->hello_timer, &tv) == -1)
127		fatal("if_hello_timer");
128}
129
130void
131if_start_hello_timer(struct iface *iface)
132{
133	struct timeval tv;
134
135	send_hello(HELLO_LINK, iface, NULL);
136
137	timerclear(&tv);
138	tv.tv_sec = iface->hello_interval;
139	if (evtimer_add(&iface->hello_timer, &tv) == -1)
140		fatal("if_start_hello_timer");
141}
142
143void
144if_stop_hello_timer(struct iface *iface)
145{
146	if (evtimer_pending(&iface->hello_timer, NULL) &&
147	    evtimer_del(&iface->hello_timer) == -1)
148		fatal("if_stop_hello_timer");
149}
150
151int
152if_start(struct iface *iface)
153{
154	struct in_addr		 addr;
155	struct timeval		 now;
156
157	log_debug("if_start: %s", iface->name);
158
159	gettimeofday(&now, NULL);
160	iface->uptime = now.tv_sec;
161
162	inet_aton(AllRouters, &addr);
163	if (if_join_group(iface, &addr))
164		return (-1);
165
166	/* hello timer needs to be started in any case */
167	if_start_hello_timer(iface);
168	return (0);
169}
170
171int
172if_reset(struct iface *iface)
173{
174	struct in_addr		 addr;
175	struct adj		*adj;
176
177	log_debug("if_reset: %s", iface->name);
178
179	while ((adj = LIST_FIRST(&iface->adj_list)) != NULL) {
180		LIST_REMOVE(adj, iface_entry);
181		adj_del(adj);
182	}
183
184	if_stop_hello_timer(iface);
185
186	/* try to cleanup */
187	inet_aton(AllRouters, &addr);
188	if_leave_group(iface, &addr);
189
190	return (0);
191}
192
193int
194if_update(struct iface *iface)
195{
196	int ret;
197
198	if (iface->state == IF_STA_DOWN) {
199		if (!(iface->flags & IFF_UP) ||
200		    !LINK_STATE_IS_UP(iface->linkstate) ||
201		    LIST_EMPTY(&iface->addr_list))
202			return (0);
203
204		iface->state = IF_STA_ACTIVE;
205		ret = if_start(iface);
206	} else {
207		if ((iface->flags & IFF_UP) &&
208		    LINK_STATE_IS_UP(iface->linkstate) &&
209		    !LIST_EMPTY(&iface->addr_list))
210			return (0);
211
212		iface->state = IF_STA_DOWN;
213		ret = if_reset(iface);
214	}
215
216	return (ret);
217}
218
219struct ctl_iface *
220if_to_ctl(struct iface *iface)
221{
222	static struct ctl_iface	 ictl;
223	struct timeval		 now;
224	struct adj		*adj;
225
226	memcpy(ictl.name, iface->name, sizeof(ictl.name));
227	ictl.ifindex = iface->ifindex;
228	ictl.state = iface->state;
229	ictl.hello_holdtime = iface->hello_holdtime;
230	ictl.hello_interval = iface->hello_interval;
231	ictl.flags = iface->flags;
232	ictl.type = iface->type;
233	ictl.linkstate = iface->linkstate;
234	ictl.mediatype = iface->media_type;
235
236	gettimeofday(&now, NULL);
237	if (iface->state != IF_STA_DOWN &&
238	    iface->uptime != 0) {
239		ictl.uptime = now.tv_sec - iface->uptime;
240	} else
241		ictl.uptime = 0;
242
243	ictl.adj_cnt = 0;
244	LIST_FOREACH(adj, &iface->adj_list, iface_entry)
245		ictl.adj_cnt++;
246
247	return (&ictl);
248}
249
250/* misc */
251int
252if_set_mcast_ttl(int fd, u_int8_t ttl)
253{
254	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
255	    (char *)&ttl, sizeof(ttl)) < 0) {
256		log_warn("if_set_mcast_ttl: error setting "
257		    "IP_MULTICAST_TTL to %d", ttl);
258		return (-1);
259	}
260
261	return (0);
262}
263
264int
265if_set_tos(int fd, int tos)
266{
267	if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
268		log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
269		return (-1);
270	}
271
272	return (0);
273}
274
275int
276if_set_recvif(int fd, int enable)
277{
278	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
279	    sizeof(enable)) < 0) {
280		log_warn("if_set_recvif: error setting IP_RECVIF");
281		return (-1);
282	}
283	return (0);
284}
285
286void
287if_set_recvbuf(int fd)
288{
289	int	bsize;
290
291	bsize = 65535;
292	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
293	    sizeof(bsize)) == -1)
294		bsize /= 2;
295}
296
297int
298if_set_reuse(int fd, int enable)
299{
300	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
301	    sizeof(int)) < 0) {
302		log_warn("if_set_reuse: error setting SO_REUSEADDR");
303		return (-1);
304	}
305
306	return (0);
307}
308
309/*
310 * only one JOIN or DROP per interface and address is allowed so we need
311 * to keep track of what is added and removed.
312 */
313struct if_group_count {
314	LIST_ENTRY(if_group_count)	entry;
315	struct in_addr			addr;
316	unsigned int			ifindex;
317	int				count;
318};
319
320LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
321
322int
323if_join_group(struct iface *iface, struct in_addr *addr)
324{
325	struct ip_mreq		 mreq;
326	struct if_group_count	*ifg;
327	struct if_addr		*if_addr;
328
329	LIST_FOREACH(ifg, &ifglist, entry)
330		if (iface->ifindex == ifg->ifindex &&
331		    addr->s_addr == ifg->addr.s_addr)
332			break;
333	if (ifg == NULL) {
334		if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
335			fatal("if_join_group");
336		ifg->addr.s_addr = addr->s_addr;
337		ifg->ifindex = iface->ifindex;
338		LIST_INSERT_HEAD(&ifglist, ifg, entry);
339	}
340
341	if (ifg->count++ != 0)
342		/* already joined */
343		return (0);
344
345	if_addr = LIST_FIRST(&iface->addr_list);
346	mreq.imr_multiaddr.s_addr = addr->s_addr;
347	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
348
349	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
350	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
351		log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
352		    "interface %s address %s", iface->name,
353		    inet_ntoa(*addr));
354		LIST_REMOVE(ifg, entry);
355		free(ifg);
356		return (-1);
357	}
358	return (0);
359}
360
361int
362if_leave_group(struct iface *iface, struct in_addr *addr)
363{
364	struct ip_mreq		 mreq;
365	struct if_group_count	*ifg;
366	struct if_addr		*if_addr;
367
368	LIST_FOREACH(ifg, &ifglist, entry)
369		if (iface->ifindex == ifg->ifindex &&
370		    addr->s_addr == ifg->addr.s_addr)
371			break;
372
373	/* if interface is not found just try to drop membership */
374	if (ifg) {
375		if (--ifg->count != 0)
376			/* others still joined */
377			return (0);
378
379		LIST_REMOVE(ifg, entry);
380		free(ifg);
381	}
382
383	if_addr = LIST_FIRST(&iface->addr_list);
384	if (!if_addr)
385		return (0);
386
387	mreq.imr_multiaddr.s_addr = addr->s_addr;
388	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
389
390	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
391	    IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
392		log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
393		    "interface %s address %s", iface->name,
394		    inet_ntoa(*addr));
395		return (-1);
396	}
397
398	return (0);
399}
400
401int
402if_set_mcast(struct iface *iface)
403{
404	struct if_addr		*if_addr;
405
406	if_addr = LIST_FIRST(&iface->addr_list);
407
408	if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF,
409	    &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) {
410		log_debug("if_set_mcast: error setting "
411		    "IP_MULTICAST_IF, interface %s", iface->name);
412		return (-1);
413	}
414
415	return (0);
416}
417
418int
419if_set_mcast_loop(int fd)
420{
421	u_int8_t	loop = 0;
422
423	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
424	    (char *)&loop, sizeof(loop)) < 0) {
425		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
426		return (-1);
427	}
428
429	return (0);
430}
431