]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/plugins/pppoe/if.c
Rename rp-pppoe.so plugin to pppoe.so
[ppp.git] / pppd / plugins / pppoe / if.c
diff --git a/pppd/plugins/pppoe/if.c b/pppd/plugins/pppoe/if.c
new file mode 100644 (file)
index 0000000..225dd56
--- /dev/null
@@ -0,0 +1,242 @@
+/***********************************************************************
+*
+* if.c
+*
+* Implementation of user-space PPPoE redirector for Linux.
+*
+* Functions for opening a raw socket and reading/writing raw Ethernet frames.
+*
+* Copyright (C) 2000 by Roaring Penguin Software Inc.
+*
+* This program may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: if.c,v 1.2 2008/06/09 08:34:23 paulus Exp $";
+
+#define _GNU_SOURCE 1
+#include "pppoe.h"
+#include "pppd/pppd.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_NETPACKET_PACKET_H
+#include <netpacket/packet.h>
+#elif defined(HAVE_LINUX_IF_PACKET_H)
+#include <linux/if_packet.h>
+#endif
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_NET_IF_ARP_H
+#include <net/if_arp.h>
+#endif
+
+/* Initialize frame types to RFC 2516 values.  Some broken peers apparently
+   use different frame types... sigh... */
+
+UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
+UINT16_t Eth_PPPOE_Session   = ETH_PPPOE_SESSION;
+
+/**********************************************************************
+*%FUNCTION: etherType
+*%ARGUMENTS:
+* packet -- a received PPPoE packet
+*%RETURNS:
+* ethernet packet type (see /usr/include/net/ethertypes.h)
+*%DESCRIPTION:
+* Checks the ethernet packet header to determine its type.
+* We should only be receveing DISCOVERY and SESSION types if the BPF
+* is set up correctly.  Logs an error if an unexpected type is received.
+* Note that the ethernet type names come from "pppoe.h" and the packet
+* packet structure names use the LINUX dialect to maintain consistency
+* with the rest of this file.  See the BSD section of "pppoe.h" for
+* translations of the data structure names.
+***********************************************************************/
+UINT16_t
+etherType(PPPoEPacket *packet)
+{
+    UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
+    if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
+       error("Invalid ether type 0x%x", type);
+    }
+    return type;
+}
+
+/**********************************************************************
+*%FUNCTION: openInterface
+*%ARGUMENTS:
+* ifname -- name of interface
+* type -- Ethernet frame type
+* hwaddr -- if non-NULL, set to the hardware address
+*%RETURNS:
+* A raw socket for talking to the Ethernet card.  Exits on error.
+*%DESCRIPTION:
+* Opens a raw Ethernet socket
+***********************************************************************/
+int
+openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
+{
+    int optval=1;
+    int fd;
+    struct ifreq ifr;
+    int domain, stype;
+
+#ifdef HAVE_STRUCT_SOCKADDR_LL
+    struct sockaddr_ll sa;
+#else
+    struct sockaddr sa;
+#endif
+
+    memset(&sa, 0, sizeof(sa));
+
+#ifdef HAVE_STRUCT_SOCKADDR_LL
+    domain = PF_PACKET;
+    stype = SOCK_RAW;
+#else
+    domain = PF_INET;
+    stype = SOCK_PACKET;
+#endif
+
+    if ((fd = socket(domain, stype, htons(type))) < 0) {
+       /* Give a more helpful message for the common error case */
+       if (errno == EPERM) {
+           fatal("Cannot create raw socket -- pppoe must be run as root.");
+       }
+       error("Can't open socket for pppoe: %m");
+       return -1;
+    }
+
+    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
+       error("Can't set socket options for pppoe: %m");
+       close(fd);
+       return -1;
+    }
+
+    /* Fill in hardware address */
+    if (hwaddr) {
+       strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+           error("Can't get hardware address for %s: %m", ifname);
+           close(fd);
+           return -1;
+       }
+       memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+#ifdef ARPHRD_ETHER
+       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+           warn("Interface %.16s is not Ethernet", ifname);
+       }
+#endif
+       if (NOT_UNICAST(hwaddr)) {
+           fatal("Can't use interface %.16s: it has broadcast/multicast MAC address",
+                 ifname);
+       }
+    }
+
+    /* Sanity check on MTU */
+    strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+    if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
+       error("Can't get MTU for %s: %m", ifname);
+    } else if (ifr.ifr_mtu < ETH_DATA_LEN) {
+       error("Interface %.16s has MTU of %d -- should be at least %d.",
+             ifname, ifr.ifr_mtu, ETH_DATA_LEN);
+       error("This may cause serious connection problems.");
+    }
+
+#ifdef HAVE_STRUCT_SOCKADDR_LL
+    /* Get interface index */
+    sa.sll_family = AF_PACKET;
+    sa.sll_protocol = htons(type);
+
+    strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+    if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
+       error("Could not get interface index for %s: %m", ifname);
+       close(fd);
+       return -1;
+    }
+    sa.sll_ifindex = ifr.ifr_ifindex;
+
+#else
+    strcpy(sa.sa_data, ifname);
+#endif
+
+    /* We're only interested in packets on specified interface */
+    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+       error("Failed to bind to interface %s: %m", ifname);
+       close(fd);
+       return -1;
+    }
+
+    return fd;
+}
+
+
+/***********************************************************************
+*%FUNCTION: sendPacket
+*%ARGUMENTS:
+* sock -- socket to send to
+* pkt -- the packet to transmit
+* size -- size of packet (in bytes)
+*%RETURNS:
+* 0 on success; -1 on failure
+*%DESCRIPTION:
+* Transmits a packet
+***********************************************************************/
+int
+sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
+{
+    int err;
+
+    if (debug)
+       pppoe_log_packet("Send ", pkt);
+#if defined(HAVE_STRUCT_SOCKADDR_LL)
+    err = send(sock, pkt, size, 0);
+#else
+    struct sockaddr sa;
+
+    strcpy(sa.sa_data, conn->ifName);
+    err = sendto(sock, pkt, size, 0, &sa, sizeof(sa));
+#endif
+    if (err < 0) {
+       error("error sending pppoe packet: %m");
+       return -1;
+    }
+    return 0;
+}
+
+/***********************************************************************
+*%FUNCTION: receivePacket
+*%ARGUMENTS:
+* sock -- socket to read from
+* pkt -- place to store the received packet
+* size -- set to size of packet in bytes
+*%RETURNS:
+* >= 0 if all OK; < 0 if error
+*%DESCRIPTION:
+* Receives a packet
+***********************************************************************/
+int
+receivePacket(int sock, PPPoEPacket *pkt, int *size)
+{
+    if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
+       error("error receiving pppoe packet: %m");
+       return -1;
+    }
+    if (debug)
+       pppoe_log_packet("Recv ", pkt);
+    return 0;
+}