linux.c: Do NAT detection using address-independent machinery.
[yaid] / linux.c
diff --git a/linux.c b/linux.c
index 91c03aa..87da9fa 100644 (file)
--- a/linux.c
+++ b/linux.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * Discover the owner of a connection
+ * Discover the owner of a connection (Linux version)
  *
  * (c) 2012 Straylight/Edgeware
  */
 
 /*----- Static variables --------------------------------------------------*/
 
-const char *const errtok[] = {
-#define DEFTOK(err, tok) tok,
-  ERROR(DEFTOK)
-#undef DEFTOK
+static FILE *natfp;
+
+/*----- Address-type operations -------------------------------------------*/
+
+struct addrops_sys {
+  const char *procfile;
+  const char *nfl3name;
+  int (*parseaddr)(char **, union addr *);
 };
 
-static int parseaddr4(char **pp, union addr *a)
+#define PROCFILE_IPV4 "/proc/net/tcp"
+#define NFL3NAME_IPV4 "ipv4"
+
+static int parseaddr_ipv4(char **pp, union addr *a)
   { a->ipv4.s_addr = strtoul(*pp, pp, 16); return (0); }
 
-static int addreq4(const union addr *a, const union addr *aa)
-  { return a->ipv4.s_addr == aa->ipv4.s_addr; }
+#define PROCFILE_IPV6 "/proc/net/tcp6"
+#define NFL3NAME_IPV6 "ipv6"
 
-static int parseaddr6(char **pp, union addr *a)
+static int parseaddr_ipv6(char **pp, union addr *a)
 {
   int i, j;
   unsigned long y;
@@ -65,26 +72,15 @@ static int parseaddr6(char **pp, union addr *a)
   return (0);
 }
 
-static int addreq6(const union addr *a, const union addr *b)
-  { return !memcmp(a->ipv6.s6_addr, b->ipv6.s6_addr, 16); }
-
-static const struct addrfamily {
-  int af;
-  const char *procfile;
-  int (*parseaddr)(char **pp, union addr *a);
-  int (*addreq)(const union addr *a, const union addr *aa);
-} addrfamilytab[] = {
-  { AF_INET, "/proc/net/tcp", parseaddr4, addreq4 },
-  { AF_INET6, "/proc/net/tcp6", parseaddr6, addreq6 },
-  { -1 }
-};
+#define DEFOPSYS(ty, TY)                                               \
+  const struct addrops_sys addrops_sys_##ty = {                                \
+    PROCFILE_##TY, NFL3NAME_##TY, parseaddr_##ty                       \
+  };
+ADDRTYPES(DEFOPSYS)
+#undef DEFOPSYS
 
 /*----- Main code ---------------------------------------------------------*/
 
-static int sockeq(const struct addrfamily *af,
-                 const struct socket *sa, const struct socket *sb)
-  { return (af->addreq(&sa->addr, &sb->addr) && sa->port == sb->port); }
-
 static int get_default_gw(int af, union addr *a)
 {
   int fd;
@@ -152,7 +148,6 @@ done:
 
 void identify(struct query *q)
 {
-  const struct addrfamily *af;
   FILE *fp = 0;
   dstr d = DSTR_INIT;
   char *p, *pp;
@@ -170,19 +165,13 @@ void identify(struct query *q)
   enum { LOC, REM, ST, UID, NFIELD };
   int f, ff[NFIELD];
 
-  for (af = addrfamilytab; af->af >= 0; af++)
-    if (af->af == q->af) goto found_af;
-  logmsg(q, LOG_ERR, "unexpected address family `%d'", q->af);
-  goto err_unk;
-found_af:;
-
-  if (get_default_gw(q->af, &s[0].addr) &&
-      af->addreq(&s[0].addr, &q->s[R].addr))
+  if (get_default_gw(q->ao->af, &s[0].addr) &&
+      q->ao->addreq(&s[0].addr, &q->s[R].addr))
     gwp = 1;
 
-  if ((fp = fopen(af->procfile, "r")) == 0) {
+  if ((fp = fopen(q->ao->sys->procfile, "r")) == 0) {
     logmsg(q, LOG_ERR, "failed to open `%s' for reading: %s",
-          af->procfile, strerror(errno));
+          q->ao->sys->procfile, strerror(errno));
     goto err_unk;
   }
 
@@ -194,7 +183,8 @@ found_af:;
 
   if (dstr_putline(&d, fp) == EOF) {
     logmsg(q, LOG_ERR, "failed to read header line from `%s': %s",
-          af->procfile, ferror(fp) ? strerror(errno) : "unexpected EOF");
+          q->ao->sys->procfile,
+          ferror(fp) ? strerror(errno) : "unexpected EOF");
     goto err_unk;
   }
 
@@ -218,7 +208,7 @@ found_af:;
   for (i = 0; i < NFIELD; i++) {
     if (ff[i] < 0) {
       logmsg(q, LOG_ERR, "failed to find required fields in `%s'",
-            af->procfile);
+            q->ao->sys->procfile);
       goto err_unk;
     }
   }
@@ -239,10 +229,10 @@ found_af:;
       continue;
 
     compare:
-      if (af->parseaddr(&p, &s[0].addr)) goto next_row;
+      if (q->ao->sys->parseaddr(&p, &s[0].addr)) goto next_row;
       if (*p != ':') break; p++;
       s[0].port = strtoul(p, 0, 16);
