1 /* $Id: ifacewatcher.c,v 1.7 2012/05/27 22:16:10 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2012 Thomas Bernard
8 * This file implements dynamic serving of new network interfaces
9 * which weren't available during daemon start. It also takes care
10 * of interfaces which become unavailable.
12 * Copyright (c) 2011, Alexey Osipov <simba@lerlan.ru>
13 * All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions are met:
18 * * Redistributions of source code must retain the above copyright notice,
19 * this list of conditions and the following disclaimer.
20 * * Redistributions in binary form must reproduce the above copyright notice,
21 * this list of conditions and the following disclaimer in the documentation
22 * and/or other materials provided with the distribution.
23 * * The name of the author may not be used to endorse or promote products
24 * derived from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE. */
39 #include "../config.h"
41 #ifdef USE_IFACEWATCHER
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <linux/netlink.h>
47 #include <linux/rtnetlink.h>
48 #include <arpa/inet.h>
56 #include "../ifacewatcher.h"
57 #include "../minissdp.h"
58 #include "../getifaddr.h"
59 #include "../upnpglobalvars.h"
60 #include "../natpmp.h"
62 extern volatile sig_atomic_t should_send_public_address_change_notif
;
66 OpenAndConfInterfaceWatchSocket(void)
69 struct sockaddr_nl addr
;
71 s
= socket(PF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
74 syslog(LOG_ERR
, "socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE): %m");
78 memset(&addr
, 0, sizeof(addr
));
79 addr
.nl_family
= AF_NETLINK
;
80 addr
.nl_groups
= RTMGRP_LINK
| RTMGRP_IPV4_IFADDR
;
81 /*addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;*/
83 if (bind(s
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
85 syslog(LOG_ERR
, "bind(netlink): %m");
94 /* disabled at the moment */
96 ProcessInterfaceUp(struct ifinfomsg
*ifi
)
98 struct lan_iface_s
* lan_iface
;
99 struct lan_iface_s
* lan_iface2
;
100 struct lan_addr_s
* lan_addr
;
101 char ifname
[IFNAMSIZ
];
103 struct in_addr ifaddr
;
105 /* check if we already have this iface */
106 for(lan_iface
= lan_ifaces
.lh_first
; lan_iface
!= NULL
; lan_iface
= lan_iface
->list
.le_next
)
107 if (lan_iface
->iface
.index
== ifi
->ifi_index
)
110 if (lan_iface
!= NULL
)
113 if (if_indextoname(ifi
->ifi_index
, ifname
) == NULL
)
115 syslog(LOG_ERR
, "if_indextoname(%d, ifname) failed", ifi
->ifi_index
);
119 if (getifaddr(ifname
, ifstraddr
, 16) < 0)
121 syslog(LOG_DEBUG
, "getifaddr(%s, ifaddr, 16) failed", ifname
);
125 if (inet_pton(AF_INET
, ifstraddr
, &ifaddr
) != 1)
127 syslog(LOG_ERR
, "inet_pton(AF_INET, \"%s\", &ifaddr) failed", ifstraddr
);
131 /* check if this new interface has address which we need to listen to */
132 for(lan_addr
= lan_addrs
.lh_first
; lan_addr
!= NULL
; lan_addr
= lan_addr
->list
.le_next
)
134 if (lan_addr
->addr
.s_addr
!= ifaddr
.s_addr
)
137 syslog(LOG_INFO
, "Interface up: %s (%s)", ifname
, ifstraddr
);
139 /* adding new lan_iface entry */
140 lan_iface
= (struct lan_iface_s
*) malloc(sizeof(struct lan_iface_s
));
141 if (lan_iface
== NULL
)
143 syslog(LOG_ERR
, "malloc(sizeof(struct lan_iface_s): %m");
147 lan_iface
->lan_addr
= lan_addr
;
148 strncpy(lan_iface
->iface
.name
, ifname
, IFNAMSIZ
);
149 lan_iface
->iface
.index
= ifi
->ifi_index
;
150 lan_iface
->iface
.addr
= ifaddr
;
151 lan_iface
->snotify
= -1;
153 lan_iface
->snatpmp
= -1;
156 LIST_INSERT_HEAD(&lan_ifaces
, lan_iface
, list
);
158 /* adding multicast membership for SSDP */
159 if(AddMulticastMembership(sudp
, ifaddr
.s_addr
, ifi
->ifi_index
) < 0)
160 syslog(LOG_WARNING
, "Failed to add multicast membership for interface %s (%s)", ifname
, ifstraddr
);
162 syslog(LOG_INFO
, "Multicast membership added for %s (%s)", ifname
, ifstraddr
);
164 /* create SSDP notify socket */
165 if (OpenAndConfSSDPNotifySocket(lan_iface
) < 0)
166 syslog(LOG_WARNING
, "Failed to open SSDP notify socket for interface %s (%s)", ifname
, ifstraddr
);
169 /* create NAT-PMP socket */
170 for(lan_iface2
= lan_ifaces
.lh_first
; lan_iface2
!= NULL
; lan_iface2
= lan_iface2
->list
.le_next
)
171 if (lan_iface2
->lan_addr
->addr
.s_addr
== lan_iface
->lan_addr
->addr
.s_addr
&&
172 lan_iface2
->snatpmp
>= 0)
173 lan_iface
->snatpmp
= lan_iface2
->snatpmp
;
175 if (lan_iface
->snatpmp
< 0)
177 lan_iface
->snatpmp
= OpenAndConfNATPMPSocket(ifaddr
.s_addr
);
178 if (lan_iface
->snatpmp
< 0)
179 syslog(LOG_ERR
, "OpenAndConfNATPMPSocket(ifaddr.s_addr) failed for %s (%s)", ifname
, ifstraddr
);
181 syslog(LOG_INFO
, "Listening for NAT-PMP connection on %s:%d", ifstraddr
, NATPMP_PORT
);
190 ProcessInterfaceDown(struct ifinfomsg
*ifi
)
192 struct lan_iface_s
* lan_iface
;
193 struct lan_iface_s
* lan_iface2
;
195 /* check if we have this iface */
196 for(lan_iface
= lan_ifaces
.lh_first
; lan_iface
!= NULL
; lan_iface
= lan_iface
->list
.le_next
)
197 if (lan_iface
->iface
.index
== ifi
->ifi_index
)
200 if (lan_iface
== NULL
)
203 /* one of our interfaces is going down, clean up */
204 syslog(LOG_INFO
, "Interface down: %s", lan_iface
->iface
.name
);
206 /* remove multicast membership for SSDP */
207 if(DropMulticastMembership(sudp
, lan_iface
->lan_addr
->addr
.s_addr
, lan_iface
->iface
.index
) < 0)
208 syslog(LOG_WARNING
, "Failed to drop multicast membership for interface %s (%s)", lan_iface
->iface
.name
, lan_iface
->lan_addr
->str
);
210 syslog(LOG_INFO
, "Multicast membership dropped for %s (%s)", lan_iface
->iface
.name
, lan_iface
->lan_addr
->str
);
212 /* closing SSDP notify socket */
213 close(lan_iface
->snotify
);
216 /* closing NAT-PMP socket if it's not used anymore */
217 for(lan_iface2
= lan_ifaces
.lh_first
; lan_iface2
!= NULL
; lan_iface2
= lan_iface2
->list
.le_next
)
218 if (lan_iface2
!= lan_iface
&& lan_iface2
->snatpmp
== lan_iface
->snatpmp
)
221 if (lan_iface2
== NULL
)
222 close(lan_iface
->snatpmp
);
225 /* del corresponding lan_iface entry */
226 LIST_REMOVE(lan_iface
, list
);
234 ProcessInterfaceWatchNotify(int s
)
239 struct nlmsghdr
*nlhdr
;
240 struct ifinfomsg
*ifi
;
241 struct ifaddrmsg
*ifa
;
247 unsigned int ext_if_name_index
= 0;
249 iov
.iov_base
= buffer
;
250 iov
.iov_len
= sizeof(buffer
);
252 memset(&hdr
, 0, sizeof(hdr
));
256 len
= recvmsg(s
, &hdr
, 0);
259 syslog(LOG_ERR
, "recvmsg(s, &hdr, 0): %m");
264 ext_if_name_index
= if_nametoindex(ext_if_name
);
267 for (nlhdr
= (struct nlmsghdr
*) buffer
;
268 NLMSG_OK (nlhdr
, (unsigned int)len
);
269 nlhdr
= NLMSG_NEXT (nlhdr
, len
))
273 char ifname
[IFNAMSIZ
];
276 if (nlhdr
->nlmsg_type
== NLMSG_DONE
)
278 switch(nlhdr
->nlmsg_type
) {
282 ifi
= (struct ifinfomsg
*) NLMSG_DATA(nlhdr
);
285 if(ProcessInterfaceDown(ifi
) < 0)
286 syslog(LOG_ERR
, "ProcessInterfaceDown(ifi) failed");
288 if(ProcessInterfaceUp(ifi
) < 0)
289 syslog(LOG_ERR
, "ProcessInterfaceUp(ifi) failed");
296 /* see /usr/include/linux/netlink.h
297 * and /usr/include/linux/rtnetlink.h */
298 ifa
= (struct ifaddrmsg
*) NLMSG_DATA(nlhdr
);
299 syslog(LOG_DEBUG
, "%s %s index=%d fam=%d", "ProcessInterfaceWatchNotify",
300 is_del
? "RTM_DELADDR" : "RTM_NEWADDR",
301 ifa
->ifa_index
, ifa
->ifa_family
);
302 for(rth
= IFA_RTA(ifa
), rtl
= IFA_PAYLOAD(nlhdr
);
303 rtl
&& RTA_OK(rth
, rtl
);
304 rth
= RTA_NEXT(rth
, rtl
)) {
306 memset(tmp
, 0, sizeof(tmp
));
307 switch(rth
->rta_type
) {
312 inet_ntop(ifa
->ifa_family
, RTA_DATA(rth
), tmp
, sizeof(tmp
));
313 if(rth
->rta_type
== IFA_ADDRESS
)
314 strncpy(address
, tmp
, sizeof(address
));
317 strncpy(tmp
, RTA_DATA(rth
), sizeof(tmp
));
318 strncpy(ifname
, tmp
, sizeof(ifname
));
322 struct ifa_cacheinfo
*cache_info
;
323 cache_info
= RTA_DATA(rth
);
324 snprintf(tmp
, sizeof(tmp
), "valid=%u prefered=%u",
325 cache_info
->ifa_valid
, cache_info
->ifa_prefered
);
329 strncpy(tmp
, "*unknown*", sizeof(tmp
));
331 syslog(LOG_DEBUG
, " - %u - %s type=%d",
335 if(ifa
->ifa_index
== ext_if_name_index
) {
336 should_send_public_address_change_notif
= 1;
340 syslog(LOG_DEBUG
, "%s type %d ignored",
341 "ProcessInterfaceWatchNotify", nlhdr
->nlmsg_type
);