Browse code

core: rewrite add_interfaces() with getifaddrs() based implementation

To make IPV6 work with Solaris, add_interfaces() had to be modified.
I decided to reimplement it with a getifaddrs() based implementation,
which is available from Solaris 11 upwards, and also on FreeBSD and
Linux. By default, the latter continues to use the netlink based
variant.

Christian Kuehnke authored on 16/12/2015 17:47:36
Showing 2 changed files
... ...
@@ -1785,7 +1785,7 @@ ifeq ($(CC_NAME), suncc)
1785 1785
 endif
1786 1786
 	OLD_SOLARIS= $(shell echo "$(OSREL)" | \
1787 1787
 				sed -e 's/^5\.[0-6][^0-9]*$$/yes/' )
1788
-	LIBS+= -L$(LOCALBASE)/lib -lxnet -lnsl 
1788
+	LIBS+= -L$(LOCALBASE)/lib -lxnet -lsocket -lnsl 
1789 1789
 ifeq	($(OLD_SOLARIS), yes)
1790 1790
 		LIBS+=-lposix4
1791 1791
 else
... ...
@@ -41,6 +41,7 @@
41 41
 
42 42
 #include <sys/ioctl.h>
43 43
 #include <net/if.h>
44
+#include <ifaddrs.h>
44 45
 #ifdef HAVE_SYS_SOCKIO_H
45 46
 #include <sys/sockio.h>
46 47
 #endif
... ...
@@ -1129,7 +1130,8 @@ error:
1129 1129
 	return -1;
1130 1130
 
1131 1131
 }
1132
-/* add all family type addresses of interface if_to the socket_info array
1132
+/* add all family type addresses of interface if_name to the socket_info array
1133
+ * if family ==0, uses all families
1133 1134
  * if if_name==0, adds all addresses on all interfaces
1134 1135
  * uses RTNETLINK sockets to get addresses on the present interface on LINUX
1135 1136
  * return: -1 on error, 0 on success
... ...
@@ -1160,7 +1162,7 @@ int add_interfaces_via_netlink(char* if_name, int family, unsigned short port,
1160 1160
 				LM_DBG("in add_iface_via_netlink Name %s Address %s\n",
1161 1161
 							ifaces[i].name, tmp->addr);
1162 1162
 					/* match family */
