#include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; /* This is used when constructing routing messages. Unfortunately, as far as I can tell, it's documented nowhere but the kernel source (!). */ #define RTROUNDUP(len) \ ( (((len)?:1) + sizeof(int32_t) - 1) & ~(sizeof(int32_t)-1U) ) static struct in_addr ip; static struct ether_addr ea; static int ifindex; static int rtsock; static void usage(void) __attribute__((__noreturn__)); static void usage(void) { fprintf(stderr,"Usage: %s IP MAC interface\n",__progname); exit(1); } /* Find the index value for an interface. Using SIOCGIFCONF is gross; SIOCGIFADDR on AF_LINK, or at least AF_ARP, should give us the AF_LINK address. As it stands, there seems to be no way to find out the index of an interface without assuming something about some address family - fortunately, it's enough to assume we can create an AF_INET/SOCK_DGRAM socket. Returns zero on error; real interface index values are never 0. */ static int index_of_if(const char *name) { int s; int i; int n; struct ifconf ifc; struct ifreq *ifr; char confbuf[8192]; /* XXX on fixed size */ char ifrbuf[8192] __attribute__((__aligned__(__alignof__(struct ifreq)))); ifc.ifc_len = sizeof(confbuf); ifc.ifc_buf = &confbuf[0]; s = socket(AF_INET,SOCK_DGRAM,0); if (s < 0) { fprintf(stderr,"%s: can't create AF_INET socket: %s\n",__progname,strerror(errno)); exit(1); } if (ioctl(s,SIOCGIFCONF,&ifc) < 0) { fprintf(stderr,"%s: can't SIOCGIFCONF: %s\n",__progname,strerror(errno)); exit(1); } i = 0; close(s); ifr = (void *)&ifrbuf[0]; while (i < ifc.ifc_len) { bcopy(i+(char *)ifc.ifc_req,&ifrbuf[0],sizeof(struct ifreq)); n = ifr->ifr_addr.sa_len; if (n < sizeof(ifr->ifr_addr)) n = sizeof(ifr->ifr_addr); n += sizeof(ifr->ifr_name); if (n > sizeof(ifrbuf)) abort(); bcopy(i+(char *)ifc.ifc_req,&ifrbuf[0],n); i += n; if (i > ifc.ifc_len) abort(); if ( (ifr->ifr_addr.sa_family == AF_LINK) && !strncmp(&ifr->ifr_name[0],name,sizeof(ifr->ifr_name)) ) { return(((struct sockaddr_dl *)&ifr->ifr_addr)->sdl_index); } } return(0); } int main(int, char **); int main(int ac, char **av) { struct { struct rt_msghdr rmh; char data[sizeof(struct sockaddr_in)+sizeof(struct sockaddr_dl)+(2*sizeof(int32_t))]; } rtmsg; char *rmp; struct sockaddr_in sin; struct sockaddr_dl sdl; int rv; if (ac != 4) usage(); if (! inet_aton(av[1],&ip)) usage(); { struct ether_addr *eap; eap = ether_aton(av[2]); if (! eap) usage(); ea = *eap; } ifindex = index_of_if(av[3]); if (ifindex == 0) usage(); rtsock = socket(AF_ROUTE,SOCK_RAW,0); if (rtsock < 0) { fprintf(stderr,"%s: can't create routing socket: %s\n",__progname,strerror(errno)); exit(1); } bzero(&rtmsg,sizeof(rtmsg)); rtmsg.rmh.rtm_type = RTM_ADD; rtmsg.rmh.rtm_flags = RTF_UP | RTF_HOST | RTF_LLINFO | RTF_STATIC; rtmsg.rmh.rtm_version = RTM_VERSION; rtmsg.rmh.rtm_seq = 0; rtmsg.rmh.rtm_addrs = RTA_DST | RTA_GATEWAY; rtmsg.rmh.rtm_inits = 0; rmp = (void *)(1+&rtmsg.rmh); bzero(&sin,sizeof(sin)); sin.sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_addr = ip; sin.sin_port = 0; bcopy(&sin,rmp,sizeof(sin)); rmp += RTROUNDUP(sin.sin_len); bzero(&sdl,sizeof(sdl)); sdl.sdl_len = sizeof(struct sockaddr_dl); sdl.sdl_family = AF_LINK; sdl.sdl_index = ifindex; sdl.sdl_type = IFT_ETHER; sdl.sdl_nlen = 0; sdl.sdl_alen = 6; sdl.sdl_slen = 0; bcopy(&ea.ether_addr_octet[0],&sdl.sdl_data[0],6); bcopy(&sdl,rmp,sdl.sdl_len); rmp += RTROUNDUP(sdl.sdl_len); rtmsg.rmh.rtm_msglen = rmp - (char *)&rtmsg; rv = write(rtsock,&rtmsg,rtmsg.rmh.rtm_msglen); if (rv < 0) { fprintf(stderr,"%s: writing routing message: %s\n",__progname,strerror(errno)); exit(1); } if (rv != rtmsg.rmh.rtm_msglen) { fprintf(stderr,"%s: short write writing routing message (wanted %d, wrote %d)\n",__progname,(int)rtmsg.rmh.rtm_msglen,rv); exit(1); } exit(0); }