dnsmasq: fix a race condition possibly leading to lockup
[librecmc/librecmc.git] / package / network / services / dnsmasq / patches / 002-fix-race-on-interface-flaps.patch
1 From a0358e5ddbc1ef3dec791f11f95f5dbe56087a5e Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Sat, 7 Jun 2014 13:38:48 +0100
4 Subject: [PATCH] Handle async notification of address changes using the event
5  system.
6
7 ---
8  CHANGELOG     |  4 ++++
9  src/bpf.c     |  6 +++---
10  src/dhcp6.c   | 10 ----------
11  src/dnsmasq.c | 13 +++++++++++--
12  src/dnsmasq.h |  6 ++++--
13  src/netlink.c | 39 ++++++++++-----------------------------
14  src/network.c | 11 +++--------
15  7 files changed, 35 insertions(+), 54 deletions(-)
16
17 --- a/CHANGELOG
18 +++ b/CHANGELOG
19 @@ -15,6 +15,10 @@ version 2.71
20             regression introduced in 2.69. Thanks to James Hunt and
21             the Ubuntu crowd for assistance in fixing this.
22  
23 +           Fix race condition which could lock up dnsmasq when an 
24 +           interface goes down and up rapidly. Thanks to Conrad 
25 +           Kostecki for helping to chase this down.
26 +           
27  
28  version 2.70
29              Fix crash, introduced in 2.69, on TCP request when dnsmasq
30 --- a/src/bpf.c
31 +++ b/src/bpf.c
32 @@ -376,7 +376,7 @@ void route_init(void)
33      die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
34  }
35  
36 -void route_sock(time_t now)
37 +void route_sock(void)
38  {
39    struct if_msghdr *msg;
40    int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
41 @@ -401,7 +401,7 @@ void route_sock(time_t now)
42     else if (msg->ifm_type == RTM_NEWADDR)
43       {
44         del_family = 0;
45 -       newaddress(now);
46 +       send_newaddr();
47       }
48     else if (msg->ifm_type == RTM_DELADDR)
49       {
50 @@ -439,7 +439,7 @@ void route_sock(time_t now)
51                of += sizeof(long) - (diff & (sizeof(long) - 1));
52            }
53         
54 -       newaddress(now);
55 +       send_newaddr();
56       }
57  }
58  
59 --- a/src/dnsmasq.c
60 +++ b/src/dnsmasq.c
61 @@ -917,10 +917,10 @@ int main (int argc, char **argv)
62  
63  #if defined(HAVE_LINUX_NETWORK)
64        if (FD_ISSET(daemon->netlinkfd, &rset))
65 -       netlink_multicast(now);
66 +       netlink_multicast();
67  #elif defined(HAVE_BSD_NETWORK)
68        if (FD_ISSET(daemon->routefd, &rset))
69 -       route_sock(now);
70 +       route_sock();
71  #endif
72  
73        /* Check for changes to resolv files once per second max. */
74 @@ -1037,6 +1037,11 @@ void send_alarm(time_t event, time_t now
75      }
76  }
77  
78 +void send_newaddr(void)
79 +{
80 +  send_event(pipewrite, EVENT_NEWADDR, 0, NULL);
81 +}
82 +
83  void send_event(int fd, int event, int data, char *msg)
84  {
85    struct event_desc ev;
86 @@ -1230,6 +1235,10 @@ static void async_event(int pipe, time_t
87         if (daemon->log_file != NULL)
88           log_reopen(daemon->log_file);
89         break;
90 +
91 +      case EVENT_NEWADDR:
92 +       newaddress(now);
93 +       break;
94         
95        case EVENT_TERM:
96         /* Knock all our children on the head. */
97 --- a/src/dnsmasq.h
98 +++ b/src/dnsmasq.h
99 @@ -165,6 +165,7 @@ struct event_desc {
100  #define EVENT_LUA_ERR   19
101  #define EVENT_TFTP_ERR  20
102  #define EVENT_INIT      21
103 +#define EVENT_NEWADDR   22
104  
105  /* Exit codes. */
106  #define EC_GOOD        0
107 @@ -1289,6 +1290,7 @@ unsigned char *extended_hwaddr(int hwtyp
108  int make_icmp_sock(void);
109  int icmp_ping(struct in_addr addr);
110  #endif
111 +void send_newaddr(void);
112  void send_alarm(time_t event, time_t now);
113  void send_event(int fd, int event, int data, char *msg);
114  void clear_cache_and_reload(time_t now);
115 @@ -1297,7 +1299,7 @@ void poll_resolv(int force, int do_reloa
116  /* netlink.c */
117  #ifdef HAVE_LINUX_NETWORK
118  void netlink_init(void);
119 -void netlink_multicast(time_t now);
120 +void netlink_multicast(void);
121  #endif
122  
123  /* bpf.c */
124 @@ -1306,7 +1308,7 @@ void init_bpf(void);
125  void send_via_bpf(struct dhcp_packet *mess, size_t len,
126                   struct in_addr iface_addr, struct ifreq *ifr);
127  void route_init(void);
128 -void route_sock(time_t now);
129 +void route_sock(void);
130  #endif
131  
132  /* bpf.c or netlink.c */
133 --- a/src/netlink.c
134 +++ b/src/netlink.c
135 @@ -38,7 +38,7 @@
136  static struct iovec iov;
137  static u32 netlink_pid;
138  
139 -static int nl_async(struct nlmsghdr *h);
140 +static void nl_async(struct nlmsghdr *h);
141  
142  void netlink_init(void)
143  {
144 @@ -142,7 +142,7 @@ int iface_enumerate(int family, void *pa
145    struct nlmsghdr *h;
146    ssize_t len;
147    static unsigned int seq = 0;
148 -  int callback_ok = 1, newaddr = 0;
149 +  int callback_ok = 1;
150  
151    struct {
152      struct nlmsghdr nlh;
153 @@ -191,21 +191,10 @@ int iface_enumerate(int family, void *pa
154         if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
155           {
156             /* May be multicast arriving async */
157 -           if (nl_async(h))
158 -             {
159 -               newaddr = 1; 
160 -               enumerate_interfaces(1); /* reset */
161 -             }
162 +           nl_async(h);
163           }
164         else if (h->nlmsg_type == NLMSG_DONE)
165 -         {
166 -           /* handle async new interface address arrivals, these have to be done
167 -              after we complete as we're not re-entrant */
168 -           if (newaddr) 
169 -             newaddress(dnsmasq_time());
170 -               
171 -           return callback_ok;
172 -         }
173 +         return callback_ok;
174         else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
175           {
176             struct ifaddrmsg *ifa = NLMSG_DATA(h);  
177 @@ -330,11 +319,11 @@ int iface_enumerate(int family, void *pa
178      }
179  }
180  
181 -void netlink_multicast(time_t now)
182 +void netlink_multicast(void)
183  {
184    ssize_t len;
185    struct nlmsghdr *h;
186 -  int flags, newaddr = 0;
187 +  int flags;
188    
189    /* don't risk blocking reading netlink messages here. */
190    if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
191 @@ -343,24 +332,19 @@ void netlink_multicast(time_t now)
192    
193    if ((len = netlink_recv()) != -1)
194      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
195 -      if (nl_async(h))
196 -       newaddr = 1;
197 +      nl_async(h);
198    
199    /* restore non-blocking status */
200    fcntl(daemon->netlinkfd, F_SETFL, flags);
201 -  
202 -  if (newaddr) 
203 -    newaddress(now);
204  }
205  
206 -static int nl_async(struct nlmsghdr *h)
207 +static void nl_async(struct nlmsghdr *h)
208  {
209    if (h->nlmsg_type == NLMSG_ERROR)
210      {
211        struct nlmsgerr *err = NLMSG_DATA(h);
212        if (err->error != 0)
213         my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
214 -      return 0;
215      }
216    else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
217      {
218 @@ -385,18 +369,15 @@ static int nl_async(struct nlmsghdr *h)
219               else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
220                 fd = daemon->rfd_save->fd;
221               else
222 -               return 0;
223 +               return;
224               
225               while(sendto(fd, daemon->packet, daemon->packet_len, 0,
226                            &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
227             }
228         }
229 -      return 0;
230      }
231    else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) 
232 -    return 1; /* clever bind mode - rescan */
233 -  
234 -  return 0;
235 +    send_newaddr();
236  }
237  #endif
238  
239 --- a/src/network.c
240 +++ b/src/network.c
241 @@ -551,7 +551,7 @@ static int iface_allowed_v4(struct in_ad
242  int enumerate_interfaces(int reset)
243  {
244    static struct addrlist *spare = NULL;
245 -  static int done = 0, active = 0;
246 +  static int done = 0;
247    struct iface_param param;
248    int errsave, ret = 1;
249    struct addrlist *addr, *tmp;
250 @@ -570,14 +570,11 @@ int enumerate_interfaces(int reset)
251        return 1;
252      }
253  
254 -  if (done || active)
255 +  if (done)
256      return 1;
257  
258    done = 1;
259  
260 -  /* protect against recusive calls from iface_enumerate(); */
261 -  active = 1;
262 -
263    if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
264      return 0;
265   
266 @@ -677,10 +674,8 @@ int enumerate_interfaces(int reset)
267      }
268    
269    errno = errsave;
270 -  
271    spare = param.spare;
272 -  active = 0;
273 -  
274 +    
275    return ret;
276  }
277