blob: 62b589c262a0882d1bd747b18210a7ebb98fe729 [file] [log] [blame]
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010015*/
16
17#include "dnsmasq.h"
18
Simon Kelley824af852008-02-12 20:43:05 +000019#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
Simon Kelley70969c12012-03-07 20:46:28 +000020#include <ifaddrs.h>
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021
Simon Kelley132255b2012-08-06 20:12:04 +010022#include <sys/param.h>
Andy Stormont8de875f2016-02-01 12:07:57 +000023#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
Simon Kelley28866e92011-02-14 20:19:14 +000024#include <sys/sysctl.h>
Andy Stormont8de875f2016-02-01 12:07:57 +000025#endif
Simon Kelley1ee9be42013-12-09 16:50:19 +000026#include <net/if.h>
Simon Kelley28866e92011-02-14 20:19:14 +000027#include <net/route.h>
28#include <net/if_dl.h>
29#include <netinet/if_ether.h>
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +010030#if defined(__FreeBSD__)
31# include <net/if_var.h>
32#endif
33#include <netinet/in_var.h>
Simon Kelleyee875042018-10-23 22:10:17 +010034#include <netinet6/in6_var.h>
Simon Kelley28866e92011-02-14 20:19:14 +000035
Simon Kelley7de060b2011-08-26 17:24:52 +010036#ifndef SA_SIZE
37#define SA_SIZE(sa) \
38 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
39 sizeof(long) : \
40 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
41#endif
42
Simon Kelley1ee9be42013-12-09 16:50:19 +000043#ifdef HAVE_BSD_NETWORK
44static int del_family = 0;
Simon Kelleycc921df2019-01-02 22:48:59 +000045static union all_addr del_addr;
Simon Kelley1ee9be42013-12-09 16:50:19 +000046#endif
47
48#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
49
Simon Kelley28866e92011-02-14 20:19:14 +000050int arp_enumerate(void *parm, int (*callback)())
51{
52 int mib[6];
53 size_t needed;
54 char *next;
55 struct rt_msghdr *rtm;
56 struct sockaddr_inarp *sin2;
57 struct sockaddr_dl *sdl;
Simon Kelley08456c62012-03-07 19:08:11 +000058 struct iovec buff;
Simon Kelley28866e92011-02-14 20:19:14 +000059 int rc;
Simon Kelley08456c62012-03-07 19:08:11 +000060
61 buff.iov_base = NULL;
Simon Kelley96fafe22012-03-07 20:25:39 +000062 buff.iov_len = 0;
Simon Kelley08456c62012-03-07 19:08:11 +000063
Simon Kelley28866e92011-02-14 20:19:14 +000064 mib[0] = CTL_NET;
65 mib[1] = PF_ROUTE;
66 mib[2] = 0;
67 mib[3] = AF_INET;
68 mib[4] = NET_RT_FLAGS;
69#ifdef RTF_LLINFO
70 mib[5] = RTF_LLINFO;
71#else
72 mib[5] = 0;
73#endif
74 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
75 return 0;
76
77 while (1)
78 {
Simon Kelley08456c62012-03-07 19:08:11 +000079 if (!expand_buf(&buff, needed))
Simon Kelley28866e92011-02-14 20:19:14 +000080 return 0;
Simon Kelley08456c62012-03-07 19:08:11 +000081 if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
Simon Kelley28866e92011-02-14 20:19:14 +000082 errno != ENOMEM)
83 break;
84 needed += needed / 8;
85 }
86 if (rc == -1)
87 return 0;
88
Simon Kelley08456c62012-03-07 19:08:11 +000089 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
Simon Kelley28866e92011-02-14 20:19:14 +000090 {
91 rtm = (struct rt_msghdr *)next;
92 sin2 = (struct sockaddr_inarp *)(rtm + 1);
93 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
94 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
95 return 0;
96 }
97
98 return 1;
99}
Simon Kelley1ee9be42013-12-09 16:50:19 +0000100#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
Simon Kelley28866e92011-02-14 20:19:14 +0000101
102
103int iface_enumerate(int family, void *parm, int (*callback)())
Simon Kelley824af852008-02-12 20:43:05 +0000104{
Simon Kelley08456c62012-03-07 19:08:11 +0000105 struct ifaddrs *head, *addrs;
Josh Soref730c6742017-02-06 16:14:04 +0000106 int errsave, fd = -1, ret = 0;
Simon Kelley96fafe22012-03-07 20:25:39 +0000107
Simon Kelley28866e92011-02-14 20:19:14 +0000108 if (family == AF_UNSPEC)
109#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
110 return arp_enumerate(parm, callback);
111#else
112 return 0; /* need code for Solaris and MacOS*/
113#endif
114
Simon Kelley6aef6002012-02-11 22:01:50 +0000115 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
116 if (family == AF_LOCAL)
117 family = AF_LINK;
118
Simon Kelley08456c62012-03-07 19:08:11 +0000119 if (getifaddrs(&head) == -1)
Simon Kelley824af852008-02-12 20:43:05 +0000120 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +0000121
Simon Kelleyee875042018-10-23 22:10:17 +0100122#if defined(HAVE_BSD_NETWORK)
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100123 if (family == AF_INET6)
124 fd = socket(PF_INET6, SOCK_DGRAM, 0);
125#endif
126
Simon Kelley08456c62012-03-07 19:08:11 +0000127 for (addrs = head; addrs; addrs = addrs->ifa_next)
128 {
Simon Kelley96fafe22012-03-07 20:25:39 +0000129 if (addrs->ifa_addr->sa_family == family)
Simon Kelley824af852008-02-12 20:43:05 +0000130 {
Simon Kelley08456c62012-03-07 19:08:11 +0000131 int iface_index = if_nametoindex(addrs->ifa_name);
132
Simon Kelleyfc4c4fd2013-07-26 15:38:59 +0100133 if (iface_index == 0 || !addrs->ifa_addr ||
134 (!addrs->ifa_netmask && family != AF_LINK))
Simon Kelley08456c62012-03-07 19:08:11 +0000135 continue;
136
Simon Kelley28866e92011-02-14 20:19:14 +0000137 if (family == AF_INET)
Simon Kelley824af852008-02-12 20:43:05 +0000138 {
Simon Kelley28866e92011-02-14 20:19:14 +0000139 struct in_addr addr, netmask, broadcast;
Simon Kelley08456c62012-03-07 19:08:11 +0000140 addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
Simon Kelley1ee9be42013-12-09 16:50:19 +0000141#ifdef HAVE_BSD_NETWORK
Simon Kelleycc921df2019-01-02 22:48:59 +0000142 if (del_family == AF_INET && del_addr.addr4.s_addr == addr.s_addr)
Simon Kelley1ee9be42013-12-09 16:50:19 +0000143 continue;
144#endif
Simon Kelley08456c62012-03-07 19:08:11 +0000145 netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
Simon Kelley39f6a042013-01-09 19:57:47 +0000146 if (addrs->ifa_broadaddr)
147 broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
148 else
149 broadcast.s_addr = 0;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100150 if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
Simon Kelley28866e92011-02-14 20:19:14 +0000151 goto err;
Simon Kelley824af852008-02-12 20:43:05 +0000152 }
Simon Kelley28866e92011-02-14 20:19:14 +0000153 else if (family == AF_INET6)
154 {
Simon Kelley08456c62012-03-07 19:08:11 +0000155 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
Simon Kelley96fafe22012-03-07 20:25:39 +0000156 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
157 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
Simon Kelley08456c62012-03-07 19:08:11 +0000158 int i, j, prefix = 0;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100159 u32 valid = 0xffffffff, preferred = 0xffffffff;
160 int flags = 0;
Simon Kelley1ee9be42013-12-09 16:50:19 +0000161#ifdef HAVE_BSD_NETWORK
Simon Kelleycc921df2019-01-02 22:48:59 +0000162 if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr6, addr))
Simon Kelley1ee9be42013-12-09 16:50:19 +0000163 continue;
164#endif
Jeremy Lavergne50d7f722013-10-28 11:26:30 +0000165#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100166 struct in6_ifreq ifr6;
167
168 memset(&ifr6, 0, sizeof(ifr6));
Petr Menšík47b45b22018-08-15 18:17:00 +0200169 safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
Simon Kelley08456c62012-03-07 19:08:11 +0000170
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100171 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
172 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
173 {
174 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
175 flags |= IFACE_TENTATIVE;
176
177 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
178 flags |= IFACE_DEPRECATED;
Vladislav Grishenko4568a6f2013-08-19 16:07:07 +0100179
Simon Kelley1b551902013-09-23 15:03:05 +0100180#ifdef IN6_IFF_TEMPORARY
Vladislav Grishenko4568a6f2013-08-19 16:07:07 +0100181 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
182 flags |= IFACE_PERMANENT;
Simon Kelley1b551902013-09-23 15:03:05 +0100183#endif
Vladislav Grishenko4568a6f2013-08-19 16:07:07 +0100184
Simon Kelley1b551902013-09-23 15:03:05 +0100185#ifdef IN6_IFF_PRIVACY
186 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
187 flags |= IFACE_PERMANENT;
188#endif
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100189 }
190
191 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
192 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
193 {
194 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
195 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
196 }
197#endif
198
Simon Kelley96fafe22012-03-07 20:25:39 +0000199 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
Simon Kelley08456c62012-03-07 19:08:11 +0000200 if (netmask[i] != 0xff)
201 break;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100202
Simon Kelley96fafe22012-03-07 20:25:39 +0000203 if (i != IN6ADDRSZ && netmask[i])
Simon Kelley08456c62012-03-07 19:08:11 +0000204 for (j = 7; j > 0; j--, prefix++)
205 if ((netmask[i] & (1 << j)) == 0)
206 break;
207
Simon Kelley28866e92011-02-14 20:19:14 +0000208 /* voodoo to clear interface field in address */
209 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
210 {
211 addr->s6_addr[2] = 0;
212 addr->s6_addr[3] = 0;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100213 }
214
215 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
216 (int) preferred, (int)valid, parm)))
217 goto err;
218 }
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100219
Simon Kelley6aef6002012-02-11 22:01:50 +0000220#ifdef HAVE_DHCP6
221 else if (family == AF_LINK)
222 {
223 /* Assume ethernet again here */
Simon Kelley96fafe22012-03-07 20:25:39 +0000224 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
Simon Kelley08456c62012-03-07 19:08:11 +0000225 if (sdl->sdl_alen != 0 &&
226 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
Simon Kelley6aef6002012-02-11 22:01:50 +0000227 goto err;
228 }
229#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000230 }
Simon Kelley824af852008-02-12 20:43:05 +0000231 }
232
233 ret = 1;
234
235 err:
Josh Soref730c6742017-02-06 16:14:04 +0000236 errsave = errno;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100237 freeifaddrs(head);
238 if (fd != -1)
239 close(fd);
Josh Soref730c6742017-02-06 16:14:04 +0000240 errno = errsave;
Simon Kelley824af852008-02-12 20:43:05 +0000241
242 return ret;
243}
Simon Kelley1ee9be42013-12-09 16:50:19 +0000244#endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
Simon Kelley824af852008-02-12 20:43:05 +0000245
246
Simon Kelley7622fc02009-06-04 20:32:05 +0100247#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
Simon Kelley824af852008-02-12 20:43:05 +0000248#include <net/bpf.h>
249
Simon Kelley5aabfc72007-08-29 11:24:47 +0100250void init_bpf(void)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100251{
252 int i = 0;
253
254 while (1)
255 {
Simon Kelley08456c62012-03-07 19:08:11 +0000256 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
257 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
258 return;
259
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100260 if (errno != EBUSY)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100261 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100262 }
263}
264
Simon Kelley5aabfc72007-08-29 11:24:47 +0100265void send_via_bpf(struct dhcp_packet *mess, size_t len,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100266 struct in_addr iface_addr, struct ifreq *ifr)
267{
268 /* Hairy stuff, packet either has to go to the
269 net broadcast or the destination can't reply to ARP yet,
270 but we do know the physical address.
271 Build the packet by steam, and send directly, bypassing
272 the kernel IP stack */
273
Simon Kelley849a8352006-06-09 21:02:31 +0100274 struct ether_header ether;
275 struct ip ip;
276 struct udphdr {
277 u16 uh_sport; /* source port */
278 u16 uh_dport; /* destination port */
279 u16 uh_ulen; /* udp length */
280 u16 uh_sum; /* udp checksum */
281 } udp;
282
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100283 u32 i, sum;
Simon Kelley849a8352006-06-09 21:02:31 +0100284 struct iovec iov[4];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100285
286 /* Only know how to do ethernet on *BSD */
287 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
288 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100289 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100290 mess->htype, ifr->ifr_name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100291 return;
292 }
293
294 ifr->ifr_addr.sa_family = AF_LINK;
295 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
296 return;
297
Simon Kelley849a8352006-06-09 21:02:31 +0100298 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
299 ether.ether_type = htons(ETHERTYPE_IP);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100300
301 if (ntohs(mess->flags) & 0x8000)
302 {
Simon Kelley849a8352006-06-09 21:02:31 +0100303 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
304 ip.ip_dst.s_addr = INADDR_BROADCAST;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100305 }
306 else
307 {
Simon Kelley849a8352006-06-09 21:02:31 +0100308 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
309 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100310 }
311
Simon Kelley849a8352006-06-09 21:02:31 +0100312 ip.ip_p = IPPROTO_UDP;
313 ip.ip_src.s_addr = iface_addr.s_addr;
314 ip.ip_len = htons(sizeof(struct ip) +
315 sizeof(struct udphdr) +
316 len) ;
317 ip.ip_hl = sizeof(struct ip) / 4;
318 ip.ip_v = IPVERSION;
319 ip.ip_tos = 0;
320 ip.ip_id = htons(0);
321 ip.ip_off = htons(0x4000); /* don't fragment */
322 ip.ip_ttl = IPDEFTTL;
323 ip.ip_sum = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100324 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100325 sum += ((u16 *)&ip)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100326 while (sum>>16)
327 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100328 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100329
Simon Kelley9e038942008-05-30 20:06:34 +0100330 udp.uh_sport = htons(daemon->dhcp_server_port);
331 udp.uh_dport = htons(daemon->dhcp_client_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100332 if (len & 1)
333 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
Simon Kelley849a8352006-06-09 21:02:31 +0100334 udp.uh_sum = 0;
335 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100336 sum += htons(IPPROTO_UDP);
Simon Kelley824af852008-02-12 20:43:05 +0000337 sum += ip.ip_src.s_addr & 0xffff;
338 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
339 sum += ip.ip_dst.s_addr & 0xffff;
340 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100341 for (i = 0; i < sizeof(struct udphdr)/2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100342 sum += ((u16 *)&udp)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100343 for (i = 0; i < (len + 1) / 2; i++)
344 sum += ((u16 *)mess)[i];
345 while (sum>>16)
346 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100347 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100348
349 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
350
Simon Kelley849a8352006-06-09 21:02:31 +0100351 iov[0].iov_base = &ether;
352 iov[0].iov_len = sizeof(ether);
353 iov[1].iov_base = &ip;
354 iov[1].iov_len = sizeof(ip);
355 iov[2].iov_base = &udp;
356 iov[2].iov_len = sizeof(udp);
357 iov[3].iov_base = mess;
358 iov[3].iov_len = len;
359
Simon Kelleyff841eb2015-03-11 21:36:30 +0000360 while (retry_send(writev(daemon->dhcp_raw_fd, iov, 4)));
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100361}
362
Simon Kelley1ee9be42013-12-09 16:50:19 +0000363#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
364
365
366#ifdef HAVE_BSD_NETWORK
367
368void route_init(void)
369{
370 /* AF_UNSPEC: all addr families */
371 daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
372
373 if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
374 die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
375}
376
Simon Kelleya0358e52014-06-07 13:38:48 +0100377void route_sock(void)
Simon Kelley1ee9be42013-12-09 16:50:19 +0000378{
379 struct if_msghdr *msg;
380 int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
381
382 if (rc < 4)
383 return;
384
385 msg = (struct if_msghdr *)daemon->packet;
386
387 if (rc < msg->ifm_msglen)
388 return;
389
390 if (msg->ifm_version != RTM_VERSION)
391 {
392 static int warned = 0;
393 if (!warned)
394 {
395 my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
396 warned = 1;
397 }
398 }
399 else if (msg->ifm_type == RTM_NEWADDR)
400 {
401 del_family = 0;
Simon Kelley47a95162014-07-08 22:22:02 +0100402 queue_event(EVENT_NEWADDR);
Simon Kelley1ee9be42013-12-09 16:50:19 +0000403 }
404 else if (msg->ifm_type == RTM_DELADDR)
405 {
406 /* There's a race in the kernel, such that if we run iface_enumerate() immediately
407 we get a DELADDR event, the deleted address still appears. Here we store the deleted address
408 in a static variable, and omit it from the set returned by iface_enumerate() */
409 int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
410 int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
411 RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
412 int of;
413 unsigned int i;
414
415 for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
416 if (mask & maskvec[i])
417 {
418 struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
419 size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
420
421 if (maskvec[i] == RTA_IFA)
422 {
423 del_family = sa->sa_family;
424 if (del_family == AF_INET)
Simon Kelleycc921df2019-01-02 22:48:59 +0000425 del_addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
Simon Kelley1ee9be42013-12-09 16:50:19 +0000426 else if (del_family == AF_INET6)
Simon Kelleycc921df2019-01-02 22:48:59 +0000427 del_addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
Simon Kelley1ee9be42013-12-09 16:50:19 +0000428 else
429 del_family = 0;
430 }
431
432 of += diff;
433 /* round up as needed */
434 if (diff & (sizeof(long) - 1))
435 of += sizeof(long) - (diff & (sizeof(long) - 1));
436 }
437
Simon Kelley47a95162014-07-08 22:22:02 +0100438 queue_event(EVENT_NEWADDR);
Simon Kelley1ee9be42013-12-09 16:50:19 +0000439 }
440}
441
442#endif /* HAVE_BSD_NETWORK */