]>
git.ipfire.org Git - people/dweismueller/ipfire-2.x.git/blob - src/hwinfo/src/hd/pppoe.c
da1864c0d2a285b145fb5c6191883c77831252ce
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
;
31 /* Ethernet Frame Types */
32 #define ETH_PPPOE_DISCOVERY 0x8863
33 #define ETH_PPPOE_SESSION 0x8864
36 #define CODE_PADI 0x09
37 #define CODE_PADO 0x07
38 #define CODE_PADR 0x19
39 #define CODE_PADS 0x65
40 #define CODE_PADT 0xA7
43 #define TAG_END_OF_LIST 0x0000
44 #define TAG_SERVICE_NAME 0x0101
45 #define TAG_AC_NAME 0x0102
46 #define TAG_HOST_UNIQ 0x0103
47 #define TAG_AC_COOKIE 0x0104
48 #define TAG_VENDOR_SPECIFIC 0x0105
49 #define TAG_RELAY_SESSION_ID 0x0110
50 #define TAG_SERVICE_NAME_ERROR 0x0201
51 #define TAG_AC_SYSTEM_ERROR 0x0202
52 #define TAG_GENERIC_ERROR 0x0203
54 /* Number of Attempts */
55 #define MAX_ATTEMPTS 2
57 /* Timeout for PADO Packets */
58 #define PADO_TIMEOUT 3
60 /* A PPPoE Packet, including Ethernet headers */
61 typedef struct PPPoEPacketStruct
{
62 struct ethhdr ethHdr
; /* Ethernet header */
63 unsigned int ver
:4; /* PPPoE Version (must be 1) */
64 unsigned int type
:4; /* PPPoE Type (must be 1) */
65 unsigned int code
:8; /* PPPoE code */
66 unsigned int session
:16; /* PPPoE session */
67 unsigned int length
:16; /* Payload length */
68 unsigned char payload
[ETH_DATA_LEN
]; /* A bit of room to spare */
71 /* Header size of a PPPoE Packet */
72 #define PPPOE_OVERHEAD 6 /* type, code, session, length */
73 #define HDR_SIZE (sizeof (struct ethhdr) + PPPOE_OVERHEAD)
74 #define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD)
77 typedef struct PPPoETagStruct
{
78 unsigned int type
:16; /* tag type */
79 unsigned int length
:16; /* Length of payload */
80 unsigned char payload
[ETH_DATA_LEN
]; /* A LOT of room to spare */
83 /* Header size of a PPPoE Tag */
84 #define TAG_HDR_SIZE 4
86 /* Function passed to parse_packet */
87 typedef void parse_func (uint16_t type
, uint16_t len
,
88 unsigned char* data
, void* extra
);
90 /* Keep track of the state of a connection */
91 typedef struct PPPoEConnectionStruct
{
92 char* ifname
; /* Interface name */
93 int fd
; /* Raw socket for discovery frames */
94 int received_pado
; /* Where we are in discovery */
95 unsigned char my_mac
[ETH_ALEN
]; /* My MAC address */
96 unsigned char peer_mac
[ETH_ALEN
]; /* Peer's MAC address */
100 /* Structure used to determine acceptable PADO packet */
101 typedef struct PacketCriteriaStruct
{
102 PPPoEConnection
* conn
;
108 /* True if Ethernet address is broadcast or multicast */
109 #define NOT_UNICAST(e) ((e[0] & 0x01) != 0)
113 check_room (PPPoEConnection
* conn
, unsigned char* cursor
, unsigned char* start
,
116 if (cursor
- start
+ len
> MAX_PPPOE_PAYLOAD
) {
117 ADD2LOG ("%s: Would create too-long packet\n", conn
->ifname
);
125 parse_packet (PPPoEConnection
* conn
, PPPoEPacket
* packet
, parse_func
* func
,
128 uint16_t len
= ntohs (packet
->length
);
129 unsigned char* curTag
;
130 uint16_t tagType
, tagLen
;
132 if (packet
->ver
!= 1) {
133 ADD2LOG ("%s: Invalid PPPoE version (%d)\n", conn
->ifname
,
138 if (packet
->type
!= 1) {
139 ADD2LOG ("%s: Invalid PPPoE type (%d)\n", conn
->ifname
,
144 /* Do some sanity checks on packet. */
145 if (len
> ETH_DATA_LEN
- 6) { /* 6-byte overhead for PPPoE header */
146 ADD2LOG ("%s: Invalid PPPoE packet length (%u)\n", conn
->ifname
, len
);
150 /* Step through the tags. */
151 curTag
= packet
->payload
;
152 while (curTag
- packet
->payload
< len
) {
153 /* Alignment is not guaranteed, so do this by hand. */
154 tagType
= (((uint16_t) curTag
[0]) << 8) + (uint16_t) curTag
[1];
155 tagLen
= (((uint16_t) curTag
[2]) << 8) + (uint16_t) curTag
[3];
156 if (tagType
== TAG_END_OF_LIST
)
158 if ((curTag
- packet
->payload
) + tagLen
+ TAG_HDR_SIZE
> len
) {
159 ADD2LOG ("%s: Invalid PPPoE tag length (%u)\n", conn
->ifname
,
163 func (tagType
, tagLen
, curTag
+ TAG_HDR_SIZE
, extra
);
164 curTag
= curTag
+ TAG_HDR_SIZE
+ tagLen
;
172 open_interfaces (int n
, PPPoEConnection
* conns
)
176 for (i
= 0; i
< n
; i
++)
178 PPPoEConnection
* conn
= &conns
[i
];
180 conn
->fd
= socket (PF_PACKET
, SOCK_RAW
, htons (ETH_PPPOE_DISCOVERY
));
182 ADD2LOG ("%s: socket failed: %m\n", conn
->ifname
);
187 if (setsockopt (conn
->fd
, SOL_SOCKET
, SO_BROADCAST
, &one
,
189 ADD2LOG ("%s: setsockopt failed: %m\n", conn
->ifname
);
193 /* Fill in hardware address */
195 struct sockaddr_ll sa
;
196 memset (&sa
, 0, sizeof (sa
));
197 strncpy (ifr
.ifr_name
, conn
->ifname
, sizeof (ifr
.ifr_name
));
198 if (ioctl (conn
->fd
, SIOCGIFHWADDR
, &ifr
) < 0) {
199 ADD2LOG ("%s: ioctl (SIOCGIFHWADDR) failed: %m\n", conn
->ifname
);
203 memcpy (conn
->my_mac
, ifr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
204 if (ifr
.ifr_hwaddr
.sa_family
!= ARPHRD_ETHER
) {
205 ADD2LOG ("%s: Interface is not ethernet\n", conn
->ifname
);
209 if (NOT_UNICAST (conn
->my_mac
)) {
210 ADD2LOG ("%s: Interface has broadcast/multicast MAC address?\n",
215 /* Sanity check on MTU */
216 strncpy (ifr
.ifr_name
, conn
->ifname
, sizeof (ifr
.ifr_name
));
217 if (ioctl (conn
->fd
, SIOCGIFMTU
, &ifr
) < 0) {
218 ADD2LOG ("%s: ioctl (SIOCGIFMTU) failed: %m\n", conn
->ifname
);
221 if (ifr
.ifr_mtu
< ETH_DATA_LEN
) {
222 ADD2LOG ("%s: Interface has to low MTU\n", conn
->ifname
);
226 /* Get interface index */
227 sa
.sll_family
= AF_PACKET
;
228 sa
.sll_protocol
= htons (ETH_PPPOE_DISCOVERY
);
229 strncpy (ifr
.ifr_name
, conn
->ifname
, sizeof (ifr
.ifr_name
));
230 if (ioctl (conn
->fd
, SIOCGIFINDEX
, &ifr
) < 0) {
231 ADD2LOG ("%s: ioctl (SIOCFIGINDEX) failed: Could not get interface "
232 "index\n", conn
->ifname
);
235 sa
.sll_ifindex
= ifr
.ifr_ifindex
;
237 /* We're only interested in packets on specified interface */
238 if (bind (conn
->fd
, (struct sockaddr
*) &sa
, sizeof (sa
)) < 0) {
239 ADD2LOG ("%s: bind failed: %m\n", conn
->ifname
);
258 close_intefaces (int n
, PPPoEConnection
* conns
)
262 for (i
= 0; i
< n
; i
++)
264 PPPoEConnection
* conn
= &conns
[i
];
266 if (conn
->fd
!= -1) {
275 send_packet (int fd
, PPPoEPacket
* pkt
, size_t size
)
277 if (send (fd
, pkt
, size
, 0) < 0) {
278 ADD2LOG ("send failed: %m\n");
287 receive_packet (int fd
, PPPoEPacket
* pkt
, size_t* size
)
289 int r
= recv (fd
, pkt
, sizeof (PPPoEPacket
), 0);
291 ADD2LOG ("recv failed: %m\n");
301 parse_hostuniq (uint16_t type
, uint16_t len
, unsigned char* data
, void* extra
)
303 if (type
== TAG_HOST_UNIQ
&& len
== sizeof (pid_t
)) {
305 memcpy (&tmp
, data
, len
);
306 if (tmp
== getpid ()) {
307 int* val
= (int*) extra
;
315 packet_for_me (PPPoEConnection
* conn
, PPPoEPacket
* packet
)
317 /* If packet is not directed to our MAC address, forget it. */
318 if (memcmp (packet
->ethHdr
.h_dest
, conn
->my_mac
, ETH_ALEN
))
321 /* Check for HostUniq tag. */
323 parse_packet (conn
, packet
, parse_hostuniq
, &for_me
);
329 parse_pado_tags (uint16_t type
, uint16_t len
, unsigned char* data
, void* extra
)
331 PacketCriteria
* pc
= (PacketCriteria
*) extra
;
332 PPPoEConnection
*conn
= pc
->conn
;
337 ADD2LOG ("%s: Service-Name is: %.*s\n", conn
->ifname
, (int) len
,
340 case TAG_SERVICE_NAME
:
341 pc
->servicename_ok
= len
== 0;
343 case TAG_SERVICE_NAME_ERROR
:
344 ADD2LOG ("%s: Service-Name-Error: %.*s\n", conn
->ifname
, (int) len
,
348 case TAG_AC_SYSTEM_ERROR
:
349 ADD2LOG ("%s: System-Error: %.*s\n", conn
->ifname
, (int) len
, data
);
352 case TAG_GENERIC_ERROR
:
353 ADD2LOG ("%s: Generic-Error: %.*s\n", conn
->ifname
, (int) len
, data
);
361 send_padi (int n
, PPPoEConnection
* conns
)
365 for (i
= 0; i
< n
; i
++)
367 PPPoEConnection
* conn
= &conns
[i
];
369 if (conn
->fd
== -1 || conn
->received_pado
)
373 unsigned char* cursor
= packet
.payload
;
374 PPPoETag
* svc
= (PPPoETag
*) (&packet
.payload
);
375 uint16_t namelen
= 0;
379 plen
= TAG_HDR_SIZE
+ namelen
;
380 if (!check_room (conn
, cursor
, packet
.payload
, TAG_HDR_SIZE
))
383 /* Set destination to Ethernet broadcast address */
384 memset (packet
.ethHdr
.h_dest
, 0xFF, ETH_ALEN
);
385 memcpy (packet
.ethHdr
.h_source
, conn
->my_mac
, ETH_ALEN
);
387 packet
.ethHdr
.h_proto
= htons (ETH_PPPOE_DISCOVERY
);
390 packet
.code
= CODE_PADI
;
393 svc
->type
= TAG_SERVICE_NAME
;
394 svc
->length
= htons (0);
395 if (!check_room (conn
, cursor
, packet
.payload
, namelen
+ TAG_HDR_SIZE
))
398 cursor
+= namelen
+ TAG_HDR_SIZE
;
401 pid_t pid
= getpid ();
402 hostUniq
.type
= htons (TAG_HOST_UNIQ
);
403 hostUniq
.length
= htons (sizeof (pid
));
404 memcpy (hostUniq
.payload
, &pid
, sizeof (pid
));
405 if (!check_room (conn
, cursor
, packet
.payload
, sizeof (pid
) + TAG_HDR_SIZE
))
407 memcpy (cursor
, &hostUniq
, sizeof (pid
) + TAG_HDR_SIZE
);
408 cursor
+= sizeof (pid
) + TAG_HDR_SIZE
;
409 plen
+= sizeof (pid
) + TAG_HDR_SIZE
;
411 packet
.length
= htons (plen
);
413 ADD2LOG ("%s: Sending PADI packet\n", conn
->ifname
);
415 if (send_packet (conn
->fd
, &packet
, (int) (plen
+ HDR_SIZE
)))
424 wait_for_pado (int n
, PPPoEConnection
* conns
)
433 tv
.tv_sec
= PADO_TIMEOUT
;
439 for (i
= 0; i
< n
; i
++)
440 if (conns
[i
].fd
!= -1)
441 FD_SET (conns
[i
].fd
, &readable
);
444 r
= select (FD_SETSIZE
, &readable
, NULL
, NULL
, &tv
);
445 } while (r
== -1 && errno
== EINTR
);
448 ADD2LOG ("select: %m\n");
453 ADD2LOG ("Timeout waiting for PADO packets\n");
457 for (i
= 0; i
< n
; i
++)
459 PPPoEConnection
* conn
= &conns
[i
];
461 if (conn
->fd
== -1 || !FD_ISSET (conn
->fd
, &readable
))
466 pc
.servicename_ok
= 0;
470 if (!receive_packet (conn
->fd
, &packet
, &len
))
474 if (ntohs (packet
.length
) + HDR_SIZE
> len
) {
475 ADD2LOG ("%s: Bogus PPPoE length field (%u)\n", conn
->ifname
,
476 (unsigned int) ntohs (packet
.length
));
480 /* If it's not for us, loop again */
481 if (!packet_for_me (conn
, &packet
))
484 if (packet
.code
!= CODE_PADO
)
487 if (NOT_UNICAST (packet
.ethHdr
.h_source
)) {
488 ADD2LOG ("%s: Ignoring PADO packet from non-unicast MAC "
489 "address\n", conn
->ifname
);
493 parse_packet (conn
, &packet
, parse_pado_tags
, &pc
);
496 ADD2LOG ("%s: Wrong or missing AC-Name tag\n", conn
->ifname
);
500 if (!pc
.servicename_ok
) {
501 ADD2LOG ("%s: Wrong or missing Service-Name tag\n",
507 ADD2LOG ("%s: Ignoring PADO packet with some Error tag\n",
512 memcpy (conn
->peer_mac
, packet
.ethHdr
.h_source
, ETH_ALEN
);
513 ADD2LOG ("%s: Received correct PADO packet\n", conn
->ifname
);
514 conn
->received_pado
= 1;
518 for (i
= 0; i
< n
; i
++)
519 if (conns
[i
].fd
!= -1 && !conns
[i
].received_pado
)
528 discovery (int n
, PPPoEConnection
* conns
)
532 if (open_interfaces (n
, conns
))
534 for (a
= 0; a
< MAX_ATTEMPTS
; a
++)
536 ADD2LOG ("Attempt number %d\n", a
+ 1);
538 if (!send_padi (n
, conns
))
541 if (wait_for_pado (n
, conns
))
546 close_intefaces (n
, conns
);
550 void hd_scan_pppoe(hd_data_t
*hd_data2
)
554 PPPoEConnection
*conn
;
558 if(!hd_probe_feature(hd_data
, pr_pppoe
)) return;
560 hd_data
->module
= mod_pppoe
;
562 PROGRESS(1, 0, "looking for pppoe");
564 for(interfaces
= 0, hd
= hd_data
->hd
; hd
; hd
= hd
->next
) {
566 hd
->base_class
.id
== bc_network_interface
&&
567 hd
->sub_class
.id
== sc_nif_ethernet
&&
574 if(!interfaces
) return;
576 conn
= new_mem(interfaces
* sizeof *conn
);
578 for(cnt
= 0, hd
= hd_data
->hd
; hd
&& cnt
< interfaces
; hd
= hd
->next
) {
580 hd
->base_class
.id
== bc_network_interface
&&
581 hd
->sub_class
.id
== sc_nif_ethernet
&&
586 conn
[cnt
].ifname
= hd
->unix_dev_name
;
591 PROGRESS(2, 0, "discovery");
593 discovery(interfaces
, conn
);
595 for(cnt
= 0; cnt
< interfaces
; cnt
++) {
596 conn
[cnt
].hd
->is
.pppoe
= 0;
598 if(conn
[cnt
].received_pado
) {
599 conn
[cnt
].hd
->is
.pppoe
= 1;
601 "pppoe %s: my mac %02x:%02x:%02x:%02x:%02x:%02x, "
602 "peer mac %02x:%02x:%02x:%02x:%02x:%02x\n",
604 conn
[cnt
].my_mac
[0], conn
[cnt
].my_mac
[1], conn
[cnt
].my_mac
[2],
605 conn
[cnt
].my_mac
[3], conn
[cnt
].my_mac
[4], conn
[cnt
].my_mac
[5],
606 conn
[cnt
].peer_mac
[0], conn
[cnt
].peer_mac
[1], conn
[cnt
].peer_mac
[2],
607 conn
[cnt
].peer_mac
[3], conn
[cnt
].peer_mac
[4], conn
[cnt
].peer_mac
[5]