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

Angus Lees angus.lees at urnet.com.au
Mon Nov 29 19:50:19 MST 2004


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).

Should work on Linux 2.2 - 2.6+ so there is no problem with
back-compatibility.

As far as I can see there is (after this patch) no longer any user of
ast_lookup_iface(), so this function can also be removed.

 - Gus


Index: acl.c
===================================================================
RCS file: /var/cvs/snwb/packages/asterisk/acl.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 acl.c
--- acl.c	30 Jun 2004 16:56:51 -0000	1.1.1.1
+++ acl.c	30 Nov 2004 02:25:50 -0000
@@ -35,6 +35,12 @@
 #include <net/route.h>
 
 AST_MUTEX_DEFINE_STATIC(routeseq_lock);
+#else /* Linux (netlink) */
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
+typedef int32_t __s32;
+#include <linux/rtnetlink.h>
 #endif
 
 
@@ -310,72 +316,83 @@
 
 	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));
-		return -1;
+#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;
+	}
+
+	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));
+	err:
+	  close(fd);
+	  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;
-			}
-		}
-		if (fieldnum >= 8) {
-
-			sscanf(fields[0],"%s",iface);
-			sscanf(fields[1],"%x",&dest);
-			sscanf(fields[2],"%x",&gateway);
-			sscanf(fields[7],"%x",&mask);
+	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));
+	    goto err;
+	  }
+	} while (addr.nl_pid);
+
+	close(fd);
+
+	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