• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
etherType(PPPoEPacket * packet)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
openInterface(char const * ifname,UINT16_t type,unsigned char * hwaddr)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
sendPacket(PPPoEConnection * conn,int sock,PPPoEPacket * pkt,int size)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
receivePacket(int sock,PPPoEPacket * pkt,int * size)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