diff options
Diffstat (limited to 'libdnet-stripped/src/arp-ioctl.c')
-rw-r--r-- | libdnet-stripped/src/arp-ioctl.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/libdnet-stripped/src/arp-ioctl.c b/libdnet-stripped/src/arp-ioctl.c new file mode 100644 index 0000000..31b9f8c --- /dev/null +++ b/libdnet-stripped/src/arp-ioctl.c @@ -0,0 +1,489 @@ +/* + * arp-ioctl.c + * + * Copyright (c) 2001 Dug Song <dugsong@monkey.org> + * + * $Id: arp-ioctl.c 554 2005-02-09 22:31:00Z dugsong $ + */ + +#include "config.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef HAVE_STREAMS_MIB2 +# include <sys/sockio.h> +# include <sys/stream.h> +# include <sys/tihdr.h> +# include <sys/tiuser.h> +# include <inet/common.h> +# include <inet/mib2.h> +# include <inet/ip.h> +# undef IP_ADDR_LEN +#elif defined(HAVE_SYS_MIB_H) +# include <sys/mib.h> +#endif + +#include <net/if.h> +#include <net/if_arp.h> +#ifdef HAVE_STREAMS_MIB2 +# include <netinet/in.h> +# include <stropts.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dnet.h" + +#ifdef HAVE_LINUX_PROCFS +#define PROC_ARP_FILE "/proc/net/arp" +#endif + +struct arp_handle { + int fd; +#ifdef HAVE_ARPREQ_ARP_DEV + intf_t *intf; +#endif +}; + +arp_t * +arp_open(void) +{ + arp_t *a; + + if ((a = calloc(1, sizeof(*a))) != NULL) { +#ifdef HAVE_STREAMS_MIB2 + if ((a->fd = open(IP_DEV_NAME, O_RDWR)) < 0) +#elif defined(HAVE_STREAMS_ROUTE) + if ((a->fd = open("/dev/route", O_WRONLY, 0)) < 0) +#else + if ((a->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) +#endif + return (arp_close(a)); +#ifdef HAVE_ARPREQ_ARP_DEV + if ((a->intf = intf_open()) == NULL) + return (arp_close(a)); +#endif + } + return (a); +} + +#ifdef HAVE_ARPREQ_ARP_DEV +static int +_arp_set_dev(const struct intf_entry *entry, void *arg) +{ + struct arpreq *ar = (struct arpreq *)arg; + struct addr dst; + uint32_t mask; + + if (entry->intf_type == INTF_TYPE_ETH && + entry->intf_addr.addr_type == ADDR_TYPE_IP) { + addr_btom(entry->intf_addr.addr_bits, &mask, IP_ADDR_LEN); + addr_ston((struct sockaddr *)&ar->arp_pa, &dst); + + if ((entry->intf_addr.addr_ip & mask) == + (dst.addr_ip & mask)) { + strlcpy(ar->arp_dev, entry->intf_name, + sizeof(ar->arp_dev)); + return (1); + } + } + return (0); +} +#endif + +int +arp_add(arp_t *a, const struct arp_entry *entry) +{ + struct arpreq ar; + + memset(&ar, 0, sizeof(ar)); + + if (addr_ntos(&entry->arp_pa, &ar.arp_pa) < 0) + return (-1); + + /* XXX - see arp(7) for details... */ +#ifdef __linux__ + if (addr_ntos(&entry->arp_ha, &ar.arp_ha) < 0) + return (-1); + ar.arp_ha.sa_family = ARP_HRD_ETH; +#else + /* XXX - Solaris, HP-UX, IRIX, other Mentat stacks? */ + ar.arp_ha.sa_family = AF_UNSPEC; + memcpy(ar.arp_ha.sa_data, &entry->arp_ha.addr_eth, ETH_ADDR_LEN); +#endif + +#ifdef HAVE_ARPREQ_ARP_DEV + if (intf_loop(a->intf, _arp_set_dev, &ar) != 1) { + errno = ESRCH; + return (-1); + } +#endif + ar.arp_flags = ATF_PERM | ATF_COM; +#ifdef hpux + /* XXX - screwy extended arpreq struct */ + { + struct sockaddr_in *sin; + + ar.arp_hw_addr_len = ETH_ADDR_LEN; + sin = (struct sockaddr_in *)&ar.arp_pa_mask; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = IP_ADDR_BROADCAST; + } +#endif + if (ioctl(a->fd, SIOCSARP, &ar) < 0) + return (-1); + +#ifdef HAVE_STREAMS_MIB2 + /* XXX - force entry into ipNetToMediaTable. */ + { + struct sockaddr_in sin; + int fd; + + addr_ntos(&entry->arp_pa, (struct sockaddr *)&sin); + sin.sin_port = htons(666); + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return (-1); + + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + close(fd); + return (-1); + } + write(fd, NULL, 0); + close(fd); + } +#endif + return (0); +} + +int +arp_delete(arp_t *a, const struct arp_entry *entry) +{ + struct arpreq ar; + + memset(&ar, 0, sizeof(ar)); + + if (addr_ntos(&entry->arp_pa, &ar.arp_pa) < 0) + return (-1); + + if (ioctl(a->fd, SIOCDARP, &ar) < 0) + return (-1); + + return (0); +} + +int +arp_get(arp_t *a, struct arp_entry *entry) +{ + struct arpreq ar; + + memset(&ar, 0, sizeof(ar)); + + if (addr_ntos(&entry->arp_pa, &ar.arp_pa) < 0) + return (-1); + +#ifdef HAVE_ARPREQ_ARP_DEV + if (intf_loop(a->intf, _arp_set_dev, &ar) != 1) { + errno = ESRCH; + return (-1); + } +#endif + if (ioctl(a->fd, SIOCGARP, &ar) < 0) + return (-1); + + if ((ar.arp_flags & ATF_COM) == 0) { + errno = ESRCH; + return (-1); + } + return (addr_ston(&ar.arp_ha, &entry->arp_ha)); +} + +#ifdef HAVE_LINUX_PROCFS +int +arp_loop(arp_t *a, arp_handler callback, void *arg) +{ + FILE *fp; + struct arp_entry entry; + char buf[BUFSIZ], ipbuf[100], macbuf[100], maskbuf[100], devbuf[100]; + int i, type, flags, ret; + + if ((fp = fopen(PROC_ARP_FILE, "r")) == NULL) + return (-1); + + ret = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + i = sscanf(buf, "%s 0x%x 0x%x %99s %99s %99s\n", + ipbuf, &type, &flags, macbuf, maskbuf, devbuf); + + if (i < 4 || (flags & ATF_COM) == 0) + continue; + + if (addr_aton(ipbuf, &entry.arp_pa) == 0 && + addr_aton(macbuf, &entry.arp_ha) == 0) { + if ((ret = callback(&entry, arg)) != 0) + break; + } + } + if (ferror(fp)) { + fclose(fp); + return (-1); + } + fclose(fp); + + return (ret); +} +#elif defined (HAVE_STREAMS_MIB2) +int +arp_loop(arp_t *r, arp_handler callback, void *arg) +{ + struct arp_entry entry; + struct strbuf msg; + struct T_optmgmt_req *tor; + struct T_optmgmt_ack *toa; + struct T_error_ack *tea; + struct opthdr *opt; + mib2_ipNetToMediaEntry_t *arp, *arpend; + u_char buf[8192]; + int flags, rc, atable, ret; + + tor = (struct T_optmgmt_req *)buf; + toa = (struct T_optmgmt_ack *)buf; + tea = (struct T_error_ack *)buf; + + tor->PRIM_type = T_OPTMGMT_REQ; + tor->OPT_offset = sizeof(*tor); + tor->OPT_length = sizeof(*opt); + tor->MGMT_flags = T_CURRENT; + + opt = (struct opthdr *)(tor + 1); + opt->level = MIB2_IP; + opt->name = opt->len = 0; + + msg.maxlen = sizeof(buf); + msg.len = sizeof(*tor) + sizeof(*opt); + msg.buf = buf; + + if (putmsg(r->fd, &msg, NULL, 0) < 0) + return (-1); + + opt = (struct opthdr *)(toa + 1); + msg.maxlen = sizeof(buf); + + for (;;) { + flags = 0; + if ((rc = getmsg(r->fd, &msg, NULL, &flags)) < 0) + return (-1); + + /* See if we're finished. */ + if (rc == 0 && + msg.len >= sizeof(*toa) && + toa->PRIM_type == T_OPTMGMT_ACK && + toa->MGMT_flags == T_SUCCESS && opt->len == 0) + break; + + if (msg.len >= sizeof(*tea) && tea->PRIM_type == T_ERROR_ACK) + return (-1); + + if (rc != MOREDATA || msg.len < (int)sizeof(*toa) || + toa->PRIM_type != T_OPTMGMT_ACK || + toa->MGMT_flags != T_SUCCESS) + return (-1); + + atable = (opt->level == MIB2_IP && opt->name == MIB2_IP_22); + + msg.maxlen = sizeof(buf) - (sizeof(buf) % sizeof(*arp)); + msg.len = 0; + flags = 0; + + do { + rc = getmsg(r->fd, NULL, &msg, &flags); + + if (rc != 0 && rc != MOREDATA) + return (-1); + + if (!atable) + continue; + + arp = (mib2_ipNetToMediaEntry_t *)msg.buf; + arpend = (mib2_ipNetToMediaEntry_t *) + (msg.buf + msg.len); + + entry.arp_pa.addr_type = ADDR_TYPE_IP; + entry.arp_pa.addr_bits = IP_ADDR_BITS; + + entry.arp_ha.addr_type = ADDR_TYPE_ETH; + entry.arp_ha.addr_bits = ETH_ADDR_BITS; + + for ( ; arp < arpend; arp++) { + entry.arp_pa.addr_ip = + arp->ipNetToMediaNetAddress; + + memcpy(&entry.arp_ha.addr_eth, + arp->ipNetToMediaPhysAddress.o_bytes, + ETH_ADDR_LEN); + + if ((ret = callback(&entry, arg)) != 0) + return (ret); + } + } while (rc == MOREDATA); + } + return (0); +} +#elif defined(HAVE_SYS_MIB_H) +#define MAX_ARPENTRIES 512 /* XXX */ + +int +arp_loop(arp_t *r, arp_handler callback, void *arg) +{ + struct nmparms nm; + struct arp_entry entry; + mib_ipNetToMediaEnt arpentries[MAX_ARPENTRIES]; + int fd, i, n, ret; + + if ((fd = open_mib("/dev/ip", O_RDWR, 0 /* XXX */, 0)) < 0) + return (-1); + + nm.objid = ID_ipNetToMediaTable; + nm.buffer = arpentries; + n = sizeof(arpentries); + nm.len = &n; + + if (get_mib_info(fd, &nm) < 0) { + close_mib(fd); + return (-1); + } + close_mib(fd); + + entry.arp_pa.addr_type = ADDR_TYPE_IP; + entry.arp_pa.addr_bits = IP_ADDR_BITS; + + entry.arp_ha.addr_type = ADDR_TYPE_ETH; + entry.arp_ha.addr_bits = ETH_ADDR_BITS; + + n /= sizeof(*arpentries); + ret = 0; + + for (i = 0; i < n; i++) { + if (arpentries[i].Type == INTM_INVALID || + arpentries[i].PhysAddr.o_length != ETH_ADDR_LEN) + continue; + + entry.arp_pa.addr_ip = arpentries[i].NetAddr; + memcpy(&entry.arp_ha.addr_eth, arpentries[i].PhysAddr.o_bytes, + ETH_ADDR_LEN); + + if ((ret = callback(&entry, arg)) != 0) + break; + } + return (ret); +} +#elif defined(HAVE_NET_RADIX_H) && !defined(_AIX) +/* XXX - Tru64, others? */ +#include <netinet/if_ether.h> +#include <nlist.h> + +static int +_kread(int fd, void *addr, void *buf, int len) +{ + if (lseek(fd, (off_t)addr, SEEK_SET) == (off_t)-1L) + return (-1); + return (read(fd, buf, len) == len ? 0 : -1); +} + +static int +_radix_walk(int fd, struct radix_node *rn, arp_handler callback, void *arg) +{ + struct radix_node rnode; + struct rtentry rt; + struct sockaddr_in sin; + struct arptab at; + struct arp_entry entry; + int ret = 0; + again: + _kread(fd, rn, &rnode, sizeof(rnode)); + if (rnode.rn_b < 0) { + if (!(rnode.rn_flags & RNF_ROOT)) { + _kread(fd, rn, &rt, sizeof(rt)); + _kread(fd, rt_key(&rt), &sin, sizeof(sin)); + addr_ston((struct sockaddr *)&sin, &entry.arp_pa); + _kread(fd, rt.rt_llinfo, &at, sizeof(at)); + if (at.at_flags & ATF_COM) { + addr_pack(&entry.arp_ha, ADDR_TYPE_ETH, + ETH_ADDR_BITS, at.at_hwaddr, ETH_ADDR_LEN); + if ((ret = callback(&entry, arg)) != 0) + return (ret); + } + } + if ((rn = rnode.rn_dupedkey)) + goto again; + } else { + rn = rnode.rn_r; + if ((ret = _radix_walk(fd, rnode.rn_l, callback, arg)) != 0) + return (ret); + if ((ret = _radix_walk(fd, rn, callback, arg)) != 0) + return (ret); + } + return (ret); +} + +int +arp_loop(arp_t *r, arp_handler callback, void *arg) +{ + struct ifnet *ifp, ifnet; + struct ifnet_arp_cache_head ifarp; + struct radix_node_head *head; + + struct nlist nl[2]; + int fd, ret = 0; + + memset(nl, 0, sizeof(nl)); + nl[0].n_name = "ifnet"; + + if (knlist(nl) < 0 || nl[0].n_type == 0 || + (fd = open("/dev/kmem", O_RDONLY, 0)) < 0) + return (-1); + + for (ifp = (struct ifnet *)nl[0].n_value; + ifp != NULL; ifp = ifnet.if_next) { + _kread(fd, ifp, &ifnet, sizeof(ifnet)); + if (ifnet.if_arp_cache_head != NULL) { + _kread(fd, ifnet.if_arp_cache_head, + &ifarp, sizeof(ifarp)); + /* XXX - only ever one rnh, only ever AF_INET. */ + if ((ret = _radix_walk(fd, ifarp.arp_cache_head.rnh_treetop, + callback, arg)) != 0) + break; + } + } + close(fd); + return (ret); +} +#else +int +arp_loop(arp_t *a, arp_handler callback, void *arg) +{ + errno = ENOSYS; + return (-1); +} +#endif + +arp_t * +arp_close(arp_t *a) +{ + if (a != NULL) { + if (a->fd >= 0) + close(a->fd); +#ifdef HAVE_ARPREQ_ARP_DEV + if (a->intf != NULL) + intf_close(a->intf); +#endif + free(a); + } + return (NULL); +} |