1 /*
2  *
3  *   Authors:
4  *    Lars Fenneberg		<lf@elemental.net>
5  *
6  *   This software is Copyright 1996,1997 by the above mentioned author(s),
7  *   All Rights Reserved.
8  *
9  *   The license which is distributed with this software in the file COPYRIGHT
10  *   applies to this software. If your distribution is missing this file, you
11  *   may request it from <reubenhwk@gmail.com>.
12  *
13  */
14 
15 #include "config.h"
16 #include "defaults.h"
17 #include "includes.h"
18 #include "pathnames.h"
19 #include "radvd.h"
20 
check_device(int sock,struct Interface * iface)21 int check_device(int sock, struct Interface *iface)
22 {
23 	struct ifreq ifr;
24 	memset(&ifr, 0, sizeof(ifr));
25 	strncpy(ifr.ifr_name, iface->props.name, IFNAMSIZ - 1);
26 
27 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
28 		flog(LOG_ERR, "ioctl(SIOCGIFFLAGS) failed on %s: %s", iface->props.name, strerror(errno));
29 		return -1;
30 	} else {
31 		dlog(LOG_ERR, 5, "ioctl(SIOCGIFFLAGS) succeeded on %s", iface->props.name);
32 	}
33 
34 	if (!(ifr.ifr_flags & IFF_UP)) {
35 		dlog(LOG_ERR, 4, "%s is not up", iface->props.name);
36 		return -1;
37 	} else {
38 		dlog(LOG_ERR, 4, "%s is up", iface->props.name);
39 	}
40 
41 	if (!(ifr.ifr_flags & IFF_RUNNING)) {
42 		dlog(LOG_ERR, 4, "%s is not running", iface->props.name);
43 		return -1;
44 	} else {
45 		dlog(LOG_ERR, 4, "%s is running", iface->props.name);
46 	}
47 
48 	if (!iface->UnicastOnly &&
49 	    !(ifr.ifr_flags & (IFF_MULTICAST | IFF_POINTOPOINT))) {
50 		flog(LOG_INFO,
51 		     "%s does not support multicast or point-to-point, forcing UnicastOnly",
52 		     iface->props.name);
53 		iface->UnicastOnly = 1;
54 	} else {
55 		dlog(LOG_ERR, 4, "%s supports multicast or is point-to-point",
56 		     iface->props.name);
57 	}
58 
59 	return 0;
60 }
61 
get_v4addr(const char * ifn,unsigned int * dst)62 int get_v4addr(const char *ifn, unsigned int *dst)
63 {
64 
65 	int fd = socket(AF_INET, SOCK_DGRAM, 0);
66 	if (fd < 0) {
67 		flog(LOG_ERR, "create socket for IPv4 ioctl failed on %s: %s", ifn, strerror(errno));
68 		return -1;
69 	}
70 
71 	struct ifreq ifr;
72 	memset(&ifr, 0, sizeof(ifr));
73 	strncpy(ifr.ifr_name, ifn, IFNAMSIZ - 1);
74 	ifr.ifr_name[IFNAMSIZ - 1] = '\0';
75 	ifr.ifr_addr.sa_family = AF_INET;
76 
77 	if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
78 		flog(LOG_ERR, "ioctl(SIOCGIFADDR) failed on %s: %s", ifn, strerror(errno));
79 		close(fd);
80 		return -1;
81 	}
82 
83 	struct sockaddr_in *addr = (struct sockaddr_in *)(&ifr.ifr_addr);
84 
85 	dlog(LOG_DEBUG, 3, "%s IPv4 address is: %s", ifn, inet_ntoa(addr->sin_addr));
86 
87 	*dst = addr->sin_addr.s_addr;
88 
89 	close(fd);
90 
91 	return 0;
92 }
93 
cmp_iface_addrs(void const * a,void const * b)94 static int cmp_iface_addrs(void const *a, void const *b) { return memcmp(a, b, sizeof(struct in6_addr)); }
95 
96 /*
97  * Return first IPv6 link local addr in if_addr.
98  * Return all the IPv6 addresses in if_addrs in ascending
99  * order.
100  * Return value is -1 if there was no link local addr.
101  * otherwise return value is count of addres in if_addrs
102  * not including the all zero (unspecified) addr at the
103  * end of the list.
104  */
get_iface_addrs(char const * name,struct in6_addr * if_addr,struct in6_addr ** if_addrs)105 int get_iface_addrs(char const *name, struct in6_addr *if_addr, struct in6_addr **if_addrs)
106 {
107 	struct ifaddrs *addresses = 0;
108 	int link_local_set = 0;
109 	int i = 0;
110 
111 	if (getifaddrs(&addresses) != 0) {
112 		flog(LOG_ERR, "getifaddrs failed on %s: %s", name, strerror(errno));
113 	} else {
114 		for (struct ifaddrs *ifa = addresses; ifa != NULL; ifa = ifa->ifa_next) {
115 
116 			if (!ifa->ifa_addr)
117 				continue;
118 
119 			if (ifa->ifa_addr->sa_family != AF_INET6)
120 				continue;
121 
122 			struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)ifa->ifa_addr;
123 
124 			/* Skip if it is not the interface we're looking for. */
125 			if (strcmp(ifa->ifa_name, name) != 0)
126 				continue;
127 
128 			*if_addrs = realloc(*if_addrs, (i + 1) * sizeof(struct in6_addr));
129 			(*if_addrs)[i++] = a6->sin6_addr;
130 
131 			/* Skip if it is not a linklocal address or link locak address already found*/
132 			uint8_t const ll_prefix[] = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
133 			if (link_local_set || 0 != memcmp(&(a6->sin6_addr), ll_prefix, sizeof(ll_prefix)))
134 				continue;
135 
136 			if (if_addr)
137 				memcpy(if_addr, &(a6->sin6_addr), sizeof(struct in6_addr));
138 
139 			link_local_set = 1;
140 		}
141 	}
142 
143 	if (addresses)
144 		freeifaddrs(addresses);
145 
146 	/* last item in the list is all zero (unspecified) address */
147 	*if_addrs = realloc(*if_addrs, (i + 1) * sizeof(struct in6_addr));
148 	memset(&(*if_addrs)[i], 0, sizeof(struct in6_addr));
149 
150 	/* Sort the addresses so the output is predictable. */
151 	qsort(*if_addrs, i, sizeof(struct in6_addr), cmp_iface_addrs);
152 
153 	if (!link_local_set)
154 		return -1;
155 
156 	return i;
157 }
158 
159 /*
160  * Saves the first link local address seen on the specified interface to iface->if_addr
161  * and builds a list of all the other addrs.
162  */
setup_iface_addrs(struct Interface * iface)163 int setup_iface_addrs(struct Interface *iface)
164 {
165 	int rc = get_iface_addrs(iface->props.name, &iface->props.if_addr, &iface->props.if_addrs);
166 
167 	if (-1 != rc) {
168 		iface->props.addrs_count = rc;
169 		char addr_str[INET6_ADDRSTRLEN];
170 		addrtostr(&iface->props.if_addr, addr_str, sizeof(addr_str));
171 		dlog(LOG_DEBUG, 4, "%s linklocal address: %s", iface->props.name, addr_str);
172 		for (int i = 0; i < rc; ++i) {
173 			addrtostr(&iface->props.if_addrs[i], addr_str, sizeof(addr_str));
174 			dlog(LOG_DEBUG, 4, "%s address: %s", iface->props.name, addr_str);
175 		}
176 		/* AdvRASrcAddress: allow operator selection of RA source address */
177 		if (iface->AdvRASrcAddressList != NULL) {
178 			iface->props.if_addr_rasrc = NULL;
179 			for (struct AdvRASrcAddress *current = iface->AdvRASrcAddressList; current; current = current->next) {
180 				for (int i = 0; i < iface->props.addrs_count; i++) {
181 					struct in6_addr cmp_addr = iface->props.if_addrs[i];
182 					if (0 == memcmp(&cmp_addr, &current->address, sizeof(struct in6_addr))) {
183 						addrtostr(&(cmp_addr), addr_str, sizeof(addr_str));
184 						dlog(LOG_DEBUG, 4, "AdvRASrcAddress selecting: %s", addr_str);
185 						iface->props.if_addr_rasrc = &iface->props.if_addrs[i];
186 						break;
187 					}
188 				}
189 				if (NULL != iface->props.if_addr_rasrc)
190 					break;
191 			}
192 		} else {
193 			/* AdvRASrcAddress default: Just take the first link-local */
194 			iface->props.if_addr_rasrc = &iface->props.if_addr;
195 		}
196 	} else {
197 		if (iface->IgnoreIfMissing)
198 			dlog(LOG_DEBUG, 4, "no linklocal address configured on %s", iface->props.name);
199 		else
200 			flog(LOG_ERR, "no linklocal address configured on %s", iface->props.name);
201 	}
202 
203 	return rc;
204 }
205 
update_device_index(struct Interface * iface)206 int update_device_index(struct Interface *iface)
207 {
208 	int index = if_nametoindex(iface->props.name);
209 
210 	if (0 == index) {
211 		/* Yes, if_nametoindex returns zero on failure.  2014/01/16 */
212 		flog(LOG_ERR, "%s not found: %s", iface->props.name, strerror(errno));
213 		return -1;
214 	}
215 
216 	if (iface->props.if_index != index) {
217 		dlog(LOG_DEBUG, 4, "%s if_index changed from %d to %d", iface->props.name, iface->props.if_index, index);
218 		iface->props.if_index = index;
219 	}
220 
221 	return 0;
222 }
223