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