-      if (!sockeq(af, &q->s[i], &s[0]) &&
+      if (!sockeq(q->ao, &q->s[i], &s[0]) &&
          (i != R || !gwp || q->s[R].port != s[0].port))
        goto next_row;
     }
@@ -255,28 +245,22 @@ found_af:;
   }
 
   if (ferror(fp)) {
-    logmsg(q, LOG_ERR, "failed to read connection table: %s",
-          strerror(errno));
+    logmsg(q, LOG_ERR, "failed to read connection table `%s': %s",
+          q->ao->sys->procfile, strerror(errno));
     goto err_unk;
   }
 
-  if (q->af == AF_INET) {
-    fclose(fp);
-    if ((fp = fopen("/proc/net/ip_conntrack", "r")) == 0) {
-      if (errno == ENOENT)
-       goto err_nouser;
-      else {
-       logmsg(q, LOG_ERR,
-              "failed to open `/proc/net/ip_conntrack' for reading: %s",
-              strerror(errno));
-       goto err_unk;
-      }
-    }
+  if (natfp) {
+    rewind(natfp);
 
     for (;;) {
       DRESET(&d);
-      if (dstr_putline(&d, fp) == EOF) break;
+      if (dstr_putline(&d, natfp) == EOF) break;
       pp = d.buf;
+
+      NEXTFIELD; if (!*p) break;
+      if (strcmp(p, q->ao->sys->nfl3name)) continue;
+      NEXTFIELD; if (!*p) break;
       NEXTFIELD; if (!*p) break;
       if (strcmp(p, "tcp") != 0) continue;
       i = 0;
@@ -286,10 +270,10 @@ found_af:;
        if (strcmp(p, "ESTABLISHED") == 0)
          fl |= F_ESTAB;
        else if (strncmp(p, "src=", 4) == 0) {
-         inet_pton(AF_INET, p + 4, &s[i].addr);
+         inet_pton(q->ao->af, p + 4, &s[i].addr);
          fl |= F_SADDR;
        } else if (strncmp(p, "dst=", 4) == 0) {
-         inet_pton(AF_INET, p + 4, &s[i + 1].addr);
+         inet_pton(q->ao->af, p + 4, &s[i + 1].addr);
          fl |= F_DADDR;
        } else if (strncmp(p, "sport=", 6) == 0) {
          s[i].port = atoi(p + 6);
@@ -309,13 +293,13 @@ found_af:;
       {
        dstr dd = DSTR_INIT;
        dstr_putf(&dd, "%sestab ", (fl & F_ESTAB) ? " " : "!");
-       dputsock(&dd, af->af, &s[0]);
+       dputsock(&dd, q->ao, &s[0]);
        dstr_puts(&dd, "<->");
-       dputsock(&dd, af->af, &s[1]);
+       dputsock(&dd, q->ao, &s[1]);
        dstr_puts(&dd, " | ");
-       dputsock(&dd, af->af, &s[2]);
+       dputsock(&dd, q->ao, &s[2]);
        dstr_puts(&dd, "<->");
-       dputsock(&dd, af->af, &s[3]);
+       dputsock(&dd, q->ao, &s[3]);
        printf("parsed: %s\n", dd.buf);
        dstr_destroy(&dd);
       }
@@ -324,29 +308,29 @@ found_af:;
       if (!(fl & F_ESTAB)) continue;
 
       for (i = 0; i < 4; i++)
-       if (sockeq(af, &s[i], &q->s[L])) goto found_local;
+       if (sockeq(q->ao, &s[i], &q->s[L])) goto found_local;
       continue;
       putchar('.');
     found_local:
-      if (!sockeq(af, &s[i^1], &s[i^2]) ||
-         !sockeq(af, &s[i^1], &q->s[R]))
+      if (!sockeq(q->ao, &s[i^1], &s[i^2]) ||
+         !sockeq(q->ao, &s[i^1], &q->s[R]))
        continue;
       q->resp = R_NAT;
       q->u.nat = s[i^3];
       goto done;
     }
 
-    if (ferror(fp)) {
-      logmsg(q, LOG_ERR, "failed to read `/proc/net/ip_conntrack': %s",
+    /* Reached the end of the NAT file. */
+    if (ferror(natfp)) {
+      logmsg(q, LOG_ERR, "failed to read `/proc/net/nf_conntrack': %s",
             strerror(errno));
       goto err_unk;
     }
-    logmsg(q, LOG_ERR, "connection not found");
   }
 
 #undef NEXTFIELD
 
-err_nouser:
+  logmsg(q, LOG_NOTICE, "connection not found");
   q->resp = R_ERROR;
   q->u.error = E_NOUSER;
   goto done;
@@ -355,6 +339,16 @@ err_unk:
   q->u.error = E_UNKNOWN;
 done:
   dstr_destroy(&d);
+  if (fp) fclose(fp);
+}
+
+void init_sys(void)
+{
+  if ((natfp = fopen("/proc/net/nf_conntrack", "r")) == 0 &&
+      errno != ENOENT) {
+    die(1, "failed to open `/proc/net/nf_conntrack' for reading: %s",
+       strerror(errno));
+  }
 }
 
 /*----- That's all, folks -------------------------------------------------*/