Browse code

core:socket_info Added automatic discovery of IPv6 interfaces on Linux using RT_NETLINK socks

The behavior now is identical for IPv4 or IPv6 interfaces : if no listen directives are given,
either via command line or config file, Kamailio will automatically detect IPv4/IPv6 interfaces and
bind to them (well, it will not bind to link-local prefixes)

Marius Zbihlei authored on 28/09/2011 10:39:39
Showing 1 changed files
... ...
@@ -1,3 +1,4 @@
1
+
1 2
 /* $Id$
2 3
  *
3 4
  * find & manage listen addresses 
... ...
@@ -713,7 +714,369 @@ error:
713 713
 	return -1;
714 714
 }
715 715
 
716
+#ifdef __OS_linux
717
+
718
+#include "linux/netlink.h"
719
+#include "linux/rtnetlink.h"
720
+#include "arpa/inet.h"
721
+
722
+
723
+#define MAX_IF_LEN 64
724
+struct idx
725
+{
726
+	struct idx * 	next;
727
+	int 		family;
728
+	unsigned	ifa_flags;
729
+	char		addr[MAX_IF_LEN];
730
+
731
+};
732
+
733
+struct idxlist{
734
+	struct idx* 	addresses;
735
+	int 		index;
736
+	char 		name[MAX_IF_LEN];
737
+	unsigned 	flags;
738
+};
739
+
740
+#define MAX_IFACE_NO 32
741
+
742
+static struct idxlist *ifaces = NULL;
743
+static int seq = 0;
744
+
745
+#define SADDR(s) ((struct sockaddr_in*)s)->sin_addr.s_addr
746
+
747
+#define NLMSG_TAIL(nmsg) \
748
+	((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
749
+
750
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
751
+	      int alen)
752
+{
753
+	int len = RTA_LENGTH(alen);
754
+	struct rtattr *rta;
755
+
756
+	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
757
+		fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
758
+		return -1;
759
+	}
760
+	rta = NLMSG_TAIL(n);
761
+	rta->rta_type = type;
762
+	rta->rta_len = len;
763
+	memcpy(RTA_DATA(rta), data, alen);
764
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
765
+	return 0;
766
+}
767
+
768
+
769
+
770
+static int nl_bound_sock(void)
771
+{
772
+	int sock;
773
+	struct sockaddr_nl la;
774
+
775
+	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
776
+	if(sock <= 0){
777
+		LM_ERR("could not create NETLINK sock to get interface list");
778
+		goto error;
779
+	}
780
+
781
+	/* bind NETLINK socket to pid */
782
+	bzero(&la, sizeof(la));
783
+	la.nl_family = AF_NETLINK;
784
+	la.nl_pad = 0;
785
+	la.nl_pid = getpid();
786
+	la.nl_groups = 0;
787
+	if ( bind(sock, (struct sockaddr*) &la, sizeof(la)) < 0){
788
+		LM_ERR("could not bind NETLINK sock to sockaddr_nl\n");
789
+		goto error;
790
+	}
791
+
792
+	return sock;
793
+error:
794
+	if(sock > 0) close(sock);
795
+	return -1;
796
+}
797
+
798
+#define fill_nl_req(req, type, family) do {\
799
+	memset(&req, 0, sizeof(req));\
800
+	req.nlh.nlmsg_len = sizeof(req);\
801
+	req.nlh.nlmsg_type = type;\
802
+	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_DUMP;\
803
+	req.nlh.nlmsg_pid = getpid();\
804
+	req.nlh.nlmsg_seq = seq++;\
805
+	req.g.rtgen_family = family;\
806
+	} while(0);
807
+
808
+	
809
+static int get_flags(int family){
810
+	struct {
811
+		struct nlmsghdr nlh;
812
+		struct rtgenmsg g;
813
+	} req;
814
+	int rtn = 0;
815
+	struct nlmsghdr*  nlp;
816
+	struct ifinfomsg *ifi;
817
+	char buf[8192];
818
+	char *p = buf;
819
+	int nll = 0;
820
+        int nl_sock = 0;
821
+
822
+	fill_nl_req(req, RTM_GETLINK, AF_INET);
823
+
824
+	if((nl_sock = nl_bound_sock()) < 0) return -1;
825
+
826
+	if(send(nl_sock, (void*)&req, sizeof(req), 0) < 0)
827
+	{
828
+		LM_ERR("error sending NETLINK request\n");
829
+		goto error;
830
+	}
831
+
832
+	while(1) {
833
+		rtn = recv(nl_sock, p, sizeof(buf) - nll, 0);
834
+		nlp = (struct nlmsghdr *) p;
835
+		if(nlp->nlmsg_type == NLMSG_DONE){
836
+			LM_DBG("done\n");
837
+			 break;
838
+		}
839
+		if(nlp->nlmsg_type == NLMSG_ERROR){
840
+			 LM_DBG("Error on message to netlink");
841
+			 break;
842
+		}
843
+		p += rtn;
844
+
845
+		nll += rtn;
846
+	}
847
+
848
+	nlp = (struct nlmsghdr *) buf;
849
+	for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll)){
850
+		ifi = NLMSG_DATA(nlp);
851
+
852
+		if (nlp->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
853
+			goto error;
854
+
855
+		LM_ERR("Interface with index %d has flags %d\n", ifi->ifi_index, ifi->ifi_flags);
856
+		if(ifaces == NULL){
857
+			LM_ERR("get_flags must not be called on empty interface list");
858
+			goto error;
859
+		}
860
+		if(ifi->ifi_index >= MAX_IFACE_NO){
861
+			LM_ERR("invalid network interface index returned %d", ifi->ifi_index);
862
+			goto error;
863
+		}
864
+		ifaces[ifi->ifi_index].flags = ifi->ifi_flags;
865
+	}
866
+
867
+	if(nl_sock>0) close(nl_sock);
868
+	return 0;
869
+
870
+error:
871
+	if(nl_sock>0) close(nl_sock);
872
+	return -1;
873
+}
874
+
875
+static int build_iface_list(void)
876
+{
877
+	struct {
878
+		struct nlmsghdr nlh;
879
+		struct rtgenmsg g;
880
+	} req;
881
+
882
+	int seq = 0;
883
+	int rtn = 0;
884
+	struct nlmsghdr*  nlp;
885
+	struct ifaddrmsg *ifi;
886
+	int rtl;
887
+	char buf[8192];
888
+	char *p = buf;
889
+	int nll = 0;
890
+	struct rtattr * rtap;
891
+	int index, i;
892
+	struct idx* entry;
893
+	struct idx* tmp;
894
+        int nl_sock = 0;
895
+        int families[] = {AF_INET, AF_INET6};
896
+        char name[MAX_IF_LEN];
897
+	int is_link_local = 0;
898
+
899
+	if(ifaces == NULL){
900
+		if((ifaces = (struct idxlist*)pkg_malloc(MAX_IFACE_NO*sizeof(struct idxlist))) == NULL){
901
+			LM_ERR("No more pkg memory\n");
902
+			return -1;
903
+		}
904
+		memset(ifaces, 0, sizeof(struct idxlist)*MAX_IFACE_NO);
905
+	}
906
+
907
+	/* bind netlink socket */
908
+	if((nl_sock = nl_bound_sock()) < 0) return -1;
716 909
 
910
+	for (i = 0 ; i < sizeof(families)/sizeof(int); i++) {
911
+		fill_nl_req(req, RTM_GETADDR, families[i]);
912
+
913
+		if(send(nl_sock, (void*)&req, sizeof(req), 0) < 0){
914
+			LM_ERR("error sending NETLINK request\n");
915
+			goto error;
916
+		};
917
+
918
+		memset(buf, 0, sizeof(buf));
919
+		nll = 0;
920
+		p = buf;
921
+		while(1) {
922
+			rtn = recv(nl_sock, p, sizeof(buf) - nll, 0);
923
+			LM_DBG("received %d byles \n", rtn);
924
+			nlp = (struct nlmsghdr *) p;
925
+			if(nlp->nlmsg_type == NLMSG_DONE){
926
+				LM_DBG("done receiving netlink info \n");
927
+				 break;
928
+			}
929
+			if(nlp->nlmsg_type == NLMSG_ERROR){
930
+				 LM_ERR("Error on message to netlink");
931
+				 break;
932
+			}
933
+			p += rtn;
934
+
935
+			nll += rtn;
936
+		}
937
+
938
+		nlp = (struct nlmsghdr *) buf;
939
+		for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll)){
940
+			ifi = NLMSG_DATA(nlp);
941
+
942
+			if (nlp->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
943
+				continue;
944
+			// init all the strings
945
+			// inner loop: loop thru all the attributes of
946
+			// one route entry
947
+			rtap = (struct rtattr *) IFA_RTA(ifi);
948
+
949
+			rtl = IFA_PAYLOAD(nlp);
950
+
951
+			index = ifi->ifa_index;
952
+			if(index >= MAX_IFACE_NO){
953
+				LM_ERR("Invalid interface index returned: %d\n", index);
954
+				goto error;
955
+			}
956
+
957
+			entry = (struct idx*)pkg_malloc(sizeof(struct idx));
958
+			if(entry == 0)
959
+			{
960
+				LM_ERR("could not allocate memory\n");
961
+				goto error;
962
+			}
963
+
964
+			entry->next = 0;
965
+			entry->family = families[i];
966
+			entry->ifa_flags = ifi->ifa_flags;
967
+                        is_link_local = 0;
968
+
969
+			for(;RTA_OK(rtap, rtl);rtap=RTA_NEXT(rtap,rtl)){
970
+				switch(rtap->rta_type){
971
+					case IFA_ADDRESS:
972
+						if((*(int*)RTA_DATA(rtap))== htons(0xfe80)){
973
+							LM_DBG("Link Local Address, ignoring ...\n");
974
+							is_link_local = 1;
975
+							break;
976
+						}
977
+						inet_ntop(families[i], RTA_DATA(rtap), entry->addr, MAX_IF_LEN);
978
+						LM_DBG("iface <IFA_ADDRESS> addr is  %s\n", entry->addr);
979
+						break;
980
+					case IFA_LOCAL:
981
+						if((*(int*)RTA_DATA(rtap))== htons(0xfe80)){
982
+							LM_DBG("Link Local Address, ignoring ...\n");
983
+							is_link_local = 1;
984
+						}
985
+						inet_ntop(families[i], RTA_DATA(rtap), entry->addr, MAX_IF_LEN);
986
+						LM_DBG("iface <IFA_LOCAL> addr is %s\n", entry->addr);
987
+						break;
988
+					case IFA_LABEL:
989
+						LM_DBG("iface name is %s\n", (char*)RTA_DATA(rtap));
990
+						strncpy(name, (char*)RTA_DATA(rtap), MAX_IF_LEN);
991
+						break;
992
+					case IFA_BROADCAST:
993
+					case IFA_ANYCAST:
994
+					case IFA_UNSPEC:
995
+					case IFA_CACHEINFO:
996
+					default:
997
+						break;
998
+				}
999
+			}
1000
+			if(is_link_local) continue;    /* link local addresses are not bindable */
1001
+
1002
+			if(strlen(ifaces[index].name)==0)
1003
+				strncpy(ifaces[index].name, name, MAX_IF_LEN);
1004
+
1005
+			ifaces[index].index = index;
1006
+
1007
+			if(ifaces[index].addresses == 0 )
1008
+				ifaces[index].addresses = entry;
1009
+			else {
1010
+				for(tmp = ifaces[index].addresses; tmp->next ; tmp = tmp->next)/*empty*/;
1011
+				tmp->next = entry;
1012
+			}
1013
+		}
1014
+	}
1015
+	if(nl_sock>0) close(nl_sock);
1016
+	/* the socket should be closed so we can bind again */
1017
+	for(i = 0; i < sizeof(families)/sizeof(int); i++){
1018
+		/* get device flags */
1019
+		get_flags(families[i]); /* AF_INET or AF_INET6 */
1020
+	}
1021
+
1022
+	return 0;
1023
+error:
1024
+	if(nl_sock>0) close(nl_sock);
1025
+	return -1;
1026
+
1027
+}
1028
+/* add all family type addresses of interface if_to the socket_info array
1029
+ * if if_name==0, adds all addresses on all interfaces
1030
+ * uses RTNETLINK sockets to get addresses on the present interface on LINUX
1031
+ * return: -1 on error, 0 on success
1032
+ */
1033
+int add_interfaces_via_netlink(char* if_name, int family, unsigned short port,
1034
+					unsigned short proto,
1035
+					struct addr_info** ai_l)
1036
+{
1037
+	int i;
1038
+	struct idx* tmp;
1039
+	enum si_flags flags;
1040
+
1041
+	if(ifaces == NULL && (build_iface_list()!=0)){
1042
+		LM_ERR("Could not get network interface list\n");
1043
+		return -1;
1044
+	}
1045
+
1046
+	flags=SI_NONE;
1047
+	for(i=0; i< MAX_IFACE_NO; ++i){
1048
+		if(ifaces[i].addresses == NULL) continue; /* not present/configured */
1049
+		if ((if_name==0)||
1050
+			(strncmp(if_name, ifaces[i].name, strlen(ifaces[i].name))==0)){
1051
+
1052
+			/* check if iface is up */
1053
+			//if(! (ifaces[i].flags & IFF_UP) ) continue;
1054
+
1055
+			for(tmp = ifaces[i].addresses; tmp; tmp = tmp->next){
1056
+				LM_DBG("\t in add_iface_via_netlink Name %s Adress %s\n", ifaces[i].name, tmp->addr);
1057
+		                /* match family */
1058
+                                if (family == tmp->family){
1059
+					/* check if loopback */
1060
+					if (ifaces[i].flags & IFF_LOOPBACK){
1061
+						LM_DBG("INTERFACE %s is loopback", ifaces[i].name);
1062
+						flags|=SI_IS_LO;
1063
+					}
1064
+					/* save the info */
1065
+					if (new_addr_info2list(tmp->addr, flags, ai_l)!=0){
1066
+						LOG(L_ERR, "ERROR: add_interfaces: "
1067
+							"new_addr_info2list failed\n");
1068
+						goto error;
1069
+			    		}
1070
+				}
1071
+			}
1072
+		}
1073
+	}
1074
+	return 0;
1075
+error:
1076
+	return -1;
1077
+}
1078
+#endif /* __OS_linux */
717 1079
 
