You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.5 KiB
247 lines
6.5 KiB
/***********************************************************************
|
|
*
|
|
* 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_NET_ETHERNET_H
|
|
#include <net/ethernet.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) {
|
|
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
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 */
|
|
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
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);
|
|
|
|
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
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;
|
|
}
|