[Asterisk-Dev] [patch] ast_ouraddrfor replacement using netlink

Karl Brose khb at brose.com
Tue Nov 30 22:08:06 MST 2004


This works great indeed for the couple cases I have tested for.
Works on interface aliases also.
Still doesn't solve all my wishes yet, but it's not meant for more
than what it does.  Thanks.
Are you going to submit this?
If the code was released under GPL previously you shouldn't need
the release from the original author again, I would think.



Karl Brose wrote:

>
> Ah, yes.  you are very correct. ourraddrfor only works for rather 
> trivial things
> I also replaced this, but still looking for better solutions.  Will 
> give yours a tryout
> and use it if it works better.
> Thanks.
>
>
> Angus Lees wrote:
>
>> On our boxes we use a lot of policy routing, etc so the naive
>> /proc/net/route implementation of ast_ouraddrfor completely fails for
>> us.  Here's the "proper" implementation for Linux, using netlink --
>> similar to "ip route get".  The code is based on findsaddr-linux.c
>> from traceroute and used with permission from the original author
>> (Herbert Xu).
>>  
>>
>
> _______________________________________________
> Asterisk-Dev mailing list
> Asterisk-Dev at lists.digium.com
> http://lists.digium.com/mailman/listinfo/asterisk-dev
> To UNSUBSCRIBE or update options visit:
>   http://lists.digium.com/mailman/listinfo/asterisk-dev
>

-------------- next part --------------
--- acl.c.orig	2004-11-30 00:17:50.000000000 -0500
+++ acl.c	2004-11-30 18:29:20.000000000 -0500
@@ -33,8 +33,11 @@
 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
 #include <fcntl.h>
 #include <net/route.h>
-
 AST_MUTEX_DEFINE_STATIC(routeseq_lock);
+#else	/* Linux (netlink) */
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 #endif
 
 
@@ -309,72 +312,77 @@
 
 	ast_log(LOG_DEBUG, "No route found for address %s!\n", p);
 	return -1;
-#else
-	FILE *PROC;
-	unsigned int remote_ip;
-	int res = 1;
-	char line[256];
-	remote_ip = them->s_addr;
-	
-	PROC = fopen("/proc/net/route","r");
-	if (!PROC) {
-		bzero(us,sizeof(struct in_addr));
+#else	/* Linux (netlink) */
+	int fd;
+	struct {
+		struct nlmsghdr n;
+		struct rtmsg r;
+		char data[1024];
+	} req;
+	struct rtattr *attr;
+	int len;
+	struct sockaddr_nl addr;
+
+	fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (fd < 0) {
+		ast_log(LOG_ERROR, "Cannot open netlink socket: %s\n", strerror(errno));
 		return -1;
 	}
-	/* First line contains headers */
-	fgets(line,sizeof(line),PROC);
 
-	while (!feof(PROC)) {
-		char iface[256];
-		unsigned int dest, gateway, mask;
-		int i,fieldnum;
-		char *fields[40];
-
-		fgets(line,sizeof(line),PROC);
-
-		fieldnum = 0;
-		for (i=0;i<sizeof(line);i++) {
-			char *offset;
-
-			fields[fieldnum++] = line + i;
-			offset = strchr(line + i,'\t');
-			if (offset == NULL) {
-				/* Exit loop */
-				break;
-			} else if (fieldnum >= 9) {
-				/* Short-circuit: can't break at 8, since the end of field 7 is figured when fieldnum=8 */
-				break;
-			} else {
-				*offset = '\0';
-				i = offset - line;
-			}
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_SPACE(sizeof(req.r)) + RTA_LENGTH(sizeof(*them));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = RTM_GETROUTE;
+	req.r.rtm_family = AF_INET;
+	req.r.rtm_dst_len = 32;
+	attr = (void*)((char*)&req + NLMSG_SPACE(sizeof(req.r)));
+	attr->rta_type = RTA_DST;
+	attr->rta_len = RTA_LENGTH(sizeof(*them));
+	memcpy(RTA_DATA(attr), them, sizeof(*them));
+
+	if (TEMP_FAILURE_RETRY(write(fd, &req, req.n.nlmsg_len)) < 0) {
+		ast_log(LOG_ERROR, "Error sending netlink message: %s\n", strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	do {
+		socklen_t alen = sizeof(addr);
+		len = TEMP_FAILURE_RETRY(recvfrom(fd, &req, sizeof(req), 0, (void*)&addr, &alen));
+		if (len < 0) {
+			ast_log(LOG_ERROR, "Error receiving netlink message: %s\n", strerror(errno));
+			close(fd);
+			return -1;
 		}
-		if (fieldnum >= 8) {
+	} while (addr.nl_pid);
+
+	close(fd);
 
-			sscanf(fields[0],"%s",iface);
-			sscanf(fields[1],"%x",&dest);
-			sscanf(fields[2],"%x",&gateway);
-			sscanf(fields[7],"%x",&mask);
+	if (req.n.nlmsg_type == NLMSG_ERROR) {
+		struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(&req.n);
+		ast_log(LOG_ERROR, "Netlink error: %s\n", strerror(-err->error));
+		return -1;
+	}
+
+	len -= NLMSG_SPACE(sizeof(req.r));
+	while (len > 0) {
+		if (attr->rta_type == RTA_PREFSRC && RTA_PAYLOAD(attr) == sizeof(*us)) {
+			memcpy(us, RTA_DATA(attr), RTA_PAYLOAD(attr));
 #if 0
-			{ char iabuf[INET_ADDRSTRLEN]; 
-			printf("Addr: %s %08x Dest: %08x Mask: %08x\n", ast_inet_ntoa(iabuf, sizeof(iabuf), *them), remote_ip, dest, mask); }
-#endif		
-			/* Looks simple, but here is the magic */
-			if (((remote_ip & mask) ^ dest) == 0) {
-				res = ast_lookup_iface(iface,us);
-				break;
+			{
+			char buf1[INET_ADDRSTRLEN], buf2[INET_ADDRSTRLEN];
+			ast_log(LOG_DEBUG, "Found source address %s for %s\n",
+					ast_inet_ntoa(buf1, sizeof(buf1), *us),
+					ast_inet_ntoa(buf2, sizeof(buf2), *them));
 			}
+#endif
+			return 0;
 		}
+		attr = RTA_NEXT(attr, len);
 	}
-	fclose(PROC);
-	if (res == 1) {
-		ast_log(LOG_WARNING, "Yikes!  No default route?!!\n");
-		bzero(us,sizeof(struct in_addr));
-		return -2;
-	} else if (res) {
-		/* We've already warned in subroutine */
-		return -1;
- 	}
-	return 0;
+
+	ast_log(LOG_WARNING, "Netlink did not return a source address\n");
+	return -1;
 #endif
 }


More information about the asterisk-dev mailing list