if.c revision 1286c078a4b93695b3812e8c7fe7918c28ea18b4
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
16static 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
56UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
57UINT16_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***********************************************************************/
74UINT16_t
75etherType(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***********************************************************************/
95int
96openInterface(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***********************************************************************/
203int
204sendPacket(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***********************************************************************/
236int
237receivePacket(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