1 /* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
2
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
5   the Free Software Foundation; version 2 dated June, 1991.
6
7   This program is distributed in the hope that it will be useful,
8   but WITHOUT ANY WARRANTY; without even the implied warranty of
9   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10   GNU General Public License for more details.
11*/
12
13#include "dnsmasq.h"
14
15#ifdef HAVE_LINUX_NETWORK
16
17#include <linux/types.h>
18#include <linux/netlink.h>
19#include <linux/rtnetlink.h>
20
21static struct iovec iov;
22
23static void nl_err(struct nlmsghdr *h);
24static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h);
25
26void netlink_init(struct daemon *daemon)
27{
28  struct sockaddr_nl addr;
29
30  addr.nl_family = AF_NETLINK;
31  addr.nl_pad = 0;
32  addr.nl_pid = 0; /* autobind */
33#ifdef HAVE_IPV6
34  addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
35#else
36  addr.nl_groups = RTMGRP_IPV4_ROUTE;
37#endif
38
39  /* May not be able to have permission to set multicast groups don't die in that case */
40  if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
41    {
42      if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
43	{
44	  addr.nl_groups = 0;
45	  if (errno != EPERM || bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
46	    daemon->netlinkfd = -1;
47	}
48    }
49
50  if (daemon->netlinkfd == -1)
51    die(_("cannot create netlink socket: %s"), NULL);
52  else
53    {
54      int flags = fcntl(daemon->netlinkfd, F_GETFD);
55      if (flags != -1)
56	fcntl(daemon->netlinkfd, F_SETFD, flags | FD_CLOEXEC);
57    }
58
59  iov.iov_len = 200;
60  iov.iov_base = safe_malloc(iov.iov_len);
61}
62
63static ssize_t netlink_recv(struct daemon *daemon)
64{
65  struct msghdr msg;
66  ssize_t rc;
67
68  msg.msg_control = NULL;
69  msg.msg_controllen = 0;
70  msg.msg_name = NULL;
71  msg.msg_namelen = 0;
72  msg.msg_iov = &iov;
73  msg.msg_iovlen = 1;
74
75  while (1)
76    {
77      msg.msg_flags = 0;
78      while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
79
80      /* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a
81	 big buffer and pray in that case. */
82      if (rc == -1 && errno == EOPNOTSUPP)
83	{
84	  if (!expand_buf(&iov, 2000))
85	    return -1;
86	  break;
87	}
88
89      if (rc == -1 || !(msg.msg_flags & MSG_TRUNC))
90        break;
91
92      if (!expand_buf(&iov, iov.iov_len + 100))
93	return -1;
94    }
95
96  /* finally, read it for real */
97  while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
98
99  return rc;
100}
101
102int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
103{
104  struct sockaddr_nl addr;
105  struct nlmsghdr *h;
106  ssize_t len;
107  static unsigned int seq = 0;
108  int family = AF_INET;
109
110  struct {
111    struct nlmsghdr nlh;
112    struct rtgenmsg g;
113  } req;
114
115  addr.nl_family = AF_NETLINK;
116  addr.nl_pad = 0;
117  addr.nl_groups = 0;
118  addr.nl_pid = 0; /* address to kernel */
119
120 again:
121  req.nlh.nlmsg_len = sizeof(req);
122  req.nlh.nlmsg_type = RTM_GETADDR;
123  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
124  req.nlh.nlmsg_pid = 0;
125  req.nlh.nlmsg_seq = ++seq;
126  req.g.rtgen_family = family;
127
128  /* Don't block in recvfrom if send fails */
129  while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
130		      (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
131
132  if (len == -1)
133    return 0;
134
135  while (1)
136    {
137      if ((len = netlink_recv(daemon)) == -1)
138	return 0;
139
140      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
141 	if (h->nlmsg_type == NLMSG_ERROR)
142	  nl_err(h);
143	else if (h->nlmsg_seq != seq)
144	  nl_routechange(daemon, h); /* May be multicast arriving async */
145	else if (h->nlmsg_type == NLMSG_DONE)
146	  {
147#ifdef HAVE_IPV6
148	    if (family == AF_INET && ipv6_callback)
149	      {
150		family = AF_INET6;
151		goto again;
152	      }
153#endif
154	    return 1;
155	  }
156	else if (h->nlmsg_type == RTM_NEWADDR)
157	  {
158	    struct ifaddrmsg *ifa = NLMSG_DATA(h);
159	    struct rtattr *rta = IFA_RTA(ifa);
160	    unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
161
162	    if (ifa->ifa_family == AF_INET)
163	      {
164		struct in_addr netmask, addr, broadcast;
165
166		netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
167		addr.s_addr = 0;
168		broadcast.s_addr = 0;
169
170		while (RTA_OK(rta, len1))
171		  {
172		    if (rta->rta_type == IFA_LOCAL)
173		      addr = *((struct in_addr *)(rta+1));
174		    else if (rta->rta_type == IFA_BROADCAST)
175		      broadcast = *((struct in_addr *)(rta+1));
176
177		    rta = RTA_NEXT(rta, len1);
178		  }
179
180		if (addr.s_addr && ipv4_callback)
181		  if (!((*ipv4_callback)(daemon, addr, ifa->ifa_index, netmask, broadcast, parm)))
182		    return 0;
183	      }
184#ifdef HAVE_IPV6
185	    else if (ifa->ifa_family == AF_INET6)
186	      {
187		struct in6_addr *addrp = NULL;
188		while (RTA_OK(rta, len1))
189		  {
190		    if (rta->rta_type == IFA_ADDRESS)
191		      addrp = ((struct in6_addr *)(rta+1));
192
193		    rta = RTA_NEXT(rta, len1);
194		  }
195
196		if (addrp && ipv6_callback)
197		  if (!((*ipv6_callback)(daemon, addrp, ifa->ifa_index, ifa->ifa_index, parm)))
198		    return 0;
199	      }
200#endif
201	  }
202    }
203}
204
205void netlink_multicast(struct daemon *daemon)
206{
207  ssize_t len;
208  struct nlmsghdr *h;
209
210  if ((len = netlink_recv(daemon)) != -1)
211    {
212      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
213	if (h->nlmsg_type == NLMSG_ERROR)
214	  nl_err(h);
215	else
216	  nl_routechange(daemon, h);
217    }
218}
219
220static void nl_err(struct nlmsghdr *h)
221{
222  struct nlmsgerr *err = NLMSG_DATA(h);
223  if (err->error != 0)
224    syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
225}
226
227/* We arrange to receive netlink multicast messages whenever the network route is added.
228   If this happens and we still have a DNS packet in the buffer, we re-send it.
229   This helps on DoD links, where frequently the packet which triggers dialling is
230   a DNS query, which then gets lost. By re-sending, we can avoid the lookup
231   failing. */
232static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h)
233{
234  if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
235    {
236      struct rtmsg *rtm = NLMSG_DATA(h);
237      if (rtm->rtm_type == RTN_UNICAST &&
238	  rtm->rtm_scope == RT_SCOPE_LINK)
239	while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
240		     &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
241    }
242}
243#endif
244
245
246