summaryrefslogtreecommitdiff
path: root/src/if.c (plain)
blob: d076b698595aa9b3fd8aba9a8630850c5d023cda
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* LIC: GPL
15*
16***********************************************************************/
17
18static char const RCSID[] =
19"$Id$";
20
21#include "pppoe.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#ifdef HAVE_SYSLOG_H
46#include <android/log.h>
47#include <syslog.h>
48#define syslog(prio, fmt...) \
49 __android_log_print(prio, "PPPOE", fmt)
50#endif
51
52#include <errno.h>
53#include <stdlib.h>
54#include <string.h>
55
56#ifdef HAVE_NET_IF_ARP_H
57#include <net/if_arp.h>
58#endif
59
60#ifdef USE_DLPI
61
62#include <limits.h>
63#include <fcntl.h>
64#include <stdlib.h>
65#include <sys/types.h>
66#include <sys/time.h>
67#include <sys/stream.h>
68#include <sys/stropts.h>
69#include <sys/dlpi.h>
70#include <sys/bufmod.h>
71#include <stdio.h>
72#include <signal.h>
73#include <stropts.h>
74
75/* function declarations */
76
77static void dlpromisconreq( int fd, u_long level);
78void dlinforeq(int fd);
79void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen);
80void dlinfoack(int fd, char *bufp);
81void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest);
82void dlattachreq(int fd, u_long ppa);
83void dlokack(int fd, char *bufp);
84void dlbindack(int fd, char *bufp);
85int strioctl(int fd, int cmd, int timout, int len, char *dp);
86void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller);
87void sigalrm(int sig);
88void expecting(int prim, union DL_primitives *dlp);
89static char *dlprim(u_long prim);
90
91/* #define DL_DEBUG */
92
93static int dl_abssaplen;
94static int dl_saplen;
95static int dl_addrlen;
96
97#endif
98
99#ifdef USE_BPF
100#include <net/bpf.h>
101#include <fcntl.h>
102
103static unsigned char *bpfBuffer; /* Packet filter buffer */
104static int bpfLength = 0; /* Packet filter buffer length */
105 int bpfSize = 0; /* Number of unread bytes in buffer */
106static int bpfOffset = 0; /* Current offset in bpfBuffer */
107#endif
108
109/* Initialize frame types to RFC 2516 values. Some broken peers apparently
110 use different frame types... sigh... */
111
112UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
113UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION;
114
115/**********************************************************************
116*%FUNCTION: etherType
117*%ARGUMENTS:
118* packet -- a received PPPoE packet
119*%RETURNS:
120* ethernet packet type (see /usr/include/net/ethertypes.h)
121*%DESCRIPTION:
122* Checks the ethernet packet header to determine its type.
123* We should only be receveing DISCOVERY and SESSION types if the BPF
124* is set up correctly. Logs an error if an unexpected type is received.
125* Note that the ethernet type names come from "pppoe.h" and the packet
126* packet structure names use the LINUX dialect to maintain consistency
127* with the rest of this file. See the BSD section of "pppoe.h" for
128* translations of the data structure names.
129***********************************************************************/
130UINT16_t
131etherType(PPPoEPacket *packet)
132{
133 UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
134 if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
135 syslog(LOG_ERR, "Invalid ether type 0x%x", type);
136 }
137 return type;
138}
139
140
141#ifdef USE_LINUX_PACKET
142/**********************************************************************
143*%FUNCTION: openInterface
144*%ARGUMENTS:
145* ifname -- name of interface
146* type -- Ethernet frame type
147* hwaddr -- if non-NULL, set to the hardware address
148*%RETURNS:
149* A raw socket for talking to the Ethernet card. Exits on error.
150*%DESCRIPTION:
151* Opens a raw Ethernet socket
152***********************************************************************/
153int
154openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
155{
156 int optval=1;
157 int fd;
158 struct ifreq ifr;
159 int domain, stype;
160
161 struct sockaddr_ll sa;
162
163 memset(&sa, 0, sizeof(sa));
164
165 domain = PF_PACKET;
166 stype = SOCK_RAW;
167
168 if ((fd = socket(domain, stype, htons(type))) < 0) {
169 /* Give a more helpful message for the common error case */
170 if (errno == EPERM) {
171 rp_fatal("Cannot create raw socket -- pppoe must be run as root.");
172 }
173 fatalSys("socket");
174 }
175
176 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
177 fatalSys("setsockopt");
178 }
179
180 /* Fill in hardware address */
181 if (hwaddr) {
182 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
183 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
184 fatalSys("ioctl(SIOCGIFHWADDR)");
185 }
186 memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
187
188 if (NOT_UNICAST(hwaddr)) {
189 char buffer[256];
190 sprintf(buffer,
191 "Interface %.16s has broadcast/multicast MAC address??",
192 ifname);
193 rp_fatal(buffer);
194 }
195 }
196
197 /* Sanity check on MTU */
198 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
199 if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
200 fatalSys("ioctl(SIOCGIFMTU)");
201 }
202 if (ifr.ifr_mtu < ETH_DATA_LEN) {
203 char buffer[256];
204 sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d. You may have serious connection problems.",
205 ifname, ifr.ifr_mtu, ETH_DATA_LEN);
206 printErr(buffer);
207 }
208
209 /* Get interface index */
210 sa.sll_family = AF_PACKET;
211 sa.sll_protocol = htons(type);
212
213 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
214 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
215 fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index");
216 }
217 sa.sll_ifindex = ifr.ifr_ifindex;
218
219 /* We're only interested in packets on specified interface */
220 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
221 fatalSys("bind");
222 }
223
224 return fd;
225}
226
227#endif /* USE_LINUX */
228
229/***********************************************************************
230*%FUNCTION: sendPacket
231*%ARGUMENTS:
232* sock -- socket to send to
233* pkt -- the packet to transmit
234* size -- size of packet (in bytes)
235*%RETURNS:
236* 0 on success; -1 on failure
237*%DESCRIPTION:
238* Transmits a packet
239***********************************************************************/
240int
241sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
242{
243 if (send(sock, pkt, size, 0) < 0 && (errno != ENOBUFS)) {
244 sysErr("send (sendPacket)");
245 return -1;
246 }
247 return 0;
248}
249
250#ifdef USE_BPF
251/***********************************************************************
252*%FUNCTION: clearPacketHeader
253*%ARGUMENTS:
254* pkt -- packet that needs its head clearing
255*%RETURNS:
256* nothing
257*%DESCRIPTION:
258* Clears a PPPoE packet header after a truncated packet has been
259* received. Insures that the packet will fail any integrity tests
260* and will be discarded by upper level routines. Also resets the
261* bpfSize and bpfOffset variables to force a new read on the next
262* call to receivePacket().
263***********************************************************************/
264void
265clearPacketHeader(PPPoEPacket *pkt)
266{
267 bpfSize = bpfOffset = 0;
268 memset(pkt, 0, HDR_SIZE);
269}
270#endif
271
272/***********************************************************************
273*%FUNCTION: receivePacket
274*%ARGUMENTS:
275* sock -- socket to read from
276* pkt -- place to store the received packet
277* size -- set to size of packet in bytes
278*%RETURNS:
279* >= 0 if all OK; < 0 if error
280*%DESCRIPTION:
281* Receives a packet
282***********************************************************************/
283int
284receivePacket(int sock, PPPoEPacket *pkt, int *size)
285{
286#ifdef USE_BPF
287 struct bpf_hdr hdr;
288 int seglen, copylen;
289
290 if (bpfSize <= 0) {
291 bpfOffset = 0;
292 if ((bpfSize = read(sock, bpfBuffer, bpfLength)) < 0) {
293 sysErr("read (receivePacket)");
294 return -1;
295 }
296 }
297 if (bpfSize < sizeof(hdr)) {
298 syslog(LOG_ERR, "Truncated bpf packet header: len=%d", bpfSize);
299 clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */
300 return 0;
301 }
302 memcpy(&hdr, bpfBuffer + bpfOffset, sizeof(hdr));
303 if (hdr.bh_caplen != hdr.bh_datalen) {
304 syslog(LOG_ERR, "Truncated bpf packet: caplen=%d, datalen=%d",
305 hdr.bh_caplen, hdr.bh_datalen);
306 clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */
307 return 0;
308 }
309 seglen = hdr.bh_hdrlen + hdr.bh_caplen;
310 if (seglen > bpfSize) {
311 syslog(LOG_ERR, "Truncated bpf packet: seglen=%d, bpfSize=%d",
312 seglen, bpfSize);
313 clearPacketHeader(pkt); /* resets bpfSize and bpfOffset */
314 return 0;
315 }
316 seglen = BPF_WORDALIGN(seglen);
317 *size = copylen = ((hdr.bh_caplen < sizeof(PPPoEPacket)) ?
318 hdr.bh_caplen : sizeof(PPPoEPacket));
319 memcpy(pkt, bpfBuffer + bpfOffset + hdr.bh_hdrlen, copylen);
320 if (seglen >= bpfSize) {
321 bpfSize = bpfOffset = 0;
322 } else {
323 bpfSize -= seglen;
324 bpfOffset += seglen;
325 }
326#else
327#ifdef USE_DLPI
328 struct strbuf data;
329 int flags = 0;
330 int retval;
331
332 data.buf = (char *) pkt;
333 data.maxlen = MAXDLBUF;
334 data.len = 0;
335
336 if ((retval = getmsg(sock, NULL, &data, &flags)) < 0) {
337 sysErr("read (receivePacket)");
338 return -1;
339 }
340
341 *size = data.len;
342
343#else
344 if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
345 sysErr("recv (receivePacket)");
346 return -1;
347 }
348#endif
349#endif
350 return 0;
351}
352
353