1 /* PPPoE support library "libpppoe"
3 * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
4 * Jamal Hadi Salim <hadi@cyberus.ca>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
13 #include <sys/sysinfo.h>
15 static int tag_map
[] = { PTT_SRV_NAME
,
28 int retransmit_time
=10;
29 int redial_immediately
=0;
31 int verify_packet( struct session
*ses
, struct pppoe_packet
*p
);
33 #define TAG_DATA(type,tag_ptr) ((type *) ((struct pppoe_tag*)tag_ptr)->tag_data)
36 /***************************************************************************
38 * Return the location where the next tag can be pu
40 **************************************************************************/
41 static struct pppoe_tag
*next_tag(struct pppoe_hdr
*ph
)
43 return (struct pppoe_tag
*)
44 (((char *) &ph
->tag
) + ntohs(ph
->length
));
47 /**************************************************************************
49 * Update header to reflect the addition of a new tag
51 **************************************************************************/
52 static void add_tag(struct pppoe_hdr
*ph
, struct pppoe_tag
*pt
)
54 int len
= (ntohs(ph
->length
) +
56 sizeof(struct pppoe_tag
));
58 if (pt
!= next_tag(ph
))
59 printf("PPPoE add_tag caller is buggy\n");
61 ph
->length
= htons(len
);
64 /*************************************************************************
66 * Look for a tag of a specific type
68 ************************************************************************/
69 struct pppoe_tag
*get_tag(struct pppoe_hdr
*ph
, u_int16_t idx
)
71 char *end
= (char *) next_tag(ph
);
73 struct pppoe_tag
*pt
= &ph
->tag
[0];
76 * Keep processing tags while a tag header will still fit.
78 * This check will ensure that the entire tag header pointed
79 * to by pt will fit inside the message, and thus it will be
80 * valid to check the tag_type and tag_len fields.
82 while ((char *)(pt
+ 1) <= end
) {
84 * If the tag data would go past the end of the packet, abort.
86 ptn
= (((char *) (pt
+ 1)) + ntohs(pt
->tag_len
));
90 if (pt
->tag_type
== idx
)
93 pt
= (struct pppoe_tag
*) ptn
;
99 /* We want to use tag names to reference into arrays containing the tag data.
100 This takes an RFC 2516 tag identifier and maps it into a local one.
101 The reverse mapping is accomplished via the tag_map array */
102 #define UNMAP_TAG(x) case PTT_##x : return TAG_##x
103 static inline int tag_index(int tag
){
107 UNMAP_TAG(HOST_UNIQ
);
108 UNMAP_TAG(AC_COOKIE
);
110 UNMAP_TAG(RELAY_SID
);
119 /*************************************************************************
121 * Makes a copy of a tag into a PPPoE packe
123 ************************************************************************/
124 void copy_tag(struct pppoe_packet
*dest
, struct pppoe_tag
*pt
)
126 struct pppoe_tag
*end_tag
= get_tag(dest
->hdr
, PTT_EOL
);
132 tagid
= tag_index(pt
->tag_type
);
134 tag_len
= sizeof(struct pppoe_tag
) + ntohs(pt
->tag_len
);
137 memcpy(((char*)end_tag
)+tag_len
,
138 end_tag
, sizeof(struct pppoe_tag
));
140 dest
->tags
[tagid
]=end_tag
;
141 dest
->tags
[TAG_EOL
] = (struct pppoe_tag
*)((char*)dest
->tags
[TAG_EOL
] + tag_len
);
142 memcpy(end_tag
, pt
, tag_len
);
143 dest
->hdr
->length
= htons(ntohs(dest
->hdr
->length
) + tag_len
);
146 memcpy(next_tag(dest
->hdr
),pt
, tag_len
);
147 dest
->tags
[tagid
]=next_tag(dest
->hdr
);
148 add_tag(dest
->hdr
,next_tag(dest
->hdr
));
155 /*************************************************************************
157 * Put tags from a packet into a nice array
159 ************************************************************************/
160 static void extract_tags(struct pppoe_hdr
*ph
, struct pppoe_tag
** buf
){
162 for(;i
<MAX_TAGS
;++i
){
163 buf
[i
] = get_tag(ph
,tag_map
[i
]);
168 /*************************************************************************
170 * Verify that a packet has a tag containint a specific value
172 ************************************************************************/
173 static int verify_tag(struct session
* ses
,
174 struct pppoe_packet
* p
,
180 struct pppoe_tag
*pt
= p
->tags
[id
];
183 poe_info(ses
,"Missing tag %d. Expected %s\n",
187 len
= ntohs(pt
->tag_len
);
189 poe_info(ses
,"Length mismatch on tag %d: expect: %d got: %d\n",
194 if( 0!=memcmp(pt
->tag_data
,data
,data_len
)){
195 poe_info(ses
,"Tag data mismatch on tag %d: expect: %s vs %s\n",
196 id
, data
,pt
->tag_data
);
203 /*************************************************************************
205 * Verify the existence of an ethernet device.
206 * Construct an AF_PACKET address struct to match.
208 ************************************************************************/
209 int get_sockaddr_ll(const char *devnam
,struct sockaddr_ll
* sll
){
215 disc_sock
= socket(PF_PACKET
, SOCK_DGRAM
, 0);
221 strncpy(ifr
.ifr_name
, devnam
, sizeof(ifr
.ifr_name
));
223 retval
= ioctl( disc_sock
, SIOCGIFINDEX
, &ifr
);
226 // error("Bad device name: %s (%m)",devnam);
230 if(sll
) sll
->sll_ifindex
= ifr
.ifr_ifindex
;
232 retval
= ioctl (disc_sock
, SIOCGIFHWADDR
, &ifr
);
234 // error("Bad device name: %s (%m)",devnam);
238 if (ifr
.ifr_hwaddr
.sa_family
!= ARPHRD_ETHER
) {
239 error("Interface %s is not Ethernet!", devnam
);
243 sll
->sll_family
= AF_PACKET
;
244 sll
->sll_protocol
= ntohs(ETH_P_PPP_DISC
);
245 sll
->sll_hatype
= ARPHRD_ETHER
;
246 sll
->sll_pkttype
= PACKET_BROADCAST
;
247 sll
->sll_hatype
= ETH_ALEN
;
248 memcpy( sll
->sll_addr
, ifr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
256 /*************************************************************************
258 * Construct and send a discovery message.
260 ************************************************************************/
261 int send_disc(struct session
*ses
, struct pppoe_packet
*p
)
263 char buf
[MAX_PAYLOAD
+ sizeof(struct pppoe_hdr
)];
264 int data_len
= sizeof(struct pppoe_hdr
);
266 struct pppoe_hdr
*ph
= NULL
;
267 struct pppoe_tag
*tag
= NULL
;
269 int got_host_uniq
= 0;
270 int got_srv_name
= 0;
273 for (i
= 0; i
< MAX_TAGS
; i
++) {
277 got_host_uniq
|= (p
->tags
[i
]->tag_type
== PTT_HOST_UNIQ
);
279 /* Relay identifiers qualify as HOST_UNIQ's:
280 we need HOST_UNIQ to uniquely identify the packet,
281 PTT_RELAY_SID is sufficient for us for outgoing packets */
282 got_host_uniq
|= (p
->tags
[i
]->tag_type
== PTT_RELAY_SID
);
284 got_srv_name
|= (p
->tags
[i
]->tag_type
== PTT_SRV_NAME
);
285 got_ac_name
|= (p
->tags
[i
]->tag_type
== PTT_AC_NAME
);
287 data_len
+= (ntohs(p
->tags
[i
]->tag_len
) +
288 sizeof(struct pppoe_tag
));
291 ph
= (struct pppoe_hdr
*) buf
;
294 memcpy(ph
, p
->hdr
, sizeof(struct pppoe_hdr
));
295 ph
->length
= __constant_htons(0);
297 /* if no HOST_UNIQ tags --- add one with process id */
299 data_len
+= (sizeof(struct pppoe_tag
) +
300 sizeof(struct session
*));
302 tag
->tag_type
= PTT_HOST_UNIQ
;
303 tag
->tag_len
= htons(sizeof(struct session
*));
304 memcpy(tag
->tag_data
,
306 sizeof(struct session
*));
312 data_len
+= sizeof(struct pppoe_tag
);
314 tag
->tag_type
= PTT_SRV_NAME
;
319 if(!got_ac_name
&& ph
->code
==PADO_CODE
){
320 data_len
+= sizeof(struct pppoe_tag
);
322 tag
->tag_type
= PTT_AC_NAME
;
327 for (i
= 0; i
< MAX_TAGS
; i
++) {
332 memcpy(tag
, p
->tags
[i
],
333 sizeof(struct pppoe_tag
) + ntohs(p
->tags
[i
]->tag_len
));
338 /* Now fixup the packet struct to make sure all of its pointers
339 are self-contained */
340 memcpy( p
->hdr
, ph
, data_len
);
341 extract_tags( p
->hdr
, p
->tags
);
343 err
= sendto(disc_sock
, buf
, data_len
, 0,
344 (struct sockaddr
*) &p
->addr
,
345 sizeof(struct sockaddr_ll
));
348 poe_error(ses
,"sendto returned: %m\n");
353 /*************************************************************************
355 * Verify that a packet is legal
357 *************************************************************************/
358 int verify_packet( struct session
*ses
, struct pppoe_packet
*p
){
359 struct session
* hu_val
;
361 /* This code here should do all of the error checking and
362 validation on the incoming packet */
365 /* If we receive any error tags, abort */
366 #define CHECK_TAG(name, val) \
367 if((NULL==p->tags[name])== val){ \
368 poe_error(ses,"Tag error: " #name ); \
373 /* If packet is not directed to our MAC address, forget it */
374 if (ses
->state
== PADS_CODE
) {
375 if (memcmp(p
->addr
.sll_addr
, ses
->remote
.sll_addr
, ETH_ALEN
)) {
376 poe_info(ses
,"ETH_DEST mismatch: %E %E \n",p
->addr
.sll_addr
, ses
->remote
.sll_addr
);
381 CHECK_TAG(TAG_SRV_ERR
,0);
382 CHECK_TAG(TAG_SYS_ERR
,0);
383 CHECK_TAG(TAG_GEN_ERR
,0);
385 /* A HOST_UNIQ must be present */
386 CHECK_TAG(TAG_HOST_UNIQ
,1);
388 hu_val
= *TAG_DATA(struct session
* ,p
->tags
[TAG_HOST_UNIQ
]);
391 poe_info(ses
,"HOST_UNIQ mismatch: %08x %i\n",(int)hu_val
,getpid());
395 if(ses
->filt
->htag
&&
396 !verify_tag(ses
,p
,TAG_HOST_UNIQ
,ses
->filt
->htag
->tag_data
,(int)ntohs(ses
->filt
->htag
->tag_len
))) {
397 poe_info(ses
,"HOST_UNIQ failure");
402 if(ses
->filt
->ntag
&& ses
->state
== PADO_CODE
&&
403 !verify_tag(ses
,p
,TAG_AC_NAME
,ses
->filt
->ntag
->tag_data
,(int)ntohs(ses
->filt
->ntag
->tag_len
))){
404 poe_info(ses
,"AC_NAME failure");
408 if(ses
->filt
->stag
&&
409 !verify_tag(ses
,p
,TAG_SRV_NAME
,ses
->filt
->stag
->tag_data
,(int)ntohs(ses
->filt
->stag
->tag_len
))){
410 poe_info(ses
,"SRV_NAME failure");
418 /*************************************************************************
420 * Receive and verify an incoming packet.
422 *************************************************************************/
423 static int recv_disc( struct session
*ses
,
424 struct pppoe_packet
*p
){
426 unsigned int from_len
= sizeof(struct sockaddr_ll
);
428 p
->hdr
= (struct pppoe_hdr
*)p
->buf
;
430 error
= recvfrom( disc_sock
, p
->buf
, 1500, 0,
431 (struct sockaddr
*)&p
->addr
, &from_len
);
433 if(error
< 0) return error
;
435 extract_tags(p
->hdr
,p
->tags
);
441 /*************************************************************************
445 *************************************************************************/
446 int session_disconnect(struct session
*ses
){
447 struct pppoe_packet padt
;
449 memset(&padt
,0,sizeof(struct pppoe_packet
));
450 memcpy(&padt
.addr
, &ses
->remote
, sizeof(struct sockaddr_ll
));
452 padt
.hdr
= (struct pppoe_hdr
*) ses
->curr_pkt
.buf
;
455 padt
.hdr
->code
= PADT_CODE
;
456 padt
.hdr
->sid
= ses
->sp
.sa_addr
.pppoe
.sid
;
458 LOGX_INFO("Sending PADT.");
460 send_disc(ses
,&padt
);
461 ses
->sp
.sa_addr
.pppoe
.sid
= 0 ;
462 ses
->state
= PADO_CODE
;
468 /*************************************************************************
470 * Make a connection -- behaviour depends on callbacks specified in "ses"
472 *************************************************************************/
473 int session_connect(struct session
*ses
)
476 struct pppoe_packet
*p_out
=NULL
;
477 struct pppoe_packet rcv_packet
;
482 ret
= (*ses
->init_disc
)(ses
, NULL
, &p_out
);
484 LOGX_DEBUG("%s: ses->init_disc() == %d", __FUNCTION__
, ret
);
489 /* main discovery loop */
492 //while(ses->retransmits < ses->retries || ses->retries==-1 ){
493 while ((!idle_time_limit
&& dial_cnt
< (3 - 1)) || (ses
->retransmits
< ses
->retries
|| ses
->retries
==-1)) {
497 if(ses
->retransmits
< 0) {
499 FD_SET(disc_sock
,&in
);
500 ret
= select(disc_sock
+1, &in
, NULL
, NULL
, NULL
);
504 //tv.tv_sec = 1 << ses->retransmits;
505 /*******************************************
506 * modify by tanghui @ 2006-03-27
508 *******************************************/
509 if(!idle_time_limit
) {
510 if((ses
->curr_pkt
.hdr
->code
!= PADI_CODE
) || (dial_cnt
< (3 - 1))) {
514 tv
.tv_sec
= (int)(3 + (((retransmit_time
- 3.0) * rand())/ (RAND_MAX
+ 1.0)));
515 if((tv
.tv_sec
> 93) && (tv
.tv_sec
> retransmit_time
- 90)) {
518 //tv.tv_sec = 3 + gen_random_int(retransmit_time - 3);
525 /*******************************************/
529 FD_SET(disc_sock
,&in
);
530 ret
= select(disc_sock
+1, &in
, NULL
, NULL
, &tv
);
533 if( ret
< 0 && errno
!= EINTR
){
534 LOGX_DEBUG("%s: select() == %d", __FUNCTION__
, ret
);
537 else if( ret
== 0 ) {
538 if (!((!idle_time_limit
&& dial_cnt
< (3 - 1)) || (ses
->retransmits
< ses
->retries
|| ses
->retries
==-1))) {
539 redial_immediately
= 1;
542 poe_dbglog(ses
, "Re-sending ...");
545 ret
= (*ses
->timeout
)(ses
, NULL
, &p_out
);
547 LOGX_DEBUG("%s: ses->timeout() == %d", __FUNCTION__
, ret
);
551 else if (p_out
&& !redial_immediately
) {
552 LOGX_INFO("Resending...");
553 send_disc(ses
,p_out
);
559 ret
= recv_disc(ses
, &rcv_packet
);
560 /* Should differentiate between system errors and
561 bad packets and the like... */
562 if( ret
< 0 && errno
) {
563 LOGX_DEBUG("%s: recv_disc() == %d, errno == %d", __FUNCTION__
, ret
, errno
);
567 #if 1 // see rc/redial -- zzz
571 if ((f
= fopen(pppoe_disc_file
, "w")) != NULL
) {
573 fwrite(&si
.uptime
, sizeof(si
.uptime
), 1, f
);
578 switch (rcv_packet
.hdr
->code
) {
583 ret
= (*ses
->rcv_padi
)(ses
,&rcv_packet
,&p_out
);
585 LOGX_DEBUG("%s: ses->rcv_padi() == %d", __FUNCTION__
, ret
);
592 case PADO_CODE
: /* wait for PADO */
595 ret
= (*ses
->rcv_pado
)(ses
,&rcv_packet
,&p_out
);
597 LOGX_DEBUG("%s: ses->rcv_pado() == %d", __FUNCTION__
, ret
);
610 ret
= (*ses
->rcv_padr
)(ses
,&rcv_packet
,&p_out
);
612 LOGX_DEBUG("%s: ses->rcv_padr() == %d", __FUNCTION__
, ret
);
619 case PADS_CODE
: /* wait for PADS */
622 ret
= (*ses
->rcv_pads
)(ses
,&rcv_packet
,&p_out
);
623 LOGX_DEBUG("%s: ses->rcv_pads() == %d", __FUNCTION__
, ret
);
636 if (rcv_packet
.hdr
->sid
!= ses
->sp
.sa_addr
.pppoe
.sid
) {
637 #if 1 // probably from previous session, ignore -- zzz
638 LOGX_INFO("Received PADT for 0x%04X, expecting 0x%04X", rcv_packet
.hdr
->sid
, ses
->sp
.sa_addr
.pppoe
.sid
);
641 redial_immediately
= 1;
643 // --ses->retransmits;
648 // checkme: ?? sid will never be the same here. Dead code ?? -- zzz
651 ret
= (*ses
->rcv_padt
)(ses
,&rcv_packet
,&p_out
);
653 LOGX_DEBUG("%s: ses->rcv_padt() == %d", __FUNCTION__
, ret
);
657 redial_immediately
= 1;
658 LOGX_DEBUG("%s: PADT rcv_padt", __FUNCTION__
);
664 LOGX_DEBUG("%s: PADT !rcv_padt", __FUNCTION__
);
665 poe_error (ses
,"connection terminated");
666 redial_immediately
= 1;
672 LOGX_DEBUG("%s: invalid packet %d", __FUNCTION__
, rcv_packet
.hdr
->code
);
678 LOGX_DEBUG("%s: return default", __FUNCTION__
);
683 /*************************************************************************
685 * Register an ethernet address as a client of relaying services.
687 *************************************************************************/
689 int add_client(char *addr)
691 struct pppoe_con* pc = (struct pppoe_con*)malloc(sizeof(struct pppoe_con));
696 memset(pc, 0 , sizeof(struct pppoe_con));
698 memcpy(pc->client,addr, ETH_ALEN);
699 memcpy(pc->key, addr, ETH_ALEN);
701 pc->key_len = ETH_ALEN;
703 if( (ret=store_con(pc)) < 0 ){
711 struct pppoe_tag
*make_filter_tag(short type
, short length
, char* data
){
712 struct pppoe_tag
*pt
=
713 (struct pppoe_tag
* )malloc( sizeof(struct pppoe_tag
) + length
);
715 if(pt
== NULL
) return NULL
;
717 pt
->tag_len
=htons(length
);
720 if(length
>0 && data
){
721 memcpy( pt
+1, data
, length
);