Home | History | Annotate | Download | only in rp-pppoe
      1 /***********************************************************************
      2 *
      3 * if.c
      4 *
      5 * Implementation of user-space PPPoE redirector for Linux.
      6 *
      7 * Functions for opening a raw socket and reading/writing raw Ethernet frames.
      8 *
      9 * Copyright (C) 2000 by Roaring Penguin Software Inc.
     10 *
     11 * This program may be distributed according to the terms of the GNU
     12 * General Public License, version 2 or (at your option) any later version.
     13 *
     14 ***********************************************************************/
     15 
     16 static char const RCSID[] =
     17 "$Id: if.c,v 1.2 2008/06/09 08:34:23 paulus Exp $";
     18 
     19 #define _GNU_SOURCE 1
     20 #include "pppoe.h"
     21 #include "pppd/pppd.h"
     22 
     23 #ifdef HAVE_UNISTD_H
     24 #include <unistd.h>
     25 #endif
     26 
     27 #ifdef HAVE_NETPACKET_PACKET_H
     28 #include <netpacket/packet.h>
     29 #elif defined(HAVE_LINUX_IF_PACKET_H)
     30 #include <linux/if_packet.h>
     31 #endif
     32 
     33 #ifdef HAVE_NET_ETHERNET_H
     34 #include <net/ethernet.h>
     35 #endif
     36 
     37 #ifdef HAVE_ASM_TYPES_H
     38 #include <asm/types.h>
     39 #endif
     40 
     41 #ifdef HAVE_SYS_IOCTL_H
     42 #include <sys/ioctl.h>
     43 #endif
     44 
     45 #include <errno.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 
     49 #ifdef HAVE_NET_IF_ARP_H
     50 #include <net/if_arp.h>
     51 #endif
     52 
     53 /* Initialize frame types to RFC 2516 values.  Some broken peers apparently
     54    use different frame types... sigh... */
     55 
     56 UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
     57 UINT16_t Eth_PPPOE_Session   = ETH_PPPOE_SESSION;
     58 
     59 /**********************************************************************
     60 *%FUNCTION: etherType
     61 *%ARGUMENTS:
     62 * packet -- a received PPPoE packet
     63 *%RETURNS:
     64 * ethernet packet type (see /usr/include/net/ethertypes.h)
     65 *%DESCRIPTION:
     66 * Checks the ethernet packet header to determine its type.
     67 * We should only be receveing DISCOVERY and SESSION types if the BPF
     68 * is set up correctly.  Logs an error if an unexpected type is received.
     69 * Note that the ethernet type names come from "pppoe.h" and the packet
     70 * packet structure names use the LINUX dialect to maintain consistency
     71 * with the rest of this file.  See the BSD section of "pppoe.h" for
     72 * translations of the data structure names.
     73 ***********************************************************************/
     74 UINT16_t
     75 etherType(PPPoEPacket *packet)
     76 {
     77     UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
     78     if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
     79 	error("Invalid ether type 0x%x", type);
     80     }
     81     return type;
     82 }
     83 
     84 /**********************************************************************
     85 *%FUNCTION: openInterface
     86 *%ARGUMENTS:
     87 * ifname -- name of interface
     88 * type -- Ethernet frame type
     89 * hwaddr -- if non-NULL, set to the hardware address
     90 *%RETURNS:
     91 * A raw socket for talking to the Ethernet card.  Exits on error.
     92 *%DESCRIPTION:
     93 * Opens a raw Ethernet socket
     94 ***********************************************************************/
     95 int
     96 openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
     97 {
     98     int optval=1;
     99     int fd;
    100     struct ifreq ifr;
    101     int domain, stype;
    102 
    103 #ifdef HAVE_STRUCT_SOCKADDR_LL
    104     struct sockaddr_ll sa;
    105 #else
    106     struct sockaddr sa;
    107 #endif
    108 
    109     memset(&sa, 0, sizeof(sa));
    110 
    111 #ifdef HAVE_STRUCT_SOCKADDR_LL
    112     domain = PF_PACKET;
    113     stype = SOCK_RAW;
    114 #else
    115     domain = PF_INET;
    116     stype = SOCK_PACKET;
    117 #endif
    118 
    119     if ((fd = socket(domain, stype, htons(type))) < 0) {
    120 	/* Give a more helpful message for the common error case */
    121 	if (errno == EPERM) {
    122 	    fatal("Cannot create raw socket -- pppoe must be run as root.");
    123 	}
    124 	error("Can't open socket for pppoe: %m");
    125 	return -1;
    126     }
    127 
    128     if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
    129 	error("Can't set socket options for pppoe: %m");
    130 	close(fd);
    131 	return -1;
    132     }
    133 
    134     /* Fill in hardware address */
    135     if (hwaddr) {
    136 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
    137 	if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
    138 	    error("Can't get hardware address for %s: %m", ifname);
    139 	    close(fd);
    140 	    return -1;
    141 	}
    142 	memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
    143 #ifdef ARPHRD_ETHER
    144 	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
    145 	    warn("Interface %.16s is not Ethernet", ifname);
    146 	}
    147 #endif
    148 	if (NOT_UNICAST(hwaddr)) {
    149 	    fatal("Can't use interface %.16s: it has broadcast/multicast MAC address",
    150 		  ifname);
    151 	}
    152     }
    153 
    154     /* Sanity check on MTU */
    155     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
    156     if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
    157 	error("Can't get MTU for %s: %m", ifname);
    158     } else if (ifr.ifr_mtu < ETH_DATA_LEN) {
    159 	error("Interface %.16s has MTU of %d -- should be at least %d.",
    160 	      ifname, ifr.ifr_mtu, ETH_DATA_LEN);
    161 	error("This may cause serious connection problems.");
    162     }
    163 
    164 #ifdef HAVE_STRUCT_SOCKADDR_LL
    165     /* Get interface index */
    166     sa.sll_family = AF_PACKET;
    167     sa.sll_protocol = htons(type);
    168 
    169     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
    170     if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
    171 	error("Could not get interface index for %s: %m", ifname);
    172 	close(fd);
    173 	return -1;
    174     }
    175     sa.sll_ifindex = ifr.ifr_ifindex;
    176 
    177 #else
    178     strcpy(sa.sa_data, ifname);
    179 #endif
    180 
    181     /* We're only interested in packets on specified interface */
    182     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
    183 	error("Failed to bind to interface %s: %m", ifname);
    184 	close(fd);
    185 	return -1;
    186     }
    187 
    188     return fd;
    189 }
    190 
    191 
    192 /***********************************************************************
    193 *%FUNCTION: sendPacket
    194 *%ARGUMENTS:
    195 * sock -- socket to send to
    196 * pkt -- the packet to transmit
    197 * size -- size of packet (in bytes)
    198 *%RETURNS:
    199 * 0 on success; -1 on failure
    200 *%DESCRIPTION:
    201 * Transmits a packet
    202 ***********************************************************************/
    203 int
    204 sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
    205 {
    206     int err;
    207 
    208     if (debug)
    209 	pppoe_log_packet("Send ", pkt);
    210 #if defined(HAVE_STRUCT_SOCKADDR_LL)
    211     err = send(sock, pkt, size, 0);
    212 #else
    213     struct sockaddr sa;
    214 
    215     strcpy(sa.sa_data, conn->ifName);
    216     err = sendto(sock, pkt, size, 0, &sa, sizeof(sa));
    217 #endif
    218     if (err < 0) {
    219 	error("error sending pppoe packet: %m");
    220 	return -1;
    221     }
    222     return 0;
    223 }
    224 
    225 /***********************************************************************
    226 *%FUNCTION: receivePacket
    227 *%ARGUMENTS:
    228 * sock -- socket to read from
    229 * pkt -- place to store the received packet
    230 * size -- set to size of packet in bytes
    231 *%RETURNS:
    232 * >= 0 if all OK; < 0 if error
    233 *%DESCRIPTION:
    234 * Receives a packet
    235 ***********************************************************************/
    236 int
    237 receivePacket(int sock, PPPoEPacket *pkt, int *size)
    238 {
    239     if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
    240 	error("error receiving pppoe packet: %m");
    241 	return -1;
    242     }
    243     if (debug)
    244 	pppoe_log_packet("Recv ", pkt);
    245     return 0;
    246 }
    247