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