/* * Name Interfaces based on MAC address. * Writen 2000 by Andi Kleen. * Subject to the Gnu Public License, version 2. * TODO: make it support token ring etc. * $Id: nameif.c,v 1.4 2003/09/11 03:46:49 ak Exp $ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "intl.h" #include "net-support.h" #include "util.h" const char default_conf[] = "/etc/mactab"; const char *fname = default_conf; int use_syslog; int ctl_sk = -1; void err(char *msg) { if (use_syslog) { syslog(LOG_ERR,"%s: %m", msg); } else { perror(msg); } exit(1); } void complain(char *fmt, ...) { va_list ap; va_start(ap,fmt); if (use_syslog) { vsyslog(LOG_ERR,fmt,ap); } else { vfprintf(stderr,fmt,ap); fputc('\n',stderr); } va_end(ap); exit(1); } void warning(char *fmt, ...) { va_list ap; va_start(ap,fmt); if (use_syslog) { vsyslog(LOG_ERR,fmt,ap); } else { vfprintf(stderr,fmt,ap); fputc('\n',stderr); } va_end(ap); } int parsemac(char *str, unsigned char *mac) { char *s; while ((s = strsep(&str, ":")) != NULL) { unsigned byte; if (sscanf(s,"%x", &byte)!=1 || byte > 0xff) return -1; *mac++ = byte; } return 0; } void opensock(void) { if (ctl_sk < 0) ctl_sk = socket(PF_INET,SOCK_DGRAM,0); } #ifndef ifr_newname #define ifr_newname ifr_ifru.ifru_slave #endif int setname(char *oldname, char *newname) { struct ifreq ifr; opensock(); memset(&ifr,0,sizeof(struct ifreq)); safe_strncpy(ifr.ifr_name, oldname, IFNAMSIZ); safe_strncpy(ifr.ifr_newname, newname, IFNAMSIZ); return ioctl(ctl_sk, SIOCSIFNAME, &ifr); } int getmac(char *name, unsigned char *mac) { int r; struct ifreq ifr; opensock(); memset(&ifr,0,sizeof(struct ifreq)); safe_strncpy(ifr.ifr_name, name, IFNAMSIZ); r = ioctl(ctl_sk, SIOCGIFHWADDR, &ifr); memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); return r; } struct change { struct change *next; int found; char ifname[IFNAMSIZ+1]; unsigned char mac[6]; }; struct change *clist; struct change *lookupmac(unsigned char *mac) { struct change *ch; for (ch = clist;ch;ch = ch->next) if (!memcmp(ch->mac, mac, 6)) return ch; return NULL; } int addchange(char *p, struct change *ch, char *pos) { if (strchr(ch->ifname, ':')) warning(_("alias device %s at %s probably has no mac"), ch->ifname, pos); if (parsemac(p,ch->mac) < 0) complain(_("cannot parse MAC `%s' at %s"), p, pos); ch->next = clist; clist = ch; return 0; } void readconf(void) { char *line; size_t linel; int linenum; FILE *ifh; char *p; int n; struct change *ch = NULL; ifh = fopen(fname, "r"); if (!ifh) complain(_("opening configuration file %s: %s"),fname,strerror(errno)); line = NULL; linel = 0; linenum = 1; while (getdelim(&line, &linel, '\n', ifh) > 0) { char pos[20]; sprintf(pos, _("line %d"), linenum); if ((p = strchr(line,'#')) != NULL) *p = '\0'; p = line; while (isspace(*p)) ++p; if (*p == '\0') continue; n = strcspn(p, " \t"); if (n > IFNAMSIZ-1) complain(_("interface name too long at line %d"), line); ch = xmalloc(sizeof(struct change)); memcpy(ch->ifname, p, n); ch->ifname[n] = 0; p += n; p += strspn(p, " \t"); n = strspn(p, "0123456789ABCDEFabcdef:"); p[n] = 0; addchange(p, ch, pos); linenum++; } fclose(ifh); } struct option lopt[] = { {"syslog", 0, NULL, 's' }, {"config-file", 1, NULL, 'c' }, {"help", 0, NULL, '?' }, {NULL}, }; void usage(void) { fprintf(stderr, _("usage: nameif [-c configurationfile] [-s] {ifname macaddress}\n")); exit(E_USAGE); } int main(int ac, char **av) { FILE *ifh; char *p; int n; int linenum; char *line = NULL; size_t linel = 0; int ret = 0; for (;;) { int c = getopt_long(ac,av,"c:s",lopt,NULL); if (c == -1) break; switch (c) { default: case '?': usage(); case 'c': fname = optarg; break; case 's': use_syslog = 1; break; } } if (use_syslog) openlog("nameif",0,LOG_LOCAL0); while (optind < ac) { struct change *ch = xmalloc(sizeof(struct change)); char pos[30]; if ((ac-optind) & 1) usage(); if (strlen(av[optind])+1>IFNAMSIZ) complain(_("interface name `%s' too long"), av[optind]); safe_strncpy(ch->ifname, av[optind], sizeof(ch->ifname)); optind++; sprintf(pos,_("argument %d"),optind); addchange(av[optind], ch, pos); optind++; } if (!clist || fname != default_conf) readconf(); ifh = fopen("/proc/net/dev", "r"); if (!ifh) complain(_("open of /proc/net/dev: %s"), strerror(errno)); linenum = 0; while (getdelim(&line, &linel, '\n', ifh) > 0) { struct change *ch; unsigned char mac[6]; if (linenum++ < 2) continue; p = line; while (isspace(*p)) ++p; n = strcspn(p, ": \t"); p[n] = 0; if (n > IFNAMSIZ-1) complain(_("interface name `%s' too long"), p); if (getmac(p, mac) < 0) continue; ch = lookupmac(mac); if (!ch) continue; ch->found = 1; if (strcmp(p, ch->ifname)) { if (setname(p, ch->ifname) < 0) complain(_("cannot change name of %s to %s: %s"), p, ch->ifname, strerror(errno)); } } fclose(ifh); while (clist) { struct change *ch = clist; clist = clist->next; if (!ch->found){ warning(_("interface '%s' not found"), ch->ifname); ret = 1; } free(ch); } if (use_syslog) closelog(); return ret; }