Merge pull request #54 from shankerwangmiao/master
[ppp.git] / pppd / plugins / rp-pppoe / if.c
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_ASM_TYPES_H
34 #include <asm/types.h>
35 #endif
36
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #ifdef HAVE_NET_IF_ARP_H
46 #include <net/if_arp.h>
47 #endif
48
49 /* Initialize frame types to RFC 2516 values.  Some broken peers apparently
50    use different frame types... sigh... */
51
52 UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
53 UINT16_t Eth_PPPOE_Session   = ETH_PPPOE_SESSION;
54
55 /**********************************************************************
56 *%FUNCTION: etherType
57 *%ARGUMENTS:
58 * packet -- a received PPPoE packet
59 *%RETURNS:
60 * ethernet packet type (see /usr/include/net/ethertypes.h)
61 *%DESCRIPTION:
62 * Checks the ethernet packet header to determine its type.
63 * We should only be receveing DISCOVERY and SESSION types if the BPF
64 * is set up correctly.  Logs an error if an unexpected type is received.
65 * Note that the ethernet type names come from "pppoe.h" and the packet
66 * packet structure names use the LINUX dialect to maintain consistency
67 * with the rest of this file.  See the BSD section of "pppoe.h" for
68 * translations of the data structure names.
69 ***********************************************************************/
70 UINT16_t
71 etherType(PPPoEPacket *packet)
72 {
73     UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
74     if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
75         error("Invalid ether type 0x%x", type);
76     }
77     return type;
78 }
79
80 /**********************************************************************
81 *%FUNCTION: openInterface
82 *%ARGUMENTS:
83 * ifname -- name of interface
84 * type -- Ethernet frame type
85 * hwaddr -- if non-NULL, set to the hardware address
86 *%RETURNS:
87 * A raw socket for talking to the Ethernet card.  Exits on error.
88 *%DESCRIPTION:
89 * Opens a raw Ethernet socket
90 ***********************************************************************/
91 int
92 openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
93 {
94     int optval=1;
95     int fd;
96     struct ifreq ifr;
97     int domain, stype;
98
99 #ifdef HAVE_STRUCT_SOCKADDR_LL
100     struct sockaddr_ll sa;
101 #else
102     struct sockaddr sa;
103 #endif
104
105     memset(&sa, 0, sizeof(sa));
106
107 #ifdef HAVE_STRUCT_SOCKADDR_LL
108     domain = PF_PACKET;
109     stype = SOCK_RAW;
110 #else
111     domain = PF_INET;
112     stype = SOCK_PACKET;
113 #endif
114
115     if ((fd = socket(domain, stype, htons(type))) < 0) {
116         /* Give a more helpful message for the common error case */
117         if (errno == EPERM) {
118             fatal("Cannot create raw socket -- pppoe must be run as root.");
119         }
120         error("Can't open socket for pppoe: %m");
121         return -1;
122     }
123
124     if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
125         error("Can't set socket options for pppoe: %m");
126         close(fd);
127         return -1;
128     }
129
130     /* Fill in hardware address */
131     if (hwaddr) {
132         strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
133         if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
134             error("Can't get hardware address for %s: %m", ifname);
135             close(fd);
136             return -1;
137         }
138         memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
139 #ifdef ARPHRD_ETHER
140         if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
141             warn("Interface %.16s is not Ethernet", ifname);
142         }
143 #endif
144         if (NOT_UNICAST(hwaddr)) {
145             fatal("Can't use interface %.16s: it has broadcast/multicast MAC address",
146                   ifname);
147         }
148     }
149
150     /* Sanity check on MTU */
151     strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
152     if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
153         error("Can't get MTU for %s: %m", ifname);
154     } else if (ifr.ifr_mtu < ETH_DATA_LEN) {
155         error("Interface %.16s has MTU of %d -- should be at least %d.",
156               ifname, ifr.ifr_mtu, ETH_DATA_LEN);
157         error("This may cause serious connection problems.");
158     }
159
160 #ifdef HAVE_STRUCT_SOCKADDR_LL
161     /* Get interface index */
162     sa.sll_family = AF_PACKET;
163     sa.sll_protocol = htons(type);
164
165     strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
166     if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
167         error("Could not get interface index for %s: %m", ifname);
168         close(fd);
169         return -1;
170     }
171     sa.sll_ifindex = ifr.ifr_ifindex;
172
173 #else
174     strcpy(sa.sa_data, ifname);
175 #endif
176
177     /* We're only interested in packets on specified interface */
178     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
179         error("Failed to bind to interface %s: %m", ifname);
180         close(fd);
181         return -1;
182     }
183
184     return fd;
185 }
186
187
188 /***********************************************************************
189 *%FUNCTION: sendPacket
190 *%ARGUMENTS:
191 * sock -- socket to send to
192 * pkt -- the packet to transmit
193 * size -- size of packet (in bytes)
194 *%RETURNS:
195 * 0 on success; -1 on failure
196 *%DESCRIPTION:
197 * Transmits a packet
198 ***********************************************************************/
199 int
200 sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
201 {
202     int err;
203
204     if (debug)
205         pppoe_log_packet("Send ", pkt);
206 #if defined(HAVE_STRUCT_SOCKADDR_LL)
207     err = send(sock, pkt, size, 0);
208 #else
209     struct sockaddr sa;
210
211     strcpy(sa.sa_data, conn->ifName);
212     err = sendto(sock, pkt, size, 0, &sa, sizeof(sa));
213 #endif
214     if (err < 0) {
215         error("error sending pppoe packet: %m");
216         return -1;
217     }
218     return 0;
219 }
220
221 /***********************************************************************
222 *%FUNCTION: receivePacket
223 *%ARGUMENTS:
224 * sock -- socket to read from
225 * pkt -- place to store the received packet
226 * size -- set to size of packet in bytes
227 *%RETURNS:
228 * >= 0 if all OK; < 0 if error
229 *%DESCRIPTION:
230 * Receives a packet
231 ***********************************************************************/
232 int
233 receivePacket(int sock, PPPoEPacket *pkt, int *size)
234 {
235     if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
236         error("error receiving pppoe packet: %m");
237         return -1;
238     }
239     if (debug)
240         pppoe_log_packet("Recv ", pkt);
241     return 0;
242 }