lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Mon, 28 Jun 2010 16:21:39 -0700 (PDT) From: David Miller <davem@...emloft.net> To: sphink@...il.com Cc: netdev@...r.kernel.org, stephen.hemminger@...tta.com Subject: Re: ss -p is much too slow From: Steve Fink <sphink@...il.com> Date: Wed, 9 Jun 2010 11:42:38 -0700 > On closer inspection, it appears that ss -p does a quadratic scan. It > rescans every entry in /proc/*/fd/* repeatedly (once per listening > port? per process? I don't remember what I figured out.) > > I humbly suggest that this is not a good idea. Yep, this is junk. Please give this patch a try: ss: Avoid quadradic complexity with '-p' Scan the process list of open sockets once, and store in a hash table to be used by subsequent find_user() calls. Reported-by: Steve Fink <sphink@...il.com> Signed-off-by: David S. Miller <davem@...emloft.net> diff --git a/misc/ss.c b/misc/ss.c index 8a9663c..482b6bb 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -195,90 +195,147 @@ static FILE *ephemeral_ports_open(void) return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range"); } -int find_users(unsigned ino, char *buf, int buflen) +struct user_ent { + struct user_ent *next; + unsigned int ino; + int pid; + int fd; + char process[0]; +}; + +#define USER_ENT_HASH_SIZE 256 +struct user_ent *user_ent_hash[USER_ENT_HASH_SIZE]; + +static int user_ent_hashfn(unsigned int ino) { - char pattern[64]; - int pattern_len; - char *ptr = buf; - char name[1024]; - DIR *dir; - struct dirent *d; - int cnt = 0; - int nameoff; + int val = (ino >> 24) ^ (ino >> 16) ^ (ino >> 8) ^ ino; - if (!ino) - return 0; + return val & (USER_ENT_HASH_SIZE - 1); +} + +static void user_ent_add(unsigned int ino, const char *process, int pid, int fd) +{ + struct user_ent *p, **pp; + int str_len; - sprintf(pattern, "socket:[%u]", ino); - pattern_len = strlen(pattern); + str_len = strlen(process) + 1; + p = malloc(sizeof(struct user_ent) + str_len); + if (!p) + abort(); + p->next = NULL; + p->ino = ino; + p->pid = pid; + p->fd = fd; + strcpy(p->process, process); + + pp = &user_ent_hash[user_ent_hashfn(ino)]; + p->next = *pp; + *pp = p; +} - strncpy(name, getenv("PROC_ROOT") ? : "/proc/", sizeof(name)/2); - name[sizeof(name)/2] = 0; - if (strlen(name) == 0 || - name[strlen(name)-1] != '/') +static void user_ent_hash_build(void) +{ + const char *root = getenv("PROC_ROOT") ? : "/proc/"; + struct dirent *d; + char name[1024]; + int nameoff; + DIR *dir; + + strcpy(name, root); + if (strlen(name) == 0 || name[strlen(name)-1] != '/') strcat(name, "/"); + nameoff = strlen(name); - if ((dir = opendir(name)) == NULL) - return 0; + + dir = opendir(name); + if (!dir) + return; while ((d = readdir(dir)) != NULL) { - DIR *dir1; struct dirent *d1; - int pid; - int pos; - char crap; char process[16]; + int pid, pos; + DIR *dir1; + char crap; if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1) continue; - sprintf(name+nameoff, "%d/fd/", pid); + sprintf(name + nameoff, "%d/fd/", pid); pos = strlen(name); if ((dir1 = opendir(name)) == NULL) continue; - process[0] = 0; + process[0] = '\0'; while ((d1 = readdir(dir1)) != NULL) { - int fd, n; + const char *pattern = "socket:["; + unsigned int ino; char lnk[64]; + int fd, n; if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1) continue; sprintf(name+pos, "%d", fd); n = readlink(name, lnk, sizeof(lnk)-1); - if (n != pattern_len || - memcmp(lnk, pattern, n)) + if (strncmp(lnk, pattern, strlen(pattern))) continue; - if (ptr-buf >= buflen-1) - break; + sscanf(lnk, "socket:[%u]", &ino); - if (process[0] == 0) { + if (process[0] == '\0') { char tmp[1024]; FILE *fp; - snprintf(tmp, sizeof(tmp), "%s/%d/stat", - getenv("PROC_ROOT") ? : "/proc", pid); + + snprintf(tmp, sizeof(tmp), "%s/%d/stat", root, pid); if ((fp = fopen(tmp, "r")) != NULL) { fscanf(fp, "%*d (%[^)])", process); fclose(fp); } } - snprintf(ptr, buflen-(ptr-buf), "(\"%s\",%d,%d),", process, pid, fd); - ptr += strlen(ptr); - cnt++; + user_ent_add(ino, process, pid, fd); } closedir(dir1); } closedir(dir); +} + +int find_users(unsigned ino, char *buf, int buflen) +{ + struct user_ent *p; + int cnt = 0; + char *ptr; + + if (!ino) + return 0; + + p = user_ent_hash[user_ent_hashfn(ino)]; + ptr = buf; + while (p) { + if (p->ino != ino) + goto next; + + if (ptr - buf >= buflen - 1) + break; + + snprintf(ptr, buflen - (ptr - buf), + "(\"%s\",%d,%d),", + p->process, p->pid, p->fd); + ptr += strlen(ptr); + cnt++; + + next: + p = p->next; + } + if (ptr != buf) - ptr[-1] = 0; + ptr[-1] = '\0'; + return cnt; } - /* Get stats from slab */ struct slabstat @@ -2476,6 +2533,7 @@ int main(int argc, char *argv[]) break; case 'p': show_users++; + user_ent_hash_build(); break; case 'd': current_filter.dbs |= (1<<DCCP_DB); -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists