tcptable.c

Go to the documentation of this file.
00001 /*
00002  ** Copyright 2005-2007 - INL
00003  ** Written by Eric Leblond <regit@inl.fr>
00004  **            Vincent Deffontaines <vincent@inl.fr>
00005  ** INL http://www.inl.fr
00006  **
00007  ** $Id: tcptable.c 4847 2008-06-13 14:48:31Z regit $
00008  **
00009  **
00010  ** This program is free software; you can redistribute it and/or modify
00011  ** it under the terms of the GNU General Public License as published by
00012  ** the Free Software Foundation, version 3 of the License.
00013  **
00014  ** This program is distributed in the hope that it will be useful,
00015  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  ** GNU General Public License for more details.
00018  **
00019  ** You should have received a copy of the GNU General Public License
00020  ** along with this program; if not, write to the Free Software
00021  ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00022  */
00023 #include "libnuclient.h"
00024 #include "proto.h"
00025 #include "tcptable.h"
00026 #include <inttypes.h>
00027 #define USE_JHASH3
00028 #include <jhash.h>
00029 #ifdef FREEBSD
00030 
00031 #include <sys/types.h>
00032 #include <sys/socket.h>
00033 #include <sys/socketvar.h>
00034 #include <sys/param.h>
00035 #include <sys/queue.h>
00036 #include <sys/sysctl.h>
00037 #include <sys/protosw.h>
00038 
00039 #include <netinet/tcp_fsm.h>
00040 #include <netinet/in_pcb.h>
00041 #include <netinet/tcp_var.h>
00042 
00043 #endif
00044 
00045 #include <nubase.h>
00046 
00059 #ifdef LINUX
00060 
00066 int parse_tcptable_file(nuauth_session_t * session, conntable_t * ct, char *filename,
00067                         FILE ** file, int protocol, int use_ipv6)
00068 {
00069         char buf[1024];
00070         conn_t c;
00071         const char state_char = '2';    /* TCP_SYN_SENT written in hexadecimal */
00072         int state_pos;
00073         int uid_pos;
00074         char session_uid[20];
00075         int session_uid_len;
00076         int ret;
00077         char *pos;
00078 
00079         /* open file if it's not already opened */
00080         if (*file == NULL) {
00081                 *file = fopen(filename, "r");
00082                 if (*file == NULL) {
00083                         printf("Fail to open %s: %s", filename,
00084                                strerror(errno));
00085                         return 0;
00086                 }
00087         }
00088 
00089         /* rewind to the beginning of the file */
00090         rewind(*file);
00091 
00092         /* read header */
00093         if (fgets(buf, sizeof(buf), *file) == NULL)
00094                 panic("%s: missing header!", filename);
00095 
00096         /* convert session user identifier to string */
00097         secure_snprintf(session_uid, sizeof(session_uid), "%5lu",
00098                         (long)session->userid);
00099         session_uid_len = strlen(session_uid);
00100 
00101         /* get state field position in header */
00102         pos = strstr(buf, " st ");
00103         if (pos == NULL)
00104                 panic
00105                     ("Can't find position of state field in /proc/net/tcp header!");
00106         state_pos = pos - buf + 2;
00107 
00108         /* get user identifier position in header (it's just after 'retrnsmt' field) */
00109         pos = strstr(buf, " retrnsmt ");
00110         if (pos == NULL)
00111                 panic
00112                     ("Can't find position of user identifier field in /proc/net/tcp header!");
00113         uid_pos = pos - buf + strlen(" retrnsmt ");
00114 
00115         while (fgets(buf, sizeof(buf), *file) != NULL) {
00116 #ifdef USE_FILTER
00117                 int seen = 0;
00118 #endif
00119 
00120                 /* only keep connections in state "SYN packet sent" */
00121                 if (buf[state_pos] != state_char) {
00122                         continue;
00123                 }
00124 
00125                 /* only keep session user connections */
00126                 if (strncmp(buf + uid_pos, session_uid, session_uid_len) !=
00127                     0) {
00128                         continue;
00129                 }
00130 
00131                 /* get all fields */
00132                 if (!use_ipv6) {
00133                         uint32_t src, dst;
00134                         ret = sscanf(buf,
00135                                      "%*d: "
00136                                      "%" SCNx32 ":%hx "
00137                                      "%" SCNx32 ":%hx "
00138                                      "%*x %*x:%*x %*x:%*x %x "
00139                                      "%lu %*d %lu",
00140                                      &src, &c.port_src,
00141                                      &dst, &c.port_dst,
00142                                      &c.retransmit, &c.uid,
00143                                      &c.inode);
00144                         if (ret != 7) {
00145                                 continue;
00146                         }
00147                         uint32_to_ipv6(src, &c.ip_src);
00148                         uint32_to_ipv6(dst, &c.ip_dst);
00149                 } else {
00150                         char ip_src[33];
00151                         char ip_dst[33];
00152                         ret = sscanf(buf,
00153                                      "%*d: "
00154                                      "%32s"
00155                                      ":%hx "
00156                                      "%32s"
00157                                      ":%hx "
00158                                      "%*x %*x:%*x %*x:%*x %x "
00159                                      "%lu %*d %lu",
00160                                      ip_src,
00161                                      &c.port_src,
00162                                      ip_dst,
00163                                      &c.port_dst,
00164                                      &c.retransmit, &c.uid, &c.inode);
00165                         if (ret != 7) {
00166                                 continue;
00167                         }
00168                         if (!hex2ipv6(ip_src, &c.ip_src))
00169                                 continue;
00170                         if (!hex2ipv6(ip_dst, &c.ip_dst))
00171                                 continue;
00172                 }
00173 
00174                 /* skip nul inodes */
00175                 if (c.inode == 0) {
00176                         continue;
00177                 }
00178 #if DEBUG
00179                 /*  Check if there is a matching rule in the filters list */
00180                 printf("Packet dst = %ld (%lx)\n", c.rmt, c.rmt);
00181 #endif
00182 
00183 #ifdef USE_FILTER
00184                 /*  If we're sure auth_by_default is either 0 or 1, it can be simplified. */
00185                 /*  (MiKael) TODO: Make sure!! :) */
00186                 if (session->auth_by_default && seen)
00187                         continue;
00188                 if (!session->auth_by_default && !seen)
00189                         continue;
00190 #endif
00191                 c.protocol = protocol;
00192                 tcptable_add(ct, &c);
00193         }
00194         return 1;
00195 }
00196 #endif
00197 
00205 int tcptable_read(nuauth_session_t * session, conntable_t * ct)
00206 {
00207 #ifdef LINUX
00208         static FILE *fd_tcp = NULL;
00209         static FILE *fd_tcp6 = NULL;
00210         static FILE *fd_udp = NULL;
00211 
00212 #if DEBUG
00213         assert(ct != NULL);
00214         assert(TCP_SYN_SENT == 2);
00215 #endif
00216 
00217         if (!parse_tcptable_file
00218             (session, ct, "/proc/net/tcp", &fd_tcp, IPPROTO_TCP, 0))
00219                 return 0;
00220 
00221         parse_tcptable_file(session, ct, "/proc/net/tcp6", &fd_tcp6,
00222                             IPPROTO_TCP, 1);
00223 
00224         if (!parse_tcptable_file
00225             (session, ct, "/proc/net/udp", &fd_udp, IPPROTO_UDP, 0))
00226                 return 0;
00227         return 1;
00228 #elif defined(FREEBSD)
00229         conn_t c;
00230         int istcp;
00231         char *buf;
00232         const char *mibvar;
00233         struct tcpcb *tp = NULL;
00234         struct inpcb *inp;
00235         struct xinpgen *xig, *oxig;
00236         struct xsocket *so;
00237         size_t len;
00238         int proto = IPPROTO_TCP;
00239 #if 0
00240         istcp = 0;
00241         switch (proto) {
00242         case IPPROTO_TCP:
00243 #endif
00244                 istcp = 1;
00245                 mibvar = "net.inet.tcp.pcblist";
00246 #if 0
00247                 break;
00248         case IPPROTO_UDP:
00249                 mibvar = "net.inet.udp.pcblist";
00250                 break;
00251         }
00252 #endif
00253         /* get connection table size, and then allocate a buffer */
00254         len = 0;
00255         if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) {
00256                 if (errno != ENOENT)
00257                         printf("sysctl: %s", mibvar);
00258                 return 0;
00259         }
00260         buf = malloc(len);
00261         if (buf == NULL) {
00262                 printf("malloc %lu bytes", (u_long) len);
00263                 return 0;
00264         }
00265 
00266         /* read connection table */
00267         if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) {
00268                 printf("sysctl: %s", mibvar);
00269                 free(buf);
00270                 return 0;
00271         }
00272 
00273         oxig = xig = (struct xinpgen *) buf;
00274         for (xig = (struct xinpgen *) ((char *) xig + xig->xig_len);
00275              xig->xig_len > sizeof(struct xinpgen);
00276              xig = (struct xinpgen *) ((char *) xig + xig->xig_len)) {
00277                 if (istcp) {
00278                         tp = &((struct xtcpcb *) xig)->xt_tp;
00279                         inp = &((struct xtcpcb *) xig)->xt_inp;
00280                         so = &((struct xtcpcb *) xig)->xt_socket;
00281                 } else {
00282                         inp = &((struct xinpcb *) xig)->xi_inp;
00283                         so = &((struct xinpcb *) xig)->xi_socket;
00284                 }
00285 
00286                 /* Ignore sockets for protocols other than the desired one. */
00287                 if (so->xso_protocol != (int) proto)
00288                         continue;
00289 
00290                 /* Ignore PCBs which were freed during copyout. */
00291                 if (inp->inp_gencnt > oxig->xig_gen)
00292                         continue;
00293 
00294                 /* only do IPV4 for now */
00295                 if ((inp->inp_vflag & INP_IPV4) == 0)
00296                         continue;
00297 
00298                 /* check SYN_SENT and get rid of NULL address */
00299                 if ((istcp && tp->t_state != TCPS_SYN_SENT)
00300                     || (inet_lnaof(inp->inp_laddr) == INADDR_ANY))
00301                         continue;
00302 
00303                 uint32_to_ipv6(inp->inp_laddr.s_addr, &c.ip_src);
00304                 c.port_src = ntohs(inp->inp_lport);
00305 
00306                 uint32_to_ipv6(inp->inp_faddr.s_addr, &c.ip_dst);
00307                 c.port_dst = ntohs(inp->inp_fport);
00308                 c.protocol = IPPROTO_TCP;
00309 
00310                 tcptable_add(ct, &c);
00311         }
00312         free(buf);
00313         return 1;
00314 #endif
00315 }
00316 
00323 int tcptable_init(conntable_t ** ct)
00324 {
00325         int i;
00326 
00327         (*ct) = (conntable_t *) calloc(1, sizeof(conntable_t));
00328         if (*ct == NULL) {
00329                 return 0;
00330         }
00331 
00332         for (i = 0; i < CONNTABLE_BUCKETS; i++) {
00333                 (*ct)->buckets[i] = NULL;
00334         }
00335         return 1;
00336 }
00337 
00342 int tcptable_hash(conn_t * c)
00343 {
00344         /* TODO: Hash the whole ip address! */
00345 #ifndef FREEBSD
00346         return (jhash_3words(c->ip_src.s6_addr32[3],
00347                              c->ip_dst.s6_addr32[3],
00348                              (c->port_dst | c->port_src << 16),
00349                              32)) % CONNTABLE_BUCKETS;
00350 #else
00351         return (jhash_3words(c->ip_src.__u6_addr.__u6_addr32[3],
00352                              c->ip_dst.__u6_addr.__u6_addr32[3],
00353                              (c->port_dst | c->port_src << 16),
00354                              32)) % CONNTABLE_BUCKETS;
00355 #endif
00356 }
00357 
00361 void tcptable_add(conntable_t * ct, conn_t * c)
00362 {
00363         conn_t *old, *newc;
00364         int bi;
00365 #if DEBUG
00366         assert(ct != NULL);
00367         assert(c != NULL);
00368 #endif
00369 
00370         newc = (conn_t *) calloc(1, sizeof(conn_t));
00371         if (!newc) {
00372                 panic("memory exhausted");
00373         }
00374 
00375         c->createtime = time(NULL);
00376         memcpy(newc, c, sizeof(conn_t));
00377         bi = tcptable_hash(c);
00378         old = ct->buckets[bi];
00379         ct->buckets[bi] = newc;
00380         ct->buckets[bi]->next = old;
00381 }
00382 
00388 conn_t *tcptable_find(conntable_t * ct, conn_t * c)
00389 {
00390         conn_t *bucket;
00391 #if DEBUG
00392         assert(ct != NULL);
00393         assert(c != NULL);
00394 #endif
00395         bucket = ct->buckets[tcptable_hash(c)];
00396         while (bucket != NULL) {
00397                 if ((c->protocol == bucket->protocol)
00398                     && ipv6_equal(&c->ip_dst, &bucket->ip_dst)
00399                     && (c->port_dst == bucket->port_dst)
00400                     && ipv6_equal(&c->ip_src, &bucket->ip_src)
00401                     && (c->port_src == bucket->port_src)
00402                     ) {
00403                         return bucket;
00404                 }
00405                 bucket = bucket->next;
00406         }
00407 
00408         return NULL;
00409 }
00410 
00414 void tcptable_free(conntable_t * ct)
00415 {
00416         int i;
00417 
00418         if (ct == NULL)
00419                 return;
00420 
00421         for (i = 0; i < CONNTABLE_BUCKETS; i++) {
00422                 conn_t *c0, *c1;
00423 
00424                 c0 = ct->buckets[i];
00425                 while (c0 != NULL) {
00426                         c1 = c0->next;
00427                         free(c0);
00428                         c0 = c1;
00429                 }
00430                 ct->buckets[i] = NULL;
00431         }
00432 
00433         /* free structure */
00434         free(ct);
00435 }
00436 

Generated on Thu Nov 20 04:00:34 2008 for NuFW by  doxygen 1.4.7