]> git.ozlabs.org Git - ppp.git/blob - pppd/plugins/pppoe/libpppoe.c
pppoe plugin stuff - Michal Ostrowski's version, lightly hacked
[ppp.git] / pppd / plugins / pppoe / libpppoe.c
1 /* PPPoE support library "libpppoe"
2  *
3  * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
4  *                Jamal Hadi Salim <hadi@cyberus.ca>
5  *
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.
10  */
11
12 #include "pppoe.h"
13
14 int disc_sock=-1;
15
16 int verify_packet( struct session *ses, struct pppoe_packet *p);
17
18 #define TAG_DATA(type,tag_ptr) ((type *) ((struct pppoe_tag*)tag_ptr)->tag_data)
19
20
21 /***************************************************************************
22  *
23  * Return the location where the next tag can be pu
24  *
25  **************************************************************************/
26 static  struct pppoe_tag *next_tag(struct pppoe_hdr *ph)
27 {
28     return (struct pppoe_tag *)
29         (((char *) &ph->tag) + ntohs(ph->length));
30 }
31
32 /**************************************************************************
33  *
34  * Update header to reflect the addition of a new tag
35  *
36  **************************************************************************/
37 static  void add_tag(struct pppoe_hdr *ph, struct pppoe_tag *pt)
38 {
39     int len = (ntohs(ph->length) +
40                ntohs(pt->tag_len) +
41                sizeof(struct pppoe_tag));
42
43     if (pt != next_tag(ph))
44         printf("PPPoE add_tag caller is buggy\n");
45
46     ph->length = htons(len);
47 }
48
49 /*************************************************************************
50  *
51  * Look for a tag of a specific type
52  *
53  ************************************************************************/
54 struct pppoe_tag *get_tag(struct pppoe_hdr *ph, u_int16_t idx)
55 {
56     char *end = (char *) next_tag(ph);
57     char *ptn = NULL;
58     struct pppoe_tag *pt = &ph->tag[0];
59
60     /*
61      * Keep processing tags while a tag header will still fit.
62      *
63      * This check will ensure that the entire tag header pointed
64      * to by pt will fit inside the message, and thus it will be
65      * valid to check the tag_type and tag_len fields.
66      */
67     while ((char *)(pt + 1) <= end) {
68         /*
69          * If the tag data would go past the end of the packet, abort.
70          */
71         ptn = (((char *) (pt + 1)) + ntohs(pt->tag_len));
72         if (ptn > end)
73             return NULL;
74
75         if (pt->tag_type == idx)
76             return pt;
77
78         pt = (struct pppoe_tag *) ptn;
79     }
80
81     return NULL;
82 }
83
84 /* We want to use tag names to reference into arrays  containing the tag data.
85    This takes an RFC 2516 tag identifier and maps it into a local one.
86    The reverse mapping is accomplished via the tag_map array */
87 #define UNMAP_TAG(x) case PTT_##x : return TAG_##x
88 static inline int tag_index(int tag){
89     switch(tag){
90         UNMAP_TAG(SRV_NAME);
91         UNMAP_TAG(AC_NAME);
92         UNMAP_TAG(HOST_UNIQ);
93         UNMAP_TAG(AC_COOKIE);
94         UNMAP_TAG(VENDOR);
95         UNMAP_TAG(RELAY_SID);
96         UNMAP_TAG(SRV_ERR);
97         UNMAP_TAG(SYS_ERR);
98         UNMAP_TAG(GEN_ERR);
99         UNMAP_TAG(EOL);
100     };
101     return -1;
102 }
103
104 /*************************************************************************
105  *
106  * Makes a copy of a tag into a PPPoE packe
107  *
108  ************************************************************************/
109 void copy_tag(struct pppoe_packet *dest, struct pppoe_tag *pt)
110 {
111     struct pppoe_tag *end_tag = get_tag(dest->hdr, PTT_EOL);
112     int tagid;
113     int tag_len;
114     if( !pt ) {
115         return;
116     }
117     tagid = tag_index(pt->tag_type);
118
119     tag_len = sizeof(struct pppoe_tag) + ntohs(pt->tag_len);
120
121     if( end_tag ){
122         memcpy(((char*)end_tag)+tag_len ,
123                end_tag, sizeof(struct pppoe_tag));
124
125         dest->tags[tagid]=end_tag;
126         dest->tags[TAG_EOL] = (struct pppoe_tag*)((char*)dest->tags[TAG_EOL] + tag_len);
127         memcpy(end_tag, pt, tag_len);
128         dest->hdr->length = htons(ntohs(dest->hdr->length) + tag_len);
129
130     }else{
131         memcpy(next_tag(dest->hdr),pt, tag_len);
132         dest->tags[tagid]=next_tag(dest->hdr);
133         add_tag(dest->hdr,next_tag(dest->hdr));
134     }
135
136
137 }
138
139
140 /*************************************************************************
141  *
142  * Put tags from a packet into a nice array
143  *
144  ************************************************************************/
145 static void extract_tags(struct pppoe_hdr *ph, struct pppoe_tag** buf){
146     int i=0;
147     for(;i<MAX_TAGS;++i){
148         buf[i] = get_tag(ph,tag_map[i]);
149     }
150 }
151
152
153 /*************************************************************************
154  *
155  * Verify that a packet has a tag containint a specific value
156  *
157  ************************************************************************/
158 static int verify_tag(struct session* ses,
159                       struct pppoe_packet* p,
160                       unsigned short id,
161                       char* data,
162                       int data_len)
163 {
164     int len;
165     struct pppoe_tag *pt = p->tags[id];
166
167     if( !pt ){
168         poe_info(ses,"Missing tag %d. Expected %s\n",
169                  id,data);
170         return 0;
171     }
172     len = ntohs(pt->tag_len);
173     if(len != data_len){
174         poe_info(ses,"Length mismatch on tag %d: expect: %d got: %d\n",
175                  id, data_len, len);
176         return 0;
177     }
178
179     if( 0!=memcmp(pt->tag_data,data,data_len)){
180         poe_info(ses,"Tag data mismatch on tag %d: expect: %s vs %s\n",
181                  id, data,pt->tag_data);
182         return 0;
183     }
184     return 1;
185 }
186
187
188 /*************************************************************************
189  *
190  * Verify the existence of an ethernet device.
191  * Construct an AF_PACKET address struct to match.
192  *
193  ************************************************************************/
194 int get_sockaddr_ll(const char *devnam,struct sockaddr_ll* sll){
195     struct ifreq ifr;
196     int retval;
197
198     if(disc_sock<0){
199
200         disc_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
201         if( disc_sock < 0 ){
202             return -1;
203         }
204     }
205
206     strncpy(ifr.ifr_name, devnam, sizeof(ifr.ifr_name));
207
208     retval = ioctl( disc_sock , SIOCGIFINDEX, &ifr);
209
210     if( retval < 0 ){
211 //      error("Bad device name: %s  (%m)",devnam);
212         return 0;
213     }
214
215     if(sll) sll->sll_ifindex = ifr.ifr_ifindex;
216
217     retval = ioctl (disc_sock, SIOCGIFHWADDR, &ifr);
218     if( retval < 0 ){
219 //      error("Bad device name: %s  (%m)",devnam);
220         return 0;
221     }
222
223     if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
224         error("Interface %s is not Ethernet!", devnam);
225         return 0;
226     }
227     if(sll){
228         sll->sll_family = AF_PACKET;
229         sll->sll_protocol= ntohs(ETH_P_PPP_DISC);
230         sll->sll_hatype = ARPHRD_ETHER;
231         sll->sll_pkttype = PACKET_BROADCAST;
232         sll->sll_hatype = ETH_ALEN;
233         memcpy( sll->sll_addr , ifr.ifr_hwaddr.sa_data, ETH_ALEN);
234     }
235     return 1;
236 }
237
238
239
240
241 /*************************************************************************
242  *
243  * Construct and send a discovery message.
244  *
245  ************************************************************************/
246 int send_disc(struct session *ses, struct pppoe_packet *p)
247 {
248     char buf[MAX_PAYLOAD + sizeof(struct pppoe_hdr)];
249     int data_len = sizeof(struct pppoe_hdr);
250
251     struct pppoe_hdr *ph = NULL;
252     struct pppoe_tag *tag = NULL;
253     int i, error = 0;
254     int got_host_uniq = 0;
255     int got_srv_name = 0;
256     int got_ac_name = 0;
257
258     for (i = 0; i < MAX_TAGS; i++) {
259         if (!p->tags[i])
260             continue;
261
262         got_host_uniq |= (p->tags[i]->tag_type == PTT_HOST_UNIQ);
263
264         /* Relay identifiers qualify as HOST_UNIQ's:
265            we need HOST_UNIQ to uniquely identify the packet,
266            PTT_RELAY_SID is sufficient for us for outgoing packets */
267         got_host_uniq |= (p->tags[i]->tag_type == PTT_RELAY_SID);
268
269         got_srv_name |= (p->tags[i]->tag_type == PTT_SRV_NAME);
270         got_ac_name  |= (p->tags[i]->tag_type == PTT_AC_NAME);
271
272         data_len += (ntohs(p->tags[i]->tag_len) +
273                      sizeof(struct pppoe_tag));
274     }
275
276     ph = (struct pppoe_hdr *) buf;
277
278
279     memcpy(ph, p->hdr, sizeof(struct pppoe_hdr));
280     ph->length = __constant_htons(0);
281
282     /* if no HOST_UNIQ tags --- add one with process id */
283     if (!got_host_uniq){
284         data_len += (sizeof(struct pppoe_tag) +
285                      sizeof(struct session *));
286         tag = next_tag(ph);
287         tag->tag_type = PTT_HOST_UNIQ;
288         tag->tag_len = htons(sizeof(struct session *));
289         memcpy(tag->tag_data,
290                &ses,
291                sizeof(struct session *));
292
293         add_tag(ph, tag);
294     }
295
296     if( !got_srv_name ){
297         data_len += sizeof(struct pppoe_tag);
298         tag = next_tag(ph);
299         tag->tag_type = PTT_SRV_NAME;
300         tag->tag_len = 0;
301         add_tag(ph, tag);
302     }
303
304     if(!got_ac_name && ph->code==PADO_CODE){
305         data_len += sizeof(struct pppoe_tag);
306         tag = next_tag(ph);
307         tag->tag_type = PTT_AC_NAME;
308         tag->tag_len = 0;
309         add_tag(ph, tag);
310     }
311
312     for (i = 0; i < MAX_TAGS; i++) {
313         if (!p->tags[i])
314             continue;
315
316         tag = next_tag(ph);
317         memcpy(tag, p->tags[i],
318                sizeof(struct pppoe_tag) + ntohs(p->tags[i]->tag_len));
319
320         add_tag(ph, tag);
321     }
322
323     /* Now fixup the packet struct to make sure all of its pointers
324        are self-contained */
325     memcpy( p->hdr , ph, data_len );
326     extract_tags( p->hdr, p->tags);
327
328     error = sendto(disc_sock, buf, data_len, 0,
329                    (struct sockaddr*) &p->addr,
330                    sizeof(struct sockaddr_ll));
331
332     if(error < 0)
333         poe_error(ses,"sendto returned: %m\n");
334
335     return error;
336 }
337
338 /*************************************************************************
339  *
340  * Verify that a packet is legal
341  *
342  *************************************************************************/
343 int verify_packet( struct session *ses, struct pppoe_packet *p){
344     struct session * hu_val;
345
346     /* This code here should do all of the error checking and
347        validation on the incoming packet */
348
349
350     /* If we receive any error tags, abort */
351 #define CHECK_TAG(name, val)                                    \
352     if((NULL==p->tags[name])== val){                            \
353         poe_error(ses,"Tag error: " #name );                    \
354         return -1;                                              \
355     }
356
357
358
359     CHECK_TAG(TAG_SRV_ERR,0);
360     CHECK_TAG(TAG_SYS_ERR,0);
361     CHECK_TAG(TAG_GEN_ERR,0);
362
363     /* A HOST_UNIQ must be present */
364     CHECK_TAG(TAG_HOST_UNIQ,1);
365
366     hu_val = *TAG_DATA(struct session* ,p->tags[TAG_HOST_UNIQ]);
367
368     if( hu_val != ses ){
369         poe_info(ses,"HOST_UNIQ mismatch: %08x %i\n",(int)hu_val,getpid());
370         return -1;
371     }
372
373     if(ses->filt->htag &&
374        !verify_tag(ses,p,TAG_HOST_UNIQ,ses->filt->htag->tag_data,(int)ntohs(ses->filt->htag->tag_len)))
375         return -1;
376     else
377         poe_info(ses,"HOST_UNIQ successful match\n");
378
379
380     if(ses->filt->ntag &&
381        !verify_tag(ses,p,TAG_AC_NAME,ses->filt->ntag->tag_data,(int)ntohs(ses->filt->ntag->tag_len))){
382         poe_info(ses,"AC_NAME failure");
383         return -1;
384     }
385
386     if(ses->filt->stag &&
387        !verify_tag(ses,p,TAG_SRV_NAME,ses->filt->stag->tag_data,(int)ntohs(ses->filt->stag->tag_len))){
388         poe_info(ses,"SRV_NAME failure");
389         return -1;
390     }
391
392 }
393
394
395 /*************************************************************************
396  *
397  * Receive and verify an incoming packet.
398  *
399  *************************************************************************/
400 static int recv_disc( struct session *ses,
401                       struct pppoe_packet *p){
402     int error = 0;
403     unsigned int from_len = sizeof(struct sockaddr_ll);
404     struct session* hu_val;
405     struct pppoe_tag *pt;
406
407     p->hdr = (struct pppoe_hdr*)p->buf;
408
409     error = recvfrom( disc_sock, p->buf, 1500, 0,
410                       (struct sockaddr*)&p->addr, &from_len);
411
412     if(error < 0) return error;
413
414     extract_tags(p->hdr,p->tags);
415
416     return 1;
417 }
418
419
420 /*************************************************************************
421  *
422  * Send a PADT
423  *
424  *************************************************************************/
425 int session_disconnect(struct session *ses){
426     struct pppoe_packet padt;
427
428     memset(&padt,0,sizeof(struct pppoe_packet));
429     memcpy(&padt.addr, &ses->remote, sizeof(struct sockaddr_ll));
430
431     padt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
432     padt.hdr->ver  = 1;
433     padt.hdr->type = 1;
434     padt.hdr->code = PADT_CODE;
435     padt.hdr->sid  = ses->sp.sa_addr.pppoe.sid;
436
437     send_disc(ses,&padt);
438     ses->sp.sa_addr.pppoe.sid = 0 ;
439     ses->state = PADO_CODE;
440     return 0;
441
442 }
443
444
445 /*************************************************************************
446  *
447  * Make a connection -- behaviour depends on callbacks specified in "ses"
448  *
449  *************************************************************************/
450 int session_connect(struct session *ses)
451 {
452
453     int pkt_size=0;
454     int ret_pkt_size=0;
455     struct pppoe_tag *tags = NULL;
456     struct pppoe_packet *p_out=NULL;
457     struct pppoe_packet rcv_packet;
458     int ret;
459
460
461     if(ses->init_disc){
462         ret = (*ses->init_disc)(ses, NULL, &p_out);
463         if( ret != 0 ) return ret;
464     }
465
466     /* main discovery loop */
467
468
469     while(ses->retransmits < ses->retries || ses->retries==-1 ){
470
471         fd_set in;
472         struct timeval tv;
473         FD_ZERO(&in);
474
475         FD_SET(disc_sock,&in);
476
477         if(ses->retransmits>=0){
478             ++ses->retransmits;
479             tv.tv_sec = 1 << ses->retransmits;
480             tv.tv_usec = 0;
481             ret = select(disc_sock+1, &in, NULL, NULL, &tv);
482         }else{
483             ret = select(disc_sock+1, &in, NULL, NULL, NULL);
484         }
485
486         if( ret == 0 ){
487             if( DEB_DISC ){
488                 poe_dbglog(ses, "Re-sending ...");
489             }
490
491             if( ses->timeout ){
492                 ret = (*ses->timeout)(ses, NULL, &p_out);
493                 if( ret != 0 )
494                     return ret;
495
496             }else if(p_out){
497                 send_disc(ses,p_out);
498             }
499             continue;
500         }
501
502
503         ret = recv_disc(ses, &rcv_packet);
504
505         /* Should differentiate between system errors and
506            bad packets and the like... */
507         if( ret < 0 && errno != EINTR){
508
509             return -1;
510         }
511
512
513
514
515         switch (rcv_packet.hdr->code) {
516
517         case PADI_CODE:
518         {
519             if(ses->rcv_padi){
520                 ret = (*ses->rcv_padi)(ses,&rcv_packet,&p_out);
521
522                 if( ret != 0){
523                     return ret;
524                 }
525             }
526             break;
527         }
528
529         case PADO_CODE:         /* wait for PADO */
530         {
531             if(ses->rcv_pado){
532                 ret = (*ses->rcv_pado)(ses,&rcv_packet,&p_out);
533
534                 if( ret != 0){
535                     return ret;
536                 }
537             }
538             break;
539         }
540
541         case PADR_CODE:
542         {
543             if(ses->rcv_padr){
544                 ret = (*ses->rcv_padr)(ses,&rcv_packet,&p_out);
545
546                 if( ret != 0){
547                     return ret;
548                 }
549             }
550             break;
551         }
552
553         case PADS_CODE:
554         {
555             if(ses->rcv_pads){
556                 ret = (*ses->rcv_pads)(ses,&rcv_packet,&p_out);
557
558                 if( ret != 0){
559                     return ret;
560                 }
561             }
562             break;
563         }
564
565         case PADT_CODE:
566         {
567             if( rcv_packet.hdr->sid != ses->sp.sa_addr.pppoe.sid ){
568                 --ses->retransmits;
569                 continue;
570             }
571             if(ses->rcv_padt){
572                 ret = (*ses->rcv_padt)(ses,&rcv_packet,&p_out);
573
574                 if( ret != 0){
575                     return ret;
576                 }
577             }else{
578                 poe_error (ses,"connection terminated");
579                 return (-1);
580             }
581             break;
582         }
583         default:
584             poe_error(ses,"invalid packet %P",&rcv_packet);
585             return (-1);
586         }
587     }
588     return (0);
589 }
590
591
592 /*************************************************************************
593  *
594  * Register an ethernet address as a client of relaying services.
595  *
596  *************************************************************************/
597 int add_client(char *addr)
598 {
599     struct pppoe_con* pc = (struct pppoe_con*)malloc(sizeof(struct pppoe_con));
600     int ret;
601     if(!pc)
602         return -ENOMEM;
603
604     memset(pc, 0 , sizeof(struct pppoe_con));
605
606     memcpy(pc->client,addr, ETH_ALEN);
607     memcpy(pc->key, addr, ETH_ALEN);
608
609     pc->key_len = ETH_ALEN;
610
611     if( (ret=store_con(pc)) < 0 ){
612         free(pc);
613     }
614     return ret;
615
616 }
617
618 struct pppoe_tag *make_filter_tag(short type, short length, char* data){
619     struct pppoe_tag *pt =
620         (struct pppoe_tag* )malloc( sizeof(struct pppoe_tag) + length );
621
622     if(pt == NULL) return NULL;
623
624     pt->tag_len=htons(length);
625     pt->tag_type=type;
626
627     if(length>0 && data){
628         memcpy( pt+1, data, length);
629     }
630     return pt;
631 }