5
* Much inspired by rp-pppoe from Roaring Penguin Software Inc.
6
* which itself is inspired by earlier code from Luke Stras.
13
#include <sys/types.h>
14
#include <sys/ioctl.h>
15
#include <sys/socket.h>
20
#include <net/ethernet.h>
21
#include <net/if_arp.h>
22
#include <netinet/in.h>
23
#include <netpacket/packet.h>
29
static hd_data_t *hd_data;
32
* @defgroup PPPOEint PPPoE devices (DSL)
33
* @ingroup libhdDEVint
34
* @brief PPPoE devices scan functions
39
/* Ethernet Frame Types */
40
#define ETH_PPPOE_DISCOVERY 0x8863
41
#define ETH_PPPOE_SESSION 0x8864
44
#define CODE_PADI 0x09
45
#define CODE_PADO 0x07
46
#define CODE_PADR 0x19
47
#define CODE_PADS 0x65
48
#define CODE_PADT 0xA7
51
#define TAG_END_OF_LIST 0x0000
52
#define TAG_SERVICE_NAME 0x0101
53
#define TAG_AC_NAME 0x0102
54
#define TAG_HOST_UNIQ 0x0103
55
#define TAG_AC_COOKIE 0x0104
56
#define TAG_VENDOR_SPECIFIC 0x0105
57
#define TAG_RELAY_SESSION_ID 0x0110
58
#define TAG_SERVICE_NAME_ERROR 0x0201
59
#define TAG_AC_SYSTEM_ERROR 0x0202
60
#define TAG_GENERIC_ERROR 0x0203
62
/* Number of Attempts */
63
#define MAX_ATTEMPTS 2
65
/* Timeout for PADO Packets */
66
#define PADO_TIMEOUT 3
68
/* A PPPoE Packet, including Ethernet headers */
69
typedef struct PPPoEPacketStruct {
70
struct ethhdr ethHdr; /* Ethernet header */
71
unsigned int ver:4; /* PPPoE Version (must be 1) */
72
unsigned int type:4; /* PPPoE Type (must be 1) */
73
unsigned int code:8; /* PPPoE code */
74
unsigned int session:16; /* PPPoE session */
75
unsigned int length:16; /* Payload length */
76
unsigned char payload[ETH_DATA_LEN]; /* A bit of room to spare */
79
/* Header size of a PPPoE Packet */
80
#define PPPOE_OVERHEAD 6 /* type, code, session, length */
81
#define HDR_SIZE (sizeof (struct ethhdr) + PPPOE_OVERHEAD)
82
#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD)
85
typedef struct PPPoETagStruct {
86
unsigned int type:16; /* tag type */
87
unsigned int length:16; /* Length of payload */
88
unsigned char payload[ETH_DATA_LEN]; /* A LOT of room to spare */
91
/* Header size of a PPPoE Tag */
92
#define TAG_HDR_SIZE 4
94
/* Function passed to parse_packet */
95
typedef void parse_func (uint16_t type, uint16_t len,
96
unsigned char* data, void* extra);
98
/* Keep track of the state of a connection */
99
typedef struct PPPoEConnectionStruct {
100
char* ifname; /* Interface name */
101
int fd; /* Raw socket for discovery frames */
102
int received_pado; /* Where we are in discovery */
103
unsigned char my_mac[ETH_ALEN]; /* My MAC address */
104
unsigned char peer_mac[ETH_ALEN]; /* Peer's MAC address */
108
/* Structure used to determine acceptable PADO packet */
109
typedef struct PacketCriteriaStruct {
110
PPPoEConnection* conn;
116
/* True if Ethernet address is broadcast or multicast */
117
#define NOT_UNICAST(e) ((e[0] & 0x01) != 0)
121
check_room (PPPoEConnection* conn, unsigned char* cursor, unsigned char* start,
124
if (cursor - start + len > MAX_PPPOE_PAYLOAD) {
125
ADD2LOG ("%s: Would create too-long packet\n", conn->ifname);
133
parse_packet (PPPoEConnection* conn, PPPoEPacket* packet, parse_func* func,
136
uint16_t len = ntohs (packet->length);
137
unsigned char* curTag;
138
uint16_t tagType, tagLen;
140
if (packet->ver != 1) {
141
ADD2LOG ("%s: Invalid PPPoE version (%d)\n", conn->ifname,
146
if (packet->type != 1) {
147
ADD2LOG ("%s: Invalid PPPoE type (%d)\n", conn->ifname,
152
/* Do some sanity checks on packet. */
153
if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
154
ADD2LOG ("%s: Invalid PPPoE packet length (%u)\n", conn->ifname, len);
158
/* Step through the tags. */
159
curTag = packet->payload;
160
while (curTag - packet->payload < len) {
161
/* Alignment is not guaranteed, so do this by hand. */
162
tagType = (((uint16_t) curTag[0]) << 8) + (uint16_t) curTag[1];
163
tagLen = (((uint16_t) curTag[2]) << 8) + (uint16_t) curTag[3];
164
if (tagType == TAG_END_OF_LIST)
166
if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
167
ADD2LOG ("%s: Invalid PPPoE tag length (%u)\n", conn->ifname,
171
func (tagType, tagLen, curTag + TAG_HDR_SIZE, extra);
172
curTag = curTag + TAG_HDR_SIZE + tagLen;
180
open_interfaces (int n, PPPoEConnection* conns)
184
for (i = 0; i < n; i++)
186
PPPoEConnection* conn = &conns[i];
188
conn->fd = socket (PF_PACKET, SOCK_RAW, htons (ETH_PPPOE_DISCOVERY));
190
ADD2LOG ("%s: socket failed: %m\n", conn->ifname);
195
if (setsockopt (conn->fd, SOL_SOCKET, SO_BROADCAST, &one,
197
ADD2LOG ("%s: setsockopt failed: %m\n", conn->ifname);
201
/* Fill in hardware address */
203
struct sockaddr_ll sa;
204
memset (&sa, 0, sizeof (sa));
205
strncpy (ifr.ifr_name, conn->ifname, sizeof (ifr.ifr_name));
206
if (ioctl (conn->fd, SIOCGIFHWADDR, &ifr) < 0) {
207
ADD2LOG ("%s: ioctl (SIOCGIFHWADDR) failed: %m\n", conn->ifname);
211
memcpy (conn->my_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
212
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
213
ADD2LOG ("%s: Interface is not ethernet\n", conn->ifname);
217
if (NOT_UNICAST (conn->my_mac)) {
218
ADD2LOG ("%s: Interface has broadcast/multicast MAC address?\n",
223
/* Sanity check on MTU */
224
strncpy (ifr.ifr_name, conn->ifname, sizeof (ifr.ifr_name));
225
if (ioctl (conn->fd, SIOCGIFMTU, &ifr) < 0) {
226
ADD2LOG ("%s: ioctl (SIOCGIFMTU) failed: %m\n", conn->ifname);
229
if (ifr.ifr_mtu < ETH_DATA_LEN) {
230
ADD2LOG ("%s: Interface has to low MTU\n", conn->ifname);
234
/* Get interface index */
235
sa.sll_family = AF_PACKET;
236
sa.sll_protocol = htons (ETH_PPPOE_DISCOVERY);
237
strncpy (ifr.ifr_name, conn->ifname, sizeof (ifr.ifr_name));
238
if (ioctl (conn->fd, SIOCGIFINDEX, &ifr) < 0) {
239
ADD2LOG ("%s: ioctl (SIOCFIGINDEX) failed: Could not get interface "
240
"index\n", conn->ifname);
243
sa.sll_ifindex = ifr.ifr_ifindex;
245
/* We're only interested in packets on specified interface */
246
if (bind (conn->fd, (struct sockaddr*) &sa, sizeof (sa)) < 0) {
247
ADD2LOG ("%s: bind failed: %m\n", conn->ifname);
266
close_intefaces (int n, PPPoEConnection* conns)
270
for (i = 0; i < n; i++)
272
PPPoEConnection* conn = &conns[i];
274
if (conn->fd != -1) {
283
send_packet (int fd, PPPoEPacket* pkt, size_t size)
285
if (send (fd, pkt, size, 0) < 0) {
286
ADD2LOG ("send failed: %m\n");
295
receive_packet (int fd, PPPoEPacket* pkt, size_t* size)
297
int r = recv (fd, pkt, sizeof (PPPoEPacket), 0);
299
ADD2LOG ("recv failed: %m\n");
309
parse_hostuniq (uint16_t type, uint16_t len, unsigned char* data, void* extra)
311
if (type == TAG_HOST_UNIQ && len == sizeof (pid_t)) {
313
memcpy (&tmp, data, len);
314
if (tmp == getpid ()) {
315
int* val = (int*) extra;
323
packet_for_me (PPPoEConnection* conn, PPPoEPacket* packet)
325
/* If packet is not directed to our MAC address, forget it. */
326
if (memcmp (packet->ethHdr.h_dest, conn->my_mac, ETH_ALEN))
329
/* Check for HostUniq tag. */
331
parse_packet (conn, packet, parse_hostuniq, &for_me);
337
parse_pado_tags (uint16_t type, uint16_t len, unsigned char* data, void* extra)
339
PacketCriteria* pc = (PacketCriteria*) extra;
340
PPPoEConnection *conn = pc->conn;
345
ADD2LOG ("%s: Service-Name is: %.*s\n", conn->ifname, (int) len,
348
case TAG_SERVICE_NAME:
349
pc->servicename_ok = len == 0;
351
case TAG_SERVICE_NAME_ERROR:
352
ADD2LOG ("%s: Service-Name-Error: %.*s\n", conn->ifname, (int) len,
356
case TAG_AC_SYSTEM_ERROR:
357
ADD2LOG ("%s: System-Error: %.*s\n", conn->ifname, (int) len, data);
360
case TAG_GENERIC_ERROR:
361
ADD2LOG ("%s: Generic-Error: %.*s\n", conn->ifname, (int) len, data);
369
send_padi (int n, PPPoEConnection* conns)
373
for (i = 0; i < n; i++)
375
PPPoEConnection* conn = &conns[i];
377
if (conn->fd == -1 || conn->received_pado)
381
unsigned char* cursor = packet.payload;
382
PPPoETag* svc = (PPPoETag*) (&packet.payload);
383
uint16_t namelen = 0;
387
plen = TAG_HDR_SIZE + namelen;
388
if (!check_room (conn, cursor, packet.payload, TAG_HDR_SIZE))
391
/* Set destination to Ethernet broadcast address */
392
memset (packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
393
memcpy (packet.ethHdr.h_source, conn->my_mac, ETH_ALEN);
395
packet.ethHdr.h_proto = htons (ETH_PPPOE_DISCOVERY);
398
packet.code = CODE_PADI;
401
svc->type = TAG_SERVICE_NAME;
402
svc->length = htons (0);
403
if (!check_room (conn, cursor, packet.payload, namelen + TAG_HDR_SIZE))
406
cursor += namelen + TAG_HDR_SIZE;
409
pid_t pid = getpid ();
410
hostUniq.type = htons (TAG_HOST_UNIQ);
411
hostUniq.length = htons (sizeof (pid));
412
memcpy (hostUniq.payload, &pid, sizeof (pid));
413
if (!check_room (conn, cursor, packet.payload, sizeof (pid) + TAG_HDR_SIZE))
415
memcpy (cursor, &hostUniq, sizeof (pid) + TAG_HDR_SIZE);
416
cursor += sizeof (pid) + TAG_HDR_SIZE;
417
plen += sizeof (pid) + TAG_HDR_SIZE;
419
packet.length = htons (plen);
421
ADD2LOG ("%s: Sending PADI packet\n", conn->ifname);
423
if (send_packet (conn->fd, &packet, (int) (plen + HDR_SIZE)))
432
wait_for_pado (int n, PPPoEConnection* conns)
441
tv.tv_sec = PADO_TIMEOUT;
447
for (i = 0; i < n; i++)
448
if (conns[i].fd != -1)
449
FD_SET (conns[i].fd, &readable);
452
r = select (FD_SETSIZE, &readable, NULL, NULL, &tv);
453
} while (r == -1 && errno == EINTR);
456
ADD2LOG ("select: %m\n");
461
ADD2LOG ("Timeout waiting for PADO packets\n");
465
for (i = 0; i < n; i++)
467
PPPoEConnection* conn = &conns[i];
469
if (conn->fd == -1 || !FD_ISSET (conn->fd, &readable))
474
pc.servicename_ok = 0;
478
if (!receive_packet (conn->fd, &packet, &len))
482
if (ntohs (packet.length) + HDR_SIZE > len) {
483
ADD2LOG ("%s: Bogus PPPoE length field (%u)\n", conn->ifname,
484
(unsigned int) ntohs (packet.length));
488
/* If it's not for us, loop again */
489
if (!packet_for_me (conn, &packet))
492
if (packet.code != CODE_PADO)
495
if (NOT_UNICAST (packet.ethHdr.h_source)) {
496
ADD2LOG ("%s: Ignoring PADO packet from non-unicast MAC "
497
"address\n", conn->ifname);
501
parse_packet (conn, &packet, parse_pado_tags, &pc);
504
ADD2LOG ("%s: Wrong or missing AC-Name tag\n", conn->ifname);
508
if (!pc.servicename_ok) {
509
ADD2LOG ("%s: Wrong or missing Service-Name tag\n",
515
ADD2LOG ("%s: Ignoring PADO packet with some Error tag\n",
520
memcpy (conn->peer_mac, packet.ethHdr.h_source, ETH_ALEN);
521
ADD2LOG ("%s: Received correct PADO packet\n", conn->ifname);
522
conn->received_pado = 1;
526
for (i = 0; i < n; i++)
527
if (conns[i].fd != -1 && !conns[i].received_pado)
536
discovery (int n, PPPoEConnection* conns)
540
if (open_interfaces (n, conns))
542
for (a = 0; a < MAX_ATTEMPTS; a++)
544
ADD2LOG ("Attempt number %d\n", a + 1);
546
if (!send_padi (n, conns))
549
if (wait_for_pado (n, conns))
554
close_intefaces (n, conns);
558
void hd_scan_pppoe(hd_data_t *hd_data2)
562
PPPoEConnection *conn;
566
if(!hd_probe_feature(hd_data, pr_pppoe)) return;
568
hd_data->module = mod_pppoe;
570
PROGRESS(1, 0, "looking for pppoe");
572
for(interfaces = 0, hd = hd_data->hd; hd; hd = hd->next) {
574
hd->base_class.id == bc_network_interface &&
575
hd->sub_class.id == sc_nif_ethernet &&
582
if(!interfaces) return;
584
conn = new_mem(interfaces * sizeof *conn);
586
for(cnt = 0, hd = hd_data->hd; hd && cnt < interfaces; hd = hd->next) {
588
hd->base_class.id == bc_network_interface &&
589
hd->sub_class.id == sc_nif_ethernet &&
594
conn[cnt].ifname = hd->unix_dev_name;
599
PROGRESS(2, 0, "discovery");
601
discovery(interfaces, conn);
603
for(cnt = 0; cnt < interfaces; cnt++) {
604
conn[cnt].hd->is.pppoe = 0;
606
if(conn[cnt].received_pado) {
607
conn[cnt].hd->is.pppoe = 1;
609
"pppoe %s: my mac %02x:%02x:%02x:%02x:%02x:%02x, "
610
"peer mac %02x:%02x:%02x:%02x:%02x:%02x\n",
612
conn[cnt].my_mac[0], conn[cnt].my_mac[1], conn[cnt].my_mac[2],
613
conn[cnt].my_mac[3], conn[cnt].my_mac[4], conn[cnt].my_mac[5],
614
conn[cnt].peer_mac[0], conn[cnt].peer_mac[1], conn[cnt].peer_mac[2],
615
conn[cnt].peer_mac[3], conn[cnt].peer_mac[4], conn[cnt].peer_mac[5]