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