[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