diff options
author | Phil Blundell <philb@gnu.org> | 1998-01-08 09:54:43 +0000 |
---|---|---|
committer | Phil Blundell <philb@gnu.org> | 1998-01-08 09:54:43 +0000 |
commit | 1ef3bd3bd1a104a99567cfa90485c89f7edcb812 (patch) | |
tree | f28676e85584644332eb204068cada3fd05a0ac3 /arp.c | |
download | net-tools-1ef3bd3bd1a104a99567cfa90485c89f7edcb812.tar.gz net-tools-1ef3bd3bd1a104a99567cfa90485c89f7edcb812.tar.bz2 net-tools-1ef3bd3bd1a104a99567cfa90485c89f7edcb812.zip |
Initial revision
Diffstat (limited to 'arp.c')
-rw-r--r-- | arp.c | 640 |
1 files changed, 640 insertions, 0 deletions
@@ -0,0 +1,640 @@ +/* + * arp This file contains an implementation of the command + * that maintains the kernel's ARP cache. It is derived + * from Berkeley UNIX arp(8), but cleaner and with sup- + * port for devices other than Ethernet. + * + * NET-TOOLS A collection of programs that form the base set of the + * NET-3 Networking Distribution for the LINUX operating + * system. + * + * Version: arp 1.69 (1996-05-17) + * + * Maintainer: Bernd 'eckes' Eckenfels, <net-tools@lina.inka.de> + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * + * Changes: + * Alan Cox : modified for NET3 + * Andrew Tridgell : proxy arp netmasks + * Bernd Eckenfels : -n option + * Bernd Eckenfels : Use only /proc for display + * {1.60} Bernd Eckenfels : new arpcode (-i) for 1.3.42 but works + * with 1.2.x, too + * {1.61} Bernd Eckenfels : more verbose messages + * {1.62} Bernd Eckenfels : check -t for hw adresses and try to + * explain EINVAL (jeff) + *960125 {1.63} Bernd Eckenfels : -a print hardwarename instead of tiltle + *960201 {1.64} Bernd Eckenfels : net-features.h support + *960203 {1.65} Bernd Eckenfels : "#define" in "#if", + * -H|-A additional to -t|-p + *960214 {1.66} Bernd Eckenfels : Fix optarg required for -H and -A + *960412 {1.67} Bernd Eckenfels : device=""; is default + *960514 {1.68} Bernd Eckenfels : -N and -D + *960517 {1.69} Bernd Eckenfels : usage() fixed + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include "net-support.h" +#include "pathnames.h" +#include "version.h" +#include "config.h" +#include "net-locale.h" + +#define DFLT_AF "inet" +#define DFLT_HW "ether" + +#define FEATURE_ARP +#include "lib/net-features.h" + + +char *Release = RELEASE, + *Version = "arp 1.69 (1996-05-17)"; + +int opt_n = 0; /* do not resolve addresses */ +int opt_N = 0; /* use symbolic names */ +int opt_v = 0; /* debugging output flag */ +int opt_D = 0; /* HW-address is devicename */ +struct aftype *ap; /* current address family */ +struct hwtype *hw; /* current hardware type */ +int sockfd=0; /* active socket descriptor */ +int hw_set = 0; /* flag if hw-type was set (-H) */ +char device[16]=""; /* current device */ +static void usage(void); + +/* Delete an entry from the ARP cache. */ +static int +arp_del(char **args) +{ + char host[128]; + struct arpreq req; + struct sockaddr sa; +#if HAVE_NEW_SIOCSARP + struct arpreq_old old_req; +#endif + + memset((char *) &req, 0, sizeof(req)); + + /* Resolve the host name. */ + if (*args == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hostname, "arp: need host name\n")); + return(-1); + } + strcpy(host, *args); + if (ap->input(0, host, &sa) < 0) { + ap->herror(host); + return(-1); + } + + /* If a host has more than one address, use the correct one! */ + memcpy((char *) &req.arp_pa, (char *) &sa, sizeof(struct sockaddr)); + + req.arp_flags=0; + + if (args[1]) { + if (strcmp(args[1],"pub")==0) + req.arp_flags|=ATF_PUBL; + else + usage(); + } + +#if HAVE_NEW_SIOCSARP + strcpy(req.arp_dev,device); + memcpy((char *)&old_req,(char *)&req,sizeof(old_req)); + + /* Call the kernel. */ + if (opt_v) fprintf(stderr,"arp: SIOCDARP()\n"); + if (ioctl(sockfd, SIOCDARP, &req) < 0) { + if (errno == EINVAL) { + if (opt_v) fprintf(stderr,"arp: OLD_SIOCDARP()\n"); + if (ioctl(sockfd, OLD_SIOCDARP, &old_req) < 0) { + if (errno != ENXIO) { + perror("OLD_SIOCSARP"); + return(-1); + } + } else { + return(0); + } + } + if (errno == ENXIO) { + printf(NLS_CATGETS(catfd, arpSet, arp_no_arp, + "No ARP entry for %s\n"), host); + return(-1); + } + perror("SIOCDARP"); + return(-1); + } +#else + /* Call the kernel. */ + if (opt_v) fprintf(stderr,"arp: old_SIOCDARP()\n"); + if (ioctl(sockfd, SIOCDARP, &req) < 0) { + perror("SIOCDARP"); + return(-1); + } +#endif + + return(0); +} + +/* Get the hardware address to a specified interface name */ +static int +arp_getdevhw(char *ifname, struct sockaddr *sa, struct hwtype *hw) +{ + struct ifreq ifr; + struct hwtype *xhw; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) { + fprintf(stderr,"arp: cant get HW-Address for `%s': %s.\n", ifname, strerror(errno)); + return(-1); + } + if (hw && (ifr.ifr_hwaddr.sa_family!=hw->type)) { + fprintf(stderr,"arp: protocol type missmatch.\n"); + return(-1); + } + memcpy((char *)sa, (char *)&(ifr.ifr_hwaddr), sizeof(struct sockaddr)); + + if (opt_v) { + if (!(xhw = get_hwntype(ifr.ifr_hwaddr.sa_family)) || (xhw->sprint==0)) { + xhw = get_hwntype(-1); + } + fprintf(stderr, "arp: device `%s' has HW address %s `%s'.\n",ifname, xhw->name, xhw->sprint(&ifr.ifr_hwaddr)); + } + return(0); +} + +/* Set an entry in the ARP cache. */ +static int +arp_set(char **args) +{ + char host[128]; + struct arpreq req; +#if HAVE_NEW_SIOCSARP + struct arpreq_old old_req; +#endif + struct sockaddr sa; + int flags; + + memset((char *) &req, 0, sizeof(req)); + + /* Resolve the host name. */ + if (*args == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hostname, "arp: need host name\n")); + return(-1); + } + strcpy(host, *args++); + if (ap->input(0, host, &sa) < 0) { + ap->herror(host); + return(-1); + } + + /* If a host has more than one address, use the correct one! */ + memcpy((char *) &req.arp_pa, (char *) &sa, sizeof(struct sockaddr)); + + /* Fetch the hardware address. */ + if (*args == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_need_hw, "arp: need hardware address\n")); + return(-1); + } + + if (opt_D) { + if (arp_getdevhw(*args++, &req.arp_ha, hw_set?hw:NULL) < 0) + return(-1); + } else { + if (hw->input(*args++, &req.arp_ha) < 0) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_invalidhw, "arp: invalid hardware address\n")); + return(-1); + } + } + + /* Check out any modifiers. */ + flags = ATF_PERM; + while (*args != NULL) { + if (! strcmp(*args, "temp")) flags &= ~ATF_PERM; + if (! strcmp(*args, "pub")) flags |= ATF_PUBL; +/* if (! strcmp(*args, "rarp")) flags |= ATF_RARP;*/ + if (! strcmp(*args, "trail")) flags |= ATF_USETRAILERS; + if (! strcmp(*args, "netmask")) + { + if (*++args == NULL) usage(); + if (strcmp(*args,"255.255.255.255") != 0) + { + strcpy(host, *args); + if (ap->input(0, host, &sa) < 0) { + ap->herror(host); + return(-1); + } + memcpy((char *) &req.arp_netmask, (char *) &sa, + sizeof(struct sockaddr)); + flags |= ATF_NETMASK; + } + } + args++; + } + + if ((flags & ATF_NETMASK) && !(flags & ATF_PUBL)) + usage(); + + /* Fill in the remainder of the request. */ + req.arp_flags = flags; + +#if HAVE_NEW_SIOCSARP + strcpy(req.arp_dev,device); + memcpy((char *)&old_req,(char *)&req,sizeof(old_req)); + + /* Call the kernel. */ + if (opt_v) fprintf(stderr,"arp: SIOCSARP()\n"); + if (ioctl(sockfd, SIOCSARP, &req) < 0) { + if (errno != EINVAL) { + perror("SIOCSARP"); + return(-1); + } + if (opt_v) fprintf(stderr,"arp: OLD_SIOCSARP()\n"); + if (ioctl(sockfd, OLD_SIOCSARP, &old_req) < 0) { + if (errno != EINVAL) { + perror("OLD_SIOCSARP"); + return(-1); + } + perror("SIOCSARP and OLD_SIOCSARP"); + if (flags & ATF_PUBL) + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_einv_pub, + "Probably destination is reached via ARP Interface. See arp(8)\n")); + else + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_einv_nopub, + "Probably destination is on different Interface. See arp(8)\n")); + return(-1); + } + } +#else + /* Call the kernel. */ + if (opt_v) fprintf(stderr,"arp: old_SIOCSARP()\n"); + if (ioctl(sockfd, SIOCSARP, &req) < 0) { + perror("SIOCSARP"); + return(-1); + } +#endif + + return(0); +} + + +/* Process an EtherFile */ +static int +arp_file(char *name) +{ + char buff[1024]; + char *sp, *args[32]; + int linenr, argc; + FILE *fp; + + if ((fp = fopen(name, "r")) == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_cant_open, "arp: cannot open etherfile %s !\n"), name); + return(-1); + } + + /* Read the lines in the file. */ + linenr = 0; + while (fgets(buff, sizeof(buff), fp) != (char *)NULL) { + linenr++; + if (opt_v == 1) fprintf(stderr, ">> %s", buff); + if ((sp = strchr(buff, '\n')) != (char *)NULL) *sp = '\0'; + if (buff[0] == '#' || buff[0] == '\0') continue; + + argc = getargs(buff, args); + if (argc < 2) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_formaterr, + "arp: format error on line %u of etherfile %s !\n"), + linenr, name); + continue; + } + + if (arp_set(args) != 0) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_cant_set, + "arp: cannot set entry on line %u of etherfile %s !\n"), + linenr, name); + } + } + + (void) fclose(fp); + return(0); +} + + +/* Print the contents of an ARP request block. */ +static void +arp_disp_2(char *ip,int type,int arp_flags,char *hwa,char *mask,char *dev) +{ + static int title = 0; + struct hwtype *xhw; + struct aftype *xap; + char *sp; + struct sockaddr sap; + char flags[6]; + + xhw = get_hwntype(type); + if (xhw == NULL) + xhw = get_hwtype("ether"); +/* + * xap = get_afntype(req->arp_pa.sa_family); + * if (xap == NULL) + */ + xap = get_aftype("inet"); + + if (title++ == 0) { + printf(NLS_CATGETS(catfd, arpSet, arp_address, + "Address\t\t\tHWtype\tHWaddress\t Flags Mask\t\t Iface\n")); + } + /* Setup the flags. */ + flags[0] = '\0'; + if (arp_flags & ATF_COM) strcat(flags, "C"); + if (arp_flags & ATF_PERM) strcat(flags, "M"); + if (arp_flags & ATF_PUBL) strcat(flags, "P"); +/* if (arp_flags & ATF_RARP) strcat(flags, "R");*/ + if (arp_flags & ATF_USETRAILERS) strcat(flags, "T"); + + /* This IS ugly but it works -be */ + if (xap->input(0, ip,&sap) < 0) + sp=ip; + else + sp = xap->sprint(&sap, opt_n); + + printf("%-23.23s\t%-8.8s", sp, xhw->name); + printf("%-20.20s%-6.6s%-15.15s %s\n", hwa, flags,mask,dev); +} + + +/* Display the contents of the ARP cache in the kernel. */ +static int +arp_show(char *name) +{ + char host[100]; + struct sockaddr sa; + char ip[100]; + char hwa[100]; + char mask[100]; + char line[200]; + char dev[100]; + int type,flags; + FILE *fp; + int num,entries=0,showed=0; + + host[0]='\0'; + + if (name != NULL) { + /* Resolve the host name. */ + strcpy(host, name); + if (ap->input(0, host, &sa) < 0) { + ap->herror(host); + return(-1); + } + strcpy(host,ap->sprint(&sa, 1)); + } + + /* Open the PROCps kernel table. */ + if ((fp = fopen(_PATH_PROCNET_ARP, "r")) == NULL) { + perror(_PATH_PROCNET_ARP); + return(-1); + } + + /* Bypass header -- read until newline */ + if (fgets(line, sizeof(line), fp) != (char *)NULL) { + strcpy(mask,"-"); + strcpy(dev,"-"); + /* Read the ARP cache entries. */ + for(;fgets(line,sizeof(line),fp);) + { + num=sscanf(line,"%s 0x%x 0x%x %s %s %s\n", + ip,&type,&flags,hwa,mask,dev); + if(num<4) + break; + + entries++; + /* if the user specified hw-type differs, skip it */ + if (hw_set && (type != hw->type)) + continue; + + /* if the user specified address differs, skip it */ + if (host[0] && strcmp(ip,host)) + continue; + + /* if the user specified device differs, skip it */ + if (device[0] && strcmp(dev,device)) + continue; + showed++; + arp_disp_2(ip,type,flags,hwa,mask,dev); + } + } + if (opt_v) + printf(NLS_CATGETS(catfd, arpSet, arp_sum, + "Entries: %d\tSkiped: %d\tFound: %d\n"),entries,entries-showed,showed); + + if (!showed && (hw_set || host[0] || device[0])) + printf(NLS_CATGETS(catfd, arpSet, arp_none, + "arp: in %d entries no match found.\n"),entries); + (void) fclose(fp); + return(0); +} + +static void +version(void) +{ + fprintf(stderr, "%s\n%s\n%s\n",Release,Version,Features); + NLS_CATCLOSE(catfd) + exit(-1); +} + +static void +usage(void) +{ + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage1, + "Usage: arp [-vn] [-H type] [-i if] -a [hostname]\n")); + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage2, + " arp [-v] [-i if] -d hostname [pub]\n")); + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage3, + " arp [-v] [-H type] [-i if] -s hostname hw_addr [temp]\n")); + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage4, + " arp [-v] [-H type] [-i if] -s hostname hw_addr [netmask nm] pub\n")); + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage5, + " arp [-v] [-H type] [-i if] -Ds hostname if [netmask nm] pub\n")); + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_usage6, + " arp [-vnD] [-H type] [-i if] -f filename\n")); + NLS_CATCLOSE(catfd) + exit(-1); +} + +int +main(int argc, char **argv) +{ + int i, lop, what; + struct option longopts[]= + { + {"verbose", 0, 0, 'v'}, + {"version", 0, 0, 'V'}, + {"display", 0, 0, 'a'}, + {"delete", 0, 0, 'd'}, + {"file", 0, 0, 'f'}, + {"numeric", 0, 0, 'n'}, + {"set", 0, 0, 's'}, + {"protocol", 1, 0, 'A'}, + {"hw-type", 1, 0, 'H'}, + {"device", 0, 0, 'i'}, + {"help", 0, 0, 'h'}, + {"use-device", 0, 0, 'D'}, + {"symbolic", 0, 0, 'N'}, + {NULL, 0, 0, 0} + }; + +#if NLS + setlocale (LC_MESSAGES, ""); + catfd = catopen ("nettools", MCLoadBySet); +#endif + + /* Initialize variables... */ + if ((hw = get_hwtype(DFLT_HW)) == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_hw_not_supp, + "%s: hardware type not supported!\n"), DFLT_HW); + NLS_CATCLOSE(catfd) + return(-1); + } + if ((ap = get_aftype(DFLT_AF)) == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_fam_not_supp, + "%s: address family not supported!\n"), DFLT_AF); + NLS_CATCLOSE(catfd) + return(-1); + } + what = -1; + + /* Fetch the command-line arguments. */ + /* opterr = 0; */ + while ((i = getopt_long(argc, argv, "A:H:adfp:nsi:t:vh?DNV",longopts, &lop)) != EOF) switch(i) { + case 'a': + what = 1; + break; + + case 'd': + what = 3; + break; + + case 'f': + what = 2; + break; + + case 'n': + opt_n = FLAG_NUM; + break; + case 'D': + opt_D = 1; + break; + case 'N': + opt_N = FLAG_SYM; + fprintf(stderr,"arp: -N not yet supported.\n"); + break; + case 'A': + case 'p': + ap = get_aftype(optarg); + if (ap == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_unkn_addr, + "arp: %s: unknown address family.\n"), + optarg); + NLS_CATCLOSE(catfd) + exit(-1); + } + break; + + case 's': + what = 4; + break; + + case 'H': + case 't': + hw = get_hwtype(optarg); + if (hw == NULL) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_unkn_hw, + "arp: %s: unknown hardware type.\n"), + optarg); + NLS_CATCLOSE(catfd) + exit(-1); + } + hw_set = 1; + break; + case 'i': + strncpy(device,optarg,sizeof(device)-1); + device[sizeof(device)-1]='\0'; + break; + + case 'v': + opt_v = 1; + break; + + case 'V': + version(); + + case '?': + case 'h': + default: + usage(); + } + + if (ap->af != AF_INET) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_wrong_af, + "arp: %s: kernel only supports 'inet'.\n"), + ap->name); + NLS_CATCLOSE(catfd) + exit(-1); + } + if (hw->alen <= 0) { + fprintf(stderr, NLS_CATGETS(catfd, arpSet, arp_wrong_hw, + "arp: %s: hardware type without ARP support.\n"), + hw->name); + NLS_CATCLOSE(catfd) + exit(-1); + } + if ((sockfd = socket(AF_INET,SOCK_DGRAM,0)) <0) + { + perror("socket"); + NLS_CATCLOSE(catfd) + exit(-1); + } + + /* Now see what we have to do here... */ + switch(what) { + case 1: /* show an ARP entry in the cache */ + what = arp_show(argv[optind]); + break; + + case 2: /* process an EtherFile */ + what = arp_file(argv[optind]); + break; + + case 3: /* delete an ARP entry from the cache */ + what = arp_del(&argv[optind]); + break; + + case 4: /* set an ARP entry in the cache */ + what = arp_set(&argv[optind]); + break; + + default: + usage(); + } + + NLS_CATCLOSE(catfd) + exit(what); +} |