718 1080
 /* add all family type addresses of interface if_name to the socket_info array
719 1081
  * if if_name==0, adds all addresses on all interfaces
... ...
@@ -736,7 +1099,7 @@ int add_interfaces(char* if_name, int family, unsigned short port,
736 736
 	struct ip_addr addr;
737 737
 	int ret;
738 738
 	enum si_flags flags;
739
-	
739
+
740 740
 #ifdef HAVE_SOCKADDR_SA_LEN
741 741
 	#ifndef MAX
742 742
 		#define MAX(a,b) ( ((a)>(b))?(a):(b))
... ...
@@ -1345,25 +1708,46 @@ int fix_all_socket_lists()
1345 1345
 			&& (sctp_listen==0)
1346 1346
 #endif
1347 1347
 		){
1348
-		/* get all listening ipv4 interfaces */
1349
-		if ((add_interfaces(0, AF_INET, 0,  PROTO_UDP, &ai_lst)==0) &&
1350
-			(addr_info_to_si_lst(ai_lst, 0, PROTO_UDP, 0, &udp_listen)==0)){
1348
+		/* get all listening ipv4/ipv6 interfaces */
1349
+		if ( ( (add_interfaces(0, AF_INET, 0,  PROTO_UDP, &ai_lst)==0)
1350
+#ifdef USE_IPV6
1351
+#ifdef __OS_linux
1352
+		&& (add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_UDP, &ai_lst) == 0)
1353
+#else
1354
+		&& (add_interfaces(0, AF_INET6, 0,  PROTO_UDP, &ai_lst)==0) /* add_interface does not work for IPv6 on Linux */
1355
+#endif /* __OS_linux */
1356
+#endif /* USE_IPV6 */
1357
+			 ) && (addr_info_to_si_lst(ai_lst, 0, PROTO_UDP, 0, &udp_listen)==0)){
1351 1358
 			free_addr_info_lst(&ai_lst);
1352 1359
 			ai_lst=0;
1353 1360
 			/* if ok, try to add the others too */
1354 1361
 #ifdef USE_TCP
1355 1362
 			if (!tcp_disable){
1356
-				if ((add_interfaces(0, AF_INET, 0,  PROTO_TCP, &ai_lst)!=0) ||
1357
-					(addr_info_to_si_lst(ai_lst, 0, PROTO_TCP, 0,
1363
+				if ( ((add_interfaces(0, AF_INET, 0,  PROTO_TCP, &ai_lst)!=0)
1364
+#ifdef USE_IPV6
1365
+#ifdef __OS_linux
1366
+    				|| (add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_TCP, &ai_lst) != 0)
1367
+#else
1368
+				|| (add_interfaces(0, AF_INET6, 0,  PROTO_TCP, &ai_lst)!=0)
1369
+#endif /* __OS_linux */
1370
+#endif /* USE_IPV6 */
1371
+				)|| (addr_info_to_si_lst(ai_lst, 0, PROTO_TCP, 0,
1358 1372
 										 				&tcp_listen)!=0))
1359 1373
 					goto error;
1360 1374
 				free_addr_info_lst(&ai_lst);
1361 1375
 				ai_lst=0;
1362 1376
 #ifdef USE_TLS
1363 1377
 				if (!tls_disable){
1364
-					if ((add_interfaces(0, AF_INET, 0, PROTO_TLS,
1365
-										&ai_lst)!=0) ||
1366
-						(addr_info_to_si_lst(ai_lst, 0, PROTO_TLS, 0,
1378
+					if (((add_interfaces(0, AF_INET, 0, PROTO_TLS,
1379
+										&ai_lst)!=0)
1380
+#ifdef USE_IPV6
1381
+#ifdef __OS_linux
1382
+    				|| (add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_TLS, &ai_lst) != 0)
1383
+#else
1384
+				|| (add_interfaces(0, AF_INET6, 0,  PROTO_TLS, &ai_lst)!=0)
1385
+#endif /* __OS_linux */
1386
+#endif /* USE_IPV6 */
1387
+					) || (addr_info_to_si_lst(ai_lst, 0, PROTO_TLS, 0,
1367 1388
 										 				&tls_listen)!=0))
1368 1389
 						goto error;
1369 1390
 				}
... ...
@@ -1374,9 +1758,16 @@ int fix_all_socket_lists()
1374 1374
 #endif
1375 1375
 #ifdef USE_SCTP
1376 1376
 			if (!sctp_disable){
1377
-				if ((add_interfaces(0, AF_INET, 0,  PROTO_SCTP, &ai_lst)!=0)||
1378
-					(addr_info_to_si_lst(ai_lst, 0, PROTO_SCTP, 0,
1379
-										 				&sctp_listen)!=0))
1377
+				if (((add_interfaces(0, AF_INET, 0,  PROTO_SCTP, &ai_lst)!=0)
1378
+#ifdef USE_IPV6
1379
+#ifdef __OS_linux
1380
+    				|| (add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_SCTP, &ai_lst) != 0)
1381
+#else
1382
+				|| (add_interfaces(0, AF_INET6, 0,  PROTO_SCTP, &ai_lst)!=0)
1383
+#endif /* __OS_linux */
1384
+#endif /* USE_IPV6 */
1385
+					) || (addr_info_to_si_lst(ai_lst, 0, PROTO_SCTP, 0,
1386
+							 				&sctp_listen)!=0))
1380 1387
 					goto error;
1381 1388
 				free_addr_info_lst(&ai_lst);
1382 1389
 				ai_lst=0;