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 | |
18 | static 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 | |
77 | static void dlpromisconreq( int fd, u_long level); |
78 | void dlinforeq(int fd); |
79 | void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen); |
80 | void dlinfoack(int fd, char *bufp); |
81 | void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest); |
82 | void dlattachreq(int fd, u_long ppa); |
83 | void dlokack(int fd, char *bufp); |
84 | void dlbindack(int fd, char *bufp); |
85 | int strioctl(int fd, int cmd, int timout, int len, char *dp); |
86 | void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller); |
87 | void sigalrm(int sig); |
88 | void expecting(int prim, union DL_primitives *dlp); |
89 | static char *dlprim(u_long prim); |
90 | |
91 | /* #define DL_DEBUG */ |
92 | |
93 | static int dl_abssaplen; |
94 | static int dl_saplen; |
95 | static int dl_addrlen; |
96 | |
97 | #endif |
98 | |
99 | #ifdef USE_BPF |
100 | #include <net/bpf.h> |
101 | #include <fcntl.h> |
102 | |
103 | static unsigned char *bpfBuffer; /* Packet filter buffer */ |
104 | static int bpfLength = 0; /* Packet filter buffer length */ |
105 | int bpfSize = 0; /* Number of unread bytes in buffer */ |
106 | static 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 | |
112 | UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; |
113 | UINT16_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 | ***********************************************************************/ |
130 | UINT16_t |
131 | etherType(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 | ***********************************************************************/ |
153 | int |
154 | openInterface(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 | ***********************************************************************/ |
240 | int |
241 | sendPacket(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 | ***********************************************************************/ |
264 | void |
265 | clearPacketHeader(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 | ***********************************************************************/ |
283 | int |
284 | receivePacket(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 |