1163
-					if (family == tmp->family){
1163
+					if (family && family == tmp->family){
1164 1164
 					/* check if loopback */
1165 1165
 					if (ifaces[i].flags & IFF_LOOPBACK){
1166 1166
 						LM_DBG("INTERFACE %s is loopback", ifaces[i].name);
... ...
@@ -1182,129 +1184,49 @@ error:
1182 1182
 #endif /* __OS_linux */
1183 1183
 
1184 1184
 /* add all family type addresses of interface if_name to the socket_info array
1185
+ * if family ==0, uses all families
1185 1186
  * if if_name==0, adds all addresses on all interfaces
1186
- * WARNING: it only works with ipv6 addresses on FreeBSD
1187 1187
  * return: -1 on error, 0 on success
1188 1188
  */
1189 1189
 int add_interfaces(char* if_name, int family, unsigned short port,
1190 1190
 					unsigned short proto,
1191 1191
 					struct addr_info** ai_l)
1192 1192
 {
1193
-	struct ifconf ifc;
1194
-	struct ifreq ifr;
1195
-	struct ifreq ifrcopy;
1196
-	char*  last;
1197
-	char* p;
1198
-	int size;
1199
-	int lastlen;
1200
-	int s;
1201 1193
 	char* tmp;
1202 1194
 	struct ip_addr addr;
1203
-	int ret;
1195
+	int ret = -1;
1204 1196
 	enum si_flags flags;
1197
+	struct ifaddrs *ifap, *ifa;
1205 1198
 
1206
-#ifdef HAVE_SOCKADDR_SA_LEN
1207
-	#ifndef MAX
1208
-		#define MAX(a,b) ( ((a)>(b))?(a):(b))
1209
-	#endif
1210
-#endif
1211
-	/* ipv4 or ipv6 only*/
1212
-	flags=SI_NONE;
1213
-	s=socket(family, SOCK_DGRAM, 0);
1214
-	ret=-1;
1215
-	lastlen=0;
1216
-	ifc.ifc_req=0;
1217
-	for (size=100; ; size*=2){
1218
-		ifc.ifc_len=size*sizeof(struct ifreq);
1219
-		ifc.ifc_req=(struct ifreq*) pkg_malloc(size*sizeof(struct ifreq));
1220
-		if (ifc.ifc_req==0){
1221
-			LM_ERR("memory allocation failure\n");
1222
-			goto error;
1223
-		}
1224
-		if (ioctl(s, SIOCGIFCONF, &ifc)==-1){
1225
-			if(errno==EBADF) return 0; /* invalid descriptor => no such ifs*/
1226
-			LM_ERR("ioctl failed: %s\n", strerror(errno));
1227
-			goto error;
1228
-		}
1229
-		if  ((lastlen) && (ifc.ifc_len==lastlen)) break; /*success,
1230
-														   len not changed*/
1231
-		lastlen=ifc.ifc_len;
1232
-		/* try a bigger array*/
1233
-		pkg_free(ifc.ifc_req);
1234
-	}
1235
-	
1236
-	last=(char*)ifc.ifc_req+ifc.ifc_len;
1237
-	for(p=(char*)ifc.ifc_req; p<last;
1238
-			p+=
1239
-			#ifdef __OS_linux
1240
-				sizeof(ifr) /* works on x86_64 too */
1241
-			#else
1242
-				(sizeof(ifr.ifr_name)+
1243
-				#ifdef  HAVE_SOCKADDR_SA_LEN
1244
-					MAX(ifr.ifr_addr.sa_len, sizeof(struct sockaddr))
1245
-				#else
1246
-					( (ifr.ifr_addr.sa_family==AF_INET)?
1247
-						sizeof(struct sockaddr_in):
1248
-						((ifr.ifr_addr.sa_family==AF_INET6)?
1249
-						sizeof(struct sockaddr_in6):sizeof(struct sockaddr)) )
1250
-				#endif
1251
-				)
1252
-			#endif
1253
-		)
1199
+	if (getifaddrs (&ifap) != 0) {
1200
+		LM_ERR("getifaddrs failed\n");
1201
+		return -1;
1202
+        }
1203
+
1204
+	for (ifa = ifap; ifa; ifa = ifa->ifa_next)
1254 1205
 	{
1255
-		/* copy contents into ifr structure
1256
-		 * warning: it might be longer (e.g. ipv6 address) */
1257
-		memcpy(&ifr, p, sizeof(ifr));
1258
-		if (ifr.ifr_addr.sa_family!=family){
1259
-			/*printf("strange family %d skipping...\n",
1260
-					ifr->ifr_addr.sa_family);*/
1206
+		if (if_name && strcmp(if_name, ifa->ifa_name))
1261 1207
 			continue;
1208
+		if (family && family != ifa->ifa_addr->sa_family)
1209
+			continue;
1210
+		sockaddr2ip_addr(&addr, (struct sockaddr*)ifa->ifa_addr);
1211
+		tmp=ip_addr2a(&addr);
1212
+		if (ifa->ifa_flags & IFF_LOOPBACK) 
1213
+			flags = SI_IS_LO;
1214
+		else
1215
+			flags = SI_NONE;
1216
+		if (new_addr_info2list(tmp, flags, ai_l)!=0)
1217
+		{
1218
+			LM_ERR("new_addr_info2list failed\n");
1219
+			ret = -1;
1220
+			break;
1262 1221
 		}
1263
-		
1264
-		/*get flags*/
1265
-		ifrcopy=ifr;
1266
-		if (ioctl(s, SIOCGIFFLAGS,  &ifrcopy)!=-1){ /* ignore errors */
1267
-			/* ignore down ifs only if listening on all of them*/
1268
-			if (if_name==0){ 
1269
-				/* if if not up, skip it*/
1270
-				if (!(ifrcopy.ifr_flags & IFF_UP)) continue;
1271
-			}
1272
-		}
1273
-		
1274
-		
1275
-		
1276
-		if ((if_name==0)||
1277
-			(strncmp(if_name, ifr.ifr_name, sizeof(ifr.ifr_name))==0)){
1278
-			
1279
-			/*add address*/
1280
-			sockaddr2ip_addr(&addr, 
1281
-					(struct sockaddr*)(p+(long)&((struct ifreq*)0)->ifr_addr));
1282
-			if ((tmp=ip_addr2a(&addr))==0) goto error;
1283
-			/* check if loopback */
1284
-			if (ifrcopy.ifr_flags & IFF_LOOPBACK) 
1285
-				flags|=SI_IS_LO;
1286
-			/* save the info */
1287
-			if (new_addr_info2list(tmp, flags, ai_l)!=0){
1288
-				LM_ERR("new_addr_info2list failed\n");
1289
-				goto error;
1290
-			}
1291
-			ret=0;
1292
-		}
1293
-			/*
1294
-			printf("%s:\n", ifr->ifr_name);
1295
-			printf("        ");
1296
-			print_sockaddr(&(ifr->ifr_addr));
1297
-			printf("        ");
1298
-			ls_ifflags(ifr->ifr_name, family, options);
1299
-			printf("\n");*/
1222
+		LM_DBG("If: %8s Fam: %8x Flg: %16lx Adr: %s\n", ifa->ifa_name, ifa->ifa_addr->sa_family, ifa->ifa_flags, tmp);
1223
+
1224
+		ret = 0;
1300 1225
 	}
1301
-	pkg_free(ifc.ifc_req); /*clean up*/
1302
-	close(s);
1226
+	freeifaddrs(ifap);
1303 1227
 	return  ret;
1304
-error:
1305
-	if (ifc.ifc_req) pkg_free(ifc.ifc_req);
1306
-	close(s);
1307
-	return -1;
1308 1228
 }
1309 1229
 
1310 1230
 
... ...
@@ -1474,7 +1396,7 @@ static int fix_socket_list(struct socket_info **list, int* type_flags)
1474 1474
 	for (si=*list;si;){
1475 1475
 		next=si->next;
1476 1476
 		ai_lst=0;
1477
-		if (add_interfaces(si->name.s, AF_INET, si->port_no,
1477
+		if (add_interfaces(si->name.s, 0, si->port_no,
1478 1478
 							si->proto, &ai_lst)!=-1){
1479 1479
 			if (si->flags & SI_IS_MHOMED){
1480 1480
 				if((new_si=new_sock2list_after(ai_lst->name.s, 0, si->port_no,