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.
16 int verify_packet( struct session *ses, struct pppoe_packet *p);
18 #define TAG_DATA(type,tag_ptr) ((type *) ((struct pppoe_tag*)tag_ptr)->tag_data)
21 /***************************************************************************
23 * Return the location where the next tag can be pu
25 **************************************************************************/
26 static struct pppoe_tag *next_tag(struct pppoe_hdr *ph)
28 return (struct pppoe_tag *)
29 (((char *) &ph->tag) + ntohs(ph->length));
32 /**************************************************************************
34 * Update header to reflect the addition of a new tag
36 **************************************************************************/
37 static void add_tag(struct pppoe_hdr *ph, struct pppoe_tag *pt)
39 int len = (ntohs(ph->length) +
41 sizeof(struct pppoe_tag));
43 if (pt != next_tag(ph))
44 printf("PPPoE add_tag caller is buggy\n");
46 ph->length = htons(len);
49 /*************************************************************************
51 * Look for a tag of a specific type
53 ************************************************************************/
54 struct pppoe_tag *get_tag(struct pppoe_hdr *ph, u_int16_t idx)
56 char *end = (char *) next_tag(ph);
58 struct pppoe_tag *pt = &ph->tag[0];
61 * Keep processing tags while a tag header will still fit.
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.
67 while ((char *)(pt + 1) <= end) {
69 * If the tag data would go past the end of the packet, abort.
71 ptn = (((char *) (pt + 1)) + ntohs(pt->tag_len));
75 if (pt->tag_type == idx)
78 pt = (struct pppoe_tag *) ptn;
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){
104 /*************************************************************************
106 * Makes a copy of a tag into a PPPoE packe
108 ************************************************************************/
109 void copy_tag(struct pppoe_packet *dest, struct pppoe_tag *pt)
111 struct pppoe_tag *end_tag = get_tag(dest->hdr, PTT_EOL);
117 tagid = tag_index(pt->tag_type);
119 tag_len = sizeof(struct pppoe_tag) + ntohs(pt->tag_len);
122 memcpy(((char*)end_tag)+tag_len ,
123 end_tag, sizeof(struct pppoe_tag));
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);
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));
140 /*************************************************************************
142 * Put tags from a packet into a nice array
144 ************************************************************************/
145 static void extract_tags(struct pppoe_hdr *ph, struct pppoe_tag** buf){
147 for(;i<MAX_TAGS;++i){
148 buf[i] = get_tag(ph,tag_map[i]);
153 /*************************************************************************
155 * Verify that a packet has a tag containint a specific value
157 ************************************************************************/
158 static int verify_tag(struct session* ses,
159 struct pppoe_packet* p,
165 struct pppoe_tag *pt = p->tags[id];
168 poe_info(ses,"Missing tag %d. Expected %s\n",
172 len = ntohs(pt->tag_len);
174 poe_info(ses,"Length mismatch on tag %d: expect: %d got: %d\n",
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);
188 /*************************************************************************
190 * Verify the existence of an ethernet device.
191 * Construct an AF_PACKET address struct to match.
193 ************************************************************************/
194 int get_sockaddr_ll(const char *devnam,struct sockaddr_ll* sll){
200 disc_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
206 strncpy(ifr.ifr_name, devnam, sizeof(ifr.ifr_name));
208 retval = ioctl( disc_sock , SIOCGIFINDEX, &ifr);
211 // error("Bad device name: %s (%m)",devnam);
215 if(sll) sll->sll_ifindex = ifr.ifr_ifindex;
217 retval = ioctl (disc_sock, SIOCGIFHWADDR, &ifr);
219 // error("Bad device name: %s (%m)",devnam);
223 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
224 error("Interface %s is not Ethernet!", devnam);
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);
241 /*************************************************************************
243 * Construct and send a discovery message.
245 ************************************************************************/
246 int send_disc(struct session *ses, struct pppoe_packet *p)
248 char buf[MAX_PAYLOAD + sizeof(struct pppoe_hdr)];
249 int data_len = sizeof(struct pppoe_hdr);
251 struct pppoe_hdr *ph = NULL;
252 struct pppoe_tag *tag = NULL;
254 int got_host_uniq = 0;
255 int got_srv_name = 0;
258 for (i = 0; i < MAX_TAGS; i++) {
262 got_host_uniq |= (p->tags[i]->tag_type == PTT_HOST_UNIQ);
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);
269 got_srv_name |= (p->tags[i]->tag_type == PTT_SRV_NAME);
270 got_ac_name |= (p->tags[i]->tag_type == PTT_AC_NAME);
272 data_len += (ntohs(p->tags[i]->tag_len) +
273 sizeof(struct pppoe_tag));
276 ph = (struct pppoe_hdr *) buf;
279 memcpy(ph, p->hdr, sizeof(struct pppoe_hdr));
280 ph->length = __constant_htons(0);
282 /* if no HOST_UNIQ tags --- add one with process id */
284 data_len += (sizeof(struct pppoe_tag) +
285 sizeof(struct session *));
287 tag->tag_type = PTT_HOST_UNIQ;
288 tag->tag_len = htons(sizeof(struct session *));
289 memcpy(tag->tag_data,
291 sizeof(struct session *));
297 data_len += sizeof(struct pppoe_tag);
299 tag->tag_type = PTT_SRV_NAME;
304 if(!got_ac_name && ph->code==PADO_CODE){
305 data_len += sizeof(struct pppoe_tag);
307 tag->tag_type = PTT_AC_NAME;
312 for (i = 0; i < MAX_TAGS; i++) {
317 memcpy(tag, p->tags[i],
318 sizeof(struct pppoe_tag) + ntohs(p->tags[i]->tag_len));
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);
328 error = sendto(disc_sock, buf, data_len, 0,
329 (struct sockaddr*) &p->addr,
330 sizeof(struct sockaddr_ll));
333 poe_error(ses,"sendto returned: %m\n");
338 /*************************************************************************
340 * Verify that a packet is legal
342 *************************************************************************/
343 int verify_packet( struct session *ses, struct pppoe_packet *p){
344 struct session * hu_val;
346 /* This code here should do all of the error checking and
347 validation on the incoming packet */
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 ); \
359 CHECK_TAG(TAG_SRV_ERR,0);
360 CHECK_TAG(TAG_SYS_ERR,0);
361 CHECK_TAG(TAG_GEN_ERR,0);
363 /* A HOST_UNIQ must be present */
364 CHECK_TAG(TAG_HOST_UNIQ,1);
366 hu_val = *TAG_DATA(struct session* ,p->tags[TAG_HOST_UNIQ]);
369 poe_info(ses,"HOST_UNIQ mismatch: %08x %i\n",(int)hu_val,getpid());
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)))
377 poe_info(ses,"HOST_UNIQ successful match\n");
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");
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");
395 /*************************************************************************
397 * Receive and verify an incoming packet.
399 *************************************************************************/
400 static int recv_disc( struct session *ses,
401 struct pppoe_packet *p){
403 unsigned int from_len = sizeof(struct sockaddr_ll);
404 struct session* hu_val;
405 struct pppoe_tag *pt;
407 p->hdr = (struct pppoe_hdr*)p->buf;
409 error = recvfrom( disc_sock, p->buf, 1500, 0,
410 (struct sockaddr*)&p->addr, &from_len);
412 if(error < 0) return error;
414 extract_tags(p->hdr,p->tags);
420 /*************************************************************************
424 *************************************************************************/
425 int session_disconnect(struct session *ses){
426 struct pppoe_packet padt;
428 memset(&padt,0,sizeof(struct pppoe_packet));
429 memcpy(&padt.addr, &ses->remote, sizeof(struct sockaddr_ll));
431 padt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
434 padt.hdr->code = PADT_CODE;
435 padt.hdr->sid = ses->sp.sa_addr.pppoe.sid;
437 send_disc(ses,&padt);
438 ses->sp.sa_addr.pppoe.sid = 0 ;
439 ses->state = PADO_CODE;
445 /*************************************************************************
447 * Make a connection -- behaviour depends on callbacks specified in "ses"
449 *************************************************************************/
450 int session_connect(struct session *ses)
455 struct pppoe_tag *tags = NULL;
456 struct pppoe_packet *p_out=NULL;
457 struct pppoe_packet rcv_packet;
462 ret = (*ses->init_disc)(ses, NULL, &p_out);
463 if( ret != 0 ) return ret;
466 /* main discovery loop */
469 while(ses->retransmits < ses->retries || ses->retries==-1 ){
475 FD_SET(disc_sock,&in);
477 if(ses->retransmits>=0){
479 tv.tv_sec = 1 << ses->retransmits;
481 ret = select(disc_sock+1, &in, NULL, NULL, &tv);
483 ret = select(disc_sock+1, &in, NULL, NULL, NULL);
488 poe_dbglog(ses, "Re-sending ...");
492 ret = (*ses->timeout)(ses, NULL, &p_out);
497 send_disc(ses,p_out);
503 ret = recv_disc(ses, &rcv_packet);
505 /* Should differentiate between system errors and
506 bad packets and the like... */
507 if( ret < 0 && errno != EINTR){
515 switch (rcv_packet.hdr->code) {
520 ret = (*ses->rcv_padi)(ses,&rcv_packet,&p_out);
529 case PADO_CODE: /* wait for PADO */
532 ret = (*ses->rcv_pado)(ses,&rcv_packet,&p_out);
544 ret = (*ses->rcv_padr)(ses,&rcv_packet,&p_out);
556 ret = (*ses->rcv_pads)(ses,&rcv_packet,&p_out);
567 if( rcv_packet.hdr->sid != ses->sp.sa_addr.pppoe.sid ){
572 ret = (*ses->rcv_padt)(ses,&rcv_packet,&p_out);
578 poe_error (ses,"connection terminated");
584 poe_error(ses,"invalid packet %P",&rcv_packet);
592 /*************************************************************************
594 * Register an ethernet address as a client of relaying services.
596 *************************************************************************/
597 int add_client(char *addr)
599 struct pppoe_con* pc = (struct pppoe_con*)malloc(sizeof(struct pppoe_con));
604 memset(pc, 0 , sizeof(struct pppoe_con));
606 memcpy(pc->client,addr, ETH_ALEN);
607 memcpy(pc->key, addr, ETH_ALEN);
609 pc->key_len = ETH_ALEN;
611 if( (ret=store_con(pc)) < 0 ){
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 );
622 if(pt == NULL) return NULL;
624 pt->tag_len=htons(length);
627 if(length>0 && data){
628 memcpy( pt+1, data, length);