diff options
Diffstat (limited to 'libnet/src/libnet_link_linux.c')
-rw-r--r-- | libnet/src/libnet_link_linux.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/libnet/src/libnet_link_linux.c b/libnet/src/libnet_link_linux.c new file mode 100644 index 0000000..d0bbf9a --- /dev/null +++ b/libnet/src/libnet_link_linux.c @@ -0,0 +1,295 @@ +/* + * $Id: libnet_link_linux.c,v 1.5 2004/01/03 20:31:02 mike Exp $ + * + * libnet 1.1 + * libnet_link_linux.c - linux packet socket and pack socket routines + * + * Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if (HAVE_CONFIG_H) +#include "../include/config.h" +#endif +#include <sys/time.h> + +#include <net/if.h> +#if (__GLIBC__) +#include <netinet/if_ether.h> +#include <net/if_arp.h> +#else +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#endif + +#if (HAVE_PACKET_SOCKET) +#ifndef SOL_PACKET +#define SOL_PACKET 263 +#endif /* SOL_PACKET */ +#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1 +#include <netpacket/packet.h> +#include <net/ethernet.h> /* the L2 protocols */ +#else +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#endif +#endif /* HAVE_PACKET_SOCKET */ + +#include "../include/bpf.h" +#include "../include/libnet.h" + +#include "../include/gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "../include/os-proto.h" +#endif + + +int +libnet_open_link(libnet_t *l) +{ + struct ifreq ifr; + int n = 1; + + if (l == NULL) + { + return (-1); + } + +#if (HAVE_PACKET_SOCKET) + l->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); +#else + l->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); +#endif + if (l->fd == -1) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "socket: %s", strerror(errno)); + goto bad; + } + + memset(&ifr, 0, sizeof (ifr)); + strncpy(ifr.ifr_name, l->device, sizeof (ifr.ifr_name) -1); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; + + if (ioctl(l->fd, SIOCGIFHWADDR, &ifr) < 0 ) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "SIOCGIFHWADDR: %s", strerror(errno)); + goto bad; + } + + switch (ifr.ifr_hwaddr.sa_family) + { + case ARPHRD_ETHER: + case ARPHRD_METRICOM: +#ifdef ARPHRD_LOOPBACK + case ARPHRD_LOOPBACK: +#endif + l->link_type = DLT_EN10MB; + l->link_offset = 0xe; + break; + case ARPHRD_SLIP: + case ARPHRD_CSLIP: + case ARPHRD_SLIP6: + case ARPHRD_CSLIP6: + case ARPHRD_PPP: + l->link_type = DLT_RAW; + break; + case ARPHRD_FDDI: + l->link_type = DLT_FDDI; + l->link_offset = 0x15; + break; + /* Token Ring */ + case ARPHRD_IEEE802: + case ARPHRD_IEEE802_TR: + case ARPHRD_PRONET: + l->link_type = DLT_PRONET; + l->link_offset = 0x16; + break; + + default: + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "unknown physical layer type 0x%x\n", + ifr.ifr_hwaddr.sa_family); + goto bad; + } +#ifdef SO_BROADCAST +/* + * man 7 socket + * + * Set or get the broadcast flag. When enabled, datagram sockets + * receive packets sent to a broadcast address and they are allowed + * to send packets to a broadcast address. This option has no + * effect on stream-oriented sockets. + */ + if (setsockopt(l->fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n)) == -1) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s: set SO_BROADCAST failed: %s\n", + __func__, strerror(errno)); + goto bad; + } +#endif /* SO_BROADCAST */ + + return (1); + +bad: + if (l->fd >= 0) + { + close(l->fd); + } + return (-1); +} + + +int +libnet_close_link(libnet_t *l) +{ + if (close(l->fd) == 0) + { + return (1); + } + else + { + return (-1); + } +} + + +#if (HAVE_PACKET_SOCKET) +static int +get_iface_index(int fd, const int8_t *device) +{ + struct ifreq ifr; + + /* memset(&ifr, 0, sizeof(ifr)); */ + strncpy (ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1); + ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0'; + + if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) + { + return (-1); + } + + return ifr.ifr_ifindex; +} +#endif + + +int +libnet_write_link(libnet_t *l, u_int8_t *packet, u_int32_t size) +{ + int c; +#if (HAVE_PACKET_SOCKET) + struct sockaddr_ll sa; +#else + struct sockaddr sa; +#endif + + if (l == NULL) + { + return (-1); + } + + memset(&sa, 0, sizeof (sa)); +#if (HAVE_PACKET_SOCKET) + sa.sll_family = AF_PACKET; + sa.sll_ifindex = get_iface_index(l->fd, l->device); + if (sa.sll_ifindex == -1) + { + return (-1); + } + sa.sll_protocol = htons(ETH_P_ALL); +#else + strncpy(sa.sa_data, l->device, sizeof (sa.sa_data) - 1); + sa.sa_data[sizeof (sa.sa_data) - 1] = 0; +#endif + + c = sendto(l->fd, packet, size, 0, + (struct sockaddr *)&sa, sizeof (sa)); + if (c != size) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "libnet_write_link(): only %d bytes written (%s)\n", c, + strerror(errno)); + } + return (c); +} + + +struct libnet_ether_addr * +libnet_get_hwaddr(libnet_t *l) +{ + int fd; + struct ifreq ifr; + struct libnet_ether_addr *eap; + /* + * XXX - non-re-entrant! + */ + static struct libnet_ether_addr ea; + + if (l == NULL) + { + return (NULL); + } + + if (l->device == NULL) + { + if (libnet_select_device(l) == -1) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "libnet_get_hwaddr: can't figure out a device to use\n"); + return (NULL); + } + } + + /* + * Create dummy socket to perform an ioctl upon. + */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "socket: %s", strerror(errno)); + goto bad; + } + + memset(&ifr, 0, sizeof(ifr)); + eap = &ea; + strncpy(ifr.ifr_name, l->device, sizeof(ifr.ifr_name) - 1); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; + + if (ioctl(fd, SIOCGIFHWADDR, (int8_t *)&ifr) < 0) + { + close(fd); + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "ioctl: %s", strerror(errno)); + goto bad; + } + memcpy(eap, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); + close(fd); + return (eap); + +bad: + return (NULL); +} + + +/* EOF */ |