#include #include #define _GNU_SOURCE #include #include #include /* * We import the hash function from the kernel, so we can compute a conflicting * name directly in this program and reproduce the bug easily */ #define NETDEV_HASHBITS 8 /*************** from include/linux/dcache.h *****************************/ #define init_name_hash() 0 static inline unsigned long partial_name_hash(unsigned long c, unsigned long prevhash) { return (prevhash + (c << 4) + (c >> 4)) * 11; } static inline unsigned long end_name_hash(unsigned long hash) { return (unsigned int) hash; } static inline unsigned int full_name_hash(const unsigned char *name, unsigned int len) { unsigned long hash = init_name_hash(); while (len--) hash = partial_name_hash(*name++, hash); return end_name_hash(hash); } /***************** from include/linux/hash.h *****************************/ /* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ #define GOLDEN_RATIO_PRIME_32 0x9e370001UL static inline unsigned int hash_32(unsigned int val, unsigned int bits) { /* On some cpus multiply is faster, on others gcc will do shifts */ unsigned int hash = val * GOLDEN_RATIO_PRIME_32; /* High bits are more random, so use them. */ return hash >> (32 - bits); } /*************************************************************************/ unsigned int dev_name_hash(const char *name) { return hash_32(full_name_hash(name, strnlen(name, IFNAMSIZ)), NETDEV_HASHBITS); } int main(int argc, char *argv[]) { char devname[IFNAMSIZ]; char cmd[4096]; unsigned int val, val2; if (getuid()) { fprintf(stderr, "you have to be root !\n"); return -1; } /* * Unshare the network namespace, we don't mess the network * and we can assume the eth%d rename will be eth0 */ if (unshare(CLONE_NEWNET)) { perror("unshare"); return -1; } val = dev_name_hash("eth0"); while (1) { snprintf(devname, IFNAMSIZ, "ethXXXXXX"); mktemp(devname); val2 = dev_name_hash(devname); if (val == val2) { printf("'%s' has same hash entry\n", devname); break; } } /* * We create a dummy interface with the conflicting name * and then we rename it with an kernel allocated name * The kernel will fail to rename with -ENFILE. */ sprintf(cmd, "ip link add %s type dummy", devname); if (system(cmd)) { perror("system"); return -1; } sprintf(cmd, "ip link set name eth%%d dev %s", devname); printf("%s\n", cmd); system(cmd); return 